Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig
new file mode 100644
index 0000000..c90afee
--- /dev/null
+++ b/drivers/isdn/Kconfig
@@ -0,0 +1,67 @@
+#
+# ISDN device configuration
+#
+
+menu "ISDN subsystem"
+
+config ISDN
+	tristate "ISDN support"
+	depends on NET
+	---help---
+	  ISDN ("Integrated Services Digital Networks", called RNIS in France)
+	  is a special type of fully digital telephone service; it's mostly
+	  used to connect to your Internet service provider (with SLIP or
+	  PPP).  The main advantage is that the speed is higher than ordinary
+	  modem/telephone connections, and that you can have voice
+	  conversations while downloading stuff.  It only works if your
+	  computer is equipped with an ISDN card and both you and your service
+	  provider purchased an ISDN line from the phone company.  For
+	  details, read <http://www.alumni.caltech.edu/~dank/isdn/> on the WWW.
+
+	  Select this option if you want your kernel to support ISDN.
+
+
+menu "Old ISDN4Linux"
+	depends on NET && ISDN
+
+config ISDN_I4L
+	tristate "Old ISDN4Linux (obsolete)"
+	---help---
+	  This driver allows you to use an ISDN-card for networking
+	  connections and as dialin/out device.  The isdn-tty's have a built
+	  in AT-compatible modem emulator.  Network devices support autodial,
+	  channel-bundling, callback and caller-authentication without having
+	  a daemon running.  A reduced T.70 protocol is supported with tty's
+	  suitable for German BTX.  On D-Channel, the protocols EDSS1
+	  (Euro-ISDN) and 1TR6 (German style) are supported.  See
+	  <file:Documentation/isdn/README> for more information.
+
+	  ISDN support in the linux kernel is moving towards a new API,
+	  called CAPI (Common ISDN Application Programming Interface).
+	  Therefore the old ISDN4Linux layer is becoming obsolete. It is 
+	  still usable, though, if you select this option.
+
+if ISDN_I4L
+source "drivers/isdn/i4l/Kconfig"
+endif
+
+endmenu
+
+comment "CAPI subsystem"
+	depends on NET && ISDN
+
+config ISDN_CAPI
+	tristate "CAPI2.0 support"
+	depends on ISDN
+	help
+	  This provides the CAPI (Common ISDN Application Programming
+	  Interface, a standard making it easy for programs to access ISDN
+	  hardware, see <http://www.capi.org/>.  This is needed for AVM's set
+	  of active ISDN controllers like B1, T1, M1.
+
+source "drivers/isdn/capi/Kconfig"
+
+source "drivers/isdn/hardware/Kconfig"
+
+endmenu
+
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile
new file mode 100644
index 0000000..03d8ccd
--- /dev/null
+++ b/drivers/isdn/Makefile
@@ -0,0 +1,15 @@
+# Makefile for the kernel ISDN subsystem and device drivers.
+
+# Object files in subdirectories
+
+obj-$(CONFIG_ISDN_I4L)			+= i4l/
+obj-$(CONFIG_ISDN_CAPI)			+= capi/
+obj-$(CONFIG_ISDN_CAPI)			+= hardware/
+obj-$(CONFIG_ISDN_DIVERSION)		+= divert/
+obj-$(CONFIG_ISDN_DRV_HISAX)		+= hisax/
+obj-$(CONFIG_ISDN_DRV_ICN)		+= icn/
+obj-$(CONFIG_ISDN_DRV_PCBIT)		+= pcbit/
+obj-$(CONFIG_ISDN_DRV_SC)		+= sc/
+obj-$(CONFIG_ISDN_DRV_LOOP)		+= isdnloop/
+obj-$(CONFIG_ISDN_DRV_ACT2000)		+= act2000/
+obj-$(CONFIG_HYSDN)			+= hysdn/
diff --git a/drivers/isdn/act2000/Kconfig b/drivers/isdn/act2000/Kconfig
new file mode 100644
index 0000000..78e6ad8
--- /dev/null
+++ b/drivers/isdn/act2000/Kconfig
@@ -0,0 +1,13 @@
+#
+# Config.in for IBM Active 2000 ISDN driver
+#
+config ISDN_DRV_ACT2000
+	tristate "IBM Active 2000 support"
+	depends on ISDN_I4L && ISA
+	help
+	  Say Y here if you have an IBM Active 2000 ISDN card. In order to use
+	  this card, additional firmware is necessary, which has to be loaded
+	  into the card using a utility which is part of the latest
+	  isdn4k-utils package. Please read the file
+	  <file:Documentation/isdn/README.act2000> for more information.
+
diff --git a/drivers/isdn/act2000/Makefile b/drivers/isdn/act2000/Makefile
new file mode 100644
index 0000000..05e582f
--- /dev/null
+++ b/drivers/isdn/act2000/Makefile
@@ -0,0 +1,9 @@
+# Makefile for the act2000 ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_ACT2000)	+= act2000.o
+
+# Multipart objects.
+
+act2000-y			:= module.o capi.o act2000_isa.o
diff --git a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h
new file mode 100644
index 0000000..b091d1a
--- /dev/null
+++ b/drivers/isdn/act2000/act2000.h
@@ -0,0 +1,202 @@
+/* $Id: act2000.h,v 1.8.6.3 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Author       Fritz Elfert
+ * Copyright    by Fritz Elfert      <fritz@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#ifndef act2000_h
+#define act2000_h
+
+#include <linux/compiler.h>
+
+#define ACT2000_IOCTL_SETPORT    1
+#define ACT2000_IOCTL_GETPORT    2
+#define ACT2000_IOCTL_SETIRQ     3
+#define ACT2000_IOCTL_GETIRQ     4
+#define ACT2000_IOCTL_SETBUS     5
+#define ACT2000_IOCTL_GETBUS     6
+#define ACT2000_IOCTL_SETPROTO   7
+#define ACT2000_IOCTL_GETPROTO   8
+#define ACT2000_IOCTL_SETMSN     9
+#define ACT2000_IOCTL_GETMSN    10
+#define ACT2000_IOCTL_LOADBOOT  11
+#define ACT2000_IOCTL_ADDCARD   12
+
+#define ACT2000_IOCTL_TEST      98
+#define ACT2000_IOCTL_DEBUGVAR  99
+
+#define ACT2000_BUS_ISA          1
+#define ACT2000_BUS_MCA          2
+#define ACT2000_BUS_PCMCIA       3
+
+/* Struct for adding new cards */
+typedef struct act2000_cdef {
+	int bus;
+        int port;
+        int irq;
+        char id[10];
+} act2000_cdef;
+
+/* Struct for downloading firmware */
+typedef struct act2000_ddef {
+        int length;             /* Length of code */
+        char __user *buffer;    /* Ptr. to code   */
+} act2000_ddef;
+
+typedef struct act2000_fwid {
+        char isdn[4];
+        char revlen[2];
+        char revision[504];
+} act2000_fwid;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/isdnif.h>
+
+#endif                           /* __KERNEL__ */
+
+#define ACT2000_PORTLEN        8
+
+#define ACT2000_FLAGS_RUNNING  1 /* Cards driver activated */
+#define ACT2000_FLAGS_PVALID   2 /* Cards port is valid    */
+#define ACT2000_FLAGS_IVALID   4 /* Cards irq is valid     */
+#define ACT2000_FLAGS_LOADED   8 /* Firmware loaded        */
+
+#define ACT2000_BCH            2 /* # of channels per card */
+
+/* D-Channel states */
+#define ACT2000_STATE_NULL     0
+#define ACT2000_STATE_ICALL    1
+#define ACT2000_STATE_OCALL    2
+#define ACT2000_STATE_IWAIT    3
+#define ACT2000_STATE_OWAIT    4
+#define ACT2000_STATE_IBWAIT   5
+#define ACT2000_STATE_OBWAIT   6
+#define ACT2000_STATE_BWAIT    7
+#define ACT2000_STATE_BHWAIT   8
+#define ACT2000_STATE_BHWAIT2  9
+#define ACT2000_STATE_DHWAIT  10
+#define ACT2000_STATE_DHWAIT2 11
+#define ACT2000_STATE_BSETUP  12
+#define ACT2000_STATE_ACTIVE  13
+
+#define ACT2000_MAX_QUEUED  8000 /* 2 * maxbuff */
+
+#define ACT2000_LOCK_TX 0
+#define ACT2000_LOCK_RX 1
+
+typedef struct act2000_chan {
+	unsigned short callref;          /* Call Reference              */
+	unsigned short fsm_state;        /* Current D-Channel state     */
+	unsigned short eazmask;          /* EAZ-Mask for this Channel   */
+	short queued;                    /* User-Data Bytes in TX queue */
+	unsigned short plci;
+	unsigned short ncci;
+	unsigned char  l2prot;           /* Layer 2 protocol            */
+	unsigned char  l3prot;           /* Layer 3 protocol            */
+} act2000_chan;
+
+typedef struct msn_entry {
+	char eaz;
+        char msn[16];
+        struct msn_entry * next;
+} msn_entry;
+
+typedef struct irq_data_isa {
+	__u8           *rcvptr;
+	__u16           rcvidx;
+	__u16           rcvlen;
+	struct sk_buff *rcvskb;
+	__u8            rcvignore;
+	__u8            rcvhdr[8];
+} irq_data_isa;
+
+typedef union irq_data {
+	irq_data_isa isa;
+} irq_data;
+
+/*
+ * Per card driver data
+ */
+typedef struct act2000_card {
+	unsigned short port;		/* Base-port-address                */
+	unsigned short irq;		/* Interrupt                        */
+	u_char ptype;			/* Protocol type (1TR6 or Euro)     */
+	u_char bus;			/* Cardtype (ISA, MCA, PCMCIA)      */
+	struct act2000_card *next;	/* Pointer to next device struct    */
+	spinlock_t lock;		/* protect critical operations      */
+	int myid;			/* Driver-Nr. assigned by linklevel */
+	unsigned long flags;		/* Statusflags                      */
+	unsigned long ilock;		/* Semaphores for IRQ-Routines      */
+	struct sk_buff_head rcvq;	/* Receive-Message queue            */
+	struct sk_buff_head sndq;	/* Send-Message queue               */
+	struct sk_buff_head ackq;	/* Data-Ack-Message queue           */
+	u_char *ack_msg;		/* Ptr to User Data in User skb     */
+	__u16 need_b3ack;		/* Flag: Need ACK for current skb   */
+	struct sk_buff *sbuf;		/* skb which is currently sent      */
+	struct timer_list ptimer;	/* Poll timer                       */
+	struct work_struct snd_tq;	/* Task struct for xmit bh          */
+	struct work_struct rcv_tq;	/* Task struct for rcv bh           */
+	struct work_struct poll_tq;	/* Task struct for polled rcv bh    */
+	msn_entry *msn_list;
+	unsigned short msgnum;		/* Message number for sending       */
+	spinlock_t mnlock;		/* lock for msgnum                  */
+	act2000_chan bch[ACT2000_BCH];	/* B-Channel status/control         */
+	char   status_buf[256];		/* Buffer for status messages       */
+	char   *status_buf_read;
+	char   *status_buf_write;
+	char   *status_buf_end;
+	irq_data idat;			/* Data used for IRQ handler        */
+	isdn_if interface;		/* Interface to upper layer         */
+	char regname[35];		/* Name used for request_region     */
+} act2000_card;
+
+extern __inline__ void act2000_schedule_tx(act2000_card *card)
+{
+        schedule_work(&card->snd_tq);
+}
+
+extern __inline__ void act2000_schedule_rx(act2000_card *card)
+{
+        schedule_work(&card->rcv_tq);
+}
+
+extern __inline__ void act2000_schedule_poll(act2000_card *card)
+{
+        schedule_work(&card->poll_tq);
+}
+
+extern char *act2000_find_eaz(act2000_card *, char);
+
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* act2000_h */
diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c
new file mode 100644
index 0000000..bc98d77
--- /dev/null
+++ b/drivers/isdn/act2000/act2000_isa.c
@@ -0,0 +1,449 @@
+/* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
+ *
+ * Author       Fritz Elfert
+ * Copyright    by Fritz Elfert      <fritz@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#include "act2000.h"
+#include "act2000_isa.h"
+#include "capi.h"
+
+static act2000_card *irq2card_map[16];
+
+/*
+ * Reset Controller, then try to read the Card's signature.
+ + Return:
+ *   1 = Signature found.
+ *   0 = Signature not found.
+ */
+static int
+act2000_isa_reset(unsigned short portbase)
+{
+        unsigned char reg;
+        int i;
+        int found;
+        int serial = 0;
+
+        found = 0;
+        if ((reg = inb(portbase + ISA_COR)) != 0xff) {
+                outb(reg | ISA_COR_RESET, portbase + ISA_COR);
+                mdelay(10);
+                outb(reg, portbase + ISA_COR);
+                mdelay(10);
+
+                for (i = 0; i < 16; i++) {
+                        if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL)
+                                serial |= 0x10000;
+                        serial >>= 1;
+                }
+                if (serial == ISA_SER_ID)
+                        found++;
+        }
+        return found;
+}
+
+int
+act2000_isa_detect(unsigned short portbase)
+{
+        int ret = 0;
+
+	if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) {
+                ret = act2000_isa_reset(portbase);
+		release_region(portbase, ISA_REGION);
+	}
+        return ret;
+}
+
+static irqreturn_t
+act2000_isa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        act2000_card *card = irq2card_map[irq];
+        u_char istatus;
+
+        if (!card) {
+                printk(KERN_WARNING
+                       "act2000: Spurious interrupt!\n");
+                return IRQ_NONE;
+        }
+        istatus = (inb(ISA_PORT_ISR) & 0x07);
+        if (istatus & ISA_ISR_OUT) {
+                /* RX fifo has data */
+		istatus &= ISA_ISR_OUT_MASK;
+		outb(0, ISA_PORT_SIS);
+		act2000_isa_receive(card);
+		outb(ISA_SIS_INT, ISA_PORT_SIS);
+        }
+        if (istatus & ISA_ISR_ERR) {
+                /* Error Interrupt */
+		istatus &= ISA_ISR_ERR_MASK;
+                printk(KERN_WARNING "act2000: errIRQ\n");
+        }
+	if (istatus)
+		printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus);
+	return IRQ_HANDLED;
+}
+
+static void
+act2000_isa_select_irq(act2000_card * card)
+{
+	unsigned char reg;
+
+	reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR;
+	switch (card->irq) {
+		case 3:
+			reg = ISA_COR_IRQ03;
+			break;
+		case 5:
+			reg = ISA_COR_IRQ05;
+			break;
+		case 7:
+			reg = ISA_COR_IRQ07;
+			break;
+		case 10:
+			reg = ISA_COR_IRQ10;
+			break;
+		case 11:
+			reg = ISA_COR_IRQ11;
+			break;
+		case 12:
+			reg = ISA_COR_IRQ12;
+			break;
+		case 15:
+			reg = ISA_COR_IRQ15;
+			break;
+	}
+	outb(reg, ISA_PORT_COR);
+}
+
+static void
+act2000_isa_enable_irq(act2000_card * card)
+{
+	act2000_isa_select_irq(card);
+	/* Enable READ irq */
+	outb(ISA_SIS_INT, ISA_PORT_SIS);
+}
+
+/*
+ * Install interrupt handler, enable irq on card.
+ * If irq is -1, choose next free irq, else irq is given explicitely.
+ */
+int
+act2000_isa_config_irq(act2000_card * card, short irq)
+{
+        if (card->flags & ACT2000_FLAGS_IVALID) {
+                free_irq(card->irq, NULL);
+                irq2card_map[card->irq] = NULL;
+        }
+        card->flags &= ~ACT2000_FLAGS_IVALID;
+        outb(ISA_COR_IRQOFF, ISA_PORT_COR);
+        if (!irq)
+                return 0;
+
+	if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, NULL)) {
+		card->irq = irq;
+		irq2card_map[card->irq] = card;
+		card->flags |= ACT2000_FLAGS_IVALID;
+                printk(KERN_WARNING
+                       "act2000: Could not request irq %d\n",irq);
+                return -EBUSY;
+        } else {
+		act2000_isa_select_irq(card);
+                /* Disable READ and WRITE irq */
+                outb(0, ISA_PORT_SIS);
+                outb(0, ISA_PORT_SOS);
+        }
+        return 0;
+}
+
+int
+act2000_isa_config_port(act2000_card * card, unsigned short portbase)
+{
+        if (card->flags & ACT2000_FLAGS_PVALID) {
+                release_region(card->port, ISA_REGION);
+                card->flags &= ~ACT2000_FLAGS_PVALID;
+        }
+	if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL)
+		return -EBUSY;
+	else {
+                card->port = portbase;
+                card->flags |= ACT2000_FLAGS_PVALID;
+                return 0;
+        }
+}
+
+/*
+ * Release ressources, used by an adaptor.
+ */
+void
+act2000_isa_release(act2000_card * card)
+{
+        unsigned long flags;
+
+        spin_lock_irqsave(&card->lock, flags);
+        if (card->flags & ACT2000_FLAGS_IVALID) {
+                free_irq(card->irq, NULL);
+                irq2card_map[card->irq] = NULL;
+        }
+        card->flags &= ~ACT2000_FLAGS_IVALID;
+        if (card->flags & ACT2000_FLAGS_PVALID)
+                release_region(card->port, ISA_REGION);
+        card->flags &= ~ACT2000_FLAGS_PVALID;
+        spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static int
+act2000_isa_writeb(act2000_card * card, u_char data)
+{
+        u_char timeout = 40;
+
+        while (timeout) {
+                if (inb(ISA_PORT_SOS) & ISA_SOS_READY) {
+                        outb(data, ISA_PORT_SDO);
+                        return 0;
+                } else {
+                        timeout--;
+                        udelay(10);
+                }
+        }
+        return 1;
+}
+
+static int
+act2000_isa_readb(act2000_card * card, u_char * data)
+{
+        u_char timeout = 40;
+
+        while (timeout) {
+                if (inb(ISA_PORT_SIS) & ISA_SIS_READY) {
+                        *data = inb(ISA_PORT_SDI);
+                        return 0;
+                } else {
+                        timeout--;
+                        udelay(10);
+                }
+        }
+        return 1;
+}
+
+void
+act2000_isa_receive(act2000_card *card)
+{
+	u_char c;
+
+        if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0)
+		return;
+	while (!act2000_isa_readb(card, &c)) {
+		if (card->idat.isa.rcvidx < 8) {
+                        card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c;
+			if (card->idat.isa.rcvidx == 8) {
+				int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr);
+
+				if (valid) {
+					card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len;
+					card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen);
+					if (card->idat.isa.rcvskb == NULL) {
+						card->idat.isa.rcvignore = 1;
+						printk(KERN_WARNING
+						       "act2000_isa_receive: no memory\n");
+						test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
+						return;
+					}
+					memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8);
+					card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8);
+				} else {
+					card->idat.isa.rcvidx = 0;
+					printk(KERN_WARNING
+					       "act2000_isa_receive: Invalid CAPI msg\n");
+					{
+						int i; __u8 *p; __u8 *c; __u8 tmp[30];
+						for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++)
+							c += sprintf(c, "%02x ", *(p++));
+						printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp);
+					}
+				}
+			}
+		} else {
+			if (!card->idat.isa.rcvignore)
+				*card->idat.isa.rcvptr++ = c;
+			if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) {
+				if (!card->idat.isa.rcvignore) {
+					skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb);
+					act2000_schedule_rx(card);
+				}
+				card->idat.isa.rcvidx = 0;
+				card->idat.isa.rcvlen = 8;
+				card->idat.isa.rcvignore = 0;
+				card->idat.isa.rcvskb = NULL;
+				card->idat.isa.rcvptr = card->idat.isa.rcvhdr;
+			}
+		}
+	}
+	if (!(card->flags & ACT2000_FLAGS_IVALID)) {
+		/* In polling mode, schedule myself */
+		if ((card->idat.isa.rcvidx) &&
+		    (card->idat.isa.rcvignore ||
+		     (card->idat.isa.rcvidx < card->idat.isa.rcvlen)))
+			act2000_schedule_poll(card);
+	}
+	test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
+}
+
+void
+act2000_isa_send(act2000_card * card)
+{
+	unsigned long flags;
+	struct sk_buff *skb;
+	actcapi_msg *msg;
+	int l;
+
+        if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0)
+		return;
+	while (1) {
+		spin_lock_irqsave(&card->lock, flags);
+		if (!(card->sbuf)) {
+			if ((card->sbuf = skb_dequeue(&card->sndq))) {
+				card->ack_msg = card->sbuf->data;
+				msg = (actcapi_msg *)card->sbuf->data;
+				if ((msg->hdr.cmd.cmd == 0x86) &&
+				    (msg->hdr.cmd.subcmd == 0)   ) {
+					/* Save flags in message */
+					card->need_b3ack = msg->msg.data_b3_req.flags;
+					msg->msg.data_b3_req.flags = 0;
+				}
+			}
+		}
+		spin_unlock_irqrestore(&card->lock, flags);
+		if (!(card->sbuf)) {
+			/* No more data to send */
+			test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
+			return;
+		}
+		skb = card->sbuf;
+		l = 0;
+		while (skb->len) {
+			if (act2000_isa_writeb(card, *(skb->data))) {
+				/* Fifo is full, but more data to send */
+				test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
+				/* Schedule myself */
+				act2000_schedule_tx(card);
+				return;
+			}
+			skb_pull(skb, 1);
+			l++;
+		}
+		msg = (actcapi_msg *)card->ack_msg;
+		if ((msg->hdr.cmd.cmd == 0x86) &&
+		    (msg->hdr.cmd.subcmd == 0)   ) {
+			/*
+			 * If it's user data, reset data-ptr
+			 * and put skb into ackq.
+			 */
+			skb->data = card->ack_msg;
+			/* Restore flags in message */
+			msg->msg.data_b3_req.flags = card->need_b3ack;
+			skb_queue_tail(&card->ackq, skb);
+		} else
+			dev_kfree_skb(skb);
+		card->sbuf = NULL;
+	}
+}
+
+/*
+ * Get firmware ID, check for 'ISDN' signature.
+ */
+static int
+act2000_isa_getid(act2000_card * card)
+{
+
+        act2000_fwid fid;
+        u_char *p = (u_char *) & fid;
+        int count = 0;
+
+        while (1) {
+                if (count > 510)
+                        return -EPROTO;
+                if (act2000_isa_readb(card, p++))
+                        break;
+                count++;
+        }
+        if (count <= 20) {
+                printk(KERN_WARNING "act2000: No Firmware-ID!\n");
+                return -ETIME;
+        }
+        *p = '\0';
+        fid.revlen[0] = '\0';
+        if (strcmp(fid.isdn, "ISDN")) {
+                printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n");
+                return -EPROTO;
+        }
+	if ((p = strchr(fid.revision, '\n')))
+		*p = '\0';
+        printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision);
+	if (card->flags & ACT2000_FLAGS_IVALID) {
+		printk(KERN_DEBUG "Enabling Interrupts ...\n");
+		act2000_isa_enable_irq(card);
+	}
+        return 0;
+}
+
+/*
+ * Download microcode into card, check Firmware signature.
+ */
+int
+act2000_isa_download(act2000_card * card, act2000_ddef __user * cb)
+{
+        unsigned int length;
+        int l;
+        int c;
+        long timeout;
+        u_char *b;
+        u_char __user *p;
+        u_char *buf;
+        act2000_ddef cblock;
+
+        if (!act2000_isa_reset(card->port))
+                return -ENXIO;
+        msleep_interruptible(500);
+        if (copy_from_user(&cblock, cb, sizeof(cblock)))
+        	return -EFAULT;
+        length = cblock.length;
+        p = cblock.buffer;
+        if (!access_ok(VERIFY_READ, p, length))
+                return -EFAULT;
+        buf = (u_char *) kmalloc(1024, GFP_KERNEL);
+        if (!buf)
+                return -ENOMEM;
+        timeout = 0;
+        while (length) {
+                l = (length > 1024) ? 1024 : length;
+                c = 0;
+                b = buf;
+                if (copy_from_user(buf, p, l)) {
+                        kfree(buf);
+                        return -EFAULT;
+                }
+                while (c < l) {
+                        if (act2000_isa_writeb(card, *b++)) {
+                                printk(KERN_WARNING
+                                       "act2000: loader timed out"
+                                       " len=%d c=%d\n", length, c);
+                                kfree(buf);
+                                return -ETIME;
+                        }
+                        c++;
+                }
+                length -= l;
+                p += l;
+        }
+        kfree(buf);
+        msleep_interruptible(500);
+        return (act2000_isa_getid(card));
+}
diff --git a/drivers/isdn/act2000/act2000_isa.h b/drivers/isdn/act2000/act2000_isa.h
new file mode 100644
index 0000000..ad86c5ed
--- /dev/null
+++ b/drivers/isdn/act2000/act2000_isa.h
@@ -0,0 +1,136 @@
+/* $Id: act2000_isa.h,v 1.4.6.1 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
+ *
+ * Author       Fritz Elfert
+ * Copyright    by Fritz Elfert      <fritz@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#ifndef act2000_isa_h
+#define act2000_isa_h
+
+#define ISA_POLL_LOOP 40        /* Try to read-write before give up */
+
+typedef enum {
+        INT_NO_CHANGE = 0,      /* Do not change the Mask */
+        INT_ON = 1,             /* Set to Enable */
+        INT_OFF = 2,            /* Set to Disable */
+} ISA_INT_T;
+
+/**************************************************************************/
+/*      Configuration Register COR (RW)                                   */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/* Soft Res|  IRQM  |        IRQ Select        |   N/A  |  WAIT  |Proc err */
+/**************************************************************************/
+#define        ISA_COR             0	/* Offset for ISA config register */
+#define        ISA_COR_PERR     0x01	/* Processor Error Enabled        */
+#define        ISA_COR_WS       0x02	/* Insert Wait State if 1         */
+#define        ISA_COR_IRQOFF   0x38	/* No Interrupt                   */
+#define        ISA_COR_IRQ07    0x30	/* IRQ 7 Enable                   */
+#define        ISA_COR_IRQ05    0x28	/* IRQ 5 Enable                   */
+#define        ISA_COR_IRQ03    0x20	/* IRQ 3 Enable                   */
+#define        ISA_COR_IRQ10    0x18	/* IRQ 10 Enable                  */
+#define        ISA_COR_IRQ11    0x10	/* IRQ 11 Enable                  */
+#define        ISA_COR_IRQ12    0x08	/* IRQ 12 Enable                  */
+#define        ISA_COR_IRQ15    0x00	/* IRQ 15 Enable                  */
+#define        ISA_COR_IRQPULSE 0x40	/* 0 = Level 1 = Pulse Interrupt  */
+#define        ISA_COR_RESET    0x80	/* Soft Reset for Transputer      */
+
+/**************************************************************************/
+/*      Interrupt Source Register ISR (RO)                                */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |Err sig |Ser ID  |IN Intr |Out Intr| Error  */
+/**************************************************************************/
+#define        ISA_ISR             1	/* Offset for Interrupt Register  */
+#define        ISA_ISR_ERR      0x01	/* Error Interrupt                */
+#define        ISA_ISR_OUT      0x02	/* Output Interrupt               */
+#define        ISA_ISR_INP      0x04	/* Input Interrupt                */
+#define        ISA_ISR_SERIAL   0x08	/* Read out Serial ID after Reset */
+#define        ISA_ISR_ERRSIG   0x10	/* Error Signal Input             */
+#define        ISA_ISR_ERR_MASK 0xfe    /* Mask Error Interrupt           */
+#define        ISA_ISR_OUT_MASK 0xfd    /* Mask Output Interrupt          */
+#define        ISA_ISR_INP_MASK 0xfb    /* Mask Input Interrupt           */
+
+/* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first)          */
+#define        ISA_SER_ID     0x0201	/* ID for ISA Card                */
+
+/**************************************************************************/
+/*      EEPROM Register EPR (RW)                                          */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */
+/**************************************************************************/
+#define        ISA_EPR             2	/* Offset for this Register       */
+#define        ISA_EPR_OUT      0x01	/* Rome Register Out (RO)         */
+#define        ISA_EPR_IN       0x02	/* Rom Register In (WR)           */
+#define        ISA_EPR_CLK      0x04	/* Rom Clock (WR)                 */
+#define        ISA_EPR_CS       0x08	/* Rom Cip Select (WR)            */
+#define        ISA_EPR_HOLD     0x10	/* Rom Hold Signal (WR)           */
+
+/**************************************************************************/
+/*      EEPROM enable Register EER (unused)                               */
+/**************************************************************************/
+#define        ISA_EER             3	/* Offset for this Register       */
+
+/**************************************************************************/
+/*      SLC Data Input SDI (RO)                                           */
+/**************************************************************************/
+#define        ISA_SDI             4	/* Offset for this Register       */
+
+/**************************************************************************/
+/*      SLC Data Output SDO (WO)                                          */
+/**************************************************************************/
+#define        ISA_SDO             5	/* Offset for this Register       */
+
+/**************************************************************************/
+/*      IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW)      */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |  N/A   |   N/A  |   N/A  |Int Ena |Data Pre */
+/**************************************************************************/
+#define        ISA_SIS             6	/* Offset for this Register       */
+#define        ISA_SIS_READY    0x01	/* If 1 : data is available       */
+#define        ISA_SIS_INT      0x02	/* Enable Interrupt for READ      */
+
+/**************************************************************************/
+/*      IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW)    */
+/**************************************************************************/
+/*    7    |   6    |    5   |   4    |    3   |    2   |    1   |    0   */
+/*   N/A   |  N/A   |   N/A  |  N/A   |   N/A  |   N/A  |Int Ena |Out Rdy */
+/**************************************************************************/
+#define        ISA_SOS             7	/* Offset for this Register       */
+#define        ISA_SOS_READY    0x01	/* If 1 : we can write Data       */
+#define        ISA_SOS_INT      0x02	/* Enable Interrupt for WRITE     */
+
+#define        ISA_REGION          8	/* Number of Registers            */
+
+
+/* Macros for accessing ports */
+#define ISA_PORT_COR (card->port+ISA_COR)
+#define ISA_PORT_ISR (card->port+ISA_ISR)
+#define ISA_PORT_EPR (card->port+ISA_EPR)
+#define ISA_PORT_EER (card->port+ISA_EER)
+#define ISA_PORT_SDI (card->port+ISA_SDI)
+#define ISA_PORT_SDO (card->port+ISA_SDO)
+#define ISA_PORT_SIS (card->port+ISA_SIS)
+#define ISA_PORT_SOS (card->port+ISA_SOS)
+
+/* Prototypes */
+
+extern int act2000_isa_detect(unsigned short portbase);
+extern int act2000_isa_config_irq(act2000_card * card, short irq);
+extern int act2000_isa_config_port(act2000_card * card, unsigned short portbase);
+extern int act2000_isa_download(act2000_card * card, act2000_ddef __user * cb);
+extern void act2000_isa_release(act2000_card * card);
+extern void act2000_isa_receive(act2000_card *card);
+extern void act2000_isa_send(act2000_card *card);
+
+#endif                          /* act2000_isa_h */
diff --git a/drivers/isdn/act2000/capi.c b/drivers/isdn/act2000/capi.c
new file mode 100644
index 0000000..40395f5
--- /dev/null
+++ b/drivers/isdn/act2000/capi.c
@@ -0,0 +1,1177 @@
+/* $Id: capi.c,v 1.9.6.2 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ * CAPI encoder/decoder
+ *
+ * Author       Fritz Elfert
+ * Copyright    by Fritz Elfert      <fritz@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#include "act2000.h"
+#include "capi.h"
+
+static actcapi_msgdsc valid_msg[] = {
+	{{ 0x86, 0x02}, "DATA_B3_IND"},       /* DATA_B3_IND/CONF must be first because of speed!!! */
+	{{ 0x86, 0x01}, "DATA_B3_CONF"},
+	{{ 0x02, 0x01}, "CONNECT_CONF"},
+	{{ 0x02, 0x02}, "CONNECT_IND"},
+	{{ 0x09, 0x01}, "CONNECT_INFO_CONF"},
+	{{ 0x03, 0x02}, "CONNECT_ACTIVE_IND"},
+	{{ 0x04, 0x01}, "DISCONNECT_CONF"},
+	{{ 0x04, 0x02}, "DISCONNECT_IND"},
+	{{ 0x05, 0x01}, "LISTEN_CONF"},
+	{{ 0x06, 0x01}, "GET_PARAMS_CONF"},
+	{{ 0x07, 0x01}, "INFO_CONF"},
+	{{ 0x07, 0x02}, "INFO_IND"},
+	{{ 0x08, 0x01}, "DATA_CONF"},
+	{{ 0x08, 0x02}, "DATA_IND"},
+	{{ 0x40, 0x01}, "SELECT_B2_PROTOCOL_CONF"},
+	{{ 0x80, 0x01}, "SELECT_B3_PROTOCOL_CONF"},
+	{{ 0x81, 0x01}, "LISTEN_B3_CONF"},
+	{{ 0x82, 0x01}, "CONNECT_B3_CONF"},
+	{{ 0x82, 0x02}, "CONNECT_B3_IND"},
+	{{ 0x83, 0x02}, "CONNECT_B3_ACTIVE_IND"},
+	{{ 0x84, 0x01}, "DISCONNECT_B3_CONF"},
+	{{ 0x84, 0x02}, "DISCONNECT_B3_IND"},
+	{{ 0x85, 0x01}, "GET_B3_PARAMS_CONF"},
+	{{ 0x01, 0x01}, "RESET_B3_CONF"},
+	{{ 0x01, 0x02}, "RESET_B3_IND"},
+	/* {{ 0x87, 0x02, "HANDSET_IND"}, not implemented */
+	{{ 0xff, 0x01}, "MANUFACTURER_CONF"},
+	{{ 0xff, 0x02}, "MANUFACTURER_IND"},
+#ifdef DEBUG_MSG
+	/* Requests */
+	{{ 0x01, 0x00}, "RESET_B3_REQ"},
+	{{ 0x02, 0x00}, "CONNECT_REQ"},
+	{{ 0x04, 0x00}, "DISCONNECT_REQ"},
+	{{ 0x05, 0x00}, "LISTEN_REQ"},
+	{{ 0x06, 0x00}, "GET_PARAMS_REQ"},
+	{{ 0x07, 0x00}, "INFO_REQ"},
+	{{ 0x08, 0x00}, "DATA_REQ"},
+	{{ 0x09, 0x00}, "CONNECT_INFO_REQ"},
+	{{ 0x40, 0x00}, "SELECT_B2_PROTOCOL_REQ"},
+	{{ 0x80, 0x00}, "SELECT_B3_PROTOCOL_REQ"},
+	{{ 0x81, 0x00}, "LISTEN_B3_REQ"},
+	{{ 0x82, 0x00}, "CONNECT_B3_REQ"},
+	{{ 0x84, 0x00}, "DISCONNECT_B3_REQ"},
+	{{ 0x85, 0x00}, "GET_B3_PARAMS_REQ"},
+	{{ 0x86, 0x00}, "DATA_B3_REQ"},
+	{{ 0xff, 0x00}, "MANUFACTURER_REQ"},
+	/* Responses */
+	{{ 0x01, 0x03}, "RESET_B3_RESP"},	
+	{{ 0x02, 0x03}, "CONNECT_RESP"},	
+	{{ 0x03, 0x03}, "CONNECT_ACTIVE_RESP"},	
+	{{ 0x04, 0x03}, "DISCONNECT_RESP"},	
+	{{ 0x07, 0x03}, "INFO_RESP"},	
+	{{ 0x08, 0x03}, "DATA_RESP"},	
+	{{ 0x82, 0x03}, "CONNECT_B3_RESP"},	
+	{{ 0x83, 0x03}, "CONNECT_B3_ACTIVE_RESP"},	
+	{{ 0x84, 0x03}, "DISCONNECT_B3_RESP"},
+	{{ 0x86, 0x03}, "DATA_B3_RESP"},
+	{{ 0xff, 0x03}, "MANUFACTURER_RESP"},
+#endif
+	{{ 0x00, 0x00}, NULL},
+};
+#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc))
+#define num_valid_imsg 27 /* MANUFACTURER_IND */
+
+/*
+ * Check for a valid incoming CAPI message.
+ * Return:
+ *   0 = Invalid message
+ *   1 = Valid message, no B-Channel-data
+ *   2 = Valid message, B-Channel-data
+ */
+int
+actcapi_chkhdr(act2000_card * card, actcapi_msghdr *hdr)
+{
+	int i;
+
+	if (hdr->applicationID != 1)
+		return 0;
+	if (hdr->len < 9)
+		return 0;
+	for (i = 0; i < num_valid_imsg; i++)
+		if ((hdr->cmd.cmd == valid_msg[i].cmd.cmd) &&
+		    (hdr->cmd.subcmd == valid_msg[i].cmd.subcmd)) {
+			return (i?1:2);
+		}
+	return 0;
+}
+
+#define ACTCAPI_MKHDR(l, c, s) { \
+	skb = alloc_skb(l + 8, GFP_ATOMIC); \
+	if (skb) { \
+	        m = (actcapi_msg *)skb_put(skb, l + 8); \
+		m->hdr.len = l + 8; \
+		m->hdr.applicationID = 1; \
+	        m->hdr.cmd.cmd = c; \
+	        m->hdr.cmd.subcmd = s; \
+	        m->hdr.msgnum = actcapi_nextsmsg(card); \
+	} else m = NULL;\
+}
+
+#define ACTCAPI_CHKSKB if (!skb) { \
+	printk(KERN_WARNING "actcapi: alloc_skb failed\n"); \
+	return; \
+}
+
+#define ACTCAPI_QUEUE_TX { \
+	actcapi_debug_msg(skb, 1); \
+	skb_queue_tail(&card->sndq, skb); \
+	act2000_schedule_tx(card); \
+}
+
+int
+actcapi_listen_req(act2000_card *card)
+{
+	__u16 eazmask = 0;
+	int i;
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	for (i = 0; i < ACT2000_BCH; i++)
+		eazmask |= card->bch[i].eazmask;
+	ACTCAPI_MKHDR(9, 0x05, 0x00);
+        if (!skb) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.listen_req.controller = 0;
+	m->msg.listen_req.infomask = 0x3f; /* All information */
+	m->msg.listen_req.eazmask = eazmask;
+	m->msg.listen_req.simask = (eazmask)?0x86:0; /* All SI's  */
+	ACTCAPI_QUEUE_TX;
+        return 0;
+}
+
+int
+actcapi_connect_req(act2000_card *card, act2000_chan *chan, char *phone,
+		    char eaz, int si1, int si2)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR((11 + strlen(phone)), 0x02, 0x00);
+	if (!skb) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+		chan->fsm_state = ACT2000_STATE_NULL;
+		return -ENOMEM;
+	}
+	m->msg.connect_req.controller = 0;
+	m->msg.connect_req.bchan = 0x83;
+	m->msg.connect_req.infomask = 0x3f;
+	m->msg.connect_req.si1 = si1;
+	m->msg.connect_req.si2 = si2;
+	m->msg.connect_req.eaz = eaz?eaz:'0';
+	m->msg.connect_req.addr.len = strlen(phone) + 1;
+	m->msg.connect_req.addr.tnp = 0x81;
+	memcpy(m->msg.connect_req.addr.num, phone, strlen(phone));
+	chan->callref = m->hdr.msgnum;
+	ACTCAPI_QUEUE_TX;
+	return 0;
+}
+
+static void
+actcapi_connect_b3_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(17, 0x82, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_b3_req.plci = chan->plci;
+	memset(&m->msg.connect_b3_req.ncpi, 0,
+	       sizeof(m->msg.connect_b3_req.ncpi));
+	m->msg.connect_b3_req.ncpi.len = 13;
+	m->msg.connect_b3_req.ncpi.modulo = 8;
+	ACTCAPI_QUEUE_TX;
+}
+
+/*
+ * Set net type (1TR6) or (EDSS1)
+ */
+int
+actcapi_manufacturer_req_net(act2000_card *card)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(5, 0xff, 0x00);
+        if (!skb) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.manufacturer_req_net.manuf_msg = 0x11;
+	m->msg.manufacturer_req_net.controller = 1;
+	m->msg.manufacturer_req_net.nettype = (card->ptype == ISDN_PTYPE_EURO)?1:0;
+	ACTCAPI_QUEUE_TX;
+	printk(KERN_INFO "act2000 %s: D-channel protocol now %s\n",
+	       card->interface.id, (card->ptype == ISDN_PTYPE_EURO)?"euro":"1tr6");
+	card->interface.features &=
+		~(ISDN_FEATURE_P_UNKNOWN | ISDN_FEATURE_P_EURO | ISDN_FEATURE_P_1TR6);
+	card->interface.features |=
+		((card->ptype == ISDN_PTYPE_EURO)?ISDN_FEATURE_P_EURO:ISDN_FEATURE_P_1TR6);
+        return 0;
+}
+
+/*
+ * Switch V.42 on or off
+ */
+int
+actcapi_manufacturer_req_v42(act2000_card *card, ulong arg)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(8, 0xff, 0x00);
+        if (!skb) {
+
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.manufacturer_req_v42.manuf_msg = 0x10;
+	m->msg.manufacturer_req_v42.controller = 0;
+	m->msg.manufacturer_req_v42.v42control = (arg?1:0);
+	ACTCAPI_QUEUE_TX;
+        return 0;
+}
+
+/*
+ * Set error-handler
+ */
+int
+actcapi_manufacturer_req_errh(act2000_card *card)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(4, 0xff, 0x00);
+        if (!skb) {
+
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return -ENOMEM;
+        }
+	m->msg.manufacturer_req_err.manuf_msg = 0x03;
+	m->msg.manufacturer_req_err.controller = 0;
+	ACTCAPI_QUEUE_TX;
+        return 0;
+}
+
+/*
+ * Set MSN-Mapping.
+ */
+int
+actcapi_manufacturer_req_msn(act2000_card *card)
+{
+	msn_entry *p = card->msn_list;
+	actcapi_msg *m;
+	struct sk_buff *skb;
+	int len;
+
+	while (p) {
+		int i;
+
+		len = strlen(p->msn);
+		for (i = 0; i < 2; i++) {
+			ACTCAPI_MKHDR(6 + len, 0xff, 0x00);
+			if (!skb) {
+				printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+				return -ENOMEM;
+			}
+			m->msg.manufacturer_req_msn.manuf_msg = 0x13 + i;
+			m->msg.manufacturer_req_msn.controller = 0;
+			m->msg.manufacturer_req_msn.msnmap.eaz = p->eaz;
+			m->msg.manufacturer_req_msn.msnmap.len = len;
+			memcpy(m->msg.manufacturer_req_msn.msnmap.msn, p->msn, len);
+			ACTCAPI_QUEUE_TX;
+		}
+		p = p->next;
+	}
+        return 0;
+}
+
+void
+actcapi_select_b2_protocol_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(10, 0x40, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.select_b2_protocol_req.plci = chan->plci;
+	memset(&m->msg.select_b2_protocol_req.dlpd, 0,
+	       sizeof(m->msg.select_b2_protocol_req.dlpd));
+	m->msg.select_b2_protocol_req.dlpd.len = 6;
+	switch (chan->l2prot) {
+		case ISDN_PROTO_L2_TRANS:
+			m->msg.select_b2_protocol_req.protocol = 0x03;
+			m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+			break;
+		case ISDN_PROTO_L2_HDLC:
+			m->msg.select_b2_protocol_req.protocol = 0x02;
+			m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+			break;
+		case ISDN_PROTO_L2_X75I:
+		case ISDN_PROTO_L2_X75UI:
+		case ISDN_PROTO_L2_X75BUI:
+			m->msg.select_b2_protocol_req.protocol = 0x01;
+			m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+			m->msg.select_b2_protocol_req.dlpd.laa = 3;
+			m->msg.select_b2_protocol_req.dlpd.lab = 1;
+			m->msg.select_b2_protocol_req.dlpd.win = 7;
+			m->msg.select_b2_protocol_req.dlpd.modulo = 8;
+			break;
+	}
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_select_b3_protocol_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(17, 0x80, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.select_b3_protocol_req.plci = chan->plci;
+	memset(&m->msg.select_b3_protocol_req.ncpd, 0,
+	       sizeof(m->msg.select_b3_protocol_req.ncpd));
+	switch (chan->l3prot) {
+		case ISDN_PROTO_L3_TRANS:
+			m->msg.select_b3_protocol_req.protocol = 0x04;
+			m->msg.select_b3_protocol_req.ncpd.len = 13;
+			m->msg.select_b3_protocol_req.ncpd.modulo = 8;
+			break;
+	}
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_listen_b3_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x81, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.listen_b3_req.plci = chan->plci;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(3, 0x04, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_req.plci = chan->plci;
+	m->msg.disconnect_req.cause = 0;
+	ACTCAPI_QUEUE_TX;
+}
+
+void
+actcapi_disconnect_b3_req(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(17, 0x84, 0x00);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_b3_req.ncci = chan->ncci;
+	memset(&m->msg.disconnect_b3_req.ncpi, 0,
+	       sizeof(m->msg.disconnect_b3_req.ncpi));
+	m->msg.disconnect_b3_req.ncpi.len = 13;
+	m->msg.disconnect_b3_req.ncpi.modulo = 8;
+	chan->fsm_state = ACT2000_STATE_BHWAIT;
+	ACTCAPI_QUEUE_TX;
+}
+
+void
+actcapi_connect_resp(act2000_card *card, act2000_chan *chan, __u8 cause)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(3, 0x02, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_resp.plci = chan->plci;
+	m->msg.connect_resp.rejectcause = cause;
+	if (cause) {
+		chan->fsm_state = ACT2000_STATE_NULL;
+		chan->plci = 0x8000;
+	} else
+		chan->fsm_state = ACT2000_STATE_IWAIT;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_active_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x03, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_resp.plci = chan->plci;
+	if (chan->fsm_state == ACT2000_STATE_IWAIT)
+		chan->fsm_state = ACT2000_STATE_IBWAIT;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_b3_resp(act2000_card *card, act2000_chan *chan, __u8 rejectcause)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR((rejectcause?3:17), 0x82, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_b3_resp.ncci = chan->ncci;
+	m->msg.connect_b3_resp.rejectcause = rejectcause;
+	if (!rejectcause) {
+		memset(&m->msg.connect_b3_resp.ncpi, 0,
+		       sizeof(m->msg.connect_b3_resp.ncpi));
+		m->msg.connect_b3_resp.ncpi.len = 13;
+		m->msg.connect_b3_resp.ncpi.modulo = 8;
+		chan->fsm_state = ACT2000_STATE_BWAIT;
+	}
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_b3_active_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x83, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.connect_b3_active_resp.ncci = chan->ncci;
+	chan->fsm_state = ACT2000_STATE_ACTIVE;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_info_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x07, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.info_resp.plci = chan->plci;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_b3_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x84, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_b3_resp.ncci = chan->ncci;
+	chan->ncci = 0x8000;
+	chan->queued = 0;
+	ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_resp(act2000_card *card, act2000_chan *chan)
+{
+	actcapi_msg *m;
+	struct sk_buff *skb;
+
+	ACTCAPI_MKHDR(2, 0x04, 0x03);
+	ACTCAPI_CHKSKB;
+	m->msg.disconnect_resp.plci = chan->plci;
+	chan->plci = 0x8000;
+	ACTCAPI_QUEUE_TX;
+}
+
+static int
+new_plci(act2000_card *card, __u16 plci)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if (card->bch[i].plci == 0x8000) {
+			card->bch[i].plci = plci;
+			return i;
+		}
+	return -1;
+}
+
+static int
+find_plci(act2000_card *card, __u16 plci)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if (card->bch[i].plci == plci)
+			return i;
+	return -1;
+}
+
+static int
+find_ncci(act2000_card *card, __u16 ncci)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if (card->bch[i].ncci == ncci)
+			return i;
+	return -1;
+}
+
+static int
+find_dialing(act2000_card *card, __u16 callref)
+{
+	int i;
+	for (i = 0; i < ACT2000_BCH; i++)
+		if ((card->bch[i].callref == callref) &&
+		    (card->bch[i].fsm_state == ACT2000_STATE_OCALL))
+			return i;
+	return -1;
+}
+
+static int
+actcapi_data_b3_ind(act2000_card *card, struct sk_buff *skb) {
+	__u16 plci;
+	__u16 ncci;
+	__u16 controller;
+	__u8  blocknr;
+	int chan;
+	actcapi_msg *msg = (actcapi_msg *)skb->data;
+
+	EVAL_NCCI(msg->msg.data_b3_ind.fakencci, plci, controller, ncci);
+	chan = find_ncci(card, ncci);
+	if (chan < 0)
+		return 0;
+	if (card->bch[chan].fsm_state != ACT2000_STATE_ACTIVE)
+		return 0;
+	if (card->bch[chan].plci != plci)
+		return 0;
+	blocknr = msg->msg.data_b3_ind.blocknr;
+	skb_pull(skb, 19);
+	card->interface.rcvcallb_skb(card->myid, chan, skb);
+        if (!(skb = alloc_skb(11, GFP_ATOMIC))) {
+                printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+                return 1;
+        }
+	msg = (actcapi_msg *)skb_put(skb, 11);
+	msg->hdr.len = 11;
+	msg->hdr.applicationID = 1;
+	msg->hdr.cmd.cmd = 0x86;
+	msg->hdr.cmd.subcmd = 0x03;
+	msg->hdr.msgnum = actcapi_nextsmsg(card);
+	msg->msg.data_b3_resp.ncci = ncci;
+	msg->msg.data_b3_resp.blocknr = blocknr;
+	ACTCAPI_QUEUE_TX;
+	return 1;
+}
+
+/*
+ * Walk over ackq, unlink DATA_B3_REQ from it, if
+ * ncci and blocknr are matching.
+ * Decrement queued-bytes counter.
+ */
+static int
+handle_ack(act2000_card *card, act2000_chan *chan, __u8 blocknr) {
+	unsigned long flags;
+	struct sk_buff *skb;
+	struct sk_buff *tmp;
+	struct actcapi_msg *m;
+	int ret = 0;
+
+	spin_lock_irqsave(&card->lock, flags);
+	skb = skb_peek(&card->ackq);
+	spin_unlock_irqrestore(&card->lock, flags);
+        if (!skb) {
+		printk(KERN_WARNING "act2000: handle_ack nothing found!\n");
+		return 0;
+	}
+        tmp = skb;
+        while (1) {
+                m = (actcapi_msg *)tmp->data;
+                if ((((m->msg.data_b3_req.fakencci >> 8) & 0xff) == chan->ncci) &&
+		    (m->msg.data_b3_req.blocknr == blocknr)) {
+			/* found corresponding DATA_B3_REQ */
+                        skb_unlink(tmp);
+			chan->queued -= m->msg.data_b3_req.datalen;
+			if (m->msg.data_b3_req.flags)
+				ret = m->msg.data_b3_req.datalen;
+			dev_kfree_skb(tmp);
+			if (chan->queued < 0)
+				chan->queued = 0;
+                        return ret;
+                }
+                spin_lock_irqsave(&card->lock, flags);
+                tmp = skb_peek((struct sk_buff_head *)tmp);
+                spin_unlock_irqrestore(&card->lock, flags);
+                if ((tmp == skb) || (tmp == NULL)) {
+			/* reached end of queue */
+			printk(KERN_WARNING "act2000: handle_ack nothing found!\n");
+                        return 0;
+		}
+        }
+}
+
+void
+actcapi_dispatch(act2000_card *card)
+{
+	struct sk_buff *skb;
+	actcapi_msg *msg;
+	__u16 ccmd;
+	int chan;
+	int len;
+	act2000_chan *ctmp;
+	isdn_ctrl cmd;
+	char tmp[170];
+
+	while ((skb = skb_dequeue(&card->rcvq))) {
+		actcapi_debug_msg(skb, 0);
+		msg = (actcapi_msg *)skb->data;
+		ccmd = ((msg->hdr.cmd.cmd << 8) | msg->hdr.cmd.subcmd);
+		switch (ccmd) {
+			case 0x8602:
+				/* DATA_B3_IND */
+				if (actcapi_data_b3_ind(card, skb))
+					return;
+				break;
+			case 0x8601:
+				/* DATA_B3_CONF */
+				chan = find_ncci(card, msg->msg.data_b3_conf.ncci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_ACTIVE)) {
+					if (msg->msg.data_b3_conf.info != 0)
+						printk(KERN_WARNING "act2000: DATA_B3_CONF: %04x\n",
+						       msg->msg.data_b3_conf.info);
+					len = handle_ack(card, &card->bch[chan],
+							 msg->msg.data_b3_conf.blocknr);
+					if (len) {
+						cmd.driver = card->myid;
+						cmd.command = ISDN_STAT_BSENT;
+						cmd.arg = chan;
+						cmd.parm.length = len;
+						card->interface.statcallb(&cmd);
+					}
+				}
+				break;
+			case 0x0201:
+				/* CONNECT_CONF */
+				chan = find_dialing(card, msg->hdr.msgnum);
+				if (chan >= 0) {
+					if (msg->msg.connect_conf.info) {
+						card->bch[chan].fsm_state = ACT2000_STATE_NULL;
+						cmd.driver = card->myid;
+						cmd.command = ISDN_STAT_DHUP;
+						cmd.arg = chan;
+						card->interface.statcallb(&cmd);
+					} else {
+						card->bch[chan].fsm_state = ACT2000_STATE_OWAIT;
+						card->bch[chan].plci = msg->msg.connect_conf.plci;
+					}
+				}
+				break;
+			case 0x0202:
+				/* CONNECT_IND */
+				chan = new_plci(card, msg->msg.connect_ind.plci);
+				if (chan < 0) {
+					ctmp = (act2000_chan *)tmp;
+					ctmp->plci = msg->msg.connect_ind.plci;
+					actcapi_connect_resp(card, ctmp, 0x11); /* All Card-Cannels busy */
+				} else {
+					card->bch[chan].fsm_state = ACT2000_STATE_ICALL;
+					cmd.driver = card->myid;
+					cmd.command = ISDN_STAT_ICALL;
+					cmd.arg = chan;
+					cmd.parm.setup.si1 = msg->msg.connect_ind.si1;
+					cmd.parm.setup.si2 = msg->msg.connect_ind.si2;
+					if (card->ptype == ISDN_PTYPE_EURO)
+						strcpy(cmd.parm.setup.eazmsn,
+						       act2000_find_eaz(card, msg->msg.connect_ind.eaz));
+					else {
+						cmd.parm.setup.eazmsn[0] = msg->msg.connect_ind.eaz;
+						cmd.parm.setup.eazmsn[1] = 0;
+					}
+					memset(cmd.parm.setup.phone, 0, sizeof(cmd.parm.setup.phone));
+					memcpy(cmd.parm.setup.phone, msg->msg.connect_ind.addr.num,
+					       msg->msg.connect_ind.addr.len - 1);
+					cmd.parm.setup.plan = msg->msg.connect_ind.addr.tnp;
+					cmd.parm.setup.screen = 0;
+					if (card->interface.statcallb(&cmd) == 2)
+						actcapi_connect_resp(card, &card->bch[chan], 0x15); /* Reject Call */
+				}
+				break;
+			case 0x0302:
+				/* CONNECT_ACTIVE_IND */
+				chan = find_plci(card, msg->msg.connect_active_ind.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_IWAIT:
+							actcapi_connect_active_resp(card, &card->bch[chan]);
+							break;
+						case ACT2000_STATE_OWAIT:
+							actcapi_connect_active_resp(card, &card->bch[chan]);
+							actcapi_select_b2_protocol_req(card, &card->bch[chan]);
+							break;
+					}
+				break;
+			case 0x8202:
+				/* CONNECT_B3_IND */
+				chan = find_plci(card, msg->msg.connect_b3_ind.plci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_IBWAIT)) {
+					card->bch[chan].ncci = msg->msg.connect_b3_ind.ncci;
+					actcapi_connect_b3_resp(card, &card->bch[chan], 0);
+				} else {
+					ctmp = (act2000_chan *)tmp;
+					ctmp->ncci = msg->msg.connect_b3_ind.ncci;
+					actcapi_connect_b3_resp(card, ctmp, 0x11); /* All Card-Cannels busy */
+				}
+				break;
+			case 0x8302:
+				/* CONNECT_B3_ACTIVE_IND */
+				chan = find_ncci(card, msg->msg.connect_b3_active_ind.ncci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BWAIT)) {
+					actcapi_connect_b3_active_resp(card, &card->bch[chan]);
+					cmd.driver = card->myid;
+					cmd.command = ISDN_STAT_BCONN;
+					cmd.arg = chan;
+					card->interface.statcallb(&cmd);
+				}
+				break;
+			case 0x8402:
+				/* DISCONNECT_B3_IND */
+				chan = find_ncci(card, msg->msg.disconnect_b3_ind.ncci);
+				if (chan >= 0) {
+					ctmp = &card->bch[chan];
+					actcapi_disconnect_b3_resp(card, ctmp);
+					switch (ctmp->fsm_state) {
+						case ACT2000_STATE_ACTIVE:
+							ctmp->fsm_state = ACT2000_STATE_DHWAIT2;
+							cmd.driver = card->myid;
+							cmd.command = ISDN_STAT_BHUP;
+							cmd.arg = chan;
+							card->interface.statcallb(&cmd);
+							break;
+						case ACT2000_STATE_BHWAIT2:
+							actcapi_disconnect_req(card, ctmp);
+							ctmp->fsm_state = ACT2000_STATE_DHWAIT;
+							cmd.driver = card->myid;
+							cmd.command = ISDN_STAT_BHUP;
+							cmd.arg = chan;
+							card->interface.statcallb(&cmd);
+							break;
+					}
+				}
+				break;
+			case 0x0402:
+				/* DISCONNECT_IND */
+				chan = find_plci(card, msg->msg.disconnect_ind.plci);
+				if (chan >= 0) {
+					ctmp = &card->bch[chan];
+					actcapi_disconnect_resp(card, ctmp);
+					ctmp->fsm_state = ACT2000_STATE_NULL;
+					cmd.driver = card->myid;
+					cmd.command = ISDN_STAT_DHUP;
+					cmd.arg = chan;
+					card->interface.statcallb(&cmd);
+				} else {
+					ctmp = (act2000_chan *)tmp;
+					ctmp->plci = msg->msg.disconnect_ind.plci;
+					actcapi_disconnect_resp(card, ctmp);
+				}
+				break;
+			case 0x4001:
+				/* SELECT_B2_PROTOCOL_CONF */
+				chan = find_plci(card, msg->msg.select_b2_protocol_conf.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_ICALL:
+						case ACT2000_STATE_OWAIT:
+							ctmp = &card->bch[chan];
+							if (msg->msg.select_b2_protocol_conf.info == 0)
+								actcapi_select_b3_protocol_req(card, ctmp);
+							else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+							break;
+					}
+				break;
+			case 0x8001:
+				/* SELECT_B3_PROTOCOL_CONF */
+				chan = find_plci(card, msg->msg.select_b3_protocol_conf.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_ICALL:
+						case ACT2000_STATE_OWAIT:
+							ctmp = &card->bch[chan];
+							if (msg->msg.select_b3_protocol_conf.info == 0)
+								actcapi_listen_b3_req(card, ctmp);
+							else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+					}
+				break;
+			case 0x8101:
+				/* LISTEN_B3_CONF */
+				chan = find_plci(card, msg->msg.listen_b3_conf.plci);
+				if (chan >= 0)
+					switch (card->bch[chan].fsm_state) {
+						case ACT2000_STATE_ICALL:
+							ctmp = &card->bch[chan];
+							if (msg->msg.listen_b3_conf.info == 0)
+								actcapi_connect_resp(card, ctmp, 0);
+							else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+							break;
+						case ACT2000_STATE_OWAIT:
+							ctmp = &card->bch[chan];
+							if (msg->msg.listen_b3_conf.info == 0) {
+								actcapi_connect_b3_req(card, ctmp);
+								ctmp->fsm_state = ACT2000_STATE_OBWAIT;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DCONN;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							} else {
+								ctmp->fsm_state = ACT2000_STATE_NULL;
+								cmd.driver = card->myid;
+								cmd.command = ISDN_STAT_DHUP;
+								cmd.arg = chan;
+								card->interface.statcallb(&cmd);
+							}
+							break;
+					}
+				break;
+			case 0x8201:
+				/* CONNECT_B3_CONF */
+				chan = find_plci(card, msg->msg.connect_b3_conf.plci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_OBWAIT)) {
+					ctmp = &card->bch[chan];
+					if (msg->msg.connect_b3_conf.info) {
+						ctmp->fsm_state = ACT2000_STATE_NULL;
+						cmd.driver = card->myid;
+						cmd.command = ISDN_STAT_DHUP;
+						cmd.arg = chan;
+						card->interface.statcallb(&cmd);
+					} else {
+						ctmp->ncci = msg->msg.connect_b3_conf.ncci;
+						ctmp->fsm_state = ACT2000_STATE_BWAIT;
+					}
+				}
+				break;
+			case 0x8401:
+				/* DISCONNECT_B3_CONF */
+				chan = find_ncci(card, msg->msg.disconnect_b3_conf.ncci);
+				if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BHWAIT))
+					card->bch[chan].fsm_state = ACT2000_STATE_BHWAIT2;
+				break;
+			case 0x0702:
+				/* INFO_IND */
+				chan = find_plci(card, msg->msg.info_ind.plci);
+				if (chan >= 0)
+					/* TODO: Eval Charging info / cause */
+					actcapi_info_resp(card, &card->bch[chan]);
+				break;
+			case 0x0401:
+				/* LISTEN_CONF */
+			case 0x0501:
+				/* LISTEN_CONF */
+			case 0xff01:
+				/* MANUFACTURER_CONF */
+				break;
+			case 0xff02:
+				/* MANUFACTURER_IND */
+				if (msg->msg.manuf_msg == 3) {
+					memset(tmp, 0, sizeof(tmp));
+					strncpy(tmp,
+						&msg->msg.manufacturer_ind_err.errstring,
+						msg->hdr.len - 16);
+					if (msg->msg.manufacturer_ind_err.errcode)
+						printk(KERN_WARNING "act2000: %s\n", tmp);
+					else {
+						printk(KERN_DEBUG "act2000: %s\n", tmp);
+						if ((!strncmp(tmp, "INFO: Trace buffer con", 22)) ||
+						    (!strncmp(tmp, "INFO: Compile Date/Tim", 22))) {
+							card->flags |= ACT2000_FLAGS_RUNNING;
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							actcapi_manufacturer_req_net(card);
+							actcapi_manufacturer_req_msn(card);
+							actcapi_listen_req(card);
+							card->interface.statcallb(&cmd);
+						}
+					}
+				}
+				break;
+			default:
+				printk(KERN_WARNING "act2000: UNHANDLED Message %04x\n", ccmd);
+				break;
+		}
+		dev_kfree_skb(skb);
+	}
+}
+
+#ifdef DEBUG_MSG
+static void
+actcapi_debug_caddr(actcapi_addr *addr)
+{
+	char tmp[30];
+
+	printk(KERN_DEBUG " Alen  = %d\n", addr->len);
+	if (addr->len > 0)
+		printk(KERN_DEBUG " Atnp  = 0x%02x\n", addr->tnp);
+	if (addr->len > 1) {
+		memset(tmp, 0, 30);
+		memcpy(tmp, addr->num, addr->len - 1);
+		printk(KERN_DEBUG " Anum  = '%s'\n", tmp);
+	}
+}
+
+static void
+actcapi_debug_ncpi(actcapi_ncpi *ncpi)
+{
+	printk(KERN_DEBUG " ncpi.len = %d\n", ncpi->len);
+	if (ncpi->len >= 2)
+		printk(KERN_DEBUG " ncpi.lic = 0x%04x\n", ncpi->lic);
+	if (ncpi->len >= 4)
+		printk(KERN_DEBUG " ncpi.hic = 0x%04x\n", ncpi->hic);
+	if (ncpi->len >= 6)
+		printk(KERN_DEBUG " ncpi.ltc = 0x%04x\n", ncpi->ltc);
+	if (ncpi->len >= 8)
+		printk(KERN_DEBUG " ncpi.htc = 0x%04x\n", ncpi->htc);
+	if (ncpi->len >= 10)
+		printk(KERN_DEBUG " ncpi.loc = 0x%04x\n", ncpi->loc);
+	if (ncpi->len >= 12)
+		printk(KERN_DEBUG " ncpi.hoc = 0x%04x\n", ncpi->hoc);
+	if (ncpi->len >= 13)
+		printk(KERN_DEBUG " ncpi.mod = %d\n", ncpi->modulo);
+}
+
+static void
+actcapi_debug_dlpd(actcapi_dlpd *dlpd)
+{
+	printk(KERN_DEBUG " dlpd.len = %d\n", dlpd->len);
+	if (dlpd->len >= 2)
+		printk(KERN_DEBUG " dlpd.dlen   = 0x%04x\n", dlpd->dlen);
+	if (dlpd->len >= 3)
+		printk(KERN_DEBUG " dlpd.laa    = 0x%02x\n", dlpd->laa);
+	if (dlpd->len >= 4)
+		printk(KERN_DEBUG " dlpd.lab    = 0x%02x\n", dlpd->lab);
+	if (dlpd->len >= 5)
+		printk(KERN_DEBUG " dlpd.modulo = %d\n", dlpd->modulo);
+	if (dlpd->len >= 6)
+		printk(KERN_DEBUG " dlpd.win    = %d\n", dlpd->win);
+}
+
+#ifdef DEBUG_DUMP_SKB
+static void dump_skb(struct sk_buff *skb) {
+	char tmp[80];
+	char *p = skb->data;
+	char *t = tmp;
+	int i;
+
+	for (i = 0; i < skb->len; i++) {
+		t += sprintf(t, "%02x ", *p++ & 0xff);
+		if ((i & 0x0f) == 8) {
+			printk(KERN_DEBUG "dump: %s\n", tmp);
+			t = tmp;
+		}
+	}
+	if (i & 0x07)
+		printk(KERN_DEBUG "dump: %s\n", tmp);
+}
+#endif
+
+void
+actcapi_debug_msg(struct sk_buff *skb, int direction)
+{
+	actcapi_msg *msg = (actcapi_msg *)skb->data;
+	char *descr;
+	int i;
+	char tmp[170];
+	
+#ifndef DEBUG_DATA_MSG
+	if (msg->hdr.cmd.cmd == 0x86)
+		return;
+#endif
+	descr = "INVALID";
+#ifdef DEBUG_DUMP_SKB
+	dump_skb(skb);
+#endif
+	for (i = 0; i < num_valid_msg; i++)
+		if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) &&
+		    (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) {
+			descr = valid_msg[i].description;
+			break;
+		}
+	printk(KERN_DEBUG "%s %s msg\n", direction?"Outgoing":"Incoming", descr);
+	printk(KERN_DEBUG " ApplID = %d\n", msg->hdr.applicationID);
+	printk(KERN_DEBUG " Len    = %d\n", msg->hdr.len);
+	printk(KERN_DEBUG " MsgNum = 0x%04x\n", msg->hdr.msgnum);
+	printk(KERN_DEBUG " Cmd    = 0x%02x\n", msg->hdr.cmd.cmd);
+	printk(KERN_DEBUG " SubCmd = 0x%02x\n", msg->hdr.cmd.subcmd);
+	switch (i) {
+		case 0:
+			/* DATA B3 IND */
+			printk(KERN_DEBUG " BLOCK = 0x%02x\n",
+			       msg->msg.data_b3_ind.blocknr);
+			break;
+		case 2:
+			/* CONNECT CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.connect_conf.info);
+			break;
+		case 3:
+			/* CONNECT IND */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_ind.plci);
+			printk(KERN_DEBUG " Contr = %d\n",
+			       msg->msg.connect_ind.controller);
+			printk(KERN_DEBUG " SI1   = %d\n",
+			       msg->msg.connect_ind.si1);
+			printk(KERN_DEBUG " SI2   = %d\n",
+			       msg->msg.connect_ind.si2);
+			printk(KERN_DEBUG " EAZ   = '%c'\n",
+			       msg->msg.connect_ind.eaz);
+			actcapi_debug_caddr(&msg->msg.connect_ind.addr);
+			break;
+		case 5:
+			/* CONNECT ACTIVE IND */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_active_ind.plci);
+			actcapi_debug_caddr(&msg->msg.connect_active_ind.addr);
+			break;
+		case 8:
+			/* LISTEN CONF */
+			printk(KERN_DEBUG " Contr = %d\n",
+			       msg->msg.listen_conf.controller);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.listen_conf.info);
+			break;
+		case 11:
+			/* INFO IND */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.info_ind.plci);
+			printk(KERN_DEBUG " Imsk = 0x%04x\n",
+			       msg->msg.info_ind.nr.mask);
+			if (msg->hdr.len > 12) {
+				int l = msg->hdr.len - 12;
+				int j;
+				char *p = tmp;
+				for (j = 0; j < l ; j++)
+					p += sprintf(p, "%02x ", msg->msg.info_ind.el.display[j]);
+				printk(KERN_DEBUG " D = '%s'\n", tmp);
+			}
+			break;
+		case 14:
+			/* SELECT B2 PROTOCOL CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.select_b2_protocol_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.select_b2_protocol_conf.info);
+			break;
+		case 15:
+			/* SELECT B3 PROTOCOL CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.select_b3_protocol_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.select_b3_protocol_conf.info);
+			break;
+		case 16:
+			/* LISTEN B3 CONF */
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.listen_b3_conf.plci);
+			printk(KERN_DEBUG " Info = 0x%04x\n",
+			       msg->msg.listen_b3_conf.info);
+			break;
+		case 18:
+			/* CONNECT B3 IND */
+			printk(KERN_DEBUG " NCCI = 0x%04x\n",
+			       msg->msg.connect_b3_ind.ncci);
+			printk(KERN_DEBUG " PLCI = 0x%04x\n",
+			       msg->msg.connect_b3_ind.plci);
+			actcapi_debug_ncpi(&msg->msg.connect_b3_ind.ncpi);
+			break;
+		case 19:
+			/* CONNECT B3 ACTIVE IND */
+			printk(KERN_DEBUG " NCCI = 0x%04x\n",
+			       msg->msg.connect_b3_active_ind.ncci);
+			actcapi_debug_ncpi(&msg->msg.connect_b3_active_ind.ncpi);
+			break;
+		case 26:
+			/* MANUFACTURER IND */
+			printk(KERN_DEBUG " Mmsg = 0x%02x\n",
+			       msg->msg.manufacturer_ind_err.manuf_msg);
+			switch (msg->msg.manufacturer_ind_err.manuf_msg) {
+				case 3:
+					printk(KERN_DEBUG " Contr = %d\n",
+					       msg->msg.manufacturer_ind_err.controller);
+					printk(KERN_DEBUG " Code = 0x%08x\n",
+					       msg->msg.manufacturer_ind_err.errcode);
+					memset(tmp, 0, sizeof(tmp));
+					strncpy(tmp, &msg->msg.manufacturer_ind_err.errstring,
+						msg->hdr.len - 16);
+					printk(KERN_DEBUG " Emsg = '%s'\n", tmp);
+					break;
+			}
+			break;
+		case 30:
+			/* LISTEN REQ */
+			printk(KERN_DEBUG " Imsk = 0x%08x\n",
+			       msg->msg.listen_req.infomask);
+			printk(KERN_DEBUG " Emsk = 0x%04x\n",
+			       msg->msg.listen_req.eazmask);
+			printk(KERN_DEBUG " Smsk = 0x%04x\n",
+			       msg->msg.listen_req.simask);
+			break;
+		case 35:
+			/* SELECT_B2_PROTOCOL_REQ */
+			printk(KERN_DEBUG " PLCI  = 0x%04x\n",
+			       msg->msg.select_b2_protocol_req.plci);
+			printk(KERN_DEBUG " prot  = 0x%02x\n",
+			       msg->msg.select_b2_protocol_req.protocol);
+			if (msg->hdr.len >= 11)
+				printk(KERN_DEBUG "No dlpd\n");
+			else
+				actcapi_debug_dlpd(&msg->msg.select_b2_protocol_req.dlpd);
+			break;
+		case 44:
+			/* CONNECT RESP */
+			printk(KERN_DEBUG " PLCI  = 0x%04x\n",
+			       msg->msg.connect_resp.plci);
+			printk(KERN_DEBUG " CAUSE = 0x%02x\n",
+			       msg->msg.connect_resp.rejectcause);
+			break;
+		case 45:
+			/* CONNECT ACTIVE RESP */
+			printk(KERN_DEBUG " PLCI  = 0x%04x\n",
+			       msg->msg.connect_active_resp.plci);
+			break;
+	}
+}
+#endif
diff --git a/drivers/isdn/act2000/capi.h b/drivers/isdn/act2000/capi.h
new file mode 100644
index 0000000..04d2bcd
--- /dev/null
+++ b/drivers/isdn/act2000/capi.h
@@ -0,0 +1,366 @@
+/* $Id: capi.h,v 1.6.6.2 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Author       Fritz Elfert
+ * Copyright    by Fritz Elfert      <fritz@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#ifndef CAPI_H
+#define CAPI_H
+
+/* Command-part of a CAPI message */
+typedef struct actcapi_msgcmd {
+	__u8 cmd;
+	__u8 subcmd;
+} actcapi_msgcmd;
+
+/* CAPI message header */
+typedef struct actcapi_msghdr {
+	__u16 len;
+	__u16 applicationID;
+	actcapi_msgcmd cmd;
+	__u16 msgnum;
+} actcapi_msghdr;
+
+/* CAPI message description (for debugging) */
+typedef struct actcapi_msgdsc {
+	actcapi_msgcmd cmd;
+	char *description;
+} actcapi_msgdsc;
+
+/* CAPI Address */
+typedef struct actcapi_addr {
+	__u8 len;                            /* Length of element            */
+	__u8 tnp;                            /* Type/Numbering Plan          */
+	__u8 num[20];                        /* Caller ID                    */
+} actcapi_addr;
+
+/* CAPI INFO element mask */
+typedef  union actcapi_infonr {              /* info number                  */
+	__u16 mask;                          /* info-mask field              */
+	struct bmask {                       /* bit definitions              */
+		unsigned  codes : 3;         /* code set                     */
+		unsigned  rsvd  : 5;         /* reserved                     */
+		unsigned  svind : 1;         /* single, variable length ind. */
+		unsigned  wtype : 7;         /* W-element type               */
+	} bmask;
+} actcapi_infonr;
+
+/* CAPI INFO element */
+typedef union  actcapi_infoel {              /* info element                 */
+	__u8 len;                            /* length of info element       */
+	__u8 display[40];                    /* display contents             */
+	__u8 uuinfo[40];                     /* User-user info field         */
+	struct cause {                       /* Cause information            */
+		unsigned ext2  : 1;          /* extension                    */
+		unsigned cod   : 2;          /* coding standard              */
+		unsigned spare : 1;          /* spare                        */
+		unsigned loc   : 4;          /* location                     */
+		unsigned ext1  : 1;          /* extension                    */
+		unsigned cval  : 7;          /* Cause value                  */
+	} cause;                     
+	struct charge {                      /* Charging information         */
+		__u8 toc;                    /* type of charging info        */
+		__u8 unit[10];               /* charging units               */
+	} charge;
+	__u8 date[20];                       /* date fields                  */
+	__u8 stat;                           /* state of remote party        */
+} actcapi_infoel;
+
+/* Message for EAZ<->MSN Mapping */
+typedef struct actcapi_msn {
+	__u8 eaz;
+	__u8 len;                            /* Length of MSN                */
+	__u8 msn[15] __attribute__ ((packed));
+} actcapi_msn;
+
+typedef struct actcapi_dlpd {
+	__u8 len;                            /* Length of structure          */
+	__u16 dlen __attribute__ ((packed)); /* Data Length                  */
+	__u8 laa __attribute__ ((packed));   /* Link Address A               */
+	__u8 lab;                            /* Link Address B               */
+	__u8 modulo;                         /* Modulo Mode                  */
+	__u8 win;                            /* Window size                  */
+	__u8 xid[100];                       /* XID Information              */
+} actcapi_dlpd;
+
+typedef struct actcapi_ncpd {
+	__u8   len;                          /* Length of structure          */
+	__u16  lic __attribute__ ((packed));
+	__u16  hic __attribute__ ((packed));
+	__u16  ltc __attribute__ ((packed));
+	__u16  htc __attribute__ ((packed));
+	__u16  loc __attribute__ ((packed));
+	__u16  hoc __attribute__ ((packed));
+	__u8   modulo __attribute__ ((packed));
+} actcapi_ncpd;
+#define actcapi_ncpi actcapi_ncpd
+
+/*
+ * Layout of NCCI field in a B3 DATA CAPI message is different from
+ * standard at act2000:
+ *
+ * Bit 0-4  = PLCI
+ * Bit 5-7  = Controller
+ * Bit 8-15 = NCCI
+ */
+#define MAKE_NCCI(plci,contr,ncci) \
+        ((plci & 0x1f) | ((contr & 0x7) << 5) | ((ncci & 0xff) << 8))
+
+#define EVAL_NCCI(fakencci,plci,contr,ncci) { \
+	plci  = fakencci & 0x1f; \
+	contr = (fakencci >> 5) & 0x7; \
+	ncci  = (fakencci >> 8) & 0xff; \
+}
+
+/*
+ * Layout of PLCI field in a B3 DATA CAPI message is different from
+ * standard at act2000:
+ *
+ * Bit 0-4  = PLCI
+ * Bit 5-7  = Controller
+ * Bit 8-15 = reserved (must be 0)
+ */
+#define MAKE_PLCI(plci,contr) \
+        ((plci & 0x1f) | ((contr & 0x7) << 5))
+
+#define EVAL_PLCI(fakeplci,plci,contr) { \
+	plci  = fakeplci & 0x1f; \
+	contr = (fakeplci >> 5) & 0x7; \
+}
+
+typedef struct actcapi_msg {
+	actcapi_msghdr hdr;
+	union {
+		__u16 manuf_msg;
+		struct manufacturer_req_net {
+			__u16 manuf_msg;
+			__u16 controller;
+			__u8  nettype;
+		} manufacturer_req_net;
+		struct manufacturer_req_v42 {
+			__u16 manuf_msg;
+			__u16 controller;
+			__u32 v42control;
+		} manufacturer_req_v42;
+		struct manufacturer_conf_v42 {
+			__u16 manuf_msg;
+			__u16 controller;
+		} manufacturer_conf_v42;
+		struct manufacturer_req_err {
+			__u16 manuf_msg;
+			__u16 controller;
+		} manufacturer_req_err;
+		struct manufacturer_ind_err {
+			__u16 manuf_msg;
+			__u16 controller;
+			__u32 errcode;
+			__u8  errstring; /* actually up to 160 */
+		} manufacturer_ind_err;
+		struct manufacturer_req_msn {
+			__u16 manuf_msg;
+			__u16 controller;
+			actcapi_msn msnmap;
+		} manufacturer_req_msn;
+		/* TODO: TraceInit-req/conf/ind/resp and
+		 *       TraceDump-req/conf/ind/resp
+		 */
+		struct connect_req {
+			__u8  controller;
+			__u8  bchan;
+			__u32 infomask __attribute__ ((packed));
+			__u8  si1;
+			__u8  si2;
+			__u8  eaz;
+			actcapi_addr addr;
+		} connect_req;
+		struct connect_conf {
+			__u16 plci;
+			__u16 info;
+		} connect_conf;
+		struct connect_ind {
+			__u16 plci;
+			__u8  controller;
+			__u8  si1;
+			__u8  si2;
+			__u8  eaz;
+			actcapi_addr addr;
+		} connect_ind;
+		struct connect_resp {
+			__u16 plci;
+			__u8  rejectcause;
+		} connect_resp;
+		struct connect_active_ind {
+			__u16 plci;
+			actcapi_addr addr;
+		} connect_active_ind;
+		struct connect_active_resp {
+			__u16 plci;
+		} connect_active_resp;
+		struct connect_b3_req {
+			__u16 plci;
+			actcapi_ncpi ncpi;
+		} connect_b3_req;
+		struct connect_b3_conf {
+			__u16 plci;
+			__u16 ncci;
+			__u16 info;
+		} connect_b3_conf;
+		struct connect_b3_ind {
+			__u16 ncci;
+			__u16 plci;
+			actcapi_ncpi ncpi;
+		} connect_b3_ind;
+		struct connect_b3_resp {
+			__u16 ncci;
+			__u8  rejectcause;
+			actcapi_ncpi ncpi __attribute__ ((packed));
+		} connect_b3_resp;
+		struct disconnect_req {
+			__u16 plci;
+			__u8  cause;
+		} disconnect_req;
+		struct disconnect_conf {
+			__u16 plci;
+			__u16 info;
+		} disconnect_conf;
+		struct disconnect_ind {
+			__u16 plci;
+			__u16 info;
+		} disconnect_ind;
+		struct disconnect_resp {
+			__u16 plci;
+		} disconnect_resp;
+		struct connect_b3_active_ind {
+			__u16 ncci;
+			actcapi_ncpi ncpi;
+		} connect_b3_active_ind;
+		struct connect_b3_active_resp {
+			__u16 ncci;
+		} connect_b3_active_resp;
+		struct disconnect_b3_req {
+			__u16 ncci;
+			actcapi_ncpi ncpi;
+		} disconnect_b3_req;
+		struct disconnect_b3_conf {
+			__u16 ncci;
+			__u16 info;
+		} disconnect_b3_conf;
+		struct disconnect_b3_ind {
+			__u16 ncci;
+			__u16 info;
+			actcapi_ncpi ncpi;
+		} disconnect_b3_ind;
+		struct disconnect_b3_resp {
+			__u16 ncci;
+		} disconnect_b3_resp;
+		struct info_ind {
+			__u16 plci;
+			actcapi_infonr nr;
+			actcapi_infoel el;
+		} info_ind;
+		struct info_resp {
+			__u16 plci;
+		} info_resp;
+		struct listen_b3_req {
+			__u16 plci;
+		} listen_b3_req;
+		struct listen_b3_conf {
+			__u16 plci;
+			__u16 info;
+		} listen_b3_conf;
+		struct select_b2_protocol_req {
+			__u16 plci;
+			__u8  protocol;
+			actcapi_dlpd dlpd __attribute__ ((packed));
+		} select_b2_protocol_req;
+		struct select_b2_protocol_conf {
+			__u16 plci;
+			__u16 info;
+		} select_b2_protocol_conf;
+		struct select_b3_protocol_req {
+			__u16 plci;
+			__u8  protocol;
+			actcapi_ncpd ncpd __attribute__ ((packed));
+		} select_b3_protocol_req;
+		struct select_b3_protocol_conf {
+			__u16 plci;
+			__u16 info;
+		} select_b3_protocol_conf;
+		struct listen_req {
+			__u8  controller;
+			__u32 infomask __attribute__ ((packed));  
+			__u16 eazmask __attribute__ ((packed));
+			__u16 simask __attribute__ ((packed));
+		} listen_req;
+		struct listen_conf {
+			__u8  controller;
+			__u16 info __attribute__ ((packed));
+		} listen_conf;
+		struct data_b3_req {
+			__u16 fakencci;
+			__u16 datalen;
+			__u32 unused;
+			__u8  blocknr;
+			__u16 flags __attribute__ ((packed));
+		} data_b3_req;
+		struct data_b3_ind {
+			__u16 fakencci;
+			__u16 datalen;
+			__u32 unused;
+			__u8  blocknr;
+			__u16 flags __attribute__ ((packed));
+		} data_b3_ind;
+		struct data_b3_resp {
+			__u16 ncci;
+			__u8  blocknr;
+		} data_b3_resp;
+		struct data_b3_conf {
+			__u16 ncci;
+			__u8  blocknr;
+			__u16 info __attribute__ ((packed));
+		} data_b3_conf;
+	} msg;
+} actcapi_msg;
+
+extern __inline__ unsigned short
+actcapi_nextsmsg(act2000_card *card)
+{
+	unsigned long flags;
+	unsigned short n;
+
+	spin_lock_irqsave(&card->mnlock, flags);
+	n = card->msgnum;
+	card->msgnum++;
+	card->msgnum &= 0x7fff;
+	spin_unlock_irqrestore(&card->mnlock, flags);
+	return n;
+}
+#define DEBUG_MSG
+#undef DEBUG_DATA_MSG
+#undef DEBUG_DUMP_SKB
+
+extern int actcapi_chkhdr(act2000_card *, actcapi_msghdr *);
+extern int actcapi_listen_req(act2000_card *);
+extern int actcapi_manufacturer_req_net(act2000_card *);
+extern int actcapi_manufacturer_req_v42(act2000_card *, ulong);
+extern int actcapi_manufacturer_req_errh(act2000_card *);
+extern int actcapi_manufacturer_req_msn(act2000_card *);
+extern int actcapi_connect_req(act2000_card *, act2000_chan *, char *, char, int, int);
+extern void actcapi_select_b2_protocol_req(act2000_card *, act2000_chan *);
+extern void actcapi_disconnect_b3_req(act2000_card *, act2000_chan *);
+extern void actcapi_connect_resp(act2000_card *, act2000_chan *, __u8);
+extern void actcapi_dispatch(act2000_card *);
+#ifdef DEBUG_MSG
+extern void actcapi_debug_msg(struct sk_buff *skb, int);
+#else
+#define actcapi_debug_msg(skb, len)
+#endif
+#endif
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
new file mode 100644
index 0000000..d89dcde
--- /dev/null
+++ b/drivers/isdn/act2000/module.c
@@ -0,0 +1,808 @@
+/* $Id: module.c,v 1.14.6.4 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Author       Fritz Elfert
+ * Copyright    by Fritz Elfert      <fritz@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#include "act2000.h"
+#include "act2000_isa.h"
+#include "capi.h"
+#include <linux/module.h>
+#include <linux/init.h>
+
+static unsigned short act2000_isa_ports[] =
+{
+        0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380,
+        0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60,
+};
+#define ISA_NRPORTS (sizeof(act2000_isa_ports)/sizeof(unsigned short))
+
+static act2000_card *cards = (act2000_card *) NULL;
+
+/* Parameters to be set by insmod */
+static int   act_bus  =  0;
+static int   act_port = -1;  /* -1 = Autoprobe  */
+static int   act_irq  = -1;
+static char *act_id   = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+MODULE_DESCRIPTION(       "ISDN4Linux: Driver for IBM Active 2000 ISDN card");
+MODULE_AUTHOR(            "Fritz Elfert");
+MODULE_LICENSE(           "GPL");
+MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA");
+MODULE_PARM_DESC(membase, "Base port address of first card");
+MODULE_PARM_DESC(act_irq, "IRQ of first card");
+MODULE_PARM_DESC(act_id,  "ID-String of first card");
+module_param(act_bus,  int, 0);
+module_param(act_port, int, 0);
+module_param(act_irq, int, 0);
+module_param(act_id, charp, 0);
+
+static int act2000_addcard(int, int, int, char *);
+
+static act2000_chan *
+find_channel(act2000_card *card, int channel)
+{
+	if ((channel >= 0) && (channel < ACT2000_BCH))
+        	return &(card->bch[channel]);
+	printk(KERN_WARNING "act2000: Invalid channel %d\n", channel);
+	return NULL;
+}
+
+/*
+ * Free MSN list
+ */
+static void
+act2000_clear_msn(act2000_card *card)
+{
+	struct msn_entry *p = card->msn_list;
+	struct msn_entry *q;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	card->msn_list = NULL;
+	spin_unlock_irqrestore(&card->lock, flags);
+	while (p) {
+		q  = p->next;
+		kfree(p);
+		p = q;
+	}
+}
+
+/*
+ * Find an MSN entry in the list.
+ * If ia5 != 0, return IA5-encoded EAZ, else
+ * return a bitmask with corresponding bit set.
+ */
+static __u16
+act2000_find_msn(act2000_card *card, char *msn, int ia5)
+{
+        struct msn_entry *p = card->msn_list;
+	__u8 eaz = '0';
+
+	while (p) {
+		if (!strcmp(p->msn, msn)) {
+			eaz = p->eaz;
+			break;
+		}
+		p = p->next;
+	}
+	if (!ia5)
+		return (1 << (eaz - '0'));
+	else
+		return eaz;
+}
+
+/*
+ * Find an EAZ entry in the list.
+ * return a string with corresponding msn.
+ */
+char *
+act2000_find_eaz(act2000_card *card, char eaz)
+{
+        struct msn_entry *p = card->msn_list;
+
+	while (p) {
+		if (p->eaz == eaz)
+			return(p->msn);
+		p = p->next;
+	}
+	return("\0");
+}
+
+/*
+ * Add or delete an MSN to the MSN list
+ *
+ * First character of msneaz is EAZ, rest is MSN.
+ * If length of eazmsn is 1, delete that entry.
+ */
+static int
+act2000_set_msn(act2000_card *card, char *eazmsn)
+{
+        struct msn_entry *p = card->msn_list;
+        struct msn_entry *q = NULL;
+	unsigned long flags;
+	int i;
+	
+	if (!strlen(eazmsn))
+		return 0;
+	if (strlen(eazmsn) > 16)
+		return -EINVAL;
+	for (i = 0; i < strlen(eazmsn); i++)
+		if (!isdigit(eazmsn[i]))
+			return -EINVAL;
+        if (strlen(eazmsn) == 1) {
+		/* Delete a single MSN */
+		while (p) {
+			if (p->eaz == eazmsn[0]) {
+				spin_lock_irqsave(&card->lock, flags);
+				if (q)
+					q->next = p->next;
+				else
+					card->msn_list = p->next;
+				spin_unlock_irqrestore(&card->lock, flags);
+				kfree(p);
+				printk(KERN_DEBUG
+				       "Mapping for EAZ %c deleted\n",
+				       eazmsn[0]);
+				return 0;
+			}
+			q = p;
+			p = p->next;
+		}
+		return 0;
+        }
+	/* Add a single MSN */
+	while (p) {
+		/* Found in list, replace MSN */
+		if (p->eaz == eazmsn[0]) {
+			spin_lock_irqsave(&card->lock, flags);
+			strcpy(p->msn, &eazmsn[1]);
+			spin_unlock_irqrestore(&card->lock, flags);
+			printk(KERN_DEBUG
+			       "Mapping for EAZ %c changed to %s\n",
+			       eazmsn[0],
+			       &eazmsn[1]);
+			return 0;
+		}
+		p = p->next;
+	}
+	/* Not found in list, add new entry */
+	p = kmalloc(sizeof(msn_entry), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	p->eaz = eazmsn[0];
+	strcpy(p->msn, &eazmsn[1]);
+	p->next = card->msn_list;
+	spin_lock_irqsave(&card->lock, flags);
+	card->msn_list = p;
+	spin_unlock_irqrestore(&card->lock, flags);
+	printk(KERN_DEBUG
+	       "Mapping %c -> %s added\n",
+	       eazmsn[0],
+	       &eazmsn[1]);
+	return 0;
+}
+
+static void
+act2000_transmit(struct act2000_card *card)
+{
+	switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			act2000_isa_send(card);
+			break;
+		case ACT2000_BUS_PCMCIA:
+		case ACT2000_BUS_MCA:
+		default:
+			printk(KERN_WARNING
+			       "act2000_transmit: Illegal bustype %d\n", card->bus);
+	}
+}
+
+static void
+act2000_receive(struct act2000_card *card)
+{
+	switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			act2000_isa_receive(card);
+			break;
+		case ACT2000_BUS_PCMCIA:
+		case ACT2000_BUS_MCA:
+		default:
+			printk(KERN_WARNING
+			       "act2000_receive: Illegal bustype %d\n", card->bus);
+	}
+}
+
+static void
+act2000_poll(unsigned long data)
+{
+	act2000_card * card = (act2000_card *)data;
+	unsigned long flags;
+
+	act2000_receive(card);
+	spin_lock_irqsave(&card->lock, flags);
+	mod_timer(&card->ptimer, jiffies+3);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static int
+act2000_command(act2000_card * card, isdn_ctrl * c)
+{
+        ulong a;
+        act2000_chan *chan;
+	act2000_cdef cdef;
+	isdn_ctrl cmd;
+	char tmp[17];
+	int ret;
+	unsigned long flags;
+	void __user *arg;
+ 
+        switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			arg = (void __user *)a;
+			switch (c->arg) {
+				case ACT2000_IOCTL_LOADBOOT:
+					switch (card->bus) {
+						case ACT2000_BUS_ISA:
+							ret = act2000_isa_download(card,
+									   arg);
+							if (!ret) {
+								card->flags |= ACT2000_FLAGS_LOADED;
+								if (!(card->flags & ACT2000_FLAGS_IVALID)) {
+									card->ptimer.expires = jiffies + 3;
+									card->ptimer.function = act2000_poll;
+									card->ptimer.data = (unsigned long)card;
+									add_timer(&card->ptimer);
+								}
+								actcapi_manufacturer_req_errh(card);
+							}
+							break;
+						default:
+							printk(KERN_WARNING
+							       "act2000: Illegal BUS type %d\n",
+							       card->bus);
+							ret = -EIO;
+					}
+					return ret;
+				case ACT2000_IOCTL_SETPROTO:
+					card->ptype = a?ISDN_PTYPE_EURO:ISDN_PTYPE_1TR6;
+					if (!(card->flags & ACT2000_FLAGS_RUNNING))
+						return 0;
+					actcapi_manufacturer_req_net(card);
+					return 0;
+				case ACT2000_IOCTL_SETMSN:
+					if (copy_from_user(tmp, arg,
+							   sizeof(tmp)))
+						return -EFAULT;
+					if ((ret = act2000_set_msn(card, tmp)))
+						return ret;
+					if (card->flags & ACT2000_FLAGS_RUNNING)
+						return(actcapi_manufacturer_req_msn(card));
+					return 0;
+				case ACT2000_IOCTL_ADDCARD:
+					if (copy_from_user(&cdef, arg,
+							   sizeof(cdef)))
+						return -EFAULT;
+					if (act2000_addcard(cdef.bus, cdef.port, cdef.irq, cdef.id))
+						return -EIO;
+					return 0;
+				case ACT2000_IOCTL_TEST:
+					if (!(card->flags & ACT2000_FLAGS_RUNNING))
+						return -ENODEV;
+					return 0;
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			spin_lock_irqsave(&card->lock, flags);
+			if (chan->fsm_state != ACT2000_STATE_NULL) {
+				spin_unlock_irqrestore(&card->lock, flags);
+				printk(KERN_WARNING "Dial on channel with state %d\n",
+					chan->fsm_state);
+				return -EBUSY;
+			}
+			if (card->ptype == ISDN_PTYPE_EURO)
+				tmp[0] = act2000_find_msn(card, c->parm.setup.eazmsn, 1);
+			else
+				tmp[0] = c->parm.setup.eazmsn[0];
+			chan->fsm_state = ACT2000_STATE_OCALL;
+			chan->callref = 0xffff;
+			spin_unlock_irqrestore(&card->lock, flags);
+			ret = actcapi_connect_req(card, chan, c->parm.setup.phone,
+						  tmp[0], c->parm.setup.si1,
+						  c->parm.setup.si2);
+			if (ret) {
+				cmd.driver = card->myid;
+				cmd.command = ISDN_STAT_DHUP;
+				cmd.arg &= 0x0f;
+				card->interface.statcallb(&cmd);
+			}
+			return ret;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			if (chan->fsm_state == ACT2000_STATE_ICALL)
+				actcapi_select_b2_protocol_req(card, chan);
+			return 0;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			return 0;
+		case ISDN_CMD_HANGUP:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			switch (chan->fsm_state) {
+				case ACT2000_STATE_ICALL:
+				case ACT2000_STATE_BSETUP:
+					actcapi_connect_resp(card, chan, 0x15);
+					break;
+				case ACT2000_STATE_ACTIVE:
+					actcapi_disconnect_b3_req(card, chan);
+					break;
+			}
+			return 0;
+		case ISDN_CMD_SETEAZ:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			if (strlen(c->parm.num)) {
+				if (card->ptype == ISDN_PTYPE_EURO) {
+					chan->eazmask = act2000_find_msn(card, c->parm.num, 0);
+				}
+				if (card->ptype == ISDN_PTYPE_1TR6) {
+					int i;
+					chan->eazmask = 0;
+					for (i = 0; i < strlen(c->parm.num); i++)
+						if (isdigit(c->parm.num[i]))
+							chan->eazmask |= (1 << (c->parm.num[i] - '0'));
+				}
+			} else
+				chan->eazmask = 0x3ff;
+			actcapi_listen_req(card);
+			return 0;
+		case ISDN_CMD_CLREAZ:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			chan->eazmask = 0;
+			actcapi_listen_req(card);
+			return 0;
+		case ISDN_CMD_SETL2:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			chan->l2prot = (c->arg >> 8);
+			return 0;
+		case ISDN_CMD_SETL3:
+			if (!card->flags & ACT2000_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) {
+				printk(KERN_WARNING "L3 protocol unknown\n");
+				return -1;
+			}
+			if (!(chan = find_channel(card, c->arg & 0x0f)))
+				break;
+			chan->l3prot = (c->arg >> 8);
+			return 0;
+        }
+	
+        return -EINVAL;
+}
+
+static int
+act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb)
+{
+        struct sk_buff *xmit_skb;
+        int len;
+        act2000_chan *chan;
+	actcapi_msg *msg;
+
+        if (!(chan = find_channel(card, channel)))
+		return -1;
+        if (chan->fsm_state != ACT2000_STATE_ACTIVE)
+                return -1;
+        len = skb->len;
+        if ((chan->queued + len) >= ACT2000_MAX_QUEUED)
+                return 0;
+	if (!len)
+		return 0;
+	if (skb_headroom(skb) < 19) {
+		printk(KERN_WARNING "act2000_sendbuf: Headroom only %d\n",
+		       skb_headroom(skb));
+		xmit_skb = alloc_skb(len + 19, GFP_ATOMIC);
+		if (!xmit_skb) {
+			printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
+			return 0;
+		}
+		skb_reserve(xmit_skb, 19);
+		memcpy(skb_put(xmit_skb, len), skb->data, len);
+	} else {
+		xmit_skb = skb_clone(skb, GFP_ATOMIC);
+		if (!xmit_skb) {
+			printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
+			return 0;
+		}
+	}
+	dev_kfree_skb(skb);
+	msg = (actcapi_msg *)skb_push(xmit_skb, 19);
+	msg->hdr.len = 19 + len;
+	msg->hdr.applicationID = 1;
+	msg->hdr.cmd.cmd = 0x86;
+	msg->hdr.cmd.subcmd = 0x00;
+	msg->hdr.msgnum = actcapi_nextsmsg(card);
+	msg->msg.data_b3_req.datalen = len;
+	msg->msg.data_b3_req.blocknr = (msg->hdr.msgnum & 0xff);
+	msg->msg.data_b3_req.fakencci = MAKE_NCCI(chan->plci, 0, chan->ncci);
+	msg->msg.data_b3_req.flags = ack; /* Will be set to 0 on actual sending */
+	actcapi_debug_msg(xmit_skb, 1);
+        chan->queued += len;
+	skb_queue_tail(&card->sndq, xmit_skb);
+	act2000_schedule_tx(card);
+        return len;
+}
+
+
+/* Read the Status-replies from the Interface */
+static int
+act2000_readstatus(u_char __user * buf, int len, act2000_card * card)
+{
+        int count;
+        u_char __user *p;
+
+        for (p = buf, count = 0; count < len; p++, count++) {
+                if (card->status_buf_read == card->status_buf_write)
+                        return count;
+		put_user(*card->status_buf_read++, p);
+                if (card->status_buf_read > card->status_buf_end)
+                        card->status_buf_read = card->status_buf;
+        }
+        return count;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline act2000_card *
+act2000_findcard(int driverid)
+{
+        act2000_card *p = cards;
+
+        while (p) {
+                if (p->myid == driverid)
+                        return p;
+                p = p->next;
+        }
+        return (act2000_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+        act2000_card *card = act2000_findcard(c->driver);
+
+        if (card)
+                return (act2000_command(card, c));
+        printk(KERN_ERR
+             "act2000: if_command %d called with invalid driverId %d!\n",
+               c->command, c->driver);
+        return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char __user *buf, int len, int id, int channel)
+{
+        act2000_card *card = act2000_findcard(id);
+
+        if (card) {
+                if (!card->flags & ACT2000_FLAGS_RUNNING)
+                        return -ENODEV;
+                return (len);
+        }
+        printk(KERN_ERR
+               "act2000: if_writecmd called with invalid driverId!\n");
+        return -ENODEV;
+}
+
+static int
+if_readstatus(u_char __user * buf, int len, int id, int channel)
+{
+        act2000_card *card = act2000_findcard(id);
+	
+        if (card) {
+                if (!card->flags & ACT2000_FLAGS_RUNNING)
+                        return -ENODEV;
+                return (act2000_readstatus(buf, len, card));
+        }
+        printk(KERN_ERR
+               "act2000: if_readstatus called with invalid driverId!\n");
+        return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+        act2000_card *card = act2000_findcard(id);
+	
+        if (card) {
+                if (!card->flags & ACT2000_FLAGS_RUNNING)
+                        return -ENODEV;
+		return (act2000_sendbuf(card, channel, ack, skb));
+        }
+        printk(KERN_ERR
+               "act2000: if_sendbuf called with invalid driverId!\n");
+        return -ENODEV;
+}
+
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list.
+ */
+static void
+act2000_alloccard(int bus, int port, int irq, char *id)
+{
+	int i;
+        act2000_card *card;
+        if (!(card = (act2000_card *) kmalloc(sizeof(act2000_card), GFP_KERNEL))) {
+                printk(KERN_WARNING
+		       "act2000: (%s) Could not allocate card-struct.\n", id);
+                return;
+        }
+        memset((char *) card, 0, sizeof(act2000_card));
+        spin_lock_init(&card->lock);
+        spin_lock_init(&card->mnlock);
+	skb_queue_head_init(&card->sndq);
+	skb_queue_head_init(&card->rcvq);
+	skb_queue_head_init(&card->ackq);
+	INIT_WORK(&card->snd_tq, (void *) (void *) act2000_transmit, card);
+	INIT_WORK(&card->rcv_tq, (void *) (void *) actcapi_dispatch, card);
+	INIT_WORK(&card->poll_tq, (void *) (void *) act2000_receive, card);
+	init_timer(&card->ptimer);
+	card->interface.owner = THIS_MODULE;
+        card->interface.channels = ACT2000_BCH;
+        card->interface.maxbufsize = 4000;
+        card->interface.command = if_command;
+        card->interface.writebuf_skb = if_sendbuf;
+        card->interface.writecmd = if_writecmd;
+        card->interface.readstat = if_readstatus;
+        card->interface.features =
+		ISDN_FEATURE_L2_X75I |
+		ISDN_FEATURE_L2_HDLC |
+		ISDN_FEATURE_L3_TRANS |
+		ISDN_FEATURE_P_UNKNOWN;
+        card->interface.hl_hdrlen = 20;
+        card->ptype = ISDN_PTYPE_EURO;
+        strlcpy(card->interface.id, id, sizeof(card->interface.id));
+        for (i=0; i<ACT2000_BCH; i++) {
+                card->bch[i].plci = 0x8000;
+                card->bch[i].ncci = 0x8000;
+                card->bch[i].l2prot = ISDN_PROTO_L2_X75I;
+                card->bch[i].l3prot = ISDN_PROTO_L3_TRANS;
+        }
+        card->myid = -1;
+        card->bus = bus;
+        card->port = port;
+        card->irq = irq;
+        card->next = cards;
+        cards = card;
+}
+
+/*
+ * register card at linklevel
+ */
+static int
+act2000_registercard(act2000_card * card)
+{
+        switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			break;
+		case ACT2000_BUS_MCA:
+		case ACT2000_BUS_PCMCIA:
+		default:
+			printk(KERN_WARNING
+			       "act2000: Illegal BUS type %d\n",
+			       card->bus);
+			return -1;
+        }
+        if (!register_isdn(&card->interface)) {
+                printk(KERN_WARNING
+                       "act2000: Unable to register %s\n",
+                       card->interface.id);
+                return -1;
+        }
+        card->myid = card->interface.channels;
+        sprintf(card->regname, "act2000-isdn (%s)", card->interface.id);
+        return 0;
+}
+
+static void
+unregister_card(act2000_card * card)
+{
+        isdn_ctrl cmd;
+
+        cmd.command = ISDN_STAT_UNLOAD;
+        cmd.driver = card->myid;
+        card->interface.statcallb(&cmd);
+        switch (card->bus) {
+		case ACT2000_BUS_ISA:
+			act2000_isa_release(card);
+			break;
+		case ACT2000_BUS_MCA:
+		case ACT2000_BUS_PCMCIA:
+		default:
+			printk(KERN_WARNING
+			       "act2000: Invalid BUS type %d\n",
+			       card->bus);
+			break;
+        }
+}
+
+static int
+act2000_addcard(int bus, int port, int irq, char *id)
+{
+	act2000_card *p;
+	act2000_card *q = NULL;
+	int initialized;
+	int added = 0;
+	int failed = 0;
+	int i;
+
+	if (!bus)
+		bus = ACT2000_BUS_ISA;
+	if (port != -1) {
+		/* Port defined, do fixed setup */
+		act2000_alloccard(bus, port, irq, id);
+	} else {
+		/* No port defined, perform autoprobing.
+		 * This may result in more than one card detected.
+		 */
+		switch (bus) {
+			case ACT2000_BUS_ISA:
+				for (i = 0; i < ISA_NRPORTS; i++)
+					if (act2000_isa_detect(act2000_isa_ports[i])) {
+						printk(KERN_INFO
+						       "act2000: Detected ISA card at port 0x%x\n",
+						       act2000_isa_ports[i]);
+						act2000_alloccard(bus, act2000_isa_ports[i], irq, id);
+					}
+				break;
+			case ACT2000_BUS_MCA:
+			case ACT2000_BUS_PCMCIA:
+			default:
+				printk(KERN_WARNING
+				       "act2000: addcard: Invalid BUS type %d\n",
+				       bus);
+		}
+	}
+	if (!cards)
+		return 1;
+        p = cards;
+        while (p) {
+		initialized = 0;
+		if (!p->interface.statcallb) {
+			/* Not yet registered.
+			 * Try to register and activate it.
+			 */
+			added++;
+			switch (p->bus) {
+				case ACT2000_BUS_ISA:
+					if (act2000_isa_detect(p->port)) {
+						if (act2000_registercard(p))
+							break;
+						if (act2000_isa_config_port(p, p->port)) {
+							printk(KERN_WARNING
+							       "act2000: Could not request port 0x%04x\n",
+							       p->port);
+							unregister_card(p);
+							p->interface.statcallb = NULL;
+							break;
+						}
+						if (act2000_isa_config_irq(p, p->irq)) {
+							printk(KERN_INFO
+							       "act2000: No IRQ available, fallback to polling\n");
+							/* Fall back to polled operation */
+							p->irq = 0;
+						}
+						printk(KERN_INFO
+						       "act2000: ISA"
+						       "-type card at port "
+						       "0x%04x ",
+						       p->port);
+						if (p->irq)
+							printk("irq %d\n", p->irq);
+						else
+							printk("polled\n");
+						initialized = 1;
+					}
+					break;
+				case ACT2000_BUS_MCA:
+				case ACT2000_BUS_PCMCIA:
+				default:
+					printk(KERN_WARNING
+					       "act2000: addcard: Invalid BUS type %d\n",
+					       p->bus);
+			}
+		} else
+			/* Card already initialized */
+			initialized = 1;
+                if (initialized) {
+			/* Init OK, next card ... */
+                        q = p;
+                        p = p->next;
+                } else {
+                        /* Init failed, remove card from list, free memory */
+                        printk(KERN_WARNING
+                               "act2000: Initialization of %s failed\n",
+                               p->interface.id);
+                        if (q) {
+                                q->next = p->next;
+                                kfree(p);
+                                p = q->next;
+                        } else {
+                                cards = p->next;
+                                kfree(p);
+                                p = cards;
+                        }
+			failed++;
+                }
+	}
+        return (added - failed);
+}
+
+#define DRIVERNAME "IBM Active 2000 ISDN driver"
+
+static int __init act2000_init(void)
+{
+        printk(KERN_INFO "%s\n", DRIVERNAME);
+        if (!cards)
+		act2000_addcard(act_bus, act_port, act_irq, act_id);
+        if (!cards)
+                printk(KERN_INFO "act2000: No cards defined yet\n");
+        return 0;
+}
+
+static void __exit act2000_exit(void)
+{
+        act2000_card *card = cards;
+        act2000_card *last;
+        while (card) {
+                unregister_card(card);
+		del_timer(&card->ptimer);
+                card = card->next;
+        }
+        card = cards;
+        while (card) {
+                last = card;
+                card = card->next;
+		act2000_clear_msn(last);
+                kfree(last);
+        }
+        printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
+}
+
+module_init(act2000_init);
+module_exit(act2000_exit);
diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig
new file mode 100644
index 0000000..8b6c9a4
--- /dev/null
+++ b/drivers/isdn/capi/Kconfig
@@ -0,0 +1,53 @@
+#
+# Config.in for the CAPI subsystem
+#
+config ISDN_DRV_AVMB1_VERBOSE_REASON
+	bool "Verbose reason code reporting (kernel size +=7K)"
+	depends on ISDN_CAPI
+	help
+	  If you say Y here, the AVM B1 driver will give verbose reasons for
+	  disconnecting. This will increase the size of the kernel by 7 KB. If
+	  unsure, say Y.
+
+config ISDN_CAPI_MIDDLEWARE
+	bool "CAPI2.0 Middleware support (EXPERIMENTAL)"
+	depends on ISDN_CAPI && EXPERIMENTAL
+	help
+	  This option will enhance the capabilities of the /dev/capi20
+	  interface.  It will provide a means of moving a data connection,
+	  established via the usual /dev/capi20 interface to a special tty
+	  device.  If you want to use pppd with pppdcapiplugin to dial up to
+	  your ISP, say Y here.
+
+config ISDN_CAPI_CAPI20
+	tristate "CAPI2.0 /dev/capi support"
+	depends on ISDN_CAPI
+	help
+	  This option will provide the CAPI 2.0 interface to userspace
+	  applications via /dev/capi20. Applications should use the
+	  standardized libcapi20 to access this functionality.  You should say
+	  Y/M here.
+
+config ISDN_CAPI_CAPIFS_BOOL
+	bool "CAPI2.0 filesystem support"
+	depends on ISDN_CAPI_MIDDLEWARE && ISDN_CAPI_CAPI20
+
+config ISDN_CAPI_CAPIFS
+	tristate
+	depends on ISDN_CAPI_CAPIFS_BOOL
+	default ISDN_CAPI_CAPI20
+	help
+	  This option provides a special file system, similar to /dev/pts with
+	  device nodes for the special ttys established by using the
+	  middleware extension above. If you want to use pppd with
+	  pppdcapiplugin to dial up to your ISP, say Y here.
+
+config ISDN_CAPI_CAPIDRV
+	tristate "CAPI2.0 capidrv interface support"
+	depends on ISDN_CAPI && ISDN_I4L
+	help
+	  This option provides the glue code to hook up CAPI driven cards to
+	  the legacy isdn4linux link layer.  If you have a card which is
+	  supported by a CAPI driver, but still want to use old features like
+	  ippp interfaces or ttyI emulation, say Y/M here.
+
diff --git a/drivers/isdn/capi/Makefile b/drivers/isdn/capi/Makefile
new file mode 100644
index 0000000..57123e3
--- /dev/null
+++ b/drivers/isdn/capi/Makefile
@@ -0,0 +1,15 @@
+# Makefile for the CAPI subsystem.
+
+# Ordering constraints: kernelcapi.o first
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_CAPI)			+= kernelcapi.o
+obj-$(CONFIG_ISDN_CAPI_CAPI20)		+= capi.o 
+obj-$(CONFIG_ISDN_CAPI_CAPIDRV)		+= capidrv.o
+obj-$(CONFIG_ISDN_CAPI_CAPIFS)		+= capifs.o
+
+# Multipart objects.
+
+kernelcapi-y				:= kcapi.o capiutil.o capilib.o
+kernelcapi-$(CONFIG_PROC_FS)		+= kcapi_proc.o
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
new file mode 100644
index 0000000..0616353
--- /dev/null
+++ b/drivers/isdn/capi/capi.c
@@ -0,0 +1,1554 @@
+/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $
+ *
+ * CAPI 2.0 Interface for Linux
+ *
+ * Copyright 1996 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#include <linux/tty.h>
+#ifdef CONFIG_PPP
+#include <linux/netdevice.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif /* CONFIG_PPP */
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capicmd.h>
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+#include "capifs.h"
+#endif
+
+static char *revision = "$Revision: 1.1.2.7 $";
+
+MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+#undef _DEBUG_REFCOUNT		/* alloc/free and open/close debug */
+#undef _DEBUG_TTYFUNCS		/* call to tty_driver */
+#undef _DEBUG_DATAFLOW		/* data flow */
+
+/* -------- driver information -------------------------------------- */
+
+static struct class_simple *capi_class;
+
+int capi_major = 68;		/* allocated */
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#define CAPINC_NR_PORTS	32
+#define CAPINC_MAX_PORTS	256
+int capi_ttymajor = 191;
+int capi_ttyminors = CAPINC_NR_PORTS;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+module_param_named(major, capi_major, uint, 0);
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+module_param_named(ttymajor, capi_ttymajor, uint, 0);
+module_param_named(ttyminors, capi_ttyminors, uint, 0);
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- defines ------------------------------------------------- */
+
+#define CAPINC_MAX_RECVQUEUE	10
+#define CAPINC_MAX_SENDQUEUE	10
+#define CAPI_MAX_BLKSIZE	2048
+
+/* -------- data structures ----------------------------------------- */
+
+struct capidev;
+struct capincci;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+struct capiminor;
+
+struct capiminor {
+	struct list_head list;
+	struct capincci  *nccip;
+	unsigned int      minor;
+
+	struct capi20_appl *ap;
+	u32		 ncci;
+	u16		 datahandle;
+	u16		 msgid;
+
+	struct tty_struct *tty;
+	int                ttyinstop;
+	int                ttyoutstop;
+	struct sk_buff    *ttyskb;
+	atomic_t           ttyopencount;
+
+	struct sk_buff_head inqueue;
+	int                 inbytes;
+	struct sk_buff_head outqueue;
+	int                 outbytes;
+
+	/* transmit path */
+	struct datahandle_queue {
+		    struct datahandle_queue *next;
+		    u16                    datahandle;
+	} *ackqueue;
+	int nack;
+
+};
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+struct capincci {
+	struct capincci *next;
+	u32		 ncci;
+	struct capidev	*cdev;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *minorp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+};
+
+struct capidev {
+	struct list_head list;
+	struct capi20_appl ap;
+	u16		errcode;
+	unsigned        userflags;
+
+	struct sk_buff_head recvqueue;
+	wait_queue_head_t recvwait;
+
+	struct capincci *nccis;
+
+	struct semaphore ncci_list_sem;
+};
+
+/* -------- global variables ---------------------------------------- */
+
+static DEFINE_RWLOCK(capidev_list_lock);
+static LIST_HEAD(capidev_list);
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+static DEFINE_RWLOCK(capiminor_list_lock);
+static LIST_HEAD(capiminor_list);
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- datahandles --------------------------------------------- */
+
+static int capincci_add_ack(struct capiminor *mp, u16 datahandle)
+{
+	struct datahandle_queue *n, **pp;
+
+	n = kmalloc(sizeof(*n), GFP_ATOMIC);
+	if (!n) {
+	   printk(KERN_ERR "capi: alloc datahandle failed\n");
+	   return -1;
+	}
+	n->next = NULL;
+	n->datahandle = datahandle;
+	for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) ;
+	*pp = n;
+	mp->nack++;
+	return 0;
+}
+
+static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
+{
+	struct datahandle_queue **pp, *p;
+
+	for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) {
+ 		if ((*pp)->datahandle == datahandle) {
+			p = *pp;
+			*pp = (*pp)->next;
+			kfree(p);
+			mp->nack--;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static void capiminor_del_all_ack(struct capiminor *mp)
+{
+	struct datahandle_queue **pp, *p;
+
+	pp = &mp->ackqueue;
+	while (*pp) {
+		p = *pp;
+		*pp = (*pp)->next;
+		kfree(p);
+		mp->nack--;
+	}
+}
+
+
+/* -------- struct capiminor ---------------------------------------- */
+
+static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
+{
+	struct capiminor *mp, *p;
+	unsigned int minor = 0;
+	unsigned long flags;
+
+	mp = kmalloc(sizeof(*mp), GFP_ATOMIC);
+  	if (!mp) {
+  		printk(KERN_ERR "capi: can't alloc capiminor\n");
+		return NULL;
+	}
+
+	memset(mp, 0, sizeof(struct capiminor));
+	mp->ap = ap;
+	mp->ncci = ncci;
+	mp->msgid = 0;
+	atomic_set(&mp->ttyopencount,0);
+
+	skb_queue_head_init(&mp->inqueue);
+	skb_queue_head_init(&mp->outqueue);
+
+	/* Allocate the least unused minor number.
+	 */
+	write_lock_irqsave(&capiminor_list_lock, flags);
+	if (list_empty(&capiminor_list))
+		list_add(&mp->list, &capiminor_list);
+	else {
+		list_for_each_entry(p, &capiminor_list, list) {
+			if (p->minor > minor)
+				break;
+			minor++;
+		}
+		
+		if (minor < capi_ttyminors) {
+			mp->minor = minor;
+			list_add(&mp->list, p->list.prev);
+		}
+	}
+		write_unlock_irqrestore(&capiminor_list_lock, flags);
+
+	if (!(minor < capi_ttyminors)) {
+		printk(KERN_NOTICE "capi: out of minors\n");
+			kfree(mp);
+		return NULL;
+	}
+
+	return mp;
+}
+
+static void capiminor_free(struct capiminor *mp)
+{
+	unsigned long flags;
+
+	write_lock_irqsave(&capiminor_list_lock, flags);
+	list_del(&mp->list);
+	write_unlock_irqrestore(&capiminor_list_lock, flags);
+
+	if (mp->ttyskb) kfree_skb(mp->ttyskb);
+	mp->ttyskb = NULL;
+	skb_queue_purge(&mp->inqueue);
+	skb_queue_purge(&mp->outqueue);
+	capiminor_del_all_ack(mp);
+	kfree(mp);
+}
+
+struct capiminor *capiminor_find(unsigned int minor)
+{
+	struct list_head *l;
+	struct capiminor *p = NULL;
+
+	read_lock(&capiminor_list_lock);
+	list_for_each(l, &capiminor_list) {
+		p = list_entry(l, struct capiminor, list);
+		if (p->minor == minor)
+			break;
+	}
+	read_unlock(&capiminor_list_lock);
+	if (l == &capiminor_list)
+		return NULL;
+
+	return p;
+}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- struct capincci ----------------------------------------- */
+
+static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci)
+{
+	struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *mp = NULL;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+	np = kmalloc(sizeof(*np), GFP_ATOMIC);
+	if (!np)
+		return NULL;
+	memset(np, 0, sizeof(struct capincci));
+	np->ncci = ncci;
+	np->cdev = cdev;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	mp = NULL;
+	if (cdev->userflags & CAPIFLAG_HIGHJACKING)
+		mp = np->minorp = capiminor_alloc(&cdev->ap, ncci);
+	if (mp) {
+		mp->nccip = np;
+#ifdef _DEBUG_REFCOUNT
+		printk(KERN_DEBUG "set mp->nccip\n");
+#endif
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+		capifs_new_ncci(mp->minor, MKDEV(capi_ttymajor, mp->minor));
+#endif
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+	for (pp=&cdev->nccis; *pp; pp = &(*pp)->next)
+		;
+	*pp = np;
+        return np;
+}
+
+static void capincci_free(struct capidev *cdev, u32 ncci)
+{
+	struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+	pp=&cdev->nccis;
+	while (*pp) {
+		np = *pp;
+		if (ncci == 0xffffffff || np->ncci == ncci) {
+			*pp = (*pp)->next;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+			if ((mp = np->minorp) != 0) {
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+				capifs_free_ncci(mp->minor);
+#endif
+				if (mp->tty) {
+					mp->nccip = NULL;
+#ifdef _DEBUG_REFCOUNT
+					printk(KERN_DEBUG "reset mp->nccip\n");
+#endif
+					tty_hangup(mp->tty);
+				} else {
+					capiminor_free(mp);
+				}
+			}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+			kfree(np);
+			if (*pp == 0) return;
+		} else {
+			pp = &(*pp)->next;
+		}
+	}
+}
+
+static struct capincci *capincci_find(struct capidev *cdev, u32 ncci)
+{
+	struct capincci *p;
+
+	for (p=cdev->nccis; p ; p = p->next) {
+		if (p->ncci == ncci)
+			break;
+	}
+	return p;
+}
+
+/* -------- struct capidev ------------------------------------------ */
+
+static struct capidev *capidev_alloc(void)
+{
+	struct capidev *cdev;
+	unsigned long flags;
+
+	cdev = kmalloc(sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return NULL;
+	memset(cdev, 0, sizeof(struct capidev));
+
+	init_MUTEX(&cdev->ncci_list_sem);
+	skb_queue_head_init(&cdev->recvqueue);
+	init_waitqueue_head(&cdev->recvwait);
+	write_lock_irqsave(&capidev_list_lock, flags);
+	list_add_tail(&cdev->list, &capidev_list);
+	write_unlock_irqrestore(&capidev_list_lock, flags);
+        return cdev;
+}
+
+static void capidev_free(struct capidev *cdev)
+{
+	unsigned long flags;
+
+	if (cdev->ap.applid) {
+		capi20_release(&cdev->ap);
+		cdev->ap.applid = 0;
+	}
+	skb_queue_purge(&cdev->recvqueue);
+
+	down(&cdev->ncci_list_sem);
+	capincci_free(cdev, 0xffffffff);
+	up(&cdev->ncci_list_sem);
+
+	write_lock_irqsave(&capidev_list_lock, flags);
+	list_del(&cdev->list);
+	write_unlock_irqrestore(&capidev_list_lock, flags);
+	kfree(cdev);
+}
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- handle data queue --------------------------------------- */
+
+static struct sk_buff *
+gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
+{
+	struct sk_buff *nskb;
+	nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
+	if (nskb) {
+		u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
+		unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
+		capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
+		capimsg_setu16(s, 2, mp->ap->applid);
+		capimsg_setu8 (s, 4, CAPI_DATA_B3);
+		capimsg_setu8 (s, 5, CAPI_RESP);
+		capimsg_setu16(s, 6, mp->msgid++);
+		capimsg_setu32(s, 8, mp->ncci);
+		capimsg_setu16(s, 12, datahandle);
+	}
+	return nskb;
+}
+
+static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
+{
+	struct sk_buff *nskb;
+	int datalen;
+	u16 errcode, datahandle;
+	struct tty_ldisc *ld;
+	
+	datalen = skb->len - CAPIMSG_LEN(skb->data);
+	if (mp->tty == NULL)
+	{
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi: currently no receiver\n");
+#endif
+		return -1;
+	}
+	
+	ld = tty_ldisc_ref(mp->tty);
+	if (ld == NULL)
+		return -1;
+	if (ld->receive_buf == NULL) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+		printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n");
+#endif
+		goto bad;
+	}
+	if (mp->ttyinstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+		printk(KERN_DEBUG "capi: recv tty throttled\n");
+#endif
+		goto bad;
+	}
+	if (ld->receive_room &&
+	    ld->receive_room(mp->tty) < datalen) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+		printk(KERN_DEBUG "capi: no room in tty\n");
+#endif
+		goto bad;
+	}
+	if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) {
+		printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
+		goto bad;
+	}
+	datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
+	errcode = capi20_put_message(mp->ap, nskb);
+	if (errcode != CAPI_NOERROR) {
+		printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
+				errcode);
+		kfree_skb(nskb);
+		goto bad;
+	}
+	(void)skb_pull(skb, CAPIMSG_LEN(skb->data));
+#ifdef _DEBUG_DATAFLOW
+	printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
+				datahandle, skb->len);
+#endif
+	ld->receive_buf(mp->tty, skb->data, NULL, skb->len);
+	kfree_skb(skb);
+	tty_ldisc_deref(ld);
+	return 0;
+bad:
+	tty_ldisc_deref(ld);
+	return -1;
+}
+
+static void handle_minor_recv(struct capiminor *mp)
+{
+	struct sk_buff *skb;
+	while ((skb = skb_dequeue(&mp->inqueue)) != 0) {
+		unsigned int len = skb->len;
+		mp->inbytes -= len;
+		if (handle_recv_skb(mp, skb) < 0) {
+			skb_queue_head(&mp->inqueue, skb);
+			mp->inbytes += len;
+			return;
+		}
+	}
+}
+
+static int handle_minor_send(struct capiminor *mp)
+{
+	struct sk_buff *skb;
+	u16 len;
+	int count = 0;
+	u16 errcode;
+	u16 datahandle;
+
+	if (mp->tty && mp->ttyoutstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+		printk(KERN_DEBUG "capi: send: tty stopped\n");
+#endif
+		return 0;
+	}
+
+	while ((skb = skb_dequeue(&mp->outqueue)) != 0) {
+		datahandle = mp->datahandle;
+		len = (u16)skb->len;
+		skb_push(skb, CAPI_DATA_B3_REQ_LEN);
+		memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+		capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+		capimsg_setu16(skb->data, 2, mp->ap->applid);
+		capimsg_setu8 (skb->data, 4, CAPI_DATA_B3);
+		capimsg_setu8 (skb->data, 5, CAPI_REQ);
+		capimsg_setu16(skb->data, 6, mp->msgid++);
+		capimsg_setu32(skb->data, 8, mp->ncci);	/* NCCI */
+		capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */
+		capimsg_setu16(skb->data, 16, len);	/* Data length */
+		capimsg_setu16(skb->data, 18, datahandle);
+		capimsg_setu16(skb->data, 20, 0);	/* Flags */
+
+		if (capincci_add_ack(mp, datahandle) < 0) {
+			skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+			skb_queue_head(&mp->outqueue, skb);
+			return count;
+		}
+		errcode = capi20_put_message(mp->ap, skb);
+		if (errcode == CAPI_NOERROR) {
+			mp->datahandle++;
+			count++;
+			mp->outbytes -= len;
+#ifdef _DEBUG_DATAFLOW
+			printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n",
+							datahandle, len);
+#endif
+			continue;
+		}
+		capiminor_del_ack(mp, datahandle);
+
+		if (errcode == CAPI_SENDQUEUEFULL) {
+			skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+			skb_queue_head(&mp->outqueue, skb);
+			break;
+		}
+
+		/* ups, drop packet */
+		printk(KERN_ERR "capi: put_message = %x\n", errcode);
+		mp->outbytes -= len;
+		kfree_skb(skb);
+	}
+	return count;
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+/* -------- function called by lower level -------------------------- */
+
+static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+	struct capidev *cdev = ap->private;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	struct capiminor *mp;
+	u16 datahandle;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+	struct capincci *np;
+	u32 ncci;
+
+	if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) {
+		u16 info = CAPIMSG_U16(skb->data, 12); // Info field
+		if (info == 0) {
+			down(&cdev->ncci_list_sem);
+			capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
+			up(&cdev->ncci_list_sem);
+		}
+	}
+	if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) {
+		down(&cdev->ncci_list_sem);
+		capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
+		up(&cdev->ncci_list_sem);
+	}
+	if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
+		return;
+	}
+	ncci = CAPIMSG_CONTROL(skb->data);
+	for (np = cdev->nccis; np && np->ncci != ncci; np = np->next)
+		;
+	if (!np) {
+		printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
+		return;
+	}
+#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
+	skb_queue_tail(&cdev->recvqueue, skb);
+	wake_up_interruptible(&cdev->recvwait);
+#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+	mp = np->minorp;
+	if (!mp) {
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
+		return;
+	}
+
+
+	if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
+		
+		datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2);
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n",
+				datahandle, skb->len-CAPIMSG_LEN(skb->data));
+#endif
+		skb_queue_tail(&mp->inqueue, skb);
+		mp->inbytes += skb->len;
+		handle_minor_recv(mp);
+
+	} else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
+
+		datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4);
+#ifdef _DEBUG_DATAFLOW
+		printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n",
+				datahandle,
+				CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));
+#endif
+		kfree_skb(skb);
+		(void)capiminor_del_ack(mp, datahandle);
+		if (mp->tty)
+			tty_wakeup(mp->tty);
+		(void)handle_minor_send(mp);
+
+	} else {
+		/* ups, let capi application handle it :-) */
+		skb_queue_tail(&cdev->recvqueue, skb);
+		wake_up_interruptible(&cdev->recvwait);
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+}
+
+/* -------- file_operations for capidev ----------------------------- */
+
+static ssize_t
+capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	struct capidev *cdev = (struct capidev *)file->private_data;
+	struct sk_buff *skb;
+	size_t copied;
+
+	if (!cdev->ap.applid)
+		return -ENODEV;
+
+	if ((skb = skb_dequeue(&cdev->recvqueue)) == 0) {
+
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		for (;;) {
+			interruptible_sleep_on(&cdev->recvwait);
+			if ((skb = skb_dequeue(&cdev->recvqueue)) != 0)
+				break;
+			if (signal_pending(current))
+				break;
+		}
+		if (skb == 0)
+			return -ERESTARTNOHAND;
+	}
+	if (skb->len > count) {
+		skb_queue_head(&cdev->recvqueue, skb);
+		return -EMSGSIZE;
+	}
+	if (copy_to_user(buf, skb->data, skb->len)) {
+		skb_queue_head(&cdev->recvqueue, skb);
+		return -EFAULT;
+	}
+	copied = skb->len;
+
+	kfree_skb(skb);
+
+	return copied;
+}
+
+static ssize_t
+capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct capidev *cdev = (struct capidev *)file->private_data;
+	struct sk_buff *skb;
+	u16 mlen;
+
+	if (!cdev->ap.applid)
+		return -ENODEV;
+
+	skb = alloc_skb(count, GFP_USER);
+	if (!skb)
+		return -ENOMEM;
+
+	if (copy_from_user(skb_put(skb, count), buf, count)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
+	mlen = CAPIMSG_LEN(skb->data);
+	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+		if ((size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) {
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+	} else {
+		if (mlen != count) {
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+	}
+	CAPIMSG_SETAPPID(skb->data, cdev->ap.applid);
+
+	if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) {
+		down(&cdev->ncci_list_sem);
+		capincci_free(cdev, CAPIMSG_NCCI(skb->data));
+		up(&cdev->ncci_list_sem);
+	}
+
+	cdev->errcode = capi20_put_message(&cdev->ap, skb);
+
+	if (cdev->errcode) {
+		kfree_skb(skb);
+		return -EIO;
+	}
+	return count;
+}
+
+static unsigned int
+capi_poll(struct file *file, poll_table * wait)
+{
+	struct capidev *cdev = (struct capidev *)file->private_data;
+	unsigned int mask = 0;
+
+	if (!cdev->ap.applid)
+		return POLLERR;
+
+	poll_wait(file, &(cdev->recvwait), wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (!skb_queue_empty(&cdev->recvqueue))
+		mask |= POLLIN | POLLRDNORM;
+	return mask;
+}
+
+static int
+capi_ioctl(struct inode *inode, struct file *file,
+	   unsigned int cmd, unsigned long arg)
+{
+	struct capidev *cdev = file->private_data;
+	struct capi20_appl *ap = &cdev->ap;
+	capi_ioctl_struct data;
+	int retval = -EINVAL;
+	void __user *argp = (void __user *)arg;
+
+	switch (cmd) {
+	case CAPI_REGISTER:
+		{
+			if (ap->applid)
+				return -EEXIST;
+
+			if (copy_from_user(&cdev->ap.rparam, argp,
+					   sizeof(struct capi_register_params)))
+				return -EFAULT;
+			
+			cdev->ap.private = cdev;
+			cdev->ap.recv_message = capi_recv_message;
+			cdev->errcode = capi20_register(ap);
+			if (cdev->errcode) {
+				ap->applid = 0;
+				return -EIO;
+			}
+		}
+		return (int)ap->applid;
+
+	case CAPI_GET_VERSION:
+		{
+			if (copy_from_user(&data.contr, argp,
+						sizeof(data.contr)))
+				return -EFAULT;
+		        cdev->errcode = capi20_get_version(data.contr, &data.version);
+			if (cdev->errcode)
+				return -EIO;
+			if (copy_to_user(argp, &data.version,
+					 sizeof(data.version)))
+				return -EFAULT;
+		}
+		return 0;
+
+	case CAPI_GET_SERIAL:
+		{
+			if (copy_from_user(&data.contr, argp,
+					   sizeof(data.contr)))
+				return -EFAULT;
+			cdev->errcode = capi20_get_serial (data.contr, data.serial);
+			if (cdev->errcode)
+				return -EIO;
+			if (copy_to_user(argp, data.serial,
+					 sizeof(data.serial)))
+				return -EFAULT;
+		}
+		return 0;
+	case CAPI_GET_PROFILE:
+		{
+			if (copy_from_user(&data.contr, argp,
+					   sizeof(data.contr)))
+				return -EFAULT;
+
+			if (data.contr == 0) {
+				cdev->errcode = capi20_get_profile(data.contr, &data.profile);
+				if (cdev->errcode)
+					return -EIO;
+
+				retval = copy_to_user(argp,
+				      &data.profile.ncontroller,
+				       sizeof(data.profile.ncontroller));
+
+			} else {
+				cdev->errcode = capi20_get_profile(data.contr, &data.profile);
+				if (cdev->errcode)
+					return -EIO;
+
+				retval = copy_to_user(argp, &data.profile,
+						   sizeof(data.profile));
+			}
+			if (retval)
+				return -EFAULT;
+		}
+		return 0;
+
+	case CAPI_GET_MANUFACTURER:
+		{
+			if (copy_from_user(&data.contr, argp,
+					   sizeof(data.contr)))
+				return -EFAULT;
+			cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer);
+			if (cdev->errcode)
+				return -EIO;
+
+			if (copy_to_user(argp, data.manufacturer,
+					 sizeof(data.manufacturer)))
+				return -EFAULT;
+
+		}
+		return 0;
+	case CAPI_GET_ERRCODE:
+		data.errcode = cdev->errcode;
+		cdev->errcode = CAPI_NOERROR;
+		if (arg) {
+			if (copy_to_user(argp, &data.errcode,
+					 sizeof(data.errcode)))
+				return -EFAULT;
+		}
+		return data.errcode;
+
+	case CAPI_INSTALLED:
+		if (capi20_isinstalled() == CAPI_NOERROR)
+			return 0;
+		return -ENXIO;
+
+	case CAPI_MANUFACTURER_CMD:
+		{
+			struct capi_manufacturer_cmd mcmd;
+			if (!capable(CAP_SYS_ADMIN))
+				return -EPERM;
+			if (copy_from_user(&mcmd, argp, sizeof(mcmd)))
+				return -EFAULT;
+			return capi20_manufacturer(mcmd.cmd, mcmd.data);
+		}
+		return 0;
+
+	case CAPI_SET_FLAGS:
+	case CAPI_CLR_FLAGS:
+		{
+			unsigned userflags;
+			if (copy_from_user(&userflags, argp,
+					   sizeof(userflags)))
+				return -EFAULT;
+			if (cmd == CAPI_SET_FLAGS)
+				cdev->userflags |= userflags;
+			else
+				cdev->userflags &= ~userflags;
+		}
+		return 0;
+
+	case CAPI_GET_FLAGS:
+		if (copy_to_user(argp, &cdev->userflags,
+				 sizeof(cdev->userflags)))
+			return -EFAULT;
+		return 0;
+
+	case CAPI_NCCI_OPENCOUNT:
+		{
+			struct capincci *nccip;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+			struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+			unsigned ncci;
+			int count = 0;
+			if (copy_from_user(&ncci, argp, sizeof(ncci)))
+				return -EFAULT;
+
+			down(&cdev->ncci_list_sem);
+			if ((nccip = capincci_find(cdev, (u32) ncci)) == 0) {
+				up(&cdev->ncci_list_sem);
+				return 0;
+			}
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+			if ((mp = nccip->minorp) != 0) {
+				count += atomic_read(&mp->ttyopencount);
+			}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+			up(&cdev->ncci_list_sem);
+			return count;
+		}
+		return 0;
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	case CAPI_NCCI_GETUNIT:
+		{
+			struct capincci *nccip;
+			struct capiminor *mp;
+			unsigned ncci;
+			int unit = 0;
+			if (copy_from_user(&ncci, argp,
+					   sizeof(ncci)))
+				return -EFAULT;
+			down(&cdev->ncci_list_sem);
+			nccip = capincci_find(cdev, (u32) ncci);
+			if (!nccip || (mp = nccip->minorp) == 0) {
+				up(&cdev->ncci_list_sem);
+				return -ESRCH;
+			}
+			unit = mp->minor;
+			up(&cdev->ncci_list_sem);
+			return unit;
+		}
+		return 0;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+	}
+	return -EINVAL;
+}
+
+static int
+capi_open(struct inode *inode, struct file *file)
+{
+	if (file->private_data)
+		return -EEXIST;
+
+	if ((file->private_data = capidev_alloc()) == 0)
+		return -ENOMEM;
+
+	return nonseekable_open(inode, file);
+}
+
+static int
+capi_release(struct inode *inode, struct file *file)
+{
+	struct capidev *cdev = (struct capidev *)file->private_data;
+
+	capidev_free(cdev);
+	file->private_data = NULL;
+	
+	return 0;
+}
+
+static struct file_operations capi_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= capi_read,
+	.write		= capi_write,
+	.poll		= capi_poll,
+	.ioctl		= capi_ioctl,
+	.open		= capi_open,
+	.release	= capi_release,
+};
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- tty_operations for capincci ----------------------------- */
+
+static int capinc_tty_open(struct tty_struct * tty, struct file * file)
+{
+	struct capiminor *mp;
+
+	if ((mp = capiminor_find(iminor(file->f_dentry->d_inode))) == 0)
+		return -ENXIO;
+	if (mp->nccip == 0)
+		return -ENXIO;
+
+	tty->driver_data = (void *)mp;
+
+	if (atomic_read(&mp->ttyopencount) == 0)
+		mp->tty = tty;
+	atomic_inc(&mp->ttyopencount);
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+	handle_minor_recv(mp);
+	return 0;
+}
+
+static void capinc_tty_close(struct tty_struct * tty, struct file * file)
+{
+	struct capiminor *mp;
+
+	mp = (struct capiminor *)tty->driver_data;
+	if (mp)	{
+		if (atomic_dec_and_test(&mp->ttyopencount)) {
+#ifdef _DEBUG_REFCOUNT
+			printk(KERN_DEBUG "capinc_tty_close lastclose\n");
+#endif
+			tty->driver_data = NULL;
+			mp->tty = NULL;
+		}
+#ifdef _DEBUG_REFCOUNT
+		printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+		if (mp->nccip == 0)
+			capiminor_free(mp);
+	}
+
+#ifdef _DEBUG_REFCOUNT
+	printk(KERN_DEBUG "capinc_tty_close\n");
+#endif
+}
+
+static int capinc_tty_write(struct tty_struct * tty,
+			    const unsigned char *buf, int count)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
+#endif
+
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n");
+#endif
+		return 0;
+	}
+
+	skb = mp->ttyskb;
+	if (skb) {
+		mp->ttyskb = NULL;
+		skb_queue_tail(&mp->outqueue, skb);
+		mp->outbytes += skb->len;
+	}
+
+	skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
+		return -ENOMEM;
+	}
+
+	skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+	memcpy(skb_put(skb, count), buf, count);
+
+	skb_queue_tail(&mp->outqueue, skb);
+	mp->outbytes += skb->len;
+	(void)handle_minor_send(mp);
+	(void)handle_minor_recv(mp);
+	return count;
+}
+
+static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
+#endif
+
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n");
+#endif
+		return;
+	}
+
+	skb = mp->ttyskb;
+	if (skb) {
+		if (skb_tailroom(skb) > 0) {
+			*(skb_put(skb, 1)) = ch;
+			return;
+		}
+		mp->ttyskb = NULL;
+		skb_queue_tail(&mp->outqueue, skb);
+		mp->outbytes += skb->len;
+		(void)handle_minor_send(mp);
+	}
+	skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC);
+	if (skb) {
+		skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+		*(skb_put(skb, 1)) = ch;
+		mp->ttyskb = skb;
+	} else {
+		printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
+	}
+}
+
+static void capinc_tty_flush_chars(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	struct sk_buff *skb;
+
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_flush_chars\n");
+#endif
+
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n");
+#endif
+		return;
+	}
+
+	skb = mp->ttyskb;
+	if (skb) {
+		mp->ttyskb = NULL;
+		skb_queue_tail(&mp->outqueue, skb);
+		mp->outbytes += skb->len;
+		(void)handle_minor_send(mp);
+	}
+	(void)handle_minor_recv(mp);
+}
+
+static int capinc_tty_write_room(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	int room;
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n");
+#endif
+		return 0;
+	}
+	room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue);
+	room *= CAPI_MAX_BLKSIZE;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_write_room = %d\n", room);
+#endif
+	return room;
+}
+
+int capinc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+	if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+		printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n");
+#endif
+		return 0;
+	}
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n",
+			mp->outbytes, mp->nack,
+			skb_queue_len(&mp->outqueue),
+			skb_queue_len(&mp->inqueue));
+#endif
+	return mp->outbytes;
+}
+
+static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error = 0;
+	switch (cmd) {
+	default:
+		error = n_tty_ioctl (tty, file, cmd, arg);
+		break;
+	}
+	return error;
+}
+
+static void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_set_termios\n");
+#endif
+}
+
+static void capinc_tty_throttle(struct tty_struct * tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_throttle\n");
+#endif
+	if (mp)
+		mp->ttyinstop = 1;
+}
+
+static void capinc_tty_unthrottle(struct tty_struct * tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_unthrottle\n");
+#endif
+	if (mp) {
+		mp->ttyinstop = 0;
+		handle_minor_recv(mp);
+	}
+}
+
+static void capinc_tty_stop(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_stop\n");
+#endif
+	if (mp) {
+		mp->ttyoutstop = 1;
+	}
+}
+
+static void capinc_tty_start(struct tty_struct *tty)
+{
+	struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_start\n");
+#endif
+	if (mp) {
+		mp->ttyoutstop = 0;
+		(void)handle_minor_send(mp);
+	}
+}
+
+static void capinc_tty_hangup(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_hangup\n");
+#endif
+}
+
+static void capinc_tty_break_ctl(struct tty_struct *tty, int state)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
+#endif
+}
+
+static void capinc_tty_flush_buffer(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_flush_buffer\n");
+#endif
+}
+
+static void capinc_tty_set_ldisc(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_set_ldisc\n");
+#endif
+}
+
+static void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+#ifdef _DEBUG_TTYFUNCS
+	printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch);
+#endif
+}
+
+static int capinc_tty_read_proc(char *page, char **start, off_t off,
+				int count, int *eof, void *data)
+{
+	return 0;
+}
+
+static struct tty_driver *capinc_tty_driver;
+
+static struct tty_operations capinc_ops = {
+	.open = capinc_tty_open,
+	.close = capinc_tty_close,
+	.write = capinc_tty_write,
+	.put_char = capinc_tty_put_char,
+	.flush_chars = capinc_tty_flush_chars,
+	.write_room = capinc_tty_write_room,
+	.chars_in_buffer = capinc_tty_chars_in_buffer,
+	.ioctl = capinc_tty_ioctl,
+	.set_termios = capinc_tty_set_termios,
+	.throttle = capinc_tty_throttle,
+	.unthrottle = capinc_tty_unthrottle,
+	.stop = capinc_tty_stop,
+	.start = capinc_tty_start,
+	.hangup = capinc_tty_hangup,
+	.break_ctl = capinc_tty_break_ctl,
+	.flush_buffer = capinc_tty_flush_buffer,
+	.set_ldisc = capinc_tty_set_ldisc,
+	.send_xchar = capinc_tty_send_xchar,
+	.read_proc = capinc_tty_read_proc,
+};
+
+static int capinc_tty_init(void)
+{
+	struct tty_driver *drv;
+	
+	if (capi_ttyminors > CAPINC_MAX_PORTS)
+		capi_ttyminors = CAPINC_MAX_PORTS;
+	if (capi_ttyminors <= 0)
+		capi_ttyminors = CAPINC_NR_PORTS;
+
+	drv = alloc_tty_driver(capi_ttyminors);
+	if (!drv)
+		return -ENOMEM;
+
+	drv->owner = THIS_MODULE;
+	drv->driver_name = "capi_nc";
+	drv->devfs_name = "capi/";
+	drv->name = "capi";
+	drv->major = capi_ttymajor;
+	drv->minor_start = 0;
+	drv->type = TTY_DRIVER_TYPE_SERIAL;
+	drv->subtype = SERIAL_TYPE_NORMAL;
+	drv->init_termios = tty_std_termios;
+	drv->init_termios.c_iflag = ICRNL;
+	drv->init_termios.c_oflag = OPOST | ONLCR;
+	drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	drv->init_termios.c_lflag = 0;
+	drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS;
+	tty_set_operations(drv, &capinc_ops);
+	if (tty_register_driver(drv)) {
+		put_tty_driver(drv);
+		printk(KERN_ERR "Couldn't register capi_nc driver\n");
+		return -1;
+	}
+	capinc_tty_driver = drv;
+	return 0;
+}
+
+static void capinc_tty_exit(void)
+{
+	struct tty_driver *drv = capinc_tty_driver;
+	int retval;
+	if ((retval = tty_unregister_driver(drv)))
+		printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval);
+	put_tty_driver(drv);
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- /proc functions ----------------------------------------- */
+
+/*
+ * /proc/capi/capi20:
+ *  minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
+ */
+static int proc_capidev_read_proc(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data)
+{
+        struct capidev *cdev;
+	struct list_head *l;
+	int len = 0;
+
+	read_lock(&capidev_list_lock);
+	list_for_each(l, &capidev_list) {
+		cdev = list_entry(l, struct capidev, list);
+		len += sprintf(page+len, "0 %d %lu %lu %lu %lu\n",
+			cdev->ap.applid,
+			cdev->ap.nrecvctlpkt,
+			cdev->ap.nrecvdatapkt,
+			cdev->ap.nsentctlpkt,
+			cdev->ap.nsentdatapkt);
+		if (len <= off) {
+			off -= len;
+			len = 0;
+		} else {
+			if (len-off > count)
+				goto endloop;
+		}
+	}
+
+endloop:
+	read_unlock(&capidev_list_lock);
+	if (len < count)
+		*eof = 1;
+	if (len > count) len = count;
+	if (len < 0) len = 0;
+	return len;
+}
+
+/*
+ * /proc/capi/capi20ncci:
+ *  applid ncci
+ */
+static int proc_capincci_read_proc(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data)
+{
+        struct capidev *cdev;
+        struct capincci *np;
+	struct list_head *l;
+	int len = 0;
+
+	read_lock(&capidev_list_lock);
+	list_for_each(l, &capidev_list) {
+		cdev = list_entry(l, struct capidev, list);
+		for (np=cdev->nccis; np; np = np->next) {
+			len += sprintf(page+len, "%d 0x%x\n",
+				       cdev->ap.applid,
+				       np->ncci);
+			if (len <= off) {
+				off -= len;
+				len = 0;
+			} else {
+				if (len-off > count)
+					goto endloop;
+			}
+		}
+	}
+endloop:
+	read_unlock(&capidev_list_lock);
+	*start = page+off;
+	if (len < count)
+		*eof = 1;
+	if (len>count) len = count;
+	if (len<0) len = 0;
+	return len;
+}
+
+static struct procfsentries {
+  char *name;
+  mode_t mode;
+  int (*read_proc)(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data);
+  struct proc_dir_entry *procent;
+} procfsentries[] = {
+   /* { "capi",		  S_IFDIR, 0 }, */
+   { "capi/capi20", 	  0	 , proc_capidev_read_proc },
+   { "capi/capi20ncci",   0	 , proc_capincci_read_proc },
+};
+
+static void __init proc_init(void)
+{
+    int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+    int i;
+
+    for (i=0; i < nelem; i++) {
+        struct procfsentries *p = procfsentries + i;
+	p->procent = create_proc_entry(p->name, p->mode, NULL);
+	if (p->procent) p->procent->read_proc = p->read_proc;
+    }
+}
+
+static void __exit proc_exit(void)
+{
+    int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+    int i;
+
+    for (i=nelem-1; i >= 0; i--) {
+        struct procfsentries *p = procfsentries + i;
+	if (p->procent) {
+	   remove_proc_entry(p->name, NULL);
+	   p->procent = NULL;
+	}
+    }
+}
+
+/* -------- init function and module interface ---------------------- */
+
+
+static char rev[32];
+
+static int __init capi_init(void)
+{
+	char *p;
+	char *compileinfo;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, sizeof(rev));
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	if (register_chrdev(capi_major, "capi20", &capi_fops)) {
+		printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
+		return -EIO;
+	}
+
+	capi_class = class_simple_create(THIS_MODULE, "capi");
+	if (IS_ERR(capi_class)) {
+		unregister_chrdev(capi_major, "capi20");
+		return PTR_ERR(capi_class);
+	}
+
+	class_simple_device_add(capi_class, MKDEV(capi_major, 0), NULL, "capi");
+	devfs_mk_cdev(MKDEV(capi_major, 0), S_IFCHR | S_IRUSR | S_IWUSR,
+			"isdn/capi20");
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	if (capinc_tty_init() < 0) {
+		class_simple_device_remove(MKDEV(capi_major, 0));
+		class_simple_destroy(capi_class);
+		unregister_chrdev(capi_major, "capi20");
+		return -ENOMEM;
+	}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+	proc_init();
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+        compileinfo = " (middleware+capifs)";
+#else
+        compileinfo = " (no capifs)";
+#endif
+#else
+        compileinfo = " (no middleware)";
+#endif
+	printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n",
+				rev, capi_major, compileinfo);
+
+	return 0;
+}
+
+static void __exit capi_exit(void)
+{
+	proc_exit();
+
+	class_simple_device_remove(MKDEV(capi_major, 0));
+	class_simple_destroy(capi_class);
+	unregister_chrdev(capi_major, "capi20");
+	devfs_remove("isdn/capi20");
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+	capinc_tty_exit();
+#endif
+	printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev);
+}
+
+module_init(capi_init);
+module_exit(capi_exit);
diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c
new file mode 100644
index 0000000..d10c8b8
--- /dev/null
+++ b/drivers/isdn/capi/capidrv.c
@@ -0,0 +1,2315 @@
+/* $Id: capidrv.c,v 1.1.2.2 2004/01/12 23:17:24 keil Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/isdn.h>
+#include <linux/isdnif.h>
+#include <linux/proc_fs.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capicmd.h>
+#include "capidrv.h"
+
+static char *revision = "$Revision: 1.1.2.2 $";
+static int debugmode = 0;
+
+MODULE_DESCRIPTION("CAPI4Linux: Interface to ISDN4Linux");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(debugmode, uint, 0);
+
+/* -------- type definitions ----------------------------------------- */
+
+
+struct capidrv_contr {
+
+	struct capidrv_contr *next;
+	struct module *owner;
+	u32 contrnr;
+	char name[20];
+
+	/*
+	 * for isdn4linux
+	 */
+	isdn_if interface;
+	int myid;
+
+	/*
+	 * LISTEN state
+	 */
+	int state;
+	u32 cipmask;
+	u32 cipmask2;
+        struct timer_list listentimer;
+
+	/*
+	 * ID of capi message sent
+	 */
+	u16 msgid;
+
+	/*
+	 * B-Channels
+	 */
+	int nbchan;
+	struct capidrv_bchan {
+		struct capidrv_contr *contr;
+		u8 msn[ISDN_MSNLEN];
+		int l2;
+		int l3;
+		u8 num[ISDN_MSNLEN];
+		u8 mynum[ISDN_MSNLEN];
+		int si1;
+		int si2;
+		int incoming;
+		int disconnecting;
+		struct capidrv_plci {
+			struct capidrv_plci *next;
+			u32 plci;
+			u32 ncci;	/* ncci for CONNECT_ACTIVE_IND */
+			u16 msgid;	/* to identfy CONNECT_CONF */
+			int chan;
+			int state;
+			int leasedline;
+			struct capidrv_ncci {
+				struct capidrv_ncci *next;
+				struct capidrv_plci *plcip;
+				u32 ncci;
+				u16 msgid;	/* to identfy CONNECT_B3_CONF */
+				int chan;
+				int state;
+				int oldstate;
+				/* */
+				u16 datahandle;
+				struct ncci_datahandle_queue {
+				    struct ncci_datahandle_queue *next;
+				    u16                         datahandle;
+				    int                           len;
+				} *ackqueue;
+			} *ncci_list;
+		} *plcip;
+		struct capidrv_ncci *nccip;
+	} *bchans;
+
+	struct capidrv_plci *plci_list;
+
+	/* for q931 data */
+	u8  q931_buf[4096];
+	u8 *q931_read;
+	u8 *q931_write;
+	u8 *q931_end;
+};
+
+
+struct capidrv_data {
+	struct capi20_appl ap;
+	int ncontr;
+	struct capidrv_contr *contr_list;
+};
+
+typedef struct capidrv_plci capidrv_plci;
+typedef struct capidrv_ncci capidrv_ncci;
+typedef struct capidrv_contr capidrv_contr;
+typedef struct capidrv_data capidrv_data;
+typedef struct capidrv_bchan capidrv_bchan;
+
+/* -------- data definitions ----------------------------------------- */
+
+static capidrv_data global;
+static DEFINE_SPINLOCK(global_lock);
+
+static void handle_dtrace_data(capidrv_contr *card,
+	int send, int level2, u8 *data, u16 len);
+
+/* -------- convert functions ---------------------------------------- */
+
+static inline u32 b1prot(int l2, int l3)
+{
+	switch (l2) {
+	case ISDN_PROTO_L2_X75I:
+	case ISDN_PROTO_L2_X75UI:
+	case ISDN_PROTO_L2_X75BUI:
+		return 0;
+	case ISDN_PROTO_L2_HDLC:
+	default:
+		return 0;
+	case ISDN_PROTO_L2_TRANS:
+		return 1;
+        case ISDN_PROTO_L2_V11096:
+        case ISDN_PROTO_L2_V11019:
+        case ISDN_PROTO_L2_V11038:
+		return 2;
+        case ISDN_PROTO_L2_FAX:
+		return 4;
+	case ISDN_PROTO_L2_MODEM:
+		return 8;
+	}
+}
+
+static inline u32 b2prot(int l2, int l3)
+{
+	switch (l2) {
+	case ISDN_PROTO_L2_X75I:
+	case ISDN_PROTO_L2_X75UI:
+	case ISDN_PROTO_L2_X75BUI:
+	default:
+		return 0;
+	case ISDN_PROTO_L2_HDLC:
+	case ISDN_PROTO_L2_TRANS:
+        case ISDN_PROTO_L2_V11096:
+        case ISDN_PROTO_L2_V11019:
+        case ISDN_PROTO_L2_V11038:
+	case ISDN_PROTO_L2_MODEM:
+		return 1;
+        case ISDN_PROTO_L2_FAX:
+		return 4;
+	}
+}
+
+static inline u32 b3prot(int l2, int l3)
+{
+	switch (l2) {
+	case ISDN_PROTO_L2_X75I:
+	case ISDN_PROTO_L2_X75UI:
+	case ISDN_PROTO_L2_X75BUI:
+	case ISDN_PROTO_L2_HDLC:
+	case ISDN_PROTO_L2_TRANS:
+        case ISDN_PROTO_L2_V11096:
+        case ISDN_PROTO_L2_V11019:
+        case ISDN_PROTO_L2_V11038:
+	case ISDN_PROTO_L2_MODEM:
+	default:
+		return 0;
+        case ISDN_PROTO_L2_FAX:
+		return 4;
+	}
+}
+
+static _cstruct b1config_async_v110(u16 rate)
+{
+	/* CAPI-Spec "B1 Configuration" */
+	static unsigned char buf[9];
+	buf[0] = 8; /* len */
+	/* maximum bitrate */
+	buf[1] = rate & 0xff; buf[2] = (rate >> 8) & 0xff;
+	buf[3] = 8; buf[4] = 0; /* 8 bits per character */
+	buf[5] = 0; buf[6] = 0; /* parity none */
+	buf[7] = 0; buf[8] = 0; /* 1 stop bit */
+	return buf;
+}
+
+static _cstruct b1config(int l2, int l3)
+{
+	switch (l2) {
+	case ISDN_PROTO_L2_X75I:
+	case ISDN_PROTO_L2_X75UI:
+	case ISDN_PROTO_L2_X75BUI:
+	case ISDN_PROTO_L2_HDLC:
+	case ISDN_PROTO_L2_TRANS:
+	default:
+		return NULL;
+        case ISDN_PROTO_L2_V11096:
+	    return b1config_async_v110(9600);
+        case ISDN_PROTO_L2_V11019:
+	    return b1config_async_v110(19200);
+        case ISDN_PROTO_L2_V11038:
+	    return b1config_async_v110(38400);
+	}
+}
+
+static inline u16 si2cip(u8 si1, u8 si2)
+{
+	static const u8 cip[17][5] =
+	{
+	/*  0  1  2  3  4  */
+		{0, 0, 0, 0, 0},	/*0 */
+		{16, 16, 4, 26, 16},	/*1 */
+		{17, 17, 17, 4, 4},	/*2 */
+		{2, 2, 2, 2, 2},	/*3 */
+		{18, 18, 18, 18, 18},	/*4 */
+		{2, 2, 2, 2, 2},	/*5 */
+		{0, 0, 0, 0, 0},	/*6 */
+		{2, 2, 2, 2, 2},	/*7 */
+		{2, 2, 2, 2, 2},	/*8 */
+		{21, 21, 21, 21, 21},	/*9 */
+		{19, 19, 19, 19, 19},	/*10 */
+		{0, 0, 0, 0, 0},	/*11 */
+		{0, 0, 0, 0, 0},	/*12 */
+		{0, 0, 0, 0, 0},	/*13 */
+		{0, 0, 0, 0, 0},	/*14 */
+		{22, 22, 22, 22, 22},	/*15 */
+		{27, 27, 27, 28, 27}	/*16 */
+	};
+	if (si1 > 16)
+		si1 = 0;
+	if (si2 > 4)
+		si2 = 0;
+
+	return (u16) cip[si1][si2];
+}
+
+static inline u8 cip2si1(u16 cipval)
+{
+	static const u8 si[32] =
+	{7, 1, 7, 7, 1, 1, 7, 7,	/*0-7 */
+	 7, 1, 0, 0, 0, 0, 0, 0,	/*8-15 */
+	 1, 2, 4, 10, 9, 9, 15, 7,	/*16-23 */
+	 7, 7, 1, 16, 16, 0, 0, 0};	/*24-31 */
+
+	if (cipval > 31)
+		cipval = 0;	/* .... */
+	return si[cipval];
+}
+
+static inline u8 cip2si2(u16 cipval)
+{
+	static const u8 si[32] =
+	{0, 0, 0, 0, 2, 3, 0, 0,	/*0-7 */
+	 0, 3, 0, 0, 0, 0, 0, 0,	/*8-15 */
+	 1, 2, 0, 0, 9, 0, 0, 0,	/*16-23 */
+	 0, 0, 3, 2, 3, 0, 0, 0};	/*24-31 */
+
+	if (cipval > 31)
+		cipval = 0;	/* .... */
+	return si[cipval];
+}
+
+
+/* -------- controller management ------------------------------------- */
+
+static inline capidrv_contr *findcontrbydriverid(int driverid)
+{
+    	unsigned long flags;
+	capidrv_contr *p;
+
+	spin_lock_irqsave(&global_lock, flags);
+	for (p = global.contr_list; p; p = p->next)
+		if (p->myid == driverid)
+			break;
+	spin_unlock_irqrestore(&global_lock, flags);
+	return p;
+}
+
+static capidrv_contr *findcontrbynumber(u32 contr)
+{
+	unsigned long flags;
+	capidrv_contr *p = global.contr_list;
+
+	spin_lock_irqsave(&global_lock, flags);
+	for (p = global.contr_list; p; p = p->next)
+		if (p->contrnr == contr)
+			break;
+	spin_unlock_irqrestore(&global_lock, flags);
+	return p;
+}
+
+
+/* -------- plci management ------------------------------------------ */
+
+static capidrv_plci *new_plci(capidrv_contr * card, int chan)
+{
+	capidrv_plci *plcip;
+
+	plcip = (capidrv_plci *) kmalloc(sizeof(capidrv_plci), GFP_ATOMIC);
+
+	if (plcip == 0)
+		return NULL;
+
+	memset(plcip, 0, sizeof(capidrv_plci));
+	plcip->state = ST_PLCI_NONE;
+	plcip->plci = 0;
+	plcip->msgid = 0;
+	plcip->chan = chan;
+	plcip->next = card->plci_list;
+	card->plci_list = plcip;
+	card->bchans[chan].plcip = plcip;
+
+	return plcip;
+}
+
+static capidrv_plci *find_plci_by_plci(capidrv_contr * card, u32 plci)
+{
+	capidrv_plci *p;
+	for (p = card->plci_list; p; p = p->next)
+		if (p->plci == plci)
+			return p;
+	return NULL;
+}
+
+static capidrv_plci *find_plci_by_msgid(capidrv_contr * card, u16 msgid)
+{
+	capidrv_plci *p;
+	for (p = card->plci_list; p; p = p->next)
+		if (p->msgid == msgid)
+			return p;
+	return NULL;
+}
+
+static capidrv_plci *find_plci_by_ncci(capidrv_contr * card, u32 ncci)
+{
+	capidrv_plci *p;
+	for (p = card->plci_list; p; p = p->next)
+		if (p->plci == (ncci & 0xffff))
+			return p;
+	return NULL;
+}
+
+static void free_plci(capidrv_contr * card, capidrv_plci * plcip)
+{
+	capidrv_plci **pp;
+
+	for (pp = &card->plci_list; *pp; pp = &(*pp)->next) {
+		if (*pp == plcip) {
+			*pp = (*pp)->next;
+			card->bchans[plcip->chan].plcip = NULL;
+			card->bchans[plcip->chan].disconnecting = 0;
+			card->bchans[plcip->chan].incoming = 0;
+			kfree(plcip);
+			return;
+		}
+	}
+	printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n",
+	       card->contrnr, plcip, plcip->plci);
+}
+
+/* -------- ncci management ------------------------------------------ */
+
+static inline capidrv_ncci *new_ncci(capidrv_contr * card,
+				     capidrv_plci * plcip,
+				     u32 ncci)
+{
+	capidrv_ncci *nccip;
+
+	nccip = (capidrv_ncci *) kmalloc(sizeof(capidrv_ncci), GFP_ATOMIC);
+
+	if (nccip == 0)
+		return NULL;
+
+	memset(nccip, 0, sizeof(capidrv_ncci));
+	nccip->ncci = ncci;
+	nccip->state = ST_NCCI_NONE;
+	nccip->plcip = plcip;
+	nccip->chan = plcip->chan;
+	nccip->datahandle = 0;
+
+	nccip->next = plcip->ncci_list;
+	plcip->ncci_list = nccip;
+
+	card->bchans[plcip->chan].nccip = nccip;
+
+	return nccip;
+}
+
+static inline capidrv_ncci *find_ncci(capidrv_contr * card, u32 ncci)
+{
+	capidrv_plci *plcip;
+	capidrv_ncci *p;
+
+	if ((plcip = find_plci_by_ncci(card, ncci)) == 0)
+		return NULL;
+
+	for (p = plcip->ncci_list; p; p = p->next)
+		if (p->ncci == ncci)
+			return p;
+	return NULL;
+}
+
+static inline capidrv_ncci *find_ncci_by_msgid(capidrv_contr * card,
+					       u32 ncci, u16 msgid)
+{
+	capidrv_plci *plcip;
+	capidrv_ncci *p;
+
+	if ((plcip = find_plci_by_ncci(card, ncci)) == 0)
+		return NULL;
+
+	for (p = plcip->ncci_list; p; p = p->next)
+		if (p->msgid == msgid)
+			return p;
+	return NULL;
+}
+
+static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip)
+{
+	struct capidrv_ncci **pp;
+
+	for (pp = &(nccip->plcip->ncci_list); *pp; pp = &(*pp)->next) {
+		if (*pp == nccip) {
+			*pp = (*pp)->next;
+			break;
+		}
+	}
+	card->bchans[nccip->chan].nccip = NULL;
+	kfree(nccip);
+}
+
+static int capidrv_add_ack(struct capidrv_ncci *nccip,
+		           u16 datahandle, int len)
+{
+	struct ncci_datahandle_queue *n, **pp;
+
+	n = (struct ncci_datahandle_queue *)
+		kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
+	if (!n) {
+	   printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n");
+	   return -1;
+	}
+	n->next = NULL;
+	n->datahandle = datahandle;
+	n->len = len;
+	for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ;
+	*pp = n;
+	return 0;
+}
+
+static int capidrv_del_ack(struct capidrv_ncci *nccip, u16 datahandle)
+{
+	struct ncci_datahandle_queue **pp, *p;
+	int len;
+
+	for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) {
+ 		if ((*pp)->datahandle == datahandle) {
+			p = *pp;
+			len = p->len;
+			*pp = (*pp)->next;
+		        kfree(p);
+			return len;
+		}
+	}
+	return -1;
+}
+
+/* -------- convert and send capi message ---------------------------- */
+
+static void send_message(capidrv_contr * card, _cmsg * cmsg)
+{
+	struct sk_buff *skb;
+	size_t len;
+	capi_cmsg2message(cmsg, cmsg->buf);
+	len = CAPIMSG_LEN(cmsg->buf);
+	skb = alloc_skb(len, GFP_ATOMIC);
+	memcpy(skb_put(skb, len), cmsg->buf, len);
+	if (capi20_put_message(&global.ap, skb) != CAPI_NOERROR)
+		kfree_skb(skb);
+}
+
+/* -------- state machine -------------------------------------------- */
+
+struct listenstatechange {
+	int actstate;
+	int nextstate;
+	int event;
+};
+
+static struct listenstatechange listentable[] =
+{
+  {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
+  {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
+  {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
+  {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
+  {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+  {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+  {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+  {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+  {},
+};
+
+static void listen_change_state(capidrv_contr * card, int event)
+{
+	struct listenstatechange *p = listentable;
+	while (p->event) {
+		if (card->state == p->actstate && p->event == event) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n",
+				       card->contrnr, card->state, p->nextstate);
+			card->state = p->nextstate;
+			return;
+		}
+		p++;
+	}
+	printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n",
+	       card->contrnr, card->state, event);
+
+}
+
+/* ------------------------------------------------------------------ */
+
+static void p0(capidrv_contr * card, capidrv_plci * plci)
+{
+	isdn_ctrl cmd;
+
+	card->bchans[plci->chan].contr = NULL;
+	cmd.command = ISDN_STAT_DHUP;
+	cmd.driver = card->myid;
+	cmd.arg = plci->chan;
+	card->interface.statcallb(&cmd);
+	free_plci(card, plci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct plcistatechange {
+	int actstate;
+	int nextstate;
+	int event;
+	void (*changefunc) (capidrv_contr * card, capidrv_plci * plci);
+};
+
+static struct plcistatechange plcitable[] =
+{
+  /* P-0 */
+  {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, NULL},
+  {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, NULL},
+  {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, NULL},
+  {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, NULL},
+  /* P-0.1 */
+  {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
+  {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, NULL},
+  /* P-1 */
+  {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+  {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+  {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+  {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+  /* P-ACT */
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+  {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, NULL},
+  {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, NULL},
+  /* P-2 */
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL},
+  {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, NULL},
+  {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, NULL},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+  {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, NULL},
+  /* P-3 */
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+  {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+  /* P-4 */
+  {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+  {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+  {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+  {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+  /* P-5 */
+  {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+  /* P-6 */
+  {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
+  /* P-0.Res */
+  {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0},
+  {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, NULL},
+  /* P-RES */
+  {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, NULL},
+  /* P-HELD */
+  {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, NULL},
+  {},
+};
+
+static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event)
+{
+	struct plcistatechange *p = plcitable;
+	while (p->event) {
+		if (plci->state == p->actstate && p->event == event) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n",
+				  card->contrnr, plci->plci, plci->state, p->nextstate);
+			plci->state = p->nextstate;
+			if (p->changefunc)
+				p->changefunc(card, plci);
+			return;
+		}
+		p++;
+	}
+	printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n",
+	       card->contrnr, plci->plci, plci->state, event);
+}
+
+/* ------------------------------------------------------------------ */
+
+static _cmsg cmsg;
+
+static void n0(capidrv_contr * card, capidrv_ncci * ncci)
+{
+	isdn_ctrl cmd;
+
+	capi_fill_DISCONNECT_REQ(&cmsg,
+				 global.ap.applid,
+				 card->msgid++,
+				 ncci->plcip->plci,
+				 NULL,	/* BChannelinformation */
+				 NULL,	/* Keypadfacility */
+				 NULL,	/* Useruserdata */   /* $$$$ */
+				 NULL	/* Facilitydataarray */
+	);
+	send_message(card, &cmsg);
+	plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ);
+
+	cmd.command = ISDN_STAT_BHUP;
+	cmd.driver = card->myid;
+	cmd.arg = ncci->chan;
+	card->interface.statcallb(&cmd);
+	free_ncci(card, ncci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct nccistatechange {
+	int actstate;
+	int nextstate;
+	int event;
+	void (*changefunc) (capidrv_contr * card, capidrv_ncci * ncci);
+};
+
+static struct nccistatechange nccitable[] =
+{
+  /* N-0 */
+  {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, NULL},
+  {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, NULL},
+  /* N-0.1 */
+  {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, NULL},
+  {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0},
+  /* N-1 */
+  {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, NULL},
+  {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, NULL},
+  {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+  {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+  /* N-2 */
+  {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, NULL},
+  {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+  {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+  /* N-ACT */
+  {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL},
+  {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, NULL},
+  {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+  {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+  /* N-3 */
+  {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL},
+  {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+  {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+  /* N-4 */
+  {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+  {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,NULL},
+  /* N-5 */
+  {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
+  {},
+};
+
+static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event)
+{
+	struct nccistatechange *p = nccitable;
+	while (p->event) {
+		if (ncci->state == p->actstate && p->event == event) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n",
+				  card->contrnr, ncci->ncci, ncci->state, p->nextstate);
+			if (p->nextstate == ST_NCCI_PREVIOUS) {
+				ncci->state = ncci->oldstate;
+				ncci->oldstate = p->actstate;
+			} else {
+				ncci->oldstate = p->actstate;
+				ncci->state = p->nextstate;
+			}
+			if (p->changefunc)
+				p->changefunc(card, ncci);
+			return;
+		}
+		p++;
+	}
+	printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n",
+	       card->contrnr, ncci->ncci, ncci->state, event);
+}
+
+/* ------------------------------------------------------------------- */
+
+static inline int new_bchan(capidrv_contr * card)
+{
+	int i;
+	for (i = 0; i < card->nbchan; i++) {
+		if (card->bchans[i].plcip == 0) {
+			card->bchans[i].disconnecting = 0;
+			return i;
+		}
+	}
+	return -1;
+}
+
+/* ------------------------------------------------------------------- */
+
+static void handle_controller(_cmsg * cmsg)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		return;
+	}
+	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+	case CAPI_LISTEN_CONF:	/* Controller */
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
+			       card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
+		if (cmsg->Info) {
+			listen_change_state(card, EV_LISTEN_CONF_ERROR);
+		} else if (card->cipmask == 0) {
+			listen_change_state(card, EV_LISTEN_CONF_EMPTY);
+		} else {
+			listen_change_state(card, EV_LISTEN_CONF_OK);
+		}
+		break;
+
+	case CAPI_MANUFACTURER_IND:	/* Controller */
+		if (   cmsg->ManuID == 0x214D5641
+		    && cmsg->Class == 0
+		    && cmsg->Function == 1) {
+		   u8  *data = cmsg->ManuData+3;
+		   u16  len = cmsg->ManuData[0];
+		   u16 layer;
+		   int direction;
+		   if (len == 255) {
+		      len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8));
+		      data += 2;
+		   }
+		   len -= 2;
+		   layer = ((*(data-1)) << 8) | *(data-2);
+		   if (layer & 0x300)
+			direction = (layer & 0x200) ? 0 : 1;
+		   else direction = (layer & 0x800) ? 0 : 1;
+		   if (layer & 0x0C00) {
+		   	if ((layer & 0xff) == 0x80) {
+		           handle_dtrace_data(card, direction, 1, data, len);
+		           break;
+		   	}
+		   } else if ((layer & 0xff) < 0x80) {
+		      handle_dtrace_data(card, direction, 0, data, len);
+		      break;
+		   }
+	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n",
+                        card->contrnr, 
+			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			cmsg->adr.adrController, layer);
+                   break;
+		}
+		goto ignored;
+	case CAPI_MANUFACTURER_CONF:	/* Controller */
+		if (cmsg->ManuID == 0x214D5641) {
+		   char *s = NULL;
+		   switch (cmsg->Class) {
+		      case 0: break;
+		      case 1: s = "unknown class"; break;
+		      case 2: s = "unknown function"; break;
+		      default: s = "unkown error"; break;
+		   }
+		   if (s)
+	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
+			card->contrnr,
+			capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			cmsg->adr.adrController,
+			cmsg->Function, s);
+		   break;
+		}
+		goto ignored;
+	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_INFO_IND:	/* Controller/plci */
+		goto ignored;
+	case CAPI_INFO_CONF:	/* Controller/plci */
+		goto ignored;
+
+	default:
+		printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???",
+		       card->contrnr,
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController);
+	}
+	return;
+
+      ignored:
+	printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n",
+	       card->contrnr,
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrController);
+}
+
+static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg)
+{
+	capidrv_plci *plcip;
+	capidrv_bchan *bchan;
+	isdn_ctrl cmd;
+	int chan;
+
+	if ((chan = new_bchan(card)) == -1) {
+		printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr);
+		return;
+	}
+	bchan = &card->bchans[chan];
+	if ((plcip = new_plci(card, chan)) == 0) {
+		printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr);
+		return;
+	}
+	bchan->incoming = 1;
+	plcip->plci = cmsg->adr.adrPLCI;
+	plci_change_state(card, plcip, EV_PLCI_CONNECT_IND);
+
+	cmd.command = ISDN_STAT_ICALL;
+	cmd.driver = card->myid;
+	cmd.arg = chan;
+	memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup));
+	strncpy(cmd.parm.setup.phone,
+	        cmsg->CallingPartyNumber + 3,
+		cmsg->CallingPartyNumber[0] - 2);
+	strncpy(cmd.parm.setup.eazmsn,
+	        cmsg->CalledPartyNumber + 2,
+		cmsg->CalledPartyNumber[0] - 1);
+	cmd.parm.setup.si1 = cip2si1(cmsg->CIPValue);
+	cmd.parm.setup.si2 = cip2si2(cmsg->CIPValue);
+	cmd.parm.setup.plan = cmsg->CallingPartyNumber[1];
+	cmd.parm.setup.screen = cmsg->CallingPartyNumber[2];
+
+	printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n", 
+			card->contrnr,
+			cmd.parm.setup.phone,
+			cmd.parm.setup.si1,
+			cmd.parm.setup.si2,
+			cmd.parm.setup.eazmsn);
+
+	if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) {
+		printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n", 
+			card->contrnr,
+			cmd.parm.setup.si2);
+		cmd.parm.setup.si2 = 0;
+	}
+
+	switch (card->interface.statcallb(&cmd)) {
+	case 0:
+	case 3:
+		/* No device matching this call.
+		 * and isdn_common.c has send a HANGUP command
+		 * which is ignored in state ST_PLCI_INCOMING,
+		 * so we send RESP to ignore the call
+		 */
+		capi_cmsg_answer(cmsg);
+		cmsg->Reject = 1;	/* ignore */
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+		printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n",
+			card->contrnr,
+			cmd.parm.setup.phone,
+			cmd.parm.setup.si1,
+			cmd.parm.setup.si2,
+			cmd.parm.setup.eazmsn);
+		break;
+	case 1:
+		/* At least one device matching this call (RING on ttyI)
+		 * HL-driver may send ALERTING on the D-channel in this
+		 * case.
+		 * really means: RING on ttyI or a net interface
+		 * accepted this call already.
+		 *
+		 * If the call was accepted, state has already changed,
+		 * and CONNECT_RESP already sent.
+		 */
+		if (plcip->state == ST_PLCI_INCOMING) {
+			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n",
+				card->contrnr,
+				cmd.parm.setup.phone,
+				cmd.parm.setup.si1,
+				cmd.parm.setup.si2,
+				cmd.parm.setup.eazmsn);
+			capi_fill_ALERT_REQ(cmsg,
+					    global.ap.applid,
+					    card->msgid++,
+					    plcip->plci,	/* adr */
+					    NULL,/* BChannelinformation */
+					    NULL,/* Keypadfacility */
+					    NULL,/* Useruserdata */
+					    NULL /* Facilitydataarray */
+			);
+			plcip->msgid = cmsg->Messagenumber;
+			send_message(card, cmsg);
+		} else {
+			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n",
+				card->contrnr,
+				cmd.parm.setup.phone,
+				cmd.parm.setup.si1,
+				cmd.parm.setup.si2,
+				cmd.parm.setup.eazmsn);
+		}
+		break;
+
+	case 2:		/* Call will be rejected. */
+		capi_cmsg_answer(cmsg);
+		cmsg->Reject = 2;	/* reject call, normal call clearing */
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+		break;
+
+	default:
+		/* An error happened. (Invalid parameters for example.) */
+		capi_cmsg_answer(cmsg);
+		cmsg->Reject = 8;	/* reject call,
+					   destination out of order */
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+		break;
+	}
+	return;
+}
+
+static void handle_plci(_cmsg * cmsg)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+	capidrv_plci *plcip;
+	isdn_ctrl cmd;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		return;
+	}
+	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+	case CAPI_DISCONNECT_IND:	/* plci */
+		if (cmsg->Reason) {
+			printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI);
+		}
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) {
+			capi_cmsg_answer(cmsg);
+			send_message(card, cmsg);
+			goto notfound;
+		}
+		card->bchans[plcip->chan].disconnecting = 1;
+		plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND);
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP);
+		break;
+
+	case CAPI_DISCONNECT_CONF:	/* plci */
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrPLCI);
+		}
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+			goto notfound;
+
+		card->bchans[plcip->chan].disconnecting = 1;
+		break;
+
+	case CAPI_ALERT_CONF:	/* plci */
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrPLCI);
+		}
+		break;
+
+	case CAPI_CONNECT_IND:	/* plci */
+		handle_incoming_call(card, cmsg);
+		break;
+
+	case CAPI_CONNECT_CONF:	/* plci */
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+			   card->contrnr,
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrPLCI);
+		}
+		if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber)))
+			goto notfound;
+
+		plcip->plci = cmsg->adr.adrPLCI;
+		if (cmsg->Info) {
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR);
+		} else {
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK);
+		}
+		break;
+
+	case CAPI_CONNECT_ACTIVE_IND:	/* plci */
+
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+			goto notfound;
+
+		if (card->bchans[plcip->chan].incoming) {
+			capi_cmsg_answer(cmsg);
+			send_message(card, cmsg);
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+		} else {
+			capidrv_ncci *nccip;
+			capi_cmsg_answer(cmsg);
+			send_message(card, cmsg);
+
+			nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI);
+
+			if (!nccip) {
+				printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
+				break;	/* $$$$ */
+			}
+			capi_fill_CONNECT_B3_REQ(cmsg,
+						 global.ap.applid,
+						 card->msgid++,
+						 plcip->plci,	/* adr */
+						 NULL	/* NCPI */
+			);
+			nccip->msgid = cmsg->Messagenumber;
+			send_message(card, cmsg);
+			cmd.command = ISDN_STAT_DCONN;
+			cmd.driver = card->myid;
+			cmd.arg = plcip->chan;
+			card->interface.statcallb(&cmd);
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ);
+		}
+		break;
+
+	case CAPI_INFO_IND:	/* Controller/plci */
+
+		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+			goto notfound;
+
+		if (cmsg->InfoNumber == 0x4000) {
+			if (cmsg->InfoElement[0] == 4) {
+				cmd.command = ISDN_STAT_CINF;
+				cmd.driver = card->myid;
+				cmd.arg = plcip->chan;
+				sprintf(cmd.parm.num, "%lu",
+					(unsigned long)
+					((u32) cmsg->InfoElement[1]
+				  | ((u32) (cmsg->InfoElement[2]) << 8)
+				 | ((u32) (cmsg->InfoElement[3]) << 16)
+					 | ((u32) (cmsg->InfoElement[4]) << 24)));
+				card->interface.statcallb(&cmd);
+				break;
+			}
+		}
+		printk(KERN_ERR "capidrv-%d: %s\n",
+				card->contrnr, capi_cmsg2str(cmsg));
+		break;
+
+	case CAPI_CONNECT_ACTIVE_CONF:		/* plci */
+		goto ignored;
+	case CAPI_SELECT_B_PROTOCOL_CONF:	/* plci */
+		goto ignored;
+	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */
+		goto ignored;
+
+	case CAPI_INFO_CONF:	/* Controller/plci */
+		goto ignored;
+
+	default:
+		printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???",
+		       card->contrnr,
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrPLCI);
+	}
+	return;
+      ignored:
+	printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n",
+	       card->contrnr,
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrPLCI);
+	return;
+      notfound:
+	printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n",
+	       card->contrnr,
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrPLCI);
+	return;
+}
+
+static void handle_ncci(_cmsg * cmsg)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+	capidrv_plci *plcip;
+	capidrv_ncci *nccip;
+	isdn_ctrl cmd;
+	int len;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		return;
+	}
+	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+	case CAPI_CONNECT_B3_ACTIVE_IND:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND);
+
+		cmd.command = ISDN_STAT_BCONN;
+		cmd.driver = card->myid;
+		cmd.arg = nccip->chan;
+		card->interface.statcallb(&cmd);
+
+		printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n",
+		       card->contrnr, nccip->chan, nccip->ncci);
+		break;
+
+	case CAPI_CONNECT_B3_ACTIVE_CONF:	/* ncci */
+		goto ignored;
+
+	case CAPI_CONNECT_B3_IND:	/* ncci */
+
+		plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI);
+		if (plcip) {
+			nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI);
+			if (nccip) {
+				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND);
+				capi_fill_CONNECT_B3_RESP(cmsg,
+							  global.ap.applid,
+							  card->msgid++,
+							  nccip->ncci,	/* adr */
+							  0,	/* Reject */
+							  NULL	/* NCPI */
+				);
+				send_message(card, cmsg);
+				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP);
+				break;
+			}
+			printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n",							card->contrnr);
+		} else {
+			printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n",
+			   card->contrnr,
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->adr.adrNCCI);
+		}
+		capi_fill_CONNECT_B3_RESP(cmsg,
+					  global.ap.applid,
+					  card->msgid++,
+					  cmsg->adr.adrNCCI,
+					  2,	/* Reject */
+					  NULL	/* NCPI */
+		);
+		send_message(card, cmsg);
+		break;
+
+	case CAPI_CONNECT_B3_CONF:	/* ncci */
+
+		if (!(nccip = find_ncci_by_msgid(card,
+						 cmsg->adr.adrNCCI,
+						 cmsg->Messagenumber)))
+			goto notfound;
+
+		nccip->ncci = cmsg->adr.adrNCCI;
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+			   card->contrnr,
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrNCCI);
+		}
+
+		if (cmsg->Info)
+			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR);
+		else
+			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK);
+		break;
+
+	case CAPI_CONNECT_B3_T90_ACTIVE_IND:	/* ncci */
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		break;
+
+	case CAPI_DATA_B3_IND:	/* ncci */
+		/* handled in handle_data() */
+		goto ignored;
+
+	case CAPI_DATA_B3_CONF:	/* ncci */
+		if (cmsg->Info) {
+			printk(KERN_WARNING "CAPI_DATA_B3_CONF: Info %x - %s\n",
+				cmsg->Info, capi_info2str(cmsg->Info));
+		}
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+
+		len = capidrv_del_ack(nccip, cmsg->DataHandle);
+		if (len < 0)
+			break;
+	        cmd.command = ISDN_STAT_BSENT;
+	        cmd.driver = card->myid;
+	        cmd.arg = nccip->chan;
+		cmd.parm.length = len;
+	        card->interface.statcallb(&cmd);
+		break;
+
+	case CAPI_DISCONNECT_B3_IND:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+
+		card->bchans[nccip->chan].disconnecting = 1;
+		ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND);
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP);
+		break;
+
+	case CAPI_DISCONNECT_B3_CONF:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+		if (cmsg->Info) {
+			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+			   card->contrnr,
+			   capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+			       cmsg->Info, capi_info2str(cmsg->Info), 
+			       cmsg->adr.adrNCCI);
+			ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR);
+		}
+		break;
+
+	case CAPI_RESET_B3_IND:	/* ncci */
+		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+			goto notfound;
+		ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND);
+		capi_cmsg_answer(cmsg);
+		send_message(card, cmsg);
+		break;
+
+	case CAPI_RESET_B3_CONF:	/* ncci */
+		goto ignored;	/* $$$$ */
+
+	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */
+		goto ignored;
+	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */
+		goto ignored;
+
+	default:
+		printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???",
+		       card->contrnr,
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrNCCI);
+	}
+	return;
+      ignored:
+	printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n",
+	       card->contrnr,
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrNCCI);
+	return;
+      notfound:
+	printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+	       card->contrnr,
+	       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+	       cmsg->adr.adrNCCI);
+}
+
+
+static void handle_data(_cmsg * cmsg, struct sk_buff *skb)
+{
+	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+	capidrv_ncci *nccip;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrController & 0x7f);
+		kfree_skb(skb);
+		return;
+	}
+	if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) {
+		printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+		       card->contrnr,
+		       capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+		       cmsg->adr.adrNCCI);
+		kfree_skb(skb);
+		return;
+	}
+	(void) skb_pull(skb, CAPIMSG_LEN(skb->data));
+	card->interface.rcvcallb_skb(card->myid, nccip->chan, skb);
+	capi_cmsg_answer(cmsg);
+	send_message(card, cmsg);
+}
+
+static _cmsg s_cmsg;
+
+static void capidrv_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+	capi_message2cmsg(&s_cmsg, skb->data);
+	if (debugmode > 3)
+		printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n",
+		       ap->applid, capi_cmsg2str(&s_cmsg));
+	
+	if (s_cmsg.Command == CAPI_DATA_B3
+	    && s_cmsg.Subcommand == CAPI_IND) {
+		handle_data(&s_cmsg, skb);
+		return;
+	}
+	if ((s_cmsg.adr.adrController & 0xffffff00) == 0)
+		handle_controller(&s_cmsg);
+	else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0)
+		handle_plci(&s_cmsg);
+	else
+		handle_ncci(&s_cmsg);
+	/*
+	 * data of skb used in s_cmsg,
+	 * free data when s_cmsg is not used again
+	 * thanks to Lars Heete <hel@admin.de>
+	 */
+	kfree_skb(skb);
+}
+
+/* ------------------------------------------------------------------- */
+
+#define PUTBYTE_TO_STATUS(card, byte) \
+	do { \
+		*(card)->q931_write++ = (byte); \
+        	if ((card)->q931_write > (card)->q931_end) \
+	  		(card)->q931_write = (card)->q931_buf; \
+	} while (0)
+
+static void handle_dtrace_data(capidrv_contr *card,
+			     int send, int level2, u8 *data, u16 len)
+{
+    	u8 *p, *end;
+    	isdn_ctrl cmd;
+
+    	if (!len) {
+		printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
+				card->contrnr, len);
+		return;
+	}
+
+	if (level2) {
+		PUTBYTE_TO_STATUS(card, 'D');
+		PUTBYTE_TO_STATUS(card, '2');
+        	PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        	PUTBYTE_TO_STATUS(card, ':');
+	} else {
+        	PUTBYTE_TO_STATUS(card, 'D');
+        	PUTBYTE_TO_STATUS(card, '3');
+        	PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+        	PUTBYTE_TO_STATUS(card, ':');
+    	}
+
+	for (p = data, end = data+len; p < end; p++) {
+		u8 w;
+		PUTBYTE_TO_STATUS(card, ' ');
+		w = (*p >> 4) & 0xf;
+		PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+		w = *p & 0xf;
+		PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+	}
+	PUTBYTE_TO_STATUS(card, '\n');
+
+	cmd.command = ISDN_STAT_STAVAIL;
+	cmd.driver = card->myid;
+	cmd.arg = len*3+5;
+	card->interface.statcallb(&cmd);
+}
+
+/* ------------------------------------------------------------------- */
+
+static _cmsg cmdcmsg;
+
+static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card)
+{
+	switch (c->arg) {
+	case 1:
+		debugmode = (int)(*((unsigned int *)c->parm.num));
+		printk(KERN_DEBUG "capidrv-%d: debugmode=%d\n",
+				card->contrnr, debugmode);
+		return 0;
+	default:
+		printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n",
+				card->contrnr, c->arg);
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+/*
+ * Handle leased lines (CAPI-Bundling)
+ */
+
+struct internal_bchannelinfo {
+   unsigned short channelalloc;
+   unsigned short operation;
+   unsigned char  cmask[31];
+};
+
+static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep)
+{
+	unsigned long bmask = 0;
+	int active = !0;
+	char *s;
+	int i;
+
+	if (strncmp(teln, "FV:", 3) != 0)
+		return 1;
+	s = teln + 3;
+	while (*s && *s == ' ') s++;
+	if (!*s) return -2;
+	if (*s == 'p' || *s == 'P') {
+		active = 0;
+		s++;
+	}
+	if (*s == 'a' || *s == 'A') {
+		active = !0;
+		s++;
+	}
+	while (*s) {
+		int digit1 = 0;
+		int digit2 = 0;
+		if (!isdigit(*s)) return -3;
+		while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; }
+		if (digit1 <= 0 && digit1 > 30) return -4;
+		if (*s == 0 || *s == ',' || *s == ' ') {
+			bmask |= (1 << digit1);
+			digit1 = 0;
+			if (*s) s++;
+			continue;
+		}
+		if (*s != '-') return -5;
+		s++;
+		if (!isdigit(*s)) return -3;
+		while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; }
+		if (digit2 <= 0 && digit2 > 30) return -4;
+		if (*s == 0 || *s == ',' || *s == ' ') {
+			if (digit1 > digit2)
+				for (i = digit2; i <= digit1 ; i++)
+					bmask |= (1 << i);
+			else 
+				for (i = digit1; i <= digit2 ; i++)
+					bmask |= (1 << i);
+			digit1 = digit2 = 0;
+			if (*s) s++;
+			continue;
+		}
+		return -6;
+	}
+	if (activep) *activep = active;
+	if (bmaskp) *bmaskp = bmask;
+	return 0;
+}
+
+static int FVteln2capi20(char *teln, u8 AdditionalInfo[1+2+2+31])
+{
+	unsigned long bmask;
+	int active;
+	int rc, i;
+   
+	rc = decodeFVteln(teln, &bmask, &active);
+	if (rc) return rc;
+	/* Length */
+	AdditionalInfo[0] = 2+2+31;
+        /* Channel: 3 => use channel allocation */
+        AdditionalInfo[1] = 3; AdditionalInfo[2] = 0;
+	/* Operation: 0 => DTE mode, 1 => DCE mode */
+        if (active) {
+   		AdditionalInfo[3] = 0; AdditionalInfo[4] = 0;
+   	} else {
+   		AdditionalInfo[3] = 1; AdditionalInfo[4] = 0;
+	}
+	/* Channel mask array */
+	AdditionalInfo[5] = 0; /* no D-Channel */
+	for (i=1; i <= 30; i++)
+		AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0;
+	return 0;
+}
+
+static int capidrv_command(isdn_ctrl * c, capidrv_contr * card)
+{
+	isdn_ctrl cmd;
+	struct capidrv_bchan *bchan;
+	struct capidrv_plci *plcip;
+	u8 AdditionalInfo[1+2+2+31];
+        int rc, isleasedline = 0;
+
+	if (c->command == ISDN_CMD_IOCTL)
+		return capidrv_ioctl(c, card);
+
+	switch (c->command) {
+	case ISDN_CMD_DIAL:{
+			u8 calling[ISDN_MSNLEN + 3];
+			u8 called[ISDN_MSNLEN + 2];
+
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+					card->contrnr,
+					c->arg,
+				        c->parm.setup.phone,
+				        c->parm.setup.si1,
+				        c->parm.setup.si2,
+				        c->parm.setup.eazmsn);
+
+			bchan = &card->bchans[c->arg % card->nbchan];
+
+			if (bchan->plcip) {
+				printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+					card->contrnr,
+			        	c->arg, 
+				        c->parm.setup.phone,
+				        c->parm.setup.si1,
+				        c->parm.setup.si2,
+				        c->parm.setup.eazmsn,
+				        bchan->plcip->plci);
+				return 0;
+			}
+			bchan->si1 = c->parm.setup.si1;
+			bchan->si2 = c->parm.setup.si2;
+
+			strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num));
+			strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum));
+                        rc = FVteln2capi20(bchan->num, AdditionalInfo);
+			isleasedline = (rc == 0);
+			if (rc < 0)
+				printk(KERN_ERR "capidrv-%d: WARNING: invalid leased linedefinition \"%s\"\n", card->contrnr, bchan->num);
+
+			if (isleasedline) {
+				calling[0] = 0;
+				called[0] = 0;
+			        if (debugmode)
+					printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr);
+			} else {
+		        	calling[0] = strlen(bchan->mynum) + 2;
+		        	calling[1] = 0;
+		     		calling[2] = 0x80;
+			   	strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN);
+				called[0] = strlen(bchan->num) + 1;
+				called[1] = 0x80;
+				strncpy(called + 2, bchan->num, ISDN_MSNLEN);
+			}
+
+			capi_fill_CONNECT_REQ(&cmdcmsg,
+					      global.ap.applid,
+					      card->msgid++,
+					      card->contrnr,	/* adr */
+					  si2cip(bchan->si1, bchan->si2),	/* cipvalue */
+					      called,	/* CalledPartyNumber */
+					      calling,	/* CallingPartyNumber */
+					      NULL,	/* CalledPartySubaddress */
+					      NULL,	/* CallingPartySubaddress */
+					    b1prot(bchan->l2, bchan->l3),	/* B1protocol */
+					    b2prot(bchan->l2, bchan->l3),	/* B2protocol */
+					    b3prot(bchan->l2, bchan->l3),	/* B3protocol */
+					    b1config(bchan->l2, bchan->l3),	/* B1configuration */
+					      NULL,	/* B2configuration */
+					      NULL,	/* B3configuration */
+					      NULL,	/* BC */
+					      NULL,	/* LLC */
+					      NULL,	/* HLC */
+					      /* BChannelinformation */
+					      isleasedline ? AdditionalInfo : NULL,
+					      NULL,	/* Keypadfacility */
+					      NULL,	/* Useruserdata */
+					      NULL	/* Facilitydataarray */
+			    );
+			if ((plcip = new_plci(card, (c->arg % card->nbchan))) == 0) {
+				cmd.command = ISDN_STAT_DHUP;
+				cmd.driver = card->myid;
+				cmd.arg = (c->arg % card->nbchan);
+				card->interface.statcallb(&cmd);
+				return -1;
+			}
+			plcip->msgid = cmdcmsg.Messagenumber;
+			plcip->leasedline = isleasedline;
+			plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ);
+			send_message(card, &cmdcmsg);
+			return 0;
+		}
+
+	case ISDN_CMD_ACCEPTD:
+
+		bchan = &card->bchans[c->arg % card->nbchan];
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n",
+			       card->contrnr,
+			       c->arg, bchan->l2, bchan->l3);
+
+		capi_fill_CONNECT_RESP(&cmdcmsg,
+				       global.ap.applid,
+				       card->msgid++,
+				       bchan->plcip->plci,	/* adr */
+				       0,	/* Reject */
+				       b1prot(bchan->l2, bchan->l3),	/* B1protocol */
+				       b2prot(bchan->l2, bchan->l3),	/* B2protocol */
+				       b3prot(bchan->l2, bchan->l3),	/* B3protocol */
+				       b1config(bchan->l2, bchan->l3),	/* B1configuration */
+				       NULL,	/* B2configuration */
+				       NULL,	/* B3configuration */
+				       NULL,	/* ConnectedNumber */
+				       NULL,	/* ConnectedSubaddress */
+				       NULL,	/* LLC */
+				       NULL,	/* BChannelinformation */
+				       NULL,	/* Keypadfacility */
+				       NULL,	/* Useruserdata */
+				       NULL	/* Facilitydataarray */
+		);
+		capi_cmsg2message(&cmdcmsg, cmdcmsg.buf);
+		plci_change_state(card, bchan->plcip, EV_PLCI_CONNECT_RESP);
+		send_message(card, &cmdcmsg);
+		return 0;
+
+	case ISDN_CMD_ACCEPTB:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+			       card->contrnr,
+			       c->arg);
+		return -ENOSYS;
+
+	case ISDN_CMD_HANGUP:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n",
+			       card->contrnr,
+			       c->arg);
+		bchan = &card->bchans[c->arg % card->nbchan];
+
+		if (bchan->disconnecting) {
+			if (debugmode)
+				printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n",
+				       card->contrnr,
+				       c->arg);
+			return 0;
+		}
+		if (bchan->nccip) {
+			bchan->disconnecting = 1;
+			capi_fill_DISCONNECT_B3_REQ(&cmdcmsg,
+						    global.ap.applid,
+						    card->msgid++,
+						    bchan->nccip->ncci,
+						    NULL	/* NCPI */
+			);
+			ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ);
+			send_message(card, &cmdcmsg);
+			return 0;
+		} else if (bchan->plcip) {
+			if (bchan->plcip->state == ST_PLCI_INCOMING) {
+				/*
+				 * just ignore, we a called from
+				 * isdn_status_callback(),
+				 * which will return 0 or 2, this is handled
+				 * by the CONNECT_IND handler
+				 */
+				bchan->disconnecting = 1;
+				return 0;
+			} else if (bchan->plcip->plci) {
+				bchan->disconnecting = 1;
+				capi_fill_DISCONNECT_REQ(&cmdcmsg,
+							 global.ap.applid,
+							 card->msgid++,
+						      bchan->plcip->plci,
+							 NULL,	/* BChannelinformation */
+							 NULL,	/* Keypadfacility */
+							 NULL,	/* Useruserdata */
+							 NULL	/* Facilitydataarray */
+				);
+				plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ);
+				send_message(card, &cmdcmsg);
+				return 0;
+			} else {
+				printk(KERN_ERR "capidrv-%d: chan %ld disconnect request while waiting for CONNECT_CONF\n",
+				       card->contrnr,
+				       c->arg);
+				return -EINVAL;
+			}
+		}
+		printk(KERN_ERR "capidrv-%d: chan %ld disconnect request on free channel\n",
+				       card->contrnr,
+				       c->arg);
+		return -EINVAL;
+/* ready */
+
+	case ISDN_CMD_SETL2:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n",
+			       card->contrnr,
+			       (c->arg & 0xff), (c->arg >> 8));
+		bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
+		bchan->l2 = (c->arg >> 8);
+		return 0;
+
+	case ISDN_CMD_SETL3:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n",
+			       card->contrnr,
+			       (c->arg & 0xff), (c->arg >> 8));
+		bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
+		bchan->l3 = (c->arg >> 8);
+		return 0;
+
+	case ISDN_CMD_SETEAZ:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n",
+			       card->contrnr,
+			       c->parm.num, c->arg);
+		bchan = &card->bchans[c->arg % card->nbchan];
+		strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN);
+		return 0;
+
+	case ISDN_CMD_CLREAZ:
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n",
+					card->contrnr, c->arg);
+		bchan = &card->bchans[c->arg % card->nbchan];
+		bchan->msn[0] = 0;
+		return 0;
+
+	default:
+		printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n",
+					card->contrnr, c->command);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int if_command(isdn_ctrl * c)
+{
+	capidrv_contr *card = findcontrbydriverid(c->driver);
+
+	if (card)
+		return capidrv_command(c, card);
+
+	printk(KERN_ERR
+	     "capidrv: if_command %d called with invalid driverId %d!\n",
+						c->command, c->driver);
+	return -ENODEV;
+}
+
+static _cmsg sendcmsg;
+
+static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb)
+{
+	capidrv_contr *card = findcontrbydriverid(id);
+	capidrv_bchan *bchan;
+	capidrv_ncci *nccip;
+	int len = skb->len;
+	int msglen;
+	u16 errcode;
+	u16 datahandle;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
+		       id);
+		return 0;
+	}
+	if (debugmode > 4)
+		printk(KERN_DEBUG "capidrv-%d: sendbuf len=%d skb=%p doack=%d\n",
+					card->contrnr, len, skb, doack);
+	bchan = &card->bchans[channel % card->nbchan];
+	nccip = bchan->nccip;
+	if (!nccip || nccip->state != ST_NCCI_ACTIVE) {
+		printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n",
+		       card->contrnr, card->name, channel);
+		return 0;
+	}
+	datahandle = nccip->datahandle;
+	capi_fill_DATA_B3_REQ(&sendcmsg, global.ap.applid, card->msgid++,
+			      nccip->ncci,	/* adr */
+			      (u32) skb->data,	/* Data */
+			      skb->len,		/* DataLength */
+			      datahandle,	/* DataHandle */
+			      0	/* Flags */
+	    );
+
+	if (capidrv_add_ack(nccip, datahandle, doack ? (int)skb->len : -1) < 0)
+	   return 0;
+
+	capi_cmsg2message(&sendcmsg, sendcmsg.buf);
+	msglen = CAPIMSG_LEN(sendcmsg.buf);
+	if (skb_headroom(skb) < msglen) {
+		struct sk_buff *nskb = skb_realloc_headroom(skb, msglen);
+		if (!nskb) {
+			printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n",
+				card->contrnr);
+		        (void)capidrv_del_ack(nccip, datahandle);
+			return 0;
+		}
+		printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom, need %d\n",
+		       card->contrnr, skb_headroom(skb), msglen);
+		memcpy(skb_push(nskb, msglen), sendcmsg.buf, msglen);
+		errcode = capi20_put_message(&global.ap, nskb);
+		if (errcode == CAPI_NOERROR) {
+			dev_kfree_skb(skb);
+			nccip->datahandle++;
+			return len;
+		}
+		if (debugmode > 3)
+			printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n",
+				card->contrnr, errcode, capi_info2str(errcode));
+	        (void)capidrv_del_ack(nccip, datahandle);
+	        dev_kfree_skb(nskb);
+		return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
+	} else {
+		memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen);
+		errcode = capi20_put_message(&global.ap, skb);
+		if (errcode == CAPI_NOERROR) {
+			nccip->datahandle++;
+			return len;
+		}
+		if (debugmode > 3)
+			printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n",
+				card->contrnr, errcode, capi_info2str(errcode));
+		skb_pull(skb, msglen);
+	        (void)capidrv_del_ack(nccip, datahandle);
+		return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
+	}
+}
+
+static int if_readstat(u8 __user *buf, int len, int id, int channel)
+{
+	capidrv_contr *card = findcontrbydriverid(id);
+	int count;
+	u8 __user *p;
+
+	if (!card) {
+		printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n",
+		       id);
+		return -ENODEV;
+	}
+
+	for (p=buf, count=0; count < len; p++, count++) {
+		put_user(*card->q931_read++, p);
+	        if (card->q931_read > card->q931_end)
+	                card->q931_read = card->q931_buf;
+	}
+	return count;
+
+}
+
+static void enable_dchannel_trace(capidrv_contr *card)
+{
+        u8 manufacturer[CAPI_MANUFACTURER_LEN];
+        capi_version version;
+	u16 contr = card->contrnr;
+	u16 errcode;
+	u16 avmversion[3];
+
+        errcode = capi20_get_manufacturer(contr, manufacturer);
+        if (errcode != CAPI_NOERROR) {
+	   printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n",
+			card->name, errcode);
+	   return;
+	}
+	if (strstr(manufacturer, "AVM") == 0) {
+	   printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n",
+			card->name, manufacturer);
+	   return;
+	}
+        errcode = capi20_get_version(contr, &version);
+        if (errcode != CAPI_NOERROR) {
+	   printk(KERN_ERR "%s: can't get version (0x%x)\n",
+			card->name, errcode);
+	   return;
+	}
+	avmversion[0] = (version.majormanuversion >> 4) & 0x0f;
+	avmversion[1] = (version.majormanuversion << 4) & 0xf0;
+	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
+	avmversion[2] |= version.minormanuversion & 0x0f;
+
+        if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+		printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
+		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid,
+					   card->msgid++,
+					   contr,
+					   0x214D5641,  /* ManuID */
+					   0,           /* Class */
+					   1,           /* Function */
+					   (_cstruct)"\004\200\014\000\000");
+	} else {
+		printk(KERN_INFO "%s: D3 trace enabled\n", card->name);
+		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid,
+					   card->msgid++,
+					   contr,
+					   0x214D5641,  /* ManuID */
+					   0,           /* Class */
+					   1,           /* Function */
+					   (_cstruct)"\004\002\003\000\000");
+	}
+	send_message(card, &cmdcmsg);
+}
+
+
+static void send_listen(capidrv_contr *card)
+{
+	capi_fill_LISTEN_REQ(&cmdcmsg, global.ap.applid,
+			     card->msgid++,
+			     card->contrnr, /* controller */
+			     1 << 6,	/* Infomask */
+			     card->cipmask,
+			     card->cipmask2,
+			     NULL, NULL);
+	send_message(card, &cmdcmsg);
+	listen_change_state(card, EV_LISTEN_REQ);
+}
+
+static void listentimerfunc(unsigned long x)
+{
+	capidrv_contr *card = (capidrv_contr *)x;
+	if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE)
+		printk(KERN_ERR "%s: controller dead ??\n", card->name);
+        send_listen(card);
+	mod_timer(&card->listentimer, jiffies + 60*HZ);
+}
+
+
+static int capidrv_addcontr(u16 contr, struct capi_profile *profp)
+{
+	capidrv_contr *card;
+	unsigned long flags;
+	isdn_ctrl cmd;
+	char id[20];
+	int i;
+
+	sprintf(id, "capidrv-%d", contr);
+	if (!try_module_get(THIS_MODULE)) {
+		printk(KERN_WARNING "capidrv: (%s) Could not reserve module\n", id);
+		return -1;
+	}
+	if (!(card = (capidrv_contr *) kmalloc(sizeof(capidrv_contr), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		 "capidrv: (%s) Could not allocate contr-struct.\n", id);
+		return -1;
+	}
+	memset(card, 0, sizeof(capidrv_contr));
+	card->owner = THIS_MODULE;
+	init_timer(&card->listentimer);
+	strcpy(card->name, id);
+	card->contrnr = contr;
+	card->nbchan = profp->nbchannel;
+	card->bchans = (capidrv_bchan *) kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC);
+	if (!card->bchans) {
+		printk(KERN_WARNING
+		"capidrv: (%s) Could not allocate bchan-structs.\n", id);
+		module_put(card->owner);
+		kfree(card);
+		return -1;
+	}
+	card->interface.channels = profp->nbchannel;
+	card->interface.maxbufsize = 2048;
+	card->interface.command = if_command;
+	card->interface.writebuf_skb = if_sendbuf;
+	card->interface.writecmd = NULL;
+	card->interface.readstat = if_readstat;
+	card->interface.features = ISDN_FEATURE_L2_HDLC |
+	    			   ISDN_FEATURE_L2_TRANS |
+	    			   ISDN_FEATURE_L3_TRANS |
+				   ISDN_FEATURE_P_UNKNOWN |
+				   ISDN_FEATURE_L2_X75I |
+				   ISDN_FEATURE_L2_X75UI |
+				   ISDN_FEATURE_L2_X75BUI;
+	if (profp->support1 & (1<<2))
+		card->interface.features |= ISDN_FEATURE_L2_V11096 |
+	    				    ISDN_FEATURE_L2_V11019 |
+	    				    ISDN_FEATURE_L2_V11038;
+	if (profp->support1 & (1<<8))
+		card->interface.features |= ISDN_FEATURE_L2_MODEM;
+	card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */
+	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+
+
+	card->q931_read = card->q931_buf;
+	card->q931_write = card->q931_buf;
+	card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1;
+
+	if (!register_isdn(&card->interface)) {
+		printk(KERN_ERR "capidrv: Unable to register contr %s\n", id);
+		kfree(card->bchans);
+		module_put(card->owner);
+		kfree(card);
+		return -1;
+	}
+	card->myid = card->interface.channels;
+	memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan);
+	for (i = 0; i < card->nbchan; i++) {
+		card->bchans[i].contr = card;
+	}
+
+	spin_lock_irqsave(&global_lock, flags);
+	card->next = global.contr_list;
+	global.contr_list = card;
+	global.ncontr++;
+	spin_unlock_irqrestore(&global_lock, flags);
+
+	cmd.command = ISDN_STAT_RUN;
+	cmd.driver = card->myid;
+	card->interface.statcallb(&cmd);
+
+	card->cipmask = 0x1FFF03FF;	/* any */
+	card->cipmask2 = 0;
+
+	card->listentimer.data = (unsigned long)card;
+	card->listentimer.function = listentimerfunc;
+	send_listen(card);
+	mod_timer(&card->listentimer, jiffies + 60*HZ);
+
+	printk(KERN_INFO "%s: now up (%d B channels)\n",
+		card->name, card->nbchan);
+
+	enable_dchannel_trace(card);
+
+	return 0;
+}
+
+static int capidrv_delcontr(u16 contr)
+{
+	capidrv_contr **pp, *card;
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	spin_lock_irqsave(&global_lock, flags);
+	for (card = global.contr_list; card; card = card->next) {
+		if (card->contrnr == contr)
+			break;
+	}
+	if (!card) {
+		spin_unlock_irqrestore(&global_lock, flags);
+		printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr);
+		return -1;
+	}
+	#warning FIXME: maybe a race condition the card should be removed here from global list /kkeil
+	spin_unlock_irqrestore(&global_lock, flags);
+
+	del_timer(&card->listentimer);
+
+	if (debugmode)
+		printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n",
+					card->contrnr, card->myid);
+
+	cmd.command = ISDN_STAT_STOP;
+	cmd.driver = card->myid;
+	card->interface.statcallb(&cmd);
+
+	while (card->nbchan) {
+
+		cmd.command = ISDN_STAT_DISCH;
+		cmd.driver = card->myid;
+		cmd.arg = card->nbchan-1;
+	        cmd.parm.num[0] = 0;
+		if (debugmode)
+			printk(KERN_DEBUG "capidrv-%d: id=%d disable chan=%ld\n",
+					card->contrnr, card->myid, cmd.arg);
+		card->interface.statcallb(&cmd);
+
+		if (card->bchans[card->nbchan-1].nccip)
+			free_ncci(card, card->bchans[card->nbchan-1].nccip);
+		if (card->bchans[card->nbchan-1].plcip)
+			free_plci(card, card->bchans[card->nbchan-1].plcip);
+		if (card->plci_list)
+			printk(KERN_ERR "capidrv: bug in free_plci()\n");
+		card->nbchan--;
+	}
+	kfree(card->bchans);
+	card->bchans = NULL;
+
+	if (debugmode)
+		printk(KERN_DEBUG "capidrv-%d: id=%d isdn unload\n",
+					card->contrnr, card->myid);
+
+	cmd.command = ISDN_STAT_UNLOAD;
+	cmd.driver = card->myid;
+	card->interface.statcallb(&cmd);
+
+	if (debugmode)
+		printk(KERN_DEBUG "capidrv-%d: id=%d remove contr from list\n",
+					card->contrnr, card->myid);
+
+	spin_lock_irqsave(&global_lock, flags);
+	for (pp = &global.contr_list; *pp; pp = &(*pp)->next) {
+		if (*pp == card) {
+			*pp = (*pp)->next;
+			card->next = NULL;
+			global.ncontr--;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&global_lock, flags);
+
+	module_put(card->owner);
+	printk(KERN_INFO "%s: now down.\n", card->name);
+	kfree(card);
+	return 0;
+}
+
+
+static void lower_callback(unsigned int cmd, u32 contr, void *data)
+{
+
+	switch (cmd) {
+	case KCI_CONTRUP:
+		printk(KERN_INFO "capidrv: controller %hu up\n", contr);
+		(void) capidrv_addcontr(contr, (capi_profile *) data);
+		break;
+	case KCI_CONTRDOWN:
+		printk(KERN_INFO "capidrv: controller %hu down\n", contr);
+		(void) capidrv_delcontr(contr);
+		break;
+	}
+}
+
+/*
+ * /proc/capi/capidrv:
+ * nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
+ */
+static int proc_capidrv_read_proc(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data)
+{
+	int len = 0;
+
+	len += sprintf(page+len, "%lu %lu %lu %lu\n",
+			global.ap.nrecvctlpkt,
+			global.ap.nrecvdatapkt,
+			global.ap.nsentctlpkt,
+			global.ap.nsentdatapkt);
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
+           return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+static struct procfsentries {
+  char *name;
+  mode_t mode;
+  int (*read_proc)(char *page, char **start, off_t off,
+                                       int count, int *eof, void *data);
+  struct proc_dir_entry *procent;
+} procfsentries[] = {
+   /* { "capi",		  S_IFDIR, 0 }, */
+   { "capi/capidrv", 	  0	 , proc_capidrv_read_proc },
+};
+
+static void __init proc_init(void)
+{
+    int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+    int i;
+
+    for (i=0; i < nelem; i++) {
+        struct procfsentries *p = procfsentries + i;
+	p->procent = create_proc_entry(p->name, p->mode, NULL);
+	if (p->procent) p->procent->read_proc = p->read_proc;
+    }
+}
+
+static void __exit proc_exit(void)
+{
+    int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+    int i;
+
+    for (i=nelem-1; i >= 0; i--) {
+        struct procfsentries *p = procfsentries + i;
+	if (p->procent) {
+	   remove_proc_entry(p->name, NULL);
+	   p->procent = NULL;
+	}
+    }
+}
+
+static int __init capidrv_init(void)
+{
+	capi_profile profile;
+	char rev[32];
+	char *p;
+	u32 ncontr, contr;
+	u16 errcode;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strncpy(rev, p + 2, sizeof(rev));
+		rev[sizeof(rev)-1] = 0;
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	global.ap.rparam.level3cnt = -2;  /* number of bchannels twice */
+	global.ap.rparam.datablkcnt = 16;
+	global.ap.rparam.datablklen = 2048;
+
+	global.ap.recv_message = capidrv_recv_message;
+	errcode = capi20_register(&global.ap);
+	if (errcode) {
+		return -EIO;
+	}
+
+	capi20_set_callback(&global.ap, lower_callback);
+
+	errcode = capi20_get_profile(0, &profile);
+	if (errcode != CAPI_NOERROR) {
+		capi20_release(&global.ap);
+		return -EIO;
+	}
+
+	ncontr = profile.ncontroller;
+	for (contr = 1; contr <= ncontr; contr++) {
+		errcode = capi20_get_profile(contr, &profile);
+		if (errcode != CAPI_NOERROR)
+			continue;
+		(void) capidrv_addcontr(contr, &profile);
+	}
+	proc_init();
+
+	printk(KERN_NOTICE "capidrv: Rev %s: loaded\n", rev);
+	return 0;
+}
+
+static void __exit capidrv_exit(void)
+{
+	char rev[10];
+	char *p;
+
+	if ((p = strchr(revision, ':')) != 0) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else {
+		strcpy(rev, " ??? ");
+	}
+
+	capi20_release(&global.ap);
+
+	proc_exit();
+
+	printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev);
+}
+
+module_init(capidrv_init);
+module_exit(capidrv_exit);
diff --git a/drivers/isdn/capi/capidrv.h b/drivers/isdn/capi/capidrv.h
new file mode 100644
index 0000000..1e698e1
--- /dev/null
+++ b/drivers/isdn/capi/capidrv.h
@@ -0,0 +1,140 @@
+/* $Id: capidrv.h,v 1.2.8.2 2001/09/23 22:24:33 kai Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __CAPIDRV_H__
+#define __CAPIDRV_H__
+
+/*
+ * LISTEN state machine
+ */
+#define ST_LISTEN_NONE			0	/* L-0 */
+#define ST_LISTEN_WAIT_CONF		1	/* L-0.1 */
+#define ST_LISTEN_ACTIVE		2	/* L-1 */
+#define ST_LISTEN_ACTIVE_WAIT_CONF	3	/* L-1.1 */
+
+
+#define EV_LISTEN_REQ			1	/* L-0 -> L-0.1
+						   L-1 -> L-1.1 */
+#define EV_LISTEN_CONF_ERROR		2	/* L-0.1 -> L-0
+						   L-1.1 -> L-1 */
+#define EV_LISTEN_CONF_EMPTY		3	/* L-0.1 -> L-0
+						   L-1.1 -> L-0 */
+#define EV_LISTEN_CONF_OK		4	/* L-0.1 -> L-1
+						   L-1.1 -> L.1 */
+
+/*
+ * per plci state machine
+ */
+#define ST_PLCI_NONE			0	/* P-0 */
+#define ST_PLCI_OUTGOING 		1	/* P-0.1 */
+#define ST_PLCI_ALLOCATED		2	/* P-1 */
+#define ST_PLCI_ACTIVE			3	/* P-ACT */
+#define ST_PLCI_INCOMING		4	/* P-2 */
+#define ST_PLCI_FACILITY_IND		5	/* P-3 */
+#define ST_PLCI_ACCEPTING		6	/* P-4 */
+#define ST_PLCI_DISCONNECTING		7	/* P-5 */
+#define ST_PLCI_DISCONNECTED		8	/* P-6 */
+#define ST_PLCI_RESUMEING		9	/* P-0.Res */
+#define ST_PLCI_RESUME			10	/* P-Res */
+#define ST_PLCI_HELD			11	/* P-HELD */
+
+#define EV_PLCI_CONNECT_REQ		1	/* P-0 -> P-0.1
+                                                 */
+#define EV_PLCI_CONNECT_CONF_ERROR	2	/* P-0.1 -> P-0
+                                                 */
+#define EV_PLCI_CONNECT_CONF_OK		3	/* P-0.1 -> P-1
+                                                 */
+#define EV_PLCI_FACILITY_IND_UP		4	/* P-0 -> P-1
+                                                 */
+#define EV_PLCI_CONNECT_IND		5	/* P-0 -> P-2
+                                                 */
+#define EV_PLCI_CONNECT_ACTIVE_IND	6	/* P-1 -> P-ACT
+                                                 */
+#define EV_PLCI_CONNECT_REJECT		7	/* P-2 -> P-5
+						   P-3 -> P-5
+						 */
+#define EV_PLCI_DISCONNECT_REQ		8	/* P-1 -> P-5
+						   P-2 -> P-5
+						   P-3 -> P-5
+						   P-4 -> P-5
+						   P-ACT -> P-5
+						   P-Res -> P-5 (*)
+						   P-HELD -> P-5 (*)
+						   */
+#define EV_PLCI_DISCONNECT_IND		9	/* P-1 -> P-6
+						   P-2 -> P-6
+						   P-3 -> P-6
+						   P-4 -> P-6
+						   P-5 -> P-6
+						   P-ACT -> P-6
+						   P-Res -> P-6 (*)
+						   P-HELD -> P-6 (*)
+						   */
+#define EV_PLCI_FACILITY_IND_DOWN	10	/* P-0.1 -> P-5
+						   P-1 -> P-5
+						   P-ACT -> P-5
+						   P-2 -> P-5
+						   P-3 -> P-5
+						   P-4 -> P-5
+						   */
+#define EV_PLCI_DISCONNECT_RESP		11	/* P-6 -> P-0
+                                                   */
+#define EV_PLCI_CONNECT_RESP		12	/* P-6 -> P-0
+                                                   */
+
+#define EV_PLCI_RESUME_REQ		13	/* P-0 -> P-0.Res
+                                                 */
+#define EV_PLCI_RESUME_CONF_OK		14	/* P-0.Res -> P-Res
+                                                 */
+#define EV_PLCI_RESUME_CONF_ERROR	15	/* P-0.Res -> P-0
+                                                 */
+#define EV_PLCI_RESUME_IND		16	/* P-Res -> P-ACT
+                                                 */
+#define EV_PLCI_HOLD_IND		17	/* P-ACT -> P-HELD
+                                                 */
+#define EV_PLCI_RETRIEVE_IND		18	/* P-HELD -> P-ACT
+                                                 */
+#define EV_PLCI_SUSPEND_IND		19	/* P-ACT -> P-5
+                                                 */
+#define EV_PLCI_CD_IND			20	/* P-2 -> P-5
+                                                 */
+
+/*
+ * per ncci state machine
+ */
+#define ST_NCCI_PREVIOUS			-1
+#define ST_NCCI_NONE				0	/* N-0 */
+#define ST_NCCI_OUTGOING			1	/* N-0.1 */
+#define ST_NCCI_INCOMING			2	/* N-1 */
+#define ST_NCCI_ALLOCATED			3	/* N-2 */
+#define ST_NCCI_ACTIVE				4	/* N-ACT */
+#define ST_NCCI_RESETING			5	/* N-3 */
+#define ST_NCCI_DISCONNECTING			6	/* N-4 */
+#define ST_NCCI_DISCONNECTED			7	/* N-5 */
+
+#define EV_NCCI_CONNECT_B3_REQ			1	/* N-0 -> N-0.1 */
+#define EV_NCCI_CONNECT_B3_IND			2	/* N-0 -> N.1 */
+#define EV_NCCI_CONNECT_B3_CONF_OK		3	/* N-0.1 -> N.2 */
+#define EV_NCCI_CONNECT_B3_CONF_ERROR		4	/* N-0.1 -> N.0 */
+#define EV_NCCI_CONNECT_B3_REJECT		5	/* N-1 -> N-4 */
+#define EV_NCCI_CONNECT_B3_RESP			6	/* N-1 -> N-2 */
+#define EV_NCCI_CONNECT_B3_ACTIVE_IND		7	/* N-2 -> N-ACT */
+#define EV_NCCI_RESET_B3_REQ			8	/* N-ACT -> N-3 */
+#define EV_NCCI_RESET_B3_IND			9	/* N-3 -> N-ACT */
+#define EV_NCCI_DISCONNECT_B3_IND		10	/* N-4 -> N.5 */
+#define EV_NCCI_DISCONNECT_B3_CONF_ERROR	11	/* N-4 -> previous */
+#define EV_NCCI_DISCONNECT_B3_REQ		12	/* N-1 -> N-4
+							   N-2 -> N-4
+							   N-3 -> N-4
+							   N-ACT -> N-4 */
+#define EV_NCCI_DISCONNECT_B3_RESP		13	/* N-5 -> N-0 */
+
+#endif				/* __CAPIDRV_H__ */
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
new file mode 100644
index 0000000..f8570fd
--- /dev/null
+++ b/drivers/isdn/capi/capifs.c
@@ -0,0 +1,212 @@
+/* $Id: capifs.c,v 1.1.2.3 2004/01/16 21:09:26 keil Exp $
+ * 
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ *
+ * Heavily based on devpts filesystem from H. Peter Anvin
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+
+MODULE_DESCRIPTION("CAPI4Linux: /dev/capi/ filesystem");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------------ */
+
+#define CAPIFS_SUPER_MAGIC (('C'<<8)|'N')
+
+static struct vfsmount *capifs_mnt;
+static struct dentry *capifs_root;
+
+static struct {
+	int setuid;
+	int setgid;
+	uid_t   uid;
+	gid_t   gid;
+	umode_t mode;
+} config = {.mode = 0600};
+
+/* ------------------------------------------------------------------ */
+
+static int capifs_remount(struct super_block *s, int *flags, char *data)
+{
+	int setuid = 0;
+	int setgid = 0;
+	uid_t uid = 0;
+	gid_t gid = 0;
+	umode_t mode = 0600;
+	char *this_char;
+
+	this_char = NULL;
+	while ((this_char = strsep(&data, ",")) != NULL) {
+		int n;
+		char dummy;
+		if (!*this_char)
+			continue;
+		if (sscanf(this_char, "uid=%i%c", &n, &dummy) == 1) {
+			setuid = 1;
+			uid = n;
+		} else if (sscanf(this_char, "gid=%i%c", &n, &dummy) == 1) {
+			setgid = 1;
+			gid = n;
+		} else if (sscanf(this_char, "mode=%o%c", &n, &dummy) == 1)
+			mode = n & ~S_IFMT;
+		else {
+			printk("capifs: called with bogus options\n");
+			return -EINVAL;
+		}
+	}
+	config.setuid  = setuid;
+	config.setgid  = setgid;
+	config.uid     = uid;
+	config.gid     = gid;
+	config.mode    = mode;
+	return 0;
+}
+
+static struct super_operations capifs_sops =
+{
+	.statfs		= simple_statfs,
+	.remount_fs	= capifs_remount,
+};
+
+
+static int
+capifs_fill_super(struct super_block *s, void *data, int silent)
+{
+	struct inode * inode;
+
+	s->s_blocksize = 1024;
+	s->s_blocksize_bits = 10;
+	s->s_magic = CAPIFS_SUPER_MAGIC;
+	s->s_op = &capifs_sops;
+	s->s_time_gran = 1;
+
+	inode = new_inode(s);
+	if (!inode)
+		goto fail;
+	inode->i_ino = 1;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	inode->i_blocks = 0;
+	inode->i_blksize = 1024;
+	inode->i_uid = inode->i_gid = 0;
+	inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+	inode->i_op = &simple_dir_inode_operations;
+	inode->i_fop = &simple_dir_operations;
+	inode->i_nlink = 2;
+
+	capifs_root = s->s_root = d_alloc_root(inode);
+	if (s->s_root)
+		return 0;
+	
+	printk("capifs: get root dentry failed\n");
+	iput(inode);
+fail:
+	return -ENOMEM;
+}
+
+static struct super_block *capifs_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_single(fs_type, flags, data, capifs_fill_super);
+}
+
+static struct file_system_type capifs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "capifs",
+	.get_sb		= capifs_get_sb,
+	.kill_sb	= kill_anon_super,
+};
+
+static struct dentry *get_node(int num)
+{
+	char s[10];
+	struct dentry *root = capifs_root;
+	down(&root->d_inode->i_sem);
+	return lookup_one_len(s, root, sprintf(s, "%d", num));
+}
+
+void capifs_new_ncci(unsigned int number, dev_t device)
+{
+	struct dentry *dentry;
+	struct inode *inode = new_inode(capifs_mnt->mnt_sb);
+	if (!inode)
+		return;
+	inode->i_ino = number+2;
+	inode->i_blksize = 1024;
+	inode->i_uid = config.setuid ? config.uid : current->fsuid;
+	inode->i_gid = config.setgid ? config.gid : current->fsgid;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	init_special_inode(inode, S_IFCHR|config.mode, device);
+	//inode->i_op = &capifs_file_inode_operations;
+
+	dentry = get_node(number);
+	if (!IS_ERR(dentry) && !dentry->d_inode)
+		d_instantiate(dentry, inode);
+	up(&capifs_root->d_inode->i_sem);
+}
+
+void capifs_free_ncci(unsigned int number)
+{
+	struct dentry *dentry = get_node(number);
+
+	if (!IS_ERR(dentry)) {
+		struct inode *inode = dentry->d_inode;
+		if (inode) {
+			inode->i_nlink--;
+			d_delete(dentry);
+			dput(dentry);
+		}
+		dput(dentry);
+	}
+	up(&capifs_root->d_inode->i_sem);
+}
+
+static int __init capifs_init(void)
+{
+	char rev[32];
+	char *p;
+	int err;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, sizeof(rev));
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	err = register_filesystem(&capifs_fs_type);
+	if (!err) {
+		capifs_mnt = kern_mount(&capifs_fs_type);
+		if (IS_ERR(capifs_mnt))
+			err = PTR_ERR(capifs_mnt);
+	}
+	if (!err)
+		printk(KERN_NOTICE "capifs: Rev %s\n", rev);
+	return err;
+}
+
+static void __exit capifs_exit(void)
+{
+	unregister_filesystem(&capifs_fs_type);
+	mntput(capifs_mnt);
+}
+
+EXPORT_SYMBOL(capifs_new_ncci);
+EXPORT_SYMBOL(capifs_free_ncci);
+
+module_init(capifs_init);
+module_exit(capifs_exit);
diff --git a/drivers/isdn/capi/capifs.h b/drivers/isdn/capi/capifs.h
new file mode 100644
index 0000000..d0bd4c3
--- /dev/null
+++ b/drivers/isdn/capi/capifs.h
@@ -0,0 +1,11 @@
+/* $Id: capifs.h,v 1.1.2.2 2004/01/16 21:09:26 keil Exp $
+ * 
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+void capifs_new_ncci(unsigned int num, dev_t device);
+void capifs_free_ncci(unsigned int num);
diff --git a/drivers/isdn/capi/capilib.c b/drivers/isdn/capi/capilib.c
new file mode 100644
index 0000000..68409d9
--- /dev/null
+++ b/drivers/isdn/capi/capilib.c
@@ -0,0 +1,200 @@
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isdn/capilli.h>
+
+#define DBG(format, arg...) do { \
+printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \
+} while (0)
+
+struct capilib_msgidqueue {
+	struct capilib_msgidqueue *next;
+	u16 msgid;
+};
+
+struct capilib_ncci {
+	struct list_head list;
+	u16 applid;
+	u32 ncci;
+	u32 winsize;
+	int   nmsg;
+	struct capilib_msgidqueue *msgidqueue;
+	struct capilib_msgidqueue *msgidlast;
+	struct capilib_msgidqueue *msgidfree;
+	struct capilib_msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
+};
+
+// ---------------------------------------------------------------------------
+// NCCI Handling
+
+static inline void mq_init(struct capilib_ncci * np)
+{
+	u_int i;
+	np->msgidqueue = NULL;
+	np->msgidlast = NULL;
+	np->nmsg = 0;
+	memset(np->msgidpool, 0, sizeof(np->msgidpool));
+	np->msgidfree = &np->msgidpool[0];
+	for (i = 1; i < np->winsize; i++) {
+		np->msgidpool[i].next = np->msgidfree;
+		np->msgidfree = &np->msgidpool[i];
+	}
+}
+
+static inline int mq_enqueue(struct capilib_ncci * np, u16 msgid)
+{
+	struct capilib_msgidqueue *mq;
+	if ((mq = np->msgidfree) == 0)
+		return 0;
+	np->msgidfree = mq->next;
+	mq->msgid = msgid;
+	mq->next = NULL;
+	if (np->msgidlast)
+		np->msgidlast->next = mq;
+	np->msgidlast = mq;
+	if (!np->msgidqueue)
+		np->msgidqueue = mq;
+	np->nmsg++;
+	return 1;
+}
+
+static inline int mq_dequeue(struct capilib_ncci * np, u16 msgid)
+{
+	struct capilib_msgidqueue **pp;
+	for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
+		if ((*pp)->msgid == msgid) {
+			struct capilib_msgidqueue *mq = *pp;
+			*pp = mq->next;
+			if (mq == np->msgidlast)
+				np->msgidlast = NULL;
+			mq->next = np->msgidfree;
+			np->msgidfree = mq;
+			np->nmsg--;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void capilib_new_ncci(struct list_head *head, u16 applid, u32 ncci, u32 winsize)
+{
+	struct capilib_ncci *np;
+
+	np = kmalloc(sizeof(*np), GFP_ATOMIC);
+	if (!np) {
+		printk(KERN_WARNING "capilib_new_ncci: no memory.\n");
+		return;
+	}
+	if (winsize > CAPI_MAXDATAWINDOW) {
+		printk(KERN_ERR "capi_new_ncci: winsize %d too big\n",
+		       winsize);
+		winsize = CAPI_MAXDATAWINDOW;
+	}
+	np->applid = applid;
+	np->ncci = ncci;
+	np->winsize = winsize;
+	mq_init(np);
+	list_add_tail(&np->list, head);
+	DBG("kcapi: appl %d ncci 0x%x up", applid, ncci);
+}
+
+EXPORT_SYMBOL(capilib_new_ncci);
+
+void capilib_free_ncci(struct list_head *head, u16 applid, u32 ncci)
+{
+	struct list_head *l;
+	struct capilib_ncci *np;
+
+	list_for_each(l, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		if (np->ncci != ncci)
+			continue;
+		printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", applid, ncci);
+		list_del(&np->list);
+		kfree(np);
+		return;
+	}
+	printk(KERN_ERR "capilib_free_ncci: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_free_ncci);
+
+void capilib_release_appl(struct list_head *head, u16 applid)
+{
+	struct list_head *l, *n;
+	struct capilib_ncci *np;
+
+	list_for_each_safe(l, n, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", applid, np->ncci);
+		list_del(&np->list);
+		kfree(np);
+	}
+}
+
+EXPORT_SYMBOL(capilib_release_appl);
+
+void capilib_release(struct list_head *head)
+{
+	struct list_head *l, *n;
+	struct capilib_ncci *np;
+
+	list_for_each_safe(l, n, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", np->applid, np->ncci);
+		list_del(&np->list);
+		kfree(np);
+	}
+}
+
+EXPORT_SYMBOL(capilib_release);
+
+u16 capilib_data_b3_req(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+	struct list_head *l;
+	struct capilib_ncci *np;
+
+	list_for_each(l, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		if (np->ncci != ncci)
+			continue;
+		
+		if (mq_enqueue(np, msgid) == 0)
+			return CAPI_SENDQUEUEFULL;
+
+		return CAPI_NOERROR;
+	}
+	printk(KERN_ERR "capilib_data_b3_req: ncci 0x%x not found\n", ncci);
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capilib_data_b3_req);
+
+void capilib_data_b3_conf(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+	struct list_head *l;
+	struct capilib_ncci *np;
+
+	list_for_each(l, head) {
+		np = list_entry(l, struct capilib_ncci, list);
+		if (np->applid != applid)
+			continue;
+		if (np->ncci != ncci)
+			continue;
+		
+		if (mq_dequeue(np, msgid) == 0) {
+			printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n",
+			       msgid, ncci);
+		}
+		return;
+	}
+	printk(KERN_ERR "capilib_data_b3_conf: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_data_b3_conf);
diff --git a/drivers/isdn/capi/capiutil.c b/drivers/isdn/capi/capiutil.c
new file mode 100644
index 0000000..e7cf6bc
--- /dev/null
+++ b/drivers/isdn/capi/capiutil.c
@@ -0,0 +1,859 @@
+/* $Id: capiutil.c,v 1.13.6.4 2001/09/23 22:24:33 kai Exp $
+ *
+ * CAPI 2.0 convert capi message to capi message struct
+ *
+ * From CAPI 2.0 Development Kit AVM 1995 (msg.c)
+ * Rewritten for Linux 1996 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/isdn/capiutil.h>
+
+/* from CAPI2.0 DDK AVM Berlin GmbH */
+
+#ifndef CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+char *capi_info2str(u16 reason)
+{
+    return "..";
+}
+#else
+char *capi_info2str(u16 reason)
+{
+    switch (reason) {
+
+/*-- informative values (corresponding message was processed) -----*/
+	case 0x0001:
+	   return "NCPI not supported by current protocol, NCPI ignored";
+	case 0x0002:
+	   return "Flags not supported by current protocol, flags ignored";
+	case 0x0003:
+	   return "Alert already sent by another application";
+
+/*-- error information concerning CAPI_REGISTER -----*/
+	case 0x1001:
+	   return "Too many applications";
+	case 0x1002:
+	   return "Logical block size too small, must be at least 128 Bytes";
+	case 0x1003:
+	   return "Buffer exceeds 64 kByte";
+	case 0x1004:
+	   return "Message buffer size too small, must be at least 1024 Bytes";
+	case 0x1005:
+	   return "Max. number of logical connections not supported";
+	case 0x1006:
+	   return "Reserved";
+	case 0x1007:
+	   return "The message could not be accepted because of an internal busy condition";
+	case 0x1008:
+	   return "OS resource error (no memory ?)";
+	case 0x1009:
+	   return "CAPI not installed";
+	case 0x100A:
+	   return "Controller does not support external equipment";
+	case 0x100B:
+	   return "Controller does only support external equipment";
+
+/*-- error information concerning message exchange functions -----*/
+	case 0x1101:
+	   return "Illegal application number";
+	case 0x1102:
+	   return "Illegal command or subcommand or message length less than 12 bytes";
+	case 0x1103:
+	   return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
+	case 0x1104:
+	   return "Queue is empty";
+	case 0x1105:
+	   return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
+	case 0x1106:
+	   return "Unknown notification parameter";
+	case 0x1107:
+	   return "The Message could not be accepted because of an internal busy condition";
+	case 0x1108:
+	   return "OS Resource error (no memory ?)";
+	case 0x1109:
+	   return "CAPI not installed";
+	case 0x110A:
+	   return "Controller does not support external equipment";
+	case 0x110B:
+	   return "Controller does only support external equipment";
+
+/*-- error information concerning resource / coding problems -----*/
+	case 0x2001:
+	   return "Message not supported in current state";
+	case 0x2002:
+	   return "Illegal Controller / PLCI / NCCI";
+	case 0x2003:
+	   return "Out of PLCI";
+	case 0x2004:
+	   return "Out of NCCI";
+	case 0x2005:
+	   return "Out of LISTEN";
+	case 0x2006:
+	   return "Out of FAX resources (protocol T.30)";
+	case 0x2007:
+	   return "Illegal message parameter coding";
+
+/*-- error information concerning requested services  -----*/
+	case 0x3001:
+	   return "B1 protocol not supported";
+	case 0x3002: 
+	   return "B2 protocol not supported";
+	case 0x3003: 
+	   return "B3 protocol not supported";
+	case 0x3004: 
+	   return "B1 protocol parameter not supported";
+	case 0x3005: 
+	   return "B2 protocol parameter not supported";
+	case 0x3006: 
+	   return "B3 protocol parameter not supported";
+	case 0x3007: 
+	   return "B protocol combination not supported";
+	case 0x3008: 
+	   return "NCPI not supported";
+	case 0x3009: 
+	   return "CIP Value unknown";
+	case 0x300A: 
+	   return "Flags not supported (reserved bits)";
+	case 0x300B: 
+	   return "Facility not supported";
+	case 0x300C: 
+	   return "Data length not supported by current protocol";
+	case 0x300D: 
+	   return "Reset procedure not supported by current protocol";
+
+/*-- informations about the clearing of a physical connection -----*/
+	case 0x3301: 
+	   return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
+	case 0x3302: 
+	   return "Protocol error layer 2";
+	case 0x3303: 
+	   return "Protocol error layer 3";
+	case 0x3304: 
+	   return "Another application got that call";
+/*-- T.30 specific reasons -----*/
+	case 0x3311: 
+	   return "Connecting not successful (remote station is no FAX G3 machine)";
+	case 0x3312: 
+	   return "Connecting not successful (training error)";
+	case 0x3313: 
+	   return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
+	case 0x3314: 
+	   return "Disconnected during transfer (remote abort)";
+	case 0x3315: 
+	   return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
+	case 0x3316: 
+	   return "Disconnected during transfer (local tx data underrun)";
+	case 0x3317: 
+	   return "Disconnected during transfer (local rx data overflow)";
+	case 0x3318: 
+	   return "Disconnected during transfer (local abort)";
+	case 0x3319: 
+	   return "Illegal parameter coding (e.g. SFF coding error)";
+
+/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
+	case 0x3481: return "Unallocated (unassigned) number";
+	case 0x3482: return "No route to specified transit network";
+	case 0x3483: return "No route to destination";
+	case 0x3486: return "Channel unacceptable";
+	case 0x3487: 
+	   return "Call awarded and being delivered in an established channel";
+	case 0x3490: return "Normal call clearing";
+	case 0x3491: return "User busy";
+	case 0x3492: return "No user responding";
+	case 0x3493: return "No answer from user (user alerted)";
+	case 0x3495: return "Call rejected";
+	case 0x3496: return "Number changed";
+	case 0x349A: return "Non-selected user clearing";
+	case 0x349B: return "Destination out of order";
+	case 0x349C: return "Invalid number format";
+	case 0x349D: return "Facility rejected";
+	case 0x349E: return "Response to STATUS ENQUIRY";
+	case 0x349F: return "Normal, unspecified";
+	case 0x34A2: return "No circuit / channel available";
+	case 0x34A6: return "Network out of order";
+	case 0x34A9: return "Temporary failure";
+	case 0x34AA: return "Switching equipment congestion";
+	case 0x34AB: return "Access information discarded";
+	case 0x34AC: return "Requested circuit / channel not available";
+	case 0x34AF: return "Resources unavailable, unspecified";
+	case 0x34B1: return "Quality of service unavailable";
+	case 0x34B2: return "Requested facility not subscribed";
+	case 0x34B9: return "Bearer capability not authorized";
+	case 0x34BA: return "Bearer capability not presently available";
+	case 0x34BF: return "Service or option not available, unspecified";
+	case 0x34C1: return "Bearer capability not implemented";
+	case 0x34C2: return "Channel type not implemented";
+	case 0x34C5: return "Requested facility not implemented";
+	case 0x34C6: return "Only restricted digital information bearer capability is available";
+	case 0x34CF: return "Service or option not implemented, unspecified";
+	case 0x34D1: return "Invalid call reference value";
+	case 0x34D2: return "Identified channel does not exist";
+	case 0x34D3: return "A suspended call exists, but this call identity does not";
+	case 0x34D4: return "Call identity in use";
+	case 0x34D5: return "No call suspended";
+	case 0x34D6: return "Call having the requested call identity has been cleared";
+	case 0x34D8: return "Incompatible destination";
+	case 0x34DB: return "Invalid transit network selection";
+	case 0x34DF: return "Invalid message, unspecified";
+	case 0x34E0: return "Mandatory information element is missing";
+	case 0x34E1: return "Message type non-existent or not implemented";
+	case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
+	case 0x34E3: return "Information element non-existent or not implemented";
+	case 0x34E4: return "Invalid information element contents";
+	case 0x34E5: return "Message not compatible with call state";
+	case 0x34E6: return "Recovery on timer expiry";
+	case 0x34EF: return "Protocol error, unspecified";
+	case 0x34FF: return "Interworking, unspecified";
+
+	default: return "No additional information";
+    }
+}
+#endif
+
+typedef struct {
+	int typ;
+	size_t off;
+} _cdef;
+
+#define _CBYTE	       1
+#define _CWORD	       2
+#define _CDWORD        3
+#define _CSTRUCT       4
+#define _CMSTRUCT      5
+#define _CEND	       6
+
+static _cdef cdef[] =
+{
+    /*00 */ 
+ {_CEND},
+    /*01 */ 
+ {_CEND},
+    /*02 */ 
+ {_CEND},
+    /*03 */ 
+ {_CDWORD, offsetof(_cmsg, adr.adrController)},
+    /*04 */ 
+ {_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)},
+    /*05 */ 
+ {_CSTRUCT, offsetof(_cmsg, B1configuration)},
+    /*06 */ 
+ {_CWORD, offsetof(_cmsg, B1protocol)},
+    /*07 */ 
+ {_CSTRUCT, offsetof(_cmsg, B2configuration)},
+    /*08 */ 
+ {_CWORD, offsetof(_cmsg, B2protocol)},
+    /*09 */ 
+ {_CSTRUCT, offsetof(_cmsg, B3configuration)},
+    /*0a */ 
+ {_CWORD, offsetof(_cmsg, B3protocol)},
+    /*0b */ 
+ {_CSTRUCT, offsetof(_cmsg, BC)},
+    /*0c */ 
+ {_CSTRUCT, offsetof(_cmsg, BChannelinformation)},
+    /*0d */ 
+ {_CMSTRUCT, offsetof(_cmsg, BProtocol)},
+    /*0e */ 
+ {_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)},
+    /*0f */ 
+ {_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)},
+    /*10 */ 
+ {_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)},
+    /*11 */ 
+ {_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)},
+    /*12 */ 
+ {_CDWORD, offsetof(_cmsg, CIPmask)},
+    /*13 */ 
+ {_CDWORD, offsetof(_cmsg, CIPmask2)},
+    /*14 */ 
+ {_CWORD, offsetof(_cmsg, CIPValue)},
+    /*15 */ 
+ {_CDWORD, offsetof(_cmsg, Class)},
+    /*16 */ 
+ {_CSTRUCT, offsetof(_cmsg, ConnectedNumber)},
+    /*17 */ 
+ {_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)},
+    /*18 */ 
+ {_CDWORD, offsetof(_cmsg, Data)},
+    /*19 */ 
+ {_CWORD, offsetof(_cmsg, DataHandle)},
+    /*1a */ 
+ {_CWORD, offsetof(_cmsg, DataLength)},
+    /*1b */ 
+ {_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)},
+    /*1c */ 
+ {_CSTRUCT, offsetof(_cmsg, Facilitydataarray)},
+    /*1d */ 
+ {_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)},
+    /*1e */ 
+ {_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)},
+    /*1f */ 
+ {_CWORD, offsetof(_cmsg, FacilitySelector)},
+    /*20 */ 
+ {_CWORD, offsetof(_cmsg, Flags)},
+    /*21 */ 
+ {_CDWORD, offsetof(_cmsg, Function)},
+    /*22 */ 
+ {_CSTRUCT, offsetof(_cmsg, HLC)},
+    /*23 */ 
+ {_CWORD, offsetof(_cmsg, Info)},
+    /*24 */ 
+ {_CSTRUCT, offsetof(_cmsg, InfoElement)},
+    /*25 */ 
+ {_CDWORD, offsetof(_cmsg, InfoMask)},
+    /*26 */ 
+ {_CWORD, offsetof(_cmsg, InfoNumber)},
+    /*27 */ 
+ {_CSTRUCT, offsetof(_cmsg, Keypadfacility)},
+    /*28 */ 
+ {_CSTRUCT, offsetof(_cmsg, LLC)},
+    /*29 */ 
+ {_CSTRUCT, offsetof(_cmsg, ManuData)},
+    /*2a */ 
+ {_CDWORD, offsetof(_cmsg, ManuID)},
+    /*2b */ 
+ {_CSTRUCT, offsetof(_cmsg, NCPI)},
+    /*2c */ 
+ {_CWORD, offsetof(_cmsg, Reason)},
+    /*2d */ 
+ {_CWORD, offsetof(_cmsg, Reason_B3)},
+    /*2e */ 
+ {_CWORD, offsetof(_cmsg, Reject)},
+    /*2f */ 
+ {_CSTRUCT, offsetof(_cmsg, Useruserdata)}
+};
+
+static unsigned char *cpars[] =
+{
+    /* ALERT_REQ */ [0x01] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+    /* CONNECT_REQ */ [0x02] = "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+    /* DISCONNECT_REQ */ [0x04] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+    /* LISTEN_REQ */ [0x05] = "\x03\x25\x12\x13\x10\x11\x01",
+    /* INFO_REQ */ [0x08] = "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01",
+    /* FACILITY_REQ */ [0x09] = "\x03\x1f\x1e\x01",
+    /* SELECT_B_PROTOCOL_REQ */ [0x0a] = "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01",
+    /* CONNECT_B3_REQ */ [0x0b] = "\x03\x2b\x01",
+    /* DISCONNECT_B3_REQ */ [0x0d] = "\x03\x2b\x01",
+    /* DATA_B3_REQ */ [0x0f] = "\x03\x18\x1a\x19\x20\x01",
+    /* RESET_B3_REQ */ [0x10] = "\x03\x2b\x01",
+    /* ALERT_CONF */ [0x13] = "\x03\x23\x01",
+    /* CONNECT_CONF */ [0x14] = "\x03\x23\x01",
+    /* DISCONNECT_CONF */ [0x16] = "\x03\x23\x01",
+    /* LISTEN_CONF */ [0x17] = "\x03\x23\x01",
+    /* MANUFACTURER_REQ */ [0x18] = "\x03\x2a\x15\x21\x29\x01",
+    /* INFO_CONF */ [0x1a] = "\x03\x23\x01",
+    /* FACILITY_CONF */ [0x1b] = "\x03\x23\x1f\x1b\x01",
+    /* SELECT_B_PROTOCOL_CONF */ [0x1c] = "\x03\x23\x01",
+    /* CONNECT_B3_CONF */ [0x1d] = "\x03\x23\x01",
+    /* DISCONNECT_B3_CONF */ [0x1f] = "\x03\x23\x01",
+    /* DATA_B3_CONF */ [0x21] = "\x03\x19\x23\x01",
+    /* RESET_B3_CONF */ [0x22] = "\x03\x23\x01",
+    /* CONNECT_IND */ [0x26] = "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+    /* CONNECT_ACTIVE_IND */ [0x27] = "\x03\x16\x17\x28\x01",
+    /* DISCONNECT_IND */ [0x28] = "\x03\x2c\x01",
+    /* MANUFACTURER_CONF */ [0x2a] = "\x03\x2a\x15\x21\x29\x01",
+    /* INFO_IND */ [0x2c] = "\x03\x26\x24\x01",
+    /* FACILITY_IND */ [0x2d] = "\x03\x1f\x1d\x01",
+    /* CONNECT_B3_IND */ [0x2f] = "\x03\x2b\x01",
+    /* CONNECT_B3_ACTIVE_IND */ [0x30] = "\x03\x2b\x01",
+    /* DISCONNECT_B3_IND */ [0x31] = "\x03\x2d\x2b\x01",
+    /* DATA_B3_IND */ [0x33] = "\x03\x18\x1a\x19\x20\x01",
+    /* RESET_B3_IND */ [0x34] = "\x03\x2b\x01",
+    /* CONNECT_B3_T90_ACTIVE_IND */ [0x35] = "\x03\x2b\x01",
+    /* CONNECT_RESP */ [0x38] = "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01",
+    /* CONNECT_ACTIVE_RESP */ [0x39] = "\x03\x01",
+    /* DISCONNECT_RESP */ [0x3a] = "\x03\x01",
+    /* MANUFACTURER_IND */ [0x3c] = "\x03\x2a\x15\x21\x29\x01",
+    /* INFO_RESP */ [0x3e] = "\x03\x01",
+    /* FACILITY_RESP */ [0x3f] = "\x03\x1f\x01",
+    /* CONNECT_B3_RESP */ [0x41] = "\x03\x2e\x2b\x01",
+    /* CONNECT_B3_ACTIVE_RESP */ [0x42] = "\x03\x01",
+    /* DISCONNECT_B3_RESP */ [0x43] = "\x03\x01",
+    /* DATA_B3_RESP */ [0x45] = "\x03\x19\x01",
+    /* RESET_B3_RESP */ [0x46] = "\x03\x01",
+    /* CONNECT_B3_T90_ACTIVE_RESP */ [0x47] = "\x03\x01",
+    /* MANUFACTURER_RESP */ [0x4e] = "\x03\x2a\x15\x21\x29\x01",
+};
+
+/*-------------------------------------------------------*/
+
+#define byteTLcpy(x,y)        *(u8 *)(x)=*(u8 *)(y);
+#define wordTLcpy(x,y)        *(u16 *)(x)=*(u16 *)(y);
+#define dwordTLcpy(x,y)       memcpy(x,y,4);
+#define structTLcpy(x,y,l)    memcpy (x,y,l)
+#define structTLcpyovl(x,y,l) memmove (x,y,l)
+
+#define byteTRcpy(x,y)        *(u8 *)(y)=*(u8 *)(x);
+#define wordTRcpy(x,y)        *(u16 *)(y)=*(u16 *)(x);
+#define dwordTRcpy(x,y)       memcpy(y,x,4);
+#define structTRcpy(x,y,l)    memcpy (y,x,l)
+#define structTRcpyovl(x,y,l) memmove (y,x,l)
+
+/*-------------------------------------------------------*/
+static unsigned command_2_index(unsigned c, unsigned sc)
+{
+	if (c & 0x80)
+		c = 0x9 + (c & 0x0f);
+	else if (c <= 0x0f);
+	else if (c == 0x41)
+		c = 0x9 + 0x1;
+	else if (c == 0xff)
+		c = 0x00;
+	return (sc & 3) * (0x9 + 0x9) + c;
+}
+
+/*-------------------------------------------------------*/
+#define TYP (cdef[cmsg->par[cmsg->p]].typ)
+#define OFF (((u8 *)cmsg)+cdef[cmsg->par[cmsg->p]].off)
+
+static void jumpcstruct(_cmsg * cmsg)
+{
+	unsigned layer;
+	for (cmsg->p++, layer = 1; layer;) {
+		/* $$$$$ assert (cmsg->p); */
+		cmsg->p++;
+		switch (TYP) {
+		case _CMSTRUCT:
+			layer++;
+			break;
+		case _CEND:
+			layer--;
+			break;
+		}
+	}
+}
+/*-------------------------------------------------------*/
+static void pars_2_message(_cmsg * cmsg)
+{
+
+	for (; TYP != _CEND; cmsg->p++) {
+		switch (TYP) {
+		case _CBYTE:
+			byteTLcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l++;
+			break;
+		case _CWORD:
+			wordTLcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 2;
+			break;
+		case _CDWORD:
+			dwordTLcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 4;
+			break;
+		case _CSTRUCT:
+			if (*(u8 **) OFF == 0) {
+				*(cmsg->m + cmsg->l) = '\0';
+				cmsg->l++;
+			} else if (**(_cstruct *) OFF != 0xff) {
+				structTLcpy(cmsg->m + cmsg->l, *(_cstruct *) OFF, 1 + **(_cstruct *) OFF);
+				cmsg->l += 1 + **(_cstruct *) OFF;
+			} else {
+				_cstruct s = *(_cstruct *) OFF;
+				structTLcpy(cmsg->m + cmsg->l, s, 3 + *(u16 *) (s + 1));
+				cmsg->l += 3 + *(u16 *) (s + 1);
+			}
+			break;
+		case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+			if (*(_cmstruct *) OFF == CAPI_DEFAULT) {
+				*(cmsg->m + cmsg->l) = '\0';
+				cmsg->l++;
+				jumpcstruct(cmsg);
+			}
+/*----- Metastruktur wird composed -----*/
+			else {
+				unsigned _l = cmsg->l;
+				unsigned _ls;
+				cmsg->l++;
+				cmsg->p++;
+				pars_2_message(cmsg);
+				_ls = cmsg->l - _l - 1;
+				if (_ls < 255)
+					(cmsg->m + _l)[0] = (u8) _ls;
+				else {
+					structTLcpyovl(cmsg->m + _l + 3, cmsg->m + _l + 1, _ls);
+					(cmsg->m + _l)[0] = 0xff;
+					wordTLcpy(cmsg->m + _l + 1, &_ls);
+				}
+			}
+			break;
+		}
+	}
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg2message(_cmsg * cmsg, u8 * msg)
+{
+	cmsg->m = msg;
+	cmsg->l = 8;
+	cmsg->p = 0;
+	cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+	pars_2_message(cmsg);
+
+	wordTLcpy(msg + 0, &cmsg->l);
+	byteTLcpy(cmsg->m + 4, &cmsg->Command);
+	byteTLcpy(cmsg->m + 5, &cmsg->Subcommand);
+	wordTLcpy(cmsg->m + 2, &cmsg->ApplId);
+	wordTLcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+	return 0;
+}
+
+/*-------------------------------------------------------*/
+static void message_2_pars(_cmsg * cmsg)
+{
+	for (; TYP != _CEND; cmsg->p++) {
+
+		switch (TYP) {
+		case _CBYTE:
+			byteTRcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l++;
+			break;
+		case _CWORD:
+			wordTRcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 2;
+			break;
+		case _CDWORD:
+			dwordTRcpy(cmsg->m + cmsg->l, OFF);
+			cmsg->l += 4;
+			break;
+		case _CSTRUCT:
+			*(u8 **) OFF = cmsg->m + cmsg->l;
+
+			if (cmsg->m[cmsg->l] != 0xff)
+				cmsg->l += 1 + cmsg->m[cmsg->l];
+			else
+				cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1);
+			break;
+		case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+			if (cmsg->m[cmsg->l] == '\0') {
+				*(_cmstruct *) OFF = CAPI_DEFAULT;
+				cmsg->l++;
+				jumpcstruct(cmsg);
+			} else {
+				unsigned _l = cmsg->l;
+				*(_cmstruct *) OFF = CAPI_COMPOSE;
+				cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+				cmsg->p++;
+				message_2_pars(cmsg);
+			}
+			break;
+		}
+	}
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_message2cmsg(_cmsg * cmsg, u8 * msg)
+{
+	memset(cmsg, 0, sizeof(_cmsg));
+	cmsg->m = msg;
+	cmsg->l = 8;
+	cmsg->p = 0;
+	byteTRcpy(cmsg->m + 4, &cmsg->Command);
+	byteTRcpy(cmsg->m + 5, &cmsg->Subcommand);
+	cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+	message_2_pars(cmsg);
+
+	wordTRcpy(msg + 0, &cmsg->l);
+	wordTRcpy(cmsg->m + 2, &cmsg->ApplId);
+	wordTRcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+	return 0;
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg_header(_cmsg * cmsg, u16 _ApplId,
+			  u8 _Command, u8 _Subcommand,
+			  u16 _Messagenumber, u32 _Controller)
+{
+	memset(cmsg, 0, sizeof(_cmsg));
+	cmsg->ApplId = _ApplId;
+	cmsg->Command = _Command;
+	cmsg->Subcommand = _Subcommand;
+	cmsg->Messagenumber = _Messagenumber;
+	cmsg->adr.adrController = _Controller;
+	return 0;
+}
+
+/*-------------------------------------------------------*/
+
+static char *mnames[] =
+{
+	[0x01] = "ALERT_REQ",
+	[0x02] = "CONNECT_REQ",
+	[0x04] = "DISCONNECT_REQ",
+	[0x05] = "LISTEN_REQ",
+	[0x08] = "INFO_REQ",
+	[0x09] = "FACILITY_REQ",
+	[0x0a] = "SELECT_B_PROTOCOL_REQ",
+	[0x0b] = "CONNECT_B3_REQ",
+	[0x0d] = "DISCONNECT_B3_REQ",
+	[0x0f] = "DATA_B3_REQ",
+	[0x10] = "RESET_B3_REQ",
+	[0x13] = "ALERT_CONF",
+	[0x14] = "CONNECT_CONF",
+	[0x16] = "DISCONNECT_CONF",
+	[0x17] = "LISTEN_CONF",
+	[0x18] = "MANUFACTURER_REQ",
+	[0x1a] = "INFO_CONF",
+	[0x1b] = "FACILITY_CONF",
+	[0x1c] = "SELECT_B_PROTOCOL_CONF",
+	[0x1d] = "CONNECT_B3_CONF",
+	[0x1f] = "DISCONNECT_B3_CONF",
+	[0x21] = "DATA_B3_CONF",
+	[0x22] = "RESET_B3_CONF",
+	[0x26] = "CONNECT_IND",
+	[0x27] = "CONNECT_ACTIVE_IND",
+	[0x28] = "DISCONNECT_IND",
+	[0x2a] = "MANUFACTURER_CONF",
+	[0x2c] = "INFO_IND",
+	[0x2d] = "FACILITY_IND",
+	[0x2f] = "CONNECT_B3_IND",
+	[0x30] = "CONNECT_B3_ACTIVE_IND",
+	[0x31] = "DISCONNECT_B3_IND",
+	[0x33] = "DATA_B3_IND",
+	[0x34] = "RESET_B3_IND",
+	[0x35] = "CONNECT_B3_T90_ACTIVE_IND",
+	[0x38] = "CONNECT_RESP",
+	[0x39] = "CONNECT_ACTIVE_RESP",
+	[0x3a] = "DISCONNECT_RESP",
+	[0x3c] = "MANUFACTURER_IND",
+	[0x3e] = "INFO_RESP",
+	[0x3f] = "FACILITY_RESP",
+	[0x41] = "CONNECT_B3_RESP",
+	[0x42] = "CONNECT_B3_ACTIVE_RESP",
+	[0x43] = "DISCONNECT_B3_RESP",
+	[0x45] = "DATA_B3_RESP",
+	[0x46] = "RESET_B3_RESP",
+	[0x47] = "CONNECT_B3_T90_ACTIVE_RESP",
+	[0x4e] = "MANUFACTURER_RESP"
+};
+
+char *capi_cmd2str(u8 cmd, u8 subcmd)
+{
+	return mnames[command_2_index(cmd, subcmd)];
+}
+
+
+/*-------------------------------------------------------*/
+/*-------------------------------------------------------*/
+
+static char *pnames[] =
+{
+    /*00 */ NULL,
+    /*01 */ NULL,
+    /*02 */ NULL,
+    /*03 */ "Controller/PLCI/NCCI",
+    /*04 */ "AdditionalInfo",
+    /*05 */ "B1configuration",
+    /*06 */ "B1protocol",
+    /*07 */ "B2configuration",
+    /*08 */ "B2protocol",
+    /*09 */ "B3configuration",
+    /*0a */ "B3protocol",
+    /*0b */ "BC",
+    /*0c */ "BChannelinformation",
+    /*0d */ "BProtocol",
+    /*0e */ "CalledPartyNumber",
+    /*0f */ "CalledPartySubaddress",
+    /*10 */ "CallingPartyNumber",
+    /*11 */ "CallingPartySubaddress",
+    /*12 */ "CIPmask",
+    /*13 */ "CIPmask2",
+    /*14 */ "CIPValue",
+    /*15 */ "Class",
+    /*16 */ "ConnectedNumber",
+    /*17 */ "ConnectedSubaddress",
+    /*18 */ "Data32",
+    /*19 */ "DataHandle",
+    /*1a */ "DataLength",
+    /*1b */ "FacilityConfirmationParameter",
+    /*1c */ "Facilitydataarray",
+    /*1d */ "FacilityIndicationParameter",
+    /*1e */ "FacilityRequestParameter",
+    /*1f */ "FacilitySelector",
+    /*20 */ "Flags",
+    /*21 */ "Function",
+    /*22 */ "HLC",
+    /*23 */ "Info",
+    /*24 */ "InfoElement",
+    /*25 */ "InfoMask",
+    /*26 */ "InfoNumber",
+    /*27 */ "Keypadfacility",
+    /*28 */ "LLC",
+    /*29 */ "ManuData",
+    /*2a */ "ManuID",
+    /*2b */ "NCPI",
+    /*2c */ "Reason",
+    /*2d */ "Reason_B3",
+    /*2e */ "Reject",
+    /*2f */ "Useruserdata"
+};
+
+
+static char buf[8192];
+static char *p = NULL;
+
+#include <stdarg.h>
+
+/*-------------------------------------------------------*/
+static void bufprint(char *fmt,...)
+{
+	va_list f;
+	va_start(f, fmt);
+	vsprintf(p, fmt, f);
+	va_end(f);
+	p += strlen(p);
+}
+
+static void printstructlen(u8 * m, unsigned len)
+{
+	unsigned hex = 0;
+	for (; len; len--, m++)
+		if (isalnum(*m) || *m == ' ') {
+			if (hex)
+				bufprint(">");
+			bufprint("%c", *m);
+			hex = 0;
+		} else {
+			if (!hex)
+				bufprint("<%02x", *m);
+			else
+				bufprint(" %02x", *m);
+			hex = 1;
+		}
+	if (hex)
+		bufprint(">");
+}
+
+static void printstruct(u8 * m)
+{
+	unsigned len;
+	if (m[0] != 0xff) {
+		len = m[0];
+		m += 1;
+	} else {
+		len = ((u16 *) (m + 1))[0];
+		m += 3;
+	}
+	printstructlen(m, len);
+}
+
+/*-------------------------------------------------------*/
+#define NAME (pnames[cmsg->par[cmsg->p]])
+
+static void protocol_message_2_pars(_cmsg * cmsg, int level)
+{
+	for (; TYP != _CEND; cmsg->p++) {
+		int slen = 29 + 3 - level;
+		int i;
+
+		bufprint("  ");
+		for (i = 0; i < level - 1; i++)
+			bufprint(" ");
+
+		switch (TYP) {
+		case _CBYTE:
+			bufprint("%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l));
+			cmsg->l++;
+			break;
+		case _CWORD:
+			bufprint("%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l));
+			cmsg->l += 2;
+			break;
+		case _CDWORD:
+			bufprint("%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l));
+			cmsg->l += 4;
+			break;
+		case _CSTRUCT:
+			bufprint("%-*s = ", slen, NAME);
+			if (cmsg->m[cmsg->l] == '\0')
+				bufprint("default");
+			else
+				printstruct(cmsg->m + cmsg->l);
+			bufprint("\n");
+			if (cmsg->m[cmsg->l] != 0xff)
+				cmsg->l += 1 + cmsg->m[cmsg->l];
+			else
+				cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1);
+
+			break;
+
+		case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+			if (cmsg->m[cmsg->l] == '\0') {
+				bufprint("%-*s = default\n", slen, NAME);
+				cmsg->l++;
+				jumpcstruct(cmsg);
+			} else {
+				char *name = NAME;
+				unsigned _l = cmsg->l;
+				bufprint("%-*s\n", slen, name);
+				cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+				cmsg->p++;
+				protocol_message_2_pars(cmsg, level + 1);
+			}
+			break;
+		}
+	}
+}
+/*-------------------------------------------------------*/
+char *capi_message2str(u8 * msg)
+{
+
+	_cmsg cmsg;
+	p = buf;
+	p[0] = 0;
+
+	cmsg.m = msg;
+	cmsg.l = 8;
+	cmsg.p = 0;
+	byteTRcpy(cmsg.m + 4, &cmsg.Command);
+	byteTRcpy(cmsg.m + 5, &cmsg.Subcommand);
+	cmsg.par = cpars[command_2_index(cmsg.Command, cmsg.Subcommand)];
+
+	bufprint("%-26s ID=%03d #0x%04x LEN=%04d\n",
+		 mnames[command_2_index(cmsg.Command, cmsg.Subcommand)],
+		 ((unsigned short *) msg)[1],
+		 ((unsigned short *) msg)[3],
+		 ((unsigned short *) msg)[0]);
+
+	protocol_message_2_pars(&cmsg, 1);
+	return buf;
+}
+
+char *capi_cmsg2str(_cmsg * cmsg)
+{
+	p = buf;
+	p[0] = 0;
+	cmsg->l = 8;
+	cmsg->p = 0;
+	bufprint("%s ID=%03d #0x%04x LEN=%04d\n",
+		 mnames[command_2_index(cmsg->Command, cmsg->Subcommand)],
+		 ((u16 *) cmsg->m)[1],
+		 ((u16 *) cmsg->m)[3],
+		 ((u16 *) cmsg->m)[0]);
+	protocol_message_2_pars(cmsg, 1);
+	return buf;
+}
+
+EXPORT_SYMBOL(capi_cmsg2message);
+EXPORT_SYMBOL(capi_message2cmsg);
+EXPORT_SYMBOL(capi_cmsg_header);
+EXPORT_SYMBOL(capi_cmd2str);
+EXPORT_SYMBOL(capi_cmsg2str);
+EXPORT_SYMBOL(capi_message2str);
+EXPORT_SYMBOL(capi_info2str);
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
new file mode 100644
index 0000000..feec40c
--- /dev/null
+++ b/drivers/isdn/capi/kcapi.c
@@ -0,0 +1,991 @@
+/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $
+ * 
+ * Kernel CAPI 2.0 Module
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define CONFIG_AVMB1_COMPAT
+
+#include "kcapi.h"
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#ifdef CONFIG_AVMB1_COMPAT
+#include <linux/b1lli.h>
+#endif
+
+static char *revision = "$Revision: 1.1.2.8 $";
+
+/* ------------------------------------------------------------- */
+
+static int showcapimsgs = 0;
+
+MODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(showcapimsgs, uint, 0);
+
+/* ------------------------------------------------------------- */
+
+struct capi_notifier {
+	struct work_struct work;
+	unsigned int cmd;
+	u32 controller;
+	u16 applid;
+	u32 ncci;
+};
+
+/* ------------------------------------------------------------- */
+
+static struct capi_version driver_version = {2, 0, 1, 1<<4};
+static char driver_serial[CAPI_SERIAL_LEN] = "0004711";
+static char capi_manufakturer[64] = "AVM Berlin";
+
+#define NCCI2CTRL(ncci)    (((ncci) >> 24) & 0x7f)
+
+LIST_HEAD(capi_drivers);
+DEFINE_RWLOCK(capi_drivers_list_lock);
+
+static DEFINE_RWLOCK(application_lock);
+static DECLARE_MUTEX(controller_sem);
+
+struct capi20_appl *capi_applications[CAPI_MAXAPPL];
+struct capi_ctr *capi_cards[CAPI_MAXCONTR];
+
+static int ncards;
+
+/* -------- controller ref counting -------------------------------------- */
+
+static inline struct capi_ctr *
+capi_ctr_get(struct capi_ctr *card)
+{
+	if (!try_module_get(card->owner))
+		return NULL;
+	return card;
+}
+
+static inline void
+capi_ctr_put(struct capi_ctr *card)
+{
+	module_put(card->owner);
+}
+
+/* ------------------------------------------------------------- */
+
+static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr)
+{
+	if (contr - 1 >= CAPI_MAXCONTR)
+		return NULL;
+
+	return capi_cards[contr - 1];
+}
+
+static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid)
+{
+	if (applid - 1 >= CAPI_MAXAPPL)
+		return NULL;
+
+	return capi_applications[applid - 1];
+}
+
+/* -------- util functions ------------------------------------ */
+
+static inline int capi_cmd_valid(u8 cmd)
+{
+	switch (cmd) {
+	case CAPI_ALERT:
+	case CAPI_CONNECT:
+	case CAPI_CONNECT_ACTIVE:
+	case CAPI_CONNECT_B3_ACTIVE:
+	case CAPI_CONNECT_B3:
+	case CAPI_CONNECT_B3_T90_ACTIVE:
+	case CAPI_DATA_B3:
+	case CAPI_DISCONNECT_B3:
+	case CAPI_DISCONNECT:
+	case CAPI_FACILITY:
+	case CAPI_INFO:
+	case CAPI_LISTEN:
+	case CAPI_MANUFACTURER:
+	case CAPI_RESET_B3:
+	case CAPI_SELECT_B_PROTOCOL:
+		return 1;
+	}
+	return 0;
+}
+
+static inline int capi_subcmd_valid(u8 subcmd)
+{
+	switch (subcmd) {
+	case CAPI_REQ:
+	case CAPI_CONF:
+	case CAPI_IND:
+	case CAPI_RESP:
+		return 1;
+	}
+	return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static void register_appl(struct capi_ctr *card, u16 applid, capi_register_params *rparam)
+{
+	card = capi_ctr_get(card);
+
+	if (card)
+		card->register_appl(card, applid, rparam);
+	else
+		printk(KERN_WARNING "%s: cannot get card resources\n", __FUNCTION__);
+}
+
+
+static void release_appl(struct capi_ctr *card, u16 applid)
+{
+	DBG("applid %#x", applid);
+	
+	card->release_appl(card, applid);
+	capi_ctr_put(card);
+}
+
+/* -------- KCI_CONTRUP --------------------------------------- */
+
+static void notify_up(u32 contr)
+{
+	struct capi_ctr *card = get_capi_ctr_by_nr(contr);
+	struct capi20_appl *ap;
+	u16 applid;
+
+	if (showcapimsgs & 1) {
+	        printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr);
+	}
+	if (!card) {
+		printk(KERN_WARNING "%s: invalid contr %d\n", __FUNCTION__, contr);
+		return;
+	}
+	for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+		ap = get_capi_appl_by_nr(applid);
+		if (!ap || ap->release_in_progress) continue;
+		register_appl(card, applid, &ap->rparam);
+		if (ap->callback && !ap->release_in_progress)
+			ap->callback(KCI_CONTRUP, contr, &card->profile);
+	}
+}
+
+/* -------- KCI_CONTRDOWN ------------------------------------- */
+
+static void notify_down(u32 contr)
+{
+	struct capi20_appl *ap;
+	u16 applid;
+
+	if (showcapimsgs & 1) {
+        	printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr);
+	}
+
+	for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+		ap = get_capi_appl_by_nr(applid);
+		if (ap && ap->callback && !ap->release_in_progress)
+			ap->callback(KCI_CONTRDOWN, contr, NULL);
+	}
+}
+
+static void notify_handler(void *data)
+{
+	struct capi_notifier *np = data;
+
+	switch (np->cmd) {
+	case KCI_CONTRUP:
+		notify_up(np->controller);
+		break;
+	case KCI_CONTRDOWN:
+		notify_down(np->controller);
+		break;
+	}
+
+	kfree(np);
+}
+
+/*
+ * The notifier will result in adding/deleteing of devices. Devices can
+ * only removed in user process, not in bh.
+ */
+static int notify_push(unsigned int cmd, u32 controller, u16 applid, u32 ncci)
+{
+	struct capi_notifier *np = kmalloc(sizeof(*np), GFP_ATOMIC);
+
+	if (!np)
+		return -ENOMEM;
+
+	INIT_WORK(&np->work, notify_handler, np);
+	np->cmd = cmd;
+	np->controller = controller;
+	np->applid = applid;
+	np->ncci = ncci;
+
+	schedule_work(&np->work);
+	return 0;
+}
+
+	
+/* -------- Receiver ------------------------------------------ */
+
+static void recv_handler(void *_ap)
+{
+	struct sk_buff *skb;
+	struct capi20_appl *ap = (struct capi20_appl *) _ap;
+
+	if ((!ap) || (ap->release_in_progress))
+		return;
+
+	down(&ap->recv_sem);
+	while ((skb = skb_dequeue(&ap->recv_queue))) {
+		if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND)
+			ap->nrecvdatapkt++;
+		else
+			ap->nrecvctlpkt++;
+
+		ap->recv_message(ap, skb);
+	}
+	up(&ap->recv_sem);
+}
+
+void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb)
+{
+	struct capi20_appl *ap;
+	int showctl = 0;
+	u8 cmd, subcmd;
+	unsigned long flags;
+
+	if (card->cardstate != CARD_RUNNING) {
+		printk(KERN_INFO "kcapi: controller %d not active, got: %s",
+		       card->cnr, capi_message2str(skb->data));
+		goto error;
+	}
+
+	cmd = CAPIMSG_COMMAND(skb->data);
+        subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+	if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
+		card->nrecvdatapkt++;
+	        if (card->traceflag > 2) showctl |= 2;
+	} else {
+		card->nrecvctlpkt++;
+	        if (card->traceflag) showctl |= 2;
+	}
+	showctl |= (card->traceflag & 1);
+	if (showctl & 2) {
+		if (showctl & 1) {
+			printk(KERN_DEBUG "kcapi: got [0x%lx] id#%d %s len=%u\n",
+			       (unsigned long) card->cnr,
+			       CAPIMSG_APPID(skb->data),
+			       capi_cmd2str(cmd, subcmd),
+			       CAPIMSG_LEN(skb->data));
+		} else {
+			printk(KERN_DEBUG "kcapi: got [0x%lx] %s\n",
+					(unsigned long) card->cnr,
+					capi_message2str(skb->data));
+		}
+
+	}
+
+	read_lock_irqsave(&application_lock, flags);
+	ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
+	if ((!ap) || (ap->release_in_progress)) {
+		read_unlock_irqrestore(&application_lock, flags);
+		printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n",
+			CAPIMSG_APPID(skb->data), capi_message2str(skb->data));
+		goto error;
+	}
+	skb_queue_tail(&ap->recv_queue, skb);
+	schedule_work(&ap->recv_work);
+	read_unlock_irqrestore(&application_lock, flags);
+
+	return;
+
+error:
+	kfree_skb(skb);
+}
+
+EXPORT_SYMBOL(capi_ctr_handle_message);
+
+void capi_ctr_ready(struct capi_ctr * card)
+{
+	card->cardstate = CARD_RUNNING;
+
+        printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+	       card->cnr, card->name);
+
+	notify_push(KCI_CONTRUP, card->cnr, 0, 0);
+}
+
+EXPORT_SYMBOL(capi_ctr_ready);
+
+void capi_ctr_reseted(struct capi_ctr * card)
+{
+	u16 appl;
+
+	DBG("");
+
+        if (card->cardstate == CARD_DETECTED)
+		return;
+
+        card->cardstate = CARD_DETECTED;
+
+	memset(card->manu, 0, sizeof(card->manu));
+	memset(&card->version, 0, sizeof(card->version));
+	memset(&card->profile, 0, sizeof(card->profile));
+	memset(card->serial, 0, sizeof(card->serial));
+
+	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+		struct capi20_appl *ap = get_capi_appl_by_nr(appl);
+		if (!ap || ap->release_in_progress)
+			continue;
+
+		capi_ctr_put(card);
+	}
+
+	printk(KERN_NOTICE "kcapi: card %d down.\n", card->cnr);
+
+	notify_push(KCI_CONTRDOWN, card->cnr, 0, 0);
+}
+
+EXPORT_SYMBOL(capi_ctr_reseted);
+
+void capi_ctr_suspend_output(struct capi_ctr *card)
+{
+	if (!card->blocked) {
+		printk(KERN_DEBUG "kcapi: card %d suspend\n", card->cnr);
+		card->blocked = 1;
+	}
+}
+
+EXPORT_SYMBOL(capi_ctr_suspend_output);
+
+void capi_ctr_resume_output(struct capi_ctr *card)
+{
+	if (card->blocked) {
+		printk(KERN_DEBUG "kcapi: card %d resume\n", card->cnr);
+		card->blocked = 0;
+	}
+}
+
+EXPORT_SYMBOL(capi_ctr_resume_output);
+
+/* ------------------------------------------------------------- */
+
+int
+attach_capi_ctr(struct capi_ctr *card)
+{
+	int i;
+
+	down(&controller_sem);
+
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		if (capi_cards[i] == NULL)
+			break;
+	}
+	if (i == CAPI_MAXCONTR) {
+		up(&controller_sem);
+		printk(KERN_ERR "kcapi: out of controller slots\n");
+	   	return -EBUSY;
+	}
+	capi_cards[i] = card;
+
+	up(&controller_sem);
+
+	card->nrecvctlpkt = 0;
+	card->nrecvdatapkt = 0;
+	card->nsentctlpkt = 0;
+	card->nsentdatapkt = 0;
+	card->cnr = i + 1;
+	card->cardstate = CARD_DETECTED;
+	card->blocked = 0;
+	card->traceflag = showcapimsgs;
+
+	sprintf(card->procfn, "capi/controllers/%d", card->cnr);
+	card->procent = create_proc_entry(card->procfn, 0, NULL);
+	if (card->procent) {
+	   card->procent->read_proc = 
+		(int (*)(char *,char **,off_t,int,int *,void *))
+			card->ctr_read_proc;
+	   card->procent->data = card;
+	}
+
+	ncards++;
+	printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n",
+			card->cnr, card->name);
+	return 0;
+}
+
+EXPORT_SYMBOL(attach_capi_ctr);
+
+int detach_capi_ctr(struct capi_ctr *card)
+{
+        if (card->cardstate != CARD_DETECTED)
+		capi_ctr_reseted(card);
+
+	ncards--;
+
+	if (card->procent) {
+	   remove_proc_entry(card->procfn, NULL);
+	   card->procent = NULL;
+	}
+	capi_cards[card->cnr - 1] = NULL;
+	printk(KERN_NOTICE "kcapi: Controller %d: %s unregistered\n",
+			card->cnr, card->name);
+
+	return 0;
+}
+
+EXPORT_SYMBOL(detach_capi_ctr);
+
+void register_capi_driver(struct capi_driver *driver)
+{
+	unsigned long flags;
+
+	write_lock_irqsave(&capi_drivers_list_lock, flags);
+	list_add_tail(&driver->list, &capi_drivers);
+	write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+}
+
+EXPORT_SYMBOL(register_capi_driver);
+
+void unregister_capi_driver(struct capi_driver *driver)
+{
+	unsigned long flags;
+
+	write_lock_irqsave(&capi_drivers_list_lock, flags);
+	list_del(&driver->list);
+	write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+}
+
+EXPORT_SYMBOL(unregister_capi_driver);
+
+/* ------------------------------------------------------------- */
+/* -------- CAPI2.0 Interface ---------------------------------- */
+/* ------------------------------------------------------------- */
+
+u16 capi20_isinstalled(void)
+{
+	int i;
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		if (capi_cards[i] && capi_cards[i]->cardstate == CARD_RUNNING)
+			return CAPI_NOERROR;
+	}
+	return CAPI_REGNOTINSTALLED;
+}
+
+EXPORT_SYMBOL(capi20_isinstalled);
+
+u16 capi20_register(struct capi20_appl *ap)
+{
+	int i;
+	u16 applid;
+	unsigned long flags;
+
+	DBG("");
+
+	if (ap->rparam.datablklen < 128)
+		return CAPI_LOGBLKSIZETOSMALL;
+
+	write_lock_irqsave(&application_lock, flags);
+
+	for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+		if (capi_applications[applid - 1] == NULL)
+			break;
+	}
+	if (applid > CAPI_MAXAPPL) {
+		write_unlock_irqrestore(&application_lock, flags);
+		return CAPI_TOOMANYAPPLS;
+	}
+
+	ap->applid = applid;
+	capi_applications[applid - 1] = ap;
+
+	ap->nrecvctlpkt = 0;
+	ap->nrecvdatapkt = 0;
+	ap->nsentctlpkt = 0;
+	ap->nsentdatapkt = 0;
+	ap->callback = NULL;
+	init_MUTEX(&ap->recv_sem);
+	skb_queue_head_init(&ap->recv_queue);
+	INIT_WORK(&ap->recv_work, recv_handler, (void *)ap);
+	ap->release_in_progress = 0;
+
+	write_unlock_irqrestore(&application_lock, flags);
+	
+	down(&controller_sem);
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+			continue;
+		register_appl(capi_cards[i], applid, &ap->rparam);
+	}
+	up(&controller_sem);
+
+	if (showcapimsgs & 1) {
+		printk(KERN_DEBUG "kcapi: appl %d up\n", applid);
+	}
+
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_register);
+
+u16 capi20_release(struct capi20_appl *ap)
+{
+	int i;
+	unsigned long flags;
+
+	DBG("applid %#x", ap->applid);
+
+	write_lock_irqsave(&application_lock, flags);
+	ap->release_in_progress = 1;
+	capi_applications[ap->applid - 1] = NULL;
+	write_unlock_irqrestore(&application_lock, flags);
+
+	down(&controller_sem);
+	for (i = 0; i < CAPI_MAXCONTR; i++) {
+		if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+			continue;
+		release_appl(capi_cards[i], ap->applid);
+	}
+	up(&controller_sem);
+
+	flush_scheduled_work();
+	skb_queue_purge(&ap->recv_queue);
+
+	if (showcapimsgs & 1) {
+		printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid);
+	}
+
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_release);
+
+u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+	struct capi_ctr *card;
+	int showctl = 0;
+	u8 cmd, subcmd;
+
+	DBG("applid %#x", ap->applid);
+ 
+	if (ncards == 0)
+		return CAPI_REGNOTINSTALLED;
+	if ((ap->applid == 0) || ap->release_in_progress)
+		return CAPI_ILLAPPNR;
+	if (skb->len < 12
+	    || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
+	    || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
+		return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+	card = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data));
+	if (!card || card->cardstate != CARD_RUNNING) {
+		card = get_capi_ctr_by_nr(1); // XXX why?
+	        if (!card || card->cardstate != CARD_RUNNING) 
+			return CAPI_REGNOTINSTALLED;
+	}
+	if (card->blocked)
+		return CAPI_SENDQUEUEFULL;
+
+	cmd = CAPIMSG_COMMAND(skb->data);
+        subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+	if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) {
+		card->nsentdatapkt++;
+		ap->nsentdatapkt++;
+	        if (card->traceflag > 2) showctl |= 2;
+	} else {
+		card->nsentctlpkt++;
+		ap->nsentctlpkt++;
+	        if (card->traceflag) showctl |= 2;
+	}
+	showctl |= (card->traceflag & 1);
+	if (showctl & 2) {
+		if (showctl & 1) {
+			printk(KERN_DEBUG "kcapi: put [%#x] id#%d %s len=%u\n",
+			       CAPIMSG_CONTROLLER(skb->data),
+			       CAPIMSG_APPID(skb->data),
+			       capi_cmd2str(cmd, subcmd),
+			       CAPIMSG_LEN(skb->data));
+		} else {
+			printk(KERN_DEBUG "kcapi: put [%#x] %s\n",
+			       CAPIMSG_CONTROLLER(skb->data),
+			       capi_message2str(skb->data));
+		}
+
+	}
+	return card->send_message(card, skb);
+}
+
+EXPORT_SYMBOL(capi20_put_message);
+
+u16 capi20_get_manufacturer(u32 contr, u8 *buf)
+{
+	struct capi_ctr *card;
+
+	if (contr == 0) {
+		strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
+		return CAPI_NOERROR;
+	}
+	card = get_capi_ctr_by_nr(contr);
+	if (!card || card->cardstate != CARD_RUNNING) 
+		return CAPI_REGNOTINSTALLED;
+	strlcpy(buf, card->manu, CAPI_MANUFACTURER_LEN);
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_manufacturer);
+
+u16 capi20_get_version(u32 contr, struct capi_version *verp)
+{
+	struct capi_ctr *card;
+
+	if (contr == 0) {
+		*verp = driver_version;
+		return CAPI_NOERROR;
+	}
+	card = get_capi_ctr_by_nr(contr);
+	if (!card || card->cardstate != CARD_RUNNING) 
+		return CAPI_REGNOTINSTALLED;
+
+	memcpy((void *) verp, &card->version, sizeof(capi_version));
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_version);
+
+u16 capi20_get_serial(u32 contr, u8 *serial)
+{
+	struct capi_ctr *card;
+
+	if (contr == 0) {
+		strlcpy(serial, driver_serial, CAPI_SERIAL_LEN);
+		return CAPI_NOERROR;
+	}
+	card = get_capi_ctr_by_nr(contr);
+	if (!card || card->cardstate != CARD_RUNNING) 
+		return CAPI_REGNOTINSTALLED;
+
+	strlcpy((void *) serial, card->serial, CAPI_SERIAL_LEN);
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_serial);
+
+u16 capi20_get_profile(u32 contr, struct capi_profile *profp)
+{
+	struct capi_ctr *card;
+
+	if (contr == 0) {
+		profp->ncontroller = ncards;
+		return CAPI_NOERROR;
+	}
+	card = get_capi_ctr_by_nr(contr);
+	if (!card || card->cardstate != CARD_RUNNING) 
+		return CAPI_REGNOTINSTALLED;
+
+	memcpy((void *) profp, &card->profile,
+			sizeof(struct capi_profile));
+	return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_profile);
+
+#ifdef CONFIG_AVMB1_COMPAT
+static int old_capi_manufacturer(unsigned int cmd, void __user *data)
+{
+	avmb1_loadandconfigdef ldef;
+	avmb1_extcarddef cdef;
+	avmb1_resetdef rdef;
+	capicardparams cparams;
+	struct capi_ctr *card;
+	struct capi_driver *driver = NULL;
+	capiloaddata ldata;
+	struct list_head *l;
+	unsigned long flags;
+	int retval;
+
+	switch (cmd) {
+	case AVMB1_ADDCARD:
+	case AVMB1_ADDCARD_WITH_TYPE:
+		if (cmd == AVMB1_ADDCARD) {
+		   if ((retval = copy_from_user(&cdef, data,
+					    sizeof(avmb1_carddef))))
+			   return retval;
+		   cdef.cardtype = AVM_CARDTYPE_B1;
+		} else {
+		   if ((retval = copy_from_user(&cdef, data,
+					    sizeof(avmb1_extcarddef))))
+			   return retval;
+		}
+		cparams.port = cdef.port;
+		cparams.irq = cdef.irq;
+		cparams.cardnr = cdef.cardnr;
+
+		read_lock_irqsave(&capi_drivers_list_lock, flags);
+                switch (cdef.cardtype) {
+			case AVM_CARDTYPE_B1:
+				list_for_each(l, &capi_drivers) {
+					driver = list_entry(l, struct capi_driver, list);
+					if (strcmp(driver->name, "b1isa") == 0)
+						break;
+				}
+				break;
+			case AVM_CARDTYPE_T1:
+				list_for_each(l, &capi_drivers) {
+					driver = list_entry(l, struct capi_driver, list);
+					if (strcmp(driver->name, "t1isa") == 0)
+						break;
+				}
+				break;
+			default:
+				driver = NULL;
+				break;
+		}
+		if (!driver) {
+			read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+			printk(KERN_ERR "kcapi: driver not loaded.\n");
+			return -EIO;
+		}
+		if (!driver->add_card) {
+			read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+			printk(KERN_ERR "kcapi: driver has no add card function.\n");
+			return -EIO;
+		}
+
+		retval = driver->add_card(driver, &cparams);
+		read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+		return retval;
+
+	case AVMB1_LOAD:
+	case AVMB1_LOAD_AND_CONFIG:
+
+		if (cmd == AVMB1_LOAD) {
+			if (copy_from_user(&ldef, data,
+					   sizeof(avmb1_loaddef)))
+				return -EFAULT;
+			ldef.t4config.len = 0;
+			ldef.t4config.data = NULL;
+		} else {
+			if (copy_from_user(&ldef, data,
+					   sizeof(avmb1_loadandconfigdef)))
+				return -EFAULT;
+		}
+		card = get_capi_ctr_by_nr(ldef.contr);
+		card = capi_ctr_get(card);
+		if (!card)
+			return -ESRCH;
+		if (card->load_firmware == 0) {
+			printk(KERN_DEBUG "kcapi: load: no load function\n");
+			return -ESRCH;
+		}
+
+		if (ldef.t4file.len <= 0) {
+			printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
+			return -EINVAL;
+		}
+		if (ldef.t4file.data == 0) {
+			printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n");
+			return -EINVAL;
+		}
+
+		ldata.firmware.user = 1;
+		ldata.firmware.data = ldef.t4file.data;
+		ldata.firmware.len = ldef.t4file.len;
+		ldata.configuration.user = 1;
+		ldata.configuration.data = ldef.t4config.data;
+		ldata.configuration.len = ldef.t4config.len;
+
+		if (card->cardstate != CARD_DETECTED) {
+			printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr);
+			return -EBUSY;
+		}
+		card->cardstate = CARD_LOADING;
+
+		retval = card->load_firmware(card, &ldata);
+
+		if (retval) {
+			card->cardstate = CARD_DETECTED;
+			capi_ctr_put(card);
+			return retval;
+		}
+
+		while (card->cardstate != CARD_RUNNING) {
+
+			msleep_interruptible(100);	/* 0.1 sec */
+
+			if (signal_pending(current)) {
+				capi_ctr_put(card);
+				return -EINTR;
+			}
+		}
+		capi_ctr_put(card);
+		return 0;
+
+	case AVMB1_RESETCARD:
+		if (copy_from_user(&rdef, data, sizeof(avmb1_resetdef)))
+			return -EFAULT;
+		card = get_capi_ctr_by_nr(rdef.contr);
+		if (!card)
+			return -ESRCH;
+
+		if (card->cardstate == CARD_DETECTED)
+			return 0;
+
+		card->reset_ctr(card);
+
+		while (card->cardstate > CARD_DETECTED) {
+
+			msleep_interruptible(100);	/* 0.1 sec */
+
+			if (signal_pending(current))
+				return -EINTR;
+		}
+		return 0;
+
+	}
+	return -EINVAL;
+}
+#endif
+
+int capi20_manufacturer(unsigned int cmd, void __user *data)
+{
+        struct capi_ctr *card;
+
+	switch (cmd) {
+#ifdef CONFIG_AVMB1_COMPAT
+	case AVMB1_LOAD:
+	case AVMB1_LOAD_AND_CONFIG:
+	case AVMB1_RESETCARD:
+	case AVMB1_GET_CARDINFO:
+	case AVMB1_REMOVECARD:
+		return old_capi_manufacturer(cmd, data);
+#endif
+	case KCAPI_CMD_TRACE:
+	{
+		kcapi_flagdef fdef;
+
+		if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef)))
+			return -EFAULT;
+
+		card = get_capi_ctr_by_nr(fdef.contr);
+		if (!card)
+			return -ESRCH;
+
+		card->traceflag = fdef.flag;
+		printk(KERN_INFO "kcapi: contr %d set trace=%d\n",
+			card->cnr, card->traceflag);
+		return 0;
+	}
+	case KCAPI_CMD_ADDCARD:
+	{
+		struct list_head *l;
+		struct capi_driver *driver = NULL;
+		capicardparams cparams;
+		kcapi_carddef cdef;
+		int retval;
+
+		if ((retval = copy_from_user(&cdef, data, sizeof(cdef))))
+			return retval;
+
+		cparams.port = cdef.port;
+		cparams.irq = cdef.irq;
+		cparams.membase = cdef.membase;
+		cparams.cardnr = cdef.cardnr;
+		cparams.cardtype = 0;
+		cdef.driver[sizeof(cdef.driver)-1] = 0;
+
+		list_for_each(l, &capi_drivers) {
+			driver = list_entry(l, struct capi_driver, list);
+			if (strcmp(driver->name, cdef.driver) == 0)
+				break;
+		}
+		if (driver == 0) {
+			printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n",
+					cdef.driver);
+			return -ESRCH;
+		}
+
+		if (!driver->add_card) {
+			printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver);
+			return -EIO;
+		}
+
+		return driver->add_card(driver, &cparams);
+	}
+
+	default:
+		printk(KERN_ERR "kcapi: manufacturer command %d unknown.\n",
+					cmd);
+		break;
+
+	}
+	return -EINVAL;
+}
+
+EXPORT_SYMBOL(capi20_manufacturer);
+
+/* temporary hack */
+void capi20_set_callback(struct capi20_appl *ap,
+			 void (*callback) (unsigned int cmd, __u32 contr, void *data))
+{
+	ap->callback = callback;
+}
+
+EXPORT_SYMBOL(capi20_set_callback);
+
+/* ------------------------------------------------------------- */
+/* -------- Init & Cleanup ------------------------------------- */
+/* ------------------------------------------------------------- */
+
+/*
+ * init / exit functions
+ */
+
+static int __init kcapi_init(void)
+{
+	char *p;
+	char rev[32];
+
+        kcapi_proc_init();
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, sizeof(rev));
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+        printk(KERN_NOTICE "CAPI Subsystem Rev %s\n", rev);
+
+	return 0;
+}
+
+static void __exit kcapi_exit(void)
+{
+        kcapi_proc_exit();
+
+	/* make sure all notifiers are finished */
+	flush_scheduled_work();
+}
+
+module_init(kcapi_init);
+module_exit(kcapi_exit);
diff --git a/drivers/isdn/capi/kcapi.h b/drivers/isdn/capi/kcapi.h
new file mode 100644
index 0000000..1cb2c40
--- /dev/null
+++ b/drivers/isdn/capi/kcapi.h
@@ -0,0 +1,49 @@
+/*
+ * Kernel CAPI 2.0 Module
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/isdn/capilli.h>
+
+#ifdef KCAPI_DEBUG
+#define DBG(format, arg...) do { \
+printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \
+} while (0)
+#else
+#define DBG(format, arg...) /* */
+#endif
+
+enum {
+	CARD_DETECTED = 1,
+	CARD_LOADING  =	2,
+	CARD_RUNNING  = 3,
+};
+
+extern struct list_head capi_drivers;
+extern rwlock_t capi_drivers_list_lock;
+
+extern struct capi20_appl *capi_applications[CAPI_MAXAPPL];
+extern struct capi_ctr *capi_cards[CAPI_MAXCONTR];
+
+#ifdef CONFIG_PROC_FS
+
+void kcapi_proc_init(void);
+void kcapi_proc_exit(void);
+
+#else
+
+static inline void kcapi_proc_init(void) { };
+static inline void kcapi_proc_exit(void) { };
+
+#endif
+
diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c
new file mode 100644
index 0000000..16dc541
--- /dev/null
+++ b/drivers/isdn/capi/kcapi_proc.c
@@ -0,0 +1,336 @@
+/*
+ * Kernel CAPI 2.0 Module - /proc/capi handling
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include "kcapi.h"
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+
+static char *
+cardstate2str(unsigned short cardstate)
+{
+	switch (cardstate) {
+	case CARD_DETECTED:	return "detected";
+	case CARD_LOADING:	return "loading";
+	case CARD_RUNNING:	return "running";
+	default:	        return "???";
+	}
+}
+
+// /proc/capi
+// ===========================================================================
+
+// /proc/capi/controller: 
+//      cnr driver cardstate name driverinfo
+// /proc/capi/contrstats:
+//      cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+// ---------------------------------------------------------------------------
+
+static void *controller_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos < CAPI_MAXCONTR)
+		return &capi_cards[*pos];
+
+	return NULL;
+}
+
+static void *controller_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos < CAPI_MAXCONTR)
+		return &capi_cards[*pos];
+
+	return NULL;
+}
+
+static void controller_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int controller_show(struct seq_file *seq, void *v)
+{
+	struct capi_ctr *ctr = *(struct capi_ctr **) v;
+
+	if (!ctr)
+		return 0;
+
+	seq_printf(seq, "%d %-10s %-8s %-16s %s\n",
+		   ctr->cnr, ctr->driver_name,
+		   cardstate2str(ctr->cardstate),
+		   ctr->name,
+		   ctr->procinfo ?  ctr->procinfo(ctr) : "");
+
+	return 0;
+}
+
+static int contrstats_show(struct seq_file *seq, void *v)
+{
+	struct capi_ctr *ctr = *(struct capi_ctr **) v;
+
+	if (!ctr)
+		return 0;
+
+	seq_printf(seq, "%d %lu %lu %lu %lu\n",
+		   ctr->cnr, 
+		   ctr->nrecvctlpkt,
+		   ctr->nrecvdatapkt,
+		   ctr->nsentctlpkt,
+		   ctr->nsentdatapkt);
+
+	return 0;
+}
+
+struct seq_operations seq_controller_ops = {
+	.start	= controller_start,
+	.next	= controller_next,
+	.stop	= controller_stop,
+	.show	= controller_show,
+};
+
+struct seq_operations seq_contrstats_ops = {
+	.start	= controller_start,
+	.next	= controller_next,
+	.stop	= controller_stop,
+	.show	= contrstats_show,
+};
+
+static int seq_controller_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &seq_controller_ops);
+}
+
+static int seq_contrstats_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &seq_contrstats_ops);
+}
+
+static struct file_operations proc_controller_ops = {
+	.open		= seq_controller_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct file_operations proc_contrstats_ops = {
+	.open		= seq_contrstats_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+// /proc/capi/applications: 
+//      applid l3cnt dblkcnt dblklen #ncci recvqueuelen
+// /proc/capi/applstats: 
+//      applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+// ---------------------------------------------------------------------------
+
+static void *
+applications_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos < CAPI_MAXAPPL)
+		return &capi_applications[*pos];
+
+	return NULL;
+}
+
+static void *
+applications_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos < CAPI_MAXAPPL)
+		return &capi_applications[*pos];
+
+	return NULL;
+}
+
+static void
+applications_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int
+applications_show(struct seq_file *seq, void *v)
+{
+	struct capi20_appl *ap = *(struct capi20_appl **) v;
+
+	if (!ap)
+		return 0;
+
+	seq_printf(seq, "%u %d %d %d\n",
+		   ap->applid,
+		   ap->rparam.level3cnt,
+		   ap->rparam.datablkcnt,
+		   ap->rparam.datablklen);
+
+	return 0;
+}
+
+static int
+applstats_show(struct seq_file *seq, void *v)
+{
+	struct capi20_appl *ap = *(struct capi20_appl **) v;
+
+	if (!ap)
+		return 0;
+
+	seq_printf(seq, "%u %lu %lu %lu %lu\n",
+		   ap->applid,
+		   ap->nrecvctlpkt,
+		   ap->nrecvdatapkt,
+		   ap->nsentctlpkt,
+		   ap->nsentdatapkt);
+
+	return 0;
+}
+
+struct seq_operations seq_applications_ops = {
+	.start	= applications_start,
+	.next	= applications_next,
+	.stop	= applications_stop,
+	.show	= applications_show,
+};
+
+struct seq_operations seq_applstats_ops = {
+	.start	= applications_start,
+	.next	= applications_next,
+	.stop	= applications_stop,
+	.show	= applstats_show,
+};
+
+static int
+seq_applications_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &seq_applications_ops);
+}
+
+static int
+seq_applstats_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &seq_applstats_ops);
+}
+
+static struct file_operations proc_applications_ops = {
+	.open		= seq_applications_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct file_operations proc_applstats_ops = {
+	.open		= seq_applstats_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static void
+create_seq_entry(char *name, mode_t mode, struct file_operations *f)
+{
+	struct proc_dir_entry *entry;
+	entry = create_proc_entry(name, mode, NULL);
+	if (entry)
+		entry->proc_fops = f;
+}
+
+// ---------------------------------------------------------------------------
+
+
+static __inline__ struct capi_driver *capi_driver_get_idx(loff_t pos)
+{
+	struct capi_driver *drv = NULL;
+	struct list_head *l;
+	loff_t i;
+
+	i = 0;
+	list_for_each(l, &capi_drivers) {
+		drv = list_entry(l, struct capi_driver, list);
+		if (i++ == pos)
+			return drv;
+	}
+	return NULL;
+}
+
+static void *capi_driver_start(struct seq_file *seq, loff_t *pos)
+{
+	struct capi_driver *drv;
+	read_lock(&capi_drivers_list_lock);
+	drv = capi_driver_get_idx(*pos);
+	return drv;
+}
+
+static void *capi_driver_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct capi_driver *drv = (struct capi_driver *)v;
+	++*pos;
+	if (drv->list.next == &capi_drivers) return NULL;
+	return list_entry(drv->list.next, struct capi_driver, list);
+}
+
+static void capi_driver_stop(struct seq_file *seq, void *v)
+{
+	read_unlock(&capi_drivers_list_lock);
+}
+
+static int capi_driver_show(struct seq_file *seq, void *v)
+{
+	struct capi_driver *drv = (struct capi_driver *)v;
+	seq_printf(seq, "%-32s %s\n", drv->name, drv->revision);
+	return 0;
+}
+
+struct seq_operations seq_capi_driver_ops = {
+	.start	= capi_driver_start,
+	.next	= capi_driver_next,
+	.stop	= capi_driver_stop,
+	.show	= capi_driver_show,
+};
+
+static int
+seq_capi_driver_open(struct inode *inode, struct file *file)
+{
+	int err;
+	err = seq_open(file, &seq_capi_driver_ops);
+	return err;
+}
+
+static struct file_operations proc_driver_ops = {
+	.open		= seq_capi_driver_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+// ---------------------------------------------------------------------------
+
+void __init 
+kcapi_proc_init(void)
+{
+	proc_mkdir("capi",             NULL);
+	proc_mkdir("capi/controllers", NULL);
+	create_seq_entry("capi/controller",   0, &proc_controller_ops);
+	create_seq_entry("capi/contrstats",   0, &proc_contrstats_ops);
+	create_seq_entry("capi/applications", 0, &proc_applications_ops);
+	create_seq_entry("capi/applstats",    0, &proc_applstats_ops);
+	create_seq_entry("capi/driver",       0, &proc_driver_ops);
+}
+
+void __exit
+kcapi_proc_exit(void)
+{
+	remove_proc_entry("capi/driver",       NULL);
+	remove_proc_entry("capi/controller",   NULL);
+	remove_proc_entry("capi/contrstats",   NULL);
+	remove_proc_entry("capi/applications", NULL);
+	remove_proc_entry("capi/applstats",    NULL);
+	remove_proc_entry("capi/controllers",  NULL);
+	remove_proc_entry("capi",              NULL);
+}
diff --git a/drivers/isdn/divert/Makefile b/drivers/isdn/divert/Makefile
new file mode 100644
index 0000000..dd4a202
--- /dev/null
+++ b/drivers/isdn/divert/Makefile
@@ -0,0 +1,9 @@
+# Makefile for the dss1_divert ISDN module
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DIVERSION)	+= dss1_divert.o
+
+# Multipart objects.
+
+dss1_divert-y			:= isdn_divert.o divert_procfs.o divert_init.o
diff --git a/drivers/isdn/divert/divert_init.c b/drivers/isdn/divert/divert_init.c
new file mode 100644
index 0000000..434e684
--- /dev/null
+++ b/drivers/isdn/divert/divert_init.c
@@ -0,0 +1,83 @@
+/* $Id divert_init.c,v 1.5.6.2 2001/01/24 22:18:17 kai Exp $
+ *
+ * Module init for DSS1 diversion services for i4l.
+ *
+ * Copyright 1999       by Werner Cornelius (werner@isdn4linux.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include "isdn_divert.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Call diversion support");
+MODULE_AUTHOR("Werner Cornelius");
+MODULE_LICENSE("GPL");
+
+/****************************************/
+/* structure containing interface to hl */
+/****************************************/
+isdn_divert_if divert_if =
+  { DIVERT_IF_MAGIC,  /* magic value */
+    DIVERT_CMD_REG,   /* register cmd */
+    ll_callback,      /* callback routine from ll */
+    NULL,             /* command still not specified */
+    NULL,             /* drv_to_name */
+    NULL,             /* name_to_drv */
+  };
+
+/*************************/
+/* Module interface code */
+/* no cmd line parms     */
+/*************************/
+static int __init divert_init(void)
+{ int i;
+
+  if (divert_dev_init())
+   { printk(KERN_WARNING "dss1_divert: cannot install device, not loaded\n");
+     return(-EIO);
+   }
+  if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR)
+   { divert_dev_deinit();
+     printk(KERN_WARNING "dss1_divert: error %d registering module, not loaded\n",i);
+     return(-EIO);
+   } 
+  printk(KERN_INFO "dss1_divert module successfully installed\n");
+  return(0);
+}
+
+/**********************/
+/* Module deinit code */
+/**********************/
+static void __exit divert_exit(void)
+{
+  unsigned long flags;
+  int i;
+
+  spin_lock_irqsave(&divert_lock, flags);
+  divert_if.cmd = DIVERT_CMD_REL; /* release */
+  if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR)
+   { printk(KERN_WARNING "dss1_divert: error %d releasing module\n",i);
+     spin_unlock_irqrestore(&divert_lock, flags);
+     return;
+   } 
+  if (divert_dev_deinit()) 
+   { printk(KERN_WARNING "dss1_divert: device busy, remove cancelled\n");
+     spin_unlock_irqrestore(&divert_lock, flags);
+     return;
+   }
+  spin_unlock_irqrestore(&divert_lock, flags);
+  deleterule(-1); /* delete all rules and free mem */
+  deleteprocs();
+  printk(KERN_INFO "dss1_divert module successfully removed \n");
+}
+
+module_init(divert_init);
+module_exit(divert_exit);
+
diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c
new file mode 100644
index 0000000..e1f0d87
--- /dev/null
+++ b/drivers/isdn/divert/divert_procfs.c
@@ -0,0 +1,319 @@
+/* $Id: divert_procfs.c,v 1.11.6.2 2001/09/23 22:24:36 kai Exp $
+ *
+ * Filesystem handling for the diversion supplementary services.
+ *
+ * Copyright 1998       by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#else
+#include <linux/fs.h>
+#endif
+#include <linux/isdnif.h>
+#include "isdn_divert.h"
+
+
+/*********************************/
+/* Variables for interface queue */
+/*********************************/
+ulong if_used = 0;		/* number of interface users */
+static struct divert_info *divert_info_head = NULL;	/* head of queue */
+static struct divert_info *divert_info_tail = NULL;	/* pointer to last entry */
+static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */
+static wait_queue_head_t rd_queue;
+
+/*********************************/
+/* put an info buffer into queue */
+/*********************************/
+void
+put_info_buffer(char *cp)
+{
+	struct divert_info *ib;
+	unsigned long flags;
+
+	if (if_used <= 0)
+		return;
+	if (!cp)
+		return;
+	if (!*cp)
+		return;
+	if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
+		 return;	/* no memory */
+	strcpy(ib->info_start, cp);	/* set output string */
+	ib->next = NULL;
+	spin_lock_irqsave( &divert_info_lock, flags );
+	ib->usage_cnt = if_used;
+	if (!divert_info_head)
+		divert_info_head = ib;	/* new head */
+	else
+		divert_info_tail->next = ib;	/* follows existing messages */
+	divert_info_tail = ib;	/* new tail */
+
+	/* delete old entrys */
+	while (divert_info_head->next) {
+		if ((divert_info_head->usage_cnt <= 0) &&
+		    (divert_info_head->next->usage_cnt <= 0)) {
+			ib = divert_info_head;
+			divert_info_head = divert_info_head->next;
+			kfree(ib);
+		} else
+			break;
+	}			/* divert_info_head->next */
+	spin_unlock_irqrestore( &divert_info_lock, flags );
+	wake_up_interruptible(&(rd_queue));
+}				/* put_info_buffer */
+
+/**********************************/
+/* deflection device read routine */
+/**********************************/
+static ssize_t
+isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+	struct divert_info *inf;
+	int len;
+
+	if (!*((struct divert_info **) file->private_data)) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		interruptible_sleep_on(&(rd_queue));
+	}
+	if (!(inf = *((struct divert_info **) file->private_data)))
+		return (0);
+
+	inf->usage_cnt--;	/* new usage count */
+	file->private_data = &inf->next;	/* next structure */
+	if ((len = strlen(inf->info_start)) <= count) {
+		if (copy_to_user(buf, inf->info_start, len))
+			return -EFAULT;
+		*off += len;
+		return (len);
+	}
+	return (0);
+}				/* isdn_divert_read */
+
+/**********************************/
+/* deflection device write routine */
+/**********************************/
+static ssize_t
+isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+	return (-ENODEV);
+}				/* isdn_divert_write */
+
+
+/***************************************/
+/* select routines for various kernels */
+/***************************************/
+static unsigned int
+isdn_divert_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+
+	poll_wait(file, &(rd_queue), wait);
+	/* mask = POLLOUT | POLLWRNORM; */
+	if (*((struct divert_info **) file->private_data)) {
+		mask |= POLLIN | POLLRDNORM;
+	}
+	return mask;
+}				/* isdn_divert_poll */
+
+/****************/
+/* Open routine */
+/****************/
+static int
+isdn_divert_open(struct inode *ino, struct file *filep)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave( &divert_info_lock, flags );
+ 	if_used++;
+	if (divert_info_head)
+		filep->private_data = &(divert_info_tail->next);
+	else
+		filep->private_data = &divert_info_head;
+	spin_unlock_irqrestore( &divert_info_lock, flags );
+	/*  start_divert(); */
+	return nonseekable_open(ino, filep);
+}				/* isdn_divert_open */
+
+/*******************/
+/* close routine   */
+/*******************/
+static int
+isdn_divert_close(struct inode *ino, struct file *filep)
+{
+	struct divert_info *inf;
+	unsigned long flags;
+
+	spin_lock_irqsave( &divert_info_lock, flags );
+	if_used--;
+	inf = *((struct divert_info **) filep->private_data);
+	while (inf) {
+		inf->usage_cnt--;
+		inf = inf->next;
+	}
+	if (if_used <= 0)
+		while (divert_info_head) {
+			inf = divert_info_head;
+			divert_info_head = divert_info_head->next;
+			kfree(inf);
+		}
+	spin_unlock_irqrestore( &divert_info_lock, flags );
+	return (0);
+}				/* isdn_divert_close */
+
+/*********/
+/* IOCTL */
+/*********/
+static int
+isdn_divert_ioctl(struct inode *inode, struct file *file,
+		  uint cmd, ulong arg)
+{
+	divert_ioctl dioctl;
+	int i;
+	unsigned long flags;
+	divert_rule *rulep;
+	char *cp;
+
+	if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl)))
+		return -EFAULT;
+
+	switch (cmd) {
+		case IIOCGETVER:
+			dioctl.drv_version = DIVERT_IIOC_VERSION;	/* set version */
+			break;
+
+		case IIOCGETDRV:
+			if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)
+				return (-EINVAL);
+			break;
+
+		case IIOCGETNAM:
+			cp = divert_if.drv_to_name(dioctl.getid.drvid);
+			if (!cp)
+				return (-EINVAL);
+			if (!*cp)
+				return (-EINVAL);
+			strcpy(dioctl.getid.drvnam, cp);
+			break;
+
+		case IIOCGETRULE:
+			if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
+				return (-EINVAL);
+			dioctl.getsetrule.rule = *rulep;	/* copy data */
+			break;
+
+		case IIOCMODRULE:
+			if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
+				return (-EINVAL);
+            spin_lock_irqsave(&divert_lock, flags);
+			*rulep = dioctl.getsetrule.rule;	/* copy data */
+			spin_unlock_irqrestore(&divert_lock, flags);
+			return (0);	/* no copy required */
+			break;
+
+		case IIOCINSRULE:
+			return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule));
+			break;
+
+		case IIOCDELRULE:
+			return (deleterule(dioctl.getsetrule.ruleidx));
+			break;
+
+		case IIOCDODFACT:
+			return (deflect_extern_action(dioctl.fwd_ctrl.subcmd,
+						  dioctl.fwd_ctrl.callid,
+						 dioctl.fwd_ctrl.to_nr));
+
+		case IIOCDOCFACT:
+		case IIOCDOCFDIS:
+		case IIOCDOCFINT:
+			if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid))
+				return (-EINVAL);	/* invalid driver */
+			if ((i = cf_command(dioctl.cf_ctrl.drvid,
+					    (cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2,
+					    dioctl.cf_ctrl.cfproc,
+					    dioctl.cf_ctrl.msn,
+					    dioctl.cf_ctrl.service,
+					    dioctl.cf_ctrl.fwd_nr,
+					    &dioctl.cf_ctrl.procid)))
+				return (i);
+			break;
+
+		default:
+			return (-EINVAL);
+	}			/* switch cmd */
+	return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0;
+}				/* isdn_divert_ioctl */
+
+
+#ifdef CONFIG_PROC_FS
+static struct file_operations isdn_fops =
+{
+	.owner          = THIS_MODULE,
+	.llseek         = no_llseek,
+	.read           = isdn_divert_read,
+	.write          = isdn_divert_write,
+	.poll           = isdn_divert_poll,
+	.ioctl          = isdn_divert_ioctl,
+	.open           = isdn_divert_open,
+	.release        = isdn_divert_close,                                      
+};
+
+/****************************/
+/* isdn subdir in /proc/net */
+/****************************/
+static struct proc_dir_entry *isdn_proc_entry = NULL;
+static struct proc_dir_entry *isdn_divert_entry = NULL;
+#endif	/* CONFIG_PROC_FS */
+
+/***************************************************************************/
+/* divert_dev_init must be called before the proc filesystem may be used   */
+/***************************************************************************/
+int
+divert_dev_init(void)
+{
+
+	init_waitqueue_head(&rd_queue);
+
+#ifdef CONFIG_PROC_FS
+	isdn_proc_entry = create_proc_entry("isdn", S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
+	if (!isdn_proc_entry)
+		return (-1);
+	isdn_divert_entry = create_proc_entry("divert", S_IFREG | S_IRUGO, isdn_proc_entry);
+	if (!isdn_divert_entry) {
+		remove_proc_entry("isdn", proc_net);
+		return (-1);
+	}
+	isdn_divert_entry->proc_fops = &isdn_fops; 
+	isdn_divert_entry->owner = THIS_MODULE; 
+#endif	/* CONFIG_PROC_FS */
+
+	return (0);
+}				/* divert_dev_init */
+
+/***************************************************************************/
+/* divert_dev_deinit must be called before leaving isdn when included as   */
+/* a module.                                                               */
+/***************************************************************************/
+int
+divert_dev_deinit(void)
+{
+
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry("divert", isdn_proc_entry);
+	remove_proc_entry("isdn", proc_net);
+#endif	/* CONFIG_PROC_FS */
+
+	return (0);
+}				/* divert_dev_deinit */
diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c
new file mode 100644
index 0000000..1eb1122
--- /dev/null
+++ b/drivers/isdn/divert/isdn_divert.c
@@ -0,0 +1,861 @@
+/* $Id: isdn_divert.c,v 1.6.6.3 2001/09/23 22:24:36 kai Exp $
+ *
+ * DSS1 main diversion supplementary handling for i4l.
+ *
+ * Copyright 1999       by Werner Cornelius (werner@isdn4linux.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+
+#include "isdn_divert.h"
+
+/**********************************/
+/* structure keeping calling info */
+/**********************************/
+struct call_struc
+  { isdn_ctrl ics; /* delivered setup + driver parameters */
+    ulong divert_id; /* Id delivered to user */
+    unsigned char akt_state; /* actual state */
+    char deflect_dest[35]; /* deflection destination */   
+    struct timer_list timer; /* timer control structure */
+    char info[90]; /* device info output */ 
+    struct call_struc *next; /* pointer to next entry */
+    struct call_struc *prev;
+  };
+
+
+/********************************************/
+/* structure keeping deflection table entry */
+/********************************************/
+struct deflect_struc
+  { struct deflect_struc *next,*prev; 
+    divert_rule rule; /* used rule */
+  };
+
+
+/*****************************************/
+/* variables for main diversion services */
+/*****************************************/
+/* diversion/deflection processes */
+static struct call_struc *divert_head = NULL; /* head of remembered entrys */
+static ulong next_id = 1; /* next info id */   
+static struct deflect_struc *table_head = NULL;
+static struct deflect_struc *table_tail = NULL; 
+static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */ 
+
+DEFINE_SPINLOCK(divert_lock);
+
+/***************************/
+/* timer callback function */
+/***************************/
+static void deflect_timer_expire(ulong arg)
+{
+  unsigned long flags;
+  struct call_struc *cs = (struct call_struc *) arg;
+
+  spin_lock_irqsave(&divert_lock, flags);
+  del_timer(&cs->timer); /* delete active timer */
+  spin_unlock_irqrestore(&divert_lock, flags);
+
+  switch(cs->akt_state)
+   { case DEFLECT_PROCEED:
+       cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */
+       divert_if.ll_cmd(&cs->ics);                   	  
+       spin_lock_irqsave(&divert_lock, flags);
+       cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+       cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+       add_timer(&cs->timer);
+       spin_unlock_irqrestore(&divert_lock, flags);
+       break;
+
+     case DEFLECT_ALERT:
+       cs->ics.command = ISDN_CMD_REDIR; /* protocol */
+       strcpy(cs->ics.parm.setup.phone,cs->deflect_dest);
+       strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed");
+       divert_if.ll_cmd(&cs->ics);
+       spin_lock_irqsave(&divert_lock, flags);
+       cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+       cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+       add_timer(&cs->timer);
+       spin_unlock_irqrestore(&divert_lock, flags);
+       break;
+
+     case DEFLECT_AUTODEL:
+     default:
+       spin_lock_irqsave(&divert_lock, flags);
+       if (cs->prev) 
+         cs->prev->next = cs->next; /* forward link */
+        else
+         divert_head = cs->next;
+       if (cs->next)
+         cs->next->prev = cs->prev; /* back link */           
+       spin_unlock_irqrestore(&divert_lock, flags);
+       kfree(cs);
+       return;
+
+   } /* switch */
+} /* deflect_timer_func */
+
+
+/*****************************************/
+/* handle call forwarding de/activations */
+/* 0 = deact, 1 = act, 2 = interrogate   */
+/*****************************************/
+int cf_command(int drvid, int mode, 
+               u_char proc, char *msn, 
+               u_char service, char *fwd_nr, ulong *procid)
+{ unsigned long flags;
+  int retval,msnlen;
+  int fwd_len;
+  char *p,*ielenp,tmp[60];
+  struct call_struc *cs;
+
+  if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */
+  if ((proc & 0x7F) > 2) return(-EINVAL);
+  proc &= 3;
+  p = tmp;
+  *p++ = 0x30; /* enumeration */
+  ielenp = p++; /* remember total length position */
+  *p++ = 0xa; /* proc tag */
+  *p++ = 1;   /* length */
+  *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */
+  *p++ = 0xa; /* service tag */
+  *p++ = 1;   /* length */
+  *p++ = service; /* service to handle */
+
+  if (mode == 1) 
+   { if (!*fwd_nr) return(-EINVAL); /* destination missing */
+     if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */
+     fwd_len = strlen(fwd_nr);
+     *p++ = 0x30; /* number enumeration */
+     *p++ = fwd_len + 2; /* complete forward to len */ 
+     *p++ = 0x80; /* fwd to nr */
+     *p++ = fwd_len; /* length of number */
+     strcpy(p,fwd_nr); /* copy number */
+     p += fwd_len; /* pointer beyond fwd */
+   } /* activate */
+
+  msnlen = strlen(msn);
+  *p++ = 0x80; /* msn number */
+  if (msnlen > 1)
+   { *p++ = msnlen; /* length */
+     strcpy(p,msn);
+     p += msnlen;
+   }
+  else *p++ = 0;
+
+  *ielenp = p - ielenp - 1; /* set total IE length */ 
+
+  /* allocate mem for information struct */  
+  if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) 
+             return(-ENOMEM); /* no memory */
+  init_timer(&cs->timer);
+  cs->info[0] = '\0';
+  cs->timer.function = deflect_timer_expire;
+  cs->timer.data = (ulong) cs; /* pointer to own structure */
+  cs->ics.driver = drvid;
+  cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */
+  cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */
+  cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */ 
+  cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */
+  cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */
+  cs->ics.parm.dss1_io.data = tmp; /* start of buffer */
+  
+  spin_lock_irqsave(&divert_lock, flags);
+  cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */
+  spin_unlock_irqrestore(&divert_lock, flags);
+  *procid = cs->ics.parm.dss1_io.ll_id;  
+
+  sprintf(cs->info,"%d 0x%lx %s%s 0 %s %02x %d%s%s\n",
+	  (!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT,
+          cs->ics.parm.dss1_io.ll_id,
+          (mode != 2) ? "" : "0 ",
+          divert_if.drv_to_name(cs->ics.driver),
+          msn,
+          service & 0xFF,
+          proc,
+          (mode != 1) ? "" : " 0 ",
+          (mode != 1) ? "" : fwd_nr);
+ 
+  retval = divert_if.ll_cmd(&cs->ics); /* excute command */
+
+  if (!retval)
+   { cs->prev = NULL;
+     spin_lock_irqsave(&divert_lock, flags);
+     cs->next = divert_head;
+     divert_head = cs; 
+     spin_unlock_irqrestore(&divert_lock, flags);
+   }
+  else
+   kfree(cs);
+  return(retval); 
+} /* cf_command */
+
+
+/****************************************/
+/* handle a external deflection command */
+/****************************************/
+int deflect_extern_action(u_char cmd, ulong callid, char *to_nr)
+{ struct call_struc *cs;
+  isdn_ctrl ic;
+  unsigned long flags;
+  int i;
+
+  if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */
+  cs = divert_head; /* start of parameter list */
+  while (cs)
+   { if (cs->divert_id == callid) break; /* found */
+     cs = cs->next;  
+   } /* search entry */
+  if (!cs) return(-EINVAL); /* invalid callid */
+
+  ic.driver = cs->ics.driver;
+  ic.arg = cs->ics.arg;
+  i = -EINVAL;
+  if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */
+  switch (cmd & 0x7F)
+   { case 0: /* hangup */
+       del_timer(&cs->timer); 
+       ic.command = ISDN_CMD_HANGUP;
+       i = divert_if.ll_cmd(&ic);
+       spin_lock_irqsave(&divert_lock, flags);
+       cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+       cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+       add_timer(&cs->timer);
+       spin_unlock_irqrestore(&divert_lock, flags);
+     break;      
+
+     case 1: /* alert */
+       if (cs->akt_state == DEFLECT_ALERT) return(0);
+       cmd &= 0x7F; /* never wait */
+       del_timer(&cs->timer); 
+       ic.command = ISDN_CMD_ALERT;
+       if ((i = divert_if.ll_cmd(&ic)))
+	{
+          spin_lock_irqsave(&divert_lock, flags);
+          cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+          cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+          add_timer(&cs->timer);
+          spin_unlock_irqrestore(&divert_lock, flags);
+        }
+       else
+          cs->akt_state = DEFLECT_ALERT; 
+     break;      
+
+     case 2: /* redir */
+       del_timer(&cs->timer); 
+       strcpy(cs->ics.parm.setup.phone, to_nr);
+       strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual");
+       ic.command = ISDN_CMD_REDIR;
+       if ((i = divert_if.ll_cmd(&ic)))
+	{
+          spin_lock_irqsave(&divert_lock, flags);
+          cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+          cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+          add_timer(&cs->timer);
+          spin_unlock_irqrestore(&divert_lock, flags);
+        }
+       else
+          cs->akt_state = DEFLECT_ALERT; 
+     break;      
+
+   } /* switch */
+  return(i);
+} /* deflect_extern_action */
+
+/********************************/
+/* insert a new rule before idx */
+/********************************/
+int insertrule(int idx, divert_rule *newrule)
+{ struct deflect_struc *ds,*ds1=NULL;
+  unsigned long flags;
+
+  if (!(ds = (struct deflect_struc *) kmalloc(sizeof(struct deflect_struc), 
+                                              GFP_KERNEL))) 
+    return(-ENOMEM); /* no memory */
+
+  ds->rule = *newrule; /* set rule */
+
+  spin_lock_irqsave(&divert_lock, flags);
+
+  if (idx >= 0)
+   { ds1 = table_head;
+     while ((ds1) && (idx > 0))
+      { idx--;
+        ds1 = ds1->next;
+      } 
+     if (!ds1) idx = -1; 
+   }
+
+  if (idx < 0)
+   { ds->prev = table_tail; /* previous entry */
+     ds->next = NULL; /* end of chain */
+     if (ds->prev) 
+       ds->prev->next = ds; /* last forward */
+      else
+        table_head = ds; /* is first entry */
+     table_tail = ds; /* end of queue */
+   }
+  else
+    { ds->next = ds1; /* next entry */
+      ds->prev = ds1->prev; /* prev entry */
+      ds1->prev = ds; /* backward chain old element */
+      if (!ds->prev)
+        table_head = ds; /* first element */
+   }
+
+  spin_unlock_irqrestore(&divert_lock, flags);
+  return(0);
+} /* insertrule */
+
+/***********************************/
+/* delete the rule at position idx */
+/***********************************/
+int deleterule(int idx)
+{ struct deflect_struc *ds,*ds1;
+  unsigned long flags;
+  
+  if (idx < 0) 
+   { spin_lock_irqsave(&divert_lock, flags);
+     ds = table_head;
+     table_head = NULL;
+     table_tail = NULL;
+     spin_unlock_irqrestore(&divert_lock, flags);
+     while (ds)
+      { ds1 = ds; 
+        ds = ds->next;
+        kfree(ds1);
+      } 
+     return(0); 
+   }
+
+  spin_lock_irqsave(&divert_lock, flags);
+  ds = table_head;
+
+  while ((ds) && (idx > 0))
+   { idx--; 
+     ds = ds->next;  
+   }
+
+  if (!ds) 
+   {
+     spin_unlock_irqrestore(&divert_lock, flags);
+     return(-EINVAL);
+   }  
+
+  if (ds->next) 
+    ds->next->prev = ds->prev; /* backward chain */
+   else
+     table_tail = ds->prev; /* end of chain */
+
+  if (ds->prev)
+    ds->prev->next = ds->next; /* forward chain */
+   else
+     table_head = ds->next; /* start of chain */      
+  
+  spin_unlock_irqrestore(&divert_lock, flags);
+  kfree(ds);
+  return(0);
+} /* deleterule */
+
+/*******************************************/
+/* get a pointer to a specific rule number */
+/*******************************************/
+divert_rule *getruleptr(int idx)
+{ struct deflect_struc *ds = table_head;
+  
+  if (idx < 0) return(NULL);
+  while ((ds) && (idx >= 0))
+   { if (!(idx--)) 
+      { return(&ds->rule);
+        break;
+      }
+     ds = ds->next;  
+   }
+  return(NULL);
+} /* getruleptr */
+
+/*************************************************/
+/* called from common module on an incoming call */
+/*************************************************/
+int isdn_divert_icall(isdn_ctrl *ic)
+{ int retval = 0;
+  unsigned long flags;
+  struct call_struc *cs = NULL; 
+  struct deflect_struc *dv;
+  char *p,*p1;
+  u_char accept;
+
+  /* first check the internal deflection table */
+  for (dv = table_head; dv ; dv = dv->next )
+   { /* scan table */
+     if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) ||
+         ((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL)))
+       continue; /* call option check */  
+     if (!(dv->rule.drvid & (1L << ic->driver))) 
+       continue; /* driver not matching */ 
+     if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1)) 
+       continue; /* si1 not matching */
+     if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2)) 
+       continue; /* si2 not matching */
+
+     p = dv->rule.my_msn;
+     p1 = ic->parm.setup.eazmsn;
+     accept = 0;
+     while (*p)
+      { /* complete compare */
+        if (*p == '-')
+	  { accept = 1; /* call accepted */
+            break;
+          }
+        if (*p++ != *p1++) 
+          break; /* not accepted */
+        if ((!*p) && (!*p1))
+          accept = 1;
+      } /* complete compare */
+     if (!accept) continue; /* not accepted */
+ 
+     if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0]))
+      { p = dv->rule.caller;
+        p1 = ic->parm.setup.phone;
+        accept = 0;
+        while (*p)
+	 { /* complete compare */
+           if (*p == '-')
+	    { accept = 1; /* call accepted */
+              break;
+            }
+           if (*p++ != *p1++) 
+             break; /* not accepted */
+           if ((!*p) && (!*p1))
+             accept = 1;
+         } /* complete compare */
+        if (!accept) continue; /* not accepted */
+      }  
+
+     switch (dv->rule.action)
+       { case DEFLECT_IGNORE:
+           return(0);
+           break;
+
+         case DEFLECT_ALERT:
+         case DEFLECT_PROCEED:
+         case DEFLECT_REPORT:
+         case DEFLECT_REJECT:
+           if (dv->rule.action == DEFLECT_PROCEED)
+	    if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime))) 
+              return(0); /* no external deflection needed */  
+           if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) 
+             return(0); /* no memory */
+           init_timer(&cs->timer);
+           cs->info[0] = '\0';
+           cs->timer.function = deflect_timer_expire;
+           cs->timer.data = (ulong) cs; /* pointer to own structure */
+           
+           cs->ics = *ic; /* copy incoming data */
+           if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0");
+           if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0");
+	   cs->ics.parm.setup.screen = dv->rule.screen;  
+           if (dv->rule.waittime) 
+             cs->timer.expires = jiffies + (HZ * dv->rule.waittime);
+           else
+            if (dv->rule.action == DEFLECT_PROCEED)
+              cs->timer.expires = jiffies + (HZ * extern_wait_max); 
+            else  
+              cs->timer.expires = 0;
+           cs->akt_state = dv->rule.action;                
+           spin_lock_irqsave(&divert_lock, flags);
+           cs->divert_id = next_id++; /* new sequence number */
+           spin_unlock_irqrestore(&divert_lock, flags);
+           cs->prev = NULL;
+           if (cs->akt_state == DEFLECT_ALERT)
+             { strcpy(cs->deflect_dest,dv->rule.to_nr);
+               if (!cs->timer.expires)
+		 { strcpy(ic->parm.setup.eazmsn,"Testtext direct");
+                   ic->parm.setup.screen = dv->rule.screen;
+                   strcpy(ic->parm.setup.phone,dv->rule.to_nr);
+                   cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+                   cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+                   retval = 5; 
+                 }
+               else
+                 retval = 1; /* alerting */                 
+             }
+           else
+             { cs->deflect_dest[0] = '\0';
+	       retval = 4; /* only proceed */
+             }  
+           sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",
+                   cs->akt_state,
+                   cs->divert_id,
+                   divert_if.drv_to_name(cs->ics.driver),
+                   (ic->command == ISDN_STAT_ICALLW) ? "1":"0", 
+                   cs->ics.parm.setup.phone, 
+                   cs->ics.parm.setup.eazmsn,
+                   cs->ics.parm.setup.si1,
+                   cs->ics.parm.setup.si2,
+                   cs->ics.parm.setup.screen,
+                   dv->rule.waittime,
+                   cs->deflect_dest);
+           if ((dv->rule.action == DEFLECT_REPORT) ||
+               (dv->rule.action == DEFLECT_REJECT))
+	    { put_info_buffer(cs->info);
+	      kfree(cs); /* remove */
+              return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */ 
+            }              
+           break;
+  
+         default:
+           return(0); /* ignore call */
+           break;
+       } /* switch action */    
+     break; 
+   } /* scan_table */
+
+  if (cs) 
+   { cs->prev = NULL;
+     spin_lock_irqsave(&divert_lock, flags);
+     cs->next = divert_head;
+     divert_head = cs; 
+     if (cs->timer.expires) add_timer(&cs->timer);
+     spin_unlock_irqrestore(&divert_lock, flags);
+
+     put_info_buffer(cs->info); 
+     return(retval);
+   }
+  else
+     return(0);
+} /* isdn_divert_icall */
+
+
+void deleteprocs(void)
+{ struct call_struc *cs, *cs1; 
+  unsigned long flags;
+
+  spin_lock_irqsave(&divert_lock, flags);
+  cs = divert_head;
+  divert_head = NULL;
+  while (cs)
+   { del_timer(&cs->timer);
+     cs1 = cs;
+     cs = cs->next;
+     kfree(cs1);
+   } 
+  spin_unlock_irqrestore(&divert_lock, flags);
+} /* deleteprocs */
+
+/****************************************************/
+/* put a address including address type into buffer */
+/****************************************************/
+int put_address(char *st, u_char *p, int len)
+{ u_char retval = 0;
+  u_char adr_typ = 0; /* network standard */
+
+  if (len < 2) return(retval);
+  if (*p == 0xA1)
+   { retval = *(++p) + 2; /* total length */
+     if (retval > len) return(0); /* too short */
+     len = retval - 2; /* remaining length */
+     if (len < 3) return(0);
+     if ((*(++p) != 0x0A) || (*(++p) != 1)) return(0);
+     adr_typ = *(++p);
+     len -= 3;
+     p++;
+     if (len < 2) return(0);
+     if (*p++ != 0x12) return(0);
+     if (*p > len) return(0); /* check number length */
+     len = *p++;
+   }   
+  else
+   if (*p == 0x80)
+    { retval = *(++p) + 2; /* total length */
+      if (retval > len) return(0);
+      len = retval - 2;
+      p++;
+    }
+   else  
+    return(0); /* invalid address information */
+
+  sprintf(st,"%d ",adr_typ);
+  st += strlen(st);
+  if (!len) 
+    *st++ = '-';
+  else
+   while (len--)
+     *st++ = *p++;
+  *st = '\0';
+  return(retval);
+} /* put_address */
+
+/*************************************/
+/* report a succesfull interrogation */
+/*************************************/
+int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)
+{ char *src = ic->parm.dss1_io.data;
+  int restlen = ic->parm.dss1_io.datalen;
+  int cnt = 1;
+  u_char n,n1;
+  char st[90], *p, *stp;
+
+  if (restlen < 2) return(-100); /* frame too short */
+  if (*src++ != 0x30) return(-101);
+  if ((n = *src++) > 0x81) return(-102); /* invalid length field */
+  restlen -= 2; /* remaining bytes */
+  if (n == 0x80)
+   { if (restlen < 2) return(-103);
+     if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104);
+     restlen -= 2;
+   }
+  else
+   if ( n == 0x81)
+    { n = *src++;
+      restlen--;
+      if (n > restlen) return(-105);
+      restlen = n;
+    }
+   else
+    if (n > restlen) return(-106);
+     else 
+      restlen = n; /* standard format */   
+  if (restlen < 3) return(-107); /* no procedure */
+  if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108);
+  restlen -= 3; 
+  if (restlen < 2) return(-109); /* list missing */
+  if (*src == 0x31)
+   { src++; 
+     if ((n = *src++) > 0x81) return(-110); /* invalid length field */
+     restlen -= 2; /* remaining bytes */
+     if (n == 0x80)
+      { if (restlen < 2) return(-111);
+        if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112);
+        restlen -= 2;
+      }
+     else
+      if ( n == 0x81)
+       { n = *src++;
+         restlen--;
+         if (n > restlen) return(-113);
+         restlen = n;
+       }
+      else
+       if (n > restlen) return(-114);
+        else 
+         restlen = n; /* standard format */   
+   } /* result list header */ 
+
+  while (restlen >= 2)
+   { stp = st;
+     sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id,
+                 cnt++,divert_if.drv_to_name(ic->driver));
+     stp += strlen(stp);
+     if (*src++ != 0x30) return(-115); /* invalid enum */
+     n = *src++;
+     restlen -= 2;
+     if (n > restlen) return(-116); /* enum length wrong */
+     restlen -= n;
+     p = src; /* one entry */
+     src += n;
+     if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
+     stp += strlen(stp);
+     p += n1;
+     n -= n1;
+     if (n < 6) continue; /* no service and proc */
+     if ((*p++ != 0x0A) || (*p++ != 1)) continue;
+     sprintf(stp," 0x%02x ",(*p++) & 0xFF);
+     stp += strlen(stp);
+     if ((*p++ != 0x0A) || (*p++ != 1)) continue;
+     sprintf(stp,"%d ",(*p++) & 0xFF);
+     stp += strlen(stp);
+     n -= 6;
+     if (n > 2)
+      { if (*p++ != 0x30) continue;
+        if (*p > (n-2)) continue;
+        n = *p++;
+        if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
+        stp += strlen(stp);
+      }
+     sprintf(stp,"\n");
+     put_info_buffer(st);
+   } /* while restlen */
+  if (restlen) return(-117);
+  return(0);   
+} /* interrogate_success */
+
+/*********************************************/
+/* callback for protocol specific extensions */
+/*********************************************/
+int prot_stat_callback(isdn_ctrl *ic)
+{ struct call_struc *cs, *cs1;
+  int i;
+  unsigned long flags;
+
+  cs = divert_head; /* start of list */
+  cs1 = NULL;
+  while (cs)
+   { if (ic->driver == cs->ics.driver) 
+      { switch (cs->ics.arg)
+	 { case DSS1_CMD_INVOKE:
+             if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) &&
+                 (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id))
+	      { switch (ic->arg)
+		{  case DSS1_STAT_INVOKE_ERR:
+                     sprintf(cs->info,"128 0x%lx 0x%x\n", 
+                             ic->parm.dss1_io.ll_id,
+                             ic->parm.dss1_io.timeout);
+                     put_info_buffer(cs->info);
+                   break;
+                   
+                   case DSS1_STAT_INVOKE_RES:
+                     switch (cs->ics.parm.dss1_io.proc)
+		      {  case  7:
+                         case  8:
+                            put_info_buffer(cs->info); 
+                           break;
+                       
+                         case  11:
+                           i = interrogate_success(ic,cs);
+                           if (i)
+                             sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT, 
+                                     ic->parm.dss1_io.ll_id,i);
+                           put_info_buffer(cs->info); 
+                           break;
+                       
+		         default: 
+                           printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc);
+                           break;
+                      } 
+
+
+                   break;
+ 
+		   default:
+                     printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg);
+                   break;  
+                 } 
+                cs1 = cs; /* remember structure */
+                cs = NULL; 
+                continue; /* abort search */
+              } /* id found */ 
+           break;
+   
+	   case DSS1_CMD_INVOKE_ABORT:
+             printk(KERN_WARNING "dss1_divert unhandled invoke abort\n"); 
+           break;   
+         
+	   default:
+             printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg); 
+           break; 
+         } /* switch ics.arg */ 
+        cs = cs->next; 
+      } /* driver ok */
+   }  
+   
+  if (!cs1) 
+   { printk(KERN_WARNING "dss1_divert unhandled process\n");
+     return(0);
+   }  
+
+  if (cs1->ics.driver == -1)
+   {
+     spin_lock_irqsave(&divert_lock, flags);
+     del_timer(&cs1->timer);
+     if (cs1->prev) 
+       cs1->prev->next = cs1->next; /* forward link */
+     else
+       divert_head = cs1->next;
+     if (cs1->next)
+       cs1->next->prev = cs1->prev; /* back link */           
+     spin_unlock_irqrestore(&divert_lock, flags);
+     kfree(cs1);
+   } 
+
+  return(0);
+} /* prot_stat_callback */
+
+
+/***************************/
+/* status callback from HL */
+/***************************/
+int isdn_divert_stat_callback(isdn_ctrl *ic)
+{ struct call_struc *cs, *cs1;
+  unsigned long flags;
+  int retval;
+
+  retval = -1;
+  cs = divert_head; /* start of list */
+     while (cs)
+      { if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg))
+         { switch (ic->command)
+	    { case ISDN_STAT_DHUP:
+                sprintf(cs->info,"129 0x%lx\n",cs->divert_id);
+                del_timer(&cs->timer);
+                cs->ics.driver = -1;
+                break;
+
+	      case ISDN_STAT_CAUSE:
+                sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num);
+                break;
+
+	      case ISDN_STAT_REDIR:
+                sprintf(cs->info,"131 0x%lx\n",cs->divert_id);
+                del_timer(&cs->timer);
+                cs->ics.driver = -1;
+                break; 
+
+	      default:
+                sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command));
+                break; 
+            }
+          put_info_buffer(cs->info);
+          retval = 0; 
+         }
+        cs1 = cs; 
+        cs = cs->next;
+        if (cs1->ics.driver == -1)
+          { 
+            spin_lock_irqsave(&divert_lock, flags);
+            if (cs1->prev) 
+              cs1->prev->next = cs1->next; /* forward link */
+            else
+              divert_head = cs1->next;
+            if (cs1->next)
+              cs1->next->prev = cs1->prev; /* back link */           
+            spin_unlock_irqrestore(&divert_lock, flags);
+            kfree(cs1);
+          } 
+      }  
+  return(retval); /* not found */
+} /* isdn_divert_stat_callback */ 
+
+
+/********************/
+/* callback from ll */
+/********************/ 
+int ll_callback(isdn_ctrl *ic)
+{
+  switch (ic->command)
+   { case ISDN_STAT_ICALL:
+     case ISDN_STAT_ICALLW:
+       return(isdn_divert_icall(ic));
+     break;
+
+     case ISDN_STAT_PROT:
+       if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO)
+	{ if (ic->arg != DSS1_STAT_INVOKE_BRD)
+            return(prot_stat_callback(ic));
+          else
+            return(0); /* DSS1 invoke broadcast */
+        }
+       else
+         return(-1); /* protocol not euro */    
+
+     default:
+       return(isdn_divert_stat_callback(ic));
+   }
+} /* ll_callback */
+
diff --git a/drivers/isdn/divert/isdn_divert.h b/drivers/isdn/divert/isdn_divert.h
new file mode 100644
index 0000000..19439a6
--- /dev/null
+++ b/drivers/isdn/divert/isdn_divert.h
@@ -0,0 +1,132 @@
+/* $Id: isdn_divert.h,v 1.5.6.1 2001/09/23 22:24:36 kai Exp $
+ *
+ * Header for the diversion supplementary ioctl interface.
+ *
+ * Copyright 1998       by Werner Cornelius (werner@ikt.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/******************************************/
+/* IOCTL codes for interface to user prog */
+/******************************************/
+#define DIVERT_IIOC_VERSION 0x01 /* actual version */
+#define IIOCGETVER   _IO('I', 1)  /* get version of interface */
+#define IIOCGETDRV   _IO('I', 2)  /* get driver number */
+#define IIOCGETNAM   _IO('I', 3)  /* get driver name */
+#define IIOCGETRULE  _IO('I', 4)  /* read one rule */
+#define IIOCMODRULE  _IO('I', 5)  /* modify/replace a rule */  
+#define IIOCINSRULE  _IO('I', 6)  /* insert/append one rule */
+#define IIOCDELRULE  _IO('I', 7)  /* delete a rule */
+#define IIOCDODFACT  _IO('I', 8)  /* hangup/reject/alert/immediately deflect a call */
+#define IIOCDOCFACT  _IO('I', 9)  /* activate control forwarding in PBX */
+#define IIOCDOCFDIS  _IO('I',10)  /* deactivate control forwarding in PBX */
+#define IIOCDOCFINT  _IO('I',11)  /* interrogate control forwarding in PBX */
+
+/*************************************/
+/* states reported through interface */
+/*************************************/
+#define DEFLECT_IGNORE    0  /* ignore incoming call */
+#define DEFLECT_REPORT    1  /* only report */
+#define DEFLECT_PROCEED   2  /* deflect when externally triggered */
+#define DEFLECT_ALERT     3  /* alert and deflect after delay */ 
+#define DEFLECT_REJECT    4  /* reject immediately */
+#define DIVERT_ACTIVATE   5  /* diversion activate */
+#define DIVERT_DEACTIVATE 6  /* diversion deactivate */
+#define DIVERT_REPORT     7  /* interrogation result */ 
+#define DEFLECT_AUTODEL 255  /* only for internal use */ 
+
+#define DEFLECT_ALL_IDS   0xFFFFFFFF /* all drivers selected */
+
+typedef struct
+ { ulong drvid;     /* driver ids, bit mapped */
+   char my_msn[35]; /* desired msn, subaddr allowed */
+   char caller[35]; /* caller id, partial string with * + subaddr allowed */
+   char to_nr[35];  /* deflected to number incl. subaddress */
+   u_char si1,si2;  /* service indicators, si1=bitmask, si1+2 0 = all */
+   u_char screen;   /* screening: 0 = no info, 1 = info, 2 = nfo with nr */
+   u_char callopt;  /* option for call handling: 
+                       0 = all calls
+                       1 = only non waiting calls
+                       2 = only waiting calls */
+   u_char action;   /* desired action: 
+                       0 = don't report call -> ignore
+                       1 = report call, do not allow/proceed for deflection
+                       2 = report call, send proceed, wait max waittime secs
+                       3 = report call, alert and deflect after waittime 
+                       4 = report call, reject immediately  
+                       actions 1-2 only take place if interface is opened 
+		    */
+   u_char waittime; /* maximum wait time for proceeding */ 
+ } divert_rule;
+
+typedef union
+ { int drv_version; /* return of driver version */
+   struct 
+   { int drvid;		/* id of driver */
+     char drvnam[30];	/* name of driver */
+   } getid;
+   struct
+   { int ruleidx;	/* index of rule */
+     divert_rule rule;	/* rule parms */ 
+   } getsetrule;
+   struct
+   { u_char subcmd;  /* 0 = hangup/reject,
+                        1 = alert,
+                        2 = deflect */
+     ulong callid;   /* id of call delivered by ascii output */
+     char to_nr[35]; /* destination when deflect,
+                        else uus1 string (maxlen 31),
+                        data from rule used if empty */ 
+   } fwd_ctrl; 
+   struct
+   { int drvid;      /* id of driver */
+     u_char cfproc;  /* cfu = 0, cfb = 1, cfnr = 2 */
+     ulong procid;   /* process id returned when no error */ 
+     u_char service; /* basically coded service, 0 = all */
+     char msn[25];   /* desired msn, empty = all */
+     char fwd_nr[35];/* forwarded to number + subaddress */
+   } cf_ctrl;  
+ } divert_ioctl;
+
+#ifdef __KERNEL__
+
+#include <linux/isdnif.h>
+#include <linux/isdn_divertif.h>
+
+#define AUTODEL_TIME 30 /* timeout in s to delete internal entries */
+
+/**************************************************/
+/* structure keeping ascii info for device output */
+/**************************************************/
+struct divert_info
+  { struct divert_info *next;
+    ulong usage_cnt; /* number of files still to work */   
+    char info_start[2]; /* info string start */ 
+  }; 
+
+
+/**************/
+/* Prototypes */
+/**************/
+extern spinlock_t divert_lock;
+
+extern ulong if_used; /* number of interface users */
+extern int divert_dev_deinit(void);
+extern int divert_dev_init(void);
+extern void put_info_buffer(char *);
+extern int ll_callback(isdn_ctrl *);
+extern isdn_divert_if divert_if;
+extern divert_rule *getruleptr(int);
+extern int insertrule(int, divert_rule *);
+extern int deleterule(int);
+extern void deleteprocs(void);
+extern int deflect_extern_action(u_char, ulong, char *);
+extern int cf_command(int, int, u_char, char *, u_char, char *, ulong *);
+
+#endif /* __KERNEL__ */
diff --git a/drivers/isdn/hardware/Kconfig b/drivers/isdn/hardware/Kconfig
new file mode 100644
index 0000000..139f197
--- /dev/null
+++ b/drivers/isdn/hardware/Kconfig
@@ -0,0 +1,10 @@
+#
+# ISDN hardware drivers
+#
+comment "CAPI hardware drivers"
+	depends on NET && ISDN && ISDN_CAPI
+
+source "drivers/isdn/hardware/avm/Kconfig"
+
+source "drivers/isdn/hardware/eicon/Kconfig"
+
diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile
new file mode 100644
index 0000000..11c8a18
--- /dev/null
+++ b/drivers/isdn/hardware/Makefile
@@ -0,0 +1,6 @@
+# Makefile for the CAPI hardware drivers
+
+# Object files in subdirectories
+
+obj-$(CONFIG_CAPI_AVM)		+= avm/
+obj-$(CONFIG_CAPI_EICON)	+= eicon/
diff --git a/drivers/isdn/hardware/avm/Kconfig b/drivers/isdn/hardware/avm/Kconfig
new file mode 100644
index 0000000..29a32a8
--- /dev/null
+++ b/drivers/isdn/hardware/avm/Kconfig
@@ -0,0 +1,66 @@
+#
+# ISDN AVM drivers
+#
+
+menu "Active AVM cards"
+	depends on NET && ISDN && ISDN_CAPI!=n
+
+config CAPI_AVM
+	bool "Support AVM cards"
+	help
+	  Enable support for AVM active ISDN cards.
+
+config ISDN_DRV_AVMB1_B1ISA
+	tristate "AVM B1 ISA support"
+	depends on CAPI_AVM && ISDN_CAPI && ISA
+	help
+	  Enable support for the ISA version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_B1PCI
+	tristate "AVM B1 PCI support"
+	depends on CAPI_AVM && ISDN_CAPI && PCI
+	help
+	  Enable support for the PCI version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_B1PCIV4
+	bool "AVM B1 PCI V4 support"
+	depends on ISDN_DRV_AVMB1_B1PCI
+	help
+	  Enable support for the V4 version of AVM B1 PCI card.
+
+config ISDN_DRV_AVMB1_T1ISA
+	tristate "AVM T1/T1-B ISA support"
+	depends on CAPI_AVM && ISDN_CAPI && ISA
+	help
+	  Enable support for the AVM T1 T1B card.
+	  Note: This is a PRI card and handle 30 B-channels.
+
+config ISDN_DRV_AVMB1_B1PCMCIA
+	tristate "AVM B1/M1/M2 PCMCIA support"
+	depends on CAPI_AVM && ISDN_CAPI
+	help
+	  Enable support for the PCMCIA version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_AVM_CS
+	tristate "AVM B1/M1/M2 PCMCIA cs module"
+	depends on ISDN_DRV_AVMB1_B1PCMCIA && PCMCIA
+	help
+	  Enable the PCMCIA client driver for the AVM B1/M1/M2
+	  PCMCIA cards.
+
+config ISDN_DRV_AVMB1_T1PCI
+	tristate "AVM T1/T1-B PCI support"
+	depends on CAPI_AVM && ISDN_CAPI && PCI
+	help
+	  Enable support for the AVM T1 T1B card.
+	  Note: This is a PRI card and handle 30 B-channels.
+
+config ISDN_DRV_AVMB1_C4
+	tristate "AVM C4/C2 support"
+	depends on CAPI_AVM && ISDN_CAPI && PCI
+	help
+	  Enable support for the AVM C4/C2 PCI cards.
+	  These cards handle 4/2 BRI ISDN lines (8/4 channels).
+
+endmenu
+
diff --git a/drivers/isdn/hardware/avm/Makefile b/drivers/isdn/hardware/avm/Makefile
new file mode 100644
index 0000000..b540e8f
--- /dev/null
+++ b/drivers/isdn/hardware/avm/Makefile
@@ -0,0 +1,11 @@
+# Makefile for the AVM ISDN device drivers
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1ISA)	+= b1isa.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCI)	+= b1pci.o b1.o b1dma.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCMCIA)	+= b1pcmcia.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_AVM_CS)	+= avm_cs.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_T1ISA)	+= t1isa.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_T1PCI)	+= t1pci.o b1.o b1dma.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_C4)		+= c4.o b1.o
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
new file mode 100644
index 0000000..dc00c85
--- /dev/null
+++ b/drivers/isdn/hardware/avm/avm_cs.c
@@ -0,0 +1,510 @@
+/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
+ *
+ * A PCMCIA client driver for AVM B1/M1/M2
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <linux/skbuff.h>
+#include <linux/capi.h>
+#include <linux/b1lli.h>
+#include <linux/b1pcmcia.h>
+
+/*====================================================================*/
+
+MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the skeleton event
+   handler.
+*/
+
+static void avmcs_config(dev_link_t *link);
+static void avmcs_release(dev_link_t *link);
+static int avmcs_event(event_t event, int priority,
+			  event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *avmcs_attach(void);
+static void avmcs_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "avm_cs";
+
+/*
+   A linked list of "instances" of the skeleton device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally can't be allocated dynamically.
+*/
+   
+typedef struct local_info_t {
+    dev_node_t	node;
+} local_info_t;
+
+/*======================================================================
+
+    avmcs_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+    
+======================================================================*/
+
+static dev_link_t *avmcs_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret;
+    
+    /* Initialize the dev_link_t structure */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    if (!link)
+        goto err;
+    memset(link, 0, sizeof(struct dev_link_t));
+
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 0;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local)
+        goto err_kfree;
+    memset(local, 0, sizeof(local_info_t));
+    link->priv = local;
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &avmcs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	avmcs_detach(link);
+	goto err;
+    }
+    return link;
+
+ err_kfree:
+    kfree(link);
+ err:
+    return NULL;
+} /* avmcs_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void avmcs_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    /*
+       If the device is currently configured and active, we won't
+       actually delete it yet.  Instead, it is marked so that when
+       the release() function is called, that will trigger a proper
+       detach().
+    */
+    if (link->state & DEV_CONFIG) {
+	link->state |= DEV_STALE_LINK;
+	return;
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if (link->priv) {
+	kfree(link->priv);
+    }
+    kfree(link);
+    
+} /* avmcs_detach */
+
+/*======================================================================
+
+    avmcs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+    
+======================================================================*/
+
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_tuple_data(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_first_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_next_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static void avmcs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    local_info_t *dev;
+    int i;
+    u_char buf[64];
+    char devname[128];
+    int cardtype;
+    int (*addcard)(unsigned int port, unsigned irq);
+    
+    handle = link->handle;
+    dev = link->priv;
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    do {
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	i = pcmcia_get_first_tuple(handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 64;
+	tuple.TupleOffset = 0;
+	i = pcmcia_get_tuple_data(handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	i = pcmcia_parse_tuple(handle, &tuple, &parse);
+	if (i != CS_SUCCESS) break;
+	link->conf.ConfigBase = parse.config.base;
+    } while (0);
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, ParseTuple, i);
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+    }
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    do {
+
+	tuple.Attributes = 0;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 254;
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_VERS_1;
+
+	devname[0] = 0;
+	if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
+	    strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], 
+			sizeof(devname));
+	}
+	/*
+         * find IO port
+         */
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	i = first_tuple(handle, &tuple, &parse);
+	while (i == CS_SUCCESS) {
+	    if (cf->io.nwin > 0) {
+		link->conf.ConfigIndex = cf->index;
+		link->io.BasePort1 = cf->io.win[0].base;
+		link->io.NumPorts1 = cf->io.win[0].len;
+		link->io.NumPorts2 = 0;
+                printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
+			link->io.BasePort1,
+		        link->io.BasePort1+link->io.NumPorts1-1);
+		i = pcmcia_request_io(link->handle, &link->io);
+		if (i == CS_SUCCESS) goto found_port;
+	    }
+	    i = next_tuple(handle, &tuple, &parse);
+	}
+
+found_port:
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIO, i);
+	    break;
+	}
+	
+	/*
+	 * allocate an interrupt line
+	 */
+	i = pcmcia_request_irq(link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIRQ, i);
+	    pcmcia_release_io(link->handle, &link->io);
+	    break;
+	}
+	
+	/*
+         * configure the PCMCIA socket
+	  */
+	i = pcmcia_request_configuration(link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestConfiguration, i);
+	    pcmcia_release_io(link->handle, &link->io);
+	    pcmcia_release_irq(link->handle, &link->irq);
+	    break;
+	}
+
+    } while (0);
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. */
+
+    if (devname[0]) {
+	char *s = strrchr(devname, ' ');
+	if (!s)
+	   s = devname;
+	else s++;
+	strcpy(dev->node.dev_name, s);
+        if (strcmp("M1", s) == 0) {
+           cardtype = AVM_CARDTYPE_M1;
+        } else if (strcmp("M2", s) == 0) {
+           cardtype = AVM_CARDTYPE_M2;
+	} else {
+           cardtype = AVM_CARDTYPE_B1;
+	}
+    } else {
+        strcpy(dev->node.dev_name, "b1");
+        cardtype = AVM_CARDTYPE_B1;
+    }
+
+    dev->node.major = 64;
+    dev->node.minor = 0;
+    link->dev = &dev->node;
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    /* If any step failed, release any partially configured state */
+    if (i != 0) {
+	avmcs_release(link);
+	return;
+    }
+
+
+    switch (cardtype) {
+        case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
+        case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
+	default:
+        case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
+    }
+    if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) {
+        printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
+		dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
+	avmcs_release(link);
+	return;
+    }
+    dev->node.minor = i;
+
+} /* avmcs_config */
+
+/*======================================================================
+
+    After a card is removed, avmcs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+static void avmcs_release(dev_link_t *link)
+{
+    b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
+
+    /* Unlink the device chain */
+    link->dev = NULL;
+    
+    /* Don't bother checking to see if these succeed or not */
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+    
+    if (link->state & DEV_STALE_LINK)
+	avmcs_detach(link);
+    
+} /* avmcs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+    
+======================================================================*/
+
+static int avmcs_event(event_t event, int priority,
+			  event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+		avmcs_release(link);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	avmcs_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG)
+	    pcmcia_release_configuration(link->handle);
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG)
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	break;
+    }
+    return 0;
+} /* avmcs_event */
+
+static struct pcmcia_driver avmcs_driver = {
+	.owner	= THIS_MODULE,
+	.drv	= {
+		.name	= "avm_cs",
+	},
+	.attach	= avmcs_attach,
+	.detach	= avmcs_detach,
+};
+
+static int __init avmcs_init(void)
+{
+	return pcmcia_register_driver(&avmcs_driver);
+}
+
+static void __exit avmcs_exit(void)
+{
+	pcmcia_unregister_driver(&avmcs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(avmcs_init);
+module_exit(avmcs_exit);
diff --git a/drivers/isdn/hardware/avm/avmcard.h b/drivers/isdn/hardware/avm/avmcard.h
new file mode 100644
index 0000000..296d6a6
--- /dev/null
+++ b/drivers/isdn/hardware/avm/avmcard.h
@@ -0,0 +1,585 @@
+/* $Id: avmcard.h,v 1.1.4.1.2.1 2001/12/21 15:00:17 kai Exp $
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _AVMCARD_H_
+#define _AVMCARD_H_
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#define	AVMB1_PORTLEN		0x1f
+#define AVM_MAXVERSION		8
+#define AVM_NCCI_PER_CHANNEL	4
+
+/*
+ * Versions
+ */
+
+#define	VER_DRIVER	0
+#define	VER_CARDTYPE	1
+#define	VER_HWID	2
+#define	VER_SERIAL	3
+#define	VER_OPTION	4
+#define	VER_PROTO	5
+#define	VER_PROFILE	6
+#define	VER_CAPI	7
+
+enum avmcardtype {
+	avm_b1isa,
+	avm_b1pci,
+	avm_b1pcmcia,
+	avm_m1,
+	avm_m2,
+	avm_t1isa,
+	avm_t1pci,
+	avm_c4,
+	avm_c2
+};
+
+typedef struct avmcard_dmabuf {
+    long        size;
+    u8       *dmabuf;
+    dma_addr_t  dmaaddr;
+} avmcard_dmabuf;
+
+typedef struct avmcard_dmainfo {
+	u32                recvlen;
+        avmcard_dmabuf       recvbuf;
+
+        avmcard_dmabuf       sendbuf;
+	struct sk_buff_head  send_queue;
+
+	struct pci_dev      *pcidev;
+} avmcard_dmainfo;
+
+typedef	struct avmctrl_info {
+	char cardname[32];
+	
+	int versionlen;
+	char versionbuf[1024];
+	char *version[AVM_MAXVERSION];
+	
+	char infobuf[128];	/* for function procinfo */
+	
+	struct avmcard  *card;
+	struct capi_ctr  capi_ctrl;
+	
+	struct list_head ncci_head;
+} avmctrl_info;
+
+typedef struct avmcard {
+	char name[32];
+  
+	spinlock_t lock;
+	unsigned int port;
+	unsigned irq;
+	unsigned long membase;
+	enum avmcardtype cardtype;
+	unsigned char revision;
+	unsigned char class;
+	int cardnr; /* for t1isa */
+
+	char msgbuf[128];	/* capimsg msg part */
+	char databuf[2048];	/* capimsg data part */
+
+	void __iomem *mbase;
+	volatile u32 csr;
+	avmcard_dmainfo *dma;
+
+	struct avmctrl_info *ctrlinfo;
+
+	u_int nr_controllers;
+	u_int nlogcontr;
+	struct list_head list;
+} avmcard;
+
+extern int b1_irq_table[16];
+
+/*
+ * LLI Messages to the ISDN-ControllerISDN Controller 
+ */
+
+#define	SEND_POLL		0x72	/*
+					   * after load <- RECEIVE_POLL 
+					 */
+#define SEND_INIT		0x11	/*
+					   * first message <- RECEIVE_INIT
+					   * int32 NumApplications  int32
+					   * NumNCCIs int32 BoardNumber 
+					 */
+#define SEND_REGISTER		0x12	/*
+					   * register an application int32
+					   * ApplIDId int32 NumMessages
+					   * int32 NumB3Connections int32
+					   * NumB3Blocks int32 B3Size
+					   * 
+					   * AnzB3Connection != 0 &&
+					   * AnzB3Blocks >= 1 && B3Size >= 1 
+					 */
+#define SEND_RELEASE		0x14	/*
+					   * deregister an application int32 
+					   * ApplID 
+					 */
+#define SEND_MESSAGE		0x15	/*
+					   * send capi-message int32 length
+					   * capi-data ... 
+					 */
+#define SEND_DATA_B3_REQ	0x13	/*
+					   * send capi-data-message int32
+					   * MsgLength capi-data ... int32
+					   * B3Length data .... 
+					 */
+
+#define SEND_CONFIG		0x21    /*
+                                         */
+
+#define SEND_POLLACK		0x73    /* T1 Watchdog */
+
+/*
+ * LLI Messages from the ISDN-ControllerISDN Controller 
+ */
+
+#define RECEIVE_POLL		0x32	/*
+					   * <- after SEND_POLL 
+					 */
+#define RECEIVE_INIT		0x27	/*
+					   * <- after SEND_INIT int32 length
+					   * byte total length b1struct board 
+					   * driver revision b1struct card
+					   * type b1struct reserved b1struct
+					   * serial number b1struct driver
+					   * capability b1struct d-channel
+					   * protocol b1struct CAPI-2.0
+					   * profile b1struct capi version 
+					 */
+#define RECEIVE_MESSAGE		0x21	/*
+					   * <- after SEND_MESSAGE int32
+					   * AppllID int32 Length capi-data
+					   * .... 
+					 */
+#define RECEIVE_DATA_B3_IND	0x22	/*
+					   * received data int32 AppllID
+					   * int32 Length capi-data ...
+					   * int32 B3Length data ... 
+					 */
+#define RECEIVE_START		0x23	/*
+					   * Handshake 
+					 */
+#define RECEIVE_STOP		0x24	/*
+					   * Handshake 
+					 */
+#define RECEIVE_NEW_NCCI	0x25	/*
+					   * int32 AppllID int32 NCCI int32
+					   * WindowSize 
+					 */
+#define RECEIVE_FREE_NCCI	0x26	/*
+					   * int32 AppllID int32 NCCI 
+					 */
+#define RECEIVE_RELEASE		0x26	/*
+					   * int32 AppllID int32 0xffffffff 
+					 */
+#define RECEIVE_TASK_READY	0x31	/*
+					   * int32 tasknr
+					   * int32 Length Taskname ...
+					 */
+#define RECEIVE_DEBUGMSG	0x71	/*
+					   * int32 Length message
+					   * 
+					 */
+#define RECEIVE_POLLDWORD	0x75	/* t1pci in dword mode */
+
+#define WRITE_REGISTER		0x00
+#define READ_REGISTER		0x01
+
+/*
+ * port offsets
+ */
+
+#define B1_READ			0x00
+#define B1_WRITE		0x01
+#define B1_INSTAT		0x02
+#define B1_OUTSTAT		0x03
+#define B1_ANALYSE		0x04
+#define B1_REVISION		0x05
+#define B1_RESET		0x10
+
+
+#define B1_STAT0(cardtype)  ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l)
+#define B1_STAT1(cardtype)  (0x80E00000l)
+
+/* ---------------------------------------------------------------- */
+
+static inline unsigned char b1outp(unsigned int base,
+				   unsigned short offset,
+				   unsigned char value)
+{
+	outb(value, base + offset);
+	return inb(base + B1_ANALYSE);
+}
+
+
+static inline int b1_rx_full(unsigned int base)
+{
+	return inb(base + B1_INSTAT) & 0x1;
+}
+
+static inline unsigned char b1_get_byte(unsigned int base)
+{
+	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	while (!b1_rx_full(base) && time_before(jiffies, stop));
+	if (b1_rx_full(base))
+		return inb(base + B1_READ);
+	printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
+	return 0;
+}
+
+static inline unsigned int b1_get_word(unsigned int base)
+{
+	unsigned int val = 0;
+	val |= b1_get_byte(base);
+	val |= (b1_get_byte(base) << 8);
+	val |= (b1_get_byte(base) << 16);
+	val |= (b1_get_byte(base) << 24);
+	return val;
+}
+
+static inline int b1_tx_empty(unsigned int base)
+{
+	return inb(base + B1_OUTSTAT) & 0x1;
+}
+
+static inline void b1_put_byte(unsigned int base, unsigned char val)
+{
+	while (!b1_tx_empty(base));
+	b1outp(base, B1_WRITE, val);
+}
+
+static inline int b1_save_put_byte(unsigned int base, unsigned char val)
+{
+	unsigned long stop = jiffies + 2 * HZ;
+	while (!b1_tx_empty(base) && time_before(jiffies,stop));
+	if (!b1_tx_empty(base)) return -1;
+	b1outp(base, B1_WRITE, val);
+	return 0;
+}
+
+static inline void b1_put_word(unsigned int base, unsigned int val)
+{
+	b1_put_byte(base, val & 0xff);
+	b1_put_byte(base, (val >> 8) & 0xff);
+	b1_put_byte(base, (val >> 16) & 0xff);
+	b1_put_byte(base, (val >> 24) & 0xff);
+}
+
+static inline unsigned int b1_get_slice(unsigned int base,
+					unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = b1_get_word(base);
+	while (i-- > 0) *dp++ = b1_get_byte(base);
+	return len;
+}
+
+static inline void b1_put_slice(unsigned int base,
+				unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	b1_put_word(base, i);
+	while (i-- > 0)
+		b1_put_byte(base, *dp++);
+}
+
+static void b1_wr_reg(unsigned int base,
+                      unsigned int reg,
+		      unsigned int value)
+{
+	b1_put_byte(base, WRITE_REGISTER);
+        b1_put_word(base, reg);
+        b1_put_word(base, value);
+}
+
+static inline unsigned int b1_rd_reg(unsigned int base,
+                                     unsigned int reg)
+{
+	b1_put_byte(base, READ_REGISTER);
+        b1_put_word(base, reg);
+        return b1_get_word(base);
+	
+}
+
+static inline void b1_reset(unsigned int base)
+{
+	b1outp(base, B1_RESET, 0);
+	mdelay(55 * 2);	/* 2 TIC's */
+
+	b1outp(base, B1_RESET, 1);
+	mdelay(55 * 2);	/* 2 TIC's */
+
+	b1outp(base, B1_RESET, 0);
+	mdelay(55 * 2);	/* 2 TIC's */
+}
+
+static inline unsigned char b1_disable_irq(unsigned int base)
+{
+	return b1outp(base, B1_INSTAT, 0x00);
+}
+
+/* ---------------------------------------------------------------- */
+
+static inline void b1_set_test_bit(unsigned int base,
+				   enum avmcardtype cardtype,
+				   int onoff)
+{
+    b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
+}
+
+static inline int b1_get_test_bit(unsigned int base,
+                                  enum avmcardtype cardtype)
+{
+    return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+#define T1_FASTLINK		0x00
+#define T1_SLOWLINK		0x08
+
+#define T1_READ			B1_READ
+#define T1_WRITE		B1_WRITE
+#define T1_INSTAT		B1_INSTAT
+#define T1_OUTSTAT		B1_OUTSTAT
+#define T1_IRQENABLE		0x05
+#define T1_FIFOSTAT		0x06
+#define T1_RESETLINK		0x10
+#define T1_ANALYSE		0x11
+#define T1_IRQMASTER		0x12
+#define T1_IDENT		0x17
+#define T1_RESETBOARD		0x1f
+
+#define	T1F_IREADY		0x01
+#define	T1F_IHALF		0x02
+#define	T1F_IFULL		0x04
+#define	T1F_IEMPTY		0x08
+#define	T1F_IFLAGS		0xF0
+
+#define	T1F_OREADY		0x10
+#define	T1F_OHALF		0x20
+#define	T1F_OEMPTY		0x40
+#define	T1F_OFULL		0x80
+#define	T1F_OFLAGS		0xF0
+
+/* there are HEMA cards with 1k and 4k FIFO out */
+#define FIFO_OUTBSIZE		256
+#define FIFO_INPBSIZE		512
+
+#define HEMA_VERSION_ID		0
+#define HEMA_PAL_ID		0
+
+static inline void t1outp(unsigned int base,
+			  unsigned short offset,
+			  unsigned char value)
+{
+	outb(value, base + offset);
+}
+
+static inline unsigned char t1inp(unsigned int base,
+			          unsigned short offset)
+{
+	return inb(base + offset);
+}
+
+static inline int t1_isfastlink(unsigned int base)
+{
+	return (inb(base + T1_IDENT) & ~0x82) == 1;
+}
+
+static inline unsigned char t1_fifostatus(unsigned int base)
+{
+	return inb(base + T1_FIFOSTAT);
+}
+
+static inline unsigned int t1_get_slice(unsigned int base,
+					unsigned char *dp)
+{
+	unsigned int len, i;
+#ifdef FASTLINK_DEBUG
+	unsigned wcnt = 0, bcnt = 0;
+#endif
+
+	len = i = b1_get_word(base);
+        if (t1_isfastlink(base)) {
+		int status;
+		while (i > 0) {
+			status = t1_fifostatus(base) & (T1F_IREADY|T1F_IHALF);
+			if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
+
+			switch (status) {
+				case T1F_IREADY|T1F_IHALF|T1F_IFULL:
+					insb(base+B1_READ, dp, FIFO_INPBSIZE);
+					dp += FIFO_INPBSIZE;
+					i -= FIFO_INPBSIZE;
+#ifdef FASTLINK_DEBUG
+					wcnt += FIFO_INPBSIZE;
+#endif
+					break;
+				case T1F_IREADY|T1F_IHALF: 
+					insb(base+B1_READ,dp, i);
+#ifdef FASTLINK_DEBUG
+					wcnt += i;
+#endif
+					dp += i;
+					i = 0;
+					if (i == 0)
+						break;
+					/* fall through */
+				default:
+					*dp++ = b1_get_byte(base);
+					i--;
+#ifdef FASTLINK_DEBUG
+					bcnt++;
+#endif
+					break;
+			}
+	    }
+#ifdef FASTLINK_DEBUG
+	    if (wcnt)
+	    printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
+				base, len, wcnt, bcnt);
+#endif
+	} else {
+		while (i-- > 0)
+			*dp++ = b1_get_byte(base);
+	}
+	return len;
+}
+
+static inline void t1_put_slice(unsigned int base,
+				unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	b1_put_word(base, i);
+        if (t1_isfastlink(base)) {
+		int status;
+		while (i > 0) {
+			status = t1_fifostatus(base) & (T1F_OREADY|T1F_OHALF);
+			if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
+			switch (status) {
+				case T1F_OREADY|T1F_OHALF|T1F_OEMPTY: 
+					outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE);
+					dp += FIFO_OUTBSIZE;
+					i -= FIFO_OUTBSIZE;
+					break;
+				case T1F_OREADY|T1F_OHALF: 
+					outsb(base+B1_WRITE, dp, i);
+					dp += i;
+					i = 0;
+				        break;
+				default:
+					b1_put_byte(base, *dp++);
+					i--;
+					break;
+			}
+		}
+	} else {
+		while (i-- > 0)
+			b1_put_byte(base, *dp++);
+	}
+}
+
+static inline void t1_disable_irq(unsigned int base)
+{
+      t1outp(base, T1_IRQMASTER, 0x00);
+}
+
+static inline void t1_reset(unsigned int base)
+{
+        /* reset T1 Controller */
+        b1_reset(base);
+        /* disable irq on HEMA */
+        t1outp(base, B1_INSTAT, 0x00);
+        t1outp(base, B1_OUTSTAT, 0x00);
+        t1outp(base, T1_IRQMASTER, 0x00);
+        /* reset HEMA board configuration */
+	t1outp(base, T1_RESETBOARD, 0xf);
+}
+
+static inline void b1_setinterrupt(unsigned int base, unsigned irq,
+				   enum avmcardtype cardtype)
+{
+	switch (cardtype) {
+	   case avm_t1isa:
+              t1outp(base, B1_INSTAT, 0x00);
+              t1outp(base, B1_INSTAT, 0x02);
+	      t1outp(base, T1_IRQMASTER, 0x08);
+	      break;
+	   case avm_b1isa:
+	      b1outp(base, B1_INSTAT, 0x00);
+	      b1outp(base, B1_RESET, b1_irq_table[irq]);
+	      b1outp(base, B1_INSTAT, 0x02);
+	      break;
+	   default:
+	   case avm_m1:
+	   case avm_m2:
+	   case avm_b1pci:
+	      b1outp(base, B1_INSTAT, 0x00);
+	      b1outp(base, B1_RESET, 0xf0);
+	      b1outp(base, B1_INSTAT, 0x02);
+	      break;
+	   case avm_c4:
+	   case avm_t1pci:
+	      b1outp(base, B1_RESET, 0xf0);
+	      break;
+	 }
+}
+
+/* b1.c */
+avmcard *b1_alloc_card(int nr_controllers);
+void b1_free_card(avmcard *card);
+int b1_detect(unsigned int base, enum avmcardtype cardtype);
+void b1_getrevision(avmcard *card);
+int b1_load_t4file(avmcard *card, capiloaddatapart * t4file);
+int b1_load_config(avmcard *card, capiloaddatapart * config);
+int b1_loaded(avmcard *card);
+
+int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
+void b1_reset_ctr(struct capi_ctr *ctrl);
+void b1_register_appl(struct capi_ctr *ctrl, u16 appl,
+				capi_register_params *rp);
+void b1_release_appl(struct capi_ctr *ctrl, u16 appl);
+u16  b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+void b1_parse_version(avmctrl_info *card);
+irqreturn_t b1_interrupt(int interrupt, void *devptr, struct pt_regs *regs);
+
+int b1ctl_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl);
+
+avmcard_dmainfo *avmcard_dma_alloc(char *name, struct pci_dev *,
+				   long rsize, long ssize);
+void avmcard_dma_free(avmcard_dmainfo *);
+
+/* b1dma.c */
+int b1pciv4_detect(avmcard *card);
+int t1pci_detect(avmcard *card);
+void b1dma_reset(avmcard *card);
+irqreturn_t b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs);
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
+void b1dma_reset_ctr(struct capi_ctr *ctrl);
+void b1dma_remove_ctr(struct capi_ctr *ctrl);
+void b1dma_register_appl(struct capi_ctr *ctrl,
+				u16 appl,
+				capi_register_params *rp);
+void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl);
+u16  b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+int b1dmactl_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl);
+
+#endif /* _AVMCARD_H_ */
diff --git a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c
new file mode 100644
index 0000000..0c7061d
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1.c
@@ -0,0 +1,814 @@
+/* $Id: b1.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ * 
+ * Common module for AVM B1 cards.
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+int b1_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 192,				/* irq 3 */
+ 32,				/* irq 4 */
+ 160,				/* irq 5 */
+ 96,				/* irq 6 */
+ 224,				/* irq 7 */
+ 0,
+ 64,				/* irq 9 */
+ 80,				/* irq 10 */
+ 208,				/* irq 11 */
+ 48,				/* irq 12 */
+ 0,
+ 0,
+ 112,				/* irq 15 */
+};
+
+/* ------------------------------------------------------------- */	
+
+avmcard *b1_alloc_card(int nr_controllers)
+{
+	avmcard *card;
+	avmctrl_info *cinfo;
+	int i;
+
+	card = kmalloc(sizeof(*card), GFP_KERNEL);
+	if (!card)
+		return NULL;
+
+	memset(card, 0, sizeof(*card));
+
+        cinfo = kmalloc(sizeof(*cinfo) * nr_controllers, GFP_KERNEL);
+	if (!cinfo) {
+		kfree(card);
+		return NULL;
+	}
+	memset(cinfo, 0, sizeof(*cinfo) * nr_controllers);
+
+	card->ctrlinfo = cinfo;
+	for (i = 0; i < nr_controllers; i++) {
+		INIT_LIST_HEAD(&cinfo[i].ncci_head);
+		cinfo[i].card = card;
+	}
+	spin_lock_init(&card->lock);
+	card->nr_controllers = nr_controllers;
+
+	return card;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1_free_card(avmcard *card)
+{
+	kfree(card->ctrlinfo);
+	kfree(card);
+}
+
+/* ------------------------------------------------------------- */
+
+int b1_detect(unsigned int base, enum avmcardtype cardtype)
+{
+	int onoff, i;
+
+	/*
+	 * Statusregister 0000 00xx 
+	 */
+	if ((inb(base + B1_INSTAT) & 0xfc)
+	    || (inb(base + B1_OUTSTAT) & 0xfc))
+		return 1;
+	/*
+	 * Statusregister 0000 001x 
+	 */
+	b1outp(base, B1_INSTAT, 0x2);	/* enable irq */
+	/* b1outp(base, B1_OUTSTAT, 0x2); */
+	if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
+	    /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
+		return 2;
+	/*
+	 * Statusregister 0000 000x 
+	 */
+	b1outp(base, B1_INSTAT, 0x0);	/* disable irq */
+	b1outp(base, B1_OUTSTAT, 0x0);
+	if ((inb(base + B1_INSTAT) & 0xfe)
+	    || (inb(base + B1_OUTSTAT) & 0xfe))
+		return 3;
+        
+	for (onoff = !0, i= 0; i < 10 ; i++) {
+		b1_set_test_bit(base, cardtype, onoff);
+		if (b1_get_test_bit(base, cardtype) != onoff)
+		   return 4;
+		onoff = !onoff;
+	}
+
+	if (cardtype == avm_m1)
+	   return 0;
+
+        if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
+	   return 5;
+
+	return 0;
+}
+
+void b1_getrevision(avmcard *card)
+{
+    card->class = inb(card->port + B1_ANALYSE);
+    card->revision = inb(card->port + B1_REVISION);
+}
+
+#define FWBUF_SIZE	256
+int b1_load_t4file(avmcard *card, capiloaddatapart * t4file)
+{
+	unsigned char buf[FWBUF_SIZE];
+	unsigned char *dp;
+	int i, left;
+	unsigned int base = card->port;
+
+	dp = t4file->data;
+	left = t4file->len;
+	while (left > FWBUF_SIZE) {
+		if (t4file->user) {
+			if (copy_from_user(buf, dp, FWBUF_SIZE))
+				return -EFAULT;
+		} else {
+			memcpy(buf, dp, FWBUF_SIZE);
+		}
+		for (i = 0; i < FWBUF_SIZE; i++)
+			if (b1_save_put_byte(base, buf[i]) < 0) {
+				printk(KERN_ERR "%s: corrupted firmware file ?\n",
+						card->name);
+				return -EIO;
+			}
+		left -= FWBUF_SIZE;
+		dp += FWBUF_SIZE;
+	}
+	if (left) {
+		if (t4file->user) {
+			if (copy_from_user(buf, dp, left))
+				return -EFAULT;
+		} else {
+			memcpy(buf, dp, left);
+		}
+		for (i = 0; i < left; i++)
+			if (b1_save_put_byte(base, buf[i]) < 0) {
+				printk(KERN_ERR "%s: corrupted firmware file ?\n",
+						card->name);
+				return -EIO;
+			}
+	}
+	return 0;
+}
+
+int b1_load_config(avmcard *card, capiloaddatapart * config)
+{
+	unsigned char buf[FWBUF_SIZE];
+	unsigned char *dp;
+	unsigned int base = card->port;
+	int i, j, left;
+
+	dp = config->data;
+	left = config->len;
+	if (left) {
+		b1_put_byte(base, SEND_CONFIG);
+        	b1_put_word(base, 1);
+		b1_put_byte(base, SEND_CONFIG);
+        	b1_put_word(base, left);
+	}
+	while (left > FWBUF_SIZE) {
+		if (config->user) {
+			if (copy_from_user(buf, dp, FWBUF_SIZE))
+				return -EFAULT;
+		} else {
+			memcpy(buf, dp, FWBUF_SIZE);
+		}
+		for (i = 0; i < FWBUF_SIZE; ) {
+			b1_put_byte(base, SEND_CONFIG);
+			for (j=0; j < 4; j++) {
+				b1_put_byte(base, buf[i++]);
+			}
+		}
+		left -= FWBUF_SIZE;
+		dp += FWBUF_SIZE;
+	}
+	if (left) {
+		if (config->user) {
+			if (copy_from_user(buf, dp, left))
+				return -EFAULT;
+		} else {
+			memcpy(buf, dp, left);
+		}
+		for (i = 0; i < left; ) {
+			b1_put_byte(base, SEND_CONFIG);
+			for (j=0; j < 4; j++) {
+				if (i < left)
+					b1_put_byte(base, buf[i++]);
+				else
+					b1_put_byte(base, 0);
+			}
+		}
+	}
+	return 0;
+}
+
+int b1_loaded(avmcard *card)
+{
+	unsigned int base = card->port;
+	unsigned long stop;
+	unsigned char ans;
+	unsigned long tout = 2;
+
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_tx_empty(base))
+			break;
+	}
+	if (!b1_tx_empty(base)) {
+		printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n",
+				card->name);
+		return 0;
+	}
+	b1_put_byte(base, SEND_POLL);
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_rx_full(base)) {
+			if ((ans = b1_get_byte(base)) == RECEIVE_POLL) {
+				return 1;
+			}
+			printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n",
+					card->name, ans);
+			return 0;
+		}
+	}
+	printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name);
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+	unsigned long flags;
+	int retval;
+
+	b1_reset(port);
+
+	if ((retval = b1_load_t4file(card, &data->firmware))) {
+		b1_reset(port);
+		printk(KERN_ERR "%s: failed to load t4file!!\n",
+					card->name);
+		return retval;
+	}
+
+	b1_disable_irq(port);
+
+	if (data->configuration.len > 0 && data->configuration.data) {
+		if ((retval = b1_load_config(card, &data->configuration))) {
+			b1_reset(port);
+			printk(KERN_ERR "%s: failed to load config!!\n",
+					card->name);
+			return retval;
+		}
+	}
+
+	if (!b1_loaded(card)) {
+		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+		return -EIO;
+	}
+
+	spin_lock_irqsave(&card->lock, flags);
+	b1_setinterrupt(port, card->irq, card->cardtype);
+	b1_put_byte(port, SEND_INIT);
+	b1_put_word(port, CAPI_MAXAPPL);
+	b1_put_word(port, AVM_NCCI_PER_CHANNEL*2);
+	b1_put_word(port, ctrl->cnr - 1);
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return 0;
+}
+
+void b1_reset_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+
+	b1_reset(port);
+	b1_reset(port);
+
+	memset(cinfo->version, 0, sizeof(cinfo->version));
+	capilib_release(&cinfo->ncci_head);
+	capi_ctr_reseted(ctrl);
+}
+
+void b1_register_appl(struct capi_ctr *ctrl,
+				u16 appl,
+				capi_register_params *rp)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+	unsigned long flags;
+	int nconn, want = rp->level3cnt;
+
+	if (want > 0) nconn = want;
+	else nconn = ctrl->profile.nbchannel * -want;
+	if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+	spin_lock_irqsave(&card->lock, flags);
+	b1_put_byte(port, SEND_REGISTER);
+	b1_put_word(port, appl);
+	b1_put_word(port, 1024 * (nconn+1));
+	b1_put_word(port, nconn);
+	b1_put_word(port, rp->datablkcnt);
+	b1_put_word(port, rp->datablklen);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+void b1_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+	unsigned long flags;
+
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
+	spin_lock_irqsave(&card->lock, flags);
+	b1_put_byte(port, SEND_RELEASE);
+	b1_put_word(port, appl);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+	unsigned long flags;
+	u16 len = CAPIMSG_LEN(skb->data);
+	u8 cmd = CAPIMSG_COMMAND(skb->data);
+	u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+	u16 dlen, retval;
+
+	if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+		if (retval != CAPI_NOERROR) 
+			return retval;
+
+		dlen = CAPIMSG_DATALEN(skb->data);
+
+	 	spin_lock_irqsave(&card->lock, flags);
+		b1_put_byte(port, SEND_DATA_B3_REQ);
+		b1_put_slice(port, skb->data, len);
+		b1_put_slice(port, skb->data + len, dlen);
+		spin_unlock_irqrestore(&card->lock, flags);
+	} else {
+	 	spin_lock_irqsave(&card->lock, flags);
+		b1_put_byte(port, SEND_MESSAGE);
+		b1_put_slice(port, skb->data, len);
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+
+	dev_kfree_skb_any(skb);
+	return CAPI_NOERROR;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1_parse_version(avmctrl_info *cinfo)
+{
+	struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+	avmcard *card = cinfo->card;
+	capi_profile *profp;
+	u8 *dversion;
+	u8 flag;
+	int i, j;
+
+	for (j = 0; j < AVM_MAXVERSION; j++)
+		cinfo->version[j] = "\0\0" + 1;
+	for (i = 0, j = 0;
+	     j < AVM_MAXVERSION && i < cinfo->versionlen;
+	     j++, i += cinfo->versionbuf[i] + 1)
+		cinfo->version[j] = &cinfo->versionbuf[i + 1];
+
+	strlcpy(ctrl->serial, cinfo->version[VER_SERIAL], sizeof(ctrl->serial));
+	memcpy(&ctrl->profile, cinfo->version[VER_PROFILE],sizeof(capi_profile));
+	strlcpy(ctrl->manu, "AVM GmbH", sizeof(ctrl->manu));
+	dversion = cinfo->version[VER_DRIVER];
+	ctrl->version.majorversion = 2;
+	ctrl->version.minorversion = 0;
+	ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
+	ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf);
+	ctrl->version.minormanuversion = (dversion[3] - '0') << 4;
+	ctrl->version.minormanuversion |=
+			(dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
+
+	profp = &ctrl->profile;
+
+	flag = ((u8 *)(profp->manu))[1];
+	switch (flag) {
+	case 0: if (cinfo->version[VER_CARDTYPE])
+	           strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]);
+	        else strcpy(cinfo->cardname, "B1");
+		break;
+	case 3: strcpy(cinfo->cardname,"PCMCIA B"); break;
+	case 4: strcpy(cinfo->cardname,"PCMCIA M1"); break;
+	case 5: strcpy(cinfo->cardname,"PCMCIA M2"); break;
+	case 6: strcpy(cinfo->cardname,"B1 V3.0"); break;
+	case 7: strcpy(cinfo->cardname,"B1 PCI"); break;
+	default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break;
+        }
+        printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n",
+				card->name, ctrl->cnr, cinfo->cardname);
+
+        flag = ((u8 *)(profp->manu))[3];
+        if (flag)
+		printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n",
+			card->name,
+			ctrl->cnr,
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+
+        flag = ((u8 *)(profp->manu))[5];
+	if (flag)
+		printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n",
+			card->name,
+			ctrl->cnr,
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+}
+
+/* ------------------------------------------------------------- */
+
+irqreturn_t b1_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmcard *card = devptr;
+	avmctrl_info *cinfo = &card->ctrlinfo[0];
+	struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+	unsigned char b1cmd;
+	struct sk_buff *skb;
+
+	unsigned ApplId;
+	unsigned MsgLen;
+	unsigned DataB3Len;
+	unsigned NCCI;
+	unsigned WindowSize;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	if (!b1_rx_full(card->port)) {
+		spin_unlock_irqrestore(&card->lock, flags);
+		return IRQ_NONE;
+	}
+
+	b1cmd = b1_get_byte(card->port);
+
+	switch (b1cmd) {
+
+	case RECEIVE_DATA_B3_IND:
+
+		ApplId = (unsigned) b1_get_word(card->port);
+		MsgLen = b1_get_slice(card->port, card->msgbuf);
+		DataB3Len = b1_get_slice(card->port, card->databuf);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		if (MsgLen < 30) { /* not CAPI 64Bit */
+			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+			MsgLen = 30;
+			CAPIMSG_SETLEN(card->msgbuf, 30);
+		}
+		if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+			capi_ctr_handle_message(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_MESSAGE:
+
+		ApplId = (unsigned) b1_get_word(card->port);
+		MsgLen = b1_get_slice(card->port, card->msgbuf);
+		spin_unlock_irqrestore(&card->lock, flags);
+		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+				capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+						     CAPIMSG_NCCI(skb->data),
+						     CAPIMSG_MSGID(skb->data));
+
+			capi_ctr_handle_message(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_NEW_NCCI:
+
+		ApplId = b1_get_word(card->port);
+		NCCI = b1_get_word(card->port);
+		WindowSize = b1_get_word(card->port);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+
+		break;
+
+	case RECEIVE_FREE_NCCI:
+
+		ApplId = b1_get_word(card->port);
+		NCCI = b1_get_word(card->port);
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		if (NCCI != 0xffffffff)
+			capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+	       
+		break;
+
+	case RECEIVE_START:
+	   	/* b1_put_byte(card->port, SEND_POLLACK); */
+		spin_unlock_irqrestore(&card->lock, flags);
+		capi_ctr_resume_output(ctrl);
+		break;
+
+	case RECEIVE_STOP:
+		spin_unlock_irqrestore(&card->lock, flags);
+		capi_ctr_suspend_output(ctrl);
+		break;
+
+	case RECEIVE_INIT:
+
+		cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf);
+		spin_unlock_irqrestore(&card->lock, flags);
+		b1_parse_version(cinfo);
+		printk(KERN_INFO "%s: %s-card (%s) now active\n",
+		       card->name,
+		       cinfo->version[VER_CARDTYPE],
+		       cinfo->version[VER_DRIVER]);
+		capi_ctr_ready(ctrl);
+		break;
+
+	case RECEIVE_TASK_READY:
+		ApplId = (unsigned) b1_get_word(card->port);
+		MsgLen = b1_get_slice(card->port, card->msgbuf);
+		spin_unlock_irqrestore(&card->lock, flags);
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
+		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+				card->name, ApplId, card->msgbuf);
+		break;
+
+	case RECEIVE_DEBUGMSG:
+		MsgLen = b1_get_slice(card->port, card->msgbuf);
+		spin_unlock_irqrestore(&card->lock, flags);
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
+		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+		break;
+
+	case 0xff:
+		spin_unlock_irqrestore(&card->lock, flags);
+		printk(KERN_ERR "%s: card removed ?\n", card->name);
+		return IRQ_NONE;
+	default:
+		spin_unlock_irqrestore(&card->lock, flags);
+		printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
+				card->name, b1cmd);
+		return IRQ_HANDLED;
+	}
+	return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+int b1ctl_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	u8 flag;
+	int len = 0;
+	char *s;
+
+	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+	switch (card->cardtype) {
+	case avm_b1isa: s = "B1 ISA"; break;
+	case avm_b1pci: s = "B1 PCI"; break;
+	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+	case avm_m1: s = "M1"; break;
+	case avm_m2: s = "M2"; break;
+	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+	case avm_t1pci: s = "T1 PCI"; break;
+	case avm_c4: s = "C4"; break;
+	case avm_c2: s = "C2"; break;
+	default: s = "???"; break;
+	}
+	len += sprintf(page+len, "%-16s %s\n", "type", s);
+	if (card->cardtype == avm_t1isa)
+	   len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr);
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+	if (card->cardtype != avm_m1) {
+        	flag = ((u8 *)(ctrl->profile.manu))[3];
+        	if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+			"protocol",
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+	}
+	if (card->cardtype != avm_m1) {
+        	flag = ((u8 *)(ctrl->profile.manu))[5];
+		if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s\n",
+			"linetype",
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+	}
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
+           return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+#ifdef CONFIG_PCI
+
+avmcard_dmainfo *
+avmcard_dma_alloc(char *name, struct pci_dev *pdev, long rsize, long ssize)
+{
+	avmcard_dmainfo *p;
+	void *buf;
+
+	p = kmalloc(sizeof(avmcard_dmainfo), GFP_KERNEL);
+	if (!p) {
+		printk(KERN_WARNING "%s: no memory.\n", name);
+		goto err;
+	}
+	memset(p, 0, sizeof(avmcard_dmainfo));
+
+	p->recvbuf.size = rsize;
+	buf = pci_alloc_consistent(pdev, rsize, &p->recvbuf.dmaaddr);
+	if (!buf) {
+		printk(KERN_WARNING "%s: allocation of receive dma buffer failed.\n", name);
+		goto err_kfree;
+	}
+	p->recvbuf.dmabuf = buf;
+
+	p->sendbuf.size = ssize;
+	buf = pci_alloc_consistent(pdev, ssize, &p->sendbuf.dmaaddr);
+	if (!buf) {
+		printk(KERN_WARNING "%s: allocation of send dma buffer failed.\n", name);
+		goto err_free_consistent;
+	}
+
+	p->sendbuf.dmabuf = buf;
+	skb_queue_head_init(&p->send_queue);
+
+	return p;
+
+ err_free_consistent:
+	pci_free_consistent(p->pcidev, p->recvbuf.size,
+			    p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
+ err_kfree:
+	kfree(p);
+ err:
+	return NULL;
+}
+
+void avmcard_dma_free(avmcard_dmainfo *p)
+{
+	pci_free_consistent(p->pcidev, p->recvbuf.size,
+			    p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
+	pci_free_consistent(p->pcidev, p->sendbuf.size,
+			    p->sendbuf.dmabuf, p->sendbuf.dmaaddr);
+	skb_queue_purge(&p->send_queue);
+	kfree(p);
+}
+
+EXPORT_SYMBOL(avmcard_dma_alloc);
+EXPORT_SYMBOL(avmcard_dma_free);
+
+#endif
+
+EXPORT_SYMBOL(b1_irq_table);
+
+EXPORT_SYMBOL(b1_alloc_card);
+EXPORT_SYMBOL(b1_free_card);
+EXPORT_SYMBOL(b1_detect);
+EXPORT_SYMBOL(b1_getrevision);
+EXPORT_SYMBOL(b1_load_t4file);
+EXPORT_SYMBOL(b1_load_config);
+EXPORT_SYMBOL(b1_loaded);
+EXPORT_SYMBOL(b1_load_firmware);
+EXPORT_SYMBOL(b1_reset_ctr);
+EXPORT_SYMBOL(b1_register_appl);
+EXPORT_SYMBOL(b1_release_appl);
+EXPORT_SYMBOL(b1_send_message);
+
+EXPORT_SYMBOL(b1_parse_version);
+EXPORT_SYMBOL(b1_interrupt);
+
+EXPORT_SYMBOL(b1ctl_read_proc);
+
+static int __init b1_init(void)
+{
+	char *p;
+	char rev[32];
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, 32);
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	printk(KERN_INFO "b1: revision %s\n", rev);
+
+	return 0;
+}
+
+static void __exit b1_exit(void)
+{
+}
+
+module_init(b1_init);
+module_exit(b1_exit);
diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c
new file mode 100644
index 0000000..55bed00
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1dma.c
@@ -0,0 +1,980 @@
+/* $Id: b1dma.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ * 
+ * Common module for AVM B1 cards that support dma with AMCC
+ * 
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+#undef CONFIG_B1DMA_DEBUG
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: DMA support for active AVM cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+static int suppress_pollack = 0;
+MODULE_PARM(suppress_pollack, "0-1i");
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+/* S5933 */
+
+#define	AMCC_RXPTR	0x24
+#define	AMCC_RXLEN	0x28
+#define	AMCC_TXPTR	0x2c
+#define	AMCC_TXLEN	0x30
+
+#define	AMCC_INTCSR	0x38
+#	define EN_READ_TC_INT		0x00008000L
+#	define EN_WRITE_TC_INT		0x00004000L
+#	define EN_TX_TC_INT		EN_READ_TC_INT
+#	define EN_RX_TC_INT		EN_WRITE_TC_INT
+#	define AVM_FLAG			0x30000000L
+
+#	define ANY_S5933_INT		0x00800000L
+#	define READ_TC_INT		0x00080000L
+#	define WRITE_TC_INT		0x00040000L
+#	define	TX_TC_INT		READ_TC_INT
+#	define	RX_TC_INT		WRITE_TC_INT
+#	define MASTER_ABORT_INT		0x00100000L
+#	define TARGET_ABORT_INT		0x00200000L
+#	define BUS_MASTER_INT		0x00200000L
+#	define ALL_INT			0x000C0000L
+
+#define	AMCC_MCSR	0x3c
+#	define A2P_HI_PRIORITY		0x00000100L
+#	define EN_A2P_TRANSFERS		0x00000400L
+#	define P2A_HI_PRIORITY		0x00001000L
+#	define EN_P2A_TRANSFERS		0x00004000L
+#	define RESET_A2P_FLAGS		0x04000000L
+#	define RESET_P2A_FLAGS		0x02000000L
+
+/* ------------------------------------------------------------- */
+
+static inline void b1dma_writel(avmcard *card, u32 value, int off)
+{
+	writel(value, card->mbase + off);
+}
+
+static inline u32 b1dma_readl(avmcard *card, int off)
+{
+	return readl(card->mbase + off);
+}
+
+/* ------------------------------------------------------------- */
+
+static inline int b1dma_tx_empty(unsigned int port)
+{
+	return inb(port + 0x03) & 0x1;
+}
+
+static inline int b1dma_rx_full(unsigned int port)
+{
+	return inb(port + 0x02) & 0x1;
+}
+
+static int b1dma_tolink(avmcard *card, void *buf, unsigned int len)
+{
+	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	unsigned char *s = (unsigned char *)buf;
+	while (len--) {
+		while (   !b1dma_tx_empty(card->port)
+		       && time_before(jiffies, stop));
+		if (!b1dma_tx_empty(card->port)) 
+			return -1;
+	        t1outp(card->port, 0x01, *s++);
+	}
+	return 0;
+}
+
+static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len)
+{
+	unsigned long stop = jiffies + 1 * HZ;	/* maximum wait time 1 sec */
+	unsigned char *s = (unsigned char *)buf;
+	while (len--) {
+		while (   !b1dma_rx_full(card->port)
+		       && time_before(jiffies, stop));
+		if (!b1dma_rx_full(card->port)) 
+			return -1;
+	        *s++ = t1inp(card->port, 0x00);
+	}
+	return 0;
+}
+
+static int WriteReg(avmcard *card, u32 reg, u8 val)
+{
+	u8 cmd = 0x00;
+	if (   b1dma_tolink(card, &cmd, 1) == 0
+	    && b1dma_tolink(card, &reg, 4) == 0) {
+		u32 tmp = val;
+		return b1dma_tolink(card, &tmp, 4);
+	}
+	return -1;
+}
+
+static u8 ReadReg(avmcard *card, u32 reg)
+{
+	u8 cmd = 0x01;
+	if (   b1dma_tolink(card, &cmd, 1) == 0
+	    && b1dma_tolink(card, &reg, 4) == 0) {
+		u32 tmp;
+		if (b1dma_fromlink(card, &tmp, 4) == 0)
+			return (u8)tmp;
+	}
+	return 0xff;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, u8 val)
+{
+	u8 *s = *pp;
+	*s++ = val;
+	*pp = s;
+}
+
+static inline void _put_word(void **pp, u32 val)
+{
+	u8 *s = *pp;
+	*s++ = val & 0xff;
+	*s++ = (val >> 8) & 0xff;
+	*s++ = (val >> 16) & 0xff;
+	*s++ = (val >> 24) & 0xff;
+	*pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	_put_word(pp, i);
+	while (i-- > 0)
+		_put_byte(pp, *dp++);
+}
+
+static inline u8 _get_byte(void **pp)
+{
+	u8 *s = *pp;
+	u8 val;
+	val = *s++;
+	*pp = s;
+	return val;
+}
+
+static inline u32 _get_word(void **pp)
+{
+	u8 *s = *pp;
+	u32 val;
+	val = *s++;
+	val |= (*s++ << 8);
+	val |= (*s++ << 16);
+	val |= (*s++ << 24);
+	*pp = s;
+	return val;
+}
+
+static inline u32 _get_slice(void **pp, unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = _get_word(pp);
+	while (i-- > 0) *dp++ = _get_byte(pp);
+	return len;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_reset(avmcard *card)
+{
+	card->csr = 0x0;
+	b1dma_writel(card, card->csr, AMCC_INTCSR);
+	b1dma_writel(card, 0, AMCC_MCSR);
+	b1dma_writel(card, 0, AMCC_RXLEN);
+	b1dma_writel(card, 0, AMCC_TXLEN);
+
+	t1outp(card->port, 0x10, 0x00);
+	t1outp(card->port, 0x07, 0x00);
+
+	b1dma_writel(card, 0, AMCC_MCSR);
+	mdelay(10);
+	b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
+	mdelay(10);
+	b1dma_writel(card, 0, AMCC_MCSR);
+	if (card->cardtype == avm_t1pci)
+		mdelay(42);
+	else
+		mdelay(10);
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1dma_detect(avmcard *card)
+{
+	b1dma_writel(card, 0, AMCC_MCSR);
+	mdelay(10);
+	b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
+	mdelay(10);
+	b1dma_writel(card, 0, AMCC_MCSR);
+	mdelay(42);
+
+	b1dma_writel(card, 0, AMCC_RXLEN);
+	b1dma_writel(card, 0, AMCC_TXLEN);
+	card->csr = 0x0;
+	b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+	if (b1dma_readl(card, AMCC_MCSR) != 0x000000E6)
+		return 1;
+
+	b1dma_writel(card, 0xffffffff, AMCC_RXPTR);
+	b1dma_writel(card, 0xffffffff, AMCC_TXPTR);
+	if (   b1dma_readl(card, AMCC_RXPTR) != 0xfffffffc
+	    || b1dma_readl(card, AMCC_TXPTR) != 0xfffffffc)
+		return 2;
+
+	b1dma_writel(card, 0x0, AMCC_RXPTR);
+	b1dma_writel(card, 0x0, AMCC_TXPTR);
+	if (   b1dma_readl(card, AMCC_RXPTR) != 0x0
+	    || b1dma_readl(card, AMCC_TXPTR) != 0x0)
+		return 3;
+
+	t1outp(card->port, 0x10, 0x00);
+	t1outp(card->port, 0x07, 0x00);
+	
+	t1outp(card->port, 0x02, 0x02);
+	t1outp(card->port, 0x03, 0x02);
+
+	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x02
+	    || t1inp(card->port, 0x3) != 0x03)
+		return 4;
+
+	t1outp(card->port, 0x02, 0x00);
+	t1outp(card->port, 0x03, 0x00);
+
+	if (   (t1inp(card->port, 0x02) & 0xFE) != 0x00
+	    || t1inp(card->port, 0x3) != 0x01)
+		return 5;
+
+	return 0;
+}
+
+int t1pci_detect(avmcard *card)
+{
+	int ret;
+
+	if ((ret = b1dma_detect(card)) != 0)
+		return ret;
+	
+	/* Transputer test */
+	
+	if (   WriteReg(card, 0x80001000, 0x11) != 0
+	    || WriteReg(card, 0x80101000, 0x22) != 0
+	    || WriteReg(card, 0x80201000, 0x33) != 0
+	    || WriteReg(card, 0x80301000, 0x44) != 0)
+		return 6;
+
+	if (   ReadReg(card, 0x80001000) != 0x11
+	    || ReadReg(card, 0x80101000) != 0x22
+	    || ReadReg(card, 0x80201000) != 0x33
+	    || ReadReg(card, 0x80301000) != 0x44)
+		return 7;
+
+	if (   WriteReg(card, 0x80001000, 0x55) != 0
+	    || WriteReg(card, 0x80101000, 0x66) != 0
+	    || WriteReg(card, 0x80201000, 0x77) != 0
+	    || WriteReg(card, 0x80301000, 0x88) != 0)
+		return 8;
+
+	if (   ReadReg(card, 0x80001000) != 0x55
+	    || ReadReg(card, 0x80101000) != 0x66
+	    || ReadReg(card, 0x80201000) != 0x77
+	    || ReadReg(card, 0x80301000) != 0x88)
+		return 9;
+
+	return 0;
+}
+
+int b1pciv4_detect(avmcard *card)
+{
+	int ret, i;
+
+	if ((ret = b1dma_detect(card)) != 0)
+		return ret;
+	
+	for (i=0; i < 5 ; i++) {
+		if (WriteReg(card, 0x80A00000, 0x21) != 0)
+			return 6;
+		if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01)
+			return 7;
+	}
+	for (i=0; i < 5 ; i++) {
+		if (WriteReg(card, 0x80A00000, 0x20) != 0)
+			return 8;
+		if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00)
+			return 9;
+	}
+	
+	return 0;
+}
+
+static void b1dma_queue_tx(avmcard *card, struct sk_buff *skb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+
+	if (!(card->csr & EN_TX_TC_INT)) {
+		b1dma_dispatch_tx(card);
+		b1dma_writel(card, card->csr, AMCC_INTCSR);
+	}
+
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card)
+{
+	avmcard_dmainfo *dma = card->dma;
+	struct sk_buff *skb;
+	u8 cmd, subcmd;
+	u16 len;
+	u32 txlen;
+	void *p;
+	
+	skb = skb_dequeue(&dma->send_queue);
+
+	len = CAPIMSG_LEN(skb->data);
+
+	if (len) {
+		cmd = CAPIMSG_COMMAND(skb->data);
+		subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+		p = dma->sendbuf.dmabuf;
+
+		if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+			u16 dlen = CAPIMSG_DATALEN(skb->data);
+			_put_byte(&p, SEND_DATA_B3_REQ);
+			_put_slice(&p, skb->data, len);
+			_put_slice(&p, skb->data + len, dlen);
+		} else {
+			_put_byte(&p, SEND_MESSAGE);
+			_put_slice(&p, skb->data, len);
+		}
+		txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
+#ifdef CONFIG_B1DMA_DEBUG
+		printk(KERN_DEBUG "tx: put msg len=%d\n", txlen);
+#endif
+	} else {
+		txlen = skb->len-2;
+#ifdef CONFIG_B1DMA_POLLDEBUG
+		if (skb->data[2] == SEND_POLLACK)
+			printk(KERN_INFO "%s: send ack\n", card->name);
+#endif
+#ifdef CONFIG_B1DMA_DEBUG
+		printk(KERN_DEBUG "tx: put 0x%x len=%d\n", 
+		       skb->data[2], txlen);
+#endif
+		memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2);
+	}
+	txlen = (txlen + 3) & ~3;
+
+	b1dma_writel(card, dma->sendbuf.dmaaddr, AMCC_TXPTR);
+	b1dma_writel(card, txlen, AMCC_TXLEN);
+
+	card->csr |= EN_TX_TC_INT;
+
+	dev_kfree_skb_any(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(3, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_POLLACK);
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_rx(avmcard *card)
+{
+	avmctrl_info *cinfo = &card->ctrlinfo[0];
+	avmcard_dmainfo *dma = card->dma;
+	struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+	struct sk_buff *skb;
+	void *p = dma->recvbuf.dmabuf+4;
+	u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+	u8 b1cmd =  _get_byte(&p);
+
+#ifdef CONFIG_B1DMA_DEBUG
+	printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
+#endif
+	
+	switch (b1cmd) {
+	case RECEIVE_DATA_B3_IND:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		DataB3Len = _get_slice(&p, card->databuf);
+
+		if (MsgLen < 30) { /* not CAPI 64Bit */
+			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+			MsgLen = 30;
+			CAPIMSG_SETLEN(card->msgbuf, 30);
+		}
+		if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+			capi_ctr_handle_message(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_MESSAGE:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+				capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+						     CAPIMSG_NCCI(skb->data),
+						     CAPIMSG_MSGID(skb->data));
+
+			capi_ctr_handle_message(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_NEW_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+		WindowSize = _get_word(&p);
+
+		capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+
+		break;
+
+	case RECEIVE_FREE_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+
+		if (NCCI != 0xffffffff)
+			capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+
+		break;
+
+	case RECEIVE_START:
+#ifdef CONFIG_B1DMA_POLLDEBUG
+		printk(KERN_INFO "%s: receive poll\n", card->name);
+#endif
+		if (!suppress_pollack)
+			queue_pollack(card);
+		capi_ctr_resume_output(ctrl);
+		break;
+
+	case RECEIVE_STOP:
+		capi_ctr_suspend_output(ctrl);
+		break;
+
+	case RECEIVE_INIT:
+
+		cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+		b1_parse_version(cinfo);
+		printk(KERN_INFO "%s: %s-card (%s) now active\n",
+		       card->name,
+		       cinfo->version[VER_CARDTYPE],
+		       cinfo->version[VER_DRIVER]);
+		capi_ctr_ready(ctrl);
+		break;
+
+	case RECEIVE_TASK_READY:
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
+		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+				card->name, ApplId, card->msgbuf);
+		break;
+
+	case RECEIVE_DEBUGMSG:
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
+		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+		break;
+
+	default:
+		printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n",
+				card->name, b1cmd);
+		return;
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_interrupt(avmcard *card)
+{
+	u32 status;
+	u32 newcsr;
+
+	spin_lock(&card->lock);
+
+	status = b1dma_readl(card, AMCC_INTCSR);
+	if ((status & ANY_S5933_INT) == 0) {
+		spin_unlock(&card->lock);
+		return;
+	}
+
+        newcsr = card->csr | (status & ALL_INT);
+	if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
+	if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
+	b1dma_writel(card, newcsr, AMCC_INTCSR);
+
+	if ((status & RX_TC_INT) != 0) {
+		struct avmcard_dmainfo *dma = card->dma;
+		u32 rxlen;
+	   	if (card->dma->recvlen == 0) {
+	        	rxlen = b1dma_readl(card, AMCC_RXLEN);
+			if (rxlen == 0) {
+				dma->recvlen = *((u32 *)dma->recvbuf.dmabuf);
+				rxlen = (dma->recvlen + 3) & ~3;
+				b1dma_writel(card, dma->recvbuf.dmaaddr+4, AMCC_RXPTR);
+				b1dma_writel(card, rxlen, AMCC_RXLEN);
+#ifdef CONFIG_B1DMA_DEBUG
+			} else {
+				printk(KERN_ERR "%s: rx not complete (%d).\n",
+					card->name, rxlen);
+#endif
+			}
+		} else {
+			spin_unlock(&card->lock);
+			b1dma_handle_rx(card);
+	   		dma->recvlen = 0;
+			spin_lock(&card->lock);
+			b1dma_writel(card, dma->recvbuf.dmaaddr, AMCC_RXPTR);
+			b1dma_writel(card, 4, AMCC_RXLEN);
+		}
+	}
+
+	if ((status & TX_TC_INT) != 0) {
+		if (skb_queue_empty(&card->dma->send_queue))
+			card->csr &= ~EN_TX_TC_INT;
+		else
+			b1dma_dispatch_tx(card);
+	}
+	b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+	spin_unlock(&card->lock);
+}
+
+irqreturn_t b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmcard *card = devptr;
+
+	b1dma_handle_interrupt(card);
+	return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1dma_loaded(avmcard *card)
+{
+	unsigned long stop;
+	unsigned char ans;
+	unsigned long tout = 2;
+	unsigned int base = card->port;
+
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_tx_empty(base))
+			break;
+	}
+	if (!b1_tx_empty(base)) {
+		printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n",
+				card->name);
+		return 0;
+	}
+	b1_put_byte(base, SEND_POLLACK);
+	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+		if (b1_rx_full(base)) {
+			if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
+				return 1;
+			}
+			printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
+			return 0;
+		}
+	}
+	printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name);
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_send_init(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(15, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_INIT);
+	_put_word(&p, CAPI_MAXAPPL);
+	_put_word(&p, AVM_NCCI_PER_CHANNEL*30);
+	_put_word(&p, card->cardnr - 1);
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	b1dma_queue_tx(card, skb);
+}
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	int retval;
+
+	b1dma_reset(card);
+
+	if ((retval = b1_load_t4file(card, &data->firmware))) {
+		b1dma_reset(card);
+		printk(KERN_ERR "%s: failed to load t4file!!\n",
+					card->name);
+		return retval;
+	}
+
+	if (data->configuration.len > 0 && data->configuration.data) {
+		if ((retval = b1_load_config(card, &data->configuration))) {
+			b1dma_reset(card);
+			printk(KERN_ERR "%s: failed to load config!!\n",
+					card->name);
+			return retval;
+		}
+	}
+
+	if (!b1dma_loaded(card)) {
+		b1dma_reset(card);
+		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+		return -EIO;
+	}
+
+	card->csr = AVM_FLAG;
+	b1dma_writel(card, card->csr, AMCC_INTCSR);
+	b1dma_writel(card, EN_A2P_TRANSFERS|EN_P2A_TRANSFERS|A2P_HI_PRIORITY|
+		     P2A_HI_PRIORITY|RESET_A2P_FLAGS|RESET_P2A_FLAGS, 
+		     AMCC_MCSR);
+	t1outp(card->port, 0x07, 0x30);
+	t1outp(card->port, 0x10, 0xF0);
+
+	card->dma->recvlen = 0;
+	b1dma_writel(card, card->dma->recvbuf.dmaaddr, AMCC_RXPTR);
+	b1dma_writel(card, 4, AMCC_RXLEN);
+	card->csr |= EN_RX_TC_INT;
+	b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+        b1dma_send_init(card);
+
+	return 0;
+}
+
+void b1dma_reset_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+ 	b1dma_reset(card);
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	memset(cinfo->version, 0, sizeof(cinfo->version));
+	capilib_release(&cinfo->ncci_head);
+	capi_ctr_reseted(ctrl);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_register_appl(struct capi_ctr *ctrl,
+				u16 appl,
+				capi_register_params *rp)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	int want = rp->level3cnt;
+	int nconn;
+	void *p;
+
+	if (want > 0) nconn = want;
+	else nconn = ctrl->profile.nbchannel * -want;
+	if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+	skb = alloc_skb(23, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_REGISTER);
+	_put_word(&p, appl);
+	_put_word(&p, 1024 * (nconn+1));
+	_put_word(&p, nconn);
+	_put_word(&p, rp->datablkcnt);
+	_put_word(&p, rp->datablklen);
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	void *p;
+
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
+	skb = alloc_skb(7, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_RELEASE);
+	_put_word(&p, appl);
+
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	u16 retval = CAPI_NOERROR;
+
+ 	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+	}
+	if (retval == CAPI_NOERROR) 
+		b1dma_queue_tx(card, skb);
+
+	return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1dmactl_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	u8 flag;
+	int len = 0;
+	char *s;
+	u32 txoff, txlen, rxoff, rxlen, csr;
+	unsigned long flags;
+
+	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+	len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
+	switch (card->cardtype) {
+	case avm_b1isa: s = "B1 ISA"; break;
+	case avm_b1pci: s = "B1 PCI"; break;
+	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+	case avm_m1: s = "M1"; break;
+	case avm_m2: s = "M2"; break;
+	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+	case avm_t1pci: s = "T1 PCI"; break;
+	case avm_c4: s = "C4"; break;
+	case avm_c2: s = "C2"; break;
+	default: s = "???"; break;
+	}
+	len += sprintf(page+len, "%-16s %s\n", "type", s);
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+	if (card->cardtype != avm_m1) {
+        	flag = ((u8 *)(ctrl->profile.manu))[3];
+        	if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+			"protocol",
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+	}
+	if (card->cardtype != avm_m1) {
+        	flag = ((u8 *)(ctrl->profile.manu))[5];
+		if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s\n",
+			"linetype",
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+	}
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	txoff = (dma_addr_t)b1dma_readl(card, AMCC_TXPTR)-card->dma->sendbuf.dmaaddr;
+	txlen = b1dma_readl(card, AMCC_TXLEN);
+
+	rxoff = (dma_addr_t)b1dma_readl(card, AMCC_RXPTR)-card->dma->recvbuf.dmaaddr;
+	rxlen = b1dma_readl(card, AMCC_RXLEN);
+
+	csr  = b1dma_readl(card, AMCC_INTCSR);
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+        len += sprintf(page+len, "%-16s 0x%lx\n",
+				"csr (cached)", (unsigned long)card->csr);
+        len += sprintf(page+len, "%-16s 0x%lx\n",
+				"csr", (unsigned long)csr);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"txoff", (unsigned long)txoff);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"txlen", (unsigned long)txlen);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"rxoff", (unsigned long)rxoff);
+        len += sprintf(page+len, "%-16s %lu\n",
+				"rxlen", (unsigned long)rxlen);
+
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
+           return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+EXPORT_SYMBOL(b1dma_reset);
+EXPORT_SYMBOL(t1pci_detect);
+EXPORT_SYMBOL(b1pciv4_detect);
+EXPORT_SYMBOL(b1dma_interrupt);
+
+EXPORT_SYMBOL(b1dma_load_firmware);
+EXPORT_SYMBOL(b1dma_reset_ctr);
+EXPORT_SYMBOL(b1dma_register_appl);
+EXPORT_SYMBOL(b1dma_release_appl);
+EXPORT_SYMBOL(b1dma_send_message);
+EXPORT_SYMBOL(b1dmactl_read_proc);
+
+int b1dma_init(void)
+{
+	char *p;
+	char rev[32];
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, sizeof(rev));
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	printk(KERN_INFO "b1dma: revision %s\n", rev);
+
+	return 0;
+}
+
+void b1dma_exit(void)
+{
+}
+
+module_init(b1dma_init);
+module_exit(b1dma_exit);
diff --git a/drivers/isdn/hardware/avm/b1isa.c b/drivers/isdn/hardware/avm/b1isa.c
new file mode 100644
index 0000000..38bd4df
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1isa.c
@@ -0,0 +1,245 @@
+/* $Id: b1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ * 
+ * Module for AVM B1 ISA-card.
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 ISA card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static void b1isa_remove(struct pci_dev *pdev)
+{
+	avmctrl_info *cinfo = pci_get_drvdata(pdev);
+	avmcard *card;
+
+	if (!cinfo)
+		return;
+
+	card = cinfo->card;
+
+	b1_reset(card->port);
+	b1_reset(card->port);
+
+	detach_capi_ctr(&cinfo->capi_ctrl);
+	free_irq(card->irq, card);
+	release_region(card->port, AVMB1_PORTLEN);
+	b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *b1isa_procinfo(struct capi_ctr *ctrl);
+
+static int b1isa_probe(struct pci_dev *pdev)
+{
+	avmctrl_info *cinfo;
+	avmcard *card;
+	int retval;
+
+	card = b1_alloc_card(1);
+	if (!card) {
+		printk(KERN_WARNING "b1isa: no memory.\n");
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	cinfo = card->ctrlinfo;
+
+	card->port = pci_resource_start(pdev, 0);
+	card->irq = pdev->irq;
+	card->cardtype = avm_b1isa;
+	sprintf(card->name, "b1isa-%x", card->port);
+
+	if (   card->port != 0x150 && card->port != 0x250
+	    && card->port != 0x300 && card->port != 0x340) {
+		printk(KERN_WARNING "b1isa: invalid port 0x%x.\n", card->port);
+		retval = -EINVAL;
+		goto err_free;
+	}
+	if (b1_irq_table[card->irq & 0xf] == 0) {
+		printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq);
+		retval = -EINVAL;
+		goto err_free;
+	}
+	if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+		printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n",
+		       card->port, card->port + AVMB1_PORTLEN);
+		retval = -EBUSY;
+		goto err_free;
+	}
+	retval = request_irq(card->irq, b1_interrupt, 0, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq);
+		goto err_release_region;
+	}
+	b1_reset(card->port);
+	if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
+		printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n",
+		       card->port, retval);
+		retval = -ENODEV;
+		goto err_free_irq;
+	}
+	b1_reset(card->port);
+	b1_getrevision(card);
+
+	cinfo->capi_ctrl.owner = THIS_MODULE;
+	cinfo->capi_ctrl.driver_name   = "b1isa";
+	cinfo->capi_ctrl.driverdata    = cinfo;
+	cinfo->capi_ctrl.register_appl = b1_register_appl;
+	cinfo->capi_ctrl.release_appl  = b1_release_appl;
+	cinfo->capi_ctrl.send_message  = b1_send_message;
+	cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+	cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
+	cinfo->capi_ctrl.procinfo      = b1isa_procinfo;
+	cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+	strcpy(cinfo->capi_ctrl.name, card->name);
+
+	retval = attach_capi_ctr(&cinfo->capi_ctrl);
+	if (retval) {
+		printk(KERN_ERR "b1isa: attach controller failed.\n");
+		goto err_free_irq;
+	}
+
+	printk(KERN_INFO "b1isa: AVM B1 ISA at i/o %#x, irq %d, revision %d\n",
+	       card->port, card->irq, card->revision);
+
+	pci_set_drvdata(pdev, cinfo);
+	return 0;
+
+ err_free_irq:
+	free_irq(card->irq, card);
+ err_release_region:
+	release_region(card->port, AVMB1_PORTLEN);
+ err_free:
+	b1_free_card(card);
+ err:
+	return retval;
+}
+
+static char *b1isa_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->revision : 0
+		);
+	return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+
+static int b1isa_add_card(struct capi_driver *driver, capicardparams *data)
+{
+	int i;
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (isa_dev[i].resource[0].start)
+			continue;
+
+		isa_dev[i].resource[0].start = data->port;
+		isa_dev[i].irq = data->irq;
+
+		if (b1isa_probe(&isa_dev[i]) == 0)
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static struct capi_driver capi_driver_b1isa = {
+	.name		= "b1isa",
+	.revision	= "1.0",
+	.add_card       = b1isa_add_card,
+};
+
+static int __init b1isa_init(void)
+{
+	char *p;
+	char rev[32];
+	int i;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, 32);
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
+
+		isa_dev[i].resource[0].start = io[i];
+		isa_dev[i].irq = irq[i];
+
+		if (b1isa_probe(&isa_dev[i]) != 0)
+			return -ENODEV;
+	}
+
+	strlcpy(capi_driver_b1isa.revision, rev, 32);
+	register_capi_driver(&capi_driver_b1isa);
+	printk(KERN_INFO "b1isa: revision %s\n", rev);
+
+	return 0;
+}
+
+static void __exit b1isa_exit(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
+
+		b1isa_remove(&isa_dev[i]);
+	}
+	unregister_capi_driver(&capi_driver_b1isa);
+}
+
+module_init(b1isa_init);
+module_exit(b1isa_exit);
diff --git a/drivers/isdn/hardware/avm/b1pci.c b/drivers/isdn/hardware/avm/b1pci.c
new file mode 100644
index 0000000..5435a6c
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1pci.c
@@ -0,0 +1,417 @@
+/* $Id: b1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ * 
+ * Module for AVM B1 PCI-card.
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+static struct pci_device_id b1pci_pci_tbl[] = {
+	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, PCI_ANY_ID, PCI_ANY_ID },
+	{ }				/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, b1pci_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 PCI card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static char *b1pci_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->revision : 0
+		);
+	return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1pci_probe(struct capicardparams *p, struct pci_dev *pdev)
+{
+	avmcard *card;
+	avmctrl_info *cinfo;
+	int retval;
+
+	card = b1_alloc_card(1);
+	if (!card) {
+		printk(KERN_WARNING "b1pci: no memory.\n");
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	cinfo = card->ctrlinfo;
+	sprintf(card->name, "b1pci-%x", p->port);
+	card->port = p->port;
+	card->irq = p->irq;
+	card->cardtype = avm_b1pci;
+	
+	if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+		printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
+		       card->port, card->port + AVMB1_PORTLEN);
+		retval = -EBUSY;
+		goto err_free;
+	}
+	b1_reset(card->port);
+	retval = b1_detect(card->port, card->cardtype);
+	if (retval) {
+		printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
+		       card->port, retval);
+		retval = -ENODEV;
+		goto err_release_region;
+	}
+	b1_reset(card->port);
+	b1_getrevision(card);
+	
+	retval = request_irq(card->irq, b1_interrupt, SA_SHIRQ, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq);
+		retval = -EBUSY;
+		goto err_release_region;
+	}
+	
+	cinfo->capi_ctrl.driver_name   = "b1pci";
+	cinfo->capi_ctrl.driverdata    = cinfo;
+	cinfo->capi_ctrl.register_appl = b1_register_appl;
+	cinfo->capi_ctrl.release_appl  = b1_release_appl;
+	cinfo->capi_ctrl.send_message  = b1_send_message;
+	cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+	cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
+	cinfo->capi_ctrl.procinfo      = b1pci_procinfo;
+	cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+	strcpy(cinfo->capi_ctrl.name, card->name);
+	cinfo->capi_ctrl.owner         = THIS_MODULE;
+
+	retval = attach_capi_ctr(&cinfo->capi_ctrl);
+	if (retval) {
+		printk(KERN_ERR "b1pci: attach controller failed.\n");
+		goto err_free_irq;
+	}
+
+	if (card->revision >= 4) {
+		printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n",
+		       card->port, card->irq, card->revision);
+	} else {
+		printk(KERN_INFO "b1pci: AVM B1 PCI at i/o %#x, irq %d, revision %d\n",
+		       card->port, card->irq, card->revision);
+	}
+
+	pci_set_drvdata(pdev, card);
+	return 0;
+
+ err_free_irq:
+	free_irq(card->irq, card);
+ err_release_region:
+	release_region(card->port, AVMB1_PORTLEN);
+ err_free:
+	b1_free_card(card);
+ err:
+	return retval;
+}
+
+static void b1pci_remove(struct pci_dev *pdev)
+{
+	avmcard *card = pci_get_drvdata(pdev);
+	avmctrl_info *cinfo = card->ctrlinfo;
+	unsigned int port = card->port;
+
+	b1_reset(port);
+	b1_reset(port);
+
+	detach_capi_ctr(&cinfo->capi_ctrl);
+	free_irq(card->irq, card);
+	release_region(card->port, AVMB1_PORTLEN);
+	b1_free_card(card);
+}
+
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+/* ------------------------------------------------------------- */
+
+static char *b1pciv4_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->membase : 0,
+		cinfo->card ? cinfo->card->revision : 0
+		);
+	return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1pciv4_probe(struct capicardparams *p, struct pci_dev *pdev)
+{
+	avmcard *card;
+	avmctrl_info *cinfo;
+	int retval;
+
+	card = b1_alloc_card(1);
+	if (!card) {
+		printk(KERN_WARNING "b1pci: no memory.\n");
+		retval = -ENOMEM;
+		goto err;
+	}
+
+        card->dma = avmcard_dma_alloc("b1pci", pdev, 2048+128, 2048+128);
+	if (!card->dma) {
+		printk(KERN_WARNING "b1pci: dma alloc.\n");
+		retval = -ENOMEM;
+		goto err_free;
+	}
+
+	cinfo = card->ctrlinfo;
+	sprintf(card->name, "b1pciv4-%x", p->port);
+	card->port = p->port;
+	card->irq = p->irq;
+	card->membase = p->membase;
+	card->cardtype = avm_b1pci;
+
+	if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+		printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
+		       card->port, card->port + AVMB1_PORTLEN);
+		retval = -EBUSY;
+		goto err_free_dma;
+	}
+
+	card->mbase = ioremap(card->membase, 64);
+	if (!card->mbase) {
+		printk(KERN_NOTICE "b1pci: can't remap memory at 0x%lx\n",
+		       card->membase);
+		retval = -ENOMEM;
+		goto err_release_region;
+	}
+
+	b1dma_reset(card);
+
+	retval = b1pciv4_detect(card);
+	if (retval) {
+		printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
+		       card->port, retval);
+		retval = -ENODEV;
+		goto err_unmap;
+	}
+	b1dma_reset(card);
+	b1_getrevision(card);
+
+	retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "b1pci: unable to get IRQ %d.\n",
+		       card->irq);
+		retval = -EBUSY;
+		goto err_unmap;
+	}
+
+	cinfo->capi_ctrl.owner         = THIS_MODULE;
+	cinfo->capi_ctrl.driver_name   = "b1pciv4";
+	cinfo->capi_ctrl.driverdata    = cinfo;
+	cinfo->capi_ctrl.register_appl = b1dma_register_appl;
+	cinfo->capi_ctrl.release_appl  = b1dma_release_appl;
+	cinfo->capi_ctrl.send_message  = b1dma_send_message;
+	cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
+	cinfo->capi_ctrl.reset_ctr     = b1dma_reset_ctr;
+	cinfo->capi_ctrl.procinfo      = b1pciv4_procinfo;
+	cinfo->capi_ctrl.ctr_read_proc = b1dmactl_read_proc;
+	strcpy(cinfo->capi_ctrl.name, card->name);
+
+	retval = attach_capi_ctr(&cinfo->capi_ctrl);
+	if (retval) {
+		printk(KERN_ERR "b1pci: attach controller failed.\n");
+		goto err_free_irq;
+	}
+	card->cardnr = cinfo->capi_ctrl.cnr;
+
+	printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n",
+	       card->port, card->irq, card->membase, card->revision);
+
+	pci_set_drvdata(pdev, card);
+	return 0;
+
+ err_free_irq:
+	free_irq(card->irq, card);
+ err_unmap:
+	iounmap(card->mbase);
+ err_release_region:
+	release_region(card->port, AVMB1_PORTLEN);
+ err_free_dma:
+	avmcard_dma_free(card->dma);
+ err_free:
+	b1_free_card(card);
+ err:
+	return retval;
+
+}
+
+static void b1pciv4_remove(struct pci_dev *pdev)
+{
+	avmcard *card = pci_get_drvdata(pdev);
+	avmctrl_info *cinfo = card->ctrlinfo;
+
+ 	b1dma_reset(card);
+
+	detach_capi_ctr(&cinfo->capi_ctrl);
+	free_irq(card->irq, card);
+	iounmap(card->mbase);
+	release_region(card->port, AVMB1_PORTLEN);
+        avmcard_dma_free(card->dma);
+	b1_free_card(card);
+}
+
+#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
+
+static int __devinit b1pci_pci_probe(struct pci_dev *pdev,
+				     const struct pci_device_id *ent)
+{
+	struct capicardparams param;
+	int retval;
+
+	if (pci_enable_device(pdev) < 0) {
+		printk(KERN_ERR "b1pci: failed to enable AVM-B1\n");
+		return -ENODEV;
+	}
+	param.irq = pdev->irq;
+
+	if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+		pci_set_master(pdev);
+#endif
+		param.membase = pci_resource_start(pdev, 0);
+		param.port = pci_resource_start(pdev, 2);
+
+		printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
+		       param.port, param.irq, param.membase);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+		retval = b1pciv4_probe(&param, pdev);
+#else
+		retval = b1pci_probe(&param, pdev);
+#endif
+		if (retval != 0) {
+		        printk(KERN_ERR "b1pci: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n",
+			       param.port, param.irq, param.membase);
+		}
+	} else {
+		param.membase = 0;
+		param.port = pci_resource_start(pdev, 1);
+
+		printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
+		       param.port, param.irq);
+		retval = b1pci_probe(&param, pdev);
+		if (retval != 0) {
+		        printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
+			       param.port, param.irq);
+		}
+	}
+	return retval;
+}
+
+static void __devexit b1pci_pci_remove(struct pci_dev *pdev)
+{
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+	avmcard *card = pci_get_drvdata(pdev);
+
+	if (card->dma)
+		b1pciv4_remove(pdev);
+	else
+		b1pci_remove(pdev);
+#else
+	b1pci_remove(pdev);
+#endif
+}
+
+static struct pci_driver b1pci_pci_driver = {
+	.name		= "b1pci",
+	.id_table	= b1pci_pci_tbl,
+	.probe		= b1pci_pci_probe,
+	.remove		= __devexit_p(b1pci_pci_remove),
+};
+
+static struct capi_driver capi_driver_b1pci = {
+	.name		= "b1pci",
+	.revision	= "1.0",
+};
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+static struct capi_driver capi_driver_b1pciv4 = {
+	.name		= "b1pciv4",
+	.revision	= "1.0",
+};
+#endif
+
+static int __init b1pci_init(void)
+{
+	char *p;
+	char rev[32];
+	int err;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, 32);
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+
+	err = pci_register_driver(&b1pci_pci_driver);
+	if (!err) {
+		strlcpy(capi_driver_b1pci.revision, rev, 32);
+		register_capi_driver(&capi_driver_b1pci);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+		strlcpy(capi_driver_b1pciv4.revision, rev, 32);
+		register_capi_driver(&capi_driver_b1pciv4);
+#endif
+		printk(KERN_INFO "b1pci: revision %s\n", rev);
+	}
+	return err;
+}
+
+static void __exit b1pci_exit(void)
+{
+	unregister_capi_driver(&capi_driver_b1pci);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+	unregister_capi_driver(&capi_driver_b1pciv4);
+#endif
+	pci_unregister_driver(&b1pci_pci_driver);
+}
+
+module_init(b1pci_init);
+module_exit(b1pci_exit);
diff --git a/drivers/isdn/hardware/avm/b1pcmcia.c b/drivers/isdn/hardware/avm/b1pcmcia.c
new file mode 100644
index 0000000..9746cc5
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1pcmcia.c
@@ -0,0 +1,224 @@
+/* $Id: b1pcmcia.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ * 
+ * Module for AVM B1/M1/M2 PCMCIA-card.
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/capi.h>
+#include <linux/b1pcmcia.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM PCMCIA cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+
+	b1_reset(port);
+	b1_reset(port);
+
+	detach_capi_ctr(ctrl);
+	free_irq(card->irq, card);
+	b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static LIST_HEAD(cards);
+
+static char *b1pcmcia_procinfo(struct capi_ctr *ctrl);
+
+static int b1pcmcia_add_card(unsigned int port, unsigned irq,
+			     enum avmcardtype cardtype)
+{
+	avmctrl_info *cinfo;
+	avmcard *card;
+	char *cardname;
+	int retval;
+
+	card = b1_alloc_card(1);
+	if (!card) {
+		printk(KERN_WARNING "b1pcmcia: no memory.\n");
+		retval = -ENOMEM;
+		goto err;
+	}
+	cinfo = card->ctrlinfo;
+
+	switch (cardtype) {
+		case avm_m1: sprintf(card->name, "m1-%x", port); break;
+		case avm_m2: sprintf(card->name, "m2-%x", port); break;
+		default: sprintf(card->name, "b1pcmcia-%x", port); break;
+	}
+	card->port = port;
+	card->irq = irq;
+	card->cardtype = cardtype;
+
+	retval = request_irq(card->irq, b1_interrupt, 0, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n",
+		       card->irq);
+		retval = -EBUSY;
+		goto err_free;
+	}
+	b1_reset(card->port);
+	if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
+		printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n",
+		       card->port, retval);
+		retval = -ENODEV;
+		goto err_free_irq;
+	}
+	b1_reset(card->port);
+	b1_getrevision(card);
+
+	cinfo->capi_ctrl.owner         = THIS_MODULE;
+	cinfo->capi_ctrl.driver_name   = "b1pcmcia";
+	cinfo->capi_ctrl.driverdata    = cinfo;
+	cinfo->capi_ctrl.register_appl = b1_register_appl;
+	cinfo->capi_ctrl.release_appl  = b1_release_appl;
+	cinfo->capi_ctrl.send_message  = b1_send_message;
+	cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+	cinfo->capi_ctrl.reset_ctr     = b1_reset_ctr;
+	cinfo->capi_ctrl.procinfo      = b1pcmcia_procinfo;
+	cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+	strcpy(cinfo->capi_ctrl.name, card->name);
+
+	retval = attach_capi_ctr(&cinfo->capi_ctrl);
+	if (retval) {
+		printk(KERN_ERR "b1pcmcia: attach controller failed.\n");
+		goto err_free_irq;
+	}
+	switch (cardtype) {
+		case avm_m1: cardname = "M1"; break;
+		case avm_m2: cardname = "M2"; break;
+		default    : cardname = "B1 PCMCIA"; break;
+	}
+
+	printk(KERN_INFO "b1pcmcia: AVM %s at i/o %#x, irq %d, revision %d\n",
+	       cardname, card->port, card->irq, card->revision);
+
+	list_add(&card->list, &cards);
+	return cinfo->capi_ctrl.cnr;
+
+ err_free_irq:
+	free_irq(card->irq, card);
+ err_free:
+	b1_free_card(card);
+ err:
+	return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static char *b1pcmcia_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->revision : 0
+		);
+	return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1pcmcia_addcard_b1(unsigned int port, unsigned irq)
+{
+	return b1pcmcia_add_card(port, irq, avm_b1pcmcia);
+}
+
+int b1pcmcia_addcard_m1(unsigned int port, unsigned irq)
+{
+	return b1pcmcia_add_card(port, irq, avm_m1);
+}
+
+int b1pcmcia_addcard_m2(unsigned int port, unsigned irq)
+{
+	return b1pcmcia_add_card(port, irq, avm_m2);
+}
+
+int b1pcmcia_delcard(unsigned int port, unsigned irq)
+{
+	struct list_head *l;
+	avmcard *card;
+	
+	list_for_each(l, &cards) {
+		card = list_entry(l, avmcard, list);
+		if (card->port == port && card->irq == irq) {
+			b1pcmcia_remove_ctr(&card->ctrlinfo[0].capi_ctrl);
+			return 0;
+		}
+	}
+	return -ESRCH;
+}
+
+EXPORT_SYMBOL(b1pcmcia_addcard_b1);
+EXPORT_SYMBOL(b1pcmcia_addcard_m1);
+EXPORT_SYMBOL(b1pcmcia_addcard_m2);
+EXPORT_SYMBOL(b1pcmcia_delcard);
+
+static struct capi_driver capi_driver_b1pcmcia = {
+	.name		= "b1pcmcia",
+	.revision	= "1.0",
+};
+
+static int __init b1pcmcia_init(void)
+{
+	char *p;
+	char rev[32];
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, 32);
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	strlcpy(capi_driver_b1pcmcia.revision, rev, 32);
+	register_capi_driver(&capi_driver_b1pcmcia);
+	printk(KERN_INFO "b1pci: revision %s\n", rev);
+
+	return 0;
+}
+
+static void __exit b1pcmcia_exit(void)
+{
+	unregister_capi_driver(&capi_driver_b1pcmcia);
+}
+
+module_init(b1pcmcia_init);
+module_exit(b1pcmcia_exit);
diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c
new file mode 100644
index 0000000..fa6b93b
--- /dev/null
+++ b/drivers/isdn/hardware/avm/c4.c
@@ -0,0 +1,1310 @@
+/* $Id: c4.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ * 
+ * Module for AVM C4 & C2 card.
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+#undef CONFIG_C4_DEBUG
+#undef CONFIG_C4_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+static int suppress_pollack;
+
+static struct pci_device_id c4_pci_tbl[] = {
+	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 },
+	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 },
+	{ }			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, c4_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+MODULE_PARM(suppress_pollack, "0-1i");
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+#define DC21285_DRAM_A0MR	0x40000000
+#define DC21285_DRAM_A1MR	0x40004000
+#define DC21285_DRAM_A2MR	0x40008000
+#define DC21285_DRAM_A3MR	0x4000C000
+
+#define	CAS_OFFSET	0x88
+
+#define DC21285_ARMCSR_BASE	0x42000000
+
+#define	PCI_OUT_INT_STATUS	0x30
+#define	PCI_OUT_INT_MASK	0x34
+#define	MAILBOX_0		0x50
+#define	MAILBOX_1		0x54
+#define	MAILBOX_2		0x58
+#define	MAILBOX_3		0x5C
+#define	DOORBELL		0x60
+#define	DOORBELL_SETUP		0x64
+
+#define CHAN_1_CONTROL		0x90
+#define CHAN_2_CONTROL		0xB0
+#define DRAM_TIMING		0x10C
+#define DRAM_ADDR_SIZE_0	0x110
+#define DRAM_ADDR_SIZE_1	0x114
+#define DRAM_ADDR_SIZE_2	0x118
+#define DRAM_ADDR_SIZE_3	0x11C
+#define	SA_CONTROL		0x13C
+#define	XBUS_CYCLE		0x148
+#define	XBUS_STROBE		0x14C
+#define	DBELL_PCI_MASK		0x150
+#define DBELL_SA_MASK		0x154
+
+#define SDRAM_SIZE		0x1000000
+
+/* ------------------------------------------------------------- */
+
+#define	MBOX_PEEK_POKE		MAILBOX_0
+
+#define DBELL_ADDR		0x01
+#define DBELL_DATA		0x02
+#define DBELL_RNWR		0x40
+#define DBELL_INIT		0x80
+
+/* ------------------------------------------------------------- */
+
+#define	MBOX_UP_ADDR		MAILBOX_0
+#define	MBOX_UP_LEN		MAILBOX_1
+#define	MBOX_DOWN_ADDR		MAILBOX_2
+#define	MBOX_DOWN_LEN		MAILBOX_3
+
+#define	DBELL_UP_HOST		0x00000100
+#define	DBELL_UP_ARM		0x00000200
+#define	DBELL_DOWN_HOST		0x00000400
+#define	DBELL_DOWN_ARM		0x00000800
+#define	DBELL_RESET_HOST	0x40000000
+#define	DBELL_RESET_ARM		0x80000000
+
+/* ------------------------------------------------------------- */
+
+#define	DRAM_TIMING_DEF		0x001A01A5
+#define DRAM_AD_SZ_DEF0		0x00000045
+#define DRAM_AD_SZ_NULL		0x00000000
+
+#define SA_CTL_ALLRIGHT		0x64AA0271
+
+#define	INIT_XBUS_CYCLE		0x100016DB
+#define	INIT_XBUS_STROBE	0xF1F1F1F1
+
+/* ------------------------------------------------------------- */
+
+#define	RESET_TIMEOUT		(15*HZ)	/* 15 sec */
+#define	PEEK_POKE_TIMEOUT	(HZ/10)	/* 0.1 sec */
+
+/* ------------------------------------------------------------- */
+
+#define c4outmeml(addr, value)	writel(value, addr)
+#define c4inmeml(addr)	readl(addr)
+#define c4outmemw(addr, value)	writew(value, addr)
+#define c4inmemw(addr)	readw(addr)
+#define c4outmemb(addr, value)	writeb(value, addr)
+#define c4inmemb(addr)	readb(addr)
+
+/* ------------------------------------------------------------- */
+
+static inline int wait_for_doorbell(avmcard *card, unsigned long t)
+{
+	unsigned long stop;
+
+	stop = jiffies + t;
+	while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+		if (!time_before(jiffies, stop))
+			return -1;
+		mb();
+	}
+	return 0;
+}
+
+static int c4_poke(avmcard *card,  unsigned long off, unsigned long value)
+{
+
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+	
+	c4outmeml(card->mbase+MBOX_PEEK_POKE, off);
+	c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+
+	c4outmeml(card->mbase+MBOX_PEEK_POKE, value);
+	c4outmeml(card->mbase+DOORBELL, DBELL_DATA | DBELL_ADDR);
+
+	return 0;
+}
+
+static int c4_peek(avmcard *card,  unsigned long off, unsigned long *valuep)
+{
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+
+	c4outmeml(card->mbase+MBOX_PEEK_POKE, off);
+	c4outmeml(card->mbase+DOORBELL, DBELL_RNWR | DBELL_ADDR);
+
+	if (wait_for_doorbell(card, HZ/10) < 0)
+		return -1;
+
+	*valuep = c4inmeml(card->mbase+MBOX_PEEK_POKE);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_load_t4file(avmcard *card, capiloaddatapart * t4file)
+{
+	u32 val;
+	unsigned char *dp;
+	u_int left;
+	u32 loadoff = 0;
+
+	dp = t4file->data;
+	left = t4file->len;
+	while (left >= sizeof(u32)) {
+	        if (t4file->user) {
+			if (copy_from_user(&val, dp, sizeof(val)))
+				return -EFAULT;
+		} else {
+			memcpy(&val, dp, sizeof(val));
+		}
+		if (c4_poke(card, loadoff, val)) {
+			printk(KERN_ERR "%s: corrupted firmware file ?\n",
+					card->name);
+			return -EIO;
+		}
+		left -= sizeof(u32);
+		dp += sizeof(u32);
+		loadoff += sizeof(u32);
+	}
+	if (left) {
+		val = 0;
+		if (t4file->user) {
+			if (copy_from_user(&val, dp, left))
+				return -EFAULT;
+		} else {
+			memcpy(&val, dp, left);
+		}
+		if (c4_poke(card, loadoff, val)) {
+			printk(KERN_ERR "%s: corrupted firmware file ?\n",
+					card->name);
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, u8 val)
+{
+	u8 *s = *pp;
+	*s++ = val;
+	*pp = s;
+}
+
+static inline void _put_word(void **pp, u32 val)
+{
+	u8 *s = *pp;
+	*s++ = val & 0xff;
+	*s++ = (val >> 8) & 0xff;
+	*s++ = (val >> 16) & 0xff;
+	*s++ = (val >> 24) & 0xff;
+	*pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+	unsigned i = len;
+	_put_word(pp, i);
+	while (i-- > 0)
+		_put_byte(pp, *dp++);
+}
+
+static inline u8 _get_byte(void **pp)
+{
+	u8 *s = *pp;
+	u8 val;
+	val = *s++;
+	*pp = s;
+	return val;
+}
+
+static inline u32 _get_word(void **pp)
+{
+	u8 *s = *pp;
+	u32 val;
+	val = *s++;
+	val |= (*s++ << 8);
+	val |= (*s++ << 16);
+	val |= (*s++ << 24);
+	*pp = s;
+	return val;
+}
+
+static inline u32 _get_slice(void **pp, unsigned char *dp)
+{
+	unsigned int len, i;
+
+	len = i = _get_word(pp);
+	while (i-- > 0) *dp++ = _get_byte(pp);
+	return len;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_reset(avmcard *card)
+{
+	unsigned long stop;
+
+	c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM);
+
+	stop = jiffies + HZ*10;
+	while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+		if (!time_before(jiffies, stop))
+			return;
+		c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+		mb();
+	}
+
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_detect(avmcard *card)
+{
+	unsigned long stop, dummy;
+
+	c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+	if (c4inmeml(card->mbase+PCI_OUT_INT_MASK) != 0x0c)
+		return	1;
+
+	c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM);
+
+	stop = jiffies + HZ*10;
+	while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+		if (!time_before(jiffies, stop))
+			return 2;
+		c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+		mb();
+	}
+
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+	c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+
+	c4outmeml(card->mbase+MAILBOX_0, 0x55aa55aa);
+	if (c4inmeml(card->mbase+MAILBOX_0) != 0x55aa55aa) return 3;
+
+	c4outmeml(card->mbase+MAILBOX_0, 0xaa55aa55);
+	if (c4inmeml(card->mbase+MAILBOX_0) != 0xaa55aa55) return 4;
+
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_SA_MASK, 0)) return 5;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_PCI_MASK, 0)) return 6;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+SA_CONTROL, SA_CTL_ALLRIGHT))
+		return 7;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_CYCLE, INIT_XBUS_CYCLE))
+		return 8;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_STROBE, INIT_XBUS_STROBE))
+		return 8;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, 0)) return 9;
+
+        mdelay(1);
+
+	if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10;
+	if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11;
+	if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12;
+	if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13;
+
+	if (c4_poke(card, DC21285_DRAM_A0MR+CAS_OFFSET, 0)) return 14;
+	if (c4_poke(card, DC21285_DRAM_A1MR+CAS_OFFSET, 0)) return 15;
+	if (c4_poke(card, DC21285_DRAM_A2MR+CAS_OFFSET, 0)) return 16;
+	if (c4_poke(card, DC21285_DRAM_A3MR+CAS_OFFSET, 0)) return 17;
+
+        mdelay(1);
+
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, DRAM_TIMING_DEF))
+		return 18;
+
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_0,DRAM_AD_SZ_DEF0))
+		return 19;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_1,DRAM_AD_SZ_NULL))
+		return 20;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_2,DRAM_AD_SZ_NULL))
+		return 21;
+	if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_3,DRAM_AD_SZ_NULL))
+		return 22;
+
+	/* Transputer test */
+	
+	if (   c4_poke(card, 0x000000, 0x11111111)
+	    || c4_poke(card, 0x400000, 0x22222222)
+	    || c4_poke(card, 0x800000, 0x33333333)
+	    || c4_poke(card, 0xC00000, 0x44444444))
+		return 23;
+
+	if (   c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111
+	    || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222
+	    || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333
+	    || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444)
+		return 24;
+
+	if (   c4_poke(card, 0x000000, 0x55555555)
+	    || c4_poke(card, 0x400000, 0x66666666)
+	    || c4_poke(card, 0x800000, 0x77777777)
+	    || c4_poke(card, 0xC00000, 0x88888888))
+		return 25;
+
+	if (   c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555
+	    || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666
+	    || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777
+	    || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888)
+		return 26;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card)
+{
+	avmcard_dmainfo *dma = card->dma;
+	struct sk_buff *skb;
+	u8 cmd, subcmd;
+	u16 len;
+	u32 txlen;
+	void *p;
+
+
+	if (card->csr & DBELL_DOWN_ARM) { /* tx busy */
+		return;
+	}
+
+	skb = skb_dequeue(&dma->send_queue);
+	if (!skb) {
+#ifdef CONFIG_C4_DEBUG
+		printk(KERN_DEBUG "%s: tx underrun\n", card->name);
+#endif
+		return;
+	}
+
+	len = CAPIMSG_LEN(skb->data);
+
+	if (len) {
+		cmd = CAPIMSG_COMMAND(skb->data);
+		subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+		p = dma->sendbuf.dmabuf;
+
+		if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+			u16 dlen = CAPIMSG_DATALEN(skb->data);
+			_put_byte(&p, SEND_DATA_B3_REQ);
+			_put_slice(&p, skb->data, len);
+			_put_slice(&p, skb->data + len, dlen);
+		} else {
+			_put_byte(&p, SEND_MESSAGE);
+			_put_slice(&p, skb->data, len);
+		}
+		txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
+#ifdef CONFIG_C4_DEBUG
+		printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen);
+#endif
+	} else {
+		txlen = skb->len-2;
+#ifdef CONFIG_C4_POLLDEBUG
+		if (skb->data[2] == SEND_POLLACK)
+			printk(KERN_INFO "%s: ack to c4\n", card->name);
+#endif
+#ifdef CONFIG_C4_DEBUG
+		printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n",
+				card->name, skb->data[2], txlen);
+#endif
+		memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2);
+	}
+	txlen = (txlen + 3) & ~3;
+
+	c4outmeml(card->mbase+MBOX_DOWN_ADDR, dma->sendbuf.dmaaddr);
+	c4outmeml(card->mbase+MBOX_DOWN_LEN, txlen);
+
+	card->csr |= DBELL_DOWN_ARM;
+
+	c4outmeml(card->mbase+DOORBELL, DBELL_DOWN_ARM);
+
+	dev_kfree_skb_any(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(3, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_POLLACK);
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_handle_rx(avmcard *card)
+{
+	avmcard_dmainfo *dma = card->dma;
+	struct capi_ctr *ctrl;
+	avmctrl_info *cinfo;
+	struct sk_buff *skb;
+	void *p = dma->recvbuf.dmabuf;
+	u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+	u8 b1cmd =  _get_byte(&p);
+	u32 cidx;
+
+
+#ifdef CONFIG_C4_DEBUG
+	printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name,
+				b1cmd, (unsigned long)dma->recvlen);
+#endif
+	
+	switch (b1cmd) {
+	case RECEIVE_DATA_B3_IND:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		DataB3Len = _get_slice(&p, card->databuf);
+		cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+		if (cidx >= card->nlogcontr) cidx = 0;
+		ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+
+		if (MsgLen < 30) { /* not CAPI 64Bit */
+			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+			MsgLen = 30;
+			CAPIMSG_SETLEN(card->msgbuf, 30);
+		}
+		if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+			capi_ctr_handle_message(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_MESSAGE:
+
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+		if (cidx >= card->nlogcontr) cidx = 0;
+		cinfo = &card->ctrlinfo[cidx];
+		ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+
+		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+			printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+		} else {
+			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+			if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+				capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+						     CAPIMSG_NCCI(skb->data),
+						     CAPIMSG_MSGID(skb->data));
+
+			capi_ctr_handle_message(ctrl, ApplId, skb);
+		}
+		break;
+
+	case RECEIVE_NEW_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+		WindowSize = _get_word(&p);
+		cidx = (NCCI&0x7f) - card->cardnr;
+		if (cidx >= card->nlogcontr) cidx = 0;
+
+		capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize);
+
+		break;
+
+	case RECEIVE_FREE_NCCI:
+
+		ApplId = _get_word(&p);
+		NCCI = _get_word(&p);
+
+		if (NCCI != 0xffffffff) {
+			cidx = (NCCI&0x7f) - card->cardnr;
+			if (cidx >= card->nlogcontr) cidx = 0;
+			capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI);
+		}
+		break;
+
+	case RECEIVE_START:
+#ifdef CONFIG_C4_POLLDEBUG
+		printk(KERN_INFO "%s: poll from c4\n", card->name);
+#endif
+		if (!suppress_pollack)
+			queue_pollack(card);
+		for (cidx=0; cidx < card->nr_controllers; cidx++) {
+			ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+			capi_ctr_resume_output(ctrl);
+		}
+		break;
+
+	case RECEIVE_STOP:
+		for (cidx=0; cidx < card->nr_controllers; cidx++) {
+			ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+			capi_ctr_suspend_output(ctrl);
+		}
+		break;
+
+	case RECEIVE_INIT:
+
+	        cidx = card->nlogcontr;
+		if (cidx >= card->nr_controllers) {
+			printk(KERN_ERR "%s: card with %d controllers ??\n",
+					card->name, cidx+1);
+			break;
+		}
+	        card->nlogcontr++;
+	        cinfo = &card->ctrlinfo[cidx];
+		ctrl = &cinfo->capi_ctrl;
+		cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+		b1_parse_version(cinfo);
+		printk(KERN_INFO "%s: %s-card (%s) now active\n",
+		       card->name,
+		       cinfo->version[VER_CARDTYPE],
+		       cinfo->version[VER_DRIVER]);
+		capi_ctr_ready(&cinfo->capi_ctrl);
+		break;
+
+	case RECEIVE_TASK_READY:
+		ApplId = (unsigned) _get_word(&p);
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
+		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+				card->name, ApplId, card->msgbuf);
+		break;
+
+	case RECEIVE_DEBUGMSG:
+		MsgLen = _get_slice(&p, card->msgbuf);
+		card->msgbuf[MsgLen] = 0;
+		while (    MsgLen > 0
+		       && (   card->msgbuf[MsgLen-1] == '\n'
+			   || card->msgbuf[MsgLen-1] == '\r')) {
+			card->msgbuf[MsgLen-1] = 0;
+			MsgLen--;
+		}
+		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+		break;
+
+	default:
+		printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n",
+				card->name, b1cmd);
+		return;
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+static irqreturn_t c4_handle_interrupt(avmcard *card)
+{
+	unsigned long flags;
+	u32 status;
+
+	spin_lock_irqsave(&card->lock, flags);
+	status = c4inmeml(card->mbase+DOORBELL);
+
+	if (status & DBELL_RESET_HOST) {
+		u_int i;
+		c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+		spin_unlock_irqrestore(&card->lock, flags);
+		if (card->nlogcontr == 0)
+			return IRQ_HANDLED;
+		printk(KERN_ERR "%s: unexpected reset\n", card->name);
+                for (i=0; i < card->nr_controllers; i++) {
+			avmctrl_info *cinfo = &card->ctrlinfo[i];
+			memset(cinfo->version, 0, sizeof(cinfo->version));
+			capilib_release(&cinfo->ncci_head);
+			capi_ctr_reseted(&cinfo->capi_ctrl);
+		}
+		card->nlogcontr = 0;
+		return IRQ_HANDLED;
+	}
+
+	status &= (DBELL_UP_HOST | DBELL_DOWN_HOST);
+	if (!status) {
+		spin_unlock_irqrestore(&card->lock, flags);
+		return IRQ_HANDLED;
+	}
+	c4outmeml(card->mbase+DOORBELL, status);
+
+	if ((status & DBELL_UP_HOST) != 0) {
+		card->dma->recvlen = c4inmeml(card->mbase+MBOX_UP_LEN);
+		c4outmeml(card->mbase+MBOX_UP_LEN, 0);
+		c4_handle_rx(card);
+		card->dma->recvlen = 0;
+		c4outmeml(card->mbase+MBOX_UP_LEN, card->dma->recvbuf.size);
+		c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM);
+	}
+
+	if ((status & DBELL_DOWN_HOST) != 0) {
+		card->csr &= ~DBELL_DOWN_ARM;
+	        c4_dispatch_tx(card);
+	} else if (card->csr & DBELL_DOWN_HOST) {
+		if (c4inmeml(card->mbase+MBOX_DOWN_LEN) == 0) {
+		        card->csr &= ~DBELL_DOWN_ARM;
+			c4_dispatch_tx(card);
+		}
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t c4_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmcard *card = devptr;
+
+	return c4_handle_interrupt(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_send_init(avmcard *card)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(15, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+					card->name);
+		return;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_INIT);
+	_put_word(&p, CAPI_MAXAPPL);
+	_put_word(&p, AVM_NCCI_PER_CHANNEL*30);
+	_put_word(&p, card->cardnr - 1);
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+}
+
+static int queue_sendconfigword(avmcard *card, u32 val)
+{
+	struct sk_buff *skb;
+	void *p;
+
+	skb = alloc_skb(3+4, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, send config\n",
+					card->name);
+		return -ENOMEM;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_CONFIG);
+	_put_word(&p, val);
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	c4_dispatch_tx(card);
+	return 0;
+}
+
+static int queue_sendconfig(avmcard *card, char cval[4])
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	void *p;
+
+	skb = alloc_skb(3+4, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_CRIT "%s: no memory, send config\n",
+					card->name);
+		return -ENOMEM;
+	}
+	p = skb->data;
+	_put_byte(&p, 0);
+	_put_byte(&p, 0);
+	_put_byte(&p, SEND_CONFIG);
+	_put_byte(&p, cval[0]);
+	_put_byte(&p, cval[1]);
+	_put_byte(&p, cval[2]);
+	_put_byte(&p, cval[3]);
+	skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+	skb_queue_tail(&card->dma->send_queue, skb);
+	
+	spin_lock_irqsave(&card->lock, flags);
+	c4_dispatch_tx(card);
+	spin_unlock_irqrestore(&card->lock, flags);
+	return 0;
+}
+
+static int c4_send_config(avmcard *card, capiloaddatapart * config)
+{
+	u8 val[4];
+	unsigned char *dp;
+	u_int left;
+	int retval;
+	
+	if ((retval = queue_sendconfigword(card, 1)) != 0)
+		return retval;
+	if ((retval = queue_sendconfigword(card, config->len)) != 0)
+		return retval;
+
+	dp = config->data;
+	left = config->len;
+	while (left >= sizeof(u32)) {
+	        if (config->user) {
+			if (copy_from_user(val, dp, sizeof(val)))
+				return -EFAULT;
+		} else {
+			memcpy(val, dp, sizeof(val));
+		}
+		if ((retval = queue_sendconfig(card, val)) != 0)
+			return retval;
+		left -= sizeof(val);
+		dp += sizeof(val);
+	}
+	if (left) {
+		memset(val, 0, sizeof(val));
+		if (config->user) {
+			if (copy_from_user(&val, dp, left))
+				return -EFAULT;
+		} else {
+			memcpy(&val, dp, left);
+		}
+		if ((retval = queue_sendconfig(card, val)) != 0)
+			return retval;
+	}
+
+	return 0;
+}
+
+static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	int retval;
+
+	if ((retval = c4_load_t4file(card, &data->firmware))) {
+		printk(KERN_ERR "%s: failed to load t4file!!\n",
+					card->name);
+		c4_reset(card);
+		return retval;
+	}
+
+	card->csr = 0;
+	c4outmeml(card->mbase+MBOX_UP_LEN, 0);
+	c4outmeml(card->mbase+MBOX_DOWN_LEN, 0);
+	c4outmeml(card->mbase+DOORBELL, DBELL_INIT);
+	mdelay(1);
+	c4outmeml(card->mbase+DOORBELL,
+			DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST);
+
+	c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x08);
+
+	card->dma->recvlen = 0;
+	c4outmeml(card->mbase+MBOX_UP_ADDR, card->dma->recvbuf.dmaaddr);
+	c4outmeml(card->mbase+MBOX_UP_LEN, card->dma->recvbuf.size);
+	c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM);
+
+	if (data->configuration.len > 0 && data->configuration.data) {
+		retval = c4_send_config(card, &data->configuration);
+		if (retval) {
+			printk(KERN_ERR "%s: failed to set config!!\n",
+					card->name);
+			c4_reset(card);
+			return retval;
+		}
+	}
+
+        c4_send_init(card);
+
+	return 0;
+}
+
+
+void c4_reset_ctr(struct capi_ctr *ctrl)
+{
+	avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card;
+	avmctrl_info *cinfo;
+	u_int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+ 	c4_reset(card);
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+        for (i=0; i < card->nr_controllers; i++) {
+		cinfo = &card->ctrlinfo[i];
+		memset(cinfo->version, 0, sizeof(cinfo->version));
+		capi_ctr_reseted(&cinfo->capi_ctrl);
+	}
+	card->nlogcontr = 0;
+}
+
+static void c4_remove(struct pci_dev *pdev)
+{
+	avmcard *card = pci_get_drvdata(pdev);
+	avmctrl_info *cinfo;
+	u_int i;
+
+	if (!card)
+		return;
+
+ 	c4_reset(card);
+
+        for (i=0; i < card->nr_controllers; i++) {
+		cinfo = &card->ctrlinfo[i];
+		detach_capi_ctr(&cinfo->capi_ctrl);
+	}
+
+	free_irq(card->irq, card);
+	iounmap(card->mbase);
+	release_region(card->port, AVMB1_PORTLEN);
+        avmcard_dma_free(card->dma);
+        pci_set_drvdata(pdev, NULL);
+	b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+
+void c4_register_appl(struct capi_ctr *ctrl,
+				u16 appl,
+				capi_register_params *rp)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	struct sk_buff *skb;
+	int want = rp->level3cnt;
+	unsigned long flags;
+	int nconn;
+	void *p;
+
+	if (ctrl->cnr == card->cardnr) {
+
+		if (want > 0) nconn = want;
+		else nconn = ctrl->profile.nbchannel * 4 * -want;
+		if (nconn == 0) nconn = ctrl->profile.nbchannel * 4;
+
+		skb = alloc_skb(23, GFP_ATOMIC);
+		if (!skb) {
+			printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+						card->name);
+			return;
+		}
+		p = skb->data;
+		_put_byte(&p, 0);
+		_put_byte(&p, 0);
+		_put_byte(&p, SEND_REGISTER);
+		_put_word(&p, appl);
+		_put_word(&p, 1024 * (nconn+1));
+		_put_word(&p, nconn);
+		_put_word(&p, rp->datablkcnt);
+		_put_word(&p, rp->datablklen);
+		skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+		skb_queue_tail(&card->dma->send_queue, skb);
+	
+		spin_lock_irqsave(&card->lock, flags);
+		c4_dispatch_tx(card);
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+void c4_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned long flags;
+	struct sk_buff *skb;
+	void *p;
+
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
+	if (ctrl->cnr == card->cardnr) {
+		skb = alloc_skb(7, GFP_ATOMIC);
+		if (!skb) {
+			printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+						card->name);
+			return;
+		}
+		p = skb->data;
+		_put_byte(&p, 0);
+		_put_byte(&p, 0);
+		_put_byte(&p, SEND_RELEASE);
+		_put_word(&p, appl);
+
+		skb_put(skb, (u8 *)p - (u8 *)skb->data);
+		skb_queue_tail(&card->dma->send_queue, skb);
+		spin_lock_irqsave(&card->lock, flags);
+		c4_dispatch_tx(card);
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+}
+
+/* ------------------------------------------------------------- */
+
+
+static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	u16 retval = CAPI_NOERROR;
+	unsigned long flags;
+
+ 	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+	}
+	if (retval == CAPI_NOERROR) {
+		skb_queue_tail(&card->dma->send_queue, skb);
+		spin_lock_irqsave(&card->lock, flags);
+		c4_dispatch_tx(card);
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+	return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static char *c4_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->membase : 0
+		);
+	return cinfo->infobuf;
+}
+
+static int c4_read_proc(char *page, char **start, off_t off,
+        		int count, int *eof, struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	u8 flag;
+	int len = 0;
+	char *s;
+
+	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+	len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
+	switch (card->cardtype) {
+	case avm_b1isa: s = "B1 ISA"; break;
+	case avm_b1pci: s = "B1 PCI"; break;
+	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+	case avm_m1: s = "M1"; break;
+	case avm_m2: s = "M2"; break;
+	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+	case avm_t1pci: s = "T1 PCI"; break;
+	case avm_c4: s = "C4"; break;
+	case avm_c2: s = "C2"; break;
+	default: s = "???"; break;
+	}
+	len += sprintf(page+len, "%-16s %s\n", "type", s);
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
+	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+	if (card->cardtype != avm_m1) {
+        	flag = ((u8 *)(ctrl->profile.manu))[3];
+        	if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+			"protocol",
+			(flag & 0x01) ? " DSS1" : "",
+			(flag & 0x02) ? " CT1" : "",
+			(flag & 0x04) ? " VN3" : "",
+			(flag & 0x08) ? " NI1" : "",
+			(flag & 0x10) ? " AUSTEL" : "",
+			(flag & 0x20) ? " ESS" : "",
+			(flag & 0x40) ? " 1TR6" : ""
+			);
+	}
+	if (card->cardtype != avm_m1) {
+        	flag = ((u8 *)(ctrl->profile.manu))[5];
+		if (flag)
+			len += sprintf(page+len, "%-16s%s%s%s%s\n",
+			"linetype",
+			(flag & 0x01) ? " point to point" : "",
+			(flag & 0x02) ? " point to multipoint" : "",
+			(flag & 0x08) ? " leased line without D-channel" : "",
+			(flag & 0x04) ? " leased line with D-channel" : ""
+			);
+	}
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+	if (off+count >= len)
+	   *eof = 1;
+	if (len < off)
+           return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_add_card(struct capicardparams *p, struct pci_dev *dev,
+		       int nr_controllers)
+{
+	avmcard *card;
+	avmctrl_info *cinfo;
+	int retval;
+	int i;
+
+	card = b1_alloc_card(nr_controllers);
+	if (!card) {
+		printk(KERN_WARNING "c4: no memory.\n");
+		retval = -ENOMEM;
+		goto err;
+	}
+        card->dma = avmcard_dma_alloc("c4", dev, 2048+128, 2048+128);
+	if (!card->dma) {
+		printk(KERN_WARNING "c4: no memory.\n");
+		retval = -ENOMEM;
+		goto err_free;
+	}
+
+	sprintf(card->name, "c%d-%x", nr_controllers, p->port);
+	card->port = p->port;
+	card->irq = p->irq;
+	card->membase = p->membase;
+	card->cardtype = (nr_controllers == 4) ? avm_c4 : avm_c2;
+
+	if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+		printk(KERN_WARNING "c4: ports 0x%03x-0x%03x in use.\n",
+		       card->port, card->port + AVMB1_PORTLEN);
+		retval = -EBUSY;
+		goto err_free_dma;
+	}
+
+	card->mbase = ioremap(card->membase, 128);
+	if (card->mbase == 0) {
+		printk(KERN_NOTICE "c4: can't remap memory at 0x%lx\n",
+		       card->membase);
+		retval = -EIO;
+		goto err_release_region;
+	}
+
+	retval = c4_detect(card);
+	if (retval != 0) {
+		printk(KERN_NOTICE "c4: NO card at 0x%x error(%d)\n",
+		       card->port, retval);
+		retval = -EIO;
+		goto err_unmap;
+	}
+	c4_reset(card);
+
+	retval = request_irq(card->irq, c4_interrupt, SA_SHIRQ, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "c4: unable to get IRQ %d.\n",card->irq);
+		retval = -EBUSY;
+		goto err_unmap;
+	}
+
+	for (i=0; i < nr_controllers ; i++) {
+		cinfo = &card->ctrlinfo[i];
+		cinfo->capi_ctrl.owner = THIS_MODULE;
+		cinfo->capi_ctrl.driver_name   = "c4";
+		cinfo->capi_ctrl.driverdata    = cinfo;
+		cinfo->capi_ctrl.register_appl = c4_register_appl;
+		cinfo->capi_ctrl.release_appl  = c4_release_appl;
+		cinfo->capi_ctrl.send_message  = c4_send_message;
+		cinfo->capi_ctrl.load_firmware = c4_load_firmware;
+		cinfo->capi_ctrl.reset_ctr     = c4_reset_ctr;
+		cinfo->capi_ctrl.procinfo      = c4_procinfo;
+		cinfo->capi_ctrl.ctr_read_proc = c4_read_proc;
+		strcpy(cinfo->capi_ctrl.name, card->name);
+
+		retval = attach_capi_ctr(&cinfo->capi_ctrl);
+		if (retval) {
+			printk(KERN_ERR "c4: attach controller failed (%d).\n", i);
+			for (i--; i >= 0; i--) {
+				cinfo = &card->ctrlinfo[i];
+				detach_capi_ctr(&cinfo->capi_ctrl);
+			}
+			goto err_free_irq;
+		}
+		if (i == 0)
+			card->cardnr = cinfo->capi_ctrl.cnr;
+	}
+
+	printk(KERN_INFO "c4: AVM C%d at i/o %#x, irq %d, mem %#lx\n",
+	       nr_controllers, card->port, card->irq,
+	       card->membase);
+	pci_set_drvdata(dev, card);
+	return 0;
+
+ err_free_irq:
+	free_irq(card->irq, card);
+ err_unmap:
+	iounmap(card->mbase);
+ err_release_region:
+	release_region(card->port, AVMB1_PORTLEN);
+ err_free_dma:
+	avmcard_dma_free(card->dma);
+ err_free:
+	b1_free_card(card);
+ err:
+	return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static int __devinit c4_probe(struct pci_dev *dev,
+			      const struct pci_device_id *ent)
+{
+	int nr = ent->driver_data;
+	int retval = 0;
+	struct capicardparams param;
+
+	if (pci_enable_device(dev) < 0) {
+		printk(KERN_ERR "c4: failed to enable AVM-C%d\n", nr);
+		return -ENODEV;
+	}
+	pci_set_master(dev);
+
+	param.port = pci_resource_start(dev, 1);
+	param.irq = dev->irq;
+	param.membase = pci_resource_start(dev, 0);
+	
+	printk(KERN_INFO "c4: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n",
+	       nr, param.port, param.irq, param.membase);
+	
+	retval = c4_add_card(&param, dev, nr);
+	if (retval != 0) {
+		printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n",
+		       nr, param.port, param.irq, param.membase);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static struct pci_driver c4_pci_driver = {
+       .name           = "c4",
+       .id_table       = c4_pci_tbl,
+       .probe          = c4_probe,
+       .remove         = c4_remove,
+};
+
+static struct capi_driver capi_driver_c2 = {
+	.name		= "c2",
+	.revision	= "1.0",
+};
+
+static struct capi_driver capi_driver_c4 = {
+	.name		= "c4",
+	.revision	= "1.0",
+};
+
+static int __init c4_init(void)
+{
+	char *p;
+	char rev[32];
+	int err;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, 32);
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	err = pci_register_driver(&c4_pci_driver);
+	if (!err) {
+		strlcpy(capi_driver_c2.revision, rev, 32);
+		register_capi_driver(&capi_driver_c2);
+		strlcpy(capi_driver_c4.revision, rev, 32);
+		register_capi_driver(&capi_driver_c4);
+		printk(KERN_INFO "c4: revision %s\n", rev);
+	}
+	return err;
+}
+
+static void __exit c4_exit(void)
+{
+	unregister_capi_driver(&capi_driver_c2);
+	unregister_capi_driver(&capi_driver_c4);
+	pci_unregister_driver(&c4_pci_driver);
+}
+
+module_init(c4_init);
+module_exit(c4_exit);
diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c
new file mode 100644
index 0000000..cb9d9ce
--- /dev/null
+++ b/drivers/isdn/hardware/avm/t1isa.c
@@ -0,0 +1,596 @@
+/* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ * 
+ * Module for AVM T1 HEMA-card.
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/netdevice.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static int hema_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 0x80,				/* irq 3 */
+ 0,
+ 0x90,				/* irq 5 */
+ 0,
+ 0xA0,				/* irq 7 */
+ 0,
+ 0xB0,				/* irq 9 */
+ 0xC0,				/* irq 10 */
+ 0xD0,				/* irq 11 */
+ 0xE0,				/* irq 12 */
+ 0,
+ 0,
+ 0xF0,				/* irq 15 */
+};
+
+static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr)
+{
+	unsigned char cregs[8];
+	unsigned char reverse_cardnr;
+	unsigned char dummy;
+	int i;
+
+	reverse_cardnr =   ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
+		         | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
+	cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
+	cregs[1] = 0x00; /* fast & slow link connected to CON1 */
+	cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
+	cregs[3] = 0;
+	cregs[4] = 0x11; /* zero wait state */
+	cregs[5] = hema_irq_table[irq & 0xf];
+	cregs[6] = 0;
+	cregs[7] = 0;
+
+	/*
+	 * no one else should use the ISA bus in this moment,
+	 * but no function there to prevent this :-(
+	 * save_flags(flags); cli();
+	 */
+
+	/* board reset */
+	t1outp(base, T1_RESETBOARD, 0xf);
+	mdelay(100);
+	dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */
+
+	/* write config */
+	dummy = (base >> 4) & 0xff;
+	for (i=1;i<=0xf;i++) t1outp(base, i, dummy);
+	t1outp(base, HEMA_PAL_ID & 0xf, dummy);
+	t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
+	for(i=1;i<7;i++) t1outp(base, 0, cregs[i]);
+	t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
+	/* restore_flags(flags); */
+
+	mdelay(100);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+	mdelay(10);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 1);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1);
+	mdelay(100);
+	t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+	t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+	mdelay(10);
+	t1outp(base, T1_FASTLINK+T1_ANALYSE, 0);
+	mdelay(5);
+	t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0);
+
+	if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+		return 1;
+	if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */
+		return 2;
+	if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0)
+		return 3;
+	if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70)
+		return 4;
+	if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0)
+		return 5;
+	if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1)
+		return 6;
+	if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+		return 7;
+	if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0)
+		return 8;
+	if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0)
+		return 9;
+        return 0;
+}
+
+static irqreturn_t t1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	avmcard *card = devptr;
+	avmctrl_info *cinfo = &card->ctrlinfo[0];
+	struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+	unsigned char b1cmd;
+	struct sk_buff *skb;
+
+	unsigned ApplId;
+	unsigned MsgLen;
+	unsigned DataB3Len;
+	unsigned NCCI;
+	unsigned WindowSize;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	while (b1_rx_full(card->port)) {
+
+		b1cmd = b1_get_byte(card->port);
+
+		switch (b1cmd) {
+
+		case RECEIVE_DATA_B3_IND:
+
+			ApplId = (unsigned) b1_get_word(card->port);
+			MsgLen = t1_get_slice(card->port, card->msgbuf);
+			DataB3Len = t1_get_slice(card->port, card->databuf);
+			spin_unlock_irqrestore(&card->lock, flags);
+
+			if (MsgLen < 30) { /* not CAPI 64Bit */
+				memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+				MsgLen = 30;
+				CAPIMSG_SETLEN(card->msgbuf, 30);
+			}
+			if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+				printk(KERN_ERR "%s: incoming packet dropped\n",
+					card->name);
+			} else {
+				memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+				memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+				capi_ctr_handle_message(ctrl, ApplId, skb);
+			}
+			break;
+
+		case RECEIVE_MESSAGE:
+
+			ApplId = (unsigned) b1_get_word(card->port);
+			MsgLen = t1_get_slice(card->port, card->msgbuf);
+			spin_unlock_irqrestore(&card->lock, flags);
+			if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+				printk(KERN_ERR "%s: incoming packet dropped\n",
+						card->name);
+			} else {
+				memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+				if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3)
+					capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+							     CAPIMSG_NCCI(skb->data),
+							     CAPIMSG_MSGID(skb->data));
+
+				capi_ctr_handle_message(ctrl, ApplId, skb);
+			}
+			break;
+
+		case RECEIVE_NEW_NCCI:
+
+			ApplId = b1_get_word(card->port);
+			NCCI = b1_get_word(card->port);
+			WindowSize = b1_get_word(card->port);
+			spin_unlock_irqrestore(&card->lock, flags);
+
+			capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+
+			break;
+
+		case RECEIVE_FREE_NCCI:
+
+			ApplId = b1_get_word(card->port);
+			NCCI = b1_get_word(card->port);
+			spin_unlock_irqrestore(&card->lock, flags);
+
+			if (NCCI != 0xffffffff)
+				capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+
+			break;
+
+		case RECEIVE_START:
+			b1_put_byte(card->port, SEND_POLLACK);
+			spin_unlock_irqrestore(&card->lock, flags);
+			capi_ctr_resume_output(ctrl);
+			break;
+
+		case RECEIVE_STOP:
+			spin_unlock_irqrestore(&card->lock, flags);
+			capi_ctr_suspend_output(ctrl);
+			break;
+
+		case RECEIVE_INIT:
+
+			cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf);
+			spin_unlock_irqrestore(&card->lock, flags);
+			b1_parse_version(cinfo);
+			printk(KERN_INFO "%s: %s-card (%s) now active\n",
+			       card->name,
+			       cinfo->version[VER_CARDTYPE],
+			       cinfo->version[VER_DRIVER]);
+			capi_ctr_ready(ctrl);
+			break;
+
+		case RECEIVE_TASK_READY:
+			ApplId = (unsigned) b1_get_word(card->port);
+			MsgLen = t1_get_slice(card->port, card->msgbuf);
+			spin_unlock_irqrestore(&card->lock, flags);
+			card->msgbuf[MsgLen] = 0;
+			while (    MsgLen > 0
+			       && (   card->msgbuf[MsgLen-1] == '\n'
+				   || card->msgbuf[MsgLen-1] == '\r')) {
+				card->msgbuf[MsgLen-1] = 0;
+				MsgLen--;
+			}
+			printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+					card->name, ApplId, card->msgbuf);
+			break;
+
+		case RECEIVE_DEBUGMSG:
+			MsgLen = t1_get_slice(card->port, card->msgbuf);
+			spin_unlock_irqrestore(&card->lock, flags);
+			card->msgbuf[MsgLen] = 0;
+			while (    MsgLen > 0
+			       && (   card->msgbuf[MsgLen-1] == '\n'
+				   || card->msgbuf[MsgLen-1] == '\r')) {
+				card->msgbuf[MsgLen-1] = 0;
+				MsgLen--;
+			}
+			printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+			break;
+
+
+		case 0xff:
+			spin_unlock_irqrestore(&card->lock, flags);
+			printk(KERN_ERR "%s: card reseted ?\n", card->name);
+			return IRQ_HANDLED;
+		default:
+			spin_unlock_irqrestore(&card->lock, flags);
+			printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
+					card->name, b1cmd);
+			return IRQ_NONE;
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+
+static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+	unsigned long flags;
+	int retval;
+
+	t1_disable_irq(port);
+	b1_reset(port);
+
+	if ((retval = b1_load_t4file(card, &data->firmware))) {
+		b1_reset(port);
+		printk(KERN_ERR "%s: failed to load t4file!!\n",
+					card->name);
+		return retval;
+	}
+
+	if (data->configuration.len > 0 && data->configuration.data) {
+		if ((retval = b1_load_config(card, &data->configuration))) {
+			b1_reset(port);
+			printk(KERN_ERR "%s: failed to load config!!\n",
+					card->name);
+			return retval;
+		}
+	}
+
+	if (!b1_loaded(card)) {
+		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+		return -EIO;
+	}
+
+	spin_lock_irqsave(&card->lock, flags);
+	b1_setinterrupt(port, card->irq, card->cardtype);
+	b1_put_byte(port, SEND_INIT);
+	b1_put_word(port, CAPI_MAXAPPL);
+	b1_put_word(port, AVM_NCCI_PER_CHANNEL*30);
+	b1_put_word(port, ctrl->cnr - 1);
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	return 0;
+}
+
+void t1isa_reset_ctr(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+
+	t1_disable_irq(port);
+	b1_reset(port);
+	b1_reset(port);
+
+	memset(cinfo->version, 0, sizeof(cinfo->version));
+	capilib_release(&cinfo->ncci_head);
+	capi_ctr_reseted(ctrl);
+}
+
+static void t1isa_remove(struct pci_dev *pdev)
+{
+	avmctrl_info *cinfo = pci_get_drvdata(pdev);
+	avmcard *card;
+	
+	if (!cinfo)
+		return;
+
+	card = cinfo->card;
+
+	t1_disable_irq(card->port);
+	b1_reset(card->port);
+	b1_reset(card->port);
+	t1_reset(card->port);
+
+	detach_capi_ctr(&cinfo->capi_ctrl);
+	free_irq(card->irq, card);
+	release_region(card->port, AVMB1_PORTLEN);
+	b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+static char *t1isa_procinfo(struct capi_ctr *ctrl);
+
+static int t1isa_probe(struct pci_dev *pdev, int cardnr)
+{
+	avmctrl_info *cinfo;
+	avmcard *card;
+	int retval;
+
+	card = b1_alloc_card(1);
+	if (!card) {
+		printk(KERN_WARNING "t1isa: no memory.\n");
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	cinfo = card->ctrlinfo;
+	card->port = pci_resource_start(pdev, 0);
+	card->irq = pdev->irq;
+	card->cardtype = avm_t1isa;
+	card->cardnr = cardnr;
+	sprintf(card->name, "t1isa-%x", card->port);
+
+	if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) {
+		printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port);
+		retval = -EINVAL;
+		goto err_free;
+        }
+	if (hema_irq_table[card->irq & 0xf] == 0) {
+		printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq);
+		retval = -EINVAL;
+		goto err_free;
+	}
+	if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+		printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n",
+		       card->port, card->port + AVMB1_PORTLEN);
+		retval = -EBUSY;
+		goto err_free;
+	}
+	retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card);
+	if (retval) {
+		printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq);
+		retval = -EBUSY;
+		goto err_release_region;
+	}
+
+        if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) {
+		printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n",
+		       card->port, retval);
+		retval = -ENODEV;
+		goto err_free_irq;
+	}
+	t1_disable_irq(card->port);
+	b1_reset(card->port);
+
+	cinfo->capi_ctrl.owner = THIS_MODULE;
+	cinfo->capi_ctrl.driver_name   = "t1isa";
+	cinfo->capi_ctrl.driverdata    = cinfo;
+	cinfo->capi_ctrl.register_appl = b1_register_appl;
+	cinfo->capi_ctrl.release_appl  = b1_release_appl;
+	cinfo->capi_ctrl.send_message  = t1isa_send_message;
+	cinfo->capi_ctrl.load_firmware = t1isa_load_firmware;
+	cinfo->capi_ctrl.reset_ctr     = t1isa_reset_ctr;
+	cinfo->capi_ctrl.procinfo      = t1isa_procinfo;
+	cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+	strcpy(cinfo->capi_ctrl.name, card->name);
+
+	retval = attach_capi_ctr(&cinfo->capi_ctrl);
+	if (retval) {
+		printk(KERN_INFO "t1isa: attach controller failed.\n");
+		goto err_free_irq;
+	}
+
+	printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
+	       card->port, card->irq, card->cardnr);
+
+	pci_set_drvdata(pdev, cinfo);
+	return 0;
+
+ err_free_irq:
+	free_irq(card->irq, card);
+ err_release_region:
+	release_region(card->port, AVMB1_PORTLEN);
+ err_free:
+	b1_free_card(card);
+ err:
+	return retval;
+}
+
+static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+	avmcard *card = cinfo->card;
+	unsigned int port = card->port;
+	unsigned long flags;
+	u16 len = CAPIMSG_LEN(skb->data);
+	u8 cmd = CAPIMSG_COMMAND(skb->data);
+	u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+	u16 dlen, retval;
+
+	if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+		retval = capilib_data_b3_req(&cinfo->ncci_head,
+					     CAPIMSG_APPID(skb->data),
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+		if (retval != CAPI_NOERROR) 
+			return retval;
+
+		dlen = CAPIMSG_DATALEN(skb->data);
+
+		spin_lock_irqsave(&card->lock, flags);
+		b1_put_byte(port, SEND_DATA_B3_REQ);
+		t1_put_slice(port, skb->data, len);
+		t1_put_slice(port, skb->data + len, dlen);
+		spin_unlock_irqrestore(&card->lock, flags);
+	} else {
+
+		spin_lock_irqsave(&card->lock, flags);
+		b1_put_byte(port, SEND_MESSAGE);
+		t1_put_slice(port, skb->data, len);
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+
+	dev_kfree_skb_any(skb);
+	return CAPI_NOERROR;
+}
+/* ------------------------------------------------------------- */
+
+static char *t1isa_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d %d",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->cardnr : 0
+		);
+	return cinfo->infobuf;
+}
+
+
+/* ------------------------------------------------------------- */
+
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+static int cardnr[MAX_CARDS];
+
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM(cardnr, "1-" __MODULE_STRING(MAX_CARDS) "i");
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)");
+
+static int t1isa_add_card(struct capi_driver *driver, capicardparams *data)
+{
+	int i;
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (isa_dev[i].resource[0].start)
+			continue;
+
+		isa_dev[i].resource[0].start = data->port;
+		isa_dev[i].irq = data->irq;
+
+		if (t1isa_probe(&isa_dev[i], data->cardnr) == 0)
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static struct capi_driver capi_driver_t1isa = {
+	.name		= "t1isa",
+	.revision	= "1.0",
+	.add_card       = t1isa_add_card,
+};
+
+static int __init t1isa_init(void)
+{
+	char rev[32];
+	char *p;
+	int i;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, 32);
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
+
+		isa_dev[i].resource[0].start = io[i];
+		isa_dev[i].irq = irq[i];
+
+		if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0)
+			return -ENODEV;
+	}
+
+	strlcpy(capi_driver_t1isa.revision, rev, 32);
+	register_capi_driver(&capi_driver_t1isa);
+	printk(KERN_INFO "t1isa: revision %s\n", rev);
+
+	return 0;
+}
+
+static void __exit t1isa_exit(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_CARDS; i++) {
+		if (!io[i])
+			break;
+
+		t1isa_remove(&isa_dev[i]);
+	}
+}
+
+module_init(t1isa_init);
+module_exit(t1isa_exit);
diff --git a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c
new file mode 100644
index 0000000..2ceec8e
--- /dev/null
+++ b/drivers/isdn/hardware/avm/t1pci.c
@@ -0,0 +1,260 @@
+/* $Id: t1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ * 
+ * Module for AVM T1 PCI-card.
+ * 
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+#undef CONFIG_T1PCI_DEBUG
+#undef CONFIG_T1PCI_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+static char *revision = "$Revision: 1.1.2.2 $";
+/* ------------------------------------------------------------- */
+
+static struct pci_device_id t1pci_pci_tbl[] = {
+	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, PCI_ANY_ID, PCI_ANY_ID },
+	{ }				/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, t1pci_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 PCI card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static char *t1pci_procinfo(struct capi_ctr *ctrl);
+
+static int t1pci_add_card(struct capicardparams *p, struct pci_dev *pdev)
+{
+	avmcard *card;
+	avmctrl_info *cinfo;
+	int retval;
+
+	card = b1_alloc_card(1);
+	if (!card) {
+		printk(KERN_WARNING "t1pci: no memory.\n");
+		retval = -ENOMEM;
+		goto err;
+	}
+
+        card->dma = avmcard_dma_alloc("t1pci", pdev, 2048+128, 2048+128);
+	if (!card->dma) {
+		printk(KERN_WARNING "t1pci: no memory.\n");
+		retval = -ENOMEM;
+		goto err_free;
+	}
+
+	cinfo = card->ctrlinfo;
+	sprintf(card->name, "t1pci-%x", p->port);
+	card->port = p->port;
+	card->irq = p->irq;
+	card->membase = p->membase;
+	card->cardtype = avm_t1pci;
+
+	if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+		printk(KERN_WARNING "t1pci: ports 0x%03x-0x%03x in use.\n",
+		       card->port, card->port + AVMB1_PORTLEN);
+		retval = -EBUSY;
+		goto err_free_dma;
+	}
+
+	card->mbase = ioremap(card->membase, 64);
+	if (!card->mbase) {
+		printk(KERN_NOTICE "t1pci: can't remap memory at 0x%lx\n",
+		       card->membase);
+		retval = -EIO;
+		goto err_release_region;
+	}
+
+	b1dma_reset(card);
+
+	retval = t1pci_detect(card);
+	if (retval != 0) {
+		if (retval < 6)
+			printk(KERN_NOTICE "t1pci: NO card at 0x%x (%d)\n",
+			       card->port, retval);
+		else
+			printk(KERN_NOTICE "t1pci: card at 0x%x, but cable not connected or T1 has no power (%d)\n",
+			       card->port, retval);
+		retval = -EIO;
+		goto err_unmap;
+	}
+	b1dma_reset(card);
+
+	retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card);
+	if (retval) {
+		printk(KERN_ERR "t1pci: unable to get IRQ %d.\n", card->irq);
+		retval = -EBUSY;
+		goto err_unmap;
+	}
+
+	cinfo->capi_ctrl.owner         = THIS_MODULE;
+	cinfo->capi_ctrl.driver_name   = "t1pci";
+	cinfo->capi_ctrl.driverdata    = cinfo;
+	cinfo->capi_ctrl.register_appl = b1dma_register_appl;
+	cinfo->capi_ctrl.release_appl  = b1dma_release_appl;
+	cinfo->capi_ctrl.send_message  = b1dma_send_message;
+	cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
+	cinfo->capi_ctrl.reset_ctr     = b1dma_reset_ctr;
+	cinfo->capi_ctrl.procinfo      = t1pci_procinfo;
+	cinfo->capi_ctrl.ctr_read_proc = b1dmactl_read_proc;
+	strcpy(cinfo->capi_ctrl.name, card->name);
+
+	retval = attach_capi_ctr(&cinfo->capi_ctrl);
+	if (retval) {
+		printk(KERN_ERR "t1pci: attach controller failed.\n");
+		retval = -EBUSY;
+		goto err_free_irq;
+	}
+	card->cardnr = cinfo->capi_ctrl.cnr;
+
+	printk(KERN_INFO "t1pci: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n",
+	       card->port, card->irq, card->membase);
+
+	pci_set_drvdata(pdev, card);
+	return 0;
+
+ err_free_irq:
+	free_irq(card->irq, card);
+ err_unmap:
+	iounmap(card->mbase);
+ err_release_region:
+	release_region(card->port, AVMB1_PORTLEN);
+ err_free_dma:
+	avmcard_dma_free(card->dma);
+ err_free:
+	b1_free_card(card);
+ err:
+	return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_remove(struct pci_dev *pdev)
+{
+	avmcard *card = pci_get_drvdata(pdev);
+	avmctrl_info *cinfo = card->ctrlinfo;
+
+ 	b1dma_reset(card);
+
+	detach_capi_ctr(&cinfo->capi_ctrl);
+	free_irq(card->irq, card);
+	iounmap(card->mbase);
+	release_region(card->port, AVMB1_PORTLEN);
+	avmcard_dma_free(card->dma);
+	b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *t1pci_procinfo(struct capi_ctr *ctrl)
+{
+	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->port : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		cinfo->card ? cinfo->card->membase : 0
+		);
+	return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int __devinit t1pci_probe(struct pci_dev *dev,
+				 const struct pci_device_id *ent)
+{
+	struct capicardparams param;
+	int retval;
+
+	if (pci_enable_device(dev) < 0) {
+		printk(KERN_ERR	"t1pci: failed to enable AVM-T1-PCI\n");
+		return -ENODEV;
+	}
+	pci_set_master(dev);
+
+	param.port = pci_resource_start(dev, 1);
+	param.irq = dev->irq;
+	param.membase = pci_resource_start(dev, 0);
+
+	printk(KERN_INFO "t1pci: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n",
+	       param.port, param.irq, param.membase);
+
+	retval = t1pci_add_card(&param, dev);
+	if (retval != 0) {
+		printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n",
+		       param.port, param.irq, param.membase);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static struct pci_driver t1pci_pci_driver = {
+       .name           = "t1pci",
+       .id_table       = t1pci_pci_tbl,
+       .probe          = t1pci_probe,
+       .remove         = t1pci_remove,
+};
+
+static struct capi_driver capi_driver_t1pci = {
+	.name		= "t1pci",
+	.revision	= "1.0",
+};
+
+static int __init t1pci_init(void)
+{
+	char *p;
+	char rev[32];
+	int err;
+
+	if ((p = strchr(revision, ':')) != 0 && p[1]) {
+		strlcpy(rev, p + 2, 32);
+		if ((p = strchr(rev, '$')) != 0 && p > rev)
+		   *(p-1) = 0;
+	} else
+		strcpy(rev, "1.0");
+
+	err = pci_register_driver(&t1pci_pci_driver);
+	if (!err) {
+		strlcpy(capi_driver_t1pci.revision, rev, 32);
+		register_capi_driver(&capi_driver_t1pci);
+		printk(KERN_INFO "t1pci: revision %s\n", rev);
+	}
+	return err;
+}
+
+static void __exit t1pci_exit(void)
+{
+	unregister_capi_driver(&capi_driver_t1pci);
+	pci_unregister_driver(&t1pci_pci_driver);
+}
+
+module_init(t1pci_init);
+module_exit(t1pci_exit);
diff --git a/drivers/isdn/hardware/eicon/Kconfig b/drivers/isdn/hardware/eicon/Kconfig
new file mode 100644
index 0000000..51e66bc
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/Kconfig
@@ -0,0 +1,53 @@
+#
+# ISDN DIVAS Eicon driver
+#
+
+menu "Active Eicon DIVA Server cards"
+	depends on NET && ISDN && ISDN_CAPI!=n
+
+config CAPI_EICON
+	bool "Support Eicon cards"
+	help
+	  Enable support for Eicon Networks active ISDN cards.
+
+config ISDN_DIVAS
+	tristate "Support Eicon DIVA Server cards"
+	depends on CAPI_EICON && PROC_FS && PCI
+	help
+	  Say Y here if you have an Eicon Networks DIVA Server PCI ISDN card.
+	  In order to use this card, additional firmware is necessary, which
+	  has to be downloaded into the card using the divactrl utility.
+
+config ISDN_DIVAS_BRIPCI
+	bool "DIVA Server BRI/PCI support"
+	depends on ISDN_DIVAS
+	help
+	  Enable support for DIVA Server BRI-PCI.
+
+config ISDN_DIVAS_PRIPCI
+	bool "DIVA Server PRI/PCI support"
+	depends on ISDN_DIVAS
+	help
+	  Enable support for DIVA Server PRI-PCI.
+
+config ISDN_DIVAS_DIVACAPI
+	tristate "DIVA CAPI2.0 interface support"
+	depends on ISDN_DIVAS && ISDN_CAPI
+	help
+	  You need this to provide the CAPI interface
+	  for DIVA Server cards.
+
+config ISDN_DIVAS_USERIDI
+	tristate "DIVA User-IDI interface support"
+	depends on ISDN_DIVAS
+	help
+	  Enable support for user-mode IDI interface.
+
+config ISDN_DIVAS_MAINT
+	tristate "DIVA Maint driver support"
+	depends on ISDN_DIVAS && m
+	help
+	  Enable Divas Maintainance driver.
+
+endmenu
+
diff --git a/drivers/isdn/hardware/eicon/Makefile b/drivers/isdn/hardware/eicon/Makefile
new file mode 100644
index 0000000..4fa7fdb
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/Makefile
@@ -0,0 +1,23 @@
+# Makefile for the Eicon DIVA ISDN drivers.
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DIVAS)		+= divadidd.o divas.o
+obj-$(CONFIG_ISDN_DIVAS_MAINT)		+= diva_mnt.o
+obj-$(CONFIG_ISDN_DIVAS_USERIDI)	+= diva_idi.o
+obj-$(CONFIG_ISDN_DIVAS_DIVACAPI)	+= divacapi.o
+
+# Multipart objects. 
+
+divas-y					:= divasmain.o divasfunc.o di.o io.o istream.o \
+					   diva.o divasproc.o diva_dma.o
+divas-$(CONFIG_ISDN_DIVAS_BRIPCI)	+= os_bri.o  s_bri.o os_4bri.o s_4bri.o
+divas-$(CONFIG_ISDN_DIVAS_PRIPCI)	+= os_pri.o  s_pri.o
+
+divacapi-y				:= capimain.o capifunc.o message.o capidtmf.o
+
+divadidd-y				:= diva_didd.o diddfunc.o dadapter.o
+
+diva_mnt-y				:= divamnt.o mntfunc.o debug.o maintidi.o
+
+diva_idi-y				:= divasi.o idifunc.o um_idi.o dqueue.o
diff --git a/drivers/isdn/hardware/eicon/adapter.h b/drivers/isdn/hardware/eicon/adapter.h
new file mode 100644
index 0000000..71a7c2f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/adapter.h
@@ -0,0 +1,17 @@
+/* $Id: adapter.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVA_USER_MODE_IDI_ADAPTER_H__
+#define __DIVA_USER_MODE_IDI_ADAPTER_H__
+
+#define DIVA_UM_IDI_ADAPTER_REMOVED 0x00000001
+
+typedef struct _diva_um_idi_adapter {
+	struct list_head link;
+	DESCRIPTOR d;
+	int adapter_nr;
+	struct list_head entity_q;	/* entities linked to this adapter */
+	dword status;
+} diva_um_idi_adapter_t;
+
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/capi20.h b/drivers/isdn/hardware/eicon/capi20.h
new file mode 100644
index 0000000..7ebcccd
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capi20.h
@@ -0,0 +1,699 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 _INC_CAPI20  
+#define _INC_CAPI20
+        /* operations on message queues                             */
+        /* the common device type for CAPI20 drivers */
+#define FILE_DEVICE_CAPI20 0x8001
+        /* DEVICE_CONTROL codes for user and kernel mode applications */
+#define CAPI20_CTL_REGISTER             0x0801
+#define CAPI20_CTL_RELEASE              0x0802
+#define CAPI20_CTL_GET_MANUFACTURER     0x0805
+#define CAPI20_CTL_GET_VERSION          0x0806
+#define CAPI20_CTL_GET_SERIAL           0x0807
+#define CAPI20_CTL_GET_PROFILE          0x0808
+        /* INTERNAL_DEVICE_CONTROL codes for kernel mode applicatios only */
+#define CAPI20_CTL_PUT_MESSAGE          0x0803
+#define CAPI20_CTL_GET_MESSAGE          0x0804
+        /* the wrapped codes as required by the system */
+#define CAPI_CTL_CODE(f,m)              CTL_CODE(FILE_DEVICE_CAPI20,f,m,FILE_ANY_ACCESS)
+#define IOCTL_CAPI_REGISTER             CAPI_CTL_CODE(CAPI20_CTL_REGISTER,METHOD_BUFFERED)
+#define IOCTL_CAPI_RELEASE              CAPI_CTL_CODE(CAPI20_CTL_RELEASE,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_MANUFACTURER     CAPI_CTL_CODE(CAPI20_CTL_GET_MANUFACTURER,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_VERSION          CAPI_CTL_CODE(CAPI20_CTL_GET_VERSION,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_SERIAL           CAPI_CTL_CODE(CAPI20_CTL_GET_SERIAL,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_PROFILE          CAPI_CTL_CODE(CAPI20_CTL_GET_PROFILE,METHOD_BUFFERED)
+#define IOCTL_CAPI_PUT_MESSAGE          CAPI_CTL_CODE(CAPI20_CTL_PUT_MESSAGE,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_MESSAGE          CAPI_CTL_CODE(CAPI20_CTL_GET_MESSAGE,METHOD_BUFFERED)
+struct divas_capi_register_params  {
+  word MessageBufferSize;
+  word maxLogicalConnection;
+  word maxBDataBlocks;
+  word maxBDataLen;
+};
+struct divas_capi_version  {
+  word CapiMajor;
+  word CapiMinor;
+  word ManuMajor;
+  word ManuMinor;
+};
+typedef struct api_profile_s {
+  word          Number;
+  word          Channels;
+  dword         Global_Options;
+  dword         B1_Protocols;
+  dword         B2_Protocols;
+  dword         B3_Protocols;
+} API_PROFILE;
+        /* ISDN Common API message types                            */
+#define _ALERT_R                        0x8001
+#define _CONNECT_R                      0x8002
+#define _CONNECT_I                      0x8202
+#define _CONNECT_ACTIVE_I               0x8203
+#define _DISCONNECT_R                   0x8004
+#define _DISCONNECT_I                   0x8204
+#define _LISTEN_R                       0x8005
+#define _INFO_R                         0x8008
+#define _INFO_I                         0x8208
+#define _SELECT_B_REQ                   0x8041
+#define _FACILITY_R                     0x8080
+#define _FACILITY_I                     0x8280
+#define _CONNECT_B3_R                   0x8082
+#define _CONNECT_B3_I                   0x8282
+#define _CONNECT_B3_ACTIVE_I            0x8283
+#define _DISCONNECT_B3_R                0x8084
+#define _DISCONNECT_B3_I                0x8284
+#define _DATA_B3_R                      0x8086
+#define _DATA_B3_I                      0x8286
+#define _RESET_B3_R                     0x8087
+#define _RESET_B3_I                     0x8287
+#define _CONNECT_B3_T90_ACTIVE_I        0x8288
+#define _MANUFACTURER_R                 0x80ff
+#define _MANUFACTURER_I                 0x82ff
+        /* OR this to convert a REQUEST to a CONFIRM                */
+#define CONFIRM                 0x0100
+        /* OR this to convert a INDICATION to a RESPONSE            */
+#define RESPONSE                0x0100
+/*------------------------------------------------------------------*/
+/* diehl isdn private MANUFACTURER codes                            */
+/*------------------------------------------------------------------*/
+#define _DI_MANU_ID             0x44444944
+#define _DI_ASSIGN_PLCI         0x0001
+#define _DI_ADV_CODEC           0x0002
+#define _DI_DSP_CTRL            0x0003
+#define _DI_SIG_CTRL            0x0004
+#define _DI_RXT_CTRL            0x0005
+#define _DI_IDI_CTRL            0x0006
+#define _DI_CFG_CTRL            0x0007
+#define _DI_REMOVE_CODEC        0x0008
+#define _DI_OPTIONS_REQUEST     0x0009
+#define _DI_SSEXT_CTRL          0x000a
+#define _DI_NEGOTIATE_B3        0x000b
+/*------------------------------------------------------------------*/
+/* parameter structures                                             */
+/*------------------------------------------------------------------*/
+        /* ALERT-REQUEST                                            */
+typedef struct {
+  byte structs[1];      /* Additional Info */
+} _ALT_REQP;
+        /* ALERT-CONFIRM                                            */
+typedef struct {
+  word Info;
+} _ALT_CONP;
+        /* CONNECT-REQUEST                                          */
+typedef struct {
+  word CIP_Value;
+  byte structs[1];      /* Called party number,
+                           Called party subaddress,
+                           Calling party number,
+                           Calling party subaddress,
+                           B_protocol,
+                           BC,
+                           LLC,
+                           HLC,
+                           Additional Info */
+} _CON_REQP;
+        /* CONNECT-CONFIRM                                          */
+typedef struct {
+  word Info;
+} _CON_CONP;
+        /* CONNECT-INDICATION                                       */
+typedef struct {
+  word CIP_Value;
+  byte structs[1];      /* Called party number,
+                           Called party subaddress,
+                           Calling party number,
+                           Calling party subaddress,
+                           BC,
+                           LLC,
+                           HLC,
+                           Additional Info */
+} _CON_INDP;
+        /* CONNECT-RESPONSE                                         */
+typedef struct {
+  word Accept;
+  byte structs[1];      /* B_protocol,
+                           Connected party number,
+                           Connected party subaddress,
+                           LLC */
+} _CON_RESP;
+        /* CONNECT-ACTIVE-INDICATION                                */
+typedef struct {
+  byte structs[1];      /* Connected party number,
+                           Connected party subaddress,
+                           LLC */
+} _CON_A_INDP;
+        /* CONNECT-ACTIVE-RESPONSE                                  */
+typedef struct {
+  byte structs[1];      /* empty */
+} _CON_A_RESP;
+        /* DISCONNECT-REQUEST                                       */
+typedef struct {
+  byte structs[1];      /* Additional Info */
+} _DIS_REQP;
+        /* DISCONNECT-CONFIRM                                       */
+typedef struct {
+  word Info;
+} _DIS_CONP;
+        /* DISCONNECT-INDICATION                                    */
+typedef struct {
+  word Info;
+} _DIS_INDP;
+        /* DISCONNECT-RESPONSE                                      */
+typedef struct {
+  byte structs[1];      /* empty */
+} _DIS_RESP;
+        /* LISTEN-REQUEST                                           */
+typedef struct {
+  dword Info_Mask;
+  dword CIP_Mask;
+  byte structs[1];      /* Calling party number,
+                           Calling party subaddress */
+} _LIS_REQP;
+        /* LISTEN-CONFIRM                                           */
+typedef struct {
+  word Info;
+} _LIS_CONP;
+        /* INFO-REQUEST                                             */
+typedef struct {
+  byte structs[1];      /* Called party number,
+                           Additional Info */
+} _INF_REQP;
+        /* INFO-CONFIRM                                             */
+typedef struct {
+  word Info;
+} _INF_CONP;
+        /* INFO-INDICATION                                          */
+typedef struct {
+  word Number;
+  byte structs[1];      /* Info element */
+} _INF_INDP;
+        /* INFO-RESPONSE                                            */
+typedef struct {
+  byte structs[1];      /* empty */
+} _INF_RESP;
+        /* SELECT-B-REQUEST                                         */
+typedef struct {
+  byte structs[1];      /* B-protocol */
+} _SEL_B_REQP;
+        /* SELECT-B-CONFIRM                                         */
+typedef struct {
+  word Info;
+} _SEL_B_CONP;
+        /* FACILITY-REQUEST */
+typedef struct {
+  word Selector;
+  byte structs[1];      /* Facility parameters */
+} _FAC_REQP;
+        /* FACILITY-CONFIRM STRUCT FOR SUPPLEMENT. SERVICES */
+typedef struct {
+  byte  struct_length;
+  word  function;
+  byte  length;
+  word  SupplementaryServiceInfo;
+  dword SupportedServices;
+} _FAC_CON_STRUCTS;
+        /* FACILITY-CONFIRM */
+typedef struct {
+  word Info;
+  word Selector;
+  byte structs[1];      /* Facility parameters */
+} _FAC_CONP;
+        /* FACILITY-INDICATION */
+typedef struct {
+  word Selector;
+  byte structs[1];      /* Facility parameters */
+} _FAC_INDP;
+        /* FACILITY-RESPONSE */
+typedef struct {
+  word Selector;
+  byte structs[1];      /* Facility parameters */
+} _FAC_RESP;
+        /* CONNECT-B3-REQUEST                                       */
+typedef struct {
+  byte structs[1];      /* NCPI */
+} _CON_B3_REQP;
+        /* CONNECT-B3-CONFIRM                                       */
+typedef struct {
+  word Info;
+} _CON_B3_CONP;
+        /* CONNECT-B3-INDICATION                                    */
+typedef struct {
+  byte structs[1];      /* NCPI */
+} _CON_B3_INDP;
+        /* CONNECT-B3-RESPONSE                                      */
+typedef struct {
+  word Accept;
+  byte structs[1];      /* NCPI */
+} _CON_B3_RESP;
+        /* CONNECT-B3-ACTIVE-INDICATION                             */
+typedef struct {
+  byte structs[1];      /* NCPI */
+} _CON_B3_A_INDP;
+        /* CONNECT-B3-ACTIVE-RESPONSE                               */
+typedef struct {
+  byte structs[1];      /* empty */
+} _CON_B3_A_RESP;
+        /* DISCONNECT-B3-REQUEST                                    */
+typedef struct {
+  byte structs[1];      /* NCPI */
+} _DIS_B3_REQP;
+        /* DISCONNECT-B3-CONFIRM                                    */
+typedef struct {
+  word Info;
+} _DIS_B3_CONP;
+        /* DISCONNECT-B3-INDICATION                                 */
+typedef struct {
+  word Info;
+  byte structs[1];      /* NCPI */
+} _DIS_B3_INDP;
+        /* DISCONNECT-B3-RESPONSE                                   */
+typedef struct {
+  byte structs[1];      /* empty */
+} _DIS_B3_RESP;
+        /* DATA-B3-REQUEST                                          */
+typedef struct {
+  dword         Data;
+  word          Data_Length;
+  word          Number;
+  word          Flags;
+} _DAT_B3_REQP;
+        /* DATA-B3-REQUEST 64 BIT Systems                           */
+typedef struct {
+  dword         Data;
+  word          Data_Length;
+  word          Number;
+  word          Flags;
+  void          *pData;
+} _DAT_B3_REQ64P;
+        /* DATA-B3-CONFIRM                                          */
+typedef struct {
+  word          Number;
+  word          Info;
+} _DAT_B3_CONP;
+        /* DATA-B3-INDICATION                                       */
+typedef struct {
+  dword         Data;
+  word          Data_Length;
+  word          Number;
+  word          Flags;
+} _DAT_B3_INDP;
+        /* DATA-B3-INDICATION  64 BIT Systems                       */
+typedef struct {
+  dword         Data;
+  word          Data_Length;
+  word          Number;
+  word          Flags;
+  void          *pData;
+} _DAT_B3_IND64P;
+        /* DATA-B3-RESPONSE                                         */
+typedef struct {
+  word          Number;
+} _DAT_B3_RESP;
+        /* RESET-B3-REQUEST                                         */
+typedef struct {
+  byte structs[1];      /* NCPI */
+} _RES_B3_REQP;
+        /* RESET-B3-CONFIRM                                         */
+typedef struct {
+  word Info;
+} _RES_B3_CONP;
+        /* RESET-B3-INDICATION                                      */
+typedef struct {
+  byte structs[1];      /* NCPI */
+} _RES_B3_INDP;
+        /* RESET-B3-RESPONSE                                        */
+typedef struct {
+  byte structs[1];      /* empty */
+} _RES_B3_RESP;
+        /* CONNECT-B3-T90-ACTIVE-INDICATION                         */
+typedef struct {
+  byte structs[1];      /* NCPI */
+} _CON_B3_T90_A_INDP;
+        /* CONNECT-B3-T90-ACTIVE-RESPONSE                           */
+typedef struct {
+  word Reject;
+  byte structs[1];      /* NCPI */
+} _CON_B3_T90_A_RESP;
+/*------------------------------------------------------------------*/
+/* message structure                                                */
+/*------------------------------------------------------------------*/
+typedef struct _API_MSG CAPI_MSG;
+typedef struct _MSG_HEADER CAPI_MSG_HEADER;
+struct _API_MSG {
+  struct _MSG_HEADER {
+    word        length;
+    word        appl_id;
+    word        command;
+    word        number;
+    byte        controller;
+    byte        plci;
+    word        ncci;
+  } header;
+  union {
+    _ALT_REQP           alert_req;
+    _ALT_CONP           alert_con;
+    _CON_REQP           connect_req;
+    _CON_CONP           connect_con;
+    _CON_INDP           connect_ind;
+    _CON_RESP           connect_res;
+    _CON_A_INDP         connect_a_ind;
+    _CON_A_RESP         connect_a_res;
+    _DIS_REQP           disconnect_req;
+    _DIS_CONP           disconnect_con;
+    _DIS_INDP           disconnect_ind;
+    _DIS_RESP           disconnect_res;
+    _LIS_REQP           listen_req;
+    _LIS_CONP           listen_con;
+    _INF_REQP           info_req;
+    _INF_CONP           info_con;
+    _INF_INDP           info_ind;
+    _INF_RESP           info_res;
+    _SEL_B_REQP         select_b_req;
+    _SEL_B_CONP         select_b_con;
+    _FAC_REQP           facility_req;
+    _FAC_CONP           facility_con;
+    _FAC_INDP           facility_ind;
+    _FAC_RESP           facility_res;
+    _CON_B3_REQP        connect_b3_req;
+    _CON_B3_CONP        connect_b3_con;
+    _CON_B3_INDP        connect_b3_ind;
+    _CON_B3_RESP        connect_b3_res;
+    _CON_B3_A_INDP      connect_b3_a_ind;
+    _CON_B3_A_RESP      connect_b3_a_res;
+    _DIS_B3_REQP        disconnect_b3_req;
+    _DIS_B3_CONP        disconnect_b3_con;
+    _DIS_B3_INDP        disconnect_b3_ind;
+    _DIS_B3_RESP        disconnect_b3_res;
+    _DAT_B3_REQP        data_b3_req;
+    _DAT_B3_REQ64P      data_b3_req64;
+    _DAT_B3_CONP        data_b3_con;
+    _DAT_B3_INDP        data_b3_ind;
+    _DAT_B3_IND64P      data_b3_ind64;
+    _DAT_B3_RESP        data_b3_res;
+    _RES_B3_REQP        reset_b3_req;
+    _RES_B3_CONP        reset_b3_con;
+    _RES_B3_INDP        reset_b3_ind;
+    _RES_B3_RESP        reset_b3_res;
+    _CON_B3_T90_A_INDP  connect_b3_t90_a_ind;
+    _CON_B3_T90_A_RESP  connect_b3_t90_a_res;
+    byte                b[200];
+  } info;
+};
+/*------------------------------------------------------------------*/
+/* non-fatal errors                                                 */
+/*------------------------------------------------------------------*/
+#define _NCPI_IGNORED           0x0001
+#define _FLAGS_IGNORED          0x0002
+#define _ALERT_IGNORED          0x0003
+/*------------------------------------------------------------------*/
+/* API function error codes                                         */
+/*------------------------------------------------------------------*/
+#define GOOD                            0x0000
+#define _TOO_MANY_APPLICATIONS          0x1001
+#define _BLOCK_TOO_SMALL                0x1002
+#define _BUFFER_TOO_BIG                 0x1003
+#define _MSG_BUFFER_TOO_SMALL           0x1004
+#define _TOO_MANY_CONNECTIONS           0x1005
+#define _REG_CAPI_BUSY                  0x1007
+#define _REG_RESOURCE_ERROR             0x1008
+#define _REG_CAPI_NOT_INSTALLED         0x1009
+#define _WRONG_APPL_ID                  0x1101
+#define _BAD_MSG                        0x1102
+#define _QUEUE_FULL                     0x1103
+#define _GET_NO_MSG                     0x1104
+#define _MSG_LOST                       0x1105
+#define _WRONG_NOTIFY                   0x1106
+#define _CAPI_BUSY                      0x1107
+#define _RESOURCE_ERROR                 0x1108
+#define _CAPI_NOT_INSTALLED             0x1109
+#define _NO_EXTERNAL_EQUIPMENT          0x110a
+#define _ONLY_EXTERNAL_EQUIPMENT        0x110b
+/*------------------------------------------------------------------*/
+/* addressing/coding error codes                                    */
+/*------------------------------------------------------------------*/
+#define _WRONG_STATE                    0x2001
+#define _WRONG_IDENTIFIER               0x2002
+#define _OUT_OF_PLCI                    0x2003
+#define _OUT_OF_NCCI                    0x2004
+#define _OUT_OF_LISTEN                  0x2005
+#define _OUT_OF_FAX                     0x2006
+#define _WRONG_MESSAGE_FORMAT           0x2007
+#define _OUT_OF_INTERCONNECT_RESOURCES  0x2008
+/*------------------------------------------------------------------*/
+/* configuration error codes                                        */
+/*------------------------------------------------------------------*/
+#define _B1_NOT_SUPPORTED                    0x3001
+#define _B2_NOT_SUPPORTED                    0x3002
+#define _B3_NOT_SUPPORTED                    0x3003
+#define _B1_PARM_NOT_SUPPORTED               0x3004
+#define _B2_PARM_NOT_SUPPORTED               0x3005
+#define _B3_PARM_NOT_SUPPORTED               0x3006
+#define _B_STACK_NOT_SUPPORTED               0x3007
+#define _NCPI_NOT_SUPPORTED                  0x3008
+#define _CIP_NOT_SUPPORTED                   0x3009
+#define _FLAGS_NOT_SUPPORTED                 0x300a
+#define _FACILITY_NOT_SUPPORTED              0x300b
+#define _DATA_LEN_NOT_SUPPORTED              0x300c
+#define _RESET_NOT_SUPPORTED                 0x300d
+#define _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED 0x300e
+#define _REQUEST_NOT_ALLOWED_IN_THIS_STATE   0x3010
+#define _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP 0x3011
+/*------------------------------------------------------------------*/
+/* reason codes                                                     */
+/*------------------------------------------------------------------*/
+#define _L1_ERROR                       0x3301
+#define _L2_ERROR                       0x3302
+#define _L3_ERROR                       0x3303
+#define _OTHER_APPL_CONNECTED           0x3304
+#define _CAPI_GUARD_ERROR               0x3305
+#define _L3_CAUSE                       0x3400
+/*------------------------------------------------------------------*/
+/* b3 reason codes                                                  */
+/*------------------------------------------------------------------*/
+#define _B_CHANNEL_LOST                 0x3301
+#define _B2_ERROR                       0x3302
+#define _B3_ERROR                       0x3303
+/*------------------------------------------------------------------*/
+/* fax error codes                                                  */
+/*------------------------------------------------------------------*/
+#define _FAX_NO_CONNECTION              0x3311
+#define _FAX_TRAINING_ERROR             0x3312
+#define _FAX_REMOTE_REJECT              0x3313
+#define _FAX_REMOTE_ABORT               0x3314
+#define _FAX_PROTOCOL_ERROR             0x3315
+#define _FAX_TX_UNDERRUN                0x3316
+#define _FAX_RX_OVERFLOW                0x3317
+#define _FAX_LOCAL_ABORT                0x3318
+#define _FAX_PARAMETER_ERROR            0x3319
+/*------------------------------------------------------------------*/
+/* line interconnect error codes                                    */
+/*------------------------------------------------------------------*/
+#define _LI_USER_INITIATED               0x0000
+#define _LI_LINE_NO_LONGER_AVAILABLE     0x3805
+#define _LI_INTERCONNECT_NOT_ESTABLISHED 0x3806
+#define _LI_LINES_NOT_COMPATIBLE         0x3807
+#define _LI2_USER_INITIATED              0x0000
+#define _LI2_PLCI_HAS_NO_BCHANNEL        0x3800
+#define _LI2_LINES_NOT_COMPATIBLE        0x3801
+#define _LI2_NOT_IN_SAME_INTERCONNECTION 0x3802
+/*------------------------------------------------------------------*/
+/* global options                                                   */
+/*------------------------------------------------------------------*/
+#define GL_INTERNAL_CONTROLLER_SUPPORTED     0x00000001L
+#define GL_EXTERNAL_EQUIPMENT_SUPPORTED      0x00000002L
+#define GL_HANDSET_SUPPORTED                 0x00000004L
+#define GL_DTMF_SUPPORTED                    0x00000008L
+#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED  0x00000010L
+#define GL_CHANNEL_ALLOCATION_SUPPORTED      0x00000020L
+#define GL_BCHANNEL_OPERATION_SUPPORTED      0x00000040L
+#define GL_LINE_INTERCONNECT_SUPPORTED       0x00000080L
+#define GL_ECHO_CANCELLER_SUPPORTED          0x00000100L
+/*------------------------------------------------------------------*/
+/* protocol selection                                               */
+/*------------------------------------------------------------------*/
+#define B1_HDLC                 0
+#define B1_TRANSPARENT          1
+#define B1_V110_ASYNC           2
+#define B1_V110_SYNC            3
+#define B1_T30                  4
+#define B1_HDLC_INVERTED        5
+#define B1_TRANSPARENT_R        6
+#define B1_MODEM_ALL_NEGOTIATE  7
+#define B1_MODEM_ASYNC          8
+#define B1_MODEM_SYNC_HDLC      9
+#define B2_X75                  0
+#define B2_TRANSPARENT          1
+#define B2_SDLC                 2
+#define B2_LAPD                 3
+#define B2_T30                  4
+#define B2_PPP                  5
+#define B2_TRANSPARENT_NO_CRC   6
+#define B2_MODEM_EC_COMPRESSION 7
+#define B2_X75_V42BIS           8
+#define B2_V120_ASYNC           9
+#define B2_V120_ASYNC_V42BIS    10
+#define B2_V120_BIT_TRANSPARENT 11
+#define B2_LAPD_FREE_SAPI_SEL   12
+#define B3_TRANSPARENT          0
+#define B3_T90NL                1
+#define B3_ISO8208              2
+#define B3_X25_DCE              3
+#define B3_T30                  4
+#define B3_T30_WITH_EXTENSIONS  5
+#define B3_RESERVED             6
+#define B3_MODEM                7
+/*------------------------------------------------------------------*/
+/*  facility definitions                                            */
+/*------------------------------------------------------------------*/
+#define SELECTOR_HANDSET            0
+#define SELECTOR_DTMF               1
+#define SELECTOR_V42BIS             2
+#define SELECTOR_SU_SERV            3
+#define SELECTOR_POWER_MANAGEMENT   4
+#define SELECTOR_LINE_INTERCONNECT  5
+#define SELECTOR_ECHO_CANCELLER     6
+/*------------------------------------------------------------------*/
+/*  supplementary services definitions                              */
+/*------------------------------------------------------------------*/
+#define S_GET_SUPPORTED_SERVICES  0x0000
+#define S_LISTEN                  0x0001
+#define S_HOLD                    0x0002
+#define S_RETRIEVE                0x0003
+#define S_SUSPEND                 0x0004
+#define S_RESUME                  0x0005
+#define S_ECT                     0x0006
+#define S_3PTY_BEGIN              0x0007
+#define S_3PTY_END                0x0008
+#define S_CALL_DEFLECTION         0x000d
+#define S_CALL_FORWARDING_START   0x0009
+#define S_CALL_FORWARDING_STOP    0x000a
+#define S_INTERROGATE_DIVERSION   0x000b /* or interrogate parameters */
+#define S_INTERROGATE_NUMBERS     0x000c
+#define S_CCBS_REQUEST            0x000f
+#define S_CCBS_DEACTIVATE         0x0010
+#define S_CCBS_INTERROGATE        0x0011
+#define S_CCBS_CALL               0x0012
+#define S_MWI_ACTIVATE            0x0013
+#define S_MWI_DEACTIVATE          0x0014
+#define S_CONF_BEGIN           0x0017
+#define S_CONF_ADD                0x0018
+#define S_CONF_SPLIT           0x0019
+#define S_CONF_DROP               0x001a
+#define S_CONF_ISOLATE           0x001b
+#define S_CONF_REATTACH           0x001c
+#define S_CCBS_ERASECALLLINKAGEID 0x800d
+#define S_CCBS_STOP_ALERTING      0x8012
+#define S_CCBS_INFO_RETAIN        0x8013
+#define S_MWI_INDICATE            0x8014
+#define S_CONF_PARTYDISC          0x8016
+#define S_CONF_NOTIFICATION       0x8017
+/* Service Masks */
+#define MASK_HOLD_RETRIEVE        0x00000001
+#define MASK_TERMINAL_PORTABILITY 0x00000002
+#define MASK_ECT                  0x00000004
+#define MASK_3PTY                 0x00000008
+#define MASK_CALL_FORWARDING      0x00000010
+#define MASK_CALL_DEFLECTION      0x00000020
+#define MASK_MWI                  0x00000100
+#define MASK_CCNR                 0x00000200
+#define MASK_CONF                 0x00000400
+/*------------------------------------------------------------------*/
+/*  dtmf definitions                                                */
+/*------------------------------------------------------------------*/
+#define DTMF_LISTEN_START     1
+#define DTMF_LISTEN_STOP      2
+#define DTMF_DIGITS_SEND      3
+#define DTMF_SUCCESS          0
+#define DTMF_INCORRECT_DIGIT  1
+#define DTMF_UNKNOWN_REQUEST  2
+/*------------------------------------------------------------------*/
+/*  line interconnect definitions                                   */
+/*------------------------------------------------------------------*/
+#define LI_GET_SUPPORTED_SERVICES       0
+#define LI_REQ_CONNECT                  1
+#define LI_REQ_DISCONNECT               2
+#define LI_IND_CONNECT_ACTIVE           1
+#define LI_IND_DISCONNECT               2
+#define LI_FLAG_CONFERENCE_A_B          ((dword) 0x00000001L)
+#define LI_FLAG_CONFERENCE_B_A          ((dword) 0x00000002L)
+#define LI_FLAG_MONITOR_A               ((dword) 0x00000004L)
+#define LI_FLAG_MONITOR_B               ((dword) 0x00000008L)
+#define LI_FLAG_ANNOUNCEMENT_A          ((dword) 0x00000010L)
+#define LI_FLAG_ANNOUNCEMENT_B          ((dword) 0x00000020L)
+#define LI_FLAG_MIX_A                   ((dword) 0x00000040L)
+#define LI_FLAG_MIX_B                   ((dword) 0x00000080L)
+#define LI_CONFERENCING_SUPPORTED       ((dword) 0x00000001L)
+#define LI_MONITORING_SUPPORTED         ((dword) 0x00000002L)
+#define LI_ANNOUNCEMENTS_SUPPORTED      ((dword) 0x00000004L)
+#define LI_MIXING_SUPPORTED             ((dword) 0x00000008L)
+#define LI_CROSS_CONTROLLER_SUPPORTED   ((dword) 0x00000010L)
+#define LI2_GET_SUPPORTED_SERVICES      0
+#define LI2_REQ_CONNECT                 1
+#define LI2_REQ_DISCONNECT              2
+#define LI2_IND_CONNECT_ACTIVE          1
+#define LI2_IND_DISCONNECT              2
+#define LI2_FLAG_INTERCONNECT_A_B       ((dword) 0x00000001L)
+#define LI2_FLAG_INTERCONNECT_B_A       ((dword) 0x00000002L)
+#define LI2_FLAG_MONITOR_B              ((dword) 0x00000004L)
+#define LI2_FLAG_MIX_B                  ((dword) 0x00000008L)
+#define LI2_FLAG_MONITOR_X              ((dword) 0x00000010L)
+#define LI2_FLAG_MIX_X                  ((dword) 0x00000020L)
+#define LI2_FLAG_LOOP_B                 ((dword) 0x00000040L)
+#define LI2_FLAG_LOOP_PC                ((dword) 0x00000080L)
+#define LI2_FLAG_LOOP_X                 ((dword) 0x00000100L)
+#define LI2_CROSS_CONTROLLER_SUPPORTED  ((dword) 0x00000001L)
+#define LI2_ASYMMETRIC_SUPPORTED        ((dword) 0x00000002L)
+#define LI2_MONITORING_SUPPORTED        ((dword) 0x00000004L)
+#define LI2_MIXING_SUPPORTED            ((dword) 0x00000008L)
+#define LI2_REMOTE_MONITORING_SUPPORTED ((dword) 0x00000010L)
+#define LI2_REMOTE_MIXING_SUPPORTED     ((dword) 0x00000020L)
+#define LI2_B_LOOPING_SUPPORTED         ((dword) 0x00000040L)
+#define LI2_PC_LOOPING_SUPPORTED        ((dword) 0x00000080L)
+#define LI2_X_LOOPING_SUPPORTED         ((dword) 0x00000100L)
+/*------------------------------------------------------------------*/
+/* echo canceller definitions                                       */
+/*------------------------------------------------------------------*/
+#define EC_GET_SUPPORTED_SERVICES            0
+#define EC_ENABLE_OPERATION                  1
+#define EC_DISABLE_OPERATION                 2
+#define EC_ENABLE_NON_LINEAR_PROCESSING      0x0001
+#define EC_DO_NOT_REQUIRE_REVERSALS          0x0002
+#define EC_DETECT_DISABLE_TONE               0x0004
+#define EC_ENABLE_ADAPTIVE_PREDELAY          0x0008
+#define EC_NON_LINEAR_PROCESSING_SUPPORTED   0x0001
+#define EC_BYPASS_ON_ANY_2100HZ_SUPPORTED    0x0002
+#define EC_BYPASS_ON_REV_2100HZ_SUPPORTED    0x0004
+#define EC_ADAPTIVE_PREDELAY_SUPPORTED       0x0008
+#define EC_BYPASS_INDICATION                 1
+#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ   1
+#define EC_BYPASS_DUE_TO_REVERSED_2100HZ     2
+#define EC_BYPASS_RELEASED                   3
+/*------------------------------------------------------------------*/
+/* function prototypes                                              */
+/*------------------------------------------------------------------*/
+/*------------------------------------------------------------------*/
+#endif /* _INC_CAPI20 */  
diff --git a/drivers/isdn/hardware/eicon/capidtmf.c b/drivers/isdn/hardware/eicon/capidtmf.c
new file mode 100644
index 0000000..f130724
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capidtmf.c
@@ -0,0 +1,685 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+
+
+  
+  
+
+
+
+
+
+#include "capidtmf.h"
+
+/* #define TRACE_ */
+
+#define FILE_ "CAPIDTMF.C"
+
+/*---------------------------------------------------------------------------*/
+
+
+#define trace(a)
+
+
+
+/*---------------------------------------------------------------------------*/
+
+static short capidtmf_expand_table_alaw[0x0100] =
+{
+   -5504,   5504,   -344,    344, -22016,  22016,  -1376,   1376,
+   -2752,   2752,    -88,     88, -11008,  11008,   -688,    688,
+   -7552,   7552,   -472,    472, -30208,  30208,  -1888,   1888,
+   -3776,   3776,   -216,    216, -15104,  15104,   -944,    944,
+   -4480,   4480,   -280,    280, -17920,  17920,  -1120,   1120,
+   -2240,   2240,    -24,     24,  -8960,   8960,   -560,    560,
+   -6528,   6528,   -408,    408, -26112,  26112,  -1632,   1632,
+   -3264,   3264,   -152,    152, -13056,  13056,   -816,    816,
+   -6016,   6016,   -376,    376, -24064,  24064,  -1504,   1504,
+   -3008,   3008,   -120,    120, -12032,  12032,   -752,    752,
+   -8064,   8064,   -504,    504, -32256,  32256,  -2016,   2016,
+   -4032,   4032,   -248,    248, -16128,  16128,  -1008,   1008,
+   -4992,   4992,   -312,    312, -19968,  19968,  -1248,   1248,
+   -2496,   2496,    -56,     56,  -9984,   9984,   -624,    624,
+   -7040,   7040,   -440,    440, -28160,  28160,  -1760,   1760,
+   -3520,   3520,   -184,    184, -14080,  14080,   -880,    880,
+   -5248,   5248,   -328,    328, -20992,  20992,  -1312,   1312,
+   -2624,   2624,    -72,     72, -10496,  10496,   -656,    656,
+   -7296,   7296,   -456,    456, -29184,  29184,  -1824,   1824,
+   -3648,   3648,   -200,    200, -14592,  14592,   -912,    912,
+   -4224,   4224,   -264,    264, -16896,  16896,  -1056,   1056,
+   -2112,   2112,     -8,      8,  -8448,   8448,   -528,    528,
+   -6272,   6272,   -392,    392, -25088,  25088,  -1568,   1568,
+   -3136,   3136,   -136,    136, -12544,  12544,   -784,    784,
+   -5760,   5760,   -360,    360, -23040,  23040,  -1440,   1440,
+   -2880,   2880,   -104,    104, -11520,  11520,   -720,    720,
+   -7808,   7808,   -488,    488, -31232,  31232,  -1952,   1952,
+   -3904,   3904,   -232,    232, -15616,  15616,   -976,    976,
+   -4736,   4736,   -296,    296, -18944,  18944,  -1184,   1184,
+   -2368,   2368,    -40,     40,  -9472,   9472,   -592,    592,
+   -6784,   6784,   -424,    424, -27136,  27136,  -1696,   1696,
+   -3392,   3392,   -168,    168, -13568,  13568,   -848,    848
+};
+
+static short capidtmf_expand_table_ulaw[0x0100] =
+{
+  -32124,  32124,  -1884,   1884,  -7932,   7932,   -372,    372,
+  -15996,  15996,   -876,    876,  -3900,   3900,   -120,    120,
+  -23932,  23932,  -1372,   1372,  -5884,   5884,   -244,    244,
+  -11900,  11900,   -620,    620,  -2876,   2876,    -56,     56,
+  -28028,  28028,  -1628,   1628,  -6908,   6908,   -308,    308,
+  -13948,  13948,   -748,    748,  -3388,   3388,    -88,     88,
+  -19836,  19836,  -1116,   1116,  -4860,   4860,   -180,    180,
+   -9852,   9852,   -492,    492,  -2364,   2364,    -24,     24,
+  -30076,  30076,  -1756,   1756,  -7420,   7420,   -340,    340,
+  -14972,  14972,   -812,    812,  -3644,   3644,   -104,    104,
+  -21884,  21884,  -1244,   1244,  -5372,   5372,   -212,    212,
+  -10876,  10876,   -556,    556,  -2620,   2620,    -40,     40,
+  -25980,  25980,  -1500,   1500,  -6396,   6396,   -276,    276,
+  -12924,  12924,   -684,    684,  -3132,   3132,    -72,     72,
+  -17788,  17788,   -988,    988,  -4348,   4348,   -148,    148,
+   -8828,   8828,   -428,    428,  -2108,   2108,     -8,      8,
+  -31100,  31100,  -1820,   1820,  -7676,   7676,   -356,    356,
+  -15484,  15484,   -844,    844,  -3772,   3772,   -112,    112,
+  -22908,  22908,  -1308,   1308,  -5628,   5628,   -228,    228,
+  -11388,  11388,   -588,    588,  -2748,   2748,    -48,     48,
+  -27004,  27004,  -1564,   1564,  -6652,   6652,   -292,    292,
+  -13436,  13436,   -716,    716,  -3260,   3260,    -80,     80,
+  -18812,  18812,  -1052,   1052,  -4604,   4604,   -164,    164,
+   -9340,   9340,   -460,    460,  -2236,   2236,    -16,     16,
+  -29052,  29052,  -1692,   1692,  -7164,   7164,   -324,    324,
+  -14460,  14460,   -780,    780,  -3516,   3516,    -96,     96,
+  -20860,  20860,  -1180,   1180,  -5116,   5116,   -196,    196,
+  -10364,  10364,   -524,    524,  -2492,   2492,    -32,     32,
+  -24956,  24956,  -1436,   1436,  -6140,   6140,   -260,    260,
+  -12412,  12412,   -652,    652,  -3004,   3004,    -64,     64,
+  -16764,  16764,   -924,    924,  -4092,   4092,   -132,    132,
+   -8316,   8316,   -396,    396,  -1980,   1980,      0,      0
+};
+
+
+/*---------------------------------------------------------------------------*/
+
+static short capidtmf_recv_window_function[CAPIDTMF_RECV_ACCUMULATE_CYCLES] =
+{
+    -500L,   -999L,  -1499L,  -1998L,  -2496L,  -2994L,  -3491L,  -3988L,
+   -4483L,  -4978L,  -5471L,  -5963L,  -6454L,  -6943L,  -7431L,  -7917L,
+   -8401L,  -8883L,  -9363L,  -9840L, -10316L, -10789L, -11259L, -11727L,
+  -12193L, -12655L, -13115L, -13571L, -14024L, -14474L, -14921L, -15364L,
+  -15804L, -16240L, -16672L, -17100L, -17524L, -17944L, -18360L, -18772L,
+  -19180L, -19583L, -19981L, -20375L, -20764L, -21148L, -21527L, -21901L,
+  -22270L, -22634L, -22993L, -23346L, -23694L, -24037L, -24374L, -24705L,
+  -25030L, -25350L, -25664L, -25971L, -26273L, -26568L, -26858L, -27141L,
+  -27418L, -27688L, -27952L, -28210L, -28461L, -28705L, -28943L, -29174L,
+  -29398L, -29615L, -29826L, -30029L, -30226L, -30415L, -30598L, -30773L,
+  -30941L, -31102L, -31256L, -31402L, -31541L, -31673L, -31797L, -31914L,
+  -32024L, -32126L, -32221L, -32308L, -32388L, -32460L, -32524L, -32581L,
+  -32631L, -32673L, -32707L, -32734L, -32753L, -32764L, -32768L, -32764L,
+  -32753L, -32734L, -32707L, -32673L, -32631L, -32581L, -32524L, -32460L,
+  -32388L, -32308L, -32221L, -32126L, -32024L, -31914L, -31797L, -31673L,
+  -31541L, -31402L, -31256L, -31102L, -30941L, -30773L, -30598L, -30415L,
+  -30226L, -30029L, -29826L, -29615L, -29398L, -29174L, -28943L, -28705L,
+  -28461L, -28210L, -27952L, -27688L, -27418L, -27141L, -26858L, -26568L,
+  -26273L, -25971L, -25664L, -25350L, -25030L, -24705L, -24374L, -24037L,
+  -23694L, -23346L, -22993L, -22634L, -22270L, -21901L, -21527L, -21148L,
+  -20764L, -20375L, -19981L, -19583L, -19180L, -18772L, -18360L, -17944L,
+  -17524L, -17100L, -16672L, -16240L, -15804L, -15364L, -14921L, -14474L,
+  -14024L, -13571L, -13115L, -12655L, -12193L, -11727L, -11259L, -10789L,
+  -10316L,  -9840L,  -9363L,  -8883L,  -8401L,  -7917L,  -7431L,  -6943L,
+   -6454L,  -5963L,  -5471L,  -4978L,  -4483L,  -3988L,  -3491L,  -2994L,
+   -2496L,  -1998L,  -1499L,   -999L,   -500L, 
+};
+
+static byte capidtmf_leading_zeroes_table[0x100] =
+{
+  8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+#define capidtmf_byte_leading_zeroes(b)  (capidtmf_leading_zeroes_table[(BYTE)(b)])
+#define capidtmf_word_leading_zeroes(w)  (((w) & 0xff00) ? capidtmf_leading_zeroes_table[(w) >> 8] : 8 + capidtmf_leading_zeroes_table[(w)])
+#define capidtmf_dword_leading_zeroes(d)  (((d) & 0xffff0000L) ?    (((d) & 0xff000000L) ? capidtmf_leading_zeroes_table[(d) >> 24] : 8 + capidtmf_leading_zeroes_table[(d) >> 16]) :    (((d) & 0xff00) ? 16 + capidtmf_leading_zeroes_table[(d) >> 8] : 24 + capidtmf_leading_zeroes_table[(d)]))
+
+
+/*---------------------------------------------------------------------------*/
+
+
+static void capidtmf_goertzel_loop (long *buffer, long *coeffs, short *sample, long count)
+{
+  int i, j;
+  long c, d, q0, q1, q2;
+
+  for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1; i++)
+  {
+    q1 = buffer[i];
+    q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+    d = coeffs[i] >> 1;
+    c = d << 1;
+    if (c >= 0)
+    {
+      for (j = 0; j < count; j++)
+      {
+        q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15);
+        q2 = q1;
+        q1 = q0;
+      }
+    }
+    else
+    {
+      c = -c;
+      d = -d;
+      for (j = 0; j < count; j++)
+      {
+        q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15));
+        q2 = q1;
+        q1 = q0;
+      }
+    }
+    buffer[i] = q1;
+    buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2;
+  }
+  q1 = buffer[i];
+  q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+  c = (coeffs[i] >> 1) << 1;
+  if (c >= 0)
+  {
+    for (j = 0; j < count; j++)
+    {
+      q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15);
+      q2 = q1;
+      q1 = q0;
+      c -= CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT;
+    }
+  }
+  else
+  {
+    c = -c;
+    for (j = 0; j < count; j++)
+    {
+      q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15));
+      q2 = q1;
+      q1 = q0;
+      c += CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT;
+    }
+  }
+  coeffs[i] = c;
+  buffer[i] = q1;
+  buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2;
+}
+
+
+static void capidtmf_goertzel_result (long *buffer, long *coeffs)
+{
+  int i;
+  long d, e, q1, q2, lo, mid, hi;
+  dword k;
+
+  for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+  {
+    q1 = buffer[i];
+    q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+    d = coeffs[i] >> 1;
+    if (d >= 0)
+      d = ((d << 1) * (-q1 >> 16)) + (((dword)(((dword) d) * ((dword)(-q1 & 0xffff)))) >> 15);
+    else
+      d = ((-d << 1) * (-q1 >> 16)) + (((dword)(((dword) -d) * ((dword)(-q1 & 0xffff)))) >> 15);
+    e = (q2 >= 0) ? q2 : -q2;
+    if (d >= 0)
+    {
+      k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff));
+      lo = k & 0xffff;
+      mid = k >> 16;
+      k = ((dword)(d >> 16)) * ((dword)(e & 0xffff));
+      mid += k & 0xffff;
+      hi = k >> 16;
+      k = ((dword)(d & 0xffff)) * ((dword)(e >> 16));
+      mid += k & 0xffff;
+      hi += k >> 16;
+      hi += ((dword)(d >> 16)) * ((dword)(e >> 16));
+    }
+    else
+    {
+      d = -d;
+      k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff));
+      lo = -((long)(k & 0xffff));
+      mid = -((long)(k >> 16));
+      k = ((dword)(d >> 16)) * ((dword)(e & 0xffff));
+      mid -= k & 0xffff;
+      hi = -((long)(k >> 16));
+      k = ((dword)(d & 0xffff)) * ((dword)(e >> 16));
+      mid -= k & 0xffff;
+      hi -= k >> 16;
+      hi -= ((dword)(d >> 16)) * ((dword)(e >> 16));
+    }
+    if (q2 < 0)
+    {
+      lo = -lo;
+      mid = -mid;
+      hi = -hi;
+    }
+    d = (q1 >= 0) ? q1 : -q1;
+    k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff));
+    lo += k & 0xffff;
+    mid += k >> 16;
+    k = ((dword)(d >> 16)) * ((dword)(d & 0xffff));
+    mid += (k & 0xffff) << 1;
+    hi += (k >> 16) << 1;
+    hi += ((dword)(d >> 16)) * ((dword)(d >> 16));
+    d = (q2 >= 0) ? q2 : -q2;
+    k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff));
+    lo += k & 0xffff;
+    mid += k >> 16;
+    k = ((dword)(d >> 16)) * ((dword)(d & 0xffff));
+    mid += (k & 0xffff) << 1;
+    hi += (k >> 16) << 1;
+    hi += ((dword)(d >> 16)) * ((dword)(d >> 16));
+    mid += lo >> 16;
+    hi += mid >> 16;
+    buffer[i] = (lo & 0xffff) | (mid << 16);
+    buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = hi;
+  }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_697     0
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_770     1
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_852     2
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_941     3
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1209    4
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1336    5
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1477    6
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1633    7
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_635     8
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1010    9
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1140    10
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1272    11
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1405    12
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1555    13
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1715    14
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1875    15
+
+#define CAPIDTMF_RECV_GUARD_SNR_DONTCARE      0xc000
+#define CAPIDTMF_RECV_NO_DIGIT                0xff
+#define CAPIDTMF_RECV_TIME_GRANULARITY        (CAPIDTMF_RECV_ACCUMULATE_CYCLES + 1)
+
+#define CAPIDTMF_RECV_INDICATION_DIGIT        0x0001
+
+static long capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
+{
+  0xda97L * 2,  /* 697 Hz (Low group 697 Hz) */
+  0xd299L * 2,  /* 770 Hz (Low group 770 Hz) */
+  0xc8cbL * 2,  /* 852 Hz (Low group 852 Hz) */
+  0xbd36L * 2,  /* 941 Hz (Low group 941 Hz) */
+  0x9501L * 2,  /* 1209 Hz (High group 1209 Hz) */
+  0x7f89L * 2,  /* 1336 Hz (High group 1336 Hz) */
+  0x6639L * 2,  /* 1477 Hz (High group 1477 Hz) */
+  0x48c6L * 2,  /* 1633 Hz (High group 1633 Hz) */
+  0xe14cL * 2,  /* 630 Hz (Lower guard of low group 631 Hz) */
+  0xb2e0L * 2,  /* 1015 Hz (Upper guard of low group 1039 Hz) */
+  0xa1a0L * 2,  /* 1130 Hz (Lower guard of high group 1140 Hz) */
+  0x8a87L * 2,  /* 1272 Hz (Guard between 1209 Hz and 1336 Hz: 1271 Hz) */
+  0x7353L * 2,  /* 1405 Hz (2nd harmonics of 697 Hz and guard between 1336 Hz and 1477 Hz: 1405 Hz) */
+  0x583bL * 2,  /* 1552 Hz (2nd harmonics of 770 Hz and guard between 1477 Hz and 1633 Hz: 1553 Hz) */
+  0x37d8L * 2,  /* 1720 Hz (2nd harmonics of 852 Hz and upper guard of high group: 1715 Hz) */
+  0x0000L * 2   /* 100-630 Hz (fundamentals) */
+};
+
+
+static word capidtmf_recv_guard_snr_low_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
+{
+  14,                                    /* Low group peak versus 697 Hz */
+  14,                                    /* Low group peak versus 770 Hz */
+  16,                                    /* Low group peak versus 852 Hz */
+  16,                                    /* Low group peak versus 941 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1209 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1336 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1477 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1633 Hz */
+  14,                                    /* Low group peak versus 635 Hz */
+  16,                                    /* Low group peak versus 1010 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1140 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* Low group peak versus 1272 Hz */
+  DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 8,  /* Low group peak versus 1405 Hz */
+  DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4,  /* Low group peak versus 1555 Hz */
+  DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4,  /* Low group peak versus 1715 Hz */
+  12                                     /* Low group peak versus 100-630 Hz */
+};
+
+
+static word capidtmf_recv_guard_snr_high_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
+{
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 697 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 770 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 852 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 941 Hz */
+  20,                                    /* High group peak versus 1209 Hz */
+  20,                                    /* High group peak versus 1336 Hz */
+  20,                                    /* High group peak versus 1477 Hz */
+  20,                                    /* High group peak versus 1633 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 635 Hz */
+  CAPIDTMF_RECV_GUARD_SNR_DONTCARE,      /* High group peak versus 1010 Hz */
+  16,                                    /* High group peak versus 1140 Hz */
+  4,                                     /* High group peak versus 1272 Hz */
+  6,                                     /* High group peak versus 1405 Hz */
+  8,                                     /* High group peak versus 1555 Hz */
+  16,                                    /* High group peak versus 1715 Hz */
+  12                                     /* High group peak versus 100-630 Hz */
+};
+
+
+/*---------------------------------------------------------------------------*/
+
+static void capidtmf_recv_init (t_capidtmf_state   *p_state)
+{
+  p_state->recv.min_gap_duration = 1;
+  p_state->recv.min_digit_duration = 1;
+
+  p_state->recv.cycle_counter = 0;
+  p_state->recv.current_digit_on_time = 0;
+  p_state->recv.current_digit_off_time = 0;
+  p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT;
+
+  p_state->recv.digit_write_pos = 0;
+  p_state->recv.digit_read_pos = 0;
+  p_state->recv.indication_state = 0;
+  p_state->recv.indication_state_ack = 0;
+  p_state->recv.state = CAPIDTMF_RECV_STATE_IDLE;
+}
+
+
+void capidtmf_recv_enable (t_capidtmf_state   *p_state, word min_digit_duration, word min_gap_duration)
+{
+  p_state->recv.indication_state_ack &= CAPIDTMF_RECV_INDICATION_DIGIT;
+  p_state->recv.min_digit_duration = (word)(((((dword) min_digit_duration) * 8) +
+    ((dword)(CAPIDTMF_RECV_TIME_GRANULARITY / 2))) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY));
+  if (p_state->recv.min_digit_duration <= 1)
+    p_state->recv.min_digit_duration = 1;
+  else
+    (p_state->recv.min_digit_duration)--;
+  p_state->recv.min_gap_duration =
+    (word)((((dword) min_gap_duration) * 8) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY));
+  if (p_state->recv.min_gap_duration <= 1)
+    p_state->recv.min_gap_duration = 1;
+  else
+    (p_state->recv.min_gap_duration)--;
+  p_state->recv.state |= CAPIDTMF_RECV_STATE_DTMF_ACTIVE;
+}
+
+
+void capidtmf_recv_disable (t_capidtmf_state   *p_state)
+{
+  p_state->recv.state &= ~CAPIDTMF_RECV_STATE_DTMF_ACTIVE;
+  if (p_state->recv.state == CAPIDTMF_RECV_STATE_IDLE)
+    capidtmf_recv_init (p_state);
+  else
+  {
+    p_state->recv.cycle_counter = 0;
+    p_state->recv.current_digit_on_time = 0;
+    p_state->recv.current_digit_off_time = 0;
+    p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT;
+  }
+}
+
+
+word capidtmf_recv_indication (t_capidtmf_state   *p_state, byte *buffer)
+{
+  word i, j, k, flags;
+
+  flags = p_state->recv.indication_state ^ p_state->recv.indication_state_ack;
+  p_state->recv.indication_state_ack ^= flags & CAPIDTMF_RECV_INDICATION_DIGIT;
+  if (p_state->recv.digit_write_pos != p_state->recv.digit_read_pos)
+  {
+    i = 0;
+    k = p_state->recv.digit_write_pos;
+    j = p_state->recv.digit_read_pos;
+    do
+    {
+      buffer[i++] = p_state->recv.digit_buffer[j];
+      j = (j == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ? 0 : j + 1;
+    } while (j != k);
+    p_state->recv.digit_read_pos = k;
+    return (i);
+  }
+  p_state->recv.indication_state_ack ^= flags;
+  return (0);
+}
+
+
+#define CAPIDTMF_RECV_WINDOWED_SAMPLES  32
+
+void capidtmf_recv_block (t_capidtmf_state   *p_state, byte   *buffer, word length)
+{
+  byte result_digit;
+  word sample_number, cycle_counter, n, i;
+  word low_peak, high_peak;
+  dword lo, hi;
+  byte   *p;
+  short *q;
+  byte goertzel_result_buffer[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+    short windowed_sample_buffer[CAPIDTMF_RECV_WINDOWED_SAMPLES];
+
+
+  if (p_state->recv.state & CAPIDTMF_RECV_STATE_DTMF_ACTIVE)
+  {
+    cycle_counter = p_state->recv.cycle_counter;
+    sample_number = 0;
+    while (sample_number < length)
+    {
+      if (cycle_counter < CAPIDTMF_RECV_ACCUMULATE_CYCLES)
+      {
+        if (cycle_counter == 0)
+        {
+          for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+          {
+            p_state->recv.goertzel_buffer[0][i] = 0;
+            p_state->recv.goertzel_buffer[1][i] = 0;
+          }
+        }
+        n = CAPIDTMF_RECV_ACCUMULATE_CYCLES - cycle_counter;
+        if (n > length - sample_number)
+          n = length - sample_number;
+        if (n > CAPIDTMF_RECV_WINDOWED_SAMPLES)
+          n = CAPIDTMF_RECV_WINDOWED_SAMPLES;
+        p = buffer + sample_number;
+        q = capidtmf_recv_window_function + cycle_counter;
+        if (p_state->ulaw)
+        {
+          for (i = 0; i < n; i++)
+          {
+            windowed_sample_buffer[i] =
+              (short)((capidtmf_expand_table_ulaw[p[i]] * ((long)(q[i]))) >> 15);
+	  }
+        }
+        else
+        {
+          for (i = 0; i < n; i++)
+          {
+            windowed_sample_buffer[i] =
+              (short)((capidtmf_expand_table_alaw[p[i]] * ((long)(q[i]))) >> 15);
+	  }
+        }
+        capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1] = CAPIDTMF_RECV_FUNDAMENTAL_OFFSET;
+        capidtmf_goertzel_loop (p_state->recv.goertzel_buffer[0],
+          capidtmf_recv_goertzel_coef_table, windowed_sample_buffer, n);
+        cycle_counter += n;
+        sample_number += n;
+      }
+      else
+      {
+        capidtmf_goertzel_result (p_state->recv.goertzel_buffer[0],
+          capidtmf_recv_goertzel_coef_table);
+        for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+        {
+          lo = (dword)(p_state->recv.goertzel_buffer[0][i]);
+          hi = (dword)(p_state->recv.goertzel_buffer[1][i]);
+          if (hi != 0)
+          {
+            n = capidtmf_dword_leading_zeroes (hi);
+            hi = (hi << n) | (lo >> (32 - n));
+          }
+          else
+          {
+            n = capidtmf_dword_leading_zeroes (lo);
+            hi = lo << n;
+	    n += 32;
+          }
+          n = 195 - 3 * n;
+          if (hi >= 0xcb300000L)
+            n += 2;
+          else if (hi >= 0xa1450000L)
+            n++;
+	  goertzel_result_buffer[i] = (byte) n;
+        }
+        low_peak = DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT;
+        result_digit = CAPIDTMF_RECV_NO_DIGIT;
+        for (i = 0; i < CAPIDTMF_LOW_GROUP_FREQUENCIES; i++)
+        {
+          if (goertzel_result_buffer[i] > low_peak)
+	  {
+	    low_peak = goertzel_result_buffer[i];
+	    result_digit = (byte) i;
+	  }
+        }
+        high_peak = DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT;
+        n = CAPIDTMF_RECV_NO_DIGIT;
+        for (i = CAPIDTMF_LOW_GROUP_FREQUENCIES; i < CAPIDTMF_RECV_BASE_FREQUENCY_COUNT; i++)
+        {
+          if (goertzel_result_buffer[i] > high_peak)
+	  {
+	    high_peak = goertzel_result_buffer[i];
+	    n = (i - CAPIDTMF_LOW_GROUP_FREQUENCIES) << 2;
+	  }
+        }
+        result_digit |= (byte) n;
+        if (low_peak + DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT < high_peak)
+          result_digit = CAPIDTMF_RECV_NO_DIGIT;
+        if (high_peak + DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT < low_peak)
+          result_digit = CAPIDTMF_RECV_NO_DIGIT;
+        n = 0;
+        for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+        {
+          if ((((short)(low_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_low_table[i])) < 0)
+           || (((short)(high_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_high_table[i])) < 0))
+	  {
+	    n++;
+	  }
+        }
+        if (n != 2)
+          result_digit = CAPIDTMF_RECV_NO_DIGIT;
+
+        if (result_digit == CAPIDTMF_RECV_NO_DIGIT)
+        {
+          if (p_state->recv.current_digit_on_time != 0)
+          {
+            if (++(p_state->recv.current_digit_off_time) >= p_state->recv.min_gap_duration)
+            {
+              p_state->recv.current_digit_on_time = 0;
+              p_state->recv.current_digit_off_time = 0;
+            }
+          }
+          else
+          {
+            if (p_state->recv.current_digit_off_time != 0)
+              (p_state->recv.current_digit_off_time)--;
+          }
+        }
+        else
+        {
+          if ((p_state->recv.current_digit_on_time == 0)
+           && (p_state->recv.current_digit_off_time != 0))
+          {
+            (p_state->recv.current_digit_off_time)--;
+          }
+          else
+          {
+            n = p_state->recv.current_digit_off_time;
+            if ((p_state->recv.current_digit_on_time != 0)
+             && (result_digit != p_state->recv.current_digit_value))
+            {
+              p_state->recv.current_digit_on_time = 0;
+              n = 0;
+            }
+            p_state->recv.current_digit_value = result_digit;
+            p_state->recv.current_digit_off_time = 0;
+            if (p_state->recv.current_digit_on_time != 0xffff)
+            {
+              p_state->recv.current_digit_on_time += n + 1;
+              if (p_state->recv.current_digit_on_time >= p_state->recv.min_digit_duration)
+              {
+                p_state->recv.current_digit_on_time = 0xffff;
+                i = (p_state->recv.digit_write_pos == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ?
+                  0 : p_state->recv.digit_write_pos + 1;
+                if (i == p_state->recv.digit_read_pos)
+                {
+                  trace (dprintf ("%s,%d: Receive digit overrun",
+                    (char   *)(FILE_), __LINE__));
+                }
+                else
+                {
+                  p_state->recv.digit_buffer[p_state->recv.digit_write_pos] = result_digit;
+                  p_state->recv.digit_write_pos = i;
+                  p_state->recv.indication_state =
+                    (p_state->recv.indication_state & ~CAPIDTMF_RECV_INDICATION_DIGIT) |
+                    (~p_state->recv.indication_state_ack & CAPIDTMF_RECV_INDICATION_DIGIT);
+                }
+              }
+            }
+          }
+        }
+        cycle_counter = 0;
+        sample_number++;
+      }
+    }
+    p_state->recv.cycle_counter = cycle_counter;
+  }
+}
+
+
+void capidtmf_init (t_capidtmf_state   *p_state, byte ulaw)
+{
+  p_state->ulaw = ulaw;
+  capidtmf_recv_init (p_state);
+}
+
+
+/*---------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/capidtmf.h b/drivers/isdn/hardware/eicon/capidtmf.h
new file mode 100644
index 0000000..242048f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capidtmf.h
@@ -0,0 +1,79 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 CAPIDTMF_H_  
+#define CAPIDTMF_H_
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+#define CAPIDTMF_TONE_GROUP_COUNT            2
+#define CAPIDTMF_LOW_GROUP_FREQUENCIES       4
+#define CAPIDTMF_HIGH_GROUP_FREQUENCIES      4
+#define DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT	50	/* -52 dBm */
+#define DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT	50	/* -52 dBm */
+#define DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT	10	/* dB */
+#define DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT	10	/* dB */
+#define DSPDTMF_RX_HARMONICS_SEL_DEFAULT	12	/* dB */
+#define CAPIDTMF_RECV_BASE_FREQUENCY_COUNT   (CAPIDTMF_LOW_GROUP_FREQUENCIES + CAPIDTMF_HIGH_GROUP_FREQUENCIES)
+#define CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT  8
+#define CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT  (CAPIDTMF_RECV_BASE_FREQUENCY_COUNT + CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT)
+#define CAPIDTMF_RECV_POSITIVE_COEFF_COUNT   16
+#define CAPIDTMF_RECV_NEGATIVE_COEFF_COUNT   (CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - CAPIDTMF_RECV_POSITIVE_COEFF_COUNT)
+#define CAPIDTMF_RECV_ACCUMULATE_CYCLES      205
+#define CAPIDTMF_RECV_FUNDAMENTAL_OFFSET     (0xff35L * 2)
+#define CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT  (0x0028L * 2)
+#define CAPIDTMF_RECV_DIGIT_BUFFER_SIZE      32
+#define CAPIDTMF_RECV_STATE_IDLE             0x00
+#define CAPIDTMF_RECV_STATE_DTMF_ACTIVE      0x01
+typedef struct tag_capidtmf_recv_state
+{
+  byte digit_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE];
+  word digit_write_pos;
+  word digit_read_pos;
+  word indication_state;
+  word indication_state_ack;
+  long goertzel_buffer[2][CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+  word min_gap_duration;
+  word min_digit_duration;
+  word cycle_counter;
+  word current_digit_on_time;
+  word current_digit_off_time;
+  byte current_digit_value;
+  byte state;
+} t_capidtmf_recv_state;
+typedef struct tag_capidtmf_state
+{
+  byte ulaw;
+  t_capidtmf_recv_state recv;
+} t_capidtmf_state;
+word capidtmf_recv_indication (t_capidtmf_state   *p_state, byte *buffer);
+void capidtmf_recv_block (t_capidtmf_state   *p_state, byte   *buffer, word length);
+void capidtmf_init (t_capidtmf_state   *p_state, byte ulaw);
+void capidtmf_recv_enable (t_capidtmf_state   *p_state, word min_digit_duration, word min_gap_duration);
+void capidtmf_recv_disable (t_capidtmf_state   *p_state);
+#define capidtmf_indication(p_state,buffer)  (((p_state)->recv.indication_state != (p_state)->recv.indication_state_ack) ?    capidtmf_recv_indication (p_state, buffer) : 0)
+#define capidtmf_recv_process_block(p_state,buffer,length)  { if ((p_state)->recv.state != CAPIDTMF_RECV_STATE_IDLE) capidtmf_recv_block (p_state, buffer, length); }
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+#endif  
diff --git a/drivers/isdn/hardware/eicon/capifunc.c b/drivers/isdn/hardware/eicon/capifunc.c
new file mode 100644
index 0000000..0afd763
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capifunc.c
@@ -0,0 +1,1219 @@
+/* $Id: capifunc.c,v 1.61.4.7 2005/02/11 19:40:25 armin Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface common functions
+ * 
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de) 
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "platform.h"
+#include "os_capi.h"
+#include "di_defs.h"
+#include "capi20.h"
+#include "divacapi.h"
+#include "divasync.h"
+#include "capifunc.h"
+
+#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+DIVA_CAPI_ADAPTER *adapter = (DIVA_CAPI_ADAPTER *) NULL;
+APPL *application = (APPL *) NULL;
+byte max_appl = MAX_APPL;
+byte max_adapter = 0;
+static CAPI_MSG *mapped_msg = (CAPI_MSG *) NULL;
+
+byte UnMapController(byte);
+char DRIVERRELEASE_CAPI[32];
+
+extern void AutomaticLaw(DIVA_CAPI_ADAPTER *);
+extern void callback(ENTITY *);
+extern word api_remove_start(void);
+extern word CapiRelease(word);
+extern word CapiRegister(word);
+extern word api_put(APPL *, CAPI_MSG *);
+
+static diva_os_spin_lock_t api_lock;
+
+static LIST_HEAD(cards);
+
+static dword notify_handle;
+static void DIRequest(ENTITY * e);
+static DESCRIPTOR MAdapter;
+static DESCRIPTOR DAdapter;
+static byte ControllerMap[MAX_DESCRIPTORS + 1];
+
+
+static void diva_register_appl(struct capi_ctr *, __u16,
+			       capi_register_params *);
+static void diva_release_appl(struct capi_ctr *, __u16);
+static char *diva_procinfo(struct capi_ctr *);
+static u16 diva_send_message(struct capi_ctr *,
+			     diva_os_message_buffer_s *);
+extern void diva_os_set_controller_struct(struct capi_ctr *);
+
+extern void DIVA_DIDD_Read(DESCRIPTOR *, int);
+
+/*
+ * debug
+ */
+static void no_printf(unsigned char *, ...);
+#include "debuglib.c"
+static void xlog(char *x, ...)
+{
+#ifndef DIVA_NO_DEBUGLIB
+	va_list ap;
+	if (myDriverDebugHandle.dbgMask & DL_XLOG) {
+		va_start(ap, x);
+		if (myDriverDebugHandle.dbg_irq) {
+			myDriverDebugHandle.dbg_irq(myDriverDebugHandle.id,
+						    DLI_XLOG, x, ap);
+		} else if (myDriverDebugHandle.dbg_old) {
+			myDriverDebugHandle.dbg_old(myDriverDebugHandle.id,
+						    x, ap);
+		}
+		va_end(ap);
+	}
+#endif
+}
+
+/*
+ * info for proc
+ */
+static char *diva_procinfo(struct capi_ctr *ctrl)
+{
+	return (ctrl->serial);
+}
+
+/*
+ * stop debugging
+ */
+static void stop_dbg(void)
+{
+	DbgDeregister();
+	memset(&MAdapter, 0, sizeof(MAdapter));
+	dprintf = no_printf;
+}
+
+/*
+ * dummy debug function
+ */
+static void no_printf(unsigned char *x, ...)
+{
+}
+
+/*
+ * Controller mapping
+ */
+byte MapController(byte Controller)
+{
+	byte i;
+	byte MappedController = 0;
+	byte ctrl = Controller & 0x7f;	/* mask external controller bit off */
+
+	for (i = 1; i < max_adapter + 1; i++) {
+		if (ctrl == ControllerMap[i]) {
+			MappedController = (byte) i;
+			break;
+		}
+	}
+	if (i > max_adapter) {
+		ControllerMap[0] = ctrl;
+		MappedController = 0;
+	}
+	return (MappedController | (Controller & 0x80));	/* put back external controller bit */
+}
+
+/*
+ * Controller unmapping
+ */
+byte UnMapController(byte MappedController)
+{
+	byte Controller;
+	byte ctrl = MappedController & 0x7f;	/* mask external controller bit off */
+
+	if (ctrl <= max_adapter) {
+		Controller = ControllerMap[ctrl];
+	} else {
+		Controller = 0;
+	}
+
+	return (Controller | (MappedController & 0x80));	/* put back external controller bit */
+}
+
+/*
+ * find a new free id
+ */
+static int find_free_id(void)
+{
+	int num = 0;
+	DIVA_CAPI_ADAPTER *a;
+
+	while (num < MAX_DESCRIPTORS) {
+		a = &adapter[num];
+		if (!a->Id)
+			break;
+		num++;
+	}
+	return(num + 1);
+}
+
+/*
+ * find a card structure by controller number
+ */
+static diva_card *find_card_by_ctrl(word controller)
+{
+	struct list_head *tmp;
+	diva_card *card;
+
+	list_for_each(tmp, &cards) {
+		card = list_entry(tmp, diva_card, list);
+		if (ControllerMap[card->Id] == controller) {
+			if (card->remove_in_progress)
+				card = NULL;
+			return(card);
+		}
+	}
+	return (diva_card *) 0;
+}
+
+/*
+ * Buffer RX/TX 
+ */
+void *TransmitBufferSet(APPL * appl, dword ref)
+{
+	appl->xbuffer_used[ref] = TRUE;
+	DBG_PRV1(("%d:xbuf_used(%d)", appl->Id, ref + 1))
+	    return (void *) ref;
+}
+
+void *TransmitBufferGet(APPL * appl, void *p)
+{
+	if (appl->xbuffer_internal[(dword) p])
+		return appl->xbuffer_internal[(dword) p];
+
+	return appl->xbuffer_ptr[(dword) p];
+}
+
+void TransmitBufferFree(APPL * appl, void *p)
+{
+	appl->xbuffer_used[(dword) p] = FALSE;
+	DBG_PRV1(("%d:xbuf_free(%d)", appl->Id, ((dword) p) + 1))
+}
+
+void *ReceiveBufferGet(APPL * appl, int Num)
+{
+	return &appl->ReceiveBuffer[Num * appl->MaxDataLength];
+}
+
+/*
+ * api_remove_start/complete for cleanup
+ */
+void api_remove_complete(void)
+{
+	DBG_PRV1(("api_remove_complete"))
+}
+
+/*
+ * main function called by message.c
+ */
+void sendf(APPL * appl, word command, dword Id, word Number, byte * format, ...)
+{
+	word i, j;
+	word length = 12, dlength = 0;
+	byte *write;
+	CAPI_MSG msg;
+	byte *string = NULL;
+	va_list ap;
+	diva_os_message_buffer_s *dmb;
+	diva_card *card = NULL;
+	dword tmp;
+
+	if (!appl)
+		return;
+
+	DBG_PRV1(("sendf(a=%d,cmd=%x,format=%s)",
+		  appl->Id, command, (byte *) format))
+
+	PUT_WORD(&msg.header.appl_id, appl->Id);
+	PUT_WORD(&msg.header.command, command);
+	if ((byte) (command >> 8) == 0x82)
+		Number = appl->Number++;
+	PUT_WORD(&msg.header.number, Number);
+
+	PUT_DWORD(&msg.header.controller, Id);
+	write = (byte *) & msg;
+	write += 12;
+
+	va_start(ap, format);
+	for (i = 0; format[i]; i++) {
+		switch (format[i]) {
+		case 'b':
+			tmp = va_arg(ap, dword);
+			*(byte *) write = (byte) (tmp & 0xff);
+			write += 1;
+			length += 1;
+			break;
+		case 'w':
+			tmp = va_arg(ap, dword);
+			PUT_WORD(write, (tmp & 0xffff));
+			write += 2;
+			length += 2;
+			break;
+		case 'd':
+			tmp = va_arg(ap, dword);
+			PUT_DWORD(write, tmp);
+			write += 4;
+			length += 4;
+			break;
+		case 's':
+		case 'S':
+			string = va_arg(ap, byte *);
+			length += string[0] + 1;
+			for (j = 0; j <= string[0]; j++)
+				*write++ = string[j];
+			break;
+		}
+	}
+	va_end(ap);
+
+	PUT_WORD(&msg.header.length, length);
+	msg.header.controller = UnMapController(msg.header.controller);
+
+	if (command == _DATA_B3_I)
+		dlength = GET_WORD(
+			      ((byte *) & msg.info.data_b3_ind.Data_Length));
+
+	if (!(dmb = diva_os_alloc_message_buffer(length + dlength,
+					  (void **) &write))) {
+		DBG_ERR(("sendf: alloc_message_buffer failed, incoming msg dropped."))
+		return;
+	}
+
+	/* copy msg header to sk_buff */
+	memcpy(write, (byte *) & msg, length);
+
+	/* if DATA_B3_IND, copy data too */
+	if (command == _DATA_B3_I) {
+		dword data = GET_DWORD(&msg.info.data_b3_ind.Data);
+		memcpy(write + length, (void *) data, dlength);
+	}
+
+#ifndef DIVA_NO_DEBUGLIB
+	if (myDriverDebugHandle.dbgMask & DL_XLOG) {
+		switch (command) {
+		default:
+			xlog("\x00\x02", &msg, 0x81, length);
+			break;
+		case _DATA_B3_R | CONFIRM:
+			if (myDriverDebugHandle.dbgMask & DL_BLK)
+				xlog("\x00\x02", &msg, 0x81, length);
+			break;
+		case _DATA_B3_I:
+			if (myDriverDebugHandle.dbgMask & DL_BLK) {
+				xlog("\x00\x02", &msg, 0x81, length);
+				for (i = 0; i < dlength; i += 256) {
+				  DBG_BLK((((char *) GET_DWORD(&msg.info.data_b3_ind.Data)) + i,
+				  	((dlength - i) < 256) ? (dlength - i) : 256))
+				  if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
+					  break; /* not more if not explicitely requested */
+				}
+			}
+			break;
+		}
+	}
+#endif
+
+	/* find the card structure for this controller */
+	if (!(card = find_card_by_ctrl(write[8] & 0x7f))) {
+		DBG_ERR(("sendf - controller %d not found, incoming msg dropped",
+			 write[8] & 0x7f))
+		diva_os_free_message_buffer(dmb);
+		return;
+	}
+	/* send capi msg to capi layer */
+	capi_ctr_handle_message(&card->capi_ctrl, appl->Id, dmb);
+}
+
+/*
+ * cleanup adapter
+ */
+static void clean_adapter(int id, struct list_head *free_mem_q)
+{
+	DIVA_CAPI_ADAPTER *a;
+	int i, k;
+
+	a = &adapter[id];
+	k = li_total_channels - a->li_channels;
+	if (k == 0) {
+		if (li_config_table) {
+			list_add((struct list_head *)li_config_table, free_mem_q);
+			li_config_table = NULL;
+		}
+	} else {
+		if (a->li_base < k) {
+			memmove(&li_config_table[a->li_base],
+				&li_config_table[a->li_base + a->li_channels],
+				(k - a->li_base) * sizeof(LI_CONFIG));
+			for (i = 0; i < k; i++) {
+				memmove(&li_config_table[i].flag_table[a->li_base],
+					&li_config_table[i].flag_table[a->li_base + a->li_channels],
+					k - a->li_base);
+				memmove(&li_config_table[i].
+					coef_table[a->li_base],
+					&li_config_table[i].coef_table[a->li_base + a->li_channels],
+					k - a->li_base);
+			}
+		}
+	}
+	li_total_channels = k;
+	for (i = id; i < max_adapter; i++) {
+		if (adapter[i].request)
+			adapter[i].li_base -= a->li_channels;
+	}
+	if (a->plci)
+		list_add((struct list_head *)a->plci, free_mem_q);
+
+	memset(a, 0x00, sizeof(DIVA_CAPI_ADAPTER));
+	while ((max_adapter != 0) && !adapter[max_adapter - 1].request)
+		max_adapter--;
+}
+
+/*
+ * remove a card, but ensures consistent state of LI tables
+ * in the time adapter is removed
+ */
+static void divacapi_remove_card(DESCRIPTOR * d)
+{
+	diva_card *card = NULL;
+	diva_os_spin_lock_magic_t old_irql;
+	LIST_HEAD(free_mem_q);
+	struct list_head *link;
+	struct list_head *tmp;
+
+	/*
+	 * Set "remove in progress flag".
+	 * Ensures that there is no call from sendf to CAPI in
+	 * the time CAPI controller is about to be removed.
+	 */
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
+	list_for_each(tmp, &cards) {
+		card = list_entry(tmp, diva_card, list);
+		if (card->d.request == d->request) {
+			card->remove_in_progress = 1;
+			list_del(tmp);
+			break;
+		}
+	}
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
+
+	if (card) {
+		/*
+		 * Detach CAPI. Sendf cannot call to CAPI any more.
+		 * After detach no call to send_message() is done too.
+		 */
+		detach_capi_ctr(&card->capi_ctrl);
+
+		/*
+		 * Now get API lock (to ensure stable state of LI tables)
+		 * and update the adapter map/LI table.
+		 */
+		diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
+
+		clean_adapter(card->Id - 1, &free_mem_q);
+		DBG_TRC(("DelAdapterMap (%d) -> (%d)",
+				ControllerMap[card->Id], card->Id))
+				ControllerMap[card->Id] = 0;
+		DBG_TRC(("adapter remove, max_adapter=%d",
+				max_adapter));
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
+		
+		/* After releasing the lock, we can free the memory */
+		diva_os_free (0, card);
+	}
+
+	/* free queued memory areas */
+	list_for_each_safe(link, tmp, &free_mem_q) {
+		list_del(link);
+		diva_os_free(0, link);
+	}
+}
+
+/*
+ * remove cards
+ */
+static void divacapi_remove_cards(void)
+{
+	DESCRIPTOR d;
+	struct list_head *tmp;
+	diva_card *card;
+	diva_os_spin_lock_magic_t old_irql;
+
+rescan:
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "remove cards");
+	list_for_each(tmp, &cards) {
+		card = list_entry(tmp, diva_card, list);
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
+		d.request = card->d.request;
+		divacapi_remove_card(&d);
+		goto rescan;
+	}
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
+}
+
+/*
+ * sync_callback
+ */
+static void sync_callback(ENTITY * e)
+{
+	diva_os_spin_lock_magic_t old_irql;
+
+	DBG_TRC(("cb:Id=%x,Rc=%x,Ind=%x", e->Id, e->Rc, e->Ind))
+
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "sync_callback");
+	callback(e);
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "sync_callback");
+}
+
+/*
+ * add a new card
+ */
+static int diva_add_card(DESCRIPTOR * d)
+{
+	int k = 0, i = 0;
+	diva_os_spin_lock_magic_t old_irql;
+	diva_card *card = NULL;
+	struct capi_ctr *ctrl = NULL;
+	DIVA_CAPI_ADAPTER *a = NULL;
+	IDI_SYNC_REQ sync_req;
+	char serial[16];
+	void* mem_to_free;
+	LI_CONFIG *new_li_config_table;
+	int j;
+
+	if (!(card = (diva_card *) diva_os_malloc(0, sizeof(diva_card)))) {
+		DBG_ERR(("diva_add_card: failed to allocate card struct."))
+		    return (0);
+	}
+	memset((char *) card, 0x00, sizeof(diva_card));
+	memcpy(&card->d, d, sizeof(DESCRIPTOR));
+	sync_req.GetName.Req = 0;
+	sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME;
+	card->d.request((ENTITY *) & sync_req);
+	strlcpy(card->name, sync_req.GetName.name, sizeof(card->name));
+	ctrl = &card->capi_ctrl;
+	strcpy(ctrl->name, card->name);
+	ctrl->register_appl = diva_register_appl;
+	ctrl->release_appl = diva_release_appl;
+	ctrl->send_message = diva_send_message;
+	ctrl->procinfo = diva_procinfo;
+	ctrl->driverdata = card;
+	diva_os_set_controller_struct(ctrl);
+
+	if (attach_capi_ctr(ctrl)) {
+		DBG_ERR(("diva_add_card: failed to attach controller."))
+		    diva_os_free(0, card);
+		return (0);
+	}
+	
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "find id");
+	card->Id = find_free_id();
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "find id");
+	
+	strlcpy(ctrl->manu, M_COMPANY, sizeof(ctrl->manu));
+	ctrl->version.majorversion = 2;
+	ctrl->version.minorversion = 0;
+	ctrl->version.majormanuversion = DRRELMAJOR;
+	ctrl->version.minormanuversion = DRRELMINOR;
+	sync_req.GetSerial.Req = 0;
+	sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
+	sync_req.GetSerial.serial = 0;
+	card->d.request((ENTITY *) & sync_req);
+	if ((i = ((sync_req.GetSerial.serial & 0xff000000) >> 24))) {
+		sprintf(serial, "%ld-%d",
+			sync_req.GetSerial.serial & 0x00ffffff, i + 1);
+	} else {
+		sprintf(serial, "%ld", sync_req.GetSerial.serial);
+	}
+	serial[CAPI_SERIAL_LEN - 1] = 0;
+	strlcpy(ctrl->serial, serial, sizeof(ctrl->serial));
+
+	a = &adapter[card->Id - 1];
+	card->adapter = a;
+	a->os_card = card;
+	ControllerMap[card->Id] = (byte) (ctrl->cnr);
+
+	DBG_TRC(("AddAdapterMap (%d) -> (%d)", ctrl->cnr, card->Id))
+
+	    sync_req.xdi_capi_prms.Req = 0;
+	sync_req.xdi_capi_prms.Rc = IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS;
+	sync_req.xdi_capi_prms.info.structure_length =
+	    sizeof(diva_xdi_get_capi_parameters_t);
+	card->d.request((ENTITY *) & sync_req);
+	a->flag_dynamic_l1_down =
+	    sync_req.xdi_capi_prms.info.flag_dynamic_l1_down;
+	a->group_optimization_enabled =
+	    sync_req.xdi_capi_prms.info.group_optimization_enabled;
+	a->request = DIRequest;	/* card->d.request; */
+	a->max_plci = card->d.channels + 30;
+	a->max_listen = (card->d.channels > 2) ? 8 : 2;
+	if (!
+	    (a->plci =
+	     (PLCI *) diva_os_malloc(0, sizeof(PLCI) * a->max_plci))) {
+		DBG_ERR(("diva_add_card: failed alloc plci struct."))
+		    memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
+		return (0);
+	}
+	memset(a->plci, 0, sizeof(PLCI) * a->max_plci);
+
+	for (k = 0; k < a->max_plci; k++) {
+		a->Id = (byte) card->Id;
+		a->plci[k].Sig.callback = sync_callback;
+		a->plci[k].Sig.XNum = 1;
+		a->plci[k].Sig.X = a->plci[k].XData;
+		a->plci[k].Sig.user[0] = (word) (card->Id - 1);
+		a->plci[k].Sig.user[1] = (word) k;
+		a->plci[k].NL.callback = sync_callback;
+		a->plci[k].NL.XNum = 1;
+		a->plci[k].NL.X = a->plci[k].XData;
+		a->plci[k].NL.user[0] = (word) ((card->Id - 1) | 0x8000);
+		a->plci[k].NL.user[1] = (word) k;
+		a->plci[k].adapter = a;
+	}
+
+	a->profile.Number = card->Id;
+	a->profile.Channels = card->d.channels;
+	if (card->d.features & DI_FAX3) {
+		a->profile.Global_Options = 0x71;
+		if (card->d.features & DI_CODEC)
+			a->profile.Global_Options |= 0x6;
+#if IMPLEMENT_DTMF
+		a->profile.Global_Options |= 0x8;
+#endif				/* IMPLEMENT_DTMF */
+		a->profile.Global_Options |= 0x80; /* Line Interconnect */
+#if IMPLEMENT_ECHO_CANCELLER
+		a->profile.Global_Options |= 0x100;
+#endif				/* IMPLEMENT_ECHO_CANCELLER */
+		a->profile.B1_Protocols = 0xdf;
+		a->profile.B2_Protocols = 0x1fdb;
+		a->profile.B3_Protocols = 0xb7;
+		a->manufacturer_features = MANUFACTURER_FEATURE_HARDDTMF;
+	} else {
+		a->profile.Global_Options = 0x71;
+		if (card->d.features & DI_CODEC)
+			a->profile.Global_Options |= 0x2;
+		a->profile.B1_Protocols = 0x43;
+		a->profile.B2_Protocols = 0x1f0f;
+		a->profile.B3_Protocols = 0x07;
+		a->manufacturer_features = 0;
+	}
+
+	a->li_pri = (a->profile.Channels > 2);
+	a->li_channels = a->li_pri ? MIXER_CHANNELS_PRI : MIXER_CHANNELS_BRI;
+	a->li_base = 0;
+	for (i = 0; &adapter[i] != a; i++) {
+		if (adapter[i].request)
+			a->li_base = adapter[i].li_base + adapter[i].li_channels;
+	}
+	k = li_total_channels + a->li_channels;
+	new_li_config_table =
+		(LI_CONFIG *) diva_os_malloc(0, ((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * k) * ((k + 3) & ~3));
+	if (new_li_config_table == NULL) {
+		DBG_ERR(("diva_add_card: failed alloc li_config table."))
+		memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
+		return (0);
+	}
+
+	/* Prevent access to line interconnect table in process update */
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "add card");
+	
+	j = 0;
+	for (i = 0; i < k; i++) {
+		if ((i >= a->li_base) && (i < a->li_base + a->li_channels))
+			memset(&new_li_config_table[i], 0, sizeof(LI_CONFIG));
+		else
+			memcpy(&new_li_config_table[i], &li_config_table[j], sizeof(LI_CONFIG));
+		new_li_config_table[i].flag_table =
+			((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i) * ((k + 3) & ~3));
+		new_li_config_table[i].coef_table =
+			((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i + 1) * ((k + 3) & ~3));
+		if ((i >= a->li_base) && (i < a->li_base + a->li_channels)) {
+			new_li_config_table[i].adapter = a;
+			memset(&new_li_config_table[i].flag_table[0], 0, k);
+			memset(&new_li_config_table[i].coef_table[0], 0, k);
+		} else {
+			if (a->li_base != 0) {
+				memcpy(&new_li_config_table[i].flag_table[0],
+				       &li_config_table[j].flag_table[0],
+				       a->li_base);
+				memcpy(&new_li_config_table[i].coef_table[0],
+				       &li_config_table[j].coef_table[0],
+				       a->li_base);
+			}
+			memset(&new_li_config_table[i].flag_table[a->li_base], 0, a->li_channels);
+			memset(&new_li_config_table[i].coef_table[a->li_base], 0, a->li_channels);
+			if (a->li_base + a->li_channels < k) {
+				memcpy(&new_li_config_table[i].flag_table[a->li_base +
+				       a->li_channels],
+				       &li_config_table[j].flag_table[a->li_base],
+				       k - (a->li_base + a->li_channels));
+				memcpy(&new_li_config_table[i].coef_table[a->li_base +
+				       a->li_channels],
+				       &li_config_table[j].coef_table[a->li_base],
+				       k - (a->li_base + a->li_channels));
+			}
+			j++;
+		}
+	}
+	li_total_channels = k;
+
+	mem_to_free = li_config_table;
+
+	li_config_table = new_li_config_table;
+	for (i = card->Id; i < max_adapter; i++) {
+		if (adapter[i].request)
+			adapter[i].li_base += a->li_channels;
+	}
+
+	if (a == &adapter[max_adapter])
+		max_adapter++;
+
+	list_add(&(card->list), &cards);
+	AutomaticLaw(a);
+
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "add card");
+
+	if (mem_to_free) {
+		diva_os_free (0, mem_to_free);
+	}
+
+	i = 0;
+	while (i++ < 30) {
+		if (a->automatic_law > 3)
+			break;
+		diva_os_sleep(10);
+	}
+
+	/* profile information */
+	PUT_WORD(&ctrl->profile.nbchannel, card->d.channels);
+	ctrl->profile.goptions = a->profile.Global_Options;
+	ctrl->profile.support1 = a->profile.B1_Protocols;
+	ctrl->profile.support2 = a->profile.B2_Protocols;
+	ctrl->profile.support3 = a->profile.B3_Protocols;
+	/* manufacturer profile information */
+	ctrl->profile.manu[0] = a->man_profile.private_options;
+	ctrl->profile.manu[1] = a->man_profile.rtp_primary_payloads;
+	ctrl->profile.manu[2] = a->man_profile.rtp_additional_payloads;
+	ctrl->profile.manu[3] = 0;
+	ctrl->profile.manu[4] = 0;
+
+	capi_ctr_ready(ctrl);
+
+	DBG_TRC(("adapter added, max_adapter=%d", max_adapter));
+	return (1);
+}
+
+/*
+ *  register appl
+ */
+static void diva_register_appl(struct capi_ctr *ctrl, __u16 appl,
+			       capi_register_params * rp)
+{
+	APPL *this;
+	word bnum, xnum;
+	int i = 0;
+	unsigned char *p;
+	void *DataNCCI, *DataFlags, *ReceiveBuffer, *xbuffer_used;
+	void **xbuffer_ptr, **xbuffer_internal;
+	diva_os_spin_lock_magic_t old_irql;
+	unsigned int mem_len;
+	int nconn = rp->level3cnt;
+
+
+	if (diva_os_in_irq()) {
+		DBG_ERR(("CAPI_REGISTER - in irq context !"))
+		return;
+	}
+
+	DBG_TRC(("application register Id=%d", appl))
+
+	if (appl > MAX_APPL) {
+		DBG_ERR(("CAPI_REGISTER - appl.Id exceeds MAX_APPL"))
+		return;
+	}
+
+	if (nconn <= 0)
+		nconn = ctrl->profile.nbchannel * -nconn;
+
+        if (nconn == 0)
+		nconn = ctrl->profile.nbchannel;
+
+	DBG_LOG(("CAPI_REGISTER - Id = %d", appl))
+	DBG_LOG(("  MaxLogicalConnections = %d(%d)", nconn, rp->level3cnt))
+	DBG_LOG(("  MaxBDataBuffers       = %d", rp->datablkcnt))
+	DBG_LOG(("  MaxBDataLength        = %d", rp->datablklen))
+
+	if (nconn < 1 ||
+	    nconn > 255 ||
+	    rp->datablklen < 80 ||
+	    rp->datablklen > 2150 || rp->datablkcnt > 255) {
+		DBG_ERR(("CAPI_REGISTER - invalid parameters"))
+		return;
+	}
+
+	if (application[appl - 1].Id == appl) {
+		DBG_LOG(("CAPI_REGISTER - appl already registered"))
+		return;	/* appl already registered */
+	}
+
+	/* alloc memory */
+
+	bnum = nconn * rp->datablkcnt;
+	xnum = nconn * MAX_DATA_B3;
+
+	mem_len  = bnum * sizeof(word);		/* DataNCCI */
+	mem_len += bnum * sizeof(word);		/* DataFlags */
+	mem_len += bnum * rp->datablklen;	/* ReceiveBuffer */
+	mem_len += xnum;			/* xbuffer_used */
+	mem_len += xnum * sizeof(void *);	/* xbuffer_ptr */
+	mem_len += xnum * sizeof(void *);	/* xbuffer_internal */
+	mem_len += xnum * rp->datablklen;	/* xbuffer_ptr[xnum] */
+
+	DBG_LOG(("  Allocated Memory      = %d", mem_len))
+	if (!(p = diva_os_malloc(0, mem_len))) {
+		DBG_ERR(("CAPI_REGISTER - memory allocation failed"))
+		return;
+	}
+	memset(p, 0, mem_len);
+
+	DataNCCI = (void *)p;
+	p += bnum * sizeof(word);
+	DataFlags = (void *)p;
+	p += bnum * sizeof(word);
+	ReceiveBuffer = (void *)p;
+	p += bnum * rp->datablklen;
+	xbuffer_used = (void *)p;
+	p += xnum;
+	xbuffer_ptr = (void **)p;
+	p += xnum * sizeof(void *);
+	xbuffer_internal = (void **)p;
+	p += xnum * sizeof(void *);
+	for (i = 0; i < xnum; i++) {
+		xbuffer_ptr[i] = (void *)p;
+		p += rp->datablklen;
+	}
+
+	/* initialize application data */
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "register_appl");
+
+	this = &application[appl - 1];
+	memset(this, 0, sizeof(APPL));
+
+	this->Id = appl;
+
+	for (i = 0; i < max_adapter; i++) {
+		adapter[i].CIP_Mask[appl - 1] = 0;
+	}
+
+	this->queue_size = 1000;
+
+	this->MaxNCCI = (byte) nconn;
+	this->MaxNCCIData = (byte) rp->datablkcnt;
+	this->MaxBuffer = bnum;
+	this->MaxDataLength = rp->datablklen;
+
+	this->DataNCCI = DataNCCI;
+	this->DataFlags = DataFlags;
+	this->ReceiveBuffer = ReceiveBuffer;
+	this->xbuffer_used = xbuffer_used;
+	this->xbuffer_ptr = xbuffer_ptr;
+	this->xbuffer_internal = xbuffer_internal;
+	for (i = 0; i < xnum; i++) {
+		this->xbuffer_ptr[i] = xbuffer_ptr[i];
+	}
+
+	CapiRegister(this->Id);
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "register_appl");
+
+}
+
+/*
+ *  release appl
+ */
+static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	APPL *this = &application[appl - 1];
+	void *mem_to_free = NULL;
+
+	DBG_TRC(("application %d(%d) cleanup", this->Id, appl))
+
+	if (diva_os_in_irq()) {
+		DBG_ERR(("CAPI_RELEASE - in irq context !"))
+		return;
+	}
+
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "release_appl");
+	if (this->Id) {
+		CapiRelease(this->Id);
+		mem_to_free = this->DataNCCI;
+		this->DataNCCI = NULL;
+		this->Id = 0;
+	}
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "release_appl");
+
+	if (mem_to_free)
+		diva_os_free(0, mem_to_free);
+
+}
+
+/*
+ *  send message
+ */
+static u16 diva_send_message(struct capi_ctr *ctrl,
+			     diva_os_message_buffer_s * dmb)
+{
+	int i = 0;
+	word ret = 0;
+	diva_os_spin_lock_magic_t old_irql;
+	CAPI_MSG *msg = (CAPI_MSG *) DIVA_MESSAGE_BUFFER_DATA(dmb);
+	APPL *this = &application[GET_WORD(&msg->header.appl_id) - 1];
+	diva_card *card = ctrl->driverdata;
+	__u32 length = DIVA_MESSAGE_BUFFER_LEN(dmb);
+	word clength = GET_WORD(&msg->header.length);
+	word command = GET_WORD(&msg->header.command);
+	u16 retval = CAPI_NOERROR;
+
+	if (diva_os_in_irq()) {
+		DBG_ERR(("CAPI_SEND_MSG - in irq context !"))
+		return CAPI_REGOSRESOURCEERR;
+	}
+	DBG_PRV1(("Write - appl = %d, cmd = 0x%x", this->Id, command))
+
+	if (card->remove_in_progress) {
+		DBG_ERR(("CAPI_SEND_MSG - remove in progress!"))
+		return CAPI_REGOSRESOURCEERR;
+	}
+
+	diva_os_enter_spin_lock(&api_lock, &old_irql, "send message");
+
+	if (!this->Id) {
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
+		return CAPI_ILLAPPNR;
+	}
+
+	/* patch controller number */
+	msg->header.controller = ControllerMap[card->Id]
+	    | (msg->header.controller & 0x80);	/* preserve external controller bit */
+
+	switch (command) {
+	default:
+		xlog("\x00\x02", msg, 0x80, clength);
+		break;
+
+	case _DATA_B3_I | RESPONSE:
+#ifndef DIVA_NO_DEBUGLIB
+		if (myDriverDebugHandle.dbgMask & DL_BLK)
+			xlog("\x00\x02", msg, 0x80, clength);
+#endif
+		break;
+
+	case _DATA_B3_R:
+#ifndef DIVA_NO_DEBUGLIB
+		if (myDriverDebugHandle.dbgMask & DL_BLK)
+			xlog("\x00\x02", msg, 0x80, clength);
+#endif
+
+		if (clength == 24)
+			clength = 22;	/* workaround for PPcom bug */
+		/* header is always 22      */
+		if (GET_WORD(&msg->info.data_b3_req.Data_Length) >
+		    this->MaxDataLength
+		    || GET_WORD(&msg->info.data_b3_req.Data_Length) >
+		    (length - clength)) {
+			DBG_ERR(("Write - invalid message size"))
+			retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+			goto write_end;
+		}
+
+		for (i = 0; i < (MAX_DATA_B3 * this->MaxNCCI)
+		     && this->xbuffer_used[i]; i++);
+		if (i == (MAX_DATA_B3 * this->MaxNCCI)) {
+			DBG_ERR(("Write - too many data pending"))
+			retval = CAPI_SENDQUEUEFULL;
+			goto write_end;
+		}
+		msg->info.data_b3_req.Data = i;
+
+		this->xbuffer_internal[i] = NULL;
+		memcpy(this->xbuffer_ptr[i], &((__u8 *) msg)[clength],
+		       GET_WORD(&msg->info.data_b3_req.Data_Length));
+
+#ifndef DIVA_NO_DEBUGLIB
+		if ((myDriverDebugHandle.dbgMask & DL_BLK)
+		    && (myDriverDebugHandle.dbgMask & DL_XLOG)) {
+			int j;
+			for (j = 0; j <
+			     GET_WORD(&msg->info.data_b3_req.Data_Length);
+			     j += 256) {
+				DBG_BLK((((char *) this->xbuffer_ptr[i]) + j,
+					((GET_WORD(&msg->info.data_b3_req.Data_Length) - j) <
+					  256) ? (GET_WORD(&msg->info.data_b3_req.Data_Length) - j) : 256))
+				if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
+					break;	/* not more if not explicitely requested */
+			}
+		}
+#endif
+		break;
+	}
+
+	memcpy(mapped_msg, msg, (__u32) clength);
+	mapped_msg->header.controller = MapController(mapped_msg->header.controller);
+	mapped_msg->header.length = clength;
+	mapped_msg->header.command = command;
+	mapped_msg->header.number = GET_WORD(&msg->header.number);
+
+	ret = api_put(this, mapped_msg);
+	switch (ret) {
+	case 0:
+		break;
+	case _BAD_MSG:
+		DBG_ERR(("Write - bad message"))
+		retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+		break;
+	case _QUEUE_FULL:
+		DBG_ERR(("Write - queue full"))
+		retval = CAPI_SENDQUEUEFULL;
+		break;
+	default:
+		DBG_ERR(("Write - api_put returned unknown error"))
+		retval = CAPI_UNKNOWNNOTPAR;
+		break;
+	}
+
+      write_end:
+	diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
+	if (retval == CAPI_NOERROR)
+		diva_os_free_message_buffer(dmb);
+	return retval;
+}
+
+
+/*
+ * cards request function
+ */
+static void DIRequest(ENTITY * e)
+{
+	DIVA_CAPI_ADAPTER *a = &(adapter[(byte) e->user[0]]);
+	diva_card *os_card = (diva_card *) a->os_card;
+
+	if (e->Req && (a->FlowControlIdTable[e->ReqCh] == e->Id)) {
+		a->FlowControlSkipTable[e->ReqCh] = 1;
+	}
+
+	(*(os_card->d.request)) (e);
+}
+
+/*
+ * callback function from didd
+ */
+static void didd_callback(void *context, DESCRIPTOR * adapter, int removal)
+{
+	if (adapter->type == IDI_DADAPTER) {
+		DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
+		return;
+	} else if (adapter->type == IDI_DIMAINT) {
+		if (removal) {
+			stop_dbg();
+		} else {
+			memcpy(&MAdapter, adapter, sizeof(MAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
+		}
+	} else if ((adapter->type > 0) && (adapter->type < 16)) {	/* IDI Adapter */
+		if (removal) {
+			divacapi_remove_card(adapter);
+		} else {
+			diva_add_card(adapter);
+		}
+	}
+	return;
+}
+
+/*
+ * connect to didd
+ */
+static int divacapi_connect_didd(void)
+{
+	int x = 0;
+	int dadapter = 0;
+	IDI_SYNC_REQ req;
+	DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+	DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DIMAINT) {	/* MAINT found */
+			memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
+			break;
+		}
+	}
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DADAPTER) {	/* DADAPTER found */
+			dadapter = 1;
+			memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			req.didd_notify.e.Req = 0;
+			req.didd_notify.e.Rc =
+			    IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+			req.didd_notify.info.callback = (void *)didd_callback;
+			req.didd_notify.info.context = NULL;
+			DAdapter.request((ENTITY *) & req);
+			if (req.didd_notify.e.Rc != 0xff) {
+				stop_dbg();
+				return (0);
+			}
+			notify_handle = req.didd_notify.info.handle;
+		}
+			else if ((DIDD_Table[x].type > 0) && (DIDD_Table[x].type < 16)) {	/* IDI Adapter found */
+			diva_add_card(&DIDD_Table[x]);
+		}
+	}
+
+	if (!dadapter) {
+		stop_dbg();
+	}
+
+	return (dadapter);
+}
+
+/*
+ * diconnect from didd
+ */
+static void divacapi_disconnect_didd(void)
+{
+	IDI_SYNC_REQ req;
+
+	stop_dbg();
+
+	req.didd_notify.e.Req = 0;
+	req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+	req.didd_notify.info.handle = notify_handle;
+	DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * we do not provide date/time here,
+ * the application should do this. 
+ */
+int fax_head_line_time(char *buffer)
+{
+	return (0);
+}
+
+/*
+ * init (alloc) main structures
+ */
+static int DIVA_INIT_FUNCTION init_main_structs(void)
+{
+	if (!(mapped_msg = (CAPI_MSG *) diva_os_malloc(0, MAX_MSG_SIZE))) {
+		DBG_ERR(("init: failed alloc mapped_msg."))
+		    return 0;
+	}
+
+	if (!(adapter = diva_os_malloc(0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS))) {
+		DBG_ERR(("init: failed alloc adapter struct."))
+		diva_os_free(0, mapped_msg);
+		return 0;
+	}
+	memset(adapter, 0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS);
+
+	if (!(application = diva_os_malloc(0, sizeof(APPL) * MAX_APPL))) {
+		DBG_ERR(("init: failed alloc application struct."))
+		diva_os_free(0, mapped_msg);
+		diva_os_free(0, adapter);
+		return 0;
+	}
+	memset(application, 0, sizeof(APPL) * MAX_APPL);
+
+	return (1);
+}
+
+/*
+ * remove (free) main structures
+ */
+static void remove_main_structs(void)
+{
+	if (application)
+		diva_os_free(0, application);
+	if (adapter)
+		diva_os_free(0, adapter);
+	if (mapped_msg)
+		diva_os_free(0, mapped_msg);
+}
+
+/*
+ * api_remove_start
+ */
+static void do_api_remove_start(void)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	int ret = 1, count = 100;
+
+	do {
+		diva_os_enter_spin_lock(&api_lock, &old_irql, "api remove start");
+		ret = api_remove_start();
+		diva_os_leave_spin_lock(&api_lock, &old_irql, "api remove start");
+
+		diva_os_sleep(10);
+	} while (ret && count--);
+
+	if (ret)
+		DBG_ERR(("could not remove signaling ID's"))
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION init_capifunc(void)
+{
+	diva_os_initialize_spin_lock(&api_lock, "capifunc");
+	memset(ControllerMap, 0, MAX_DESCRIPTORS + 1);
+	max_adapter = 0;
+
+
+	if (!init_main_structs()) {
+		DBG_ERR(("init: failed to init main structs."))
+		diva_os_destroy_spin_lock(&api_lock, "capifunc");
+		return (0);
+	}
+
+	if (!divacapi_connect_didd()) {
+		DBG_ERR(("init: failed to connect to DIDD."))
+		do_api_remove_start();
+		divacapi_remove_cards();
+		remove_main_structs();
+		diva_os_destroy_spin_lock(&api_lock, "capifunc");
+		return (0);
+	}
+
+	return (1);
+}
+
+/*
+ * finit
+ */
+void DIVA_EXIT_FUNCTION finit_capifunc(void)
+{
+	do_api_remove_start();
+	divacapi_disconnect_didd();
+	divacapi_remove_cards();
+	remove_main_structs();
+	diva_os_destroy_spin_lock(&api_lock, "capifunc");
+}
diff --git a/drivers/isdn/hardware/eicon/capifunc.h b/drivers/isdn/hardware/eicon/capifunc.h
new file mode 100644
index 0000000..bd256f2
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capifunc.h
@@ -0,0 +1,40 @@
+/* $Id: capifunc.h,v 1.11.4.1 2004/08/28 20:03:53 armin Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface common functions
+ * 
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de) 
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef __CAPIFUNC_H__
+#define __CAPIFUNC_H__
+
+#define DRRELMAJOR  2
+#define DRRELMINOR  0
+#define DRRELEXTRA  ""
+
+#define M_COMPANY "Eicon Networks"
+
+extern char DRIVERRELEASE_CAPI[];
+
+typedef struct _diva_card {
+	struct list_head list;
+	int remove_in_progress;
+	int Id;
+	struct capi_ctr capi_ctrl;
+	DIVA_CAPI_ADAPTER *adapter;
+	DESCRIPTOR d;
+	char name[32];
+} diva_card;
+
+/*
+ * prototypes
+ */
+int init_capifunc(void);
+void finit_capifunc(void);
+
+#endif /* __CAPIFUNC_H__ */
diff --git a/drivers/isdn/hardware/eicon/capimain.c b/drivers/isdn/hardware/eicon/capimain.c
new file mode 100644
index 0000000..8fe4f3f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capimain.c
@@ -0,0 +1,147 @@
+/* $Id: capimain.c,v 1.24 2003/09/09 06:51:05 schindler Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface
+ * 
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de) 
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/skbuff.h>
+
+#include "os_capi.h"
+
+#include "platform.h"
+#include "di_defs.h"
+#include "capi20.h"
+#include "divacapi.h"
+#include "cp_vers.h"
+#include "capifunc.h"
+
+static char *main_revision = "$Revision: 1.24 $";
+static char *DRIVERNAME =
+    "Eicon DIVA - CAPI Interface driver (http://www.melware.net)";
+static char *DRIVERLNAME = "divacapi";
+
+MODULE_DESCRIPTION("CAPI driver for Eicon DIVA cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("CAPI and DIVA card drivers");
+MODULE_LICENSE("GPL");
+
+/*
+ * get revision number from revision string
+ */
+static char *getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "1.0";
+	return rev;
+
+}
+
+/*
+ * alloc a message buffer
+ */
+diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size,
+						       void **data_buf)
+{
+	diva_os_message_buffer_s *dmb = alloc_skb(size, GFP_ATOMIC);
+	if (dmb) {
+		*data_buf = skb_put(dmb, size);
+	}
+	return (dmb);
+}
+
+/*
+ * free a message buffer
+ */
+void diva_os_free_message_buffer(diva_os_message_buffer_s * dmb)
+{
+	kfree_skb(dmb);
+}
+
+/*
+ * proc function for controller info
+ */
+static int diva_ctl_read_proc(char *page, char **start, off_t off,
+			      int count, int *eof, struct capi_ctr *ctrl)
+{
+	diva_card *card = (diva_card *) ctrl->driverdata;
+	int len = 0;
+
+	len += sprintf(page + len, "%s\n", ctrl->name);
+	len += sprintf(page + len, "Serial No. : %s\n", ctrl->serial);
+	len += sprintf(page + len, "Id         : %d\n", card->Id);
+	len += sprintf(page + len, "Channels   : %d\n", card->d.channels);
+
+	if (off + count >= len)
+		*eof = 1;
+	if (len < off)
+		return 0;
+	*start = page + off;
+	return ((count < len - off) ? count : len - off);
+}
+
+/*
+ * set additional os settings in capi_ctr struct
+ */
+void diva_os_set_controller_struct(struct capi_ctr *ctrl)
+{
+	ctrl->driver_name = DRIVERLNAME;
+	ctrl->load_firmware = NULL;
+	ctrl->reset_ctr = NULL;
+	ctrl->ctr_read_proc = diva_ctl_read_proc;
+	ctrl->owner = THIS_MODULE;
+}
+
+/*
+ * module init
+ */
+static int DIVA_INIT_FUNCTION divacapi_init(void)
+{
+	char tmprev[32];
+	int ret = 0;
+
+	sprintf(DRIVERRELEASE_CAPI, "%d.%d%s", DRRELMAJOR, DRRELMINOR,
+		DRRELEXTRA);
+
+	printk(KERN_INFO "%s\n", DRIVERNAME);
+	printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_CAPI);
+	strcpy(tmprev, main_revision);
+	printk("%s  Build: %s(%s)\n", getrev(tmprev),
+	       diva_capi_common_code_build, DIVA_BUILD);
+
+	if (!(init_capifunc())) {
+		printk(KERN_ERR "%s: failed init capi_driver.\n",
+		       DRIVERLNAME);
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
+/*
+ * module exit
+ */
+static void DIVA_EXIT_FUNCTION divacapi_exit(void)
+{
+	finit_capifunc();
+	printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divacapi_init);
+module_exit(divacapi_exit);
diff --git a/drivers/isdn/hardware/eicon/cardtype.h b/drivers/isdn/hardware/eicon/cardtype.h
new file mode 100644
index 0000000..18a5c42
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/cardtype.h
@@ -0,0 +1,1098 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 _CARDTYPE_H_
+#define _CARDTYPE_H_
+#ifndef CARDTYPE_H_WANT_DATA
+#define CARDTYPE_H_WANT_DATA   0
+#endif
+#ifndef CARDTYPE_H_WANT_IDI_DATA
+#define CARDTYPE_H_WANT_IDI_DATA  0
+#endif
+#ifndef CARDTYPE_H_WANT_RESOURCE_DATA
+#define CARDTYPE_H_WANT_RESOURCE_DATA 1
+#endif
+#ifndef CARDTYPE_H_WANT_FILE_DATA
+#define CARDTYPE_H_WANT_FILE_DATA  1
+#endif
+/*
+ * D-channel protocol identifiers
+ *
+ * Attention: Unfortunately the identifiers defined here differ from
+ *      the identifiers used in Protocol/1/Common/prot/q931.h .
+ *     The only reason for this is that q931.h has not a global
+ *     scope and we did not know about the definitions there.
+ *     But the definitions here cannot be changed easily because
+ *     they are used in setup scripts and programs.
+ *     Thus the definitions here have to be mapped if they are
+ *     used in the protocol code context !
+ *
+ * Now the identifiers are defined in the q931lib/constant.h file.
+ * Unfortunately this file has also not a global scope.
+ * But beginning with PROTTYPE_US any new identifier will get the same
+ * value as the corresponding PROT_* definition in q931lib/constant.h !
+ */
+#define PROTTYPE_MINVAL     0
+#define PROTTYPE_ETSI       0
+#define PROTTYPE_1TR6       1
+#define PROTTYPE_BELG       2
+#define PROTTYPE_FRANC      3
+#define PROTTYPE_ATEL       4
+#define PROTTYPE_NI         5  /* DMS 100, Nortel, National ISDN */
+#define PROTTYPE_5ESS       6  /* 5ESS   , AT&T,   5ESS Custom   */
+#define PROTTYPE_JAPAN      7
+#define PROTTYPE_SWED       8
+#define PROTTYPE_US         9  /* US autodetect */
+#define PROTTYPE_ITALY      10
+#define PROTTYPE_TWAN       11
+#define PROTTYPE_AUSTRAL    12
+#define PROTTYPE_4ESDN      13
+#define PROTTYPE_4ESDS      14
+#define PROTTYPE_4ELDS      15
+#define PROTTYPE_4EMGC      16
+#define PROTTYPE_4EMGI      17
+#define PROTTYPE_HONGKONG   18
+#define PROTTYPE_RBSCAS     19
+#define PROTTYPE_CORNETN    20
+#define PROTTYPE_QSIG       21
+#define PROTTYPE_NI_EWSD    22 /* EWSD, Siemens, National ISDN   */
+#define PROTTYPE_5ESS_NI    23 /* 5ESS, Lucent, National ISDN    */
+#define PROTTYPE_T1CORNETN  24
+#define PROTTYPE_CORNETNQ   25
+#define PROTTYPE_T1CORNETNQ 26
+#define PROTTYPE_T1QSIG     27
+#define PROTTYPE_E1UNCH     28
+#define PROTTYPE_T1UNCH     29
+#define PROTTYPE_E1CHAN     30
+#define PROTTYPE_T1CHAN     31
+#define PROTTYPE_R2CAS      32
+#define PROTTYPE_MAXVAL     32
+/*
+ * Card type identifiers
+ */
+#define CARD_UNKNOWN                      0
+#define CARD_NONE                         0
+  /* DIVA cards */
+#define CARDTYPE_DIVA_MCA                 0
+#define CARDTYPE_DIVA_ISA                 1
+#define CARDTYPE_DIVA_PCM                 2
+#define CARDTYPE_DIVAPRO_ISA              3
+#define CARDTYPE_DIVAPRO_PCM              4
+#define CARDTYPE_DIVAPICO_ISA             5
+#define CARDTYPE_DIVAPICO_PCM             6
+  /* DIVA 2.0 cards */
+#define CARDTYPE_DIVAPRO20_PCI            7
+#define CARDTYPE_DIVA20_PCI               8
+  /* S cards */
+#define CARDTYPE_QUADRO_ISA               9
+#define CARDTYPE_S_ISA                    10
+#define CARDTYPE_S_MCA                    11
+#define CARDTYPE_SX_ISA                   12
+#define CARDTYPE_SX_MCA                   13
+#define CARDTYPE_SXN_ISA                  14
+#define CARDTYPE_SXN_MCA                  15
+#define CARDTYPE_SCOM_ISA                 16
+#define CARDTYPE_SCOM_MCA                 17
+#define CARDTYPE_PR_ISA                   18
+#define CARDTYPE_PR_MCA                   19
+  /* Diva Server cards (formerly called Maestra, later Amadeo) */
+#define CARDTYPE_MAESTRA_ISA              20
+#define CARDTYPE_MAESTRA_PCI              21
+  /* Diva Server cards to be developed (Quadro, Primary rate) */
+#define CARDTYPE_DIVASRV_Q_8M_PCI         22
+#define CARDTYPE_DIVASRV_P_30M_PCI        23
+#define CARDTYPE_DIVASRV_P_2M_PCI         24
+#define CARDTYPE_DIVASRV_P_9M_PCI         25
+  /* DIVA 2.0 cards */
+#define CARDTYPE_DIVA20_ISA               26
+#define CARDTYPE_DIVA20U_ISA              27
+#define CARDTYPE_DIVA20U_PCI              28
+#define CARDTYPE_DIVAPRO20_ISA            29
+#define CARDTYPE_DIVAPRO20U_ISA           30
+#define CARDTYPE_DIVAPRO20U_PCI           31
+  /* DIVA combi cards (piccola ISDN + rockwell V.34 modem) */
+#define CARDTYPE_DIVAMOBILE_PCM           32
+#define CARDTYPE_TDKGLOBALPRO_PCM         33
+  /* DIVA Pro PC OEM card for 'New Media Corporation' */
+#define CARDTYPE_NMC_DIVAPRO_PCM          34
+  /* DIVA Pro 2.0 OEM cards for 'British Telecom' */
+#define CARDTYPE_BT_EXLANE_PCI            35
+#define CARDTYPE_BT_EXLANE_ISA            36
+  /* DIVA low cost cards, 1st name DIVA 3.0, 2nd DIVA 2.01, 3rd ??? */
+#define CARDTYPE_DIVALOW_ISA              37
+#define CARDTYPE_DIVALOWU_ISA             38
+#define CARDTYPE_DIVALOW_PCI              39
+#define CARDTYPE_DIVALOWU_PCI             40
+  /* DIVA combi cards (piccola ISDN + rockwell V.90 modem) */
+#define CARDTYPE_DIVAMOBILE_V90_PCM       41
+#define CARDTYPE_TDKGLOBPRO_V90_PCM       42
+#define CARDTYPE_DIVASRV_P_23M_PCI        43
+#define CARDTYPE_DIVALOW_USB              44
+  /* DIVA Audio (CT) family */
+#define CARDTYPE_DIVA_CT_ST               45
+#define CARDTYPE_DIVA_CT_U                46
+#define CARDTYPE_DIVA_CTLITE_ST           47
+#define CARDTYPE_DIVA_CTLITE_U            48
+  /* DIVA ISDN plus V.90 series */
+#define CARDTYPE_DIVAISDN_V90_PCM         49
+#define CARDTYPE_DIVAISDN_V90_PCI         50
+#define CARDTYPE_DIVAISDN_TA              51
+  /* DIVA Server Voice cards */
+#define CARDTYPE_DIVASRV_VOICE_Q_8M_PCI   52
+  /* DIVA Server V2 cards */
+#define CARDTYPE_DIVASRV_Q_8M_V2_PCI      53
+#define CARDTYPE_DIVASRV_P_30M_V2_PCI     54
+  /* DIVA Server Voice V2 cards */
+#define CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI 55
+#define CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI 56
+    /* Diva LAN */
+#define CARDTYPE_DIVAISDN_LAN             57
+#define CARDTYPE_DIVA_202_PCI_ST          58
+#define CARDTYPE_DIVA_202_PCI_U           59
+#define CARDTYPE_DIVASRV_B_2M_V2_PCI      60
+#define CARDTYPE_DIVASRV_B_2F_PCI         61
+#define CARDTYPE_DIVALOW_USBV2            62
+#define CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI 63
+#define CARDTYPE_DIVA_PRO_30_PCI_ST       64
+#define CARDTYPE_DIVA_CT_ST_V20           65
+/* Diva Mobile V.90 PC Card and Diva ISDN PC Card */
+#define CARDTYPE_DIVAMOBILE_V2_PCM        66
+#define CARDTYPE_DIVA_V2_PCM              67
+/* Re-badged Diva Pro PC Card */
+#define CARDTYPE_DIVA_PC_CARD             68
+  /* next free card type identifier */
+#define CARDTYPE_MAX                      69
+/*
+ * The card families
+ */
+#define FAMILY_DIVA   1
+#define FAMILY_S   2
+#define FAMILY_MAESTRA  3
+#define FAMILY_MAX   4
+/*
+ * The basic card types
+ */
+#define CARD_DIVA           1  /* DSP based, old DSP */
+#define CARD_PRO            2  /* DSP based, new DSP */
+#define CARD_PICO           3  /* HSCX based   */
+#define CARD_S    4  /* IDI on board based */
+#define CARD_SX    5  /* IDI on board based */
+#define CARD_SXN   6  /* IDI on board based */
+#define CARD_SCOM   7  /* IDI on board based */
+#define CARD_QUAD   8  /* IDI on board based */
+#define CARD_PR    9  /* IDI on board based */
+#define CARD_MAE         10  /* IDI on board based */
+#define CARD_MAEQ        11  /* IDI on board based */
+#define CARD_MAEP        12  /* IDI on board based */
+#define CARD_DIVALOW  13  /* IPAC based   */
+#define CARD_CT    14  /* SCOUT based          */
+#define CARD_DIVATA   15  /* DIVA TA */
+#define CARD_DIVALAN  16  /* DIVA LAN */
+#define CARD_MAE2         17  /* IDI on board based */
+#define CARD_MAX   18
+/*
+ * The internal card types of the S family
+ */
+#define CARD_I_NONE   0
+#define CARD_I_S   0
+#define CARD_I_SX   1
+#define CARD_I_SCOM   2
+#define CARD_I_QUAD   3
+#define CARD_I_PR   4
+/*
+ * The bus types we support
+ */
+#define BUS_ISA             1
+#define BUS_PCM             2
+#define BUS_PCI             3
+#define BUS_MCA             4
+#define BUS_USB             5
+#define BUS_COM    6
+#define BUS_LAN    7
+/*
+ * The chips we use for B-channel traffic
+ */
+#define CHIP_NONE           0
+#define CHIP_DSP            1
+#define CHIP_HSCX           2
+#define CHIP_IPAC           3
+#define CHIP_SCOUT          4
+#define CHIP_EXTERN         5
+#define CHIP_IPACX          6
+/*
+ * The structures where the card properties are aggregated by id
+ */
+typedef struct CARD_PROPERTIES
+{   char     *Name;  /* official marketing name     */
+ unsigned short PnPId;  /* plug and play ID (for non PCMIA cards) */
+ unsigned short Version; /* major and minor version no of the card */
+ unsigned char DescType; /* card type to set in the IDI descriptor */
+ unsigned char  Family;  /* basic family of the card     */
+ unsigned short  Features; /* features bits to set in the IDI desc. */
+ unsigned char Card;  /* basic card type       */
+ unsigned char IType;  /* internal type of S cards (read from ram) */
+ unsigned char  Bus;  /* bus type this card is designed for  */
+ unsigned char  Chip;  /* chipset used on card      */
+ unsigned char Adapters; /* number of adapters on card    */
+ unsigned char Channels; /* # of channels per adapter    */
+ unsigned short E_info;  /* # of ram entity info structs per adapter */
+ unsigned short SizeIo;  /* size of IO window per adapter   */
+ unsigned short SizeMem; /* size of memory window per adapter  */
+} CARD_PROPERTIES;
+typedef struct CARD_RESOURCE
+{ unsigned char Int [10];
+ unsigned short IoFirst;
+ unsigned short IoStep;
+ unsigned short IoCnt;
+ unsigned long MemFirst;
+ unsigned long MemStep;
+ unsigned short MemCnt;
+} CARD_RESOURCE;
+/* test if the card of type 't' is a plug & play card */
+#define IS_PNP(t) \
+( \
+ ( \
+  CardProperties[t].Bus != BUS_ISA \
+  && \
+  CardProperties[t].Bus != BUS_MCA \
+ ) \
+ || \
+ ( \
+  CardProperties[t].Family != FAMILY_S \
+  && \
+  CardProperties[t].Card != CARD_DIVA \
+ ) \
+)
+/* extract IDI Descriptor info for card type 't' (p == DescType/Features) */
+#define IDI_PROP(t,p) (CardProperties[t].p)
+#if CARDTYPE_H_WANT_DATA
+#if CARDTYPE_H_WANT_IDI_DATA
+/* include "di_defs.h" for IDI adapter type and feature flag definitions */
+#include "di_defs.h"
+#else /*!CARDTYPE_H_WANT_IDI_DATA*/
+/* define IDI adapter types and feature flags here to prevent inclusion  */
+#ifndef IDI_ADAPTER_S
+#define IDI_ADAPTER_S           1
+#define IDI_ADAPTER_PR          2
+#define IDI_ADAPTER_DIVA        3
+#define IDI_ADAPTER_MAESTRA     4
+#endif
+#ifndef DI_VOICE
+#define DI_VOICE          0x0 /* obsolete define */
+#define DI_FAX3           0x1
+#define DI_MODEM          0x2
+#define DI_POST           0x4
+#define DI_V110           0x8
+#define DI_V120           0x10
+#define DI_POTS           0x20
+#define DI_CODEC          0x40
+#define DI_MANAGE         0x80
+#define DI_V_42           0x0100
+#define DI_EXTD_FAX       0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */
+#define DI_AT_PARSER      0x0400 /* Build-in AT Parser in the L2 */
+#define DI_VOICE_OVER_IP  0x0800 /* Voice over IP support */
+#endif
+#endif /*CARDTYPE_H_WANT_IDI_DATA*/
+#define DI_V1x0         (DI_V110 | DI_V120)
+#define DI_NULL         0x0000
+#if defined(SOFT_DSP_SUPPORT)
+#define SOFT_DSP_ADD_FEATURES  (DI_MODEM | DI_FAX3 | DI_AT_PARSER)
+#else
+#define SOFT_DSP_ADD_FEATURES  0
+#endif
+#if defined(SOFT_V110_SUPPORT)
+#define DI_SOFT_V110  DI_V110
+#else
+#define DI_SOFT_V110  0
+#endif
+/*--- CardProperties [Index=CARDTYPE_....] ---------------------------------*/
+CARD_PROPERTIES CardProperties [ ] =
+{
+{ /*  0  */
+ "Diva MCA",       0x6336,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
+ CARD_DIVA,   CARD_I_NONE, BUS_MCA, CHIP_DSP,
+ 1, 2,  0,   8,      0
+},
+{ /*  1  */
+ "Diva ISA",       0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
+ CARD_DIVA,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2,  0,   8,      0
+},
+{ /*  2  */
+ "Diva/PCM",       0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
+ CARD_DIVA,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2,  0,   8,      0
+},
+{ /*  3  */
+ "Diva PRO ISA",      0x0031,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2,  0,   8,      0
+},
+{ /*  4  */
+ "Diva PRO PC-Card",     0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_PRO,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2,   0,   8,      0
+},
+{ /*  5  */
+ "Diva PICCOLA ISA",     0x0051,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_ISA, CHIP_HSCX,
+ 1, 2,   0,   8,      0
+},
+{ /*  6  */
+ "Diva PICCOLA PCM",     0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2,   0,   8,      0
+},
+{ /*  7  */
+ "Diva PRO 2.0 S/T PCI",    0xe001,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2,   0,   8,      0
+},
+{ /*  8  */
+ "Diva 2.0 S/T PCI",     0xe002,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCI, CHIP_HSCX,
+ 1, 2,   0,   8,      0
+},
+{ /*  9  */
+ "QUADRO ISA",      0x0000,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
+ CARD_QUAD,   CARD_I_QUAD, BUS_ISA, CHIP_NONE,
+ 4, 2,   16,  0,  0x800
+},
+{ /* 10  */
+ "S ISA",       0x0000,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
+ CARD_S,    CARD_I_S,  BUS_ISA, CHIP_NONE,
+ 1, 1,   16,  0,  0x800
+},
+{ /* 11  */
+ "S MCA",       0x6a93,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
+ CARD_S,    CARD_I_S,  BUS_MCA, CHIP_NONE,
+ 1, 1,   16,  16,  0x400
+},
+{ /* 12 */
+ "SX ISA",       0x0000,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
+ CARD_SX,   CARD_I_SX,  BUS_ISA, CHIP_NONE,
+ 1, 2,  16,  0,  0x800
+},
+{ /* 13 */
+ "SX MCA",       0x6a93,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
+ CARD_SX,   CARD_I_SX,  BUS_MCA, CHIP_NONE,
+ 1, 2,  16,  16,  0x400
+},
+{ /* 14 */
+ "SXN ISA",       0x0000,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
+ CARD_SXN,   CARD_I_SCOM, BUS_ISA, CHIP_NONE,
+ 1, 2,   16,  0,   0x800
+},
+{ /* 15 */
+ "SXN MCA",       0x6a93,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_NULL,
+ CARD_SXN,   CARD_I_SCOM, BUS_MCA, CHIP_NONE,
+ 1, 2,  16,  16,  0x400
+},
+{ /* 16 */
+ "SCOM ISA",       0x0000,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
+ CARD_SCOM,   CARD_I_SCOM, BUS_ISA, CHIP_NONE,
+ 1, 2,   16,  0,   0x800
+},
+{ /* 17 */
+ "SCOM MCA",       0x6a93,  0x0100,
+ IDI_ADAPTER_S,  FAMILY_S,  DI_CODEC,
+ CARD_SCOM,   CARD_I_SCOM, BUS_MCA, CHIP_NONE,
+ 1, 2,  16,  16,  0x400
+},
+{ /* 18 */
+ "S2M ISA",       0x0000,  0x0100,
+ IDI_ADAPTER_PR,  FAMILY_S,  DI_NULL,
+ CARD_PR,   CARD_I_PR,  BUS_ISA, CHIP_NONE,
+ 1, 30,  256, 0,   0x4000
+},
+{ /* 19 */
+ "S2M MCA",       0x6abb,  0x0100,
+ IDI_ADAPTER_PR,  FAMILY_S,  DI_NULL,
+ CARD_PR,   CARD_I_PR,  BUS_MCA, CHIP_NONE,
+ 1, 30,  256, 16,  0x4000
+},
+{ /* 20 */
+ "Diva Server BRI-2M ISA",   0x0041,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAE,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2,   16,  8,  0
+},
+{ /* 21 */
+ "Diva Server BRI-2M PCI",   0xE010,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAE,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2,   16,  8,   0
+},
+{ /* 22 */
+ "Diva Server 4BRI-8M PCI",   0xE012,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2,   16,  8,   0
+},
+{ /* 23 */
+ "Diva Server PRI-30M PCI",   0xE014,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30,  256,  8,   0
+},
+{ /* 24 */
+ "Diva Server PRI-2M PCI",   0xe014,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30,  256,  8,   0
+},
+{ /* 25 */
+ "Diva Server PRI-9M PCI",   0x0000,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30,     256,  8,   0
+},
+{ /* 26 */
+ "Diva 2.0 S/T ISA",     0x0071,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_ISA, CHIP_HSCX,
+ 1, 2,  0,   8,   0
+},
+{ /* 27 */
+ "Diva 2.0 U ISA",     0x0091,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_ISA, CHIP_HSCX,
+ 1, 2,   0,   8,   0
+},
+{ /* 28 */
+ "Diva 2.0 U PCI",     0xe004,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCI, CHIP_HSCX,
+ 1, 2,   0,   8,   0
+},
+{ /* 29 */
+ "Diva PRO 2.0 S/T ISA",    0x0061,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2,  0,   8,   0
+},
+{ /* 30 */
+ "Diva PRO 2.0 U ISA",    0x0081,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2,  0,   8,   0
+},
+{ /* 31 */
+ "Diva PRO 2.0 U PCI",    0xe003,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2,   0,   8,   0
+},
+{ /* 32 */
+ "Diva MOBILE",      0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2,  0,   8,   0
+},
+{ /* 33 */
+ "TDK DFI3600",      0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2,  0,   8,   0
+},
+{ /* 34 (OEM version of 4 - "Diva PRO PC-Card") */
+ "New Media ISDN",     0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_PRO,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2,   0,   8,   0
+},
+{ /* 35 (OEM version of 7 - "Diva PRO 2.0 S/T PCI") */
+ "BT ExLane PCI",     0xe101,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2,   0,   8,   0
+},
+{ /* 36 (OEM version of 29 - "Diva PRO 2.0 S/T ISA") */
+ "BT ExLane ISA",     0x1061,  0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO,   CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2,   0,   8,   0
+},
+{ /* 37 */
+ "Diva 2.01 S/T ISA",    0x00A1,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_ISA, CHIP_IPAC,
+ 1, 2,   0,   8,      0
+},
+{ /* 38 */
+ "Diva 2.01 U ISA",     0x00B1,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_ISA, CHIP_IPAC,
+ 1, 2,   0,   8,      0
+},
+{ /* 39 */
+ "Diva 2.01 S/T PCI",    0xe005,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPAC,
+ 1, 2,   0,   8,   0
+},
+{ /* 40        no ID yet */
+ "Diva 2.01 U PCI",     0x0000,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPAC,
+ 1, 2,   0,   8,   0
+},
+{ /* 41 */
+ "Diva MOBILE V.90",     0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2,  0,   8,   0
+},
+{ /* 42 */
+ "TDK DFI3600 V.90",     0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2,  0,   8,   0
+},
+{ /* 43 */
+ "Diva Server PRI-23M PCI",   0xe014,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30,  256,  8,   0
+},
+{ /* 44 */
+ "Diva 2.01 S/T USB",    0x1000,     0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_USB, CHIP_IPAC,
+ 1,  2,  0,  8,   0
+},
+{ /* 45 */
+ "Diva CT S/T PCI",    0xe006,  0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1,  2,  0,  0,   0
+},
+{ /* 46 */
+ "Diva CT U PCI",     0xe007,  0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1,  2,  0,  0,   0
+},
+{ /* 47 */
+ "Diva CT Lite S/T PCI",   0xe008,  0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1,  2,  0,  0,   0
+},
+{ /* 48 */
+ "Diva CT Lite U PCI",   0xe009,  0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1,  2,  0,  0,   0
+},
+{ /* 49 */
+ "Diva ISDN+V.90 PC Card", 0x8D8C, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_DIVALOW, CARD_I_NONE, BUS_PCM, CHIP_IPAC,
+ 1, 2,  0,   8,   0
+},
+{ /* 50 */
+ "Diva ISDN+V.90 PCI",    0xe00A,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120  | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPAC,
+ 1, 2,   0,   8,   0
+},
+{ /* 51 (DivaTA)      no ID */
+ "Diva TA",       0x0000,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVATA,  CARD_I_NONE, BUS_COM, CHIP_EXTERN,
+ 1, 1,   0,   8,   0
+},
+{ /* 52 (Diva Server 4BRI-8M PCI adapter enabled for Voice) */
+ "Diva Server Voice 4BRI-8M PCI", 0xE016,  0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2,   16,  8,   0
+},
+{ /* 53 (Diva Server 4BRI 2.0 adapter) */
+ "Diva Server 4BRI-8M 2.0 PCI",  0xE013,  0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2,   16,  8,   0
+},
+{ /* 54 (Diva Server PRI 2.0 adapter) */
+ "Diva Server PRI 2.0 PCI",   0xE015,  0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30,  256,  8,   0
+},
+{ /* 55 (Diva Server 4BRI-8M 2.0 PCI adapter enabled for Voice) */
+ "Diva Server Voice 4BRI-8M 2.0 PCI", 0xE017,  0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAEQ,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2,   16,  8,   0
+},
+{ /* 56 (Diva Server PRI 2.0 PCI adapter enabled for Voice) */
+ "Diva Server Voice PRI 2.0 PCI",  0xE019,  0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAEP,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30,  256,  8,   0
+},
+{
+ /* 57 (DivaLan )      no ID */
+ "Diva LAN",       0x0000,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALAN,  CARD_I_NONE, BUS_LAN, CHIP_EXTERN,
+ 1, 1,   0,   8,   0
+},
+{ /* 58 */
+ "Diva 2.02 PCI S/T",    0xE00B,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES | DI_SOFT_V110,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPACX,
+ 1, 2,   0,   8,   0
+},
+{ /* 59 */
+ "Diva 2.02 PCI U",     0xE00C,  0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_PCI, CHIP_IPACX,
+ 1, 2,   0,   8,   0
+},
+{ /* 60 */
+ "Diva Server BRI-2M 2.0 PCI",     0xE018,  0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAE2,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2,   16,  8,   0
+},
+{ /* 61  (the previous name was Diva Server BRI-2F 2.0 PCI) */
+ "Diva Server 2FX",                      0xE01A,     0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_SOFT_V110,
+ CARD_MAE2,          CARD_I_NONE,    BUS_PCI,    CHIP_IPACX,
+ 1,  2,      16,     8,   0
+},
+{ /* 62 */
+ " Diva ISDN USB 2.0",    0x1003,     0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW,  CARD_I_NONE, BUS_USB, CHIP_IPACX,
+ 1, 2,  0,  8,   0
+},
+{ /* 63 (Diva Server BRI-2M 2.0 PCI adapter enabled for Voice) */
+ "Diva Server Voice BRI-2M 2.0 PCI", 0xE01B,  0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAE2,   CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2,   16,  8,   0
+},
+{ /* 64 */
+ "Diva Pro 3.0 PCI",    0xe00d,  0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_PRO,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1,  2,  0,  0,   0
+},
+{ /* 65 */
+ "Diva ISDN + CT 2.0",    0xE00E,  0x0300,
+ IDI_ADAPTER_DIVA   ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT,       CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1,  2,  0,  0,   0
+},
+{ /* 66 */
+ "Diva Mobile V.90 PC Card",  0x8331,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_IPACX,
+ 1, 2,  0,   8,   0
+},
+{ /* 67 */
+ "Diva ISDN PC Card",  0x8311,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO,   CARD_I_NONE, BUS_PCM, CHIP_IPACX,
+ 1, 2,  0,   8,   0
+},
+{ /* 68 */
+ "Diva ISDN PC Card",  0x0000,  0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PRO,   CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2,   0,   8,      0
+},
+} ;
+#if CARDTYPE_H_WANT_RESOURCE_DATA
+/*--- CardResource [Index=CARDTYPE_....]   ---------------------------(GEI)-*/
+CARD_RESOURCE CardResource [ ] =  {
+/*   Interrupts     IO-Address   Mem-Address */
+/* 0*/ {  3,4,9,0,0,0,0,0,0,0,   0x200,0x20,16,   0x0,0x0,0   }, // DIVA MCA
+/* 1*/ {  3,4,9,10,11,12,0,0,0,0,  0x200,0x20,16,   0x0,0x0,0   }, // DIVA ISA
+/* 2*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PCMCIA
+/* 3*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // DIVA PRO ISA
+/* 4*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PRO PCMCIA
+/* 5*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA PICCOLA ISA
+/* 6*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA PICCOLA PCMCIA
+/* 7*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PRO 2.0 PCI
+/* 8*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.0 PCI
+/* 9*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0x80000,0x2000,64 }, // QUADRO ISA
+/*10*/ {  3,4,9,10,11,12,0,0,0,0,  0x0,0x0,0,   0xc0000,0x2000,16 }, // S ISA
+/*11*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // S MCA
+/*12*/ {  3,4,9,10,11,12,0,0,0,0,  0x0,0x0,0,   0xc0000,0x2000,16 }, // SX ISA
+/*13*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // SX MCA
+/*14*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0x80000,0x0800,256 }, // SXN ISA
+/*15*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // SXN MCA
+/*16*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0x80000,0x0800,256 }, // SCOM ISA
+/*17*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x2000,16 }, // SCOM MCA
+/*18*/ {  3,4,5,7,9,10,11,12,0,0,  0x0,0x0,0,   0xc0000,0x4000,16 }, // S2M ISA
+/*19*/ {  3,4,9,0,0,0,0,0,0,0,  0xc00,0x10,16,  0xc0000,0x4000,16 }, // S2M MCA
+/*20*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // MAESTRA ISA
+/*21*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA PCI
+/*22*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // MAESTRA QUADRO ISA
+/*23*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA QUADRO PCI
+/*24*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // MAESTRA PRIMARY ISA
+/*25*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA PRIMARY PCI
+/*26*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.0 ISA
+/*27*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.0 /U ISA
+/*28*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.0 /U PCI
+/*29*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // DIVA PRO 2.0 ISA
+/*30*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // DIVA PRO 2.0 /U ISA
+/*31*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA PRO 2.0 /U PCI
+/*32*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA MOBILE
+/*33*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // TDK DFI3600 (same as DIVA MOBILE [32])
+/*34*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // New Media ISDN (same as DIVA PRO PCMCIA [4])
+/*35*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // BT ExLane PCI (same as DIVA PRO 2.0 PCI [7])
+/*36*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,   0x0,0x0,0   }, // BT ExLane ISA (same as DIVA PRO 2.0 ISA [29])
+/*37*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.01 S/T ISA
+/*38*/ {  3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16,  0x0,0x0,0   }, // DIVA 2.01 U ISA
+/*39*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.01 S/T PCI
+/*40*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.01 U PCI
+/*41*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA MOBILE V.90
+/*42*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // TDK DFI3600 V.90 (same as DIVA MOBILE V.90 [39])
+/*43*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // DIVA Server PRI-23M PCI
+/*44*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA 2.01 S/T USB
+/*45*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT S/T PCI
+/*46*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT U PCI
+/*47*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT Lite S/T PCI
+/*48*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT Lite U PCI
+/*49*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA ISDN+V.90 PC Card
+/*50*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA ISDN+V.90 PCI
+/*51*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA TA
+/*52*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA VOICE QUADRO PCI
+/*53*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA VOICE QUADRO PCI
+/*54*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA VOICE PRIMARY PCI
+/*55*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048,  0x0,0x0,0   }, // MAESTRA VOICE QUADRO PCI
+/*56*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // MAESTRA VOICE PRIMARY PCI
+/*57*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA LAN
+/*58*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.02 S/T PCI
+/*59*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 2.02 U PCI
+/*60*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // Diva Server BRI-2M 2.0 PCI
+/*61*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // Diva Server BRI-2F PCI
+/*62*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA 2.01 S/T USB
+/*63*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // Diva Server Voice BRI-2M 2.0 PCI
+/*64*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA 3.0 PCI
+/*65*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA CT S/T PCI V2.0
+/*66*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA Mobile V.90 PC Card
+/*67*/ {  0,0,0,0,0,0,0,0,0,0,  0x0,0x0,0,   0x0,0x0,0   }, // DIVA ISDN PC Card
+/*68*/ {  3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192,  0x0,0x0,0   }, // DIVA ISDN PC Card
+};
+#endif /*CARDTYPE_H_WANT_RESOURCE_DATA*/
+#else /*!CARDTYPE_H_WANT_DATA*/
+extern CARD_PROPERTIES  CardProperties [] ;
+extern CARD_RESOURCE  CardResource [] ;
+#endif /*CARDTYPE_H_WANT_DATA*/
+/*
+ * all existing download files
+ */
+#define CARD_DSP_CNT  5
+#define CARD_PROT_CNT  9
+#define CARD_FT_UNKNOWN     0
+#define CARD_FT_B   1
+#define CARD_FT_D   2
+#define CARD_FT_S   3
+#define CARD_FT_M   4
+#define CARD_FT_NEW_DSP_COMBIFILE 5  /* File format of new DSP code (the DSP code powered by Telindus) */
+#define CARD_FILE_NONE      0
+#define CARD_B_S   1
+#define CARD_B_P   2
+#define CARD_D_K1   3
+#define CARD_D_K2   4
+#define CARD_D_H   5
+#define CARD_D_V   6
+#define CARD_D_M   7
+#define CARD_D_F   8
+#define CARD_P_S_E   9
+#define CARD_P_S_1   10
+#define CARD_P_S_B   11
+#define CARD_P_S_F   12
+#define CARD_P_S_A   13
+#define CARD_P_S_N   14
+#define CARD_P_S_5   15
+#define CARD_P_S_J   16
+#define CARD_P_SX_E   17
+#define CARD_P_SX_1   18
+#define CARD_P_SX_B   19
+#define CARD_P_SX_F   20
+#define CARD_P_SX_A   21
+#define CARD_P_SX_N   22
+#define CARD_P_SX_5   23
+#define CARD_P_SX_J   24
+#define CARD_P_SY_E   25
+#define CARD_P_SY_1   26
+#define CARD_P_SY_B   27
+#define CARD_P_SY_F   28
+#define CARD_P_SY_A   29
+#define CARD_P_SY_N   30
+#define CARD_P_SY_5   31
+#define CARD_P_SY_J   32
+#define CARD_P_SQ_E   33
+#define CARD_P_SQ_1   34
+#define CARD_P_SQ_B   35
+#define CARD_P_SQ_F   36
+#define CARD_P_SQ_A   37
+#define CARD_P_SQ_N   38
+#define CARD_P_SQ_5   39
+#define CARD_P_SQ_J   40
+#define CARD_P_P_E   41
+#define CARD_P_P_1   42
+#define CARD_P_P_B   43
+#define CARD_P_P_F   44
+#define CARD_P_P_A   45
+#define CARD_P_P_N   46
+#define CARD_P_P_5   47
+#define CARD_P_P_J   48
+#define CARD_P_M_E   49
+#define CARD_P_M_1   50
+#define CARD_P_M_B   51
+#define CARD_P_M_F   52
+#define CARD_P_M_A   53
+#define CARD_P_M_N   54
+#define CARD_P_M_5   55
+#define CARD_P_M_J   56
+#define CARD_P_S_S   57
+#define CARD_P_SX_S   58
+#define CARD_P_SY_S   59
+#define CARD_P_SQ_S   60
+#define CARD_P_P_S   61
+#define CARD_P_M_S   62
+#define CARD_D_NEW_DSP_COMBIFILE 63
+typedef struct CARD_FILES_DATA
+{
+ char *    Name;
+ unsigned char  Type;
+}
+CARD_FILES_DATA;
+typedef struct CARD_FILES
+{
+ unsigned char  Boot;
+ unsigned char  Dsp  [CARD_DSP_CNT];
+ unsigned char  DspTelindus;
+ unsigned char  Prot [CARD_PROT_CNT];
+}
+CARD_FILES;
+#if CARDTYPE_H_WANT_DATA
+#if CARDTYPE_H_WANT_FILE_DATA
+CARD_FILES_DATA CardFData [] =  {
+// Filename   Filetype
+ 0,     CARD_FT_UNKNOWN,
+ "didnload.bin",  CARD_FT_B,
+ "diprload.bin",  CARD_FT_B,
+ "didiva.bin",  CARD_FT_D,
+ "didivapp.bin",  CARD_FT_D,
+ "dihscx.bin",  CARD_FT_D,
+ "div110.bin",  CARD_FT_D,
+ "dimodem.bin",  CARD_FT_D,
+ "difax.bin",  CARD_FT_D,
+ "di_etsi.bin",  CARD_FT_S,
+ "di_1tr6.bin",  CARD_FT_S,
+ "di_belg.bin",  CARD_FT_S,
+ "di_franc.bin",  CARD_FT_S,
+ "di_atel.bin",  CARD_FT_S,
+ "di_ni.bin",  CARD_FT_S,
+ "di_5ess.bin",  CARD_FT_S,
+ "di_japan.bin",  CARD_FT_S,
+ "di_etsi.sx",  CARD_FT_S,
+ "di_1tr6.sx",  CARD_FT_S,
+ "di_belg.sx",  CARD_FT_S,
+ "di_franc.sx",  CARD_FT_S,
+ "di_atel.sx",  CARD_FT_S,
+ "di_ni.sx",   CARD_FT_S,
+ "di_5ess.sx",  CARD_FT_S,
+ "di_japan.sx",  CARD_FT_S,
+ "di_etsi.sy",  CARD_FT_S,
+ "di_1tr6.sy",  CARD_FT_S,
+ "di_belg.sy",  CARD_FT_S,
+ "di_franc.sy",  CARD_FT_S,
+ "di_atel.sy",  CARD_FT_S,
+ "di_ni.sy",   CARD_FT_S,
+ "di_5ess.sy",  CARD_FT_S,
+ "di_japan.sy",  CARD_FT_S,
+ "di_etsi.sq",  CARD_FT_S,
+ "di_1tr6.sq",  CARD_FT_S,
+ "di_belg.sq",  CARD_FT_S,
+ "di_franc.sq",  CARD_FT_S,
+ "di_atel.sq",  CARD_FT_S,
+ "di_ni.sq",   CARD_FT_S,
+ "di_5ess.sq",  CARD_FT_S,
+ "di_japan.sq",  CARD_FT_S,
+ "di_etsi.p",  CARD_FT_S,
+ "di_1tr6.p",  CARD_FT_S,
+ "di_belg.p",  CARD_FT_S,
+ "di_franc.p",  CARD_FT_S,
+ "di_atel.p",  CARD_FT_S,
+ "di_ni.p",   CARD_FT_S,
+ "di_5ess.p",  CARD_FT_S,
+ "di_japan.p",  CARD_FT_S,
+ "di_etsi.sm",  CARD_FT_M,
+ "di_1tr6.sm",  CARD_FT_M,
+ "di_belg.sm",  CARD_FT_M,
+ "di_franc.sm",  CARD_FT_M,
+ "di_atel.sm",  CARD_FT_M,
+ "di_ni.sm",   CARD_FT_M,
+ "di_5ess.sm",  CARD_FT_M,
+ "di_japan.sm",  CARD_FT_M,
+ "di_swed.bin",  CARD_FT_S,
+ "di_swed.sx",  CARD_FT_S,
+ "di_swed.sy",  CARD_FT_S,
+ "di_swed.sq",  CARD_FT_S,
+ "di_swed.p",  CARD_FT_S,
+ "di_swed.sm",  CARD_FT_M,
+    "didspdld.bin",     CARD_FT_NEW_DSP_COMBIFILE
+};
+CARD_FILES CardFiles [] =
+{
+ { /* CARD_UNKNOWN */
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE
+ },
+ { /* CARD_DIVA */
+  CARD_FILE_NONE,
+  CARD_D_K1, CARD_D_H, CARD_D_V, CARD_FILE_NONE, CARD_D_F,
+  CARD_D_NEW_DSP_COMBIFILE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE
+ },
+ { /* CARD_PRO  */
+  CARD_FILE_NONE,
+  CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F,
+  CARD_D_NEW_DSP_COMBIFILE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE
+ },
+ { /* CARD_PICO */
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE
+ },
+ { /* CARD_S    */
+  CARD_B_S,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_P_S_E, CARD_P_S_1, CARD_P_S_B, CARD_P_S_F,
+  CARD_P_S_A, CARD_P_S_N, CARD_P_S_5, CARD_P_S_J,
+  CARD_P_S_S
+ },
+ { /* CARD_SX   */
+  CARD_B_S,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_P_SX_E, CARD_P_SX_1, CARD_P_SX_B, CARD_P_SX_F,
+  CARD_P_SX_A, CARD_P_SX_N, CARD_P_SX_5, CARD_P_SX_J,
+  CARD_P_SX_S
+ },
+ { /* CARD_SXN  */
+  CARD_B_S,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F,
+  CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J,
+  CARD_P_SY_S
+ },
+ { /* CARD_SCOM */
+  CARD_B_S,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F,
+  CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J,
+  CARD_P_SY_S
+ },
+ { /* CARD_QUAD */
+  CARD_B_S,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_P_SQ_E, CARD_P_SQ_1, CARD_P_SQ_B, CARD_P_SQ_F,
+  CARD_P_SQ_A, CARD_P_SQ_N, CARD_P_SQ_5, CARD_P_SQ_J,
+  CARD_P_SQ_S
+ },
+ { /* CARD_PR   */
+  CARD_B_P,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_P_P_E, CARD_P_P_1, CARD_P_P_B, CARD_P_P_F,
+  CARD_P_P_A, CARD_P_P_N, CARD_P_P_5, CARD_P_P_J,
+  CARD_P_P_S
+ },
+ { /* CARD_MAE  */
+  CARD_FILE_NONE,
+  CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F,
+  CARD_D_NEW_DSP_COMBIFILE,
+  CARD_P_M_E, CARD_P_M_1, CARD_P_M_B, CARD_P_M_F,
+  CARD_P_M_A, CARD_P_M_N, CARD_P_M_5, CARD_P_M_J,
+  CARD_P_M_S
+ },
+ { /* CARD_MAEQ */  /* currently not supported */
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE
+ },
+ { /* CARD_MAEP */  /* currently not supported */
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+  CARD_FILE_NONE
+ }
+};
+#endif /*CARDTYPE_H_WANT_FILE_DATA*/
+#else /*!CARDTYPE_H_WANT_DATA*/
+extern CARD_FILES_DATA  CardFData [] ;
+extern CARD_FILES   CardFiles [] ;
+#endif /*CARDTYPE_H_WANT_DATA*/
+#endif /* _CARDTYPE_H_ */
diff --git a/drivers/isdn/hardware/eicon/cp_vers.h b/drivers/isdn/hardware/eicon/cp_vers.h
new file mode 100644
index 0000000..cb5ada3
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/cp_vers.h
@@ -0,0 +1,26 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+static char diva_capi_common_code_build[] = "102-28";  
diff --git a/drivers/isdn/hardware/eicon/dadapter.c b/drivers/isdn/hardware/eicon/dadapter.c
new file mode 100644
index 0000000..6e548a2
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dadapter.c
@@ -0,0 +1,366 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "pc.h"
+#include "debuglib.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "dadapter.h"
+/* --------------------------------------------------------------------------
+  Adapter array change notification framework
+   -------------------------------------------------------------------------- */
+typedef struct _didd_adapter_change_notification {
+ didd_adapter_change_callback_t callback;
+ void IDI_CALL_ENTITY_T *    context;
+} didd_adapter_change_notification_t, \
+ * IDI_CALL_ENTITY_T pdidd_adapter_change_notification_t;
+#define DIVA_DIDD_MAX_NOTIFICATIONS 256
+static didd_adapter_change_notification_t\
+              NotificationTable[DIVA_DIDD_MAX_NOTIFICATIONS];
+/* --------------------------------------------------------------------------
+  Array to held adapter information
+   -------------------------------------------------------------------------- */
+static DESCRIPTOR  HandleTable[NEW_MAX_DESCRIPTORS];
+dword Adapters = 0; /* Number of adapters */
+/* --------------------------------------------------------------------------
+  Shadow IDI_DIMAINT
+  and 'shadow' debug stuff
+   -------------------------------------------------------------------------- */
+static void no_printf (unsigned char * format, ...)
+{
+#ifdef EBUG
+	va_list ap;
+	va_start (ap, format);
+	debug((format, ap));
+	va_end (ap);
+#endif
+}
+
+/* -------------------------------------------------------------------------
+  Portable debug Library
+  ------------------------------------------------------------------------- */
+#include "debuglib.c"
+  
+static DESCRIPTOR  MAdapter =  {IDI_DIMAINT, /* Adapter Type */
+                0x00,     /* Channels */
+                0x0000,    /* Features */
+                (IDI_CALL)no_printf};
+/* --------------------------------------------------------------------------
+  DAdapter. Only IDI clients with buffer, that is huge enough to
+  get all descriptors will receive information about DAdapter
+  { byte type, byte channels, word features, IDI_CALL request }
+   -------------------------------------------------------------------------- */
+static void IDI_CALL_LINK_T diva_dadapter_request (ENTITY IDI_CALL_ENTITY_T *);
+static DESCRIPTOR  DAdapter =  {IDI_DADAPTER, /* Adapter Type */
+                0x00,     /* Channels */
+                0x0000,    /* Features */
+                diva_dadapter_request };
+/* --------------------------------------------------------------------------
+  LOCALS
+   -------------------------------------------------------------------------- */
+static dword diva_register_adapter_callback (\
+                   didd_adapter_change_callback_t callback,
+                   void IDI_CALL_ENTITY_T* context);
+static void diva_remove_adapter_callback (dword handle);
+static void diva_notify_adapter_change (DESCRIPTOR* d, int removal);
+static diva_os_spin_lock_t didd_spin;
+/* --------------------------------------------------------------------------
+  Should be called as first step, after driver init
+  -------------------------------------------------------------------------- */
+void diva_didd_load_time_init (void) {
+ memset (&HandleTable[0], 0x00, sizeof(HandleTable));
+ memset (&NotificationTable[0], 0x00, sizeof(NotificationTable));
+ diva_os_initialize_spin_lock (&didd_spin, "didd");
+}
+/* --------------------------------------------------------------------------
+  Should be called as last step, if driver does unload
+  -------------------------------------------------------------------------- */
+void diva_didd_load_time_finit (void) {
+ diva_os_destroy_spin_lock (&didd_spin, "didd");
+}
+/* --------------------------------------------------------------------------
+  Called in order to register new adapter in adapter array
+  return adapter handle (> 0) on success
+  return -1 adapter array overflow
+  -------------------------------------------------------------------------- */
+static int diva_didd_add_descriptor (DESCRIPTOR* d) {
+ diva_os_spin_lock_magic_t      irql;
+ int i;
+ if (d->type == IDI_DIMAINT) {
+  if (d->request) {
+   MAdapter.request = d->request;
+   dprintf = (DIVA_DI_PRINTF)d->request;
+   diva_notify_adapter_change (&MAdapter, 0); /* Inserted */
+   DBG_TRC (("DIMAINT registered, dprintf=%08x", d->request))
+  } else {
+   DBG_TRC (("DIMAINT removed"))
+   diva_notify_adapter_change (&MAdapter, 1); /* About to remove */
+   MAdapter.request = (IDI_CALL)no_printf;
+   dprintf = no_printf;
+  }
+  return (NEW_MAX_DESCRIPTORS);
+ }
+ for (i = 0; i < NEW_MAX_DESCRIPTORS; i++) {
+  diva_os_enter_spin_lock (&didd_spin, &irql, "didd_add");
+  if (HandleTable[i].type == 0) {
+   memcpy (&HandleTable[i], d, sizeof(*d));
+   Adapters++;
+   diva_os_leave_spin_lock (&didd_spin, &irql, "didd_add");
+   diva_notify_adapter_change (d, 0); /* we have new adapter */
+   DBG_TRC (("Add adapter[%d], request=%08x", (i+1), d->request))
+   return (i+1);
+  }
+  diva_os_leave_spin_lock (&didd_spin, &irql, "didd_add");
+ }
+ DBG_ERR (("Can't add adapter, out of resources"))
+ return (-1);
+}
+/* --------------------------------------------------------------------------
+  Called in order to remove one registered adapter from array
+  return adapter handle (> 0) on success
+  return 0 on success
+  -------------------------------------------------------------------------- */
+static int diva_didd_remove_descriptor (IDI_CALL request) {
+ diva_os_spin_lock_magic_t      irql;
+ int i;
+ if (request == MAdapter.request) {
+  DBG_TRC(("DIMAINT removed"))
+  dprintf = no_printf;
+  diva_notify_adapter_change (&MAdapter, 1); /* About to remove */
+  MAdapter.request = (IDI_CALL)no_printf;
+  return (0);
+ }
+ for (i = 0; (Adapters && (i < NEW_MAX_DESCRIPTORS)); i++) {
+  if (HandleTable[i].request == request) {
+   diva_notify_adapter_change (&HandleTable[i], 1); /* About to remove */
+   diva_os_enter_spin_lock (&didd_spin, &irql, "didd_rm");
+   memset (&HandleTable[i], 0x00, sizeof(HandleTable[0]));
+   Adapters--;
+   diva_os_leave_spin_lock (&didd_spin, &irql, "didd_rm");
+   DBG_TRC (("Remove adapter[%d], request=%08x", (i+1), request))
+   return (0);
+  }
+ }
+ DBG_ERR (("Invalid request=%08x, can't remove adapter", request))
+ return (-1);
+}
+/* --------------------------------------------------------------------------
+  Read adapter array
+  return 1 if not enough space to save all available adapters
+   -------------------------------------------------------------------------- */
+static int diva_didd_read_adapter_array (DESCRIPTOR* buffer, int length) {
+ diva_os_spin_lock_magic_t      irql;
+ int src, dst;
+ memset (buffer, 0x00, length);
+ length /= sizeof(DESCRIPTOR);
+ DBG_TRC (("DIDD_Read, space = %d, Adapters = %d", length, Adapters+2))
+ 
+ diva_os_enter_spin_lock (&didd_spin, &irql, "didd_read");
+ for (src = 0, dst = 0;
+    (Adapters && (src < NEW_MAX_DESCRIPTORS) && (dst < length));
+    src++) {
+  if (HandleTable[src].type) {
+   memcpy (&buffer[dst], &HandleTable[src], sizeof(DESCRIPTOR));
+   dst++;
+  }
+ }
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_read");
+ if (dst < length) {
+  memcpy (&buffer[dst], &MAdapter, sizeof(DESCRIPTOR));
+  dst++;
+ } else {
+  DBG_ERR (("Can't write DIMAINT. Array too small"))
+ }
+ if (dst < length) {
+  memcpy (&buffer[dst], &DAdapter, sizeof(DESCRIPTOR));
+  dst++;
+ } else {
+  DBG_ERR (("Can't write DADAPTER. Array too small"))
+ }
+ DBG_TRC (("Read %d adapters", dst))
+ return (dst == length);
+}
+/* --------------------------------------------------------------------------
+  DAdapter request function.
+  This function does process only synchronous requests, and is used
+  for reception/registration of new interfaces
+   -------------------------------------------------------------------------- */
+static void IDI_CALL_LINK_T diva_dadapter_request (\
+                      ENTITY IDI_CALL_ENTITY_T *e) {
+ IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e ;
+ if (e->Req) { /* We do not process it, also return error */
+  e->Rc = OUT_OF_RESOURCES;
+  DBG_ERR (("Can't process async request, Req=%02x", e->Req))
+  return;
+ }
+ /*
+  So, we process sync request
+  */
+ switch (e->Rc) {
+  case IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY: {
+   diva_didd_adapter_notify_t* pinfo = &syncReq->didd_notify.info;
+   pinfo->handle = diva_register_adapter_callback (\
+             (didd_adapter_change_callback_t)pinfo->callback,
+             (void IDI_CALL_ENTITY_T *)pinfo->context);
+   e->Rc = 0xff;
+  } break;
+  case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY: {
+   diva_didd_adapter_notify_t* pinfo = &syncReq->didd_notify.info;
+   diva_remove_adapter_callback (pinfo->handle);
+   e->Rc = 0xff;
+  } break;
+  case IDI_SYNC_REQ_DIDD_ADD_ADAPTER: {
+   diva_didd_add_adapter_t* pinfo = &syncReq->didd_add_adapter.info;
+   if (diva_didd_add_descriptor ((DESCRIPTOR*)pinfo->descriptor) < 0) {
+    e->Rc = OUT_OF_RESOURCES;
+   } else {
+    e->Rc = 0xff;
+   }
+  } break;
+  case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER: {
+   diva_didd_remove_adapter_t* pinfo = &syncReq->didd_remove_adapter.info;
+   if (diva_didd_remove_descriptor ((IDI_CALL)pinfo->p_request) < 0) {
+    e->Rc = OUT_OF_RESOURCES;
+   } else {
+    e->Rc = 0xff;
+   }
+  } break;
+  case IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY: {
+   diva_didd_read_adapter_array_t* pinfo =\
+                &syncReq->didd_read_adapter_array.info;
+   if (diva_didd_read_adapter_array ((DESCRIPTOR*)pinfo->buffer,
+                  (int)pinfo->length)) {
+    e->Rc = OUT_OF_RESOURCES;
+   } else {
+    e->Rc = 0xff;
+   }
+  } break;
+  default:
+   DBG_ERR (("Can't process sync request, Req=%02x", e->Rc))
+   e->Rc = OUT_OF_RESOURCES;
+ }
+}
+/* --------------------------------------------------------------------------
+  IDI client does register his notification function
+  -------------------------------------------------------------------------- */
+static dword diva_register_adapter_callback (\
+                   didd_adapter_change_callback_t callback,
+                   void IDI_CALL_ENTITY_T* context) {
+ diva_os_spin_lock_magic_t irql;
+ dword i;
+ 
+ for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
+  diva_os_enter_spin_lock (&didd_spin, &irql, "didd_nfy_add");
+  if (!NotificationTable[i].callback) {
+   NotificationTable[i].callback = callback;
+   NotificationTable[i].context = context;
+   diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy_add");
+   DBG_TRC(("Register adapter notification[%d]=%08x", i+1, callback))
+   return (i+1);
+  }
+  diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy_add");
+ }
+ DBG_ERR (("Can't register adapter notification, overflow"))
+ return (0);
+}
+/* --------------------------------------------------------------------------
+  IDI client does register his notification function
+  -------------------------------------------------------------------------- */
+static void diva_remove_adapter_callback (dword handle) {
+ diva_os_spin_lock_magic_t irql;
+ if (handle && ((--handle) < DIVA_DIDD_MAX_NOTIFICATIONS)) {
+  diva_os_enter_spin_lock (&didd_spin, &irql, "didd_nfy_rm");
+  NotificationTable[handle].callback = NULL;
+  NotificationTable[handle].context  = NULL;
+  diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy_rm");
+  DBG_TRC(("Remove adapter notification[%d]", (int)(handle+1)))
+  return;
+ }
+ DBG_ERR(("Can't remove adapter notification, handle=%d", handle))
+}
+/* --------------------------------------------------------------------------
+  Notify all client about adapter array change
+  Does suppose following behavior in the client side:
+  Step 1: Redister Notification
+  Step 2: Read Adapter Array
+  -------------------------------------------------------------------------- */
+static void diva_notify_adapter_change (DESCRIPTOR* d, int removal) {
+ int i, do_notify;
+ didd_adapter_change_notification_t nfy;
+ diva_os_spin_lock_magic_t irql;
+ for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
+  do_notify = 0;
+  diva_os_enter_spin_lock (&didd_spin, &irql, "didd_nfy");
+  if (NotificationTable[i].callback) {
+   memcpy (&nfy, &NotificationTable[i], sizeof(nfy));
+   do_notify = 1;
+  }
+  diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy");
+  if (do_notify) {
+   (*(nfy.callback))(nfy.context, d, removal);
+  }
+ }
+}
+/* --------------------------------------------------------------------------
+  For all systems, that are linked by Kernel Mode Linker this is ONLY one
+  function thet should be exported by this device driver
+  IDI clients should look for IDI_DADAPTER, and use request function
+  of this adapter (sync request) in order to receive appropriate services:
+  - add new adapter
+  - remove existing adapter
+  - add adapter array notification
+  - remove adapter array notification
+  (read adapter is redundant in this case)
+  INPUT:
+   buffer - pointer to buffer that will receive adapter array
+   length  - length (in bytes) of space in buffer
+  OUTPUT:
+   Adapter array will be written to memory described by 'buffer'
+   If the last adapter seen in the returned adapter array is
+   IDI_DADAPTER or if last adapter in array does have type '0', then
+   it was enougth space in buffer to accommodate all available
+   adapter descriptors
+  *NOTE 1 (debug interface):
+   The IDI adapter of type 'IDI_DIMAINT' does register as 'request'
+   famous 'dprintf' function (of type DI_PRINTF, please look
+   include/debuglib.c and include/debuglib.h) for details.
+   So dprintf is not exported from module debug module directly,
+   instead of this IDI_DIMAINT is registered.
+   Module load order will receive in this case:
+    1. DIDD (this file)
+    2. DIMAINT does load and register 'IDI_DIMAINT', at this step
+      DIDD should be able to get 'dprintf', save it, and
+      register with DIDD by means of 'dprintf' function.
+    3. any other driver is loaded and is able to access adapter array
+      and debug interface
+   This approach does allow to load/unload debug interface on demand,
+   and save memory, it it is necessary.
+  -------------------------------------------------------------------------- */
+void IDI_CALL_LINK_T DIVA_DIDD_Read (void IDI_CALL_ENTITY_T * buffer,
+                   int length) {
+ diva_didd_read_adapter_array (buffer, length);
+}
+
diff --git a/drivers/isdn/hardware/eicon/dadapter.h b/drivers/isdn/hardware/eicon/dadapter.h
new file mode 100644
index 0000000..3575ac9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dadapter.h
@@ -0,0 +1,34 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_DIDD_DADAPTER_INC__
+#define __DIVA_DIDD_DADAPTER_INC__
+ 
+void diva_didd_load_time_init (void);
+void diva_didd_load_time_finit (void);
+
+#define NEW_MAX_DESCRIPTORS     64
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/dbgioctl.h b/drivers/isdn/hardware/eicon/dbgioctl.h
new file mode 100644
index 0000000..0fb6f5e
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dbgioctl.h
@@ -0,0 +1,198 @@
+
+/*
+ *
+  Copyright (c) Eicon Technology Corporation, 2000.
+ *
+  This source file is supplied for the use with Eicon
+  Technology Corporation's range of DIVA Server Adapters.
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the GNU General Public License for more details.
+ *
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+/*------------------------------------------------------------------*/
+/* file: dbgioctl.h                                                 */
+/*------------------------------------------------------------------*/
+
+#if !defined(__DBGIOCTL_H__)
+
+#define __DBGIOCTL_H__
+
+#ifdef NOT_YET_NEEDED
+/*
+ * The requested operation is passed in arg0 of DbgIoctlArgs,
+ * additional arguments (if any) in arg1, arg2 and arg3.
+ */
+
+typedef struct
+{	ULONG	arg0 ;
+	ULONG	arg1 ;
+	ULONG	arg2 ;
+	ULONG	arg3 ;
+} DbgIoctlArgs ;
+
+#define	DBG_COPY_LOGS	0	/* copy debugs to user until buffer full	*/
+							/* arg1: size threshold						*/
+							/* arg2: timeout in milliseconds			*/
+
+#define DBG_FLUSH_LOGS	1	/* flush pending debugs to user buffer		*/
+							/* arg1: internal driver id					*/
+
+#define DBG_LIST_DRVS	2	/* return the list of registered drivers	*/
+
+#define	DBG_GET_MASK	3	/* get current debug mask of driver			*/
+							/* arg1: internal driver id					*/
+
+#define	DBG_SET_MASK	4	/* set/change debug mask of driver			*/
+							/* arg1: internal driver id					*/
+							/* arg2: new debug mask						*/
+
+#define	DBG_GET_BUFSIZE	5	/* get current buffer size of driver		*/
+							/* arg1: internal driver id					*/
+							/* arg2: new debug mask						*/
+
+#define	DBG_SET_BUFSIZE	6	/* set new buffer size of driver			*/
+							/* arg1: new buffer size					*/
+
+/*
+ *	common internal debug message structure
+ */
+
+typedef struct
+{	unsigned short id ;		/* virtual driver id                  */
+	unsigned short type ;	/* special message type               */
+	unsigned long  seq ;	/* sequence number of message         */
+	unsigned long  size ;	/* size of message in bytes           */
+	unsigned long  next ;	/* offset to next buffered message    */
+	LARGE_INTEGER  NTtime ;	/* 100 ns  since 1.1.1601             */
+	unsigned char  data[4] ;/* message data                       */
+} OldDbgMessage ;
+
+typedef struct
+{	LARGE_INTEGER  NTtime ;	/* 100 ns  since 1.1.1601             */
+	unsigned short size ;	/* size of message in bytes           */
+	unsigned short ffff ;	/* always 0xffff to indicate new msg  */
+	unsigned short id ;		/* virtual driver id                  */
+	unsigned short type ;	/* special message type               */
+	unsigned long  seq ;	/* sequence number of message         */
+	unsigned char  data[4] ;/* message data                       */
+} DbgMessage ;
+
+#endif
+
+#define DRV_ID_UNKNOWN		0x0C	/* for messages via prtComp() */
+
+#define	MSG_PROC_FLAG		0x80
+#define	MSG_PROC_NO_GET(x)	(((x) & MSG_PROC_FLAG) ? (((x) >> 4) & 7) : -1)
+#define	MSG_PROC_NO_SET(x)	(MSG_PROC_FLAG | (((x) & 7) << 4))
+
+#define MSG_TYPE_DRV_ID		0x0001
+#define MSG_TYPE_FLAGS		0x0002
+#define MSG_TYPE_STRING		0x0003
+#define MSG_TYPE_BINARY		0x0004
+
+#define MSG_HEAD_SIZE	((unsigned long)&(((DbgMessage *)0)->data[0]))
+#define MSG_ALIGN(len)	(((unsigned long)(len) + MSG_HEAD_SIZE + 3) & ~3)
+#define MSG_SIZE(pMsg)	MSG_ALIGN((pMsg)->size)
+#define MSG_NEXT(pMsg)	((DbgMessage *)( ((char *)(pMsg)) + MSG_SIZE(pMsg) ))
+
+#define OLD_MSG_HEAD_SIZE	((unsigned long)&(((OldDbgMessage *)0)->data[0]))
+#define OLD_MSG_ALIGN(len)	(((unsigned long)(len)+OLD_MSG_HEAD_SIZE+3) & ~3)
+
+/*
+ * manifest constants
+ */
+
+#define MSG_FRAME_MAX_SIZE	2150		/* maximum size of B1 frame	 */
+#define MSG_TEXT_MAX_SIZE	1024		/* maximum size of msg text	 */
+#define MSG_MAX_SIZE		MSG_ALIGN(MSG_FRAME_MAX_SIZE)
+#define DBG_MIN_BUFFER_SIZE	0x00008000	/* minimal total buffer size  32 KB */
+#define DBG_DEF_BUFFER_SIZE	0x00020000	/* default total buffer size 128 KB */
+#define DBG_MAX_BUFFER_SIZE	0x00400000	/* maximal total buffer size   4 MB */
+
+#define DBGDRV_NAME		"Diehl_DIMAINT"
+#define UNIDBG_DRIVER	L"\\Device\\Diehl_DIMAINT" /* UNICODE name for kernel */
+#define DEBUG_DRIVER	"\\\\.\\" DBGDRV_NAME  /* traditional string for apps */
+#define DBGVXD_NAME		"DIMAINT"
+#define DEBUG_VXD		"\\\\.\\" DBGVXD_NAME  /* traditional string for apps */
+
+/*
+ *	Special IDI interface debug construction
+ */
+
+#define	DBG_IDI_SIG_REQ		(unsigned long)0xF479C402
+#define	DBG_IDI_SIG_IND		(unsigned long)0xF479C403
+#define	DBG_IDI_NL_REQ		(unsigned long)0xF479C404
+#define	DBG_IDI_NL_IND		(unsigned long)0xF479C405
+
+typedef struct
+{	unsigned long  magic_type ;
+	unsigned short data_len ;
+	unsigned char  layer_ID ;
+	unsigned char  entity_ID ;
+	unsigned char  request ;
+	unsigned char  ret_code ;
+	unsigned char  indication ;
+	unsigned char  complete ;
+	unsigned char  data[4] ;
+} DbgIdiAct, *DbgIdiAction ;
+
+/*
+ * We want to use the same IOCTL codes in Win95 and WinNT.
+ * The official constructor for IOCTL codes is the CTL_CODE macro
+ * from <winoctl.h> (<devioctl.h> in WinNT DDK environment).
+ * The problem here is that we don't know how to get <winioctl.h>
+ * working in a Win95 DDK environment!
+ */
+
+# ifdef CTL_CODE	/*{*/
+
+/* Assert that we have the same idea of the CTL_CODE macro.	*/
+
+#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
+    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
+)
+
+# else	/* !CTL_CODE */ /*}{*/
+
+/* Use the definitions stolen from <winioctl.h>.  */
+
+#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
+    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
+)
+
+#define METHOD_BUFFERED                 0
+#define METHOD_IN_DIRECT                1
+#define METHOD_OUT_DIRECT               2
+#define METHOD_NEITHER                  3
+
+#define FILE_ANY_ACCESS                 0
+#define FILE_READ_ACCESS          ( 0x0001 )    // file & pipe
+#define FILE_WRITE_ACCESS         ( 0x0002 )    // file & pipe
+
+# endif	/* CTL_CODE */ /*}*/
+
+/*
+ * Now we can define WinNT/Win95 DeviceIoControl codes.
+ *
+ * These codes are defined in di_defs.h too, a possible mismatch will be
+ * detected when the dbgtool is compiled.
+ */
+
+#define IOCTL_DRIVER_LNK \
+	CTL_CODE(0x8001U,0x701,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)
+#define IOCTL_DRIVER_DBG \
+	CTL_CODE(0x8001U,0x702,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)
+
+#endif /* __DBGIOCTL_H__ */
diff --git a/drivers/isdn/hardware/eicon/debug.c b/drivers/isdn/hardware/eicon/debug.c
new file mode 100644
index 0000000..6851c62
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debug.c
@@ -0,0 +1,2133 @@
+#include "platform.h"
+#include "pc.h"
+#include "di_defs.h"
+#include "debug_if.h"
+#include "divasync.h"
+#include "kst_ifc.h"
+#include "maintidi.h"
+#include "man_defs.h"
+
+/*
+  LOCALS
+  */
+#define DBG_MAGIC (0x47114711L)
+
+static void DI_register (void *arg);
+static void DI_deregister (pDbgHandle hDbg);
+static void DI_format (int do_lock, word id, int type, char *format, va_list argument_list);
+static void DI_format_locked   (word id, int type, char *format, va_list argument_list);
+static void DI_format_old (word id, char *format, va_list ap) { }
+static void DiProcessEventLog (unsigned short id, unsigned long msgID, va_list ap) { }
+static void single_p (byte * P, word * PLength, byte Id);
+static void diva_maint_xdi_cb (ENTITY* e);
+static word SuperTraceCreateReadReq (byte* P, const char* path);
+static int diva_mnt_cmp_nmbr (const char* nmbr);
+static void diva_free_dma_descriptor (IDI_CALL request, int nr);
+static int diva_get_dma_descriptor (IDI_CALL request, dword *dma_magic);
+void diva_mnt_internal_dprintf (dword drv_id, dword type, char* p, ...);
+
+static dword MaxDumpSize = 256 ;
+static dword MaxXlogSize = 2 + 128 ;
+static char  TraceFilter[DIVA_MAX_SELECTIVE_FILTER_LENGTH+1];
+static int TraceFilterIdent   = -1;
+static int TraceFilterChannel = -1;
+
+typedef struct _diva_maint_client {
+  dword       sec;
+  dword       usec;
+  pDbgHandle  hDbg;
+  char        drvName[128];
+  dword       dbgMask;
+  dword       last_dbgMask;
+  IDI_CALL    request;
+  _DbgHandle_ Dbg;
+  int         logical;
+  int         channels;
+  diva_strace_library_interface_t* pIdiLib;
+  BUFFERS     XData;
+  char        xbuffer[2048+512];
+  byte*       pmem;
+  int         request_pending;
+  int         dma_handle;
+} diva_maint_client_t;
+static diva_maint_client_t clients[MAX_DESCRIPTORS];
+
+static void diva_change_management_debug_mask (diva_maint_client_t* pC, dword old_mask);
+
+static void diva_maint_error (void* user_context,
+                              diva_strace_library_interface_t* hLib,
+                              int Adapter,
+                              int error,
+                              const char* file,
+                              int line);
+static void diva_maint_state_change_notify (void* user_context,
+                                            diva_strace_library_interface_t* hLib,
+                                            int Adapter,
+                                            diva_trace_line_state_t* channel,
+                                            int notify_subject);
+static void diva_maint_trace_notify (void* user_context,
+                                     diva_strace_library_interface_t* hLib,
+                                     int Adapter,
+                                     void* xlog_buffer,
+                                     int length);
+
+
+
+typedef struct MSG_QUEUE {
+	dword	Size;		/* total size of queue (constant)	*/
+	byte	*Base;		/* lowest address (constant)		*/
+	byte	*High;		/* Base + Size (constant)		*/
+	byte	*Head;		/* first message in queue (if any)	*/
+	byte	*Tail;		/* first free position			*/
+	byte	*Wrap;		/* current wraparound position 		*/
+	dword	Count;		/* current no of bytes in queue		*/
+} MSG_QUEUE;
+
+typedef struct MSG_HEAD {
+	volatile dword	Size;		/* size of data following MSG_HEAD	*/
+#define	MSG_INCOMPLETE	0x8000	/* ored to Size until queueCompleteMsg 	*/
+} MSG_HEAD;
+
+#define queueCompleteMsg(p) do{ ((MSG_HEAD *)p - 1)->Size &= ~MSG_INCOMPLETE; }while(0)
+#define queueCount(q)	((q)->Count)
+#define MSG_NEED(size) \
+	( (sizeof(MSG_HEAD) + size + sizeof(dword) - 1) & ~(sizeof(dword) - 1) )
+
+static void queueInit (MSG_QUEUE *Q, byte *Buffer, dword sizeBuffer) {
+	Q->Size = sizeBuffer;
+	Q->Base = Q->Head = Q->Tail = Buffer;
+	Q->High = Buffer + sizeBuffer;
+	Q->Wrap = NULL;
+	Q->Count= 0;
+}
+
+static byte *queueAllocMsg (MSG_QUEUE *Q, word size) {
+	/* Allocate 'size' bytes at tail of queue which will be filled later
+   * directly with callers own message header info and/or message.
+   * An 'alloced' message is marked incomplete by oring the 'Size' field
+   * with MSG_INCOMPLETE.
+   * This must be reset via queueCompleteMsg() after the message is filled.
+   * As long as a message is marked incomplete queuePeekMsg() will return
+   * a 'queue empty' condition when it reaches such a message.  */
+
+	MSG_HEAD *Msg;
+	word need = MSG_NEED(size);
+
+	if (Q->Tail == Q->Head) {
+		if (Q->Wrap || need > Q->Size) {
+			return NULL; /* full */
+		}
+		goto alloc; /* empty */
+	}
+	
+	if (Q->Tail > Q->Head) {
+		if (Q->Tail + need <= Q->High) goto alloc; /* append */
+		if (Q->Base + need > Q->Head) {
+			return NULL; /* too much */
+		}
+		/* wraparound the queue (but not the message) */
+		Q->Wrap = Q->Tail;
+		Q->Tail = Q->Base;
+		goto alloc;
+	}
+
+	if (Q->Tail + need > Q->Head) {
+		return NULL; /* too much */
+	}
+
+alloc:
+	Msg = (MSG_HEAD *)Q->Tail;
+
+	Msg->Size = size | MSG_INCOMPLETE;
+
+	Q->Tail	 += need;
+	Q->Count += size;
+
+
+
+	return ((byte*)(Msg + 1));
+}
+
+static void queueFreeMsg (MSG_QUEUE *Q) {
+/* Free the message at head of queue */
+
+	word size = ((MSG_HEAD *)Q->Head)->Size & ~MSG_INCOMPLETE;
+
+	Q->Head  += MSG_NEED(size);
+	Q->Count -= size;
+
+	if (Q->Wrap) {
+		if (Q->Head >= Q->Wrap) {
+			Q->Head = Q->Base;
+			Q->Wrap = NULL;
+		}
+	} else if (Q->Head >= Q->Tail) {
+		Q->Head = Q->Tail = Q->Base;
+	}
+}
+
+static byte *queuePeekMsg (MSG_QUEUE *Q, word *size) {
+	/* Show the first valid message in queue BUT DON'T free the message.
+   * After looking on the message contents it can be freed queueFreeMsg()
+   * or simply remain in message queue.  */
+
+	MSG_HEAD *Msg = (MSG_HEAD *)Q->Head;
+
+	if (((byte *)Msg == Q->Tail && !Q->Wrap) ||
+	    (Msg->Size & MSG_INCOMPLETE)) {
+		return NULL;
+	} else {
+		*size = Msg->Size;
+		return ((byte *)(Msg + 1));
+	}
+}
+
+/*
+  Message queue header
+  */
+static MSG_QUEUE*          dbg_queue;
+static byte*               dbg_base;
+static int                 external_dbg_queue;
+static diva_os_spin_lock_t dbg_q_lock;
+static diva_os_spin_lock_t dbg_adapter_lock;
+static int                 dbg_q_busy;
+static volatile dword      dbg_sequence;
+static dword               start_sec;
+static dword               start_usec;
+
+/*
+	INTERFACE:
+    Initialize run time queue structures.
+    base:    base of the message queue
+    length:  length of the message queue
+    do_init: perfor queue reset
+
+    return:  zero on success, -1 on error
+  */
+int diva_maint_init (byte* base, unsigned long length, int do_init) {
+  if (dbg_queue || (!base) || (length < (4096*4))) {
+    return (-1);
+  }
+
+  TraceFilter[0]     =  0;
+  TraceFilterIdent   = -1;
+  TraceFilterChannel = -1;
+
+  dbg_base = base;
+
+  diva_os_get_time (&start_sec, &start_usec);
+
+  *(dword*)base  = (dword)DBG_MAGIC; /* Store Magic */
+  base   += sizeof(dword);
+  length -= sizeof(dword);
+
+  *(dword*)base = 2048; /* Extension Field Length */
+  base   += sizeof(dword);
+  length -= sizeof(dword);
+
+  strcpy (base, "KERNEL MODE BUFFER\n");
+  base   += 2048;
+  length -= 2048;
+
+  *(dword*)base = 0; /* Terminate extension */
+  base   += sizeof(dword);
+  length -= sizeof(dword);
+
+  *(void**)base  =  (void*)(base+sizeof(void*)); /* Store Base  */
+  base   += sizeof(void*);
+  length -= sizeof(void*);
+
+  dbg_queue = (MSG_QUEUE*)base;
+  queueInit (dbg_queue, base + sizeof(MSG_QUEUE), length - sizeof(MSG_QUEUE) - 512);
+  external_dbg_queue = 0;
+
+  if (!do_init) {
+    external_dbg_queue = 1; /* memory was located on the external device */
+  }
+
+
+	if (diva_os_initialize_spin_lock (&dbg_q_lock, "dbg_init")) {
+    dbg_queue = NULL;
+    dbg_base = NULL;
+    external_dbg_queue = 0;
+		return (-1);
+  }
+
+	if (diva_os_initialize_spin_lock (&dbg_adapter_lock, "dbg_init")) {
+    diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_init");
+    dbg_queue = NULL;
+    dbg_base = NULL;
+    external_dbg_queue = 0;
+		return (-1);
+  }
+
+  return (0);
+}
+
+/*
+  INTERFACE:
+    Finit at unload time
+    return address of internal queue or zero if queue
+    was external
+  */
+void* diva_maint_finit (void) {
+  void* ret = (void*)dbg_base;
+  int i;
+
+  dbg_queue = NULL;
+  dbg_base  = NULL;
+
+  if (ret) {
+    diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_finit");
+    diva_os_destroy_spin_lock(&dbg_adapter_lock, "dbg_finit");
+  }
+
+  if (external_dbg_queue) {
+    ret = NULL;
+  }
+  external_dbg_queue = 0;
+
+  for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) {
+    if (clients[i].pmem) {
+      diva_os_free (0, clients[i].pmem);
+    }
+  }
+
+  return (ret);
+}
+
+/*
+  INTERFACE:
+    Return amount of messages in debug queue
+  */
+dword diva_dbg_q_length (void) {
+	return (dbg_queue ? queueCount(dbg_queue)	: 0);
+}
+
+/*
+  INTERFACE:
+    Lock message queue and return the pointer to the first
+    entry.
+  */
+diva_dbg_entry_head_t* diva_maint_get_message (word* size,
+                                               diva_os_spin_lock_magic_t* old_irql) {
+  diva_dbg_entry_head_t*     pmsg = NULL;
+
+  diva_os_enter_spin_lock (&dbg_q_lock, old_irql, "read");
+  if (dbg_q_busy) {
+    diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_busy");
+    return NULL;
+  }
+  dbg_q_busy = 1;
+
+  if (!(pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, size))) {
+    dbg_q_busy = 0;
+    diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_empty");
+  }
+
+  return (pmsg);
+}
+
+/*
+  INTERFACE:
+    acknowledge last message and unlock queue
+  */
+void diva_maint_ack_message (int do_release,
+                             diva_os_spin_lock_magic_t* old_irql) {
+	if (!dbg_q_busy) {
+		return;
+	}
+	if (do_release) {
+		queueFreeMsg (dbg_queue);
+	}
+	dbg_q_busy = 0;
+  diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_ack");
+}
+
+
+/*
+  INTERFACE:
+    PRT COMP function used to register
+    with MAINT adapter or log in compatibility
+    mode in case older driver version is connected too
+  */
+void diva_maint_prtComp (char *format, ...) {
+  void    *hDbg;
+  va_list ap;
+
+  if (!format)
+    return;
+
+  va_start(ap, format);
+
+  /*
+    register to new log driver functions
+   */
+  if ((format[0] == 0) && ((unsigned char)format[1] == 255)) {
+    hDbg = va_arg(ap, void *); /* ptr to DbgHandle */
+    DI_register (hDbg);
+  }
+
+  va_end (ap);
+}
+
+static void DI_register (void *arg) {
+  diva_os_spin_lock_magic_t old_irql;
+  dword sec, usec;
+  pDbgHandle  	hDbg ;
+  int id, free_id = -1, best_id = 0;
+  
+  diva_os_get_time (&sec, &usec);
+
+	hDbg = (pDbgHandle)arg ;
+  /*
+    Check for bad args, specially for the old obsolete debug handle
+    */
+  if ((hDbg == NULL) ||
+      ((hDbg->id == 0) && (((_OldDbgHandle_ *)hDbg)->id == -1)) ||
+      (hDbg->Registered != 0)) {
+		return ;
+  }
+
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "register");
+
+  for (id = 1; id < (sizeof(clients)/sizeof(clients[0])); id++) {
+    if (clients[id].hDbg == hDbg) {
+      /*
+        driver already registered
+        */
+      diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+      return;
+    }
+    if (clients[id].hDbg) { /* slot is busy */
+      continue;
+    }
+    free_id = id;
+    if (!strcmp (clients[id].drvName, hDbg->drvName)) {
+      /*
+        This driver was already registered with this name
+        and slot is still free - reuse it
+        */
+      best_id = 1;
+      break;
+    }
+    if (!clients[id].hDbg) { /* slot is busy */
+      break;
+    }
+  }
+
+  if (free_id != -1) {
+    diva_dbg_entry_head_t* pmsg = NULL;
+    int len;
+    char tmp[256];
+    word size;
+
+    /*
+      Register new driver with id == free_id
+      */
+    clients[free_id].hDbg = hDbg;
+    clients[free_id].sec  = sec;
+    clients[free_id].usec = usec;
+    strcpy (clients[free_id].drvName, hDbg->drvName);
+
+    clients[free_id].dbgMask = hDbg->dbgMask;
+    if (best_id) {
+      hDbg->dbgMask |= clients[free_id].last_dbgMask;
+    } else {
+      clients[free_id].last_dbgMask = 0;
+    }
+
+    hDbg->Registered = DBG_HANDLE_REG_NEW ;
+    hDbg->id         = (byte)free_id;
+    hDbg->dbg_end    = DI_deregister;
+    hDbg->dbg_prt    = DI_format_locked;
+    hDbg->dbg_ev     = DiProcessEventLog;
+    hDbg->dbg_irq    = DI_format_locked;
+    if (hDbg->Version > 0) {
+      hDbg->dbg_old  = DI_format_old;
+    }
+    hDbg->next       = (pDbgHandle)DBG_MAGIC;
+
+    /*
+      Log driver register, MAINT driver ID is '0'
+      */
+    len = sprintf (tmp, "DIMAINT - drv # %d = '%s' registered",
+                   free_id, hDbg->drvName);
+
+    while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                                        (word)(len+1+sizeof(*pmsg))))) {
+      if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+        queueFreeMsg (dbg_queue);
+      } else {
+        break;
+      }
+    }
+
+    if (pmsg) {
+      pmsg->sequence    = dbg_sequence++;
+      pmsg->time_sec    = sec;
+      pmsg->time_usec   = usec;
+      pmsg->facility    = MSG_TYPE_STRING;
+      pmsg->dli         = DLI_REG;
+      pmsg->drv_id      = 0; /* id 0 - DIMAINT */
+      pmsg->di_cpu      = 0;
+      pmsg->data_length = len+1;
+
+      memcpy (&pmsg[1], tmp, len+1);
+		  queueCompleteMsg (pmsg);
+      diva_maint_wakeup_read();
+    }
+  }
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+}
+
+static void DI_deregister (pDbgHandle hDbg) {
+  diva_os_spin_lock_magic_t old_irql, old_irql1;
+  dword sec, usec;
+  int i;
+  word size;
+  byte* pmem = NULL;
+
+  diva_os_get_time (&sec, &usec);
+
+  diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "read");
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read");
+
+  for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) {
+    if (clients[i].hDbg == hDbg) {
+      diva_dbg_entry_head_t* pmsg;
+      char tmp[256];
+      int len;
+
+      clients[i].hDbg = NULL;
+
+      hDbg->id       = -1;
+      hDbg->dbgMask  = 0;
+      hDbg->dbg_end  = NULL;
+      hDbg->dbg_prt  = NULL;
+      hDbg->dbg_irq  = NULL;
+      if (hDbg->Version > 0)
+        hDbg->dbg_old = NULL;
+      hDbg->Registered = 0;
+      hDbg->next     = NULL;
+
+      if (clients[i].pIdiLib) {
+        (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
+        clients[i].pIdiLib = NULL;
+
+        pmem = clients[i].pmem;
+        clients[i].pmem = NULL;
+      }
+
+      /*
+        Log driver register, MAINT driver ID is '0'
+        */
+      len = sprintf (tmp, "DIMAINT - drv # %d = '%s' de-registered",
+                     i, hDbg->drvName);
+
+      while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                                        (word)(len+1+sizeof(*pmsg))))) {
+        if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+          queueFreeMsg (dbg_queue);
+        } else {
+          break;
+        }
+      }
+
+      if (pmsg) {
+        pmsg->sequence    = dbg_sequence++;
+        pmsg->time_sec    = sec;
+        pmsg->time_usec   = usec;
+        pmsg->facility    = MSG_TYPE_STRING;
+        pmsg->dli         = DLI_REG;
+        pmsg->drv_id      = 0; /* id 0 - DIMAINT */
+        pmsg->di_cpu      = 0;
+        pmsg->data_length = len+1;
+
+        memcpy (&pmsg[1], tmp, len+1);
+  		  queueCompleteMsg (pmsg);
+        diva_maint_wakeup_read();
+      }
+
+      break;
+    }
+  }
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_ack");
+  diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "read_ack");
+
+  if (pmem) {
+    diva_os_free (0, pmem);
+  }
+}
+
+static void DI_format_locked (unsigned short id,
+                       int type,
+                       char *format,
+                       va_list argument_list) {
+  DI_format (1, id, type, format, argument_list);
+}
+
+static void DI_format (int do_lock,
+                       unsigned short id,
+                       int type,
+                       char *format,
+                       va_list ap) {
+  diva_os_spin_lock_magic_t old_irql;
+  dword sec, usec;
+  diva_dbg_entry_head_t* pmsg = NULL;
+  dword length;
+  word size;
+  static char fmtBuf[MSG_FRAME_MAX_SIZE+sizeof(*pmsg)+1];
+  char          *data;
+  unsigned short code;
+
+  if (diva_os_in_irq()) {
+    dbg_sequence++;
+    return;
+  }
+
+	if ((!format) ||
+			((TraceFilter[0] != 0) && ((TraceFilterIdent < 0) || (TraceFilterChannel < 0)))) {
+		return;
+	}
+
+
+  
+  diva_os_get_time (&sec, &usec);
+
+  if (do_lock) {
+    diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "format");
+  }
+
+  switch (type) {
+  case DLI_MXLOG :
+  case DLI_BLK :
+  case DLI_SEND:
+  case DLI_RECV:
+    if (!(length = va_arg(ap, unsigned long))) {
+      break;
+    }
+    if (length > MaxDumpSize) {
+      length = MaxDumpSize;
+    }
+    while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                                (word)length+sizeof(*pmsg)))) {
+      if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+        queueFreeMsg (dbg_queue);
+      } else {
+        break;
+      }
+    }
+    if (pmsg) {
+      memcpy (&pmsg[1], format, length);
+      pmsg->sequence    = dbg_sequence++;
+      pmsg->time_sec    = sec;
+      pmsg->time_usec   = usec;
+      pmsg->facility    = MSG_TYPE_BINARY ;
+      pmsg->dli         = type; /* DLI_XXX */
+      pmsg->drv_id      = id;   /* driver MAINT id */
+      pmsg->di_cpu      = 0;
+      pmsg->data_length = length;
+      queueCompleteMsg (pmsg);
+    }
+		break;
+
+  case DLI_XLOG: {
+    byte* p;
+    data    = va_arg(ap, char*);
+    code    = (unsigned short)va_arg(ap, unsigned int);
+    length	= (unsigned long) va_arg(ap, unsigned int);
+
+    if (length > MaxXlogSize)
+      length = MaxXlogSize;
+
+    while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                                  (word)length+sizeof(*pmsg)+2))) {
+      if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+        queueFreeMsg (dbg_queue);
+      } else {
+        break;
+      }
+    }
+    if (pmsg) {
+      p = (byte*)&pmsg[1];
+      p[0] = (char)(code) ;
+      p[1] = (char)(code >> 8) ;
+      if (data && length) {
+        memcpy (&p[2], &data[0], length) ;
+      }
+      length += 2 ;
+
+      pmsg->sequence    = dbg_sequence++;
+      pmsg->time_sec    = sec;
+      pmsg->time_usec   = usec;
+      pmsg->facility    = MSG_TYPE_BINARY ;
+      pmsg->dli         = type; /* DLI_XXX */
+      pmsg->drv_id      = id;   /* driver MAINT id */
+      pmsg->di_cpu      = 0;
+      pmsg->data_length = length;
+      queueCompleteMsg (pmsg);
+    }
+  } break;
+
+  case DLI_LOG :
+  case DLI_FTL :
+  case DLI_ERR :
+  case DLI_TRC :
+  case DLI_REG :
+  case DLI_MEM :
+  case DLI_SPL :
+  case DLI_IRP :
+  case DLI_TIM :
+  case DLI_TAPI:
+  case DLI_NDIS:
+  case DLI_CONN:
+  case DLI_STAT:
+  case DLI_PRV0:
+  case DLI_PRV1:
+  case DLI_PRV2:
+  case DLI_PRV3:
+    if ((length = (unsigned long)vsprintf (&fmtBuf[0], format, ap)) > 0) {
+      length += (sizeof(*pmsg)+1);
+
+      while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                                                          (word)length))) {
+        if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+          queueFreeMsg (dbg_queue);
+        } else {
+          break;
+        }
+      }
+
+      pmsg->sequence    = dbg_sequence++;
+      pmsg->time_sec    = sec;
+      pmsg->time_usec   = usec;
+      pmsg->facility    = MSG_TYPE_STRING;
+      pmsg->dli         = type; /* DLI_XXX */
+      pmsg->drv_id      = id;   /* driver MAINT id */
+      pmsg->di_cpu      = 0;
+      pmsg->data_length = length - sizeof(*pmsg);
+
+      memcpy (&pmsg[1], fmtBuf, pmsg->data_length);
+		  queueCompleteMsg (pmsg);
+    }
+    break;
+
+  } /* switch type */
+
+
+  if (queueCount(dbg_queue)) {
+    diva_maint_wakeup_read();
+  }
+
+  if (do_lock) {
+    diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "format");
+  }
+}
+
+/*
+  Write driver ID and driver revision to callers buffer
+  */
+int diva_get_driver_info (dword id, byte* data, int data_length) {
+  diva_os_spin_lock_magic_t old_irql;
+  byte* p = data;
+  int to_copy;
+
+  if (!data || !id || (data_length < 17) ||
+      (id >= (sizeof(clients)/sizeof(clients[0])))) {
+    return (-1);
+  }
+
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+  if (clients[id].hDbg) {
+    *p++ = 1;
+    *p++ = (byte)clients[id].sec; /* save seconds */
+    *p++ = (byte)(clients[id].sec >>  8);
+    *p++ = (byte)(clients[id].sec >> 16);
+    *p++ = (byte)(clients[id].sec >> 24);
+
+    *p++ = (byte)(clients[id].usec/1000); /* save mseconds */
+    *p++ = (byte)((clients[id].usec/1000) >>  8);
+    *p++ = (byte)((clients[id].usec/1000) >> 16);
+    *p++ = (byte)((clients[id].usec/1000) >> 24);
+
+    data_length -= 9;
+
+    if ((to_copy = MIN(strlen(clients[id].drvName), data_length-1))) {
+      memcpy (p, clients[id].drvName, to_copy);
+      p += to_copy;
+      data_length -= to_copy;
+      if ((data_length >= 4) && clients[id].hDbg->drvTag[0]) {
+        *p++ = '(';
+        data_length -= 1;
+        if ((to_copy = MIN(strlen(clients[id].hDbg->drvTag), data_length-2))) {
+          memcpy (p, clients[id].hDbg->drvTag, to_copy);
+          p += to_copy;
+          data_length -= to_copy;
+          if (data_length >= 2) {
+            *p++ = ')';
+            data_length--;
+          }
+        }
+      }
+    }
+  }
+  *p++ = 0;
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+  return (p - data);
+}
+
+int diva_get_driver_dbg_mask (dword id, byte* data) {
+  diva_os_spin_lock_magic_t old_irql;
+  int ret = -1;
+
+  if (!data || !id || (id >= (sizeof(clients)/sizeof(clients[0])))) {
+    return (-1);
+  }
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+  if (clients[id].hDbg) {
+    ret = 4;
+    *data++= (byte)(clients[id].hDbg->dbgMask);
+    *data++= (byte)(clients[id].hDbg->dbgMask >>  8);
+    *data++= (byte)(clients[id].hDbg->dbgMask >> 16);
+    *data++= (byte)(clients[id].hDbg->dbgMask >> 24);
+  }
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+  return (ret);
+}
+
+int diva_set_driver_dbg_mask (dword id, dword mask) {
+  diva_os_spin_lock_magic_t old_irql, old_irql1;
+  int ret = -1;
+  
+
+  if (!id || (id >= (sizeof(clients)/sizeof(clients[0])))) {
+    return (-1);
+  }
+
+  diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "dbg mask");
+
+  if (clients[id].hDbg) {
+    dword old_mask = clients[id].hDbg->dbgMask;
+    mask &= 0x7fffffff;
+    clients[id].hDbg->dbgMask = mask;
+    clients[id].last_dbgMask = (clients[id].hDbg->dbgMask | clients[id].dbgMask);
+    ret = 4;
+    diva_change_management_debug_mask (&clients[id], old_mask);
+  }
+
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "dbg mask");
+
+  if (clients[id].request_pending) {
+    clients[id].request_pending = 0;
+    (*(clients[id].request))((ENTITY*)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib));
+  }
+
+  diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+
+  return (ret);
+}
+
+static int diva_get_idi_adapter_info (IDI_CALL request, dword* serial, dword* logical) {
+  IDI_SYNC_REQ sync_req;
+
+  sync_req.xdi_logical_adapter_number.Req = 0;
+  sync_req.xdi_logical_adapter_number.Rc = IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER;
+  (*request)((ENTITY *)&sync_req);
+  *logical = sync_req.xdi_logical_adapter_number.info.logical_adapter_number;
+
+  sync_req.GetSerial.Req = 0;
+  sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
+  sync_req.GetSerial.serial = 0;
+  (*request)((ENTITY *)&sync_req);
+	*serial = sync_req.GetSerial.serial;
+
+  return (0);
+}
+
+/*
+  Register XDI adapter as MAINT compatible driver
+  */
+void diva_mnt_add_xdi_adapter (const DESCRIPTOR* d) {
+  diva_os_spin_lock_magic_t old_irql, old_irql1;
+  dword sec, usec, logical, serial, org_mask;
+  int id, best_id = 0, free_id = -1;
+  char tmp[256];
+  diva_dbg_entry_head_t* pmsg = NULL;
+  int len;
+  word size;
+  byte* pmem;
+
+  diva_os_get_time (&sec, &usec);
+  diva_get_idi_adapter_info (d->request, &serial, &logical);
+  if (serial & 0xff000000) {
+    sprintf (tmp, "ADAPTER:%d SN:%u-%d",
+             (int)logical,
+             serial & 0x00ffffff,
+             (byte)(((serial & 0xff000000) >> 24) + 1));
+  } else {
+    sprintf (tmp, "ADAPTER:%d SN:%u", (int)logical, serial);
+  }
+
+  if (!(pmem = diva_os_malloc (0, DivaSTraceGetMemotyRequirement (d->channels)))) {
+    return;
+  }
+  memset (pmem, 0x00, DivaSTraceGetMemotyRequirement (d->channels));
+
+  diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "register");
+
+  for (id = 1; id < (sizeof(clients)/sizeof(clients[0])); id++) {
+    if (clients[id].hDbg && (clients[id].request == d->request)) {
+      diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+      diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+      diva_os_free(0, pmem);
+      return;
+    }
+    if (clients[id].hDbg) { /* slot is busy */
+      continue;
+    }
+    if (free_id < 0) {
+      free_id = id;
+    }
+    if (!strcmp (clients[id].drvName, tmp)) {
+      /*
+        This driver was already registered with this name
+        and slot is still free - reuse it
+        */
+      free_id = id;
+      best_id = 1;
+      break;
+    }
+  }
+
+  if (free_id < 0) {
+    diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+    diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+    diva_os_free (0, pmem);
+    return;
+  }
+
+  id = free_id;
+  clients[id].request  = d->request;
+  clients[id].request_pending = 0;
+  clients[id].hDbg     = &clients[id].Dbg;
+  clients[id].sec      = sec;
+  clients[id].usec     = usec;
+  strcpy (clients[id].drvName,     tmp);
+  strcpy (clients[id].Dbg.drvName, tmp);
+  clients[id].Dbg.drvTag[0] = 0;
+  clients[id].logical  = (int)logical;
+  clients[id].channels = (int)d->channels;
+  clients[id].dma_handle = -1;
+
+  clients[id].Dbg.dbgMask    = 0;
+  clients[id].dbgMask        = clients[id].Dbg.dbgMask;
+  if (id) {
+    clients[id].Dbg.dbgMask |= clients[free_id].last_dbgMask;
+  } else {
+    clients[id].last_dbgMask = 0;
+  }
+  clients[id].Dbg.Registered = DBG_HANDLE_REG_NEW;
+  clients[id].Dbg.id         = (byte)id;
+  clients[id].Dbg.dbg_end    = DI_deregister;
+  clients[id].Dbg.dbg_prt    = DI_format_locked;
+  clients[id].Dbg.dbg_ev     = DiProcessEventLog;
+  clients[id].Dbg.dbg_irq    = DI_format_locked;
+  clients[id].Dbg.next       = (pDbgHandle)DBG_MAGIC;
+
+  {
+    diva_trace_library_user_interface_t diva_maint_user_ifc = { &clients[id],
+																							 diva_maint_state_change_notify,
+																							 diva_maint_trace_notify,
+																							 diva_maint_error };
+
+    /*
+      Attach to adapter management interface
+      */
+    if ((clients[id].pIdiLib =
+               DivaSTraceLibraryCreateInstance ((int)logical, &diva_maint_user_ifc, pmem))) {
+      if (((*(clients[id].pIdiLib->DivaSTraceLibraryStart))(clients[id].pIdiLib->hLib))) {
+        diva_mnt_internal_dprintf (0, DLI_ERR, "Adapter(%d) Start failed", (int)logical);
+        (*(clients[id].pIdiLib->DivaSTraceLibraryFinit))(clients[id].pIdiLib->hLib);
+        clients[id].pIdiLib = NULL;
+      }
+    } else {
+      diva_mnt_internal_dprintf (0, DLI_ERR, "A(%d) management init failed", (int)logical);
+    }
+  }
+
+  if (!clients[id].pIdiLib) {
+    clients[id].request = NULL;
+    clients[id].request_pending = 0;
+    clients[id].hDbg    = NULL;
+    diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+    diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+    diva_os_free (0, pmem);
+    return;
+  }
+
+  /*
+    Log driver register, MAINT driver ID is '0'
+    */
+  len = sprintf (tmp, "DIMAINT - drv # %d = '%s' registered",
+                 id, clients[id].Dbg.drvName);
+
+  while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                                      (word)(len+1+sizeof(*pmsg))))) {
+    if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+      queueFreeMsg (dbg_queue);
+    } else {
+      break;
+    }
+  }
+
+  if (pmsg) {
+    pmsg->sequence    = dbg_sequence++;
+    pmsg->time_sec    = sec;
+    pmsg->time_usec   = usec;
+    pmsg->facility    = MSG_TYPE_STRING;
+    pmsg->dli         = DLI_REG;
+    pmsg->drv_id      = 0; /* id 0 - DIMAINT */
+    pmsg->di_cpu      = 0;
+    pmsg->data_length = len+1;
+
+    memcpy (&pmsg[1], tmp, len+1);
+    queueCompleteMsg (pmsg);
+    diva_maint_wakeup_read();
+  }
+
+  org_mask = clients[id].Dbg.dbgMask;
+  clients[id].Dbg.dbgMask = 0;
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+
+  if (clients[id].request_pending) {
+    clients[id].request_pending = 0;
+    (*(clients[id].request))((ENTITY*)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib));
+  }
+
+  diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+
+	diva_set_driver_dbg_mask (id, org_mask);
+}
+
+/*
+  De-Register XDI adapter
+  */
+void diva_mnt_remove_xdi_adapter (const DESCRIPTOR* d) {
+  diva_os_spin_lock_magic_t old_irql, old_irql1;
+  dword sec, usec;
+  int i;
+  word size;
+  byte* pmem = NULL;
+
+  diva_os_get_time (&sec, &usec);
+
+  diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "read");
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read");
+
+  for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) {
+    if (clients[i].hDbg && (clients[i].request == d->request)) {
+      diva_dbg_entry_head_t* pmsg;
+      char tmp[256];
+      int len;
+
+      if (clients[i].pIdiLib) {
+        (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
+        clients[i].pIdiLib = NULL;
+
+        pmem = clients[i].pmem;
+        clients[i].pmem = NULL;
+      }
+
+      clients[i].hDbg    = NULL;
+      clients[i].request_pending = 0;
+      if (clients[i].dma_handle >= 0) {
+        /*
+          Free DMA handle
+          */
+        diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle);
+        clients[i].dma_handle = -1;
+      }
+      clients[i].request = NULL;
+
+      /*
+        Log driver register, MAINT driver ID is '0'
+        */
+      len = sprintf (tmp, "DIMAINT - drv # %d = '%s' de-registered",
+                     i, clients[i].Dbg.drvName);
+
+      memset (&clients[i].Dbg, 0x00, sizeof(clients[i].Dbg));
+
+      while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                                        (word)(len+1+sizeof(*pmsg))))) {
+        if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+          queueFreeMsg (dbg_queue);
+        } else {
+          break;
+        }
+      }
+
+      if (pmsg) {
+        pmsg->sequence    = dbg_sequence++;
+        pmsg->time_sec    = sec;
+        pmsg->time_usec   = usec;
+        pmsg->facility    = MSG_TYPE_STRING;
+        pmsg->dli         = DLI_REG;
+        pmsg->drv_id      = 0; /* id 0 - DIMAINT */
+        pmsg->di_cpu      = 0;
+        pmsg->data_length = len+1;
+
+        memcpy (&pmsg[1], tmp, len+1);
+  		  queueCompleteMsg (pmsg);
+        diva_maint_wakeup_read();
+      }
+
+      break;
+    }
+  }
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_ack");
+  diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "read_ack");
+
+  if (pmem) {
+    diva_os_free (0, pmem);
+  }
+}
+
+/* ----------------------------------------------------------------
+     Low level interface for management interface client
+   ---------------------------------------------------------------- */
+/*
+  Return handle to client structure
+  */
+void* SuperTraceOpenAdapter   (int AdapterNumber) {
+  int i;
+
+  for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) {
+    if (clients[i].hDbg && clients[i].request && (clients[i].logical == AdapterNumber)) {
+      return (&clients[i]);
+    }
+  }
+
+  return NULL;
+}
+
+int SuperTraceCloseAdapter  (void* AdapterHandle) {
+  return (0);
+}
+
+int SuperTraceReadRequest (void* AdapterHandle, const char* name, byte* data) {
+  diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+  if (pC && pC->pIdiLib && pC->request) {
+    ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+    byte* xdata = (byte*)&pC->xbuffer[0];
+    char tmp = 0;
+    word length;
+
+    if (!strcmp(name, "\\")) { /* Read ROOT */
+      name = &tmp;
+    }
+    length = SuperTraceCreateReadReq (xdata, name);
+    single_p (xdata, &length, 0); /* End Of Message */
+
+    e->Req        = MAN_READ;
+    e->ReqCh      = 0;
+    e->X->PLength = length;
+    e->X->P			  = (byte*)xdata;
+
+    pC->request_pending = 1;
+
+    return (0);
+  }
+
+  return (-1);
+}
+
+int SuperTraceGetNumberOfChannels (void* AdapterHandle) {
+  if (AdapterHandle) {
+    diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+    return (pC->channels);
+  }
+
+  return (0);
+}
+
+int SuperTraceASSIGN (void* AdapterHandle, byte* data) {
+  diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+  if (pC && pC->pIdiLib && pC->request) {
+    ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+    IDI_SYNC_REQ* preq;
+    char buffer[((sizeof(preq->xdi_extended_features)+4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features)+4) : sizeof(ENTITY)];
+    char features[4];
+    word assign_data_length = 1;
+
+    features[0] = 0;
+    pC->xbuffer[0] = 0;
+    preq = (IDI_SYNC_REQ*)&buffer[0];
+    preq->xdi_extended_features.Req = 0;
+    preq->xdi_extended_features.Rc  = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES;
+    preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features);
+    preq->xdi_extended_features.info.features = &features[0];
+
+    (*(pC->request))((ENTITY*)preq);
+
+    if ((features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) &&
+        (features[0] & DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA)) {
+      dword rx_dma_magic;
+      if ((pC->dma_handle = diva_get_dma_descriptor (pC->request, &rx_dma_magic)) >= 0) {
+        pC->xbuffer[0] = LLI;
+        pC->xbuffer[1] = 8;
+        pC->xbuffer[2] = 0x40;
+        pC->xbuffer[3] = (byte)pC->dma_handle;
+        pC->xbuffer[4] = (byte)rx_dma_magic;
+        pC->xbuffer[5] = (byte)(rx_dma_magic >>  8);
+        pC->xbuffer[6] = (byte)(rx_dma_magic >> 16);
+        pC->xbuffer[7] = (byte)(rx_dma_magic >> 24);
+        pC->xbuffer[8] = (byte)DIVA_MAX_MANAGEMENT_TRANSFER_SIZE;
+        pC->xbuffer[9] = (byte)(DIVA_MAX_MANAGEMENT_TRANSFER_SIZE >> 8);
+        pC->xbuffer[10] = 0;
+
+        assign_data_length = 11;
+      }
+    } else {
+      pC->dma_handle = -1;
+    }
+
+    e->Id          = MAN_ID;
+    e->callback    = diva_maint_xdi_cb;
+    e->XNum        = 1;
+    e->X           = &pC->XData;
+    e->Req         = ASSIGN;
+    e->ReqCh       = 0;
+    e->X->PLength  = assign_data_length;
+    e->X->P        = (byte*)&pC->xbuffer[0];
+
+    pC->request_pending = 1;
+
+    return (0);
+  }
+
+  return (-1);
+}
+
+int SuperTraceREMOVE (void* AdapterHandle) {
+  diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+  if (pC && pC->pIdiLib && pC->request) {
+    ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+
+    e->XNum        = 1;
+    e->X           = &pC->XData;
+    e->Req         = REMOVE;
+    e->ReqCh       = 0;
+    e->X->PLength  = 1;
+    e->X->P        = (byte*)&pC->xbuffer[0];
+    pC->xbuffer[0] = 0;
+
+    pC->request_pending = 1;
+
+    return (0);
+  }
+
+  return (-1);
+}
+
+int SuperTraceTraceOnRequest(void* hAdapter, const char* name, byte* data) {
+  diva_maint_client_t* pC = (diva_maint_client_t*)hAdapter;
+
+  if (pC && pC->pIdiLib && pC->request) {
+    ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+    byte* xdata = (byte*)&pC->xbuffer[0];
+    char tmp = 0;
+    word length;
+
+    if (!strcmp(name, "\\")) { /* Read ROOT */
+      name = &tmp;
+    }
+    length = SuperTraceCreateReadReq (xdata, name);
+    single_p (xdata, &length, 0); /* End Of Message */
+    e->Req          = MAN_EVENT_ON;
+    e->ReqCh        = 0;
+    e->X->PLength   = length;
+    e->X->P			    = (byte*)xdata;
+
+    pC->request_pending = 1;
+
+    return (0);
+  }
+
+  return (-1);
+}
+
+int SuperTraceWriteVar (void* AdapterHandle,
+                        byte* data,
+                        const char* name,
+                        void* var,
+                        byte type,
+                        byte var_length) {
+  diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+  if (pC && pC->pIdiLib && pC->request) {
+    ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+    diva_man_var_header_t* pVar = (diva_man_var_header_t*)&pC->xbuffer[0];
+    word length = SuperTraceCreateReadReq ((byte*)pVar, name);
+
+    memcpy (&pC->xbuffer[length], var, var_length);
+    length += var_length;
+    pVar->length += var_length;
+    pVar->value_length = var_length;
+    pVar->type = type;
+    single_p ((byte*)pVar, &length, 0); /* End Of Message */
+
+    e->Req          = MAN_WRITE;
+    e->ReqCh			  = 0;
+    e->X->PLength   = length;
+    e->X->P			    = (byte*)pVar;
+
+    pC->request_pending = 1;
+
+    return (0);
+  }
+
+  return (-1);
+}
+
+int SuperTraceExecuteRequest (void* AdapterHandle,
+                              const char* name,
+                              byte* data) {
+  diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+  if (pC && pC->pIdiLib && pC->request) {
+    ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+    byte* xdata = (byte*)&pC->xbuffer[0];
+    word length;
+
+    length = SuperTraceCreateReadReq (xdata, name);
+    single_p (xdata, &length, 0); /* End Of Message */
+
+    e->Req          = MAN_EXECUTE;
+    e->ReqCh			  = 0;
+    e->X->PLength   = length;
+    e->X->P			    = (byte*)xdata;
+
+    pC->request_pending = 1;
+
+    return (0);
+  }
+
+  return (-1);
+}
+
+static word SuperTraceCreateReadReq (byte* P, const char* path) {
+	byte var_length;
+	byte* plen;
+
+	var_length = (byte)strlen (path);
+
+	*P++ = ESC;
+	plen = P++;
+	*P++ = 0x80; /* MAN_IE */
+	*P++ = 0x00; /* Type */
+	*P++ = 0x00; /* Attribute */
+	*P++ = 0x00; /* Status */
+	*P++ = 0x00; /* Variable Length */
+	*P++ = var_length;
+	memcpy (P, path, var_length);
+	P += var_length;
+	*plen = var_length + 0x06;
+
+	return ((word)(var_length + 0x08));
+}
+
+static void single_p (byte * P, word * PLength, byte Id) {
+  P[(*PLength)++] = Id;
+}
+
+static void diva_maint_xdi_cb (ENTITY* e) {
+  diva_strace_context_t* pLib = DIVAS_CONTAINING_RECORD(e,diva_strace_context_t,e);
+  diva_maint_client_t* pC;
+  diva_os_spin_lock_magic_t old_irql, old_irql1;
+
+
+  diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "xdi_cb");
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "xdi_cb");
+
+  pC = (diva_maint_client_t*)pLib->hAdapter;
+
+  if ((e->complete == 255) || (pC->dma_handle < 0)) {
+    if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) {
+      diva_mnt_internal_dprintf (0, DLI_ERR, "Trace internal library error");
+    }
+  } else {
+    /*
+      Process combined management interface indication
+      */
+    if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) {
+      diva_mnt_internal_dprintf (0, DLI_ERR, "Trace internal library error (DMA mode)");
+    }
+  }
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "xdi_cb");
+
+
+	if (pC->request_pending) {
+    pC->request_pending = 0;
+    (*(pC->request))(e);
+	}
+
+  diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "xdi_cb");
+}
+
+
+static void diva_maint_error (void* user_context,
+                              diva_strace_library_interface_t* hLib,
+                              int Adapter,
+                              int error,
+                              const char* file,
+                              int line) {
+	diva_mnt_internal_dprintf (0, DLI_ERR,
+                             "Trace library error(%d) A(%d) %s %d", error, Adapter, file, line);
+}
+
+static void print_ie (diva_trace_ie_t* ie, char* buffer, int length) {
+	int i;
+
+  buffer[0] = 0;
+
+  if (length > 32) {
+    for (i = 0; ((i < ie->length) && (length > 3)); i++) {
+      sprintf (buffer, "%02x", ie->data[i]);
+      buffer += 2;
+      length -= 2;
+      if (i < (ie->length-1)) {
+        strcpy (buffer, " ");
+        buffer++;
+        length--;
+      }
+    }
+  }
+}
+
+static void diva_maint_state_change_notify (void* user_context,
+                                            diva_strace_library_interface_t* hLib,
+                                            int Adapter,
+                                            diva_trace_line_state_t* channel,
+                                            int notify_subject) {
+  diva_maint_client_t*      pC    = (diva_maint_client_t*)user_context;
+  diva_trace_fax_state_t*   fax   = &channel->fax;
+  diva_trace_modem_state_t* modem = &channel->modem;
+  char tmp[256];
+
+  if (!pC->hDbg) {
+    return;
+  }
+
+  switch (notify_subject) {
+    case DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE: {
+      int view = (TraceFilter[0] == 0);
+      /*
+        Process selective Trace
+        */
+      if (channel->Line[0] == 'I' && channel->Line[1] == 'd' &&
+          channel->Line[2] == 'l' && channel->Line[3] == 'e') {
+        if ((TraceFilterIdent == pC->hDbg->id) && (TraceFilterChannel == (int)channel->ChannelNumber)) {
+          (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 0);
+          (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 0);
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG, "Selective Trace OFF for Ch=%d",
+                                     (int)channel->ChannelNumber);
+          TraceFilterIdent   = -1;
+          TraceFilterChannel = -1;
+          view = 1;
+        }
+      } else if (TraceFilter[0] && (TraceFilterIdent < 0) && !(diva_mnt_cmp_nmbr (&channel->RemoteAddress[0]) &&
+                                                               diva_mnt_cmp_nmbr (&channel->LocalAddress[0]))) {
+
+        if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0) { /* Activate B-channel trace */
+          (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 1);
+        }
+        if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0) { /* Activate AudioTap Trace */
+          (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 1);
+        }
+
+        TraceFilterIdent   = pC->hDbg->id;
+        TraceFilterChannel = (int)channel->ChannelNumber;
+
+        if (TraceFilterIdent >= 0) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG, "Selective Trace ON for Ch=%d",
+                                     (int)channel->ChannelNumber);
+          view = 1;
+        }
+      }
+      if (view && (pC->hDbg->dbgMask & DIVA_MGT_DBG_LINE_EVENTS)) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Ch     = %d",
+                                                                     (int)channel->ChannelNumber);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Status = <%s>", &channel->Line[0]);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Layer1 = <%s>", &channel->Framing[0]);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Layer2 = <%s>", &channel->Layer2[0]);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Layer3 = <%s>", &channel->Layer3[0]);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L RAddr  = <%s>",
+                                                                     &channel->RemoteAddress[0]);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L RSAddr = <%s>",
+                                                                     &channel->RemoteSubAddress[0]);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L LAddr  = <%s>",
+                                                                     &channel->LocalAddress[0]);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L LSAddr = <%s>",
+                                                                     &channel->LocalSubAddress[0]);
+        print_ie(&channel->call_BC, tmp, sizeof(tmp));
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L BC     = <%s>", tmp);
+        print_ie(&channel->call_HLC, tmp, sizeof(tmp));
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L HLC    = <%s>", tmp);
+        print_ie(&channel->call_LLC, tmp, sizeof(tmp));
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L LLC    = <%s>", tmp);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L CR     = 0x%x", channel->CallReference);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Disc   = 0x%x",
+                                                                    channel->LastDisconnecCause);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Owner  = <%s>", &channel->UserID[0]);
+      }
+
+		} break;
+
+    case DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE:
+      if (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_PROGRESS) {
+				{
+					int ch = TraceFilterChannel;
+					int id = TraceFilterIdent;
+
+					if ((id >= 0) && (ch >= 0) && (id < sizeof(clients)/sizeof(clients[0])) &&
+						(clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
+						if (ch != (int)modem->ChannelNumber) {
+							break;
+						}
+					} else if (TraceFilter[0] != 0) {
+						break;
+					}
+				}
+
+
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Ch    = %lu",
+                                                                     (int)modem->ChannelNumber);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Event = %lu",     modem->Event);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Norm  = %lu",     modem->Norm);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Opts. = 0x%08x",  modem->Options);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Tx    = %lu Bps", modem->TxSpeed);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rx    = %lu Bps", modem->RxSpeed);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RT    = %lu mSec",
+                                                                     modem->RoundtripMsec);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Sr    = %lu",     modem->SymbolRate);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rxl   = %d dBm",  modem->RxLeveldBm);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM El    = %d dBm",  modem->EchoLeveldBm);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM SNR   = %lu dB",  modem->SNRdb);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM MAE   = %lu",     modem->MAE);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRet  = %lu",
+                                                                     modem->LocalRetrains);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRet  = %lu",
+                                                                     modem->RemoteRetrains);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRes  = %lu",     modem->LocalResyncs);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRes  = %lu",
+                                                                     modem->RemoteResyncs);
+        if (modem->Event == 3) {
+          diva_mnt_internal_dprintf(pC->hDbg->id,DLI_STAT,"MDM Disc  =  %lu",    modem->DiscReason);
+        }
+      }
+      if ((modem->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_STATISTICS)) {
+        (*(pC->pIdiLib->DivaSTraceGetModemStatistics))(pC->pIdiLib);
+      }
+      break;
+
+    case DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE:
+      if (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_PROGRESS) {
+				{
+					int ch = TraceFilterChannel;
+					int id = TraceFilterIdent;
+
+					if ((id >= 0) && (ch >= 0) && (id < sizeof(clients)/sizeof(clients[0])) &&
+						(clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
+						if (ch != (int)fax->ChannelNumber) {
+							break;
+						}
+					} else if (TraceFilter[0] != 0) {
+						break;
+					}
+				}
+
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Ch    = %lu",(int)fax->ChannelNumber);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Event = %lu",     fax->Event);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pages = %lu",     fax->Page_Counter);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Feat. = 0x%08x",  fax->Features);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX ID    = <%s>",    &fax->Station_ID[0]);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Saddr = <%s>",    &fax->Subaddress[0]);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pwd   = <%s>",    &fax->Password[0]);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Speed = %lu",     fax->Speed);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Res.  = 0x%08x",  fax->Resolution);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Width = %lu",     fax->Paper_Width);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Length= %lu",     fax->Paper_Length);
+        diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX SLT   = %lu",     fax->Scanline_Time);
+        if (fax->Event == 3) {
+          diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Disc  = %lu",     fax->Disc_Reason);
+        }
+      }
+      if ((fax->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_STATISTICS)) {
+        (*(pC->pIdiLib->DivaSTraceGetFaxStatistics))(pC->pIdiLib);
+      }
+      break;
+
+    case DIVA_SUPER_TRACE_INTERFACE_CHANGE:
+      if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_EVENTS) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT,
+                                 "Layer 1 -> [%s]", channel->pInterface->Layer1);
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT,
+                                 "Layer 2 -> [%s]", channel->pInterface->Layer2);
+      }
+      break;
+
+    case DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE:
+      if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_STATISTICS) {
+        /*
+          Incoming Statistics
+          */
+        if (channel->pInterfaceStat->inc.Calls) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Calls                     =%lu", channel->pInterfaceStat->inc.Calls);
+        }
+        if (channel->pInterfaceStat->inc.Connected) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Connected                 =%lu", channel->pInterfaceStat->inc.Connected);
+        }
+        if (channel->pInterfaceStat->inc.User_Busy) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Busy                      =%lu", channel->pInterfaceStat->inc.User_Busy);
+        }
+        if (channel->pInterfaceStat->inc.Call_Rejected) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Rejected                  =%lu", channel->pInterfaceStat->inc.Call_Rejected);
+        }
+        if (channel->pInterfaceStat->inc.Wrong_Number) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Wrong Nr                  =%lu", channel->pInterfaceStat->inc.Wrong_Number);
+        }
+        if (channel->pInterfaceStat->inc.Incompatible_Dst) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Incomp. Dest              =%lu", channel->pInterfaceStat->inc.Incompatible_Dst);
+        }
+        if (channel->pInterfaceStat->inc.Out_of_Order) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Out of Order              =%lu", channel->pInterfaceStat->inc.Out_of_Order);
+        }
+        if (channel->pInterfaceStat->inc.Ignored) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Inc Ignored                   =%lu", channel->pInterfaceStat->inc.Ignored);
+        }
+        
+        /*
+          Outgoing Statistics
+          */
+        if (channel->pInterfaceStat->outg.Calls) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Outg Calls                    =%lu", channel->pInterfaceStat->outg.Calls);
+        }
+        if (channel->pInterfaceStat->outg.Connected) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Outg Connected                =%lu", channel->pInterfaceStat->outg.Connected);
+        }
+        if (channel->pInterfaceStat->outg.User_Busy) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Outg Busy                     =%lu", channel->pInterfaceStat->outg.User_Busy);
+        }
+        if (channel->pInterfaceStat->outg.No_Answer) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Outg No Answer                =%lu", channel->pInterfaceStat->outg.No_Answer);
+        }
+        if (channel->pInterfaceStat->outg.Wrong_Number) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Outg Wrong Nr                 =%lu", channel->pInterfaceStat->outg.Wrong_Number);
+        }
+        if (channel->pInterfaceStat->outg.Call_Rejected) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Outg Rejected                 =%lu", channel->pInterfaceStat->outg.Call_Rejected);
+        }
+        if (channel->pInterfaceStat->outg.Other_Failures) {
+          diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+          "Outg Other Failures           =%lu", channel->pInterfaceStat->outg.Other_Failures);
+        }
+      }
+      break;
+
+    case DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE:
+      if (channel->pInterfaceStat->mdm.Disc_Normal) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Normal        = %lu", channel->pInterfaceStat->mdm.Disc_Normal);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_Unspecified) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Unsp.         = %lu", channel->pInterfaceStat->mdm.Disc_Unspecified);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_Busy_Tone) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Busy Tone     = %lu", channel->pInterfaceStat->mdm.Disc_Busy_Tone);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_Congestion) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Congestion    = %lu", channel->pInterfaceStat->mdm.Disc_Congestion);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_Carr_Wait) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Carrier Wait  = %lu", channel->pInterfaceStat->mdm.Disc_Carr_Wait);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_Trn_Timeout) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Trn. T.o.     = %lu", channel->pInterfaceStat->mdm.Disc_Trn_Timeout);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_Incompat) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Incompatible  = %lu", channel->pInterfaceStat->mdm.Disc_Incompat);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_Frame_Rej) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc Frame Reject  = %lu", channel->pInterfaceStat->mdm.Disc_Frame_Rej);
+      }
+      if (channel->pInterfaceStat->mdm.Disc_V42bis) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "MDM Disc V.42bis       = %lu", channel->pInterfaceStat->mdm.Disc_V42bis);
+      }
+      break;
+
+    case DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE:
+      if (channel->pInterfaceStat->fax.Disc_Normal) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Normal        = %lu", channel->pInterfaceStat->fax.Disc_Normal);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Not_Ident) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Not Ident.    = %lu", channel->pInterfaceStat->fax.Disc_Not_Ident);
+      }
+      if (channel->pInterfaceStat->fax.Disc_No_Response) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc No Response   = %lu", channel->pInterfaceStat->fax.Disc_No_Response);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Retries) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Max Retries   = %lu", channel->pInterfaceStat->fax.Disc_Retries);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Unexp_Msg) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Unexp. Msg.        = %lu", channel->pInterfaceStat->fax.Disc_Unexp_Msg);
+      }
+      if (channel->pInterfaceStat->fax.Disc_No_Polling) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc No Polling    = %lu", channel->pInterfaceStat->fax.Disc_No_Polling);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Training) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Training      = %lu", channel->pInterfaceStat->fax.Disc_Training);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Unexpected) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Unexpected    = %lu", channel->pInterfaceStat->fax.Disc_Unexpected);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Application) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Application   = %lu", channel->pInterfaceStat->fax.Disc_Application);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Incompat) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Incompatible  = %lu", channel->pInterfaceStat->fax.Disc_Incompat);
+      }
+      if (channel->pInterfaceStat->fax.Disc_No_Command) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc No Command    = %lu", channel->pInterfaceStat->fax.Disc_No_Command);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Long_Msg) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Long Msg.     = %lu", channel->pInterfaceStat->fax.Disc_Long_Msg);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Supervisor) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Supervisor    = %lu", channel->pInterfaceStat->fax.Disc_Supervisor);
+      }
+      if (channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc SUP SEP PWD   = %lu", channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Invalid_Msg) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Invalid Msg.  = %lu", channel->pInterfaceStat->fax.Disc_Invalid_Msg);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Page_Coding) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Page Coding   = %lu", channel->pInterfaceStat->fax.Disc_Page_Coding);
+      }
+      if (channel->pInterfaceStat->fax.Disc_App_Timeout) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Appl. T.o.    = %lu", channel->pInterfaceStat->fax.Disc_App_Timeout);
+      }
+      if (channel->pInterfaceStat->fax.Disc_Unspecified) {
+        diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+        "FAX Disc Unspec.       = %lu", channel->pInterfaceStat->fax.Disc_Unspecified);
+      }
+      break;
+  }
+}
+
+/*
+  Receive trace information from the Management Interface and store it in the
+  internal trace buffer with MSG_TYPE_MLOG as is, without any filtering.
+  Event Filtering and formatting is done in  Management Interface self.
+  */
+static void diva_maint_trace_notify (void* user_context,
+                                     diva_strace_library_interface_t* hLib,
+                                     int Adapter,
+                                     void* xlog_buffer,
+                                     int length) {
+  diva_maint_client_t* pC = (diva_maint_client_t*)user_context;
+  diva_dbg_entry_head_t* pmsg;
+  word size;
+  dword sec, usec;
+  int ch = TraceFilterChannel;
+  int id = TraceFilterIdent;
+
+  /*
+    Selective trace
+    */
+  if ((id >= 0) && (ch >= 0) && (id < sizeof(clients)/sizeof(clients[0])) &&
+      (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
+    const char* p = NULL;
+    int ch_value = -1;
+    MI_XLOG_HDR *TrcData = (MI_XLOG_HDR *)xlog_buffer;
+
+    if (Adapter != clients[id].logical) {
+      return; /* Ignore all trace messages from other adapters */
+    }
+
+    if (TrcData->code == 24) {
+      p = (char*)&TrcData->code;
+      p += 2;
+    }
+
+    /*
+      All L1 messages start as [dsp,ch], so we can filter this information
+      and filter out all messages that use different channel
+      */
+    if (p && p[0] == '[') {
+      if (p[2] == ',') {
+        p += 3;
+        ch_value = *p - '0';
+      } else if (p[3] == ',') {
+        p += 4;
+        ch_value = *p - '0';
+      }
+      if (ch_value >= 0) {
+        if (p[2] == ']') {
+          ch_value = ch_value * 10 + p[1] - '0';
+        }
+        if (ch_value != ch) {
+          return; /* Ignore other channels */
+        }
+      }
+    }
+
+	} else if (TraceFilter[0] != 0) {
+    return; /* Ignore trace if trace filter is activated, but idle */
+  }
+
+  diva_os_get_time (&sec, &usec);
+
+  while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+                              (word)length+sizeof(*pmsg)))) {
+    if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+      queueFreeMsg (dbg_queue);
+    } else {
+      break;
+    }
+  }
+  if (pmsg) {
+    memcpy (&pmsg[1], xlog_buffer, length);
+    pmsg->sequence    = dbg_sequence++;
+    pmsg->time_sec    = sec;
+    pmsg->time_usec   = usec;
+    pmsg->facility    = MSG_TYPE_MLOG;
+    pmsg->dli         = pC->logical;
+    pmsg->drv_id      = pC->hDbg->id;
+    pmsg->di_cpu      = 0;
+    pmsg->data_length = length;
+    queueCompleteMsg (pmsg);
+    if (queueCount(dbg_queue)) {
+      diva_maint_wakeup_read();
+    }
+  }
+}
+
+
+/*
+  Convert MAINT trace mask to management interface trace mask/work/facility and
+  issue command to management interface
+  */
+static void diva_change_management_debug_mask (diva_maint_client_t* pC, dword old_mask) {
+  if (pC->request && pC->hDbg && pC->pIdiLib) {
+    dword changed = pC->hDbg->dbgMask ^ old_mask;
+
+    if (changed & DIVA_MGT_DBG_TRACE) {
+      (*(pC->pIdiLib->DivaSTraceSetInfo))(pC->pIdiLib,
+                                          (pC->hDbg->dbgMask & DIVA_MGT_DBG_TRACE) != 0);
+    }
+    if (changed & DIVA_MGT_DBG_DCHAN) {
+      (*(pC->pIdiLib->DivaSTraceSetDChannel))(pC->pIdiLib,
+                                              (pC->hDbg->dbgMask & DIVA_MGT_DBG_DCHAN) != 0);
+    }
+    if (!TraceFilter[0]) {
+      if (changed & DIVA_MGT_DBG_IFC_BCHANNEL) {
+        int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0);
+
+        for (i = 0; i < pC->channels; i++) {
+          (*(pC->pIdiLib->DivaSTraceSetBChannel))(pC->pIdiLib, i+1, state);
+        }
+      }
+      if (changed & DIVA_MGT_DBG_IFC_AUDIO) {
+        int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0);
+
+        for (i = 0; i < pC->channels; i++) {
+          (*(pC->pIdiLib->DivaSTraceSetAudioTap))(pC->pIdiLib, i+1, state);
+        }
+      }
+    }
+  }
+}
+
+
+void diva_mnt_internal_dprintf (dword drv_id, dword type, char* fmt, ...) {
+  va_list ap;
+
+	va_start(ap, fmt);
+  DI_format (0, (word)drv_id, (int)type, fmt, ap);
+	va_end(ap);
+}
+
+/*
+  Shutdown all adapters before driver removal
+  */
+int diva_mnt_shutdown_xdi_adapters (void) {
+  diva_os_spin_lock_magic_t old_irql, old_irql1;
+  int i, fret = 0;
+  byte * pmem;
+
+
+  for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) {
+    pmem = NULL;
+
+    diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "unload");
+    diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "unload");
+
+    if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) {
+      if ((*(clients[i].pIdiLib->DivaSTraceLibraryStop))(clients[i].pIdiLib) == 1) {
+        /*
+          Adapter removal complete
+          */
+        if (clients[i].pIdiLib) {
+          (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
+          clients[i].pIdiLib = NULL;
+
+          pmem = clients[i].pmem;
+          clients[i].pmem = NULL;
+        }
+        clients[i].hDbg    = NULL;
+        clients[i].request_pending = 0;
+
+        if (clients[i].dma_handle >= 0) {
+          /*
+            Free DMA handle
+            */
+          diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle);
+          clients[i].dma_handle = -1;
+				}
+        clients[i].request = NULL;
+      } else {
+        fret = -1;
+      }
+    }
+
+    diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "unload");
+    if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) {
+      clients[i].request_pending = 0;
+      (*(clients[i].request))((ENTITY*)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib));
+      if (clients[i].dma_handle >= 0) {
+        diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle);
+        clients[i].dma_handle = -1;
+      }
+    }
+    diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "unload");
+
+    if (pmem) {
+      diva_os_free (0, pmem);
+    }
+  }
+
+  return (fret);
+}
+
+/*
+  Set/Read the trace filter used for selective tracing.
+  Affects B- and Audio Tap trace mask at run time
+  */
+int diva_set_trace_filter (int filter_length, const char* filter) {
+  diva_os_spin_lock_magic_t old_irql, old_irql1;
+  int i, ch, on, client_b_on, client_atap_on;
+
+  diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+
+  if (filter_length <= DIVA_MAX_SELECTIVE_FILTER_LENGTH) {
+    memcpy (&TraceFilter[0], filter, filter_length);
+    if (TraceFilter[filter_length]) {
+      TraceFilter[filter_length] = 0;
+    }
+    if (TraceFilter[0] == '*') {
+      TraceFilter[0] = 0;
+    }
+  } else {
+    filter_length = -1;
+  }
+
+  TraceFilterIdent   = -1;
+  TraceFilterChannel = -1;
+
+  on = (TraceFilter[0] == 0);
+
+  for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) {
+    if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) {
+      client_b_on    = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0);
+      client_atap_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO)    != 0);
+      for (ch = 0; ch < clients[i].channels; ch++) {
+        (*(clients[i].pIdiLib->DivaSTraceSetBChannel))(clients[i].pIdiLib->hLib, ch+1, client_b_on);
+        (*(clients[i].pIdiLib->DivaSTraceSetAudioTap))(clients[i].pIdiLib->hLib, ch+1, client_atap_on);
+      }
+    }
+  }
+
+  for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) {
+    if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) {
+      diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+      clients[i].request_pending = 0;
+      (*(clients[i].request))((ENTITY*)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib));
+      diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+    }
+  }
+
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+  diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+
+  return (filter_length);
+}
+
+int diva_get_trace_filter (int max_length, char* filter) {
+  diva_os_spin_lock_magic_t old_irql;
+  int len;
+
+  diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read_filter");
+  len = strlen (&TraceFilter[0]) + 1;
+  if (max_length >= len) {
+    memcpy (filter, &TraceFilter[0], len);
+  }
+  diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_filter");
+
+  return (len);
+}
+
+static int diva_dbg_cmp_key (const char* ref, const char* key) {
+	while (*key && (*ref++ == *key++));
+  return (!*key && !*ref);
+}
+
+/*
+  In case trace filter starts with "C" character then
+  all following characters are interpreted as command.
+  Followings commands are available:
+  - single, trace single call at time, independent from CPN/CiPN
+  */
+static int diva_mnt_cmp_nmbr (const char* nmbr) {
+  const char* ref = &TraceFilter[0];
+  int ref_len = strlen(&TraceFilter[0]), nmbr_len = strlen(nmbr);
+
+  if (ref[0] == 'C') {
+    if (diva_dbg_cmp_key (&ref[1], "single")) {
+      return (0);
+    }
+    return (-1);
+  }
+
+  if (!ref_len || (ref_len > nmbr_len)) {
+    return (-1);
+  }
+
+  nmbr = nmbr + nmbr_len - 1;
+  ref  = ref  + ref_len  - 1;
+
+  while (ref_len--) {
+    if (*nmbr-- != *ref--) {
+      return (-1);
+    }
+  }
+
+  return (0);
+}
+
+static int diva_get_dma_descriptor (IDI_CALL request, dword *dma_magic) {
+  ENTITY e;
+  IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+  if (!request) {
+    return (-1);
+  }
+
+  pReq->xdi_dma_descriptor_operation.Req = 0;
+  pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+  pReq->xdi_dma_descriptor_operation.info.operation =     IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_number  = -1;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
+
+  (*request)((ENTITY*)pReq);
+
+  if (!pReq->xdi_dma_descriptor_operation.info.operation &&
+      (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) &&
+      pReq->xdi_dma_descriptor_operation.info.descriptor_magic) {
+    *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic;
+    return (pReq->xdi_dma_descriptor_operation.info.descriptor_number);
+  } else {
+    return (-1);
+  }
+}
+
+static void diva_free_dma_descriptor (IDI_CALL request, int nr) {
+  ENTITY e;
+  IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+  if (!request || (nr < 0)) {
+    return;
+  }
+
+  pReq->xdi_dma_descriptor_operation.Req = 0;
+  pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+  pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_number  = nr;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
+
+  (*request)((ENTITY*)pReq);
+}
+
diff --git a/drivers/isdn/hardware/eicon/debug_if.h b/drivers/isdn/hardware/eicon/debug_if.h
new file mode 100644
index 0000000..4db739d
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debug_if.h
@@ -0,0 +1,90 @@
+/*
+ *
+  Copyright (c) Eicon Technology Corporation, 2000.
+ *
+  This source file is supplied for the use with Eicon
+  Technology Corporation's range of DIVA Server Adapters.
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_DEBUG_IF_H__
+#define __DIVA_DEBUG_IF_H__
+#define MSG_TYPE_DRV_ID		0x0001
+#define MSG_TYPE_FLAGS		0x0002
+#define MSG_TYPE_STRING		0x0003
+#define MSG_TYPE_BINARY		0x0004
+#define MSG_TYPE_MLOG     0x0005
+
+#define MSG_FRAME_MAX_SIZE 2150
+
+typedef struct _diva_dbg_entry_head {
+  dword sequence;
+  dword time_sec;
+  dword time_usec;
+  dword facility;
+  dword dli;
+  dword drv_id;
+  dword di_cpu;
+  dword data_length;
+} diva_dbg_entry_head_t;
+
+int diva_maint_init (byte* base, unsigned long length, int do_init);
+void* diva_maint_finit (void);
+dword diva_dbg_q_length (void);
+diva_dbg_entry_head_t* diva_maint_get_message (word* size,
+                                               diva_os_spin_lock_magic_t* old_irql);
+void diva_maint_ack_message (int do_release,
+                             diva_os_spin_lock_magic_t* old_irql);
+void diva_maint_prtComp (char *format, ...);
+void diva_maint_wakeup_read (void);
+int diva_get_driver_info (dword id, byte* data, int data_length);
+int diva_get_driver_dbg_mask (dword id, byte* data);
+int diva_set_driver_dbg_mask (dword id, dword mask);
+void diva_mnt_remove_xdi_adapter (const DESCRIPTOR* d);
+void diva_mnt_add_xdi_adapter    (const DESCRIPTOR* d);
+int diva_mnt_shutdown_xdi_adapters (void);
+
+#define DIVA_MAX_SELECTIVE_FILTER_LENGTH 127
+int diva_set_trace_filter (int filter_length, const char* filter);
+int diva_get_trace_filter (int max_length,    char*       filter);
+
+
+#define DITRACE_CMD_GET_DRIVER_INFO   1
+#define DITRACE_READ_DRIVER_DBG_MASK  2
+#define DITRACE_WRITE_DRIVER_DBG_MASK 3
+#define DITRACE_READ_TRACE_ENTRY      4
+#define DITRACE_READ_TRACE_ENTRYS     5
+#define DITRACE_WRITE_SELECTIVE_TRACE_FILTER 6
+#define DITRACE_READ_SELECTIVE_TRACE_FILTER  7
+
+/*
+  Trace lavels for debug via management interface
+  */
+#define DIVA_MGT_DBG_TRACE          0x00000001 /* All trace messages from the card */
+#define DIVA_MGT_DBG_DCHAN          0x00000002 /* All D-channel relater trace messages */
+#define DIVA_MGT_DBG_MDM_PROGRESS   0x00000004 /* Modem progress events */
+#define DIVA_MGT_DBG_FAX_PROGRESS   0x00000008 /* Fax progress events */
+#define DIVA_MGT_DBG_IFC_STATISTICS 0x00000010 /* Interface call statistics */
+#define DIVA_MGT_DBG_MDM_STATISTICS 0x00000020 /* Global modem statistics   */
+#define DIVA_MGT_DBG_FAX_STATISTICS 0x00000040 /* Global call statistics    */
+#define DIVA_MGT_DBG_LINE_EVENTS    0x00000080 /* Line state events */
+#define DIVA_MGT_DBG_IFC_EVENTS     0x00000100 /* Interface/L1/L2 state events */
+#define DIVA_MGT_DBG_IFC_BCHANNEL   0x00000200 /* B-Channel trace for all channels */
+#define DIVA_MGT_DBG_IFC_AUDIO      0x00000400 /* Audio Tap trace for all channels */
+
+# endif /* DEBUG_IF___H */
+
+
diff --git a/drivers/isdn/hardware/eicon/debuglib.c b/drivers/isdn/hardware/eicon/debuglib.c
new file mode 100644
index 0000000..a19b7ff
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debuglib.c
@@ -0,0 +1,156 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "debuglib.h"
+
+#ifdef DIVA_NO_DEBUGLIB
+static DIVA_DI_PRINTF dprintf;
+#else /* DIVA_NO_DEBUGLIB */
+ 
+_DbgHandle_ myDriverDebugHandle = { 0 /*!Registered*/, DBG_HANDLE_VERSION };
+DIVA_DI_PRINTF dprintf = no_printf;
+/*****************************************************************************/
+#define DBG_FUNC(name) \
+void  \
+myDbgPrint_##name (char *format, ...) \
+{ va_list ap ; \
+ if ( myDriverDebugHandle.dbg_prt ) \
+ { va_start (ap, format) ; \
+  (myDriverDebugHandle.dbg_prt) \
+   (myDriverDebugHandle.id, DLI_##name, format, ap) ; \
+  va_end (ap) ; \
+} }
+DBG_FUNC(LOG)
+DBG_FUNC(FTL)
+DBG_FUNC(ERR)
+DBG_FUNC(TRC)
+DBG_FUNC(MXLOG)
+DBG_FUNC(FTL_MXLOG)
+void 
+myDbgPrint_EVL (long msgID, ...)
+{ va_list ap ;
+ if ( myDriverDebugHandle.dbg_ev )
+ { va_start (ap, msgID) ;
+  (myDriverDebugHandle.dbg_ev)
+   (myDriverDebugHandle.id, (unsigned long)msgID, ap) ;
+  va_end (ap) ;
+} }
+DBG_FUNC(REG)
+DBG_FUNC(MEM)
+DBG_FUNC(SPL)
+DBG_FUNC(IRP)
+DBG_FUNC(TIM)
+DBG_FUNC(BLK)
+DBG_FUNC(TAPI)
+DBG_FUNC(NDIS)
+DBG_FUNC(CONN)
+DBG_FUNC(STAT)
+DBG_FUNC(SEND)
+DBG_FUNC(RECV)
+DBG_FUNC(PRV0)
+DBG_FUNC(PRV1)
+DBG_FUNC(PRV2)
+DBG_FUNC(PRV3)
+/*****************************************************************************/
+int
+DbgRegister (char *drvName, char *drvTag, unsigned long dbgMask)
+{
+ int len;
+/*
+ * deregister (if already registered) and zero out myDriverDebugHandle
+ */
+ DbgDeregister () ;
+/*
+ * initialize the debug handle
+ */
+ myDriverDebugHandle.Version = DBG_HANDLE_VERSION ;
+ myDriverDebugHandle.id  = -1 ;
+ myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG) ;
+ len = strlen (drvName) ;
+ memcpy (myDriverDebugHandle.drvName, drvName,
+         (len < sizeof(myDriverDebugHandle.drvName)) ?
+    len : sizeof(myDriverDebugHandle.drvName) - 1) ;
+ len = strlen (drvTag) ;
+ memcpy (myDriverDebugHandle.drvTag, drvTag,
+         (len < sizeof(myDriverDebugHandle.drvTag)) ?
+    len : sizeof(myDriverDebugHandle.drvTag) - 1) ;
+/*
+ * Try to register debugging via old (and only) interface
+ */
+ dprintf("\000\377", &myDriverDebugHandle) ;
+ if ( myDriverDebugHandle.dbg_prt )
+ {
+  return (1) ;
+ }
+/*
+ * Check if we registered whith an old maint driver (see debuglib.h)
+ */
+ if ( myDriverDebugHandle.dbg_end != NULL
+   /* location of 'dbg_prt' in _OldDbgHandle_ struct */
+   && (myDriverDebugHandle.regTime.LowPart ||
+       myDriverDebugHandle.regTime.HighPart  ) )
+   /* same location as in _OldDbgHandle_ struct */
+ {
+  dprintf("%s: Cannot log to old maint driver !", drvName) ;
+  myDriverDebugHandle.dbg_end =
+  ((_OldDbgHandle_ *)&myDriverDebugHandle)->dbg_end ;
+  DbgDeregister () ;
+ }
+ return (0) ;
+}
+/*****************************************************************************/
+void
+DbgSetLevel (unsigned long dbgMask)
+{
+ myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG) ;
+}
+/*****************************************************************************/
+void
+DbgDeregister (void)
+{
+ if ( myDriverDebugHandle.dbg_end )
+ {
+  (myDriverDebugHandle.dbg_end)(&myDriverDebugHandle) ;
+ }
+ memset (&myDriverDebugHandle, 0, sizeof(myDriverDebugHandle)) ;
+}
+void  xdi_dbg_xlog (char* x, ...) {
+ va_list ap;
+ va_start (ap, x);
+ if (myDriverDebugHandle.dbg_end &&
+   (myDriverDebugHandle.dbg_irq || myDriverDebugHandle.dbg_old) &&
+   (myDriverDebugHandle.dbgMask & DL_STAT)) {
+  if (myDriverDebugHandle.dbg_irq) {
+   (*(myDriverDebugHandle.dbg_irq))(myDriverDebugHandle.id,
+       (x[0] != 0) ? DLI_TRC : DLI_XLOG, x, ap);
+  } else {
+   (*(myDriverDebugHandle.dbg_old))(myDriverDebugHandle.id, x, ap);
+  }
+ }
+ va_end(ap);
+}
+/*****************************************************************************/
+#endif /* DIVA_NO_DEBUGLIB */
diff --git a/drivers/isdn/hardware/eicon/debuglib.h b/drivers/isdn/hardware/eicon/debuglib.h
new file mode 100644
index 0000000..11b3b9e
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debuglib.h
@@ -0,0 +1,322 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the GNU General Public License for more details.
+ *
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#if !defined(__DEBUGLIB_H__)
+#define __DEBUGLIB_H__
+#include <stdarg.h>
+/*
+ * define global debug priorities
+ */
+#define DL_LOG  0x00000001 /* always worth mentioning */
+#define DL_FTL  0x00000002 /* always sampled error    */
+#define DL_ERR  0x00000004 /* any kind of error       */
+#define DL_TRC  0x00000008 /* verbose information     */
+#define DL_XLOG  0x00000010 /* old xlog info           */
+#define DL_MXLOG 0x00000020 /* maestra xlog info    */
+#define DL_FTL_MXLOG 0x00000021 /* fatal maestra xlog info */
+#define DL_EVL  0x00000080 /* special NT eventlog msg */
+#define DL_COMPAT (DL_MXLOG | DL_XLOG)
+#define DL_PRIOR_MASK (DL_EVL | DL_COMPAT | DL_TRC | DL_ERR | DL_FTL | DL_LOG)
+#define DLI_LOG  0x0100
+#define DLI_FTL  0x0200
+#define DLI_ERR  0x0300
+#define DLI_TRC  0x0400
+#define DLI_XLOG 0x0500
+#define DLI_MXLOG 0x0600
+#define DLI_FTL_MXLOG 0x0600
+#define DLI_EVL  0x0800
+/*
+ * define OS (operating system interface) debuglevel
+ */
+#define DL_REG  0x00000100 /* init/query registry     */
+#define DL_MEM  0x00000200 /* memory management       */
+#define DL_SPL  0x00000400 /* event/spinlock handling */
+#define DL_IRP  0x00000800 /* I/O request handling    */
+#define DL_TIM  0x00001000 /* timer/watchdog handling */
+#define DL_BLK  0x00002000 /* raw data block contents */
+#define DL_OS_MASK (DL_BLK | DL_TIM | DL_IRP | DL_SPL | DL_MEM | DL_REG)
+#define DLI_REG  0x0900
+#define DLI_MEM  0x0A00
+#define DLI_SPL  0x0B00
+#define DLI_IRP  0x0C00
+#define DLI_TIM  0x0D00
+#define DLI_BLK  0x0E00
+/*
+ * define ISDN (connection interface) debuglevel
+ */
+#define DL_TAPI  0x00010000 /* debug TAPI interface    */
+#define DL_NDIS  0x00020000 /* debug NDIS interface    */
+#define DL_CONN  0x00040000 /* connection handling     */
+#define DL_STAT  0x00080000 /* trace state machines    */
+#define DL_SEND  0x00100000 /* trace raw xmitted data  */
+#define DL_RECV  0x00200000 /* trace raw received data */
+#define DL_DATA  (DL_SEND | DL_RECV)
+#define DL_ISDN_MASK (DL_DATA | DL_STAT | DL_CONN | DL_NDIS | DL_TAPI)
+#define DLI_TAPI 0x1100
+#define DLI_NDIS 0x1200
+#define DLI_CONN 0x1300
+#define DLI_STAT 0x1400
+#define DLI_SEND 0x1500
+#define DLI_RECV 0x1600
+/*
+ * define some private (unspecified) debuglevel
+ */
+#define DL_PRV0  0x01000000
+#define DL_PRV1  0x02000000
+#define DL_PRV2  0x04000000
+#define DL_PRV3  0x08000000
+#define DL_PRIV_MASK (DL_PRV0 | DL_PRV1 | DL_PRV2 | DL_PRV3)
+#define DLI_PRV0 0x1900
+#define DLI_PRV1 0x1A00
+#define DLI_PRV2 0x1B00
+#define DLI_PRV3 0x1C00
+#define DT_INDEX(x)  ((x) & 0x000F)
+#define DL_INDEX(x)  ((((x) >> 8) & 0x00FF) - 1)
+#define DLI_NAME(x)  ((x) & 0xFF00)
+/*
+ * Debug mask for kernel mode tracing, if set the output is also sent to
+ * the system debug function. Requires that the project is compiled
+ * with _KERNEL_DBG_PRINT_
+ */
+#define DL_TO_KERNEL    0x40000000
+
+#ifdef DIVA_NO_DEBUGLIB
+#define myDbgPrint_LOG(x...) do { } while(0);
+#define myDbgPrint_FTL(x...) do { } while(0);
+#define myDbgPrint_ERR(x...) do { } while(0);
+#define myDbgPrint_TRC(x...) do { } while(0);
+#define myDbgPrint_MXLOG(x...) do { } while(0);
+#define myDbgPrint_EVL(x...) do { } while(0);
+#define myDbgPrint_REG(x...) do { } while(0);
+#define myDbgPrint_MEM(x...) do { } while(0);
+#define myDbgPrint_SPL(x...) do { } while(0);
+#define myDbgPrint_IRP(x...) do { } while(0);
+#define myDbgPrint_TIM(x...) do { } while(0);
+#define myDbgPrint_BLK(x...) do { } while(0);
+#define myDbgPrint_TAPI(x...) do { } while(0);
+#define myDbgPrint_NDIS(x...) do { } while(0);
+#define myDbgPrint_CONN(x...) do { } while(0);
+#define myDbgPrint_STAT(x...) do { } while(0);
+#define myDbgPrint_SEND(x...) do { } while(0);
+#define myDbgPrint_RECV(x...) do { } while(0);
+#define myDbgPrint_PRV0(x...) do { } while(0);
+#define myDbgPrint_PRV1(x...) do { } while(0);
+#define myDbgPrint_PRV2(x...) do { } while(0);
+#define myDbgPrint_PRV3(x...) do { } while(0);
+#define DBG_TEST(func,args) do { } while(0);
+#define DBG_EVL_ID(args) do { } while(0);
+
+#else /* DIVA_NO_DEBUGLIB */
+/*
+ * define low level macros for formatted & raw debugging
+ */
+#define DBG_DECL(func) extern void  myDbgPrint_##func (char *, ...) ;
+DBG_DECL(LOG)
+DBG_DECL(FTL)
+DBG_DECL(ERR)
+DBG_DECL(TRC)
+DBG_DECL(MXLOG)
+DBG_DECL(FTL_MXLOG)
+extern void  myDbgPrint_EVL (long, ...) ;
+DBG_DECL(REG)
+DBG_DECL(MEM)
+DBG_DECL(SPL)
+DBG_DECL(IRP)
+DBG_DECL(TIM)
+DBG_DECL(BLK)
+DBG_DECL(TAPI)
+DBG_DECL(NDIS)
+DBG_DECL(CONN)
+DBG_DECL(STAT)
+DBG_DECL(SEND)
+DBG_DECL(RECV)
+DBG_DECL(PRV0)
+DBG_DECL(PRV1)
+DBG_DECL(PRV2)
+DBG_DECL(PRV3)
+#ifdef  _KERNEL_DBG_PRINT_
+/*
+ * tracing to maint and kernel if selected in the trace mask.
+ */
+#define DBG_TEST(func,args) \
+{ if ( (myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func ) \
+ { \
+        if ( (myDriverDebugHandle.dbgMask) & DL_TO_KERNEL ) \
+            {DbgPrint args; DbgPrint ("\r\n");} \
+        myDbgPrint_##func args ; \
+} }
+#else
+/*
+ * Standard tracing to maint driver.
+ */
+#define DBG_TEST(func,args) \
+{ if ( (myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func ) \
+ { myDbgPrint_##func args ; \
+} }
+#endif
+/*
+ * For event level debug use a separate define, the paramete are
+ * different and cause compiler errors on some systems.
+ */
+#define DBG_EVL_ID(args) \
+{ if ( (myDriverDebugHandle.dbgMask) & (unsigned long)DL_EVL ) \
+ { myDbgPrint_EVL args ; \
+} }
+
+#endif /* DIVA_NO_DEBUGLIB */
+
+#define DBG_LOG(args)  DBG_TEST(LOG, args)
+#define DBG_FTL(args)  DBG_TEST(FTL, args)
+#define DBG_ERR(args)  DBG_TEST(ERR, args)
+#define DBG_TRC(args)  DBG_TEST(TRC, args)
+#define DBG_MXLOG(args)  DBG_TEST(MXLOG, args)
+#define DBG_FTL_MXLOG(args) DBG_TEST(FTL_MXLOG, args)
+#define DBG_EVL(args)  DBG_EVL_ID(args)
+#define DBG_REG(args)  DBG_TEST(REG, args)
+#define DBG_MEM(args)  DBG_TEST(MEM, args)
+#define DBG_SPL(args)  DBG_TEST(SPL, args)
+#define DBG_IRP(args)  DBG_TEST(IRP, args)
+#define DBG_TIM(args)  DBG_TEST(TIM, args)
+#define DBG_BLK(args)  DBG_TEST(BLK, args)
+#define DBG_TAPI(args)  DBG_TEST(TAPI, args)
+#define DBG_NDIS(args)  DBG_TEST(NDIS, args)
+#define DBG_CONN(args)  DBG_TEST(CONN, args)
+#define DBG_STAT(args)  DBG_TEST(STAT, args)
+#define DBG_SEND(args)  DBG_TEST(SEND, args)
+#define DBG_RECV(args)  DBG_TEST(RECV, args)
+#define DBG_PRV0(args)  DBG_TEST(PRV0, args)
+#define DBG_PRV1(args)  DBG_TEST(PRV1, args)
+#define DBG_PRV2(args)  DBG_TEST(PRV2, args)
+#define DBG_PRV3(args)  DBG_TEST(PRV3, args)
+/*
+ * prototypes for debug register/deregister functions in "debuglib.c"
+ */
+#ifdef DIVA_NO_DEBUGLIB
+#define DbgRegister(name,tag, mask) do { } while(0)
+#define DbgDeregister() do { } while(0)
+#define DbgSetLevel(mask) do { } while(0)
+#else
+extern DIVA_DI_PRINTF dprintf;
+extern int  DbgRegister (char *drvName, char *drvTag, unsigned long dbgMask) ;
+extern void DbgDeregister (void) ;
+extern void DbgSetLevel (unsigned long dbgMask) ;
+#endif
+/*
+ * driver internal structure for debug handling;
+ * in client drivers this structure is maintained in "debuglib.c",
+ * in the debug driver "debug.c" maintains a chain of such structs.
+ */
+typedef struct _DbgHandle_ *pDbgHandle ;
+typedef void ( * DbgEnd) (pDbgHandle) ;
+typedef void ( * DbgLog) (unsigned short, int, char *, va_list) ;
+typedef void ( * DbgOld) (unsigned short, char *, va_list) ;
+typedef void ( * DbgEv)  (unsigned short, unsigned long, va_list) ;
+typedef void ( * DbgIrq) (unsigned short, int, char *, va_list) ;
+typedef struct _DbgHandle_
+{ char    Registered ; /* driver successfull registered */
+#define DBG_HANDLE_REG_NEW 0x01  /* this (new) structure    */
+#define DBG_HANDLE_REG_OLD 0x7f  /* old structure (see below)  */
+ char    Version;  /* version of this structure  */
+#define DBG_HANDLE_VERSION 1   /* contains dbg_old function now */
+#define DBG_HANDLE_VER_EXT  2           /* pReserved points to extended info*/
+ short               id ;   /* internal id of registered driver */
+  struct _DbgHandle_ *next ;   /* ptr to next registered driver    */
+ struct /*LARGE_INTEGER*/ {
+  unsigned long LowPart;
+  long          HighPart;
+ }     regTime ;  /* timestamp for registration       */
+ void               *pIrp ;   /* ptr to pending i/o request       */
+ unsigned long       dbgMask ;  /* current debug mask               */
+ char                drvName[16] ; /* ASCII name of registered driver  */
+ char                drvTag[64] ; /* revision string     */
+ DbgEnd              dbg_end ;  /* function for debug closing       */
+ DbgLog              dbg_prt ;  /* function for debug appending     */
+ DbgOld              dbg_old ;  /* function for old debug appending */
+ DbgEv       dbg_ev ;  /* function for Windows NT Eventlog */
+ DbgIrq    dbg_irq ;  /* function for irql checked debug  */
+ void      *pReserved3 ;
+} _DbgHandle_ ;
+extern _DbgHandle_ myDriverDebugHandle ;
+typedef struct _OldDbgHandle_
+{ struct _OldDbgHandle_ *next ;
+ void                *pIrp ;
+ long    regTime[2] ;
+ unsigned long       dbgMask ;
+ short               id ;
+ char                drvName[78] ;
+ DbgEnd              dbg_end ;
+ DbgLog              dbg_prt ;
+} _OldDbgHandle_ ;
+/* the differences in DbgHandles
+   old:    tmp:     new:
+ 0 long next  char Registered  char Registered
+       char filler   char Version
+       short id    short id
+ 4 long pIrp  long    regTime.lo  long next
+ 8 long    regTime.lo long    regTime.hi  long    regTime.lo
+ 12 long    regTime.hi long next   long regTime.hi
+ 16 long dbgMask  long pIrp   long pIrp
+ 20 short id   long dbgMask   long dbgMask
+ 22 char    drvName[78] ..
+ 24 ..     char drvName[16]  char drvName[16]
+ 40 ..     char drvTag[64]  char drvTag[64]
+ 100 void *dbg_end ..      ..
+ 104 void *dbg_prt void *dbg_end  void *dbg_end
+ 108 ..     void *dbg_prt  void *dbg_prt
+ 112 ..     ..      void *dbg_old
+ 116 ..     ..      void *dbg_ev
+ 120 ..     ..      void *dbg_irq
+ 124 ..     ..      void *pReserved3
+ ( new->id == 0 && *((short *)&new->dbgMask) == -1 ) identifies "old",
+ new->Registered and new->Version overlay old->next,
+ new->next overlays old->pIrp, new->regTime matches old->regTime and
+ thus these fields can be maintained in new struct whithout trouble;
+ id, dbgMask, drvName, dbg_end and dbg_prt need special handling !
+*/
+#define DBG_EXT_TYPE_CARD_TRACE     0x00000001
+typedef struct
+{
+    unsigned long       ExtendedType;
+    union
+    {
+        /* DBG_EXT_TYPE_CARD_TRACE */
+        struct
+        {
+            void ( * MaskChangedNotify) (void *pContext);
+            unsigned long   ModuleTxtMask;
+            unsigned long   DebugLevel;
+            unsigned long   B_ChannelMask;
+            unsigned long   LogBufferSize;
+        } CardTrace;
+    }Data;     
+} _DbgExtendedInfo_;
+#ifndef DIVA_NO_DEBUGLIB
+/* -------------------------------------------------------------
+    Function used for xlog-style debug
+   ------------------------------------------------------------- */
+#define XDI_USE_XLOG 1
+void  xdi_dbg_xlog (char* x, ...);
+#endif /* DIVA_NO_DEBUGLIB */
+#endif /* __DEBUGLIB_H__ */
diff --git a/drivers/isdn/hardware/eicon/dfifo.h b/drivers/isdn/hardware/eicon/dfifo.h
new file mode 100644
index 0000000..9a109c7
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dfifo.h
@@ -0,0 +1,54 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_IDI_DFIFO_INC__
+#define __DIVA_IDI_DFIFO_INC__
+#define DIVA_DFIFO_CACHE_SZ   64 /* Used to isolate pipe from
+                    rest of the world
+                   should be divisible by 4
+                   */
+#define DIVA_DFIFO_RAW_SZ    (2512*8)
+#define DIVA_DFIFO_DATA_SZ   68
+#define DIVA_DFIFO_HDR_SZ    4
+#define DIVA_DFIFO_SEGMENT_SZ  (DIVA_DFIFO_DATA_SZ+DIVA_DFIFO_HDR_SZ)
+#define DIVA_DFIFO_SEGMENTS   ((DIVA_DFIFO_RAW_SZ)/(DIVA_DFIFO_SEGMENT_SZ)+1)
+#define DIVA_DFIFO_MEM_SZ (\
+        (DIVA_DFIFO_SEGMENT_SZ)*(DIVA_DFIFO_SEGMENTS)+\
+        (DIVA_DFIFO_CACHE_SZ)*2\
+             )
+#define DIVA_DFIFO_STEP DIVA_DFIFO_SEGMENT_SZ
+/* -------------------------------------------------------------------------
+  Block header layout is:
+   byte[0] -> flags
+   byte[1] -> length of data in block
+   byte[2] -> reserved
+   byte[4] -> reserved
+  ------------------------------------------------------------------------- */
+#define DIVA_DFIFO_WRAP   0x80 /* This is the last block in fifo   */
+#define DIVA_DFIFO_READY  0x40 /* This block is ready for processing */
+#define DIVA_DFIFO_LAST   0x20 /* This block is last in message      */
+#define DIVA_DFIFO_AUTO   0x10 /* Don't look for 'ready', don't ack */
+int diva_dfifo_create (void* start, int length);
+#endif
diff --git a/drivers/isdn/hardware/eicon/di.c b/drivers/isdn/hardware/eicon/di.c
new file mode 100644
index 0000000..0617d7c
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di.c
@@ -0,0 +1,835 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "di.h"
+#if !defined USE_EXTENDED_DEBUGS
+  #include "dimaint.h"
+#else
+  #define dprintf
+#endif
+#include "io.h"
+#include "dfifo.h"
+#define PR_RAM  ((struct pr_ram *)0)
+#define RAM ((struct dual *)0)
+/*------------------------------------------------------------------*/
+/* local function prototypes                                        */
+/*------------------------------------------------------------------*/
+void pr_out(ADAPTER * a);
+byte pr_dpc(ADAPTER * a);
+static byte pr_ready(ADAPTER * a);
+static byte isdn_rc(ADAPTER *, byte, byte, byte, word, dword, dword);
+static byte isdn_ind(ADAPTER *, byte, byte, byte, PBUFFER *, byte, word);
+/* -----------------------------------------------------------------
+    Functions used for the extended XDI Debug
+    macros
+    global convergence counter (used by all adapters)
+    Look by the implementation part of the functions
+    about the parameters.
+    If you change the dubugging parameters, then you should update
+    the aididbg.doc in the IDI doc's.
+   ----------------------------------------------------------------- */
+#if defined(XDI_USE_XLOG)
+#define XDI_A_NR(_x_) ((byte)(((ISDN_ADAPTER *)(_x_->io))->ANum))
+static void xdi_xlog (byte *msg, word code, int length);
+static byte xdi_xlog_sec = 0;
+#else
+#define XDI_A_NR(_x_) ((byte)0)
+#endif
+static void xdi_xlog_rc_event (byte Adapter,
+                               byte Id, byte Ch, byte Rc, byte cb, byte type);
+static void xdi_xlog_request (byte Adapter, byte Id,
+                              byte Ch, byte Req, byte type);
+static void xdi_xlog_ind (byte Adapter,
+                          byte Id,
+                          byte Ch,
+                          byte Ind,
+                          byte rnr_valid,
+                          byte rnr,
+                          byte type);
+/*------------------------------------------------------------------*/
+/* output function                                                  */
+/*------------------------------------------------------------------*/
+void pr_out(ADAPTER * a)
+{
+  byte e_no;
+  ENTITY  * this = NULL;
+  BUFFERS  *X;
+  word length;
+  word i;
+  word clength;
+  REQ * ReqOut;
+  byte more;
+  byte ReadyCount;
+  byte ReqCount;
+  byte Id;
+  dtrc(dprintf("pr_out"));
+        /* while a request is pending ...                           */
+  e_no = look_req(a);
+  if(!e_no)
+  {
+    dtrc(dprintf("no_req"));
+    return;
+  }
+  ReadyCount = pr_ready(a);
+  if(!ReadyCount)
+  {
+    dtrc(dprintf("not_ready"));
+    return;
+  }
+  ReqCount = 0;
+  while(e_no && ReadyCount) {
+    next_req(a);
+    this = entity_ptr(a, e_no);
+#ifdef USE_EXTENDED_DEBUGS
+    if ( !this )
+    {
+      DBG_FTL(("XDI: [%02x] !A%d ==> NULL entity ptr - try to ignore",
+               xdi_xlog_sec++, (int)((ISDN_ADAPTER *)a->io)->ANum))
+      e_no = look_req(a) ;
+      ReadyCount-- ;
+      continue ;
+    }
+    {
+      DBG_TRC((">A%d Id=0x%x Req=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, this->Id, this->Req))
+    }
+#else
+    dbug(dprintf("out:Req=%x,Id=%x,Ch=%x",this->Req,this->Id,this->ReqCh));
+#endif
+        /* get address of next available request buffer             */
+    ReqOut = (REQ *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextReq)];
+#if defined(DIVA_ISTREAM)
+    if (!(a->tx_stream[this->Id]   &&
+        this->Req == N_DATA)) {
+#endif
+        /* now copy the data from the current data buffer into the  */
+        /* adapters request buffer                                  */
+    length = 0;
+    i = this->XCurrent;
+    X = PTR_X(a,this);
+    while(i<this->XNum && length<270) {
+      clength = MIN((word)(270-length),X[i].PLength-this->XOffset);
+      a->ram_out_buffer(a,
+                        &ReqOut->XBuffer.P[length],
+                        PTR_P(a,this,&X[i].P[this->XOffset]),
+                        clength);
+      length +=clength;
+      this->XOffset +=clength;
+      if(this->XOffset==X[i].PLength) {
+        this->XCurrent = (byte)++i;
+        this->XOffset = 0;
+      }
+    }
+#if defined(DIVA_ISTREAM)
+   } else { /* Use CMA extension in order to transfer data to the card */
+      i = this->XCurrent;
+      X = PTR_X(a,this);
+      while (i < this->XNum) {
+        diva_istream_write (a,
+                            this->Id,
+                            PTR_P(a,this,&X[i].P[0]),
+                            X[i].PLength,
+                            ((i+1) == this->XNum),
+                            0, 0);
+        this->XCurrent = (byte)++i;
+      }
+      length = 0;
+   }
+#endif
+    a->ram_outw(a, &ReqOut->XBuffer.length, length);
+    a->ram_out(a, &ReqOut->ReqId, this->Id);
+    a->ram_out(a, &ReqOut->ReqCh, this->ReqCh);
+        /* if it's a specific request (no ASSIGN) ...                */
+    if(this->Id &0x1f) {
+        /* if buffers are left in the list of data buffers do       */
+        /* do chaining (LL_MDATA, N_MDATA)                          */
+      this->More++;
+      if(i<this->XNum && this->MInd) {
+        xdi_xlog_request (XDI_A_NR(a), this->Id, this->ReqCh, this->MInd,
+                          a->IdTypeTable[this->No]);
+        a->ram_out(a, &ReqOut->Req, this->MInd);
+        more = TRUE;
+      }
+      else {
+        xdi_xlog_request (XDI_A_NR(a), this->Id, this->ReqCh, this->Req,
+                          a->IdTypeTable[this->No]);
+        this->More |=XMOREF;
+        a->ram_out(a, &ReqOut->Req, this->Req);
+        more = FALSE;
+        if (a->FlowControlIdTable[this->ReqCh] == this->Id)
+          a->FlowControlSkipTable[this->ReqCh] = TRUE;
+        /*
+           Note that remove request was sent to the card
+           */
+        if (this->Req == REMOVE) {
+          a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_REMOVE_PENDING;
+        }
+      }
+        /* if we did chaining, this entity is put back into the     */
+        /* request queue                                            */
+      if(more) {
+        req_queue(a,this->No);
+      }
+    }
+        /* else it's a ASSIGN                                       */
+    else {
+        /* save the request code used for buffer chaining           */
+      this->MInd = 0;
+      if (this->Id==BLLC_ID) this->MInd = LL_MDATA;
+      if (this->Id==NL_ID   ||
+          this->Id==TASK_ID ||
+          this->Id==MAN_ID
+        ) this->MInd = N_MDATA;
+        /* send the ASSIGN                                          */
+      a->IdTypeTable[this->No] = this->Id;
+      xdi_xlog_request (XDI_A_NR(a),this->Id,this->ReqCh,this->Req, this->Id);
+      this->More |=XMOREF;
+      a->ram_out(a, &ReqOut->Req, this->Req);
+        /* save the reference of the ASSIGN                         */
+      assign_queue(a, this->No, a->ram_inw(a, &ReqOut->Reference));
+    }
+    a->ram_outw(a, &PR_RAM->NextReq, a->ram_inw(a, &ReqOut->next));
+    ReadyCount--;
+    ReqCount++;
+    e_no = look_req(a);
+  }
+        /* send the filled request buffers to the ISDN adapter      */
+  a->ram_out(a, &PR_RAM->ReqInput,
+             (byte)(a->ram_in(a, &PR_RAM->ReqInput) + ReqCount));
+        /* if it is a 'unreturncoded' UREMOVE request, remove the  */
+        /* Id from our table after sending the request             */
+  if(this && (this->Req==UREMOVE) && this->Id) {
+    Id = this->Id;
+    e_no = a->IdTable[Id];
+    free_entity(a, e_no);
+    for (i = 0; i < 256; i++)
+    {
+      if (a->FlowControlIdTable[i] == Id)
+        a->FlowControlIdTable[i] = 0;
+    }
+    a->IdTable[Id] = 0;
+    this->Id = 0;
+  }
+}
+static byte pr_ready(ADAPTER * a)
+{
+  byte ReadyCount;
+  ReadyCount = (byte)(a->ram_in(a, &PR_RAM->ReqOutput) -
+                      a->ram_in(a, &PR_RAM->ReqInput));
+  if(!ReadyCount) {
+    if(!a->ReadyInt) {
+      a->ram_inc(a, &PR_RAM->ReadyInt);
+      a->ReadyInt++;
+    }
+  }
+  return ReadyCount;
+}
+/*------------------------------------------------------------------*/
+/* isdn interrupt handler                                           */
+/*------------------------------------------------------------------*/
+byte pr_dpc(ADAPTER * a)
+{
+  byte Count;
+  RC * RcIn;
+  IND * IndIn;
+  byte c;
+  byte RNRId;
+  byte Rc;
+  byte Ind;
+        /* if return codes are available ...                        */
+  if((Count = a->ram_in(a, &PR_RAM->RcOutput)) != 0) {
+    dtrc(dprintf("#Rc=%x",Count));
+        /* get the buffer address of the first return code          */
+    RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextRc)];
+        /* for all return codes do ...                              */
+    while(Count--) {
+      if((Rc=a->ram_in(a, &RcIn->Rc)) != 0) {
+        dword tmp[2];
+        /*
+          Get extended information, associated with return code
+          */
+        a->ram_in_buffer(a,
+                         &RcIn->Reserved2[0],
+                         (byte*)&tmp[0],
+                         8);
+        /* call return code handler, if it is not our return code   */
+        /* the handler returns 2                                    */
+        /* for all return codes we process, we clear the Rc field   */
+        isdn_rc(a,
+                Rc,
+                a->ram_in(a, &RcIn->RcId),
+                a->ram_in(a, &RcIn->RcCh),
+                a->ram_inw(a, &RcIn->Reference),
+                tmp[0],  /* type of extended informtion */
+                tmp[1]); /* extended information        */
+        a->ram_out(a, &RcIn->Rc, 0);
+      }
+        /* get buffer address of next return code                   */
+      RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &RcIn->next)];
+    }
+        /* clear all return codes (no chaining!)                    */
+    a->ram_out(a, &PR_RAM->RcOutput ,0);
+        /* call output function                                     */
+    pr_out(a);
+  }
+        /* clear RNR flag                                           */
+  RNRId = 0;
+        /* if indications are available ...                         */
+  if((Count = a->ram_in(a, &PR_RAM->IndOutput)) != 0) {
+    dtrc(dprintf("#Ind=%x",Count));
+        /* get the buffer address of the first indication           */
+    IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextInd)];
+        /* for all indications do ...                               */
+    while(Count--) {
+        /* if the application marks an indication as RNR, all       */
+        /* indications from the same Id delivered in this interrupt */
+        /* are marked RNR                                           */
+      if(RNRId && RNRId==a->ram_in(a, &IndIn->IndId)) {
+        a->ram_out(a, &IndIn->Ind, 0);
+        a->ram_out(a, &IndIn->RNR, TRUE);
+      }
+      else {
+        Ind = a->ram_in(a, &IndIn->Ind);
+        if(Ind) {
+          RNRId = 0;
+        /* call indication handler, a return value of 2 means chain */
+        /* a return value of 1 means RNR                            */
+        /* for all indications we process, we clear the Ind field   */
+          c = isdn_ind(a,
+                       Ind,
+                       a->ram_in(a, &IndIn->IndId),
+                       a->ram_in(a, &IndIn->IndCh),
+                       &IndIn->RBuffer,
+                       a->ram_in(a, &IndIn->MInd),
+                       a->ram_inw(a, &IndIn->MLength));
+          if(c==1) {
+            dtrc(dprintf("RNR"));
+            a->ram_out(a, &IndIn->Ind, 0);
+            RNRId = a->ram_in(a, &IndIn->IndId);
+            a->ram_out(a, &IndIn->RNR, TRUE);
+          }
+        }
+      }
+        /* get buffer address of next indication                    */
+      IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &IndIn->next)];
+    }
+    a->ram_out(a, &PR_RAM->IndOutput, 0);
+  }
+  return FALSE;
+}
+byte scom_test_int(ADAPTER * a)
+{
+  return a->ram_in(a,(void *)0x3fe);
+}
+void scom_clear_int(ADAPTER * a)
+{
+  a->ram_out(a,(void *)0x3fe,0);
+}
+/*------------------------------------------------------------------*/
+/* return code handler                                              */
+/*------------------------------------------------------------------*/
+byte isdn_rc(ADAPTER * a,
+             byte Rc,
+             byte Id,
+             byte Ch,
+             word Ref,
+             dword extended_info_type,
+             dword extended_info)
+{
+  ENTITY  * this;
+  byte e_no;
+  word i;
+  int cancel_rc;
+#ifdef USE_EXTENDED_DEBUGS
+  {
+    DBG_TRC(("<A%d Id=0x%x Rc=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Rc))
+  }
+#else
+  dbug(dprintf("isdn_rc(Rc=%x,Id=%x,Ch=%x)",Rc,Id,Ch));
+#endif
+        /* check for ready interrupt                                */
+  if(Rc==READY_INT) {
+    xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 0, 0);
+    if(a->ReadyInt) {
+      a->ReadyInt--;
+      return 0;
+    }
+    return 2;
+  }
+        /* if we know this Id ...                                   */
+  e_no = a->IdTable[Id];
+  if(e_no) {
+    this = entity_ptr(a,e_no);
+    xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 0, a->IdTypeTable[this->No]);
+    this->RcCh = Ch;
+        /* if it is a return code to a REMOVE request, remove the   */
+        /* Id from our table                                        */
+    if ((a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_REMOVE_PENDING) &&
+        (Rc==OK)) {
+      if (a->IdTypeTable[e_no] == NL_ID) {
+        if (a->RcExtensionSupported &&
+            (extended_info_type != DIVA_RC_TYPE_REMOVE_COMPLETE)) {
+        dtrc(dprintf("XDI: N-REMOVE, A(%02x) Id:%02x, ignore RC=OK",
+                        XDI_A_NR(a),Id));
+          return (0);
+        }
+        if (extended_info_type == DIVA_RC_TYPE_REMOVE_COMPLETE)
+          a->RcExtensionSupported = TRUE;
+      }
+      a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_REMOVE_PENDING;
+      a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_NO_RC_CANCELLING;
+      free_entity(a, e_no);
+      for (i = 0; i < 256; i++)
+      {
+        if (a->FlowControlIdTable[i] == Id)
+          a->FlowControlIdTable[i] = 0;
+      }
+      a->IdTable[Id] = 0;
+      this->Id = 0;
+      /* ---------------------------------------------------------------
+        If we send N_DISC or N_DISK_ACK after we have received OK_FC
+        then the card will respond with OK_FC and later with RC==OK.
+        If we send N_REMOVE in this state we will receive only RC==OK
+        This will create the state in that the XDI is waiting for the
+        additional RC and does not delivery the RC to the client. This
+        code corrects the counter of outstanding RC's in this case.
+      --------------------------------------------------------------- */
+      if ((this->More & XMOREC) > 1) {
+        this->More &= ~XMOREC;
+        this->More |= 1;
+        dtrc(dprintf("XDI: correct MORE on REMOVE A(%02x) Id:%02x",
+                     XDI_A_NR(a),Id));
+      }
+    }
+    if (Rc==OK_FC) {
+      a->FlowControlIdTable[Ch] = Id;
+      a->FlowControlSkipTable[Ch] = FALSE;
+      this->Rc = Rc;
+      this->More &= ~(XBUSY | XMOREC);
+      this->complete=0xff;
+      xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+      CALLBACK(a, this);
+      return 0;
+    }
+    /*
+      New protocol code sends return codes that comes from release
+      of flow control condition marked with DIVA_RC_TYPE_OK_FC extended
+      information element type.
+      If like return code arrives then application is able to process
+      all return codes self and XDI should not cances return codes.
+      This return code does not decrement XMOREC partial return code
+      counter due to fact that it was no request for this return code,
+      also XMOREC was not incremented.
+      */
+    if (extended_info_type == DIVA_RC_TYPE_OK_FC) {
+      a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_NO_RC_CANCELLING;
+      this->Rc = Rc;
+      this->complete=0xff;
+      xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+      DBG_TRC(("XDI OK_FC A(%02x) Id:%02x Ch:%02x Rc:%02x",
+      XDI_A_NR(a), Id, Ch, Rc))
+      CALLBACK(a, this);
+      return 0;
+    }
+    cancel_rc = !(a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_NO_RC_CANCELLING);
+    if (cancel_rc && (a->FlowControlIdTable[Ch] == Id))
+    {
+      a->FlowControlIdTable[Ch] = 0;
+      if ((Rc != OK) || !a->FlowControlSkipTable[Ch])
+      {
+        this->Rc = Rc;
+        if (Ch == this->ReqCh)
+        {
+          this->More &=~(XBUSY | XMOREC);
+          this->complete=0xff;
+        }
+        xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+        CALLBACK(a, this);
+      }
+      return 0;
+    }
+    if (this->More &XMOREC)
+      this->More--;
+        /* call the application callback function                   */
+    if (((!cancel_rc) || (this->More & XMOREF)) && !(this->More & XMOREC)) {
+      this->Rc = Rc;
+      this->More &=~XBUSY;
+      this->complete=0xff;
+      xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+      CALLBACK(a, this);
+    }
+    return 0;
+  }
+        /* if it's an ASSIGN return code check if it's a return     */
+        /* code to an ASSIGN request from us                        */
+  if((Rc &0xf0)==ASSIGN_RC) {
+    e_no = get_assign(a, Ref);
+    if(e_no) {
+      this = entity_ptr(a,e_no);
+      this->Id = Id;
+      xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 2, a->IdTypeTable[this->No]);
+        /* call the application callback function                   */
+      this->Rc = Rc;
+      this->More &=~XBUSY;
+      this->complete=0xff;
+#if defined(DIVA_ISTREAM) /* { */
+      if ((Rc == ASSIGN_OK) && a->ram_offset &&
+          (a->IdTypeTable[this->No] == NL_ID) &&
+          ((extended_info_type == DIVA_RC_TYPE_RX_DMA) ||
+          (extended_info_type == DIVA_RC_TYPE_CMA_PTR)) &&
+          extended_info) {
+        dword offset = (*(a->ram_offset)) (a);
+        dword tmp[2];
+        extended_info -= offset;
+#ifdef PLATFORM_GT_32BIT
+        a->ram_in_dw(a, (void*)ULongToPtr(extended_info), (dword*)&tmp[0], 2);
+#else
+        a->ram_in_dw(a, (void*)extended_info, (dword*)&tmp[0], 2);
+#endif
+        a->tx_stream[Id]  = tmp[0];
+        a->rx_stream[Id]  = tmp[1];
+        if (extended_info_type == DIVA_RC_TYPE_RX_DMA) {
+          DBG_TRC(("Id=0x%x RxDMA=%08x:%08x",
+                    Id, a->tx_stream[Id], a->rx_stream[Id]))
+          a->misc_flags_table[this->No] |= DIVA_MISC_FLAGS_RX_DMA;
+        } else {
+          DBG_TRC(("Id=0x%x CMA=%08x:%08x",
+                    Id, a->tx_stream[Id], a->rx_stream[Id]))
+          a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA;
+          a->rx_pos[Id]     = 0;
+          a->rx_stream[Id] -= offset;
+        }
+        a->tx_pos[Id]     = 0;
+        a->tx_stream[Id] -= offset;
+      } else {
+        a->tx_stream[Id] = 0;
+        a->rx_stream[Id] = 0;
+        a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA;
+      }
+#endif /* } */
+      CALLBACK(a, this);
+      if(Rc==ASSIGN_OK) {
+        a->IdTable[Id] = e_no;
+      }
+      else
+      {
+        free_entity(a, e_no);
+        for (i = 0; i < 256; i++)
+        {
+          if (a->FlowControlIdTable[i] == Id)
+            a->FlowControlIdTable[i] = 0;
+        }
+        a->IdTable[Id] = 0;
+        this->Id = 0;
+      }
+      return 1;
+    }
+  }
+  return 2;
+}
+/*------------------------------------------------------------------*/
+/* indication handler                                               */
+/*------------------------------------------------------------------*/
+byte isdn_ind(ADAPTER * a,
+              byte Ind,
+              byte Id,
+              byte Ch,
+              PBUFFER * RBuffer,
+              byte MInd,
+              word MLength)
+{
+  ENTITY  * this;
+  word clength;
+  word offset;
+  BUFFERS  *R;
+  byte* cma = NULL;
+#ifdef USE_EXTENDED_DEBUGS
+  {
+    DBG_TRC(("<A%d Id=0x%x Ind=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Ind))
+  }
+#else
+  dbug(dprintf("isdn_ind(Ind=%x,Id=%x,Ch=%x)",Ind,Id,Ch));
+#endif
+  if(a->IdTable[Id]) {
+    this = entity_ptr(a,a->IdTable[Id]);
+    this->IndCh = Ch;
+    xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+                  0/* rnr_valid */, 0 /* rnr */, a->IdTypeTable[this->No]);
+        /* if the Receive More flag is not yet set, this is the     */
+        /* first buffer of the packet                               */
+    if(this->RCurrent==0xff) {
+        /* check for receive buffer chaining                        */
+      if(Ind==this->MInd) {
+        this->complete = 0;
+        this->Ind = MInd;
+      }
+      else {
+        this->complete = 1;
+        this->Ind = Ind;
+      }
+        /* call the application callback function for the receive   */
+        /* look ahead                                               */
+      this->RLength = MLength;
+#if defined(DIVA_ISTREAM)
+      if ((a->rx_stream[this->Id] ||
+           (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA)) &&
+          ((Ind == N_DATA) ||
+           (a->protocol_capabilities & PROTCAP_CMA_ALLPR))) {
+        PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io ;
+        if (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA) {
+#if defined(DIVA_IDI_RX_DMA)
+          dword d;
+          diva_get_dma_map_entry (\
+                   (struct _diva_dma_map_entry*)IoAdapter->dma_map,
+                   (int)a->rx_stream[this->Id], (void**)&cma, &d);
+#else
+          cma = &a->stream_buffer[0];
+          cma[0] = cma[1] = cma[2] = cma[3] = 0;
+#endif
+          this->RLength = MLength = (word)*(dword*)cma;
+          cma += 4;
+        } else {
+        int final = 0;
+        cma = &a->stream_buffer[0];
+        this->RLength = MLength = (word)diva_istream_read (a,
+                                                     Id,
+                                                     cma,
+                                                     sizeof(a->stream_buffer),
+                                                     &final, NULL, NULL);
+        }
+        IoAdapter->RBuffer.length = MIN(MLength, 270);
+        if (IoAdapter->RBuffer.length != MLength) {
+          this->complete = 0;
+        } else {
+          this->complete = 1;
+        }
+        memcpy (IoAdapter->RBuffer.P, cma, IoAdapter->RBuffer.length) ;
+        this->RBuffer = (DBUFFER *)&IoAdapter->RBuffer ;
+      }
+#endif
+      if (!cma) {
+        a->ram_look_ahead(a, RBuffer, this);
+      }
+      this->RNum = 0;
+      CALLBACK(a, this);
+        /* map entity ptr, selector could be re-mapped by call to   */
+        /* IDI from within callback                                 */
+      this = entity_ptr(a,a->IdTable[Id]);
+      xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+          1/* rnr_valid */, this->RNR/* rnr */, a->IdTypeTable[this->No]);
+        /* check for RNR                                            */
+      if(this->RNR==1) {
+        this->RNR = 0;
+        return 1;
+      }
+        /* if no buffers are provided by the application, the       */
+        /* application want to copy the data itself including       */
+        /* N_MDATA/LL_MDATA chaining                                */
+      if(!this->RNR && !this->RNum) {
+        xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+            2/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]);
+        return 0;
+      }
+        /* if there is no RNR, set the More flag                    */
+      this->RCurrent = 0;
+      this->ROffset = 0;
+    }
+    if(this->RNR==2) {
+      if(Ind!=this->MInd) {
+        this->RCurrent = 0xff;
+        this->RNR = 0;
+      }
+      return 0;
+    }
+        /* if we have received buffers from the application, copy   */
+        /* the data into these buffers                              */
+    offset = 0;
+    R = PTR_R(a,this);
+    do {
+      if(this->ROffset==R[this->RCurrent].PLength) {
+        this->ROffset = 0;
+        this->RCurrent++;
+      }
+      if (cma) {
+        clength = MIN(MLength, R[this->RCurrent].PLength-this->ROffset);
+      } else {
+        clength = MIN(a->ram_inw(a, &RBuffer->length)-offset,
+                      R[this->RCurrent].PLength-this->ROffset);
+      }
+      if(R[this->RCurrent].P) {
+        if (cma) {
+          memcpy (PTR_P(a,this,&R[this->RCurrent].P[this->ROffset]),
+                  &cma[offset],
+                  clength);
+        } else {
+          a->ram_in_buffer(a,
+                           &RBuffer->P[offset],
+                           PTR_P(a,this,&R[this->RCurrent].P[this->ROffset]),
+                           clength);
+        }
+      }
+      offset +=clength;
+      this->ROffset +=clength;
+      if (cma) {
+        if (offset >= MLength) {
+          break;
+        }
+        continue;
+      }
+    } while(offset<(a->ram_inw(a, &RBuffer->length)));
+        /* if it's the last buffer of the packet, call the          */
+        /* application callback function for the receive complete   */
+        /* call                                                     */
+    if(Ind!=this->MInd) {
+      R[this->RCurrent].PLength = this->ROffset;
+      if(this->ROffset) this->RCurrent++;
+      this->RNum = this->RCurrent;
+      this->RCurrent = 0xff;
+      this->Ind = Ind;
+      this->complete = 2;
+      xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+          3/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]);
+      CALLBACK(a, this);
+    }
+    return 0;
+  }
+  return 2;
+}
+#if defined(XDI_USE_XLOG)
+/* -----------------------------------------------------------
+   This function works in the same way as xlog on the
+   active board
+   ----------------------------------------------------------- */
+static void xdi_xlog (byte *msg, word code, int length) {
+  xdi_dbg_xlog ("\x00\x02", msg, code, length);
+}
+#endif
+/* -----------------------------------------------------------
+    This function writes the information about the Return Code
+    processing in the trace buffer. Trace ID is 221.
+    INPUT:
+        Adapter - system unicue adapter number (0 ... 255)
+        Id      - Id of the entity that had sent this return code
+        Ch      - Channel of the entity that had sent this return code
+        Rc      - return code value
+        cb:       (0...2)
+                  switch (cb) {
+                   case 0: printf ("DELIVERY"); break;
+                   case 1: printf ("CALLBACK"); break;
+                   case 2: printf ("ASSIGN"); break;
+                  }
+                  DELIVERY - have entered isdn_rc with this RC
+                  CALLBACK - about to make callback to the application
+                             for this RC
+                  ASSIGN   - about to make callback for RC that is result
+                             of ASSIGN request. It is no DELIVERY message
+                             before of this message
+        type   - the Id that was sent by the ASSIGN of this entity.
+                 This should be global Id like NL_ID, DSIG_ID, MAN_ID.
+                 An unknown Id will cause "?-" in the front of the request.
+                 In this case the log.c is to be extended.
+   ----------------------------------------------------------- */
+static void xdi_xlog_rc_event (byte Adapter,
+                               byte Id, byte Ch, byte Rc, byte cb, byte type) {
+#if defined(XDI_USE_XLOG)
+  word LogInfo[4];
+  PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
+  PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
+  PUT_WORD(&LogInfo[2], ((word)Rc | (word)(type << 8)));
+  PUT_WORD(&LogInfo[3], cb);
+  xdi_xlog ((byte*)&LogInfo[0], 221, sizeof(LogInfo));
+#endif
+}
+/* ------------------------------------------------------------------------
+    This function writes the information about the request processing
+    in the trace buffer. Trace ID is 220.
+    INPUT:
+        Adapter - system unicue adapter number (0 ... 255)
+        Id      - Id of the entity that had sent this request
+        Ch      - Channel of the entity that had sent this request
+        Req     - Code of the request
+        type    - the Id that was sent by the ASSIGN of this entity.
+                  This should be global Id like NL_ID, DSIG_ID, MAN_ID.
+                  An unknown Id will cause "?-" in the front of the request.
+                  In this case the log.c is to be extended.
+   ------------------------------------------------------------------------ */
+static void xdi_xlog_request (byte Adapter, byte Id,
+                              byte Ch, byte Req, byte type) {
+#if defined(XDI_USE_XLOG)
+  word LogInfo[3];
+  PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
+  PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
+  PUT_WORD(&LogInfo[2], ((word)Req | (word)(type << 8)));
+  xdi_xlog ((byte*)&LogInfo[0], 220, sizeof(LogInfo));
+#endif
+}
+/* ------------------------------------------------------------------------
+    This function writes the information about the indication processing
+    in the trace buffer. Trace ID is 222.
+    INPUT:
+        Adapter - system unicue adapter number (0 ... 255)
+        Id      - Id of the entity that had sent this indication
+        Ch      - Channel of the entity that had sent this indication
+        Ind     - Code of the indication
+        rnr_valid: (0 .. 3) supported
+          switch (rnr_valid) {
+            case 0: printf ("DELIVERY"); break;
+            case 1: printf ("RNR=%d", rnr);
+            case 2: printf ("RNum=0");
+            case 3: printf ("COMPLETE");
+          }
+          DELIVERY - indication entered isdn_rc function
+          RNR=...  - application had returned RNR=... after the
+                     look ahead callback
+          RNum=0   - aplication had not returned any buffer to copy
+                     this indication and will copy it self
+          COMPLETE - XDI had copied the data to the buffers provided
+                     bu the application and is about to issue the
+                     final callback
+        rnr:  Look case 1 of the rnr_valid
+        type: the Id that was sent by the ASSIGN of this entity. This should
+              be global Id like NL_ID, DSIG_ID, MAN_ID. An unknown Id will
+              cause "?-" in the front of the request. In this case the
+              log.c is to be extended.
+   ------------------------------------------------------------------------ */
+static void xdi_xlog_ind (byte Adapter,
+                          byte Id,
+                          byte Ch,
+                          byte Ind,
+                          byte rnr_valid,
+                          byte rnr,
+                          byte type) {
+#if defined(XDI_USE_XLOG)
+  word LogInfo[4];
+  PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
+  PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
+  PUT_WORD(&LogInfo[2], ((word)Ind | (word)(type << 8)));
+  PUT_WORD(&LogInfo[3], ((word)rnr | (word)(rnr_valid << 8)));
+  xdi_xlog ((byte*)&LogInfo[0], 222, sizeof(LogInfo));
+#endif
+}
diff --git a/drivers/isdn/hardware/eicon/di.h b/drivers/isdn/hardware/eicon/di.h
new file mode 100644
index 0000000..dcf37b1
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di.h
@@ -0,0 +1,118 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+/*
+ *  some macros for detailed trace management
+ */
+#include "di_dbg.h"
+/*****************************************************************************/
+#define XMOREC 0x1f
+#define XMOREF 0x20
+#define XBUSY  0x40
+#define RMORE  0x80
+#define DIVA_MISC_FLAGS_REMOVE_PENDING    0x01
+#define DIVA_MISC_FLAGS_NO_RC_CANCELLING  0x02
+#define DIVA_MISC_FLAGS_RX_DMA            0x04
+        /* structure for all information we have to keep on a per   */
+        /* adapater basis                                           */
+typedef struct adapter_s ADAPTER;
+struct adapter_s {
+  void * io;
+  byte IdTable[256];
+  byte IdTypeTable[256];
+  byte FlowControlIdTable[256];
+  byte FlowControlSkipTable[256];
+  byte ReadyInt;
+  byte RcExtensionSupported;
+  byte misc_flags_table[256];
+  dword protocol_capabilities;
+  byte ( * ram_in)(ADAPTER * a, void * adr);
+  word ( * ram_inw)(ADAPTER * a, void * adr);
+  void (* ram_in_buffer)(ADAPTER * a, void * adr, void  * P, word length);
+  void (* ram_look_ahead)(ADAPTER * a, PBUFFER * RBuffer, ENTITY  * e);
+  void ( * ram_out)(ADAPTER * a, void * adr, byte data);
+  void ( * ram_outw)(ADAPTER * a, void * adr, word data);
+  void (* ram_out_buffer)(ADAPTER * a, void * adr, void  * P, word length);
+  void ( * ram_inc)(ADAPTER * a, void * adr);
+#if defined(DIVA_ISTREAM)
+  dword rx_stream[256];
+  dword tx_stream[256];
+  word tx_pos[256];
+  word rx_pos[256];
+  byte stream_buffer[2512];
+  dword ( * ram_offset)(ADAPTER * a);
+  void ( * ram_out_dw) (ADAPTER *a,
+                                    void *addr,
+                                    const dword* data,
+                                    int dwords);
+  void ( * ram_in_dw) (ADAPTER *a,
+                                   void *addr,
+                                   dword* data,
+                                   int dwords);
+  void ( * istream_wakeup)(ADAPTER* a);
+#else
+  byte stream_buffer[4];
+#endif
+};
+/*------------------------------------------------------------------*/
+/* public functions of IDI common code                              */
+/*------------------------------------------------------------------*/
+void pr_out(ADAPTER * a);
+byte pr_dpc(ADAPTER * a);
+byte scom_test_int(ADAPTER * a);
+void scom_clear_int(ADAPTER * a);
+/*------------------------------------------------------------------*/
+/* OS specific functions used by IDI common code                    */
+/*------------------------------------------------------------------*/
+void free_entity(ADAPTER * a, byte e_no);
+void assign_queue(ADAPTER * a, byte e_no, word ref);
+byte get_assign(ADAPTER * a, word ref);
+void req_queue(ADAPTER * a, byte e_no);
+byte look_req(ADAPTER * a);
+void next_req(ADAPTER * a);
+ENTITY  * entity_ptr(ADAPTER * a, byte e_no);
+#if defined(DIVA_ISTREAM)
+struct _diva_xdi_stream_interface;
+void diva_xdi_provide_istream_info (ADAPTER* a,
+                                    struct _diva_xdi_stream_interface* pI);
+void pr_stream (ADAPTER * a);
+int diva_istream_write (void* context,
+                        int Id,
+                        void* data,
+                        int length,
+                        int final,
+                        byte usr1,
+                        byte usr2);
+int diva_istream_read (void* context,
+                        int Id,
+                        void* data,
+                        int max_length,
+                        int* final,
+                        byte* usr1,
+                        byte* usr2);
+#if defined(DIVA_IDI_RX_DMA)
+#include "diva_dma.h"
+#endif
+#endif
diff --git a/drivers/isdn/hardware/eicon/di_dbg.h b/drivers/isdn/hardware/eicon/di_dbg.h
new file mode 100644
index 0000000..d576ff3
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di_dbg.h
@@ -0,0 +1,37 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_DI_DBG_INC__
+#define __DIVA_DI_DBG_INC__
+#if !defined (dtrc)
+#define dtrc(a)
+#endif
+#if !defined (dbug)
+#define dbug(a)
+#endif
+#if !defined USE_EXTENDED_DEBUGS
+extern void (*dprintf)(char*, ...);
+#endif
+#endif
diff --git a/drivers/isdn/hardware/eicon/di_defs.h b/drivers/isdn/hardware/eicon/di_defs.h
new file mode 100644
index 0000000..4c2f6126
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di_defs.h
@@ -0,0 +1,181 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 _DI_DEFS_  
+#define _DI_DEFS_
+        /* typedefs for our data structures                         */
+typedef struct get_name_s GET_NAME;
+/*  The entity_s structure is used to pass all
+    parameters between application and IDI   */
+typedef struct entity_s ENTITY;
+typedef struct buffers_s BUFFERS;
+typedef struct postcall_s POSTCALL;
+typedef struct get_para_s GET_PARA;
+#define BOARD_NAME_LENGTH 9
+#define IDI_CALL_LINK_T
+#define IDI_CALL_ENTITY_T
+/* typedef void ( * IDI_CALL)(ENTITY *); */
+/* --------------------------------------------------------
+    IDI_CALL
+   -------------------------------------------------------- */
+typedef void (IDI_CALL_LINK_T * IDI_CALL)(ENTITY IDI_CALL_ENTITY_T *);
+typedef struct {
+  word length;          /* length of data/parameter field           */
+  byte P[270];          /* data/parameter field                     */
+} DBUFFER;
+struct get_name_s {
+  word command;         /* command = 0x0100 */
+  byte name[BOARD_NAME_LENGTH];
+};
+struct postcall_s {
+  word      command;                           /* command = 0x0300 */
+  word      dummy;                             /* not used */
+  void      (  * callback)(void   *);      /* call back */
+  void    *context;                          /* context pointer */
+};
+#define REQ_PARA            0x0600   /* request command line parameters */
+#define REQ_PARA_LEN             1   /* number of data bytes */
+#define L1_STARTUP_DOWN_POS      0   /* '-y' command line parameter in......*/
+#define L1_STARTUP_DOWN_MSK   0x01   /* first byte position (index 0) with value 0x01 */
+struct get_para_s {
+  word  command;            /* command = 0x0600 */
+  byte  len;                /* max length of para field in bytes */
+  byte  para[REQ_PARA_LEN]; /* parameter field */
+};
+struct buffers_s {
+  word PLength;
+  byte   * P;
+};
+struct entity_s {
+  byte                  Req;            /* pending request          */
+  byte                  Rc;             /* return code received     */
+  byte                  Ind;            /* indication received      */
+  byte                  ReqCh;          /* channel of current Req   */
+  byte                  RcCh;           /* channel of current Rc    */
+  byte                  IndCh;          /* channel of current Ind   */
+  byte                  Id;             /* ID used by this entity   */
+  byte                  GlobalId;       /* reserved field           */
+  byte                  XNum;           /* number of X-buffers      */
+  byte                  RNum;           /* number of R-buffers      */
+  BUFFERS                 * X;        /* pointer to X-buffer list */
+  BUFFERS                 * R;        /* pointer to R-buffer list */
+  word                  RLength;        /* length of current R-data */
+  DBUFFER   *         RBuffer;        /* buffer of current R-data */
+  byte                  RNR;            /* receive not ready flag   */
+  byte                  complete;       /* receive complete status  */
+  IDI_CALL              callback;
+  word                  user[2];
+        /* fields used by the driver internally                     */
+  byte                  No;             /* entity number            */
+  byte                  reserved2;      /* reserved field           */
+  byte                  More;           /* R/X More flags           */
+  byte                  MInd;           /* MDATA coding for this ID */
+  byte                  XCurrent;       /* current transmit buffer  */
+  byte                  RCurrent;       /* current receive buffer   */
+  word                  XOffset;        /* offset in x-buffer       */
+  word                  ROffset;        /* offset in r-buffer       */
+};
+typedef struct {
+  byte                  type;
+  byte                  channels;
+  word                  features;
+  IDI_CALL              request;
+} DESCRIPTOR;
+        /* descriptor type field coding */
+#define IDI_ADAPTER_S           1
+#define IDI_ADAPTER_PR          2
+#define IDI_ADAPTER_DIVA        3
+#define IDI_ADAPTER_MAESTRA     4
+#define IDI_VADAPTER            0x40
+#define IDI_DRIVER              0x80
+#define IDI_DADAPTER            0xfd
+#define IDI_DIDDPNP             0xfe
+#define IDI_DIMAINT             0xff
+        /* Hardware IDs ISA PNP */
+#define HW_ID_DIVA_PRO     3    /* same as IDI_ADAPTER_DIVA    */
+#define HW_ID_MAESTRA      4    /* same as IDI_ADAPTER_MAESTRA */
+#define HW_ID_PICCOLA      5
+#define HW_ID_DIVA_PRO20   6
+#define HW_ID_DIVA20       7
+#define HW_ID_DIVA_PRO20_U 8
+#define HW_ID_DIVA20_U     9
+#define HW_ID_DIVA30       10
+#define HW_ID_DIVA30_U     11
+        /* Hardware IDs PCI */
+#define HW_ID_EICON_PCI              0x1133
+#define HW_ID_SIEMENS_PCI            0x8001 /* unused SubVendor ID for Siemens Cornet-N cards */
+#define HW_ID_PROTTYPE_CORNETN       0x0014 /* SubDevice ID for Siemens Cornet-N cards */
+#define HW_ID_FUJITSU_SIEMENS_PCI    0x110A /* SubVendor ID for Fujitsu Siemens */
+#define HW_ID_GS03_PCI               0x0021 /* SubDevice ID for Fujitsu Siemens ISDN S0 card */
+#define HW_ID_DIVA_PRO20_PCI         0xe001
+#define HW_ID_DIVA20_PCI             0xe002
+#define HW_ID_DIVA_PRO20_PCI_U       0xe003
+#define HW_ID_DIVA20_PCI_U           0xe004
+#define HW_ID_DIVA201_PCI            0xe005
+#define HW_ID_DIVA_CT_ST             0xe006
+#define HW_ID_DIVA_CT_U              0xe007
+#define HW_ID_DIVA_CTL_ST            0xe008
+#define HW_ID_DIVA_CTL_U             0xe009
+#define HW_ID_DIVA_ISDN_V90_PCI      0xe00a
+#define HW_ID_DIVA202_PCI_ST         0xe00b
+#define HW_ID_DIVA202_PCI_U          0xe00c
+#define HW_ID_DIVA_PRO30_PCI         0xe00d
+#define HW_ID_MAESTRA_PCI            0xe010
+#define HW_ID_MAESTRAQ_PCI           0xe012
+#define HW_ID_DSRV_Q8M_V2_PCI        0xe013
+#define HW_ID_MAESTRAP_PCI           0xe014
+#define HW_ID_DSRV_P30M_V2_PCI       0xe015
+#define HW_ID_DSRV_VOICE_Q8M_PCI     0xe016
+#define HW_ID_DSRV_VOICE_Q8M_V2_PCI  0xe017
+#define HW_ID_DSRV_B2M_V2_PCI        0xe018
+#define HW_ID_DSRV_VOICE_P30M_V2_PCI 0xe019
+#define HW_ID_DSRV_B2F_PCI           0xe01a
+#define HW_ID_DSRV_VOICE_B2M_V2_PCI  0xe01b
+        /* Hardware IDs USB */
+#define EICON_USB_VENDOR_ID          0x071D
+#define HW_ID_DIVA_USB_REV1          0x1000
+#define HW_ID_DIVA_USB_REV2          0x1003
+#define HW_ID_TELEDAT_SURF_USB_REV2  0x1004
+#define HW_ID_TELEDAT_SURF_USB_REV1  0x2000
+/* --------------------------------------------------------------------------
+  Adapter array change notification framework
+  -------------------------------------------------------------------------- */
+typedef void (IDI_CALL_LINK_T* didd_adapter_change_callback_t)(     void IDI_CALL_ENTITY_T * context, DESCRIPTOR* adapter, int removal);
+/* -------------------------------------------------------------------------- */
+#define DI_VOICE          0x0 /* obsolete define */
+#define DI_FAX3           0x1
+#define DI_MODEM          0x2
+#define DI_POST           0x4
+#define DI_V110           0x8
+#define DI_V120           0x10
+#define DI_POTS           0x20
+#define DI_CODEC          0x40
+#define DI_MANAGE         0x80
+#define DI_V_42           0x0100
+#define DI_EXTD_FAX       0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */
+#define DI_AT_PARSER      0x0400 /* Build-in AT Parser in the L2 */
+#define DI_VOICE_OVER_IP  0x0800 /* Voice over IP support */
+typedef void (IDI_CALL_LINK_T* _IDI_CALL)(void*, ENTITY*);  
+#endif  
diff --git a/drivers/isdn/hardware/eicon/did_vers.h b/drivers/isdn/hardware/eicon/did_vers.h
new file mode 100644
index 0000000..538c590f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/did_vers.h
@@ -0,0 +1,26 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+static char diva_didd_common_code_build[] = "102-51";  
diff --git a/drivers/isdn/hardware/eicon/diddfunc.c b/drivers/isdn/hardware/eicon/diddfunc.c
new file mode 100644
index 0000000..3029234
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diddfunc.c
@@ -0,0 +1,115 @@
+/* $Id: diddfunc.c,v 1.14.6.2 2004/08/28 20:03:53 armin Exp $
+ *
+ * DIDD Interface module for Eicon active cards.
+ * 
+ * Functions are in dadapter.c 
+ * 
+ * Copyright 2002-2003 by Armin Schindler (mac@melware.de) 
+ * Copyright 2002-2003 Cytronics & Melware (info@melware.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "dadapter.h"
+#include "divasync.h"
+
+#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+
+extern void DIVA_DIDD_Read(void *, int);
+extern char *DRIVERRELEASE_DIDD;
+static dword notify_handle;
+static DESCRIPTOR _DAdapter;
+
+/*
+ * didd callback function
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+			   int removal)
+{
+	if (adapter->type == IDI_DADAPTER) {
+		DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."))
+		return (NULL);
+	} else if (adapter->type == IDI_DIMAINT) {
+		if (removal) {
+			DbgDeregister();
+		} else {
+			DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT);
+		}
+	}
+	return (NULL);
+}
+
+/*
+ * connect to didd
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+	int x = 0;
+	int dadapter = 0;
+	IDI_SYNC_REQ req;
+	DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+	DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DADAPTER) {	/* DADAPTER found */
+			dadapter = 1;
+			memcpy(&_DAdapter, &DIDD_Table[x], sizeof(_DAdapter));
+			req.didd_notify.e.Req = 0;
+			req.didd_notify.e.Rc =
+			    IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+			req.didd_notify.info.callback = (void *)didd_callback;
+			req.didd_notify.info.context = NULL;
+			_DAdapter.request((ENTITY *) & req);
+			if (req.didd_notify.e.Rc != 0xff)
+				return (0);
+			notify_handle = req.didd_notify.info.handle;
+		} else if (DIDD_Table[x].type == IDI_DIMAINT) {	/* MAINT found */
+			DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT);
+		}
+	}
+	return (dadapter);
+}
+
+/*
+ * disconnect from didd
+ */
+static void DIVA_EXIT_FUNCTION disconnect_didd(void)
+{
+	IDI_SYNC_REQ req;
+
+	req.didd_notify.e.Req = 0;
+	req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+	req.didd_notify.info.handle = notify_handle;
+	_DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION diddfunc_init(void)
+{
+	diva_didd_load_time_init();
+
+	if (!connect_didd()) {
+		DBG_ERR(("init: failed to connect to DIDD."))
+		diva_didd_load_time_finit();
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * finit
+ */
+void DIVA_EXIT_FUNCTION diddfunc_finit(void)
+{
+	DbgDeregister();
+	disconnect_didd();
+	diva_didd_load_time_finit();
+}
diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c
new file mode 100644
index 0000000..8ab8027
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva.c
@@ -0,0 +1,660 @@
+/* $Id: diva.c,v 1.21.4.1 2004/05/08 14:33:43 armin Exp $ */
+
+#define CARDTYPE_H_WANT_DATA            1
+#define CARDTYPE_H_WANT_IDI_DATA        0
+#define CARDTYPE_H_WANT_RESOURCE_DATA   0
+#define CARDTYPE_H_WANT_FILE_DATA       0
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "di_defs.h"
+#include "di.h"
+#include "io.h"
+#include "pc_maint.h"
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "diva_pci.h"
+#include "diva.h"
+
+#ifdef CONFIG_ISDN_DIVAS_PRIPCI
+#include "os_pri.h"
+#endif
+#ifdef CONFIG_ISDN_DIVAS_BRIPCI
+#include "os_bri.h"
+#include "os_4bri.h"
+#endif
+
+PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+extern IDI_CALL Requests[MAX_ADAPTER];
+extern int create_adapter_proc(diva_os_xdi_adapter_t * a);
+extern void remove_adapter_proc(diva_os_xdi_adapter_t * a);
+
+#define DivaIdiReqFunc(N) \
+static void DivaIdiRequest##N(ENTITY *e) \
+{ if ( IoAdapters[N] ) (* IoAdapters[N]->DIRequest)(IoAdapters[N], e) ; }
+
+/*
+**  Create own 32 Adapters
+*/
+DivaIdiReqFunc(0)
+DivaIdiReqFunc(1)
+DivaIdiReqFunc(2)
+DivaIdiReqFunc(3)
+DivaIdiReqFunc(4)
+DivaIdiReqFunc(5)
+DivaIdiReqFunc(6)
+DivaIdiReqFunc(7)
+DivaIdiReqFunc(8)
+DivaIdiReqFunc(9)
+DivaIdiReqFunc(10)
+DivaIdiReqFunc(11)
+DivaIdiReqFunc(12)
+DivaIdiReqFunc(13)
+DivaIdiReqFunc(14)
+DivaIdiReqFunc(15)
+DivaIdiReqFunc(16)
+DivaIdiReqFunc(17)
+DivaIdiReqFunc(18)
+DivaIdiReqFunc(19)
+DivaIdiReqFunc(20)
+DivaIdiReqFunc(21)
+DivaIdiReqFunc(22)
+DivaIdiReqFunc(23)
+DivaIdiReqFunc(24)
+DivaIdiReqFunc(25)
+DivaIdiReqFunc(26)
+DivaIdiReqFunc(27)
+DivaIdiReqFunc(28)
+DivaIdiReqFunc(29)
+DivaIdiReqFunc(30)
+DivaIdiReqFunc(31)
+
+struct pt_regs;
+
+/*
+**  LOCALS
+*/
+static LIST_HEAD(adapter_queue);
+
+typedef struct _diva_get_xlog {
+	word command;
+	byte req;
+	byte rc;
+	byte data[sizeof(struct mi_pc_maint)];
+} diva_get_xlog_t;
+
+typedef struct _diva_supported_cards_info {
+	int CardOrdinal;
+	diva_init_card_proc_t init_card;
+} diva_supported_cards_info_t;
+
+static diva_supported_cards_info_t divas_supported_cards[] = {
+#ifdef CONFIG_ISDN_DIVAS_PRIPCI
+	/*
+	   PRI Cards
+	 */
+	{CARDTYPE_DIVASRV_P_30M_PCI, diva_pri_init_card},
+	/*
+	   PRI Rev.2 Cards
+	 */
+	{CARDTYPE_DIVASRV_P_30M_V2_PCI, diva_pri_init_card},
+	/*
+	   PRI Rev.2 VoIP Cards
+	 */
+	{CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI, diva_pri_init_card},
+#endif
+#ifdef CONFIG_ISDN_DIVAS_BRIPCI
+	/*
+	   4BRI Rev 1 Cards
+	 */
+	{CARDTYPE_DIVASRV_Q_8M_PCI, diva_4bri_init_card},
+	{CARDTYPE_DIVASRV_VOICE_Q_8M_PCI, diva_4bri_init_card},
+	/*
+	   4BRI Rev 2 Cards
+	 */
+	{CARDTYPE_DIVASRV_Q_8M_V2_PCI, diva_4bri_init_card},
+	{CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI, diva_4bri_init_card},
+	/*
+	   4BRI Based BRI Rev 2 Cards
+	 */
+	{CARDTYPE_DIVASRV_B_2M_V2_PCI, diva_4bri_init_card},
+	{CARDTYPE_DIVASRV_B_2F_PCI, diva_4bri_init_card},
+	{CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI, diva_4bri_init_card},
+	/*
+	   BRI
+	 */
+	{CARDTYPE_MAESTRA_PCI, diva_bri_init_card},
+#endif
+
+	/*
+	   EOL
+	 */
+	{-1}
+};
+
+static void diva_init_request_array(void);
+static void *divas_create_pci_card(int handle, void *pci_dev_handle);
+
+static diva_os_spin_lock_t adapter_lock;
+
+static int diva_find_free_adapters(int base, int nr)
+{
+	int i;
+
+	for (i = 0; i < nr; i++) {
+		if (IoAdapters[base + i]) {
+			return (-1);
+		}
+	}
+
+	return (0);
+}
+
+static diva_os_xdi_adapter_t *diva_q_get_next(struct list_head * what)
+{
+	diva_os_xdi_adapter_t *a = NULL;
+
+	if (what && (what->next != &adapter_queue))
+		a = list_entry(what->next, diva_os_xdi_adapter_t, link);
+
+	return(a);
+}
+
+/* --------------------------------------------------------------------------
+    Add card to the card list
+   -------------------------------------------------------------------------- */
+void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	diva_os_xdi_adapter_t *pdiva, *pa;
+	int i, j, max, nr;
+
+	for (i = 0; divas_supported_cards[i].CardOrdinal != -1; i++) {
+		if (divas_supported_cards[i].CardOrdinal == CardOrdinal) {
+			if (!(pdiva = divas_create_pci_card(i, pdev))) {
+				return NULL;
+			}
+			switch (CardOrdinal) {
+			case CARDTYPE_DIVASRV_Q_8M_PCI:
+			case CARDTYPE_DIVASRV_VOICE_Q_8M_PCI:
+			case CARDTYPE_DIVASRV_Q_8M_V2_PCI:
+			case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI:
+				max = MAX_ADAPTER - 4;
+				nr = 4;
+				break;
+
+			default:
+				max = MAX_ADAPTER;
+				nr = 1;
+			}
+
+			diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
+
+			for (i = 0; i < max; i++) {
+				if (!diva_find_free_adapters(i, nr)) {
+					pdiva->controller = i + 1;
+					pdiva->xdi_adapter.ANum = pdiva->controller;
+					IoAdapters[i] = &pdiva->xdi_adapter;
+					diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+					create_adapter_proc(pdiva);	/* add adapter to proc file system */
+
+					DBG_LOG(("add %s:%d",
+						 CardProperties
+						 [CardOrdinal].Name,
+						 pdiva->controller))
+
+					diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
+					pa = pdiva;
+					for (j = 1; j < nr; j++) {	/* slave adapters, if any */
+						pa = diva_q_get_next(&pa->link);
+						if (pa && !pa->interface.cleanup_adapter_proc) {
+							pa->controller = i + 1 + j;
+							pa->xdi_adapter.ANum = pa->controller;
+							IoAdapters[i + j] = &pa->xdi_adapter;
+							diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+							DBG_LOG(("add slave adapter (%d)",
+								 pa->controller))
+							create_adapter_proc(pa);	/* add adapter to proc file system */
+							diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
+						} else {
+							DBG_ERR(("slave adapter problem"))
+							break;
+						}
+					}
+
+					diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+					return (pdiva);
+				}
+			}
+
+			diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+
+			/*
+			   Not able to add adapter - remove it and return error
+			 */
+			DBG_ERR(("can not alloc request array"))
+			diva_driver_remove_card(pdiva);
+
+			return NULL;
+		}
+	}
+
+	return NULL;
+}
+
+/* --------------------------------------------------------------------------
+    Called on driver load, MAIN, main, DriverEntry
+   -------------------------------------------------------------------------- */
+int divasa_xdi_driver_entry(void)
+{
+	diva_os_initialize_spin_lock(&adapter_lock, "adapter");
+	memset(&IoAdapters[0], 0x00, sizeof(IoAdapters));
+	diva_init_request_array();
+
+	return (0);
+}
+
+/* --------------------------------------------------------------------------
+    Remove adapter from list
+   -------------------------------------------------------------------------- */
+static diva_os_xdi_adapter_t *get_and_remove_from_queue(void)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	diva_os_xdi_adapter_t *a = NULL;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "driver_unload");
+
+	if (!list_empty(&adapter_queue)) {
+		a = list_entry(adapter_queue.next, diva_os_xdi_adapter_t, link);
+		list_del(adapter_queue.next);
+	}
+
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload");
+	return (a);
+}
+
+/* --------------------------------------------------------------------------
+    Remove card from the card list
+   -------------------------------------------------------------------------- */
+void diva_driver_remove_card(void *pdiva)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	diva_os_xdi_adapter_t *a[4];
+	diva_os_xdi_adapter_t *pa;
+	int i;
+
+	pa = a[0] = (diva_os_xdi_adapter_t *) pdiva;
+	a[1] = a[2] = a[3] = NULL;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "remode adapter");
+
+	for (i = 1; i < 4; i++) {
+		if ((pa = diva_q_get_next(&pa->link))
+		    && !pa->interface.cleanup_adapter_proc) {
+			a[i] = pa;
+		} else {
+			break;
+		}
+	}
+
+	for (i = 0; ((i < 4) && a[i]); i++) {
+		list_del(&a[i]->link);
+	}
+
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload");
+
+	(*(a[0]->interface.cleanup_adapter_proc)) (a[0]);
+
+	for (i = 0; i < 4; i++) {
+		if (a[i]) {
+			if (a[i]->controller) {
+				DBG_LOG(("remove adapter (%d)",
+					 a[i]->controller)) IoAdapters[a[i]->controller - 1] = NULL;
+				remove_adapter_proc(a[i]);
+			}
+			diva_os_free(0, a[i]);
+		}
+	}
+}
+
+/* --------------------------------------------------------------------------
+    Create diva PCI adapter and init internal adapter structures
+   -------------------------------------------------------------------------- */
+static void *divas_create_pci_card(int handle, void *pci_dev_handle)
+{
+	diva_supported_cards_info_t *pI = &divas_supported_cards[handle];
+	diva_os_spin_lock_magic_t old_irql;
+	diva_os_xdi_adapter_t *a;
+
+	DBG_LOG(("found %d-%s", pI->CardOrdinal, CardProperties[pI->CardOrdinal].Name))
+
+	if (!(a = (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a)))) {
+		DBG_ERR(("A: can't alloc adapter"));
+		return NULL;
+	}
+
+	memset(a, 0x00, sizeof(*a));
+
+	a->CardIndex = handle;
+	a->CardOrdinal = pI->CardOrdinal;
+	a->Bus = DIVAS_XDI_ADAPTER_BUS_PCI;
+	a->xdi_adapter.cardType = a->CardOrdinal;
+	a->resources.pci.bus = diva_os_get_pci_bus(pci_dev_handle);
+	a->resources.pci.func = diva_os_get_pci_func(pci_dev_handle);
+	a->resources.pci.hdev = pci_dev_handle;
+
+	/*
+	   Add master adapter first, so slave adapters will receive higher
+	   numbers as master adapter
+	 */
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+	list_add_tail(&a->link, &adapter_queue);
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+
+	if ((*(pI->init_card)) (a)) {
+		diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+		list_del(&a->link);
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+		diva_os_free(0, a);
+		DBG_ERR(("A: can't get adapter resources"));
+		return NULL;
+	}
+
+	return (a);
+}
+
+/* --------------------------------------------------------------------------
+    Called on driver unload FINIT, finit, Unload
+   -------------------------------------------------------------------------- */
+void divasa_xdi_driver_unload(void)
+{
+	diva_os_xdi_adapter_t *a;
+
+	while ((a = get_and_remove_from_queue())) {
+		if (a->interface.cleanup_adapter_proc) {
+			(*(a->interface.cleanup_adapter_proc)) (a);
+		}
+		if (a->controller) {
+			IoAdapters[a->controller - 1] = NULL;
+			remove_adapter_proc(a);
+		}
+		diva_os_free(0, a);
+	}
+	diva_os_destroy_spin_lock(&adapter_lock, "adapter");
+}
+
+/*
+**  Receive and process command from user mode utility
+*/
+void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
+			    int length,
+			    divas_xdi_copy_from_user_fn_t cp_fn)
+{
+	diva_xdi_um_cfg_cmd_t msg;
+	diva_os_xdi_adapter_t *a = NULL;
+	diva_os_spin_lock_magic_t old_irql;
+	struct list_head *tmp;
+
+	if (length < sizeof(diva_xdi_um_cfg_cmd_t)) {
+		DBG_ERR(("A: A(?) open, msg too small (%d < %d)",
+			 length, sizeof(diva_xdi_um_cfg_cmd_t)))
+		return NULL;
+	}
+	if ((*cp_fn) (os_handle, &msg, src, sizeof(msg)) <= 0) {
+		DBG_ERR(("A: A(?) open, write error"))
+		return NULL;
+	}
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "open_adapter");
+	list_for_each(tmp, &adapter_queue) {
+		a = list_entry(tmp, diva_os_xdi_adapter_t, link);
+		if (a->controller == (int)msg.adapter)
+			break;
+		a = NULL;
+	}
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "open_adapter");
+
+	if (!a) {
+		DBG_ERR(("A: A(%d) open, adapter not found", msg.adapter))
+	}
+
+	return (a);
+}
+
+/*
+**  Easy cleanup mailbox status
+*/
+void diva_xdi_close_adapter(void *adapter, void *os_handle)
+{
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
+
+	a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
+	if (a->xdi_mbox.data) {
+		diva_os_free(0, a->xdi_mbox.data);
+		a->xdi_mbox.data = NULL;
+	}
+}
+
+int
+diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
+	       int length, divas_xdi_copy_from_user_fn_t cp_fn)
+{
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
+	void *data;
+
+	if (a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY) {
+		DBG_ERR(("A: A(%d) write, mbox busy", a->controller))
+		return (-1);
+	}
+
+	if (length < sizeof(diva_xdi_um_cfg_cmd_t)) {
+		DBG_ERR(("A: A(%d) write, message too small (%d < %d)",
+			 a->controller, length,
+			 sizeof(diva_xdi_um_cfg_cmd_t)))
+		return (-3);
+	}
+
+	if (!(data = diva_os_malloc(0, length))) {
+		DBG_ERR(("A: A(%d) write, ENOMEM", a->controller))
+		return (-2);
+	}
+
+	length = (*cp_fn) (os_handle, data, src, length);
+	if (length > 0) {
+		if ((*(a->interface.cmd_proc))
+		    (a, (diva_xdi_um_cfg_cmd_t *) data, length)) {
+			length = -3;
+		}
+	} else {
+		DBG_ERR(("A: A(%d) write error (%d)", a->controller,
+			 length))
+	}
+
+	diva_os_free(0, data);
+
+	return (length);
+}
+
+/*
+**  Write answers to user mode utility, if any
+*/
+int
+diva_xdi_read(void *adapter, void *os_handle, void __user *dst,
+	      int max_length, divas_xdi_copy_to_user_fn_t cp_fn)
+{
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
+	int ret;
+
+	if (!(a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY)) {
+		DBG_ERR(("A: A(%d) rx mbox empty", a->controller))
+		return (-1);
+	}
+	if (!a->xdi_mbox.data) {
+		a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
+		DBG_ERR(("A: A(%d) rx ENOMEM", a->controller))
+		return (-2);
+	}
+
+	if (max_length < a->xdi_mbox.data_length) {
+		DBG_ERR(("A: A(%d) rx buffer too short(%d < %d)",
+			 a->controller, max_length,
+			 a->xdi_mbox.data_length))
+		return (-3);
+	}
+
+	ret = (*cp_fn) (os_handle, dst, a->xdi_mbox.data,
+		      a->xdi_mbox.data_length);
+	if (ret > 0) {
+		diva_os_free(0, a->xdi_mbox.data);
+		a->xdi_mbox.data = NULL;
+		a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
+	}
+
+	return (ret);
+}
+
+
+irqreturn_t diva_os_irq_wrapper(int irq, void *context, struct pt_regs *regs)
+{
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) context;
+	diva_xdi_clear_interrupts_proc_t clear_int_proc;
+
+	if (!a || !a->xdi_adapter.diva_isr_handler) {
+		return IRQ_NONE;
+	}
+
+	if ((clear_int_proc = a->clear_interrupts_proc)) {
+		(*clear_int_proc) (a);
+		a->clear_interrupts_proc = NULL;
+		return IRQ_HANDLED;
+	}
+
+	(*(a->xdi_adapter.diva_isr_handler)) (&a->xdi_adapter);
+	return IRQ_HANDLED;
+}
+
+static void diva_init_request_array(void)
+{
+	Requests[0] = DivaIdiRequest0;
+	Requests[1] = DivaIdiRequest1;
+	Requests[2] = DivaIdiRequest2;
+	Requests[3] = DivaIdiRequest3;
+	Requests[4] = DivaIdiRequest4;
+	Requests[5] = DivaIdiRequest5;
+	Requests[6] = DivaIdiRequest6;
+	Requests[7] = DivaIdiRequest7;
+	Requests[8] = DivaIdiRequest8;
+	Requests[9] = DivaIdiRequest9;
+	Requests[10] = DivaIdiRequest10;
+	Requests[11] = DivaIdiRequest11;
+	Requests[12] = DivaIdiRequest12;
+	Requests[13] = DivaIdiRequest13;
+	Requests[14] = DivaIdiRequest14;
+	Requests[15] = DivaIdiRequest15;
+	Requests[16] = DivaIdiRequest16;
+	Requests[17] = DivaIdiRequest17;
+	Requests[18] = DivaIdiRequest18;
+	Requests[19] = DivaIdiRequest19;
+	Requests[20] = DivaIdiRequest20;
+	Requests[21] = DivaIdiRequest21;
+	Requests[22] = DivaIdiRequest22;
+	Requests[23] = DivaIdiRequest23;
+	Requests[24] = DivaIdiRequest24;
+	Requests[25] = DivaIdiRequest25;
+	Requests[26] = DivaIdiRequest26;
+	Requests[27] = DivaIdiRequest27;
+	Requests[28] = DivaIdiRequest28;
+	Requests[29] = DivaIdiRequest29;
+	Requests[30] = DivaIdiRequest30;
+	Requests[31] = DivaIdiRequest31;
+}
+
+void diva_xdi_display_adapter_features(int card)
+{
+	dword features;
+	if (!card || ((card - 1) >= MAX_ADAPTER) || !IoAdapters[card - 1]) {
+		return;
+	}
+	card--;
+	features = IoAdapters[card]->Properties.Features;
+
+	DBG_LOG(("FEATURES FOR ADAPTER: %d", card + 1))
+	DBG_LOG((" DI_FAX3          :  %s",
+		     (features & DI_FAX3) ? "Y" : "N"))
+	DBG_LOG((" DI_MODEM         :  %s",
+		     (features & DI_MODEM) ? "Y" : "N"))
+	DBG_LOG((" DI_POST          :  %s",
+		     (features & DI_POST) ? "Y" : "N"))
+	DBG_LOG((" DI_V110          :  %s",
+		     (features & DI_V110) ? "Y" : "N"))
+	DBG_LOG((" DI_V120          :  %s",
+		     (features & DI_V120) ? "Y" : "N"))
+	DBG_LOG((" DI_POTS          :  %s",
+		     (features & DI_POTS) ? "Y" : "N"))
+	DBG_LOG((" DI_CODEC         :  %s",
+		     (features & DI_CODEC) ? "Y" : "N"))
+	DBG_LOG((" DI_MANAGE        :  %s",
+		     (features & DI_MANAGE) ? "Y" : "N"))
+	DBG_LOG((" DI_V_42          :  %s",
+		     (features & DI_V_42) ? "Y" : "N"))
+	DBG_LOG((" DI_EXTD_FAX      :  %s",
+		     (features & DI_EXTD_FAX) ? "Y" : "N"))
+	DBG_LOG((" DI_AT_PARSER     :  %s",
+		     (features & DI_AT_PARSER) ? "Y" : "N"))
+	DBG_LOG((" DI_VOICE_OVER_IP :  %s",
+		     (features & DI_VOICE_OVER_IP) ? "Y" : "N"))
+}
+
+void diva_add_slave_adapter(diva_os_xdi_adapter_t * a)
+{
+	diva_os_spin_lock_magic_t old_irql;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add_slave");
+	list_add_tail(&a->link, &adapter_queue);
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add_slave");
+}
+
+int diva_card_read_xlog(diva_os_xdi_adapter_t * a)
+{
+	diva_get_xlog_t *req;
+	byte *data;
+
+	if (!a->xdi_adapter.Initialized || !a->xdi_adapter.DIRequest) {
+		return (-1);
+	}
+	if (!(data = diva_os_malloc(0, sizeof(struct mi_pc_maint)))) {
+		return (-1);
+	}
+	memset(data, 0x00, sizeof(struct mi_pc_maint));
+
+	if (!(req = diva_os_malloc(0, sizeof(*req)))) {
+		diva_os_free(0, data);
+		return (-1);
+	}
+	req->command = 0x0400;
+	req->req = LOG;
+	req->rc = 0x00;
+
+	(*(a->xdi_adapter.DIRequest)) (&a->xdi_adapter, (ENTITY *) req);
+
+	if (!req->rc || req->req) {
+		diva_os_free(0, data);
+		diva_os_free(0, req);
+		return (-1);
+	}
+
+	memcpy(data, &req->req, sizeof(struct mi_pc_maint));
+
+	diva_os_free(0, req);
+
+	a->xdi_mbox.data_length = sizeof(struct mi_pc_maint);
+	a->xdi_mbox.data = data;
+	a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+
+	return (0);
+}
+
+void xdiFreeFile(void *handle)
+{
+}
diff --git a/drivers/isdn/hardware/eicon/diva.h b/drivers/isdn/hardware/eicon/diva.h
new file mode 100644
index 0000000..e979085
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva.h
@@ -0,0 +1,31 @@
+/* $Id: diva.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
+
+#ifndef __DIVA_XDI_OS_PART_H__
+#define __DIVA_XDI_OS_PART_H__
+
+
+int divasa_xdi_driver_entry(void);
+void divasa_xdi_driver_unload(void);
+void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal);
+void diva_driver_remove_card(void *pdiva);
+
+typedef int (*divas_xdi_copy_to_user_fn_t) (void *os_handle, void __user *dst,
+					    const void *src, int length);
+
+typedef int (*divas_xdi_copy_from_user_fn_t) (void *os_handle, void *dst,
+					      const void __user *src, int length);
+
+int diva_xdi_read(void *adapter, void *os_handle, void __user *dst,
+		  int max_length, divas_xdi_copy_to_user_fn_t cp_fn);
+
+int diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
+		   int length, divas_xdi_copy_from_user_fn_t cp_fn);
+
+void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
+			    int length,
+			    divas_xdi_copy_from_user_fn_t cp_fn);
+
+void diva_xdi_close_adapter(void *adapter, void *os_handle);
+
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/diva_didd.c b/drivers/isdn/hardware/eicon/diva_didd.c
new file mode 100644
index 0000000..7fdf8ae
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_didd.c
@@ -0,0 +1,151 @@
+/* $Id: diva_didd.c,v 1.13.6.4 2005/02/11 19:40:25 armin Exp $
+ *
+ * DIDD Interface module for Eicon active cards.
+ * 
+ * Functions are in dadapter.c 
+ * 
+ * Copyright 2002-2003 by Armin Schindler (mac@melware.de) 
+ * Copyright 2002-2003 Cytronics & Melware (info@melware.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+
+#include "platform.h"
+#include "di_defs.h"
+#include "dadapter.h"
+#include "divasync.h"
+#include "did_vers.h"
+
+static char *main_revision = "$Revision: 1.13.6.4 $";
+
+static char *DRIVERNAME =
+    "Eicon DIVA - DIDD table (http://www.melware.net)";
+static char *DRIVERLNAME = "divadidd";
+char *DRIVERRELEASE_DIDD = "2.0";
+
+static char *main_proc_dir = "eicon";
+
+MODULE_DESCRIPTION("DIDD table driver for diva drivers");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("Eicon diva drivers");
+MODULE_LICENSE("GPL");
+
+#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+extern int diddfunc_init(void);
+extern void diddfunc_finit(void);
+
+extern void DIVA_DIDD_Read(void *, int);
+
+static struct proc_dir_entry *proc_didd;
+struct proc_dir_entry *proc_net_eicon = NULL;
+
+EXPORT_SYMBOL(DIVA_DIDD_Read);
+EXPORT_SYMBOL(proc_net_eicon);
+
+static char *getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "1.0";
+	return rev;
+}
+
+static int
+proc_read(char *page, char **start, off_t off, int count, int *eof,
+	  void *data)
+{
+	int len = 0;
+	char tmprev[32];
+
+	strcpy(tmprev, main_revision);
+	len += sprintf(page + len, "%s\n", DRIVERNAME);
+	len += sprintf(page + len, "name     : %s\n", DRIVERLNAME);
+	len += sprintf(page + len, "release  : %s\n", DRIVERRELEASE_DIDD);
+	len += sprintf(page + len, "build    : %s(%s)\n",
+		       diva_didd_common_code_build, DIVA_BUILD);
+	len += sprintf(page + len, "revision : %s\n", getrev(tmprev));
+
+	if (off + count >= len)
+		*eof = 1;
+	if (len < off)
+		return 0;
+	*start = page + off;
+	return ((count < len - off) ? count : len - off);
+}
+
+static int DIVA_INIT_FUNCTION create_proc(void)
+{
+	proc_net_eicon = create_proc_entry(main_proc_dir, S_IFDIR, proc_net);
+
+	if (proc_net_eicon) {
+		if ((proc_didd =
+		     create_proc_entry(DRIVERLNAME, S_IFREG | S_IRUGO,
+				       proc_net_eicon))) {
+			proc_didd->read_proc = proc_read;
+		}
+		return (1);
+	}
+	return (0);
+}
+
+static void DIVA_EXIT_FUNCTION remove_proc(void)
+{
+	remove_proc_entry(DRIVERLNAME, proc_net_eicon);
+	remove_proc_entry(main_proc_dir, proc_net);
+}
+
+static int DIVA_INIT_FUNCTION divadidd_init(void)
+{
+	char tmprev[32];
+	int ret = 0;
+
+	printk(KERN_INFO "%s\n", DRIVERNAME);
+	printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_DIDD);
+	strcpy(tmprev, main_revision);
+	printk("%s  Build:%s(%s)\n", getrev(tmprev),
+	       diva_didd_common_code_build, DIVA_BUILD);
+
+	if (!create_proc()) {
+		printk(KERN_ERR "%s: could not create proc entry\n",
+		       DRIVERLNAME);
+		ret = -EIO;
+		goto out;
+	}
+
+	if (!diddfunc_init()) {
+		printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+		       DRIVERLNAME);
+#ifdef MODULE
+		remove_proc();
+#endif
+		ret = -EIO;
+		goto out;
+	}
+
+      out:
+	return (ret);
+}
+
+static void DIVA_EXIT_FUNCTION divadidd_exit(void)
+{
+	diddfunc_finit();
+	remove_proc();
+	printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divadidd_init);
+module_exit(divadidd_exit);
diff --git a/drivers/isdn/hardware/eicon/diva_dma.c b/drivers/isdn/hardware/eicon/diva_dma.c
new file mode 100644
index 0000000..f53a740
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_dma.c
@@ -0,0 +1,94 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "diva_dma.h"
+/*
+  Every entry has length of PAGE_SIZE
+  and represents one single physical page
+  */
+struct _diva_dma_map_entry {
+  int   busy;
+  dword phys_bus_addr;  /* 32bit address as seen by the card */
+  void* local_ram_addr; /* local address as seen by the host */
+  void* addr_handle;    /* handle uset to free allocated memory */
+};
+/*
+  Create local mapping structure and init it to default state
+  */
+struct _diva_dma_map_entry* diva_alloc_dma_map (void* os_context, int nentries) {
+  diva_dma_map_entry_t* pmap = diva_os_malloc(0, sizeof(*pmap)*(nentries+1));
+  if (pmap)
+	  memset (pmap, 0, sizeof(*pmap)*(nentries+1));
+  return pmap;
+}
+/*
+  Free local map (context should be freed before) if any
+  */
+void diva_free_dma_mapping (struct _diva_dma_map_entry* pmap) {
+  if (pmap) {
+    diva_os_free (0, pmap);
+  }
+}
+/*
+  Set information saved on the map entry
+  */
+void diva_init_dma_map_entry (struct _diva_dma_map_entry* pmap,
+                              int nr, void* virt, dword phys,
+                              void* addr_handle) {
+  pmap[nr].phys_bus_addr  = phys;
+  pmap[nr].local_ram_addr = virt;
+  pmap[nr].addr_handle    = addr_handle;
+}
+/*
+  Allocate one single entry in the map
+  */
+int diva_alloc_dma_map_entry (struct _diva_dma_map_entry* pmap) {
+  int i;
+  for (i = 0; (pmap && pmap[i].local_ram_addr); i++) {
+    if (!pmap[i].busy) {
+      pmap[i].busy = 1;
+      return (i);
+    }
+  }
+  return (-1);
+}
+/*
+  Free one single entry in the map
+  */
+void diva_free_dma_map_entry (struct _diva_dma_map_entry* pmap, int nr) {
+  pmap[nr].busy = 0;
+}
+/*
+  Get information saved on the map entry
+  */
+void diva_get_dma_map_entry (struct _diva_dma_map_entry* pmap, int nr,
+                             void** pvirt, dword* pphys) {
+  *pphys = pmap[nr].phys_bus_addr;
+  *pvirt = pmap[nr].local_ram_addr;
+}
+void* diva_get_entry_handle (struct _diva_dma_map_entry* pmap, int nr) {
+  return (pmap[nr].addr_handle);
+}
diff --git a/drivers/isdn/hardware/eicon/diva_dma.h b/drivers/isdn/hardware/eicon/diva_dma.h
new file mode 100644
index 0000000..dff8072
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_dma.h
@@ -0,0 +1,48 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_DMA_MAPPING_IFC_H__
+#define __DIVA_DMA_MAPPING_IFC_H__
+typedef struct _diva_dma_map_entry  diva_dma_map_entry_t;
+struct _diva_dma_map_entry* diva_alloc_dma_map (void* os_context, int nentries);
+void diva_init_dma_map_entry (struct _diva_dma_map_entry* pmap,
+                              int nr, void* virt, dword phys,
+                              void* addr_handle);
+int diva_alloc_dma_map_entry (struct _diva_dma_map_entry* pmap);
+void diva_free_dma_map_entry (struct _diva_dma_map_entry* pmap, int entry);
+void diva_get_dma_map_entry (struct _diva_dma_map_entry* pmap, int nr,
+                             void** pvirt, dword* pphys);
+void diva_free_dma_mapping (struct _diva_dma_map_entry* pmap);
+/*
+  Functionality to be implemented by OS wrapper
+  and running in process context
+  */
+void diva_init_dma_map (void* hdev,
+                        struct _diva_dma_map_entry** ppmap,
+                        int nentries);
+void diva_free_dma_map (void* hdev,
+                        struct _diva_dma_map_entry* pmap);
+void* diva_get_entry_handle (struct _diva_dma_map_entry* pmap, int nr);
+#endif
diff --git a/drivers/isdn/hardware/eicon/diva_pci.h b/drivers/isdn/hardware/eicon/diva_pci.h
new file mode 100644
index 0000000..cc0d510
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_pci.h
@@ -0,0 +1,19 @@
+/* $Id: diva_pci.h,v 1.6 2003/01/04 15:29:45 schindler Exp $ */
+
+#ifndef __DIVA_PCI_INTERFACE_H__
+#define __DIVA_PCI_INTERFACE_H__
+
+void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a,
+			   int id,
+			   unsigned long bar,
+			   unsigned long area_length);
+void divasa_unmap_pci_bar(void __iomem *bar);
+unsigned long divasa_get_pci_irq(unsigned char bus,
+				 unsigned char func, void *pci_dev_handle);
+unsigned long divasa_get_pci_bar(unsigned char bus,
+				 unsigned char func,
+				 int bar, void *pci_dev_handle);
+byte diva_os_get_pci_bus(void *pci_dev_handle);
+byte diva_os_get_pci_func(void *pci_dev_handle);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/divacapi.h b/drivers/isdn/hardware/eicon/divacapi.h
new file mode 100644
index 0000000..9f5b680
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divacapi.h
@@ -0,0 +1,1360 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+
+/*#define DEBUG */
+
+
+
+
+  
+  
+
+
+
+
+
+#define IMPLEMENT_DTMF 1
+#define IMPLEMENT_LINE_INTERCONNECT2 1
+#define IMPLEMENT_ECHO_CANCELLER 1
+#define IMPLEMENT_RTP 1
+#define IMPLEMENT_T38 1
+#define IMPLEMENT_FAX_SUB_SEP_PWD 1
+#define IMPLEMENT_V18 1
+#define IMPLEMENT_DTMF_TONE 1
+#define IMPLEMENT_PIAFS 1
+#define IMPLEMENT_FAX_PAPER_FORMATS 1
+#define IMPLEMENT_VOWN 1
+#define IMPLEMENT_CAPIDTMF 1
+#define IMPLEMENT_FAX_NONSTANDARD 1
+#define VSWITCH_SUPPORT 1
+
+
+#define IMPLEMENT_LINE_INTERCONNECT 0
+#define IMPLEMENT_MARKED_OK_AFTER_FC 1
+
+#include "capidtmf.h"
+
+/*------------------------------------------------------------------*/
+/* Common API internal definitions                                  */
+/*------------------------------------------------------------------*/
+
+#define MAX_APPL 240
+#define MAX_NCCI           127
+
+#define MSG_IN_QUEUE_SIZE  ((4096 + 3) & 0xfffc)  /* must be multiple of 4 */
+
+
+#define MSG_IN_OVERHEAD    sizeof(APPL   *)
+
+#define MAX_NL_CHANNEL     255
+#define MAX_DATA_B3        8
+#define MAX_DATA_ACK       MAX_DATA_B3
+#define MAX_MULTI_IE       6
+#define MAX_MSG_SIZE       256
+#define MAX_MSG_PARMS      10
+#define MAX_CPN_MASK_SIZE  16
+#define MAX_MSN_CONFIG     10
+#define EXT_CONTROLLER     0x80
+#define CODEC              0x01
+#define CODEC_PERMANENT    0x02
+#define ADV_VOICE          0x03
+#define MAX_CIP_TYPES      5  /* kind of CIP types for group optimization */
+#define C_IND_MASK_DWORDS  ((MAX_APPL+32) >> 5)
+
+
+#define FAX_CONNECT_INFO_BUFFER_SIZE  256
+#define NCPI_BUFFER_SIZE              256
+
+#define MAX_CHANNELS_PER_PLCI         8
+#define MAX_INTERNAL_COMMAND_LEVELS   4
+#define INTERNAL_REQ_BUFFER_SIZE      272
+
+#define INTERNAL_IND_BUFFER_SIZE      768
+
+#define DTMF_PARAMETER_BUFFER_SIZE    12
+#define ADV_VOICE_COEF_BUFFER_SIZE    50
+
+#define LI_PLCI_B_QUEUE_ENTRIES       256
+
+
+
+typedef struct _APPL APPL;
+typedef struct _PLCI PLCI;
+typedef struct _NCCI NCCI;
+typedef struct _DIVA_CAPI_ADAPTER DIVA_CAPI_ADAPTER;
+typedef struct _DATA_B3_DESC DATA_B3_DESC;
+typedef struct _DATA_ACK_DESC DATA_ACK_DESC;
+typedef struct manufacturer_profile_s MANUFACTURER_PROFILE;
+typedef struct fax_ncpi_s FAX_NCPI;
+typedef struct api_parse_s API_PARSE;
+typedef struct api_save_s API_SAVE;
+typedef struct msn_config_s MSN_CONFIG;
+typedef struct msn_config_max_s MSN_CONFIG_MAX;
+typedef struct msn_ld_s MSN_LD;
+
+struct manufacturer_profile_s {
+  dword private_options;
+  dword rtp_primary_payloads;
+  dword rtp_additional_payloads;
+};
+
+struct fax_ncpi_s {
+  word options;
+  word format;
+};
+
+struct msn_config_s {
+  byte msn[MAX_CPN_MASK_SIZE];
+};
+
+struct msn_config_max_s {
+  MSN_CONFIG    msn_conf[MAX_MSN_CONFIG];
+};
+
+struct msn_ld_s {
+  dword low;
+  dword high;
+};
+
+struct api_parse_s {
+  word          length;
+  byte   *    info;
+};
+
+struct api_save_s {
+  API_PARSE     parms[MAX_MSG_PARMS+1];
+  byte          info[MAX_MSG_SIZE];
+};
+
+struct _DATA_B3_DESC {
+  word          Handle;
+  word          Number;
+  word          Flags;
+  word          Length;
+  void   *    P;
+};
+
+struct _DATA_ACK_DESC {
+  word          Handle;
+  word          Number;
+};
+
+typedef void (* t_std_internal_command)(dword Id, PLCI   *plci, byte Rc);
+
+/************************************************************************/
+/* Don't forget to adapt dos.asm after changing the _APPL structure!!!! */
+struct _APPL {
+  word          Id;
+  word          NullCREnable;
+  word          CDEnable;
+  dword         S_Handle;
+
+
+
+
+
+
+  LIST_ENTRY    s_function;
+  dword         s_context;
+  word          s_count;
+  APPL *        s_next;
+  byte *        xbuffer_used;
+  void **       xbuffer_internal;
+  void **       xbuffer_ptr;
+
+
+
+
+
+
+  byte   *    queue;
+  word          queue_size;
+  word          queue_free;
+  word          queue_read;
+  word          queue_write;
+  word          queue_signal;
+  byte          msg_lost;
+  byte          appl_flags;
+  word          Number;
+
+  word          MaxBuffer;
+  byte          MaxNCCI;
+  byte          MaxNCCIData;
+  word          MaxDataLength;
+  word          NCCIDataFlowCtrlTimer;
+  byte   *    ReceiveBuffer;
+  word   *    DataNCCI;
+  word   *    DataFlags;
+};
+
+
+struct _PLCI {
+  ENTITY        Sig;
+  ENTITY        NL;
+  word          RNum;
+  word          RFlags;
+  BUFFERS       RData[2];
+  BUFFERS       XData[1];
+  BUFFERS       NData[2];
+
+  DIVA_CAPI_ADAPTER   *adapter;
+  APPL      *appl;
+  PLCI      *relatedPTYPLCI;
+  byte          Id;
+  byte          State;
+  byte          sig_req;
+  byte          nl_req;
+  byte          SuppState;
+  byte          channels;
+  byte          tel;
+  byte          B1_resource;
+  byte          B2_prot;
+  byte          B3_prot;
+
+  word          command;
+  word          m_command;
+  word          internal_command;
+  word          number;
+  word          req_in_start;
+  word          req_in;
+  word          req_out;
+  word          msg_in_write_pos;
+  word          msg_in_read_pos;
+  word          msg_in_wrap_pos;
+
+  void   *    data_sent_ptr;
+  byte          data_sent;
+  byte          send_disc;
+  byte          sig_global_req;
+  byte          sig_remove_id;
+  byte          nl_global_req;
+  byte          nl_remove_id;
+  byte          b_channel;
+  byte          adv_nl;
+  byte          manufacturer;
+  byte          call_dir;
+  byte          hook_state;
+  byte          spoofed_msg;
+  byte          ptyState;
+  byte          cr_enquiry;
+  word          hangup_flow_ctrl_timer;
+
+  word          ncci_ring_list;
+  byte          inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI];
+  t_std_internal_command internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS];
+  dword         c_ind_mask_table[C_IND_MASK_DWORDS];
+  dword         group_optimization_mask_table[C_IND_MASK_DWORDS];
+  byte          RBuffer[200];
+  dword         msg_in_queue[MSG_IN_QUEUE_SIZE/sizeof(dword)];
+  API_SAVE      saved_msg;
+  API_SAVE      B_protocol;
+  byte          fax_connect_info_length;
+  byte          fax_connect_info_buffer[FAX_CONNECT_INFO_BUFFER_SIZE];
+  byte          fax_edata_ack_length;
+  word          nsf_control_bits;
+  byte          ncpi_state;
+  byte          ncpi_buffer[NCPI_BUFFER_SIZE];
+
+  byte          internal_req_buffer[INTERNAL_REQ_BUFFER_SIZE];
+  byte          internal_ind_buffer[INTERNAL_IND_BUFFER_SIZE + 3];
+  dword         requested_options_conn;
+  dword         requested_options;
+  word          B1_facilities;
+  API_SAVE   *adjust_b_parms_msg;
+  word          adjust_b_facilities;
+  word          adjust_b_command;
+  word          adjust_b_ncci;
+  word          adjust_b_mode;
+  word          adjust_b_state;
+  byte          adjust_b_restore;
+
+  byte          dtmf_rec_active;
+  word          dtmf_rec_pulse_ms;
+  word          dtmf_rec_pause_ms;
+  byte          dtmf_send_requests;
+  word          dtmf_send_pulse_ms;
+  word          dtmf_send_pause_ms;
+  word          dtmf_cmd;
+  word          dtmf_msg_number_queue[8];
+  byte          dtmf_parameter_length;
+  byte          dtmf_parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE];
+
+
+  t_capidtmf_state capidtmf_state;
+
+
+  byte          li_bchannel_id;    /* BRI: 1..2, PRI: 1..32 */
+  byte          li_channel_bits;
+  byte          li_notify_update;
+  word          li_cmd;
+  word          li_write_command;
+  word          li_write_channel;
+  word          li_plci_b_write_pos;
+  word          li_plci_b_read_pos;
+  word          li_plci_b_req_pos;
+  dword         li_plci_b_queue[LI_PLCI_B_QUEUE_ENTRIES];
+
+
+  word          ec_cmd;
+  word          ec_idi_options;
+  word          ec_tail_length;
+
+
+  byte          tone_last_indication_code;
+
+  byte          vswitchstate;
+  byte          vsprot;
+  byte          vsprotdialect;
+  byte          notifiedcall; /* Flag if it is a spoofed call */
+
+  int           rx_dma_descriptor;
+  dword         rx_dma_magic;
+};
+
+
+struct _NCCI {
+  byte          data_out;
+  byte          data_pending;
+  byte          data_ack_out;
+  byte          data_ack_pending;
+  DATA_B3_DESC  DBuffer[MAX_DATA_B3];
+  DATA_ACK_DESC DataAck[MAX_DATA_ACK];
+};
+
+
+struct _DIVA_CAPI_ADAPTER {
+  IDI_CALL      request;
+  byte          Id;
+  byte          max_plci;
+  byte          max_listen;
+  byte          listen_active;
+  PLCI      *plci;
+  byte          ch_ncci[MAX_NL_CHANNEL+1];
+  byte          ncci_ch[MAX_NCCI+1];
+  byte          ncci_plci[MAX_NCCI+1];
+  byte          ncci_state[MAX_NCCI+1];
+  byte          ncci_next[MAX_NCCI+1];
+  NCCI          ncci[MAX_NCCI+1];
+
+  byte          ch_flow_control[MAX_NL_CHANNEL+1];  /* Used by XON protocol */
+  byte          ch_flow_control_pending;
+  byte          ch_flow_plci[MAX_NL_CHANNEL+1];
+  int           last_flow_control_ch;
+
+  dword         Info_Mask[MAX_APPL];
+  dword         CIP_Mask[MAX_APPL];
+
+  dword         Notification_Mask[MAX_APPL];
+  PLCI      *codec_listen[MAX_APPL];
+  dword         requested_options_table[MAX_APPL];
+  API_PROFILE   profile;
+  MANUFACTURER_PROFILE man_profile;
+  dword         manufacturer_features;
+
+  byte          AdvCodecFLAG;
+  PLCI      *AdvCodecPLCI;
+  PLCI      *AdvSignalPLCI;
+  APPL      *AdvSignalAppl;
+  byte          TelOAD[23];
+  byte          TelOSA[23];
+  byte          scom_appl_disable;
+  PLCI      *automatic_lawPLCI;
+  byte          automatic_law;
+  byte          u_law;
+
+  byte          adv_voice_coef_length;
+  byte          adv_voice_coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE];
+
+  byte          li_pri;
+  byte          li_channels;
+  word          li_base;
+
+  byte adapter_disabled;
+  byte group_optimization_enabled; /* use application groups if enabled */
+  dword sdram_bar;
+  byte flag_dynamic_l1_down; /* for hunt groups:down layer 1 if no appl present*/
+  byte FlowControlIdTable[256];
+  byte FlowControlSkipTable[256];
+  void* os_card; /* pointer to associated OS dependent adapter structure */
+};
+
+
+/*------------------------------------------------------------------*/
+/* Application flags                                                */
+/*------------------------------------------------------------------*/
+
+#define APPL_FLAG_OLD_LI_SPEC           0x01
+#define APPL_FLAG_PRIV_EC_SPEC          0x02
+
+
+/*------------------------------------------------------------------*/
+/* API parameter definitions                                        */
+/*------------------------------------------------------------------*/
+
+#define X75_TTX         1       /* x.75 for ttx                     */
+#define TRF             2       /* transparent with hdlc framing    */
+#define TRF_IN          3       /* transparent with hdlc fr. inc.   */
+#define SDLC            4       /* sdlc, sna layer-2                */
+#define X75_BTX         5       /* x.75 for btx                     */
+#define LAPD            6       /* lapd (Q.921)                     */
+#define X25_L2          7       /* x.25 layer-2                     */
+#define V120_L2         8       /* V.120 layer-2 protocol           */
+#define V42_IN          9       /* V.42 layer-2 protocol, incomming */
+#define V42            10       /* V.42 layer-2 protocol            */
+#define MDM_ATP        11       /* AT Parser built in the L2        */
+#define X75_V42BIS     12       /* ISO7776 (X.75 SLP) modified to support V.42 bis compression */
+#define RTPL2_IN       13       /* RTP layer-2 protocol, incomming  */
+#define RTPL2          14       /* RTP layer-2 protocol             */
+#define V120_V42BIS    15       /* V.120 layer-2 protocol supporting V.42 bis compression */
+
+#define T70NL           1
+#define X25PLP          2
+#define T70NLX          3
+#define TRANSPARENT_NL  4
+#define ISO8208         5
+#define T30             6
+
+
+/*------------------------------------------------------------------*/
+/* FAX interface to IDI                                             */
+/*------------------------------------------------------------------*/
+
+#define CAPI_MAX_HEAD_LINE_SPACE        89
+#define CAPI_MAX_DATE_TIME_LENGTH       18
+
+#define T30_MAX_STATION_ID_LENGTH       20
+#define T30_MAX_SUBADDRESS_LENGTH       20
+#define T30_MAX_PASSWORD_LENGTH         20
+
+typedef struct t30_info_s T30_INFO;
+struct t30_info_s {
+  byte          code;
+  byte          rate_div_2400;
+  byte          resolution;
+  byte          data_format;
+  byte          pages_low;
+  byte          pages_high;
+  byte          operating_mode;
+  byte          control_bits_low;
+  byte          control_bits_high;
+  byte          feature_bits_low;
+  byte          feature_bits_high;
+  byte          recording_properties;
+  byte          universal_6;
+  byte          universal_7;
+  byte          station_id_len;
+  byte          head_line_len;
+  byte          station_id[T30_MAX_STATION_ID_LENGTH];
+/* byte          head_line[];      */
+/* byte          sub_sep_length;   */
+/* byte          sub_sep_field[];  */
+/* byte          pwd_length;       */
+/* byte          pwd_field[];      */
+/* byte          nsf_info_length;   */
+/* byte          nsf_info_field[];  */
+};
+
+
+#define T30_RESOLUTION_R8_0385          0x00
+#define T30_RESOLUTION_R8_0770_OR_200   0x01
+#define T30_RESOLUTION_R8_1540          0x02
+#define T30_RESOLUTION_R16_1540_OR_400  0x04
+#define T30_RESOLUTION_R4_0385_OR_100   0x08
+#define T30_RESOLUTION_300_300          0x10
+#define T30_RESOLUTION_INCH_BASED       0x40
+#define T30_RESOLUTION_METRIC_BASED     0x80
+
+#define T30_RECORDING_WIDTH_ISO_A4      0
+#define T30_RECORDING_WIDTH_ISO_B4      1
+#define T30_RECORDING_WIDTH_ISO_A3      2
+#define T30_RECORDING_WIDTH_COUNT       3
+
+#define T30_RECORDING_LENGTH_ISO_A4     0
+#define T30_RECORDING_LENGTH_ISO_B4     1
+#define T30_RECORDING_LENGTH_UNLIMITED  2
+#define T30_RECORDING_LENGTH_COUNT      3
+
+#define T30_MIN_SCANLINE_TIME_00_00_00  0
+#define T30_MIN_SCANLINE_TIME_05_05_05  1
+#define T30_MIN_SCANLINE_TIME_10_05_05  2
+#define T30_MIN_SCANLINE_TIME_10_10_10  3
+#define T30_MIN_SCANLINE_TIME_20_10_10  4
+#define T30_MIN_SCANLINE_TIME_20_20_20  5
+#define T30_MIN_SCANLINE_TIME_40_20_20  6
+#define T30_MIN_SCANLINE_TIME_40_40_40  7
+#define T30_MIN_SCANLINE_TIME_RES_8     8
+#define T30_MIN_SCANLINE_TIME_RES_9     9
+#define T30_MIN_SCANLINE_TIME_RES_10    10
+#define T30_MIN_SCANLINE_TIME_10_10_05  11
+#define T30_MIN_SCANLINE_TIME_20_10_05  12
+#define T30_MIN_SCANLINE_TIME_20_20_10  13
+#define T30_MIN_SCANLINE_TIME_40_20_10  14
+#define T30_MIN_SCANLINE_TIME_40_40_20  15
+#define T30_MIN_SCANLINE_TIME_COUNT     16
+
+#define T30_DATA_FORMAT_SFF             0
+#define T30_DATA_FORMAT_ASCII           1
+#define T30_DATA_FORMAT_NATIVE          2
+#define T30_DATA_FORMAT_COUNT           3
+
+
+#define T30_OPERATING_MODE_STANDARD     0
+#define T30_OPERATING_MODE_CLASS2       1
+#define T30_OPERATING_MODE_CLASS1       2
+#define T30_OPERATING_MODE_CAPI         3
+#define T30_OPERATING_MODE_CAPI_NEG     4
+#define T30_OPERATING_MODE_COUNT        5
+
+        /* EDATA transmit messages */
+#define EDATA_T30_DIS         0x01
+#define EDATA_T30_FTT         0x02
+#define EDATA_T30_MCF         0x03
+#define EDATA_T30_PARAMETERS  0x04
+
+        /* EDATA receive messages */
+#define EDATA_T30_DCS         0x81
+#define EDATA_T30_TRAIN_OK    0x82
+#define EDATA_T30_EOP         0x83
+#define EDATA_T30_MPS         0x84
+#define EDATA_T30_EOM         0x85
+#define EDATA_T30_DTC         0x86
+#define EDATA_T30_PAGE_END    0x87   /* Indicates end of page data. Reserved, but not implemented ! */
+#define EDATA_T30_EOP_CAPI    0x88
+
+
+#define T30_SUCCESS                        0
+#define T30_ERR_NO_DIS_RECEIVED            1
+#define T30_ERR_TIMEOUT_NO_RESPONSE        2
+#define T30_ERR_RETRY_NO_RESPONSE          3
+#define T30_ERR_TOO_MANY_REPEATS           4
+#define T30_ERR_UNEXPECTED_MESSAGE         5
+#define T30_ERR_UNEXPECTED_DCN             6
+#define T30_ERR_DTC_UNSUPPORTED            7
+#define T30_ERR_ALL_RATES_FAILED           8
+#define T30_ERR_TOO_MANY_TRAINS            9
+#define T30_ERR_RECEIVE_CORRUPTED          10
+#define T30_ERR_UNEXPECTED_DISC            11
+#define T30_ERR_APPLICATION_DISC           12
+#define T30_ERR_INCOMPATIBLE_DIS           13
+#define T30_ERR_INCOMPATIBLE_DCS           14
+#define T30_ERR_TIMEOUT_NO_COMMAND         15
+#define T30_ERR_RETRY_NO_COMMAND           16
+#define T30_ERR_TIMEOUT_COMMAND_TOO_LONG   17
+#define T30_ERR_TIMEOUT_RESPONSE_TOO_LONG  18
+#define T30_ERR_NOT_IDENTIFIED             19
+#define T30_ERR_SUPERVISORY_TIMEOUT        20
+#define T30_ERR_TOO_LONG_SCAN_LINE         21
+/* #define T30_ERR_RETRY_NO_PAGE_AFTER_MPS    22 */
+#define T30_ERR_RETRY_NO_PAGE_RECEIVED     23
+#define T30_ERR_RETRY_NO_DCS_AFTER_FTT     24
+#define T30_ERR_RETRY_NO_DCS_AFTER_EOM     25
+#define T30_ERR_RETRY_NO_DCS_AFTER_MPS     26
+#define T30_ERR_RETRY_NO_DCN_AFTER_MCF     27
+#define T30_ERR_RETRY_NO_DCN_AFTER_RTN     28
+#define T30_ERR_RETRY_NO_CFR               29
+#define T30_ERR_RETRY_NO_MCF_AFTER_EOP     30
+#define T30_ERR_RETRY_NO_MCF_AFTER_EOM     31
+#define T30_ERR_RETRY_NO_MCF_AFTER_MPS     32
+#define T30_ERR_SUB_SEP_UNSUPPORTED        33
+#define T30_ERR_PWD_UNSUPPORTED            34
+#define T30_ERR_SUB_SEP_PWD_UNSUPPORTED    35
+#define T30_ERR_INVALID_COMMAND_FRAME      36
+#define T30_ERR_UNSUPPORTED_PAGE_CODING    37
+#define T30_ERR_INVALID_PAGE_CODING        38
+#define T30_ERR_INCOMPATIBLE_PAGE_CONFIG   39
+#define T30_ERR_TIMEOUT_FROM_APPLICATION   40
+#define T30_ERR_V34FAX_NO_REACTION_ON_MARK 41
+#define T30_ERR_V34FAX_TRAINING_TIMEOUT    42
+#define T30_ERR_V34FAX_UNEXPECTED_V21      43
+#define T30_ERR_V34FAX_PRIMARY_CTS_ON      44
+#define T30_ERR_V34FAX_TURNAROUND_POLLING  45
+#define T30_ERR_V34FAX_V8_INCOMPATIBILITY  46
+
+
+#define T30_CONTROL_BIT_DISABLE_FINE       0x0001
+#define T30_CONTROL_BIT_ENABLE_ECM         0x0002
+#define T30_CONTROL_BIT_ECM_64_BYTES       0x0004
+#define T30_CONTROL_BIT_ENABLE_2D_CODING   0x0008
+#define T30_CONTROL_BIT_ENABLE_T6_CODING   0x0010
+#define T30_CONTROL_BIT_ENABLE_UNCOMPR     0x0020
+#define T30_CONTROL_BIT_ACCEPT_POLLING     0x0040
+#define T30_CONTROL_BIT_REQUEST_POLLING    0x0080
+#define T30_CONTROL_BIT_MORE_DOCUMENTS     0x0100
+#define T30_CONTROL_BIT_ACCEPT_SUBADDRESS  0x0200
+#define T30_CONTROL_BIT_ACCEPT_SEL_POLLING 0x0400
+#define T30_CONTROL_BIT_ACCEPT_PASSWORD    0x0800
+#define T30_CONTROL_BIT_ENABLE_V34FAX      0x1000
+#define T30_CONTROL_BIT_EARLY_CONNECT      0x2000
+
+#define T30_CONTROL_BIT_ALL_FEATURES  (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING |   T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR |   T30_CONTROL_BIT_ENABLE_V34FAX)
+
+#define T30_FEATURE_BIT_FINE               0x0001
+#define T30_FEATURE_BIT_ECM                0x0002
+#define T30_FEATURE_BIT_ECM_64_BYTES       0x0004
+#define T30_FEATURE_BIT_2D_CODING          0x0008
+#define T30_FEATURE_BIT_T6_CODING          0x0010
+#define T30_FEATURE_BIT_UNCOMPR_ENABLED    0x0020
+#define T30_FEATURE_BIT_POLLING            0x0040
+#define T30_FEATURE_BIT_MORE_DOCUMENTS     0x0100
+#define T30_FEATURE_BIT_V34FAX             0x1000
+
+
+#define T30_NSF_CONTROL_BIT_ENABLE_NSF     0x0001
+#define T30_NSF_CONTROL_BIT_RAW_INFO       0x0002
+#define T30_NSF_CONTROL_BIT_NEGOTIATE_IND  0x0004
+#define T30_NSF_CONTROL_BIT_NEGOTIATE_RESP 0x0008
+
+#define T30_NSF_ELEMENT_NSF_FIF            0x00
+#define T30_NSF_ELEMENT_NSC_FIF            0x01
+#define T30_NSF_ELEMENT_NSS_FIF            0x02
+#define T30_NSF_ELEMENT_COMPANY_NAME       0x03
+
+
+/*------------------------------------------------------------------*/
+/* Analog modem definitions                                         */
+/*------------------------------------------------------------------*/
+
+typedef struct async_s ASYNC_FORMAT;
+struct async_s {
+  unsigned pe:    1;
+  unsigned parity:2;
+  unsigned spare: 2;
+  unsigned stp:   1;
+  unsigned ch_len:2;   /* 3th octett in CAI */
+};
+
+
+/*------------------------------------------------------------------*/
+/* PLCI/NCCI states                                                 */
+/*------------------------------------------------------------------*/
+
+#define IDLE                    0
+#define OUTG_CON_PENDING        1
+#define INC_CON_PENDING         2
+#define INC_CON_ALERT           3
+#define INC_CON_ACCEPT          4
+#define INC_ACT_PENDING         5
+#define LISTENING               6
+#define CONNECTED               7
+#define OUTG_DIS_PENDING        8
+#define INC_DIS_PENDING         9
+#define LOCAL_CONNECT           10
+#define INC_RES_PENDING         11
+#define OUTG_RES_PENDING        12
+#define SUSPENDING              13
+#define ADVANCED_VOICE_SIG      14
+#define ADVANCED_VOICE_NOSIG    15
+#define RESUMING                16
+#define INC_CON_CONNECTED_ALERT 17
+#define OUTG_REJ_PENDING        18
+
+
+/*------------------------------------------------------------------*/
+/* auxilliary states for supplementary services                     */
+/*------------------------------------------------------------------*/
+
+#define IDLE                0
+#define HOLD_REQUEST        1
+#define HOLD_INDICATE       2
+#define CALL_HELD           3
+#define RETRIEVE_REQUEST    4
+#define RETRIEVE_INDICATION 5
+
+/*------------------------------------------------------------------*/
+/* Capi IE + Msg types                                              */
+/*------------------------------------------------------------------*/
+#define ESC_CAUSE        0x800|CAU          /* Escape cause element */
+#define ESC_MSGTYPE      0x800|MSGTYPEIE    /* Escape message type  */
+#define ESC_CHI          0x800|CHI          /* Escape channel id    */
+#define ESC_LAW          0x800|BC           /* Escape law info      */
+#define ESC_CR           0x800|CRIE         /* Escape CallReference */
+#define ESC_PROFILE      0x800|PROFILEIE    /* Escape profile       */
+#define ESC_SSEXT        0x800|SSEXTIE      /* Escape Supplem. Serv.*/
+#define ESC_VSWITCH      0x800|VSWITCHIE    /* Escape VSwitch       */
+#define CST              0x14               /* Call State i.e.      */
+#define PI               0x1E               /* Progress Indicator   */
+#define NI               0x27               /* Notification Ind     */
+#define CONN_NR          0x4C               /* Connected Number     */
+#define CONG_RNR         0xBF               /* Congestion RNR       */
+#define CONG_RR          0xB0               /* Congestion RR        */
+#define RESERVED         0xFF               /* Res. for future use  */
+#define ON_BOARD_CODEC   0x02               /* external controller  */
+#define HANDSET          0x04               /* Codec+Handset(Pro11) */
+#define HOOK_SUPPORT     0x01               /* activate Hook signal */
+#define SCR              0x7a               /* unscreened number    */
+
+#define HOOK_OFF_REQ     0x9001             /* internal conn req    */
+#define HOOK_ON_REQ      0x9002             /* internal disc req    */
+#define SUSPEND_REQ      0x9003             /* internal susp req    */
+#define RESUME_REQ       0x9004             /* internal resume req  */
+#define USELAW_REQ       0x9005             /* internal law    req  */
+#define LISTEN_SIG_ASSIGN_PEND  0x9006
+#define PERM_LIST_REQ    0x900a             /* permanent conn DCE   */
+#define C_HOLD_REQ       0x9011
+#define C_RETRIEVE_REQ   0x9012
+#define C_NCR_FAC_REQ    0x9013
+#define PERM_COD_ASSIGN  0x9014
+#define PERM_COD_CALL    0x9015
+#define PERM_COD_HOOK    0x9016
+#define PERM_COD_CONN_PEND 0x9017           /* wait for connect_con */
+#define PTY_REQ_PEND     0x9018
+#define CD_REQ_PEND      0x9019
+#define CF_START_PEND    0x901a
+#define CF_STOP_PEND     0x901b
+#define ECT_REQ_PEND     0x901c
+#define GETSERV_REQ_PEND 0x901d
+#define BLOCK_PLCI       0x901e
+#define INTERR_NUMBERS_REQ_PEND         0x901f
+#define INTERR_DIVERSION_REQ_PEND       0x9020
+#define MWI_ACTIVATE_REQ_PEND           0x9021
+#define MWI_DEACTIVATE_REQ_PEND         0x9022
+#define SSEXT_REQ_COMMAND               0x9023
+#define SSEXT_NC_REQ_COMMAND            0x9024
+#define START_L1_SIG_ASSIGN_PEND        0x9025
+#define REM_L1_SIG_ASSIGN_PEND          0x9026
+#define CONF_BEGIN_REQ_PEND             0x9027
+#define CONF_ADD_REQ_PEND               0x9028
+#define CONF_SPLIT_REQ_PEND             0x9029
+#define CONF_DROP_REQ_PEND              0x902a
+#define CONF_ISOLATE_REQ_PEND           0x902b
+#define CONF_REATTACH_REQ_PEND          0x902c
+#define VSWITCH_REQ_PEND                0x902d
+#define GET_MWI_STATE                   0x902e
+#define CCBS_REQUEST_REQ_PEND           0x902f
+#define CCBS_DEACTIVATE_REQ_PEND        0x9030
+#define CCBS_INTERROGATE_REQ_PEND       0x9031
+
+#define NO_INTERNAL_COMMAND             0
+#define DTMF_COMMAND_1                  1
+#define DTMF_COMMAND_2                  2
+#define DTMF_COMMAND_3                  3
+#define MIXER_COMMAND_1                 4
+#define MIXER_COMMAND_2                 5
+#define MIXER_COMMAND_3                 6
+#define ADV_VOICE_COMMAND_CONNECT_1     7
+#define ADV_VOICE_COMMAND_CONNECT_2     8
+#define ADV_VOICE_COMMAND_CONNECT_3     9
+#define ADV_VOICE_COMMAND_DISCONNECT_1  10
+#define ADV_VOICE_COMMAND_DISCONNECT_2  11
+#define ADV_VOICE_COMMAND_DISCONNECT_3  12
+#define ADJUST_B_RESTORE_1              13
+#define ADJUST_B_RESTORE_2              14
+#define RESET_B3_COMMAND_1              15
+#define SELECT_B_COMMAND_1              16
+#define FAX_CONNECT_INFO_COMMAND_1      17
+#define FAX_CONNECT_INFO_COMMAND_2      18
+#define FAX_ADJUST_B23_COMMAND_1        19
+#define FAX_ADJUST_B23_COMMAND_2        20
+#define EC_COMMAND_1                    21
+#define EC_COMMAND_2                    22
+#define EC_COMMAND_3                    23
+#define RTP_CONNECT_B3_REQ_COMMAND_1    24
+#define RTP_CONNECT_B3_REQ_COMMAND_2    25
+#define RTP_CONNECT_B3_REQ_COMMAND_3    26
+#define RTP_CONNECT_B3_RES_COMMAND_1    27
+#define RTP_CONNECT_B3_RES_COMMAND_2    28
+#define RTP_CONNECT_B3_RES_COMMAND_3    29
+#define HOLD_SAVE_COMMAND_1             30
+#define RETRIEVE_RESTORE_COMMAND_1      31
+#define FAX_DISCONNECT_COMMAND_1        32
+#define FAX_DISCONNECT_COMMAND_2        33
+#define FAX_DISCONNECT_COMMAND_3        34
+#define FAX_EDATA_ACK_COMMAND_1         35
+#define FAX_EDATA_ACK_COMMAND_2         36
+#define FAX_CONNECT_ACK_COMMAND_1       37
+#define FAX_CONNECT_ACK_COMMAND_2       38
+#define STD_INTERNAL_COMMAND_COUNT      39
+
+#define UID              0x2d               /* User Id for Mgmt      */
+
+#define CALL_DIR_OUT             0x01       /* call direction of initial call */
+#define CALL_DIR_IN              0x02
+#define CALL_DIR_ORIGINATE       0x04       /* DTE/DCE direction according to */
+#define CALL_DIR_ANSWER          0x08       /*   state of B-Channel Operation */
+#define CALL_DIR_FORCE_OUTG_NL   0x10       /* for RESET_B3 reconnect, after DISC_B3... */
+
+#define AWAITING_MANUF_CON 0x80             /* command spoofing flags */
+#define SPOOFING_REQUIRED  0xff
+#define AWAITING_SELECT_B  0xef
+
+/*------------------------------------------------------------------*/
+/* B_CTRL / DSP_CTRL                                                */
+/*------------------------------------------------------------------*/
+
+#define DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS     0x01
+#define DSP_CTRL_SET_BCHANNEL_PASSIVATION_BRI   0x02
+#define DSP_CTRL_SET_DTMF_PARAMETERS            0x03
+
+#define MANUFACTURER_FEATURE_SLAVE_CODEC          0x00000001L
+#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS   0x00000002L
+#define MANUFACTURER_FEATURE_HARDDTMF             0x00000004L
+#define MANUFACTURER_FEATURE_SOFTDTMF_SEND        0x00000008L
+#define MANUFACTURER_FEATURE_DTMF_PARAMETERS      0x00000010L
+#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE     0x00000020L
+#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD      0x00000040L
+#define MANUFACTURER_FEATURE_V18                  0x00000080L
+#define MANUFACTURER_FEATURE_MIXER_CH_CH          0x00000100L
+#define MANUFACTURER_FEATURE_MIXER_CH_PC          0x00000200L
+#define MANUFACTURER_FEATURE_MIXER_PC_CH          0x00000400L
+#define MANUFACTURER_FEATURE_MIXER_PC_PC          0x00000800L
+#define MANUFACTURER_FEATURE_ECHO_CANCELLER       0x00001000L
+#define MANUFACTURER_FEATURE_RTP                  0x00002000L
+#define MANUFACTURER_FEATURE_T38                  0x00004000L
+#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L
+#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL  0x00010000L
+#define MANUFACTURER_FEATURE_OOB_CHANNEL          0x00020000L
+#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL      0x00040000L
+#define MANUFACTURER_FEATURE_IN_BAND_FEATURE      0x00080000L
+#define MANUFACTURER_FEATURE_PIAFS                0x00100000L
+#define MANUFACTURER_FEATURE_DTMF_TONE            0x00200000L
+#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS    0x00400000L
+#define MANUFACTURER_FEATURE_OK_FC_LABEL          0x00800000L
+#define MANUFACTURER_FEATURE_VOWN                 0x01000000L
+#define MANUFACTURER_FEATURE_XCONNECT             0x02000000L
+#define MANUFACTURER_FEATURE_DMACONNECT           0x04000000L
+#define MANUFACTURER_FEATURE_AUDIO_TAP            0x08000000L
+#define MANUFACTURER_FEATURE_FAX_NONSTANDARD      0x10000000L
+
+/*------------------------------------------------------------------*/
+/* DTMF interface to IDI                                            */
+/*------------------------------------------------------------------*/
+
+
+#define DTMF_DIGIT_TONE_LOW_GROUP_697_HZ        0x00
+#define DTMF_DIGIT_TONE_LOW_GROUP_770_HZ        0x01
+#define DTMF_DIGIT_TONE_LOW_GROUP_852_HZ        0x02
+#define DTMF_DIGIT_TONE_LOW_GROUP_941_HZ        0x03
+#define DTMF_DIGIT_TONE_LOW_GROUP_MASK          0x03
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ      0x00
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ      0x04
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ      0x08
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ      0x0c
+#define DTMF_DIGIT_TONE_HIGH_GROUP_MASK         0x0c
+#define DTMF_DIGIT_TONE_CODE_0                  0x07
+#define DTMF_DIGIT_TONE_CODE_1                  0x00
+#define DTMF_DIGIT_TONE_CODE_2                  0x04
+#define DTMF_DIGIT_TONE_CODE_3                  0x08
+#define DTMF_DIGIT_TONE_CODE_4                  0x01
+#define DTMF_DIGIT_TONE_CODE_5                  0x05
+#define DTMF_DIGIT_TONE_CODE_6                  0x09
+#define DTMF_DIGIT_TONE_CODE_7                  0x02
+#define DTMF_DIGIT_TONE_CODE_8                  0x06
+#define DTMF_DIGIT_TONE_CODE_9                  0x0a
+#define DTMF_DIGIT_TONE_CODE_STAR               0x03
+#define DTMF_DIGIT_TONE_CODE_HASHMARK           0x0b
+#define DTMF_DIGIT_TONE_CODE_A                  0x0c
+#define DTMF_DIGIT_TONE_CODE_B                  0x0d
+#define DTMF_DIGIT_TONE_CODE_C                  0x0e
+#define DTMF_DIGIT_TONE_CODE_D                  0x0f
+
+#define DTMF_UDATA_REQUEST_SEND_DIGITS            16
+#define DTMF_UDATA_REQUEST_ENABLE_RECEIVER        17
+#define DTMF_UDATA_REQUEST_DISABLE_RECEIVER       18
+#define DTMF_UDATA_INDICATION_DIGITS_SENT         16
+#define DTMF_UDATA_INDICATION_DIGITS_RECEIVED     17
+#define DTMF_UDATA_INDICATION_MODEM_CALLING_TONE  18
+#define DTMF_UDATA_INDICATION_FAX_CALLING_TONE    19
+#define DTMF_UDATA_INDICATION_ANSWER_TONE         20
+
+#define UDATA_REQUEST_MIXER_TAP_DATA        27
+#define UDATA_INDICATION_MIXER_TAP_DATA     27
+
+#define DTMF_LISTEN_ACTIVE_FLAG        0x01
+#define DTMF_SEND_DIGIT_FLAG           0x01
+
+
+/*------------------------------------------------------------------*/
+/* Mixer interface to IDI                                           */
+/*------------------------------------------------------------------*/
+
+
+#define LI2_FLAG_PCCONNECT_A_B 0x40000000
+#define LI2_FLAG_PCCONNECT_B_A 0x80000000
+
+#define MIXER_BCHANNELS_BRI    2
+#define MIXER_IC_CHANNELS_BRI  MIXER_BCHANNELS_BRI
+#define MIXER_IC_CHANNEL_BASE  MIXER_BCHANNELS_BRI
+#define MIXER_CHANNELS_BRI     (MIXER_BCHANNELS_BRI + MIXER_IC_CHANNELS_BRI)
+#define MIXER_CHANNELS_PRI     32
+
+typedef struct li_config_s LI_CONFIG;
+
+struct xconnect_card_address_s {
+  dword low;
+  dword high;
+};
+
+struct xconnect_transfer_address_s {
+  struct xconnect_card_address_s card_address;
+  dword offset;
+};
+
+struct li_config_s {
+  DIVA_CAPI_ADAPTER   *adapter;
+  PLCI   *plci;
+  struct xconnect_transfer_address_s send_b;
+  struct xconnect_transfer_address_s send_pc;
+  byte   *flag_table;  /* dword aligned and sized */
+  byte   *coef_table;  /* dword aligned and sized */
+  byte channel;
+  byte curchnl;
+  byte chflags;
+};
+
+extern LI_CONFIG   *li_config_table;
+extern word li_total_channels;
+
+#define LI_CHANNEL_INVOLVED        0x01
+#define LI_CHANNEL_ACTIVE          0x02
+#define LI_CHANNEL_TX_DATA         0x04
+#define LI_CHANNEL_RX_DATA         0x08
+#define LI_CHANNEL_CONFERENCE      0x10
+#define LI_CHANNEL_ADDRESSES_SET   0x80
+
+#define LI_CHFLAG_MONITOR          0x01
+#define LI_CHFLAG_MIX              0x02
+#define LI_CHFLAG_LOOP             0x04
+
+#define LI_FLAG_INTERCONNECT       0x01
+#define LI_FLAG_MONITOR            0x02
+#define LI_FLAG_MIX                0x04
+#define LI_FLAG_PCCONNECT          0x08
+#define LI_FLAG_CONFERENCE         0x10
+#define LI_FLAG_ANNOUNCEMENT       0x20
+
+#define LI_COEF_CH_CH              0x01
+#define LI_COEF_CH_PC              0x02
+#define LI_COEF_PC_CH              0x04
+#define LI_COEF_PC_PC              0x08
+#define LI_COEF_CH_CH_SET          0x10
+#define LI_COEF_CH_PC_SET          0x20
+#define LI_COEF_PC_CH_SET          0x40
+#define LI_COEF_PC_PC_SET          0x80
+
+#define LI_REQ_SILENT_UPDATE       0xffff
+
+#define LI_PLCI_B_LAST_FLAG        ((dword) 0x80000000L)
+#define LI_PLCI_B_DISC_FLAG        ((dword) 0x40000000L)
+#define LI_PLCI_B_SKIP_FLAG        ((dword) 0x20000000L)
+#define LI_PLCI_B_FLAG_MASK        ((dword) 0xe0000000L)
+
+#define UDATA_REQUEST_SET_MIXER_COEFS_BRI       24
+#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC  25
+#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_ASYN  26
+#define UDATA_INDICATION_MIXER_COEFS_SET        24
+
+#define MIXER_FEATURE_ENABLE_TX_DATA        0x0001
+#define MIXER_FEATURE_ENABLE_RX_DATA        0x0002
+
+#define MIXER_COEF_LINE_CHANNEL_MASK        0x1f
+#define MIXER_COEF_LINE_FROM_PC_FLAG        0x20
+#define MIXER_COEF_LINE_TO_PC_FLAG          0x40
+#define MIXER_COEF_LINE_ROW_FLAG            0x80
+
+#define UDATA_REQUEST_XCONNECT_FROM         28
+#define UDATA_INDICATION_XCONNECT_FROM      28
+#define UDATA_REQUEST_XCONNECT_TO           29
+#define UDATA_INDICATION_XCONNECT_TO        29
+
+#define XCONNECT_CHANNEL_PORT_B             0x0000
+#define XCONNECT_CHANNEL_PORT_PC            0x8000
+#define XCONNECT_CHANNEL_PORT_MASK          0x8000
+#define XCONNECT_CHANNEL_NUMBER_MASK        0x7fff
+#define XCONNECT_CHANNEL_PORT_COUNT         2
+
+#define XCONNECT_SUCCESS           0x0000
+#define XCONNECT_ERROR             0x0001
+
+
+/*------------------------------------------------------------------*/
+/* Echo canceller interface to IDI                                  */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_ECHO_CANCELLER         0
+
+#define PRIV_SELECTOR_ECHO_CANCELLER   255
+
+#define EC_ENABLE_OPERATION            1
+#define EC_DISABLE_OPERATION           2
+#define EC_FREEZE_COEFFICIENTS         3
+#define EC_RESUME_COEFFICIENT_UPDATE   4
+#define EC_RESET_COEFFICIENTS          5
+
+#define EC_DISABLE_NON_LINEAR_PROCESSING     0x0001
+#define EC_DO_NOT_REQUIRE_REVERSALS          0x0002
+#define EC_DETECT_DISABLE_TONE               0x0004
+
+#define EC_SUCCESS                           0
+#define EC_UNSUPPORTED_OPERATION             1
+
+#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ   1
+#define EC_BYPASS_DUE_TO_REVERSED_2100HZ     2
+#define EC_BYPASS_RELEASED                   3
+
+#define DSP_CTRL_SET_LEC_PARAMETERS          0x05
+
+#define LEC_ENABLE_ECHO_CANCELLER            0x0001
+#define LEC_ENABLE_2100HZ_DETECTOR           0x0002
+#define LEC_REQUIRE_2100HZ_REVERSALS         0x0004
+#define LEC_MANUAL_DISABLE                   0x0008
+#define LEC_ENABLE_NONLINEAR_PROCESSING      0x0010
+#define LEC_FREEZE_COEFFICIENTS              0x0020
+#define LEC_RESET_COEFFICIENTS               0x8000
+
+#define LEC_MAX_SUPPORTED_TAIL_LENGTH        32
+
+#define LEC_UDATA_INDICATION_DISABLE_DETECT  9
+
+#define LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ  0x00
+#define LEC_DISABLE_TYPE_REVERSED_2100HZ     0x01
+#define LEC_DISABLE_RELEASED                 0x02
+
+
+/*------------------------------------------------------------------*/
+/* RTP interface to IDI                                             */
+/*------------------------------------------------------------------*/
+
+
+#define B1_RTP                  31
+#define B2_RTP                  31
+#define B3_RTP                  31
+
+#define PRIVATE_RTP                    1
+
+#define RTP_PRIM_PAYLOAD_PCMU_8000     0
+#define RTP_PRIM_PAYLOAD_1016_8000     1
+#define RTP_PRIM_PAYLOAD_G726_32_8000  2
+#define RTP_PRIM_PAYLOAD_GSM_8000      3
+#define RTP_PRIM_PAYLOAD_G723_8000     4
+#define RTP_PRIM_PAYLOAD_DVI4_8000     5
+#define RTP_PRIM_PAYLOAD_DVI4_16000    6
+#define RTP_PRIM_PAYLOAD_LPC_8000      7
+#define RTP_PRIM_PAYLOAD_PCMA_8000     8
+#define RTP_PRIM_PAYLOAD_G722_16000    9
+#define RTP_PRIM_PAYLOAD_QCELP_8000    12
+#define RTP_PRIM_PAYLOAD_G728_8000     14
+#define RTP_PRIM_PAYLOAD_G729_8000     18
+#define RTP_PRIM_PAYLOAD_GSM_HR_8000   30
+#define RTP_PRIM_PAYLOAD_GSM_EFR_8000  31
+
+#define RTP_ADD_PAYLOAD_BASE           32
+#define RTP_ADD_PAYLOAD_RED            32
+#define RTP_ADD_PAYLOAD_CN_8000        33
+#define RTP_ADD_PAYLOAD_DTMF           34
+
+#define RTP_SUCCESS                         0
+#define RTP_ERR_SSRC_OR_PAYLOAD_CHANGE      1
+
+#define UDATA_REQUEST_RTP_RECONFIGURE       64
+#define UDATA_INDICATION_RTP_CHANGE         65
+#define BUDATA_REQUEST_QUERY_RTCP_REPORT    1
+#define BUDATA_INDICATION_RTCP_REPORT       1
+
+#define RTP_CONNECT_OPTION_DISC_ON_SSRC_CHANGE    0x00000001L
+#define RTP_CONNECT_OPTION_DISC_ON_PT_CHANGE      0x00000002L
+#define RTP_CONNECT_OPTION_DISC_ON_UNKNOWN_PT     0x00000004L
+#define RTP_CONNECT_OPTION_NO_SILENCE_TRANSMIT    0x00010000L
+
+#define RTP_PAYLOAD_OPTION_VOICE_ACTIVITY_DETECT  0x0001
+#define RTP_PAYLOAD_OPTION_DISABLE_POST_FILTER    0x0002
+#define RTP_PAYLOAD_OPTION_G723_LOW_CODING_RATE   0x0100
+
+#define RTP_PACKET_FILTER_IGNORE_UNKNOWN_SSRC     0x00000001L
+
+#define RTP_CHANGE_FLAG_SSRC_CHANGE               0x00000001L
+#define RTP_CHANGE_FLAG_PAYLOAD_TYPE_CHANGE       0x00000002L
+#define RTP_CHANGE_FLAG_UNKNOWN_PAYLOAD_TYPE      0x00000004L
+
+
+/*------------------------------------------------------------------*/
+/* T.38 interface to IDI                                            */
+/*------------------------------------------------------------------*/
+
+
+#define B1_T38                  30
+#define B2_T38                  30
+#define B3_T38                  30
+
+#define PRIVATE_T38                    2
+
+
+/*------------------------------------------------------------------*/
+/* PIAFS interface to IDI                                            */
+/*------------------------------------------------------------------*/
+
+
+#define B1_PIAFS                29
+#define B2_PIAFS                29
+
+#define PRIVATE_PIAFS           29 
+
+/*
+  B2 configuration for PIAFS:
++---------------------+------+-----------------------------------------+
+| PIAFS Protocol      | byte | Bit 1 - Protocol Speed                  |
+| Speed configuration |      |         0 - 32K                         |
+|                     |      |         1 - 64K (default)               |
+|                     |      | Bit 2 - Variable Protocol Speed         |
+|                     |      |         0 - Speed is fix                |
+|                     |      |         1 - Speed is variable (default) |
++---------------------+------+-----------------------------------------+
+| Direction           | word | Enable compression/decompression for    |
+|                     |      | 0: All direction                        |
+|                     |      | 1: disable outgoing data                |
+|                     |      | 2: disable incomming data               |
+|                     |      | 3: disable both direction (default)     |
++---------------------+------+-----------------------------------------+
+| Number of code      | word | Parameter P1 of V.42bis in accordance   |
+| words               |      | with V.42bis                            |
++---------------------+------+-----------------------------------------+
+| Maximum String      | word | Parameter P2 of V.42bis in accordance   |
+| Length              |      | with V.42bis                            |
++---------------------+------+-----------------------------------------+
+| control (UDATA)     | byte | enable PIAFS control communication      |
+| abilities           |      |                                         |
++---------------------+------+-----------------------------------------+
+*/
+#define PIAFS_UDATA_ABILITIES  0x80
+
+/*------------------------------------------------------------------*/
+/* FAX SUB/SEP/PWD extension                                        */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_FAX_SUB_SEP_PWD        3
+
+
+
+/*------------------------------------------------------------------*/
+/* V.18 extension                                                   */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_V18                    4
+
+
+
+/*------------------------------------------------------------------*/
+/* DTMF TONE extension                                              */
+/*------------------------------------------------------------------*/
+
+
+#define DTMF_GET_SUPPORTED_DETECT_CODES  0xf8
+#define DTMF_GET_SUPPORTED_SEND_CODES    0xf9
+#define DTMF_LISTEN_TONE_START           0xfa
+#define DTMF_LISTEN_TONE_STOP            0xfb
+#define DTMF_SEND_TONE                   0xfc
+#define DTMF_LISTEN_MF_START             0xfd
+#define DTMF_LISTEN_MF_STOP              0xfe
+#define DTMF_SEND_MF                     0xff
+
+#define DTMF_MF_DIGIT_TONE_CODE_1               0x10
+#define DTMF_MF_DIGIT_TONE_CODE_2               0x11
+#define DTMF_MF_DIGIT_TONE_CODE_3               0x12
+#define DTMF_MF_DIGIT_TONE_CODE_4               0x13
+#define DTMF_MF_DIGIT_TONE_CODE_5               0x14
+#define DTMF_MF_DIGIT_TONE_CODE_6               0x15
+#define DTMF_MF_DIGIT_TONE_CODE_7               0x16
+#define DTMF_MF_DIGIT_TONE_CODE_8               0x17
+#define DTMF_MF_DIGIT_TONE_CODE_9               0x18
+#define DTMF_MF_DIGIT_TONE_CODE_0               0x19
+#define DTMF_MF_DIGIT_TONE_CODE_K1              0x1a
+#define DTMF_MF_DIGIT_TONE_CODE_K2              0x1b
+#define DTMF_MF_DIGIT_TONE_CODE_KP              0x1c
+#define DTMF_MF_DIGIT_TONE_CODE_S1              0x1d
+#define DTMF_MF_DIGIT_TONE_CODE_ST              0x1e
+
+#define DTMF_DIGIT_CODE_COUNT                   16
+#define DTMF_MF_DIGIT_CODE_BASE                 DSP_DTMF_DIGIT_CODE_COUNT
+#define DTMF_MF_DIGIT_CODE_COUNT                15
+#define DTMF_TOTAL_DIGIT_CODE_COUNT             (DSP_MF_DIGIT_CODE_BASE + DSP_MF_DIGIT_CODE_COUNT)
+
+#define DTMF_TONE_DIGIT_BASE                    0x80
+
+#define DTMF_SIGNAL_NO_TONE                     (DTMF_TONE_DIGIT_BASE + 0)
+#define DTMF_SIGNAL_UNIDENTIFIED_TONE           (DTMF_TONE_DIGIT_BASE + 1)
+
+#define DTMF_SIGNAL_DIAL_TONE                   (DTMF_TONE_DIGIT_BASE + 2)
+#define DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE     (DTMF_TONE_DIGIT_BASE + 3)
+#define DTMF_SIGNAL_SPECIAL_DIAL_TONE           (DTMF_TONE_DIGIT_BASE + 4)   /* stutter dial tone */
+#define DTMF_SIGNAL_SECOND_DIAL_TONE            (DTMF_TONE_DIGIT_BASE + 5)
+#define DTMF_SIGNAL_RINGING_TONE                (DTMF_TONE_DIGIT_BASE + 6)
+#define DTMF_SIGNAL_SPECIAL_RINGING_TONE        (DTMF_TONE_DIGIT_BASE + 7)
+#define DTMF_SIGNAL_BUSY_TONE                   (DTMF_TONE_DIGIT_BASE + 8)
+#define DTMF_SIGNAL_CONGESTION_TONE             (DTMF_TONE_DIGIT_BASE + 9)   /* reorder tone */
+#define DTMF_SIGNAL_SPECIAL_INFORMATION_TONE    (DTMF_TONE_DIGIT_BASE + 10)
+#define DTMF_SIGNAL_COMFORT_TONE                (DTMF_TONE_DIGIT_BASE + 11)
+#define DTMF_SIGNAL_HOLD_TONE                   (DTMF_TONE_DIGIT_BASE + 12)
+#define DTMF_SIGNAL_RECORD_TONE                 (DTMF_TONE_DIGIT_BASE + 13)
+#define DTMF_SIGNAL_CALLER_WAITING_TONE         (DTMF_TONE_DIGIT_BASE + 14)
+#define DTMF_SIGNAL_CALL_WAITING_TONE           (DTMF_TONE_DIGIT_BASE + 15)
+#define DTMF_SIGNAL_PAY_TONE                    (DTMF_TONE_DIGIT_BASE + 16)
+#define DTMF_SIGNAL_POSITIVE_INDICATION_TONE    (DTMF_TONE_DIGIT_BASE + 17)
+#define DTMF_SIGNAL_NEGATIVE_INDICATION_TONE    (DTMF_TONE_DIGIT_BASE + 18)
+#define DTMF_SIGNAL_WARNING_TONE                (DTMF_TONE_DIGIT_BASE + 19)
+#define DTMF_SIGNAL_INTRUSION_TONE              (DTMF_TONE_DIGIT_BASE + 20)
+#define DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE   (DTMF_TONE_DIGIT_BASE + 21)
+#define DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE   (DTMF_TONE_DIGIT_BASE + 22)
+#define DTMF_SIGNAL_CPE_ALERTING_SIGNAL         (DTMF_TONE_DIGIT_BASE + 23)
+#define DTMF_SIGNAL_OFF_HOOK_WARNING_TONE       (DTMF_TONE_DIGIT_BASE + 24)
+
+#define DTMF_SIGNAL_INTERCEPT_TONE              (DTMF_TONE_DIGIT_BASE + 63)
+
+#define DTMF_SIGNAL_MODEM_CALLING_TONE          (DTMF_TONE_DIGIT_BASE + 64)
+#define DTMF_SIGNAL_FAX_CALLING_TONE            (DTMF_TONE_DIGIT_BASE + 65)
+#define DTMF_SIGNAL_ANSWER_TONE                 (DTMF_TONE_DIGIT_BASE + 66)
+#define DTMF_SIGNAL_REVERSED_ANSWER_TONE        (DTMF_TONE_DIGIT_BASE + 67)
+#define DTMF_SIGNAL_ANSAM_TONE                  (DTMF_TONE_DIGIT_BASE + 68)
+#define DTMF_SIGNAL_REVERSED_ANSAM_TONE         (DTMF_TONE_DIGIT_BASE + 69)
+#define DTMF_SIGNAL_BELL103_ANSWER_TONE         (DTMF_TONE_DIGIT_BASE + 70)
+#define DTMF_SIGNAL_FAX_FLAGS                   (DTMF_TONE_DIGIT_BASE + 71)
+#define DTMF_SIGNAL_G2_FAX_GROUP_ID             (DTMF_TONE_DIGIT_BASE + 72)
+#define DTMF_SIGNAL_HUMAN_SPEECH                (DTMF_TONE_DIGIT_BASE + 73)
+#define DTMF_SIGNAL_ANSWERING_MACHINE_390       (DTMF_TONE_DIGIT_BASE + 74)
+
+#define DTMF_MF_LISTEN_ACTIVE_FLAG     0x02
+#define DTMF_SEND_MF_FLAG              0x02
+#define DTMF_TONE_LISTEN_ACTIVE_FLAG   0x04
+#define DTMF_SEND_TONE_FLAG            0x04
+
+#define PRIVATE_DTMF_TONE              5
+
+
+/*------------------------------------------------------------------*/
+/* FAX paper format extension                                       */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_FAX_PAPER_FORMATS      6
+
+
+
+/*------------------------------------------------------------------*/
+/* V.OWN extension                                                  */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_VOWN                   7
+
+
+
+/*------------------------------------------------------------------*/
+/* FAX non-standard facilities extension                            */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_FAX_NONSTANDARD        8
+
+
+
+/*------------------------------------------------------------------*/
+/* Advanced voice                                                   */
+/*------------------------------------------------------------------*/
+
+#define ADV_VOICE_WRITE_ACTIVATION    0
+#define ADV_VOICE_WRITE_DEACTIVATION  1
+#define ADV_VOICE_WRITE_UPDATE        2
+
+#define ADV_VOICE_OLD_COEF_COUNT    6
+#define ADV_VOICE_NEW_COEF_BASE     (ADV_VOICE_OLD_COEF_COUNT * sizeof(word))
+
+/*------------------------------------------------------------------*/
+/* B1 resource switching                                            */
+/*------------------------------------------------------------------*/
+
+#define B1_FACILITY_LOCAL  0x01
+#define B1_FACILITY_MIXER  0x02
+#define B1_FACILITY_DTMFX  0x04
+#define B1_FACILITY_DTMFR  0x08
+#define B1_FACILITY_VOICE  0x10
+#define B1_FACILITY_EC     0x20
+
+#define ADJUST_B_MODE_SAVE          0x0001
+#define ADJUST_B_MODE_REMOVE_L23    0x0002
+#define ADJUST_B_MODE_SWITCH_L1     0x0004
+#define ADJUST_B_MODE_NO_RESOURCE   0x0008
+#define ADJUST_B_MODE_ASSIGN_L23    0x0010
+#define ADJUST_B_MODE_USER_CONNECT  0x0020
+#define ADJUST_B_MODE_CONNECT       0x0040
+#define ADJUST_B_MODE_RESTORE       0x0080
+
+#define ADJUST_B_START                     0
+#define ADJUST_B_SAVE_MIXER_1              1
+#define ADJUST_B_SAVE_DTMF_1               2
+#define ADJUST_B_REMOVE_L23_1              3
+#define ADJUST_B_REMOVE_L23_2              4
+#define ADJUST_B_SAVE_EC_1                 5
+#define ADJUST_B_SAVE_DTMF_PARAMETER_1     6
+#define ADJUST_B_SAVE_VOICE_1              7
+#define ADJUST_B_SWITCH_L1_1               8
+#define ADJUST_B_SWITCH_L1_2               9
+#define ADJUST_B_RESTORE_VOICE_1           10
+#define ADJUST_B_RESTORE_VOICE_2           11
+#define ADJUST_B_RESTORE_DTMF_PARAMETER_1  12
+#define ADJUST_B_RESTORE_DTMF_PARAMETER_2  13
+#define ADJUST_B_RESTORE_EC_1              14
+#define ADJUST_B_RESTORE_EC_2              15
+#define ADJUST_B_ASSIGN_L23_1              16
+#define ADJUST_B_ASSIGN_L23_2              17
+#define ADJUST_B_CONNECT_1                 18
+#define ADJUST_B_CONNECT_2                 19
+#define ADJUST_B_CONNECT_3                 20
+#define ADJUST_B_CONNECT_4                 21
+#define ADJUST_B_RESTORE_DTMF_1            22
+#define ADJUST_B_RESTORE_DTMF_2            23
+#define ADJUST_B_RESTORE_MIXER_1           24
+#define ADJUST_B_RESTORE_MIXER_2           25
+#define ADJUST_B_RESTORE_MIXER_3           26
+#define ADJUST_B_RESTORE_MIXER_4           27
+#define ADJUST_B_RESTORE_MIXER_5           28
+#define ADJUST_B_RESTORE_MIXER_6           29
+#define ADJUST_B_RESTORE_MIXER_7           30
+#define ADJUST_B_END                       31
+
+/*------------------------------------------------------------------*/
+/* XON Protocol def's                                               */
+/*------------------------------------------------------------------*/
+#define N_CH_XOFF               0x01
+#define N_XON_SENT              0x02
+#define N_XON_REQ               0x04
+#define N_XON_CONNECT_IND       0x08
+#define N_RX_FLOW_CONTROL_MASK  0x3f
+#define N_OK_FC_PENDING         0x80
+#define N_TX_FLOW_CONTROL_MASK  0xc0
+
+/*------------------------------------------------------------------*/
+/* NCPI state                                                       */
+/*------------------------------------------------------------------*/
+#define NCPI_VALID_CONNECT_B3_IND  0x01
+#define NCPI_VALID_CONNECT_B3_ACT  0x02
+#define NCPI_VALID_DISC_B3_IND     0x04
+#define NCPI_CONNECT_B3_ACT_SENT   0x08
+#define NCPI_NEGOTIATE_B3_SENT     0x10
+#define NCPI_MDM_CTS_ON_RECEIVED   0x40
+#define NCPI_MDM_DCD_ON_RECEIVED   0x80
+
+/*------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/divamnt.c b/drivers/isdn/hardware/eicon/divamnt.c
new file mode 100644
index 0000000..6146f76
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divamnt.c
@@ -0,0 +1,257 @@
+/* $Id: divamnt.c,v 1.32.6.10 2005/02/11 19:40:25 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * Maint module
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/uaccess.h>
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "debug_if.h"
+
+static char *main_revision = "$Revision: 1.32.6.10 $";
+
+static int major;
+
+MODULE_DESCRIPTION("Maint driver for Eicon DIVA Server cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("DIVA card driver");
+MODULE_LICENSE("GPL");
+
+static int buffer_length = 128;
+module_param(buffer_length, int, 0);
+static unsigned long diva_dbg_mem = 0;
+module_param(diva_dbg_mem, ulong, 0);
+
+static char *DRIVERNAME =
+    "Eicon DIVA - MAINT module (http://www.melware.net)";
+static char *DRIVERLNAME = "diva_mnt";
+static char *DEVNAME = "DivasMAINT";
+char *DRIVERRELEASE_MNT = "2.0";
+
+static wait_queue_head_t msgwaitq;
+static unsigned long opened;
+static struct timeval start_time;
+
+extern int mntfunc_init(int *, void **, unsigned long);
+extern void mntfunc_finit(void);
+extern int maint_read_write(void __user *buf, int count);
+
+/*
+ *  helper functions
+ */
+static char *getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "1.0";
+
+	return rev;
+}
+
+/*
+ * kernel/user space copy functions
+ */
+int diva_os_copy_to_user(void *os_handle, void __user *dst, const void *src,
+			 int length)
+{
+	return (copy_to_user(dst, src, length));
+}
+int diva_os_copy_from_user(void *os_handle, void *dst, const void __user *src,
+			   int length)
+{
+	return (copy_from_user(dst, src, length));
+}
+
+/*
+ * get time
+ */
+void diva_os_get_time(dword * sec, dword * usec)
+{
+	struct timeval tv;
+
+	do_gettimeofday(&tv);
+
+	if (tv.tv_sec > start_time.tv_sec) {
+		if (start_time.tv_usec > tv.tv_usec) {
+			tv.tv_sec--;
+			tv.tv_usec += 1000000;
+		}
+		*sec = (dword) (tv.tv_sec - start_time.tv_sec);
+		*usec = (dword) (tv.tv_usec - start_time.tv_usec);
+	} else if (tv.tv_sec == start_time.tv_sec) {
+		*sec = 0;
+		if (start_time.tv_usec < tv.tv_usec) {
+			*usec = (dword) (tv.tv_usec - start_time.tv_usec);
+		} else {
+			*usec = 0;
+		}
+	} else {
+		*sec = (dword) tv.tv_sec;
+		*usec = (dword) tv.tv_usec;
+	}
+}
+
+/*
+ * device node operations
+ */
+static unsigned int maint_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+
+	poll_wait(file, &msgwaitq, wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (file->private_data || diva_dbg_q_length()) {
+		mask |= POLLIN | POLLRDNORM;
+	}
+	return (mask);
+}
+
+static int maint_open(struct inode *ino, struct file *filep)
+{
+	/* only one open is allowed, so we test
+	   it atomically */
+	if (test_and_set_bit(0, &opened))
+		return (-EBUSY);
+
+	filep->private_data = NULL;
+
+	return nonseekable_open(ino, filep);
+}
+
+static int maint_close(struct inode *ino, struct file *filep)
+{
+	if (filep->private_data) {
+		diva_os_free(0, filep->private_data);
+		filep->private_data = NULL;
+	}
+
+	/* clear 'used' flag */
+	clear_bit(0, &opened);
+	
+	return (0);
+}
+
+static ssize_t divas_maint_write(struct file *file, const char __user *buf,
+				 size_t count, loff_t * ppos)
+{
+	return (maint_read_write((char __user *) buf, (int) count));
+}
+
+static ssize_t divas_maint_read(struct file *file, char __user *buf,
+				size_t count, loff_t * ppos)
+{
+	return (maint_read_write(buf, (int) count));
+}
+
+static struct file_operations divas_maint_fops = {
+	.owner   = THIS_MODULE,
+	.llseek  = no_llseek,
+	.read    = divas_maint_read,
+	.write   = divas_maint_write,
+	.poll    = maint_poll,
+	.open    = maint_open,
+	.release = maint_close
+};
+
+static void divas_maint_unregister_chrdev(void)
+{
+	devfs_remove(DEVNAME);
+	unregister_chrdev(major, DEVNAME);
+}
+
+static int DIVA_INIT_FUNCTION divas_maint_register_chrdev(void)
+{
+	if ((major = register_chrdev(0, DEVNAME, &divas_maint_fops)) < 0)
+	{
+		printk(KERN_ERR "%s: failed to create /dev entry.\n",
+		       DRIVERLNAME);
+		return (0);
+	}
+	devfs_mk_cdev(MKDEV(major, 0), S_IFCHR|S_IRUSR|S_IWUSR, DEVNAME);
+
+	return (1);
+}
+
+/*
+ * wake up reader
+ */
+void diva_maint_wakeup_read(void)
+{
+	wake_up_interruptible(&msgwaitq);
+}
+
+/*
+ *  Driver Load
+ */
+static int DIVA_INIT_FUNCTION maint_init(void)
+{
+	char tmprev[50];
+	int ret = 0;
+	void *buffer = NULL;
+
+	do_gettimeofday(&start_time);
+	init_waitqueue_head(&msgwaitq);
+
+	printk(KERN_INFO "%s\n", DRIVERNAME);
+	printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_MNT);
+	strcpy(tmprev, main_revision);
+	printk("%s  Build: %s \n", getrev(tmprev), DIVA_BUILD);
+
+	if (!divas_maint_register_chrdev()) {
+		ret = -EIO;
+		goto out;
+	}
+
+	if (!(mntfunc_init(&buffer_length, &buffer, diva_dbg_mem))) {
+		printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+		       DRIVERLNAME);
+		divas_maint_unregister_chrdev();
+		ret = -EIO;
+		goto out;
+	}
+
+	printk(KERN_INFO "%s: trace buffer = %p - %d kBytes, %s (Major: %d)\n",
+	       DRIVERLNAME, buffer, (buffer_length / 1024),
+	       (diva_dbg_mem == 0) ? "internal" : "external", major);
+
+      out:
+	return (ret);
+}
+
+/*
+**  Driver Unload
+*/
+static void DIVA_EXIT_FUNCTION maint_exit(void)
+{
+	divas_maint_unregister_chrdev();
+	mntfunc_finit();
+
+	printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(maint_init);
+module_exit(maint_exit);
+
diff --git a/drivers/isdn/hardware/eicon/divasfunc.c b/drivers/isdn/hardware/eicon/divasfunc.c
new file mode 100644
index 0000000..df61e51
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasfunc.c
@@ -0,0 +1,238 @@
+/* $Id: divasfunc.c,v 1.23.4.2 2004/08/28 20:03:53 armin Exp $
+ *
+ * Low level driver for Eicon DIVA Server ISDN cards.
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "di.h"
+#include "io.h"
+#include "divasync.h"
+#include "diva.h"
+#include "xdi_vers.h"
+
+#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+static int debugmask;
+
+extern void DIVA_DIDD_Read(void *, int);
+
+extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+
+extern char *DRIVERRELEASE_DIVAS;
+
+static dword notify_handle;
+static DESCRIPTOR DAdapter;
+static DESCRIPTOR MAdapter;
+
+/* --------------------------------------------------------------------------
+    MAINT driver connector section
+   -------------------------------------------------------------------------- */
+static void no_printf(unsigned char *x, ...)
+{
+	/* dummy debug function */
+}
+
+#include "debuglib.c"
+
+/*
+ * get the adapters serial number
+ */
+void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf)
+{
+	int contr = 0;
+
+	if ((contr = ((IoAdapter->serialNo & 0xff000000) >> 24))) {
+		sprintf(buf, "%d-%d",
+			IoAdapter->serialNo & 0x00ffffff, contr + 1);
+	} else {
+		sprintf(buf, "%d", IoAdapter->serialNo);
+	}
+}
+
+/*
+ * register a new adapter
+ */
+void diva_xdi_didd_register_adapter(int card)
+{
+	DESCRIPTOR d;
+	IDI_SYNC_REQ req;
+
+	if (card && ((card - 1) < MAX_ADAPTER) &&
+	    IoAdapters[card - 1] && Requests[card - 1]) {
+		d.type = IoAdapters[card - 1]->Properties.DescType;
+		d.request = Requests[card - 1];
+		d.channels = IoAdapters[card - 1]->Properties.Channels;
+		d.features = IoAdapters[card - 1]->Properties.Features;
+		DBG_TRC(("DIDD register A(%d) channels=%d", card,
+			 d.channels))
+		    /* workaround for different Name in structure */
+		    strlcpy(IoAdapters[card - 1]->Name,
+			    IoAdapters[card - 1]->Properties.Name,
+			    sizeof(IoAdapters[card - 1]->Name));
+		req.didd_remove_adapter.e.Req = 0;
+		req.didd_add_adapter.e.Rc = IDI_SYNC_REQ_DIDD_ADD_ADAPTER;
+		req.didd_add_adapter.info.descriptor = (void *) &d;
+		DAdapter.request((ENTITY *) & req);
+		if (req.didd_add_adapter.e.Rc != 0xff) {
+			DBG_ERR(("DIDD register A(%d) failed !", card))
+		}
+		IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL;
+	}
+}
+
+/*
+ * remove an adapter
+ */
+void diva_xdi_didd_remove_adapter(int card)
+{
+	IDI_SYNC_REQ req;
+	ADAPTER *a = &IoAdapters[card - 1]->a;
+
+	IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL;
+	DBG_TRC(("DIDD de-register A(%d)", card))
+	req.didd_remove_adapter.e.Req = 0;
+	req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER;
+	req.didd_remove_adapter.info.p_request =
+	    (IDI_CALL) Requests[card - 1];
+	DAdapter.request((ENTITY *) & req);
+	memset(&(a->IdTable), 0x00, 256);
+}
+
+/*
+ * start debug
+ */
+static void start_dbg(void)
+{
+	DbgRegister("DIVAS", DRIVERRELEASE_DIVAS, (debugmask) ? debugmask : DBG_DEFAULT);
+	DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s]-%s-%s)",
+		 DIVA_BUILD, diva_xdi_common_code_build, __DATE__,
+		 __TIME__))
+}
+
+/*
+ * stop debug
+ */
+static void stop_dbg(void)
+{
+	DbgDeregister();
+	memset(&MAdapter, 0, sizeof(MAdapter));
+	dprintf = no_printf;
+}
+
+/*
+ * didd callback function
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+			   int removal)
+{
+	if (adapter->type == IDI_DADAPTER) {
+		DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
+		return (NULL);
+	}
+
+	if (adapter->type == IDI_DIMAINT) {
+		if (removal) {
+			stop_dbg();
+		} else {
+			memcpy(&MAdapter, adapter, sizeof(MAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			start_dbg();
+		}
+	}
+	return (NULL);
+}
+
+/*
+ * connect to didd
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+	int x = 0;
+	int dadapter = 0;
+	IDI_SYNC_REQ req;
+	DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+	DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DADAPTER) {	/* DADAPTER found */
+			dadapter = 1;
+			memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			req.didd_notify.e.Req = 0;
+			req.didd_notify.e.Rc =
+			    IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+			req.didd_notify.info.callback = (void *)didd_callback;
+			req.didd_notify.info.context = NULL;
+			DAdapter.request((ENTITY *) & req);
+			if (req.didd_notify.e.Rc != 0xff) {
+				stop_dbg();
+				return (0);
+			}
+			notify_handle = req.didd_notify.info.handle;
+		} else if (DIDD_Table[x].type == IDI_DIMAINT) {	/* MAINT found */
+			memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			start_dbg();
+		}
+	}
+
+	if (!dadapter) {
+		stop_dbg();
+	}
+
+	return (dadapter);
+}
+
+/*
+ * disconnect from didd
+ */
+static void DIVA_EXIT_FUNCTION disconnect_didd(void)
+{
+	IDI_SYNC_REQ req;
+
+	stop_dbg();
+
+	req.didd_notify.e.Req = 0;
+	req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+	req.didd_notify.info.handle = notify_handle;
+	DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION divasfunc_init(int dbgmask)
+{
+	char *version;
+
+	debugmask = dbgmask;
+	
+	if (!connect_didd()) {
+		DBG_ERR(("divasfunc: failed to connect to DIDD."))
+		return (0);
+	}
+
+	version = diva_xdi_common_code_build;
+
+	divasa_xdi_driver_entry();
+
+	return (1);
+}
+
+/*
+ * exit
+ */
+void DIVA_EXIT_FUNCTION divasfunc_exit(void)
+{
+	divasa_xdi_driver_unload();
+	disconnect_didd();
+}
diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c
new file mode 100644
index 0000000..df715b4
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasi.c
@@ -0,0 +1,581 @@
+/* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22:20 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * User Mode IDI Interface 
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/uaccess.h>
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "um_xdi.h"
+#include "um_idi.h"
+
+static char *main_revision = "$Revision: 1.25.6.2 $";
+
+static int major;
+
+MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("DIVA card driver");
+MODULE_LICENSE("GPL");
+
+typedef struct _diva_um_idi_os_context {
+	wait_queue_head_t read_wait;
+	wait_queue_head_t close_wait;
+	struct timer_list diva_timer_id;
+	int aborted;
+	int adapter_nr;
+} diva_um_idi_os_context_t;
+
+static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)";
+static char *DRIVERLNAME = "diva_idi";
+static char *DEVNAME = "DivasIDI";
+char *DRIVERRELEASE_IDI = "2.0";
+
+extern int idifunc_init(void);
+extern void idifunc_finit(void);
+
+/*
+ *  helper functions
+ */
+static char *getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "1.0";
+	return rev;
+}
+
+/*
+ *  LOCALS
+ */
+static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count,
+			   loff_t * offset);
+static ssize_t um_idi_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t * offset);
+static unsigned int um_idi_poll(struct file *file, poll_table * wait);
+static int um_idi_open(struct inode *inode, struct file *file);
+static int um_idi_release(struct inode *inode, struct file *file);
+static int remove_entity(void *entity);
+static void diva_um_timer_function(unsigned long data);
+
+/*
+ * proc entry
+ */
+extern struct proc_dir_entry *proc_net_eicon;
+static struct proc_dir_entry *um_idi_proc_entry = NULL;
+
+static int
+um_idi_proc_read(char *page, char **start, off_t off, int count, int *eof,
+		 void *data)
+{
+	int len = 0;
+	char tmprev[32];
+
+	len += sprintf(page + len, "%s\n", DRIVERNAME);
+	len += sprintf(page + len, "name     : %s\n", DRIVERLNAME);
+	len += sprintf(page + len, "release  : %s\n", DRIVERRELEASE_IDI);
+	strcpy(tmprev, main_revision);
+	len += sprintf(page + len, "revision : %s\n", getrev(tmprev));
+	len += sprintf(page + len, "build    : %s\n", DIVA_BUILD);
+	len += sprintf(page + len, "major    : %d\n", major);
+
+	if (off + count >= len)
+		*eof = 1;
+	if (len < off)
+		return 0;
+	*start = page + off;
+	return ((count < len - off) ? count : len - off);
+}
+
+static int DIVA_INIT_FUNCTION create_um_idi_proc(void)
+{
+	um_idi_proc_entry = create_proc_entry(DRIVERLNAME,
+					      S_IFREG | S_IRUGO | S_IWUSR,
+					      proc_net_eicon);
+	if (!um_idi_proc_entry)
+		return (0);
+
+	um_idi_proc_entry->read_proc = um_idi_proc_read;
+	um_idi_proc_entry->owner = THIS_MODULE;
+
+	return (1);
+}
+
+static void remove_um_idi_proc(void)
+{
+	if (um_idi_proc_entry) {
+		remove_proc_entry(DRIVERLNAME, proc_net_eicon);
+		um_idi_proc_entry = NULL;
+	}
+}
+
+static struct file_operations divas_idi_fops = {
+	.owner   = THIS_MODULE,
+	.llseek  = no_llseek,
+	.read    = um_idi_read,
+	.write   = um_idi_write,
+	.poll    = um_idi_poll,
+	.open    = um_idi_open,
+	.release = um_idi_release
+};
+
+static void divas_idi_unregister_chrdev(void)
+{
+	devfs_remove(DEVNAME);
+	unregister_chrdev(major, DEVNAME);
+}
+
+static int DIVA_INIT_FUNCTION divas_idi_register_chrdev(void)
+{
+	if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0)
+	{
+		printk(KERN_ERR "%s: failed to create /dev entry.\n",
+		       DRIVERLNAME);
+		return (0);
+	}
+	devfs_mk_cdev(MKDEV(major, 0), S_IFCHR|S_IRUSR|S_IWUSR, DEVNAME);
+
+	return (1);
+}
+
+/*
+** Driver Load
+*/
+static int DIVA_INIT_FUNCTION divasi_init(void)
+{
+	char tmprev[50];
+	int ret = 0;
+
+	printk(KERN_INFO "%s\n", DRIVERNAME);
+	printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_IDI);
+	strcpy(tmprev, main_revision);
+	printk("%s  Build: %s\n", getrev(tmprev), DIVA_BUILD);
+
+	if (!divas_idi_register_chrdev()) {
+		ret = -EIO;
+		goto out;
+	}
+
+	if (!create_um_idi_proc()) {
+		divas_idi_unregister_chrdev();
+		printk(KERN_ERR "%s: failed to create proc entry.\n",
+		       DRIVERLNAME);
+		ret = -EIO;
+		goto out;
+	}
+
+	if (!(idifunc_init())) {
+		remove_um_idi_proc();
+		divas_idi_unregister_chrdev();
+		printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+		       DRIVERLNAME);
+		ret = -EIO;
+		goto out;
+	}
+	printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);
+
+      out:
+	return (ret);
+}
+
+
+/*
+** Driver Unload
+*/
+static void DIVA_EXIT_FUNCTION divasi_exit(void)
+{
+	idifunc_finit();
+	remove_um_idi_proc();
+	divas_idi_unregister_chrdev();
+
+	printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divasi_init);
+module_exit(divasi_exit);
+
+
+/*
+ *  FILE OPERATIONS
+ */
+
+static int
+divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src,
+			  int length)
+{
+	memcpy(dst, src, length);
+	return (length);
+}
+
+static ssize_t
+um_idi_read(struct file *file, char __user *buf, size_t count, loff_t * offset)
+{
+	diva_um_idi_os_context_t *p_os;
+	int ret = -EINVAL;
+	void *data;
+
+	if (!file->private_data) {
+		return (-ENODEV);
+	}
+
+	if (!
+	    (p_os =
+	     (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
+								    private_data)))
+	{
+		return (-ENODEV);
+	}
+	if (p_os->aborted) {
+		return (-ENODEV);
+	}
+
+	if (!(data = diva_os_malloc(0, count))) {
+		return (-ENOMEM);
+	}
+
+	ret = diva_um_idi_read(file->private_data,
+			       file, data, count,
+			       divas_um_idi_copy_to_user);
+	switch (ret) {
+	case 0:		/* no message available */
+		ret = (-EAGAIN);
+		break;
+	case (-1):		/* adapter was removed */
+		ret = (-ENODEV);
+		break;
+	case (-2):		/* message_length > length of user buffer */
+		ret = (-EFAULT);
+		break;
+	}
+
+	if (ret > 0) {
+		if (copy_to_user(buf, data, ret)) {
+			ret = (-EFAULT);
+		}
+	}
+
+	diva_os_free(0, data);
+	DBG_TRC(("read: ret %d", ret));
+	return (ret);
+}
+
+
+static int
+divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src,
+			    int length)
+{
+	memcpy(dst, src, length);
+	return (length);
+}
+
+static int um_idi_open_adapter(struct file *file, int adapter_nr)
+{
+	diva_um_idi_os_context_t *p_os;
+	void *e =
+	    divas_um_idi_create_entity((dword) adapter_nr, (void *) file);
+
+	if (!(file->private_data = e)) {
+		return (0);
+	}
+	p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e);
+	init_waitqueue_head(&p_os->read_wait);
+	init_waitqueue_head(&p_os->close_wait);
+	init_timer(&p_os->diva_timer_id);
+	p_os->diva_timer_id.function = (void *) diva_um_timer_function;
+	p_os->diva_timer_id.data = (unsigned long) p_os;
+	p_os->aborted = 0;
+	p_os->adapter_nr = adapter_nr;
+	return (1);
+}
+
+static ssize_t
+um_idi_write(struct file *file, const char __user *buf, size_t count,
+	     loff_t * offset)
+{
+	diva_um_idi_os_context_t *p_os;
+	int ret = -EINVAL;
+	void *data;
+	int adapter_nr = 0;
+
+	if (!file->private_data) {
+		/* the first write() selects the adapter_nr */
+		if (count == sizeof(int)) {
+			if (copy_from_user
+			    ((void *) &adapter_nr, buf,
+			     count)) return (-EFAULT);
+			if (!(um_idi_open_adapter(file, adapter_nr)))
+				return (-ENODEV);
+			return (count);
+		} else
+			return (-ENODEV);
+	}
+
+	if (!(p_os =
+	     (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
+								    private_data)))
+	{
+		return (-ENODEV);
+	}
+	if (p_os->aborted) {
+		return (-ENODEV);
+	}
+
+	if (!(data = diva_os_malloc(0, count))) {
+		return (-ENOMEM);
+	}
+
+	if (copy_from_user(data, buf, count)) {
+		ret = -EFAULT;
+	} else {
+		ret = diva_um_idi_write(file->private_data,
+					file, data, count,
+					divas_um_idi_copy_from_user);
+		switch (ret) {
+		case 0:	/* no space available */
+			ret = (-EAGAIN);
+			break;
+		case (-1):	/* adapter was removed */
+			ret = (-ENODEV);
+			break;
+		case (-2):	/* length of user buffer > max message_length */
+			ret = (-EFAULT);
+			break;
+		}
+	}
+	diva_os_free(0, data);
+	DBG_TRC(("write: ret %d", ret));
+	return (ret);
+}
+
+static unsigned int um_idi_poll(struct file *file, poll_table * wait)
+{
+	diva_um_idi_os_context_t *p_os;
+
+	if (!file->private_data) {
+		return (POLLERR);
+	}
+
+	if ((!(p_os =
+	       (diva_um_idi_os_context_t *)
+	       diva_um_id_get_os_context(file->private_data)))
+	    || p_os->aborted) {
+		return (POLLERR);
+	}
+
+	poll_wait(file, &p_os->read_wait, wait);
+
+	if (p_os->aborted) {
+		return (POLLERR);
+	}
+
+	switch (diva_user_mode_idi_ind_ready(file->private_data, file)) {
+	case (-1):
+		return (POLLERR);
+
+	case 0:
+		return (0);
+	}
+
+	return (POLLIN | POLLRDNORM);
+}
+
+static int um_idi_open(struct inode *inode, struct file *file)
+{
+	return (0);
+}
+
+
+static int um_idi_release(struct inode *inode, struct file *file)
+{
+	diva_um_idi_os_context_t *p_os;
+	unsigned int adapter_nr;
+	int ret = 0;
+
+	if (!(file->private_data)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!(p_os =
+		(diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	adapter_nr = p_os->adapter_nr;
+
+	if ((ret = remove_entity(file->private_data))) {
+		goto out;
+	}
+
+	if (divas_um_idi_delete_entity
+	    ((int) adapter_nr, file->private_data)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+      out:
+	return (ret);
+}
+
+int diva_os_get_context_size(void)
+{
+	return (sizeof(diva_um_idi_os_context_t));
+}
+
+void diva_os_wakeup_read(void *os_context)
+{
+	diva_um_idi_os_context_t *p_os =
+	    (diva_um_idi_os_context_t *) os_context;
+	wake_up_interruptible(&p_os->read_wait);
+}
+
+void diva_os_wakeup_close(void *os_context)
+{
+	diva_um_idi_os_context_t *p_os =
+	    (diva_um_idi_os_context_t *) os_context;
+	wake_up_interruptible(&p_os->close_wait);
+}
+
+static
+void diva_um_timer_function(unsigned long data)
+{
+	diva_um_idi_os_context_t *p_os = (diva_um_idi_os_context_t *) data;
+
+	p_os->aborted = 1;
+	wake_up_interruptible(&p_os->read_wait);
+	wake_up_interruptible(&p_os->close_wait);
+	DBG_ERR(("entity removal watchdog"))
+}
+
+/*
+**  If application exits without entity removal this function will remove
+**  entity and block until removal is complete
+*/
+static int remove_entity(void *entity)
+{
+	struct task_struct *curtask = current;
+	diva_um_idi_os_context_t *p_os;
+
+	diva_um_idi_stop_wdog(entity);
+
+	if (!entity) {
+		DBG_FTL(("Zero entity on remove"))
+		return (0);
+	}
+
+	if (!(p_os =
+	     (diva_um_idi_os_context_t *)
+	     diva_um_id_get_os_context(entity))) {
+		DBG_FTL(("Zero entity os context on remove"))
+		return (0);
+	}
+
+	if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) {
+		/*
+		   Entity is not assigned, also can be removed
+		 */
+		return (0);
+	}
+
+	DBG_TRC(("E(%08x) check remove", entity))
+
+	/*
+	   If adapter not answers on remove request inside of
+	   10 Sec, then adapter is dead
+	 */
+	diva_um_idi_start_wdog(entity);
+
+	{
+		DECLARE_WAITQUEUE(wait, curtask);
+
+		add_wait_queue(&p_os->close_wait, &wait);
+		for (;;) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!divas_um_idi_entity_start_remove(entity)
+			    || p_os->aborted) {
+				break;
+			}
+			schedule();
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&p_os->close_wait, &wait);
+	}
+
+	DBG_TRC(("E(%08x) start remove", entity))
+	{
+		DECLARE_WAITQUEUE(wait, curtask);
+
+		add_wait_queue(&p_os->close_wait, &wait);
+		for (;;) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!divas_um_idi_entity_assigned(entity)
+			    || p_os->aborted) {
+				break;
+			}
+			schedule();
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&p_os->close_wait, &wait);
+	}
+
+	DBG_TRC(("E(%08x) remove complete, aborted:%d", entity,
+		 p_os->aborted))
+
+	diva_um_idi_stop_wdog(entity);
+
+	p_os->aborted = 0;
+
+	return (0);
+}
+
+/*
+ * timer watchdog
+ */
+void diva_um_idi_start_wdog(void *entity)
+{
+	diva_um_idi_os_context_t *p_os;
+
+	if (entity &&
+	    ((p_os =
+	      (diva_um_idi_os_context_t *)
+	      diva_um_id_get_os_context(entity)))) {
+		mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ);
+	}
+}
+
+void diva_um_idi_stop_wdog(void *entity)
+{
+	diva_um_idi_os_context_t *p_os;
+
+	if (entity &&
+	    ((p_os =
+	      (diva_um_idi_os_context_t *)
+	      diva_um_id_get_os_context(entity)))) {
+		del_timer(&p_os->diva_timer_id);
+	}
+}
diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c
new file mode 100644
index 0000000..c9b26e8
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasmain.c
@@ -0,0 +1,856 @@
+/* $Id: divasmain.c,v 1.55.4.6 2005/02/09 19:28:20 armin Exp $
+ *
+ * Low level driver for Eicon DIVA Server ISDN cards.
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/kmod.h>
+
+#include "platform.h"
+#undef ID_MASK
+#undef N_DATA
+#include "pc.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "diva.h"
+#include "di.h"
+#include "io.h"
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "xdi_vers.h"
+#include "diva_dma.h"
+#include "diva_pci.h"
+
+static char *main_revision = "$Revision: 1.55.4.6 $";
+
+static int major;
+
+static int dbgmask;
+
+MODULE_DESCRIPTION("Kernel driver for Eicon DIVA Server cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_LICENSE("GPL");
+
+module_param(dbgmask, int, 0);
+MODULE_PARM_DESC(dbgmask, "initial debug mask");
+
+static char *DRIVERNAME =
+    "Eicon DIVA Server driver (http://www.melware.net)";
+static char *DRIVERLNAME = "divas";
+static char *DEVNAME = "Divas";
+char *DRIVERRELEASE_DIVAS = "2.0";
+
+extern irqreturn_t diva_os_irq_wrapper(int irq, void *context,
+				struct pt_regs *regs);
+extern int create_divas_proc(void);
+extern void remove_divas_proc(void);
+extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf);
+extern int divasfunc_init(int dbgmask);
+extern void divasfunc_exit(void);
+
+typedef struct _diva_os_thread_dpc {
+	struct tasklet_struct divas_task;
+	diva_os_soft_isr_t *psoft_isr;
+} diva_os_thread_dpc_t;
+
+/* --------------------------------------------------------------------------
+    PCI driver interface section
+   -------------------------------------------------------------------------- */
+/*
+  vendor, device	Vendor and device ID to match (or PCI_ANY_ID)
+  subvendor,	Subsystem vendor and device ID to match (or PCI_ANY_ID)
+  subdevice
+  class,		Device class to match. The class_mask tells which bits
+  class_mask	of the class are honored during the comparison.
+  driver_data	Data private to the driver.
+  */
+
+#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2)
+#define PCI_DEVICE_ID_EICON_MAESTRAP_2       0xE015
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_4BRI_VOIP)
+#define PCI_DEVICE_ID_EICON_4BRI_VOIP        0xE016
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_4BRI_2_VOIP)
+#define PCI_DEVICE_ID_EICON_4BRI_2_VOIP      0xE017
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2)
+#define PCI_DEVICE_ID_EICON_BRI2M_2          0xE018
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP)
+#define PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP  0xE019
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_2F)
+#define PCI_DEVICE_ID_EICON_2F               0xE01A
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2_VOIP)
+#define PCI_DEVICE_ID_EICON_BRI2M_2_VOIP     0xE01B
+#endif
+
+/*
+  This table should be sorted by PCI device ID
+  */
+static struct pci_device_id divas_pci_tbl[] = {
+/* Diva Server BRI-2M PCI 0xE010 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRA,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_MAESTRA_PCI},
+/* Diva Server 4BRI-8M PCI 0xE012 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAQ,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_Q_8M_PCI},
+/* Diva Server 4BRI-8M 2.0 PCI 0xE013 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAQ_U,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_Q_8M_V2_PCI},
+/* Diva Server PRI-30M PCI 0xE014 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_P_30M_PCI},
+/* Diva Server PRI 2.0 adapter 0xE015 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_P_30M_V2_PCI},
+/* Diva Server Voice 4BRI-8M PCI 0xE016 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_4BRI_VOIP,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_Q_8M_PCI},
+/* Diva Server Voice 4BRI-8M 2.0 PCI 0xE017 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_4BRI_2_VOIP,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI},
+/* Diva Server BRI-2M 2.0 PCI 0xE018 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_BRI2M_2,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_B_2M_V2_PCI},
+/* Diva Server Voice PRI 2.0 PCI 0xE019 */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+	 CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI},
+/* Diva Server 2FX 0xE01A */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_2F,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_B_2F_PCI},
+/* Diva Server Voice BRI-2M 2.0 PCI 0xE01B */
+	{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_BRI2M_2_VOIP,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI},
+	{0,}			/* 0 terminated list. */
+};
+MODULE_DEVICE_TABLE(pci, divas_pci_tbl);
+
+static int divas_init_one(struct pci_dev *pdev,
+			  const struct pci_device_id *ent);
+static void __devexit divas_remove_one(struct pci_dev *pdev);
+
+static struct pci_driver diva_pci_driver = {
+	.name     = "divas",
+	.probe    = divas_init_one,
+	.remove   = __devexit_p(divas_remove_one),
+	.id_table = divas_pci_tbl,
+};
+
+/*********************************************************
+ ** little helper functions
+ *********************************************************/
+static char *getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "1.0";
+	return rev;
+}
+
+void diva_log_info(unsigned char *format, ...)
+{
+	va_list args;
+	unsigned char line[160];
+
+	va_start(args, format);
+	vsprintf(line, format, args);
+	va_end(args);
+
+	printk(KERN_INFO "%s: %s\n", DRIVERLNAME, line);
+}
+
+void divas_get_version(char *p)
+{
+	char tmprev[32];
+
+	strcpy(tmprev, main_revision);
+	sprintf(p, "%s: %s(%s) %s(%s) major=%d\n", DRIVERLNAME, DRIVERRELEASE_DIVAS,
+		getrev(tmprev), diva_xdi_common_code_build, DIVA_BUILD, major);
+}
+
+/* --------------------------------------------------------------------------
+    PCI Bus services  
+   -------------------------------------------------------------------------- */
+byte diva_os_get_pci_bus(void *pci_dev_handle)
+{
+	struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle;
+	return ((byte) pdev->bus->number);
+}
+
+byte diva_os_get_pci_func(void *pci_dev_handle)
+{
+	struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle;
+	return ((byte) pdev->devfn);
+}
+
+unsigned long divasa_get_pci_irq(unsigned char bus, unsigned char func,
+				 void *pci_dev_handle)
+{
+	unsigned char irq = 0;
+	struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+	irq = dev->irq;
+
+	return ((unsigned long) irq);
+}
+
+unsigned long divasa_get_pci_bar(unsigned char bus, unsigned char func,
+				 int bar, void *pci_dev_handle)
+{
+	unsigned long ret = 0;
+	struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+	if (bar < 6) {
+		ret = dev->resource[bar].start;
+	}
+
+	DBG_TRC(("GOT BAR[%d]=%08x", bar, ret));
+
+	{
+		unsigned long type = (ret & 0x00000001);
+		if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+			DBG_TRC(("  I/O"));
+			ret &= PCI_BASE_ADDRESS_IO_MASK;
+		} else {
+			DBG_TRC(("  memory"));
+			ret &= PCI_BASE_ADDRESS_MEM_MASK;
+		}
+		DBG_TRC(("  final=%08x", ret));
+	}
+
+	return (ret);
+}
+
+void PCIwrite(byte bus, byte func, int offset, void *data, int length,
+	      void *pci_dev_handle)
+{
+	struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+	switch (length) {
+	case 1:		/* byte */
+		pci_write_config_byte(dev, offset,
+				      *(unsigned char *) data);
+		break;
+	case 2:		/* word */
+		pci_write_config_word(dev, offset,
+				      *(unsigned short *) data);
+		break;
+	case 4:		/* dword */
+		pci_write_config_dword(dev, offset,
+				       *(unsigned int *) data);
+		break;
+
+	default:		/* buffer */
+		if (!(length % 4) && !(length & 0x03)) {	/* Copy as dword */
+			dword *p = (dword *) data;
+			length /= 4;
+
+			while (length--) {
+				pci_write_config_dword(dev, offset,
+						       *(unsigned int *)
+						       p++);
+			}
+		} else {	/* copy as byte stream */
+			byte *p = (byte *) data;
+
+			while (length--) {
+				pci_write_config_byte(dev, offset,
+						      *(unsigned char *)
+						      p++);
+			}
+		}
+	}
+}
+
+void PCIread(byte bus, byte func, int offset, void *data, int length,
+	     void *pci_dev_handle)
+{
+	struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+	switch (length) {
+	case 1:		/* byte */
+		pci_read_config_byte(dev, offset, (unsigned char *) data);
+		break;
+	case 2:		/* word */
+		pci_read_config_word(dev, offset, (unsigned short *) data);
+		break;
+	case 4:		/* dword */
+		pci_read_config_dword(dev, offset, (unsigned int *) data);
+		break;
+
+	default:		/* buffer */
+		if (!(length % 4) && !(length & 0x03)) {	/* Copy as dword */
+			dword *p = (dword *) data;
+			length /= 4;
+
+			while (length--) {
+				pci_read_config_dword(dev, offset,
+						      (unsigned int *)
+						      p++);
+			}
+		} else {	/* copy as byte stream */
+			byte *p = (byte *) data;
+
+			while (length--) {
+				pci_read_config_byte(dev, offset,
+						     (unsigned char *)
+						     p++);
+			}
+		}
+	}
+}
+
+/*
+  Init map with DMA pages. It is not problem if some allocations fail -
+  the channels that will not get one DMA page will use standard PIO
+  interface
+  */
+static void *diva_pci_alloc_consistent(struct pci_dev *hwdev,
+				       size_t size,
+				       dma_addr_t * dma_handle,
+				       void **addr_handle)
+{
+	void *addr = pci_alloc_consistent(hwdev, size, dma_handle);
+
+	*addr_handle = addr;
+
+	return (addr);
+}
+
+void diva_init_dma_map(void *hdev,
+		       struct _diva_dma_map_entry **ppmap, int nentries)
+{
+	struct pci_dev *pdev = (struct pci_dev *) hdev;
+	struct _diva_dma_map_entry *pmap =
+	    diva_alloc_dma_map(hdev, nentries);
+
+	if (pmap) {
+		int i;
+		dma_addr_t dma_handle;
+		void *cpu_addr;
+		void *addr_handle;
+
+		for (i = 0; i < nentries; i++) {
+			if (!(cpu_addr = diva_pci_alloc_consistent(pdev,
+								   PAGE_SIZE,
+								   &dma_handle,
+								   &addr_handle)))
+			{
+				break;
+			}
+			diva_init_dma_map_entry(pmap, i, cpu_addr,
+						(dword) dma_handle,
+						addr_handle);
+			DBG_TRC(("dma map alloc [%d]=(%08lx:%08x:%08lx)",
+				 i, (unsigned long) cpu_addr,
+				 (dword) dma_handle,
+				 (unsigned long) addr_handle))}
+	}
+
+	*ppmap = pmap;
+}
+
+/*
+  Free all contained in the map entries and memory used by the map
+  Should be always called after adapter removal from DIDD array
+  */
+void diva_free_dma_map(void *hdev, struct _diva_dma_map_entry *pmap)
+{
+	struct pci_dev *pdev = (struct pci_dev *) hdev;
+	int i;
+	dword phys_addr;
+	void *cpu_addr;
+	dma_addr_t dma_handle;
+	void *addr_handle;
+
+	for (i = 0; (pmap != 0); i++) {
+		diva_get_dma_map_entry(pmap, i, &cpu_addr, &phys_addr);
+		if (!cpu_addr) {
+			break;
+		}
+		addr_handle = diva_get_entry_handle(pmap, i);
+		dma_handle = (dma_addr_t) phys_addr;
+		pci_free_consistent(pdev, PAGE_SIZE, addr_handle,
+				    dma_handle);
+		DBG_TRC(("dma map free [%d]=(%08lx:%08x:%08lx)", i,
+			 (unsigned long) cpu_addr, (dword) dma_handle,
+			 (unsigned long) addr_handle))
+	}
+
+	diva_free_dma_mapping(pmap);
+}
+
+
+/*********************************************************
+ ** I/O port utilities  
+ *********************************************************/
+
+int
+diva_os_register_io_port(void *adapter, int on, unsigned long port,
+			 unsigned long length, const char *name, int id)
+{
+	if (on) {
+		if (!request_region(port, length, name)) {
+			DBG_ERR(("A: I/O: can't register port=%08x", port))
+			return (-1);
+		}
+	} else {
+		release_region(port, length);
+	}
+	return (0);
+}
+
+void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a, int id, unsigned long bar, unsigned long area_length)
+{
+	void __iomem *ret = ioremap(bar, area_length);
+	DBG_TRC(("remap(%08x)->%p", bar, ret));
+	return (ret);
+}
+
+void divasa_unmap_pci_bar(void __iomem *bar)
+{
+	if (bar) {
+		iounmap(bar);
+	}
+}
+
+/*********************************************************
+ ** I/O port access 
+ *********************************************************/
+byte __inline__ inpp(void __iomem *addr)
+{
+	return (inb((unsigned long) addr));
+}
+
+word __inline__ inppw(void __iomem *addr)
+{
+	return (inw((unsigned long) addr));
+}
+
+void __inline__ inppw_buffer(void __iomem *addr, void *P, int length)
+{
+	insw((unsigned long) addr, (word *) P, length >> 1);
+}
+
+void __inline__ outppw_buffer(void __iomem *addr, void *P, int length)
+{
+	outsw((unsigned long) addr, (word *) P, length >> 1);
+}
+
+void __inline__ outppw(void __iomem *addr, word w)
+{
+	outw(w, (unsigned long) addr);
+}
+
+void __inline__ outpp(void __iomem *addr, word p)
+{
+	outb(p, (unsigned long) addr);
+}
+
+/* --------------------------------------------------------------------------
+    IRQ request / remove  
+   -------------------------------------------------------------------------- */
+int diva_os_register_irq(void *context, byte irq, const char *name)
+{
+	int result = request_irq(irq, diva_os_irq_wrapper,
+				 SA_INTERRUPT | SA_SHIRQ, name, context);
+	return (result);
+}
+
+void diva_os_remove_irq(void *context, byte irq)
+{
+	free_irq(irq, context);
+}
+
+/* --------------------------------------------------------------------------
+    DPC framework implementation
+   -------------------------------------------------------------------------- */
+static void diva_os_dpc_proc(unsigned long context)
+{
+	diva_os_thread_dpc_t *psoft_isr = (diva_os_thread_dpc_t *) context;
+	diva_os_soft_isr_t *pisr = psoft_isr->psoft_isr;
+
+	(*(pisr->callback)) (pisr, pisr->callback_context);
+}
+
+int diva_os_initialize_soft_isr(diva_os_soft_isr_t * psoft_isr,
+				diva_os_soft_isr_callback_t callback,
+				void *callback_context)
+{
+	diva_os_thread_dpc_t *pdpc;
+
+	pdpc = (diva_os_thread_dpc_t *) diva_os_malloc(0, sizeof(*pdpc));
+	if (!(psoft_isr->object = pdpc)) {
+		return (-1);
+	}
+	memset(pdpc, 0x00, sizeof(*pdpc));
+	psoft_isr->callback = callback;
+	psoft_isr->callback_context = callback_context;
+	pdpc->psoft_isr = psoft_isr;
+	tasklet_init(&pdpc->divas_task, diva_os_dpc_proc, (unsigned long)pdpc);
+
+	return (0);
+}
+
+int diva_os_schedule_soft_isr(diva_os_soft_isr_t * psoft_isr)
+{
+	if (psoft_isr && psoft_isr->object) {
+		diva_os_thread_dpc_t *pdpc =
+		    (diva_os_thread_dpc_t *) psoft_isr->object;
+
+		tasklet_schedule(&pdpc->divas_task);
+	}
+
+	return (1);
+}
+
+int diva_os_cancel_soft_isr(diva_os_soft_isr_t * psoft_isr)
+{
+	return (0);
+}
+
+void diva_os_remove_soft_isr(diva_os_soft_isr_t * psoft_isr)
+{
+	if (psoft_isr && psoft_isr->object) {
+		diva_os_thread_dpc_t *pdpc =
+		    (diva_os_thread_dpc_t *) psoft_isr->object;
+		void *mem;
+
+		tasklet_kill(&pdpc->divas_task);
+		flush_scheduled_work();
+		mem = psoft_isr->object;
+		psoft_isr->object = NULL;
+		diva_os_free(0, mem);
+	}
+}
+
+/*
+ * kernel/user space copy functions
+ */
+static int
+xdi_copy_to_user(void *os_handle, void __user *dst, const void *src, int length)
+{
+	if (copy_to_user(dst, src, length)) {
+		return (-EFAULT);
+	}
+	return (length);
+}
+
+static int
+xdi_copy_from_user(void *os_handle, void *dst, const void __user *src, int length)
+{
+	if (copy_from_user(dst, src, length)) {
+		return (-EFAULT);
+	}
+	return (length);
+}
+
+/*
+ * device node operations
+ */
+static int divas_open(struct inode *inode, struct file *file)
+{
+	return (0);
+}
+
+static int divas_release(struct inode *inode, struct file *file)
+{
+	if (file->private_data) {
+		diva_xdi_close_adapter(file->private_data, file);
+	}
+	return (0);
+}
+
+static ssize_t divas_write(struct file *file, const char __user *buf,
+			   size_t count, loff_t * ppos)
+{
+	int ret = -EINVAL;
+
+	if (!file->private_data) {
+		file->private_data = diva_xdi_open_adapter(file, buf,
+							   count,
+							   xdi_copy_from_user);
+	}
+	if (!file->private_data) {
+		return (-ENODEV);
+	}
+
+	ret = diva_xdi_write(file->private_data, file,
+			     buf, count, xdi_copy_from_user);
+	switch (ret) {
+	case -1:		/* Message should be removed from rx mailbox first */
+		ret = -EBUSY;
+		break;
+	case -2:		/* invalid adapter was specified in this call */
+		ret = -ENOMEM;
+		break;
+	case -3:
+		ret = -ENXIO;
+		break;
+	}
+	DBG_TRC(("write: ret %d", ret));
+	return (ret);
+}
+
+static ssize_t divas_read(struct file *file, char __user *buf,
+			  size_t count, loff_t * ppos)
+{
+	int ret = -EINVAL;
+
+	if (!file->private_data) {
+		file->private_data = diva_xdi_open_adapter(file, buf,
+							   count,
+							   xdi_copy_from_user);
+	}
+	if (!file->private_data) {
+		return (-ENODEV);
+	}
+
+	ret = diva_xdi_read(file->private_data, file,
+			    buf, count, xdi_copy_to_user);
+	switch (ret) {
+	case -1:		/* RX mailbox is empty */
+		ret = -EAGAIN;
+		break;
+	case -2:		/* no memory, mailbox was cleared, last command is failed */
+		ret = -ENOMEM;
+		break;
+	case -3:		/* can't copy to user, retry */
+		ret = -EFAULT;
+		break;
+	}
+	DBG_TRC(("read: ret %d", ret));
+	return (ret);
+}
+
+static unsigned int divas_poll(struct file *file, poll_table * wait)
+{
+	if (!file->private_data) {
+		return (POLLERR);
+	}
+	return (POLLIN | POLLRDNORM);
+}
+
+static struct file_operations divas_fops = {
+	.owner   = THIS_MODULE,
+	.llseek  = no_llseek,
+	.read    = divas_read,
+	.write   = divas_write,
+	.poll    = divas_poll,
+	.open    = divas_open,
+	.release = divas_release
+};
+
+static void divas_unregister_chrdev(void)
+{
+	devfs_remove(DEVNAME);
+	unregister_chrdev(major, DEVNAME);
+}
+
+static int DIVA_INIT_FUNCTION divas_register_chrdev(void)
+{
+	if ((major = register_chrdev(0, DEVNAME, &divas_fops)) < 0)
+	{
+		printk(KERN_ERR "%s: failed to create /dev entry.\n",
+		       DRIVERLNAME);
+		return (0);
+	}
+	devfs_mk_cdev(MKDEV(major, 0), S_IFCHR|S_IRUSR|S_IWUSR, DEVNAME);
+
+	return (1);
+}
+
+/* --------------------------------------------------------------------------
+    PCI driver section
+   -------------------------------------------------------------------------- */
+static int __devinit divas_init_one(struct pci_dev *pdev,
+				    const struct pci_device_id *ent)
+{
+	void *pdiva = NULL;
+	u8 pci_latency;
+	u8 new_latency = 32;
+
+	DBG_TRC(("%s bus: %08x fn: %08x insertion.\n",
+		 CardProperties[ent->driver_data].Name,
+		 pdev->bus->number, pdev->devfn))
+	printk(KERN_INFO "%s: %s bus: %08x fn: %08x insertion.\n",
+		DRIVERLNAME, CardProperties[ent->driver_data].Name,
+		pdev->bus->number, pdev->devfn);
+
+	if (pci_enable_device(pdev)) {
+		DBG_TRC(("%s: %s bus: %08x fn: %08x device init failed.\n",
+			 DRIVERLNAME,
+			 CardProperties[ent->driver_data].Name,
+			 pdev->bus->number,
+			 pdev->devfn))
+		printk(KERN_ERR
+			"%s: %s bus: %08x fn: %08x device init failed.\n",
+			DRIVERLNAME,
+			CardProperties[ent->driver_data].
+			Name, pdev->bus->number,
+			pdev->devfn);
+		return (-EIO);
+	}
+
+	pci_set_master(pdev);
+
+	pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+	if (!pci_latency) {
+		DBG_TRC(("%s: bus: %08x fn: %08x fix latency.\n",
+			 DRIVERLNAME, pdev->bus->number, pdev->devfn))
+		printk(KERN_INFO
+			"%s: bus: %08x fn: %08x fix latency.\n",
+			 DRIVERLNAME, pdev->bus->number, pdev->devfn);
+		pci_write_config_byte(pdev, PCI_LATENCY_TIMER, new_latency);
+	}
+
+	if (!(pdiva = diva_driver_add_card(pdev, ent->driver_data))) {
+		DBG_TRC(("%s: %s bus: %08x fn: %08x card init failed.\n",
+			 DRIVERLNAME,
+			 CardProperties[ent->driver_data].Name,
+			 pdev->bus->number,
+			 pdev->devfn))
+		printk(KERN_ERR
+			"%s: %s bus: %08x fn: %08x card init failed.\n",
+			DRIVERLNAME,
+			CardProperties[ent->driver_data].
+			Name, pdev->bus->number,
+			pdev->devfn);
+		return (-EIO);
+	}
+
+	pci_set_drvdata(pdev, pdiva);
+
+	return (0);
+}
+
+static void __devexit divas_remove_one(struct pci_dev *pdev)
+{
+	void *pdiva = pci_get_drvdata(pdev);
+
+	DBG_TRC(("bus: %08x fn: %08x removal.\n",
+		 pdev->bus->number, pdev->devfn))
+	printk(KERN_INFO "%s: bus: %08x fn: %08x removal.\n",
+		DRIVERLNAME, pdev->bus->number, pdev->devfn);
+
+	if (pdiva) {
+		diva_driver_remove_card(pdiva);
+	}
+
+}
+
+/* --------------------------------------------------------------------------
+    Driver Load / Startup  
+   -------------------------------------------------------------------------- */
+static int DIVA_INIT_FUNCTION divas_init(void)
+{
+	char tmprev[50];
+	int ret = 0;
+
+	printk(KERN_INFO "%s\n", DRIVERNAME);
+	printk(KERN_INFO "%s: Rel:%s  Rev:", DRIVERLNAME, DRIVERRELEASE_DIVAS);
+	strcpy(tmprev, main_revision);
+	printk("%s  Build: %s(%s)\n", getrev(tmprev),
+	       diva_xdi_common_code_build, DIVA_BUILD);
+	printk(KERN_INFO "%s: support for: ", DRIVERLNAME);
+#ifdef CONFIG_ISDN_DIVAS_BRIPCI
+	printk("BRI/PCI ");
+#endif
+#ifdef CONFIG_ISDN_DIVAS_PRIPCI
+	printk("PRI/PCI ");
+#endif
+	printk("adapters\n");
+
+	if (!divasfunc_init(dbgmask)) {
+		printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+		       DRIVERLNAME);
+		ret = -EIO;
+		goto out;
+	}
+
+	if (!divas_register_chrdev()) {
+#ifdef MODULE
+		divasfunc_exit();
+#endif
+		ret = -EIO;
+		goto out;
+	}
+
+	if (!create_divas_proc()) {
+#ifdef MODULE
+		remove_divas_proc();
+		divas_unregister_chrdev();
+		divasfunc_exit();
+#endif
+		printk(KERN_ERR "%s: failed to create proc entry.\n",
+		       DRIVERLNAME);
+		ret = -EIO;
+		goto out;
+	}
+
+	if ((ret = pci_register_driver(&diva_pci_driver))) {
+#ifdef MODULE
+		remove_divas_proc();
+		divas_unregister_chrdev();
+		divasfunc_exit();
+#endif
+		printk(KERN_ERR "%s: failed to init pci driver.\n",
+		       DRIVERLNAME);
+		goto out;
+	}
+	printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);
+
+      out:
+	return (ret);
+}
+
+/* --------------------------------------------------------------------------
+    Driver Unload
+   -------------------------------------------------------------------------- */
+static void DIVA_EXIT_FUNCTION divas_exit(void)
+{
+	pci_unregister_driver(&diva_pci_driver);
+	remove_divas_proc();
+	divas_unregister_chrdev();
+	divasfunc_exit();
+
+	printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divas_init);
+module_exit(divas_exit);
diff --git a/drivers/isdn/hardware/eicon/divasproc.c b/drivers/isdn/hardware/eicon/divasproc.c
new file mode 100644
index 0000000..b643558
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasproc.c
@@ -0,0 +1,441 @@
+/* $Id: divasproc.c,v 1.19.4.3 2005/01/31 12:22:20 armin Exp $
+ *
+ * Low level driver for Eicon DIVA Server ISDN cards.
+ * /proc functions
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+
+#include "platform.h"
+#include "debuglib.h"
+#undef ID_MASK
+#undef N_DATA
+#include "pc.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "di.h"
+#include "io.h"
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "diva.h"
+#include "diva_pci.h"
+
+
+extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+extern void divas_get_version(char *);
+extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf);
+
+/*********************************************************
+ ** Functions for /proc interface / File operations
+ *********************************************************/
+
+static char *divas_proc_name = "divas";
+static char *adapter_dir_name = "adapter";
+static char *info_proc_name = "info";
+static char *grp_opt_proc_name = "group_optimization";
+static char *d_l1_down_proc_name = "dynamic_l1_down";
+
+/*
+** "divas" entry
+*/
+
+extern struct proc_dir_entry *proc_net_eicon;
+static struct proc_dir_entry *divas_proc_entry = NULL;
+
+static ssize_t
+divas_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+	int len = 0;
+	int cadapter;
+	char tmpbuf[80];
+	char tmpser[16];
+
+	if (*off)
+		return 0;
+
+	divas_get_version(tmpbuf);
+	if (copy_to_user(buf + len, &tmpbuf, strlen(tmpbuf)))
+		return -EFAULT;
+	len += strlen(tmpbuf);
+
+	for (cadapter = 0; cadapter < MAX_ADAPTER; cadapter++) {
+		if (IoAdapters[cadapter]) {
+			diva_get_vserial_number(IoAdapters[cadapter],
+						tmpser);
+			sprintf(tmpbuf,
+				"%2d: %-30s Serial:%-10s IRQ:%2d\n",
+				cadapter + 1,
+				IoAdapters[cadapter]->Properties.Name,
+				tmpser,
+				IoAdapters[cadapter]->irq_info.irq_nr);
+			if ((strlen(tmpbuf) + len) > count)
+				break;
+			if (copy_to_user
+			    (buf + len, &tmpbuf,
+			     strlen(tmpbuf))) return -EFAULT;
+			len += strlen(tmpbuf);
+		}
+	}
+
+	*off += len;
+	return (len);
+}
+
+static ssize_t
+divas_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+	return (-ENODEV);
+}
+
+static unsigned int divas_poll(struct file *file, poll_table * wait)
+{
+	return (POLLERR);
+}
+
+static int divas_open(struct inode *inode, struct file *file)
+{
+	return nonseekable_open(inode, file);
+}
+
+static int divas_close(struct inode *inode, struct file *file)
+{
+	return (0);
+}
+
+static struct file_operations divas_fops = {
+	.owner   = THIS_MODULE,
+	.llseek  = no_llseek,
+	.read    = divas_read,
+	.write   = divas_write,
+	.poll    = divas_poll,
+	.open    = divas_open,
+	.release = divas_close
+};
+
+int create_divas_proc(void)
+{
+	divas_proc_entry = create_proc_entry(divas_proc_name,
+					     S_IFREG | S_IRUGO,
+					     proc_net_eicon);
+	if (!divas_proc_entry)
+		return (0);
+
+	divas_proc_entry->proc_fops = &divas_fops;
+	divas_proc_entry->owner = THIS_MODULE;
+
+	return (1);
+}
+
+void remove_divas_proc(void)
+{
+	if (divas_proc_entry) {
+		remove_proc_entry(divas_proc_name, proc_net_eicon);
+		divas_proc_entry = NULL;
+	}
+}
+
+/*
+** write group_optimization 
+*/
+static int
+write_grp_opt(struct file *file, const char __user *buffer, unsigned long count,
+	      void *data)
+{
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+	PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+	if ((count == 1) || (count == 2)) {
+		char c;
+		if (get_user(c, buffer))
+			return -EFAULT;
+		switch (c) {
+		case '0':
+			IoAdapter->capi_cfg.cfg_1 &=
+			    ~DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON;
+			break;
+		case '1':
+			IoAdapter->capi_cfg.cfg_1 |=
+			    DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON;
+			break;
+		default:
+			return (-EINVAL);
+		}
+		return (count);
+	}
+	return (-EINVAL);
+}
+
+/*
+** write dynamic_l1_down
+*/
+static int
+write_d_l1_down(struct file *file, const char __user *buffer, unsigned long count,
+		void *data)
+{
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+	PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+	if ((count == 1) || (count == 2)) {
+		char c;
+		if (get_user(c, buffer))
+			return -EFAULT;
+		switch (c) {
+		case '0':
+			IoAdapter->capi_cfg.cfg_1 &=
+			    ~DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON;
+			break;
+		case '1':
+			IoAdapter->capi_cfg.cfg_1 |=
+			    DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON;
+			break;
+		default:
+			return (-EINVAL);
+		}
+		return (count);
+	}
+	return (-EINVAL);
+}
+
+
+/*
+** read dynamic_l1_down 
+*/
+static int
+read_d_l1_down(char *page, char **start, off_t off, int count, int *eof,
+	       void *data)
+{
+	int len = 0;
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+	PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+	len += sprintf(page + len, "%s\n",
+		       (IoAdapter->capi_cfg.
+			cfg_1 & DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? "1" :
+		       "0");
+
+	if (off + count >= len)
+		*eof = 1;
+	if (len < off)
+		return 0;
+	*start = page + off;
+	return ((count < len - off) ? count : len - off);
+}
+
+/*
+** read group_optimization
+*/
+static int
+read_grp_opt(char *page, char **start, off_t off, int count, int *eof,
+	     void *data)
+{
+	int len = 0;
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+	PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+	len += sprintf(page + len, "%s\n",
+		       (IoAdapter->capi_cfg.
+			cfg_1 & DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON)
+		       ? "1" : "0");
+
+	if (off + count >= len)
+		*eof = 1;
+	if (len < off)
+		return 0;
+	*start = page + off;
+	return ((count < len - off) ? count : len - off);
+}
+
+/*
+** info write
+*/
+static int
+info_write(struct file *file, const char __user *buffer, unsigned long count,
+	   void *data)
+{
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+	PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+	char c[4];
+
+	if (count <= 4)
+		return -EINVAL;
+
+	if (copy_from_user(c, buffer, 4))
+		return -EFAULT;
+
+	/* this is for test purposes only */
+	if (!memcmp(c, "trap", 4)) {
+		(*(IoAdapter->os_trap_nfy_Fnc)) (IoAdapter, IoAdapter->ANum);
+		return (count);
+	}
+	return (-EINVAL);
+}
+
+/*
+** info read
+*/
+static int
+info_read(char *page, char **start, off_t off, int count, int *eof,
+	  void *data)
+{
+	int i = 0;
+	int len = 0;
+	char *p;
+	char tmpser[16];
+	diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+	PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+	len +=
+	    sprintf(page + len, "Name        : %s\n",
+		    IoAdapter->Properties.Name);
+	len += sprintf(page + len, "DSP state   : %08x\n", a->dsp_mask);
+	len += sprintf(page + len, "Channels    : %02d\n",
+		       IoAdapter->Properties.Channels);
+	len += sprintf(page + len, "E. max/used : %03d/%03d\n",
+		       IoAdapter->e_max, IoAdapter->e_count);
+	diva_get_vserial_number(IoAdapter, tmpser);
+	len += sprintf(page + len, "Serial      : %s\n", tmpser);
+	len +=
+	    sprintf(page + len, "IRQ         : %d\n",
+		    IoAdapter->irq_info.irq_nr);
+	len += sprintf(page + len, "CardIndex   : %d\n", a->CardIndex);
+	len += sprintf(page + len, "CardOrdinal : %d\n", a->CardOrdinal);
+	len += sprintf(page + len, "Controller  : %d\n", a->controller);
+	len += sprintf(page + len, "Bus-Type    : %s\n",
+		       (a->Bus ==
+			DIVAS_XDI_ADAPTER_BUS_ISA) ? "ISA" : "PCI");
+	len += sprintf(page + len, "Port-Name   : %s\n", a->port_name);
+	if (a->Bus == DIVAS_XDI_ADAPTER_BUS_PCI) {
+		len +=
+		    sprintf(page + len, "PCI-bus     : %d\n",
+			    a->resources.pci.bus);
+		len +=
+		    sprintf(page + len, "PCI-func    : %d\n",
+			    a->resources.pci.func);
+		for (i = 0; i < 8; i++) {
+			if (a->resources.pci.bar[i]) {
+				len +=
+				    sprintf(page + len,
+					    "Mem / I/O %d : 0x%x / mapped : 0x%lx",
+					    i, a->resources.pci.bar[i],
+					    (unsigned long) a->resources.
+					    pci.addr[i]);
+				if (a->resources.pci.length[i]) {
+					len +=
+					    sprintf(page + len,
+						    " / length : %d",
+						    a->resources.pci.
+						    length[i]);
+				}
+				len += sprintf(page + len, "\n");
+			}
+		}
+	}
+	if ((!a->xdi_adapter.port) &&
+	    ((!a->xdi_adapter.ram) ||
+	    (!a->xdi_adapter.reset)
+	     || (!a->xdi_adapter.cfg))) {
+		if (!IoAdapter->irq_info.irq_nr) {
+			p = "slave";
+		} else {
+			p = "out of service";
+		}
+	} else if (a->xdi_adapter.trapped) {
+		p = "trapped";
+	} else if (a->xdi_adapter.Initialized) {
+		p = "active";
+	} else {
+		p = "ready";
+	}
+	len += sprintf(page + len, "State       : %s\n", p);
+
+	if (off + count >= len)
+		*eof = 1;
+	if (len < off)
+		return 0;
+	*start = page + off;
+	return ((count < len - off) ? count : len - off);
+}
+
+/*
+** adapter proc init/de-init
+*/
+
+/* --------------------------------------------------------------------------
+    Create adapter directory and files in proc file system
+   -------------------------------------------------------------------------- */
+int create_adapter_proc(diva_os_xdi_adapter_t * a)
+{
+	struct proc_dir_entry *de, *pe;
+	char tmp[16];
+
+	sprintf(tmp, "%s%d", adapter_dir_name, a->controller);
+	if (!(de = create_proc_entry(tmp, S_IFDIR, proc_net_eicon)))
+		return (0);
+	a->proc_adapter_dir = (void *) de;
+
+	if (!(pe =
+	     create_proc_entry(info_proc_name, S_IFREG | S_IRUGO | S_IWUSR, de)))
+		return (0);
+	a->proc_info = (void *) pe;
+	pe->write_proc = info_write;
+	pe->read_proc = info_read;
+	pe->data = a;
+
+	if ((pe = create_proc_entry(grp_opt_proc_name,
+			       S_IFREG | S_IRUGO | S_IWUSR, de))) {
+		a->proc_grp_opt = (void *) pe;
+		pe->write_proc = write_grp_opt;
+		pe->read_proc = read_grp_opt;
+		pe->data = a;
+	}
+	if ((pe = create_proc_entry(d_l1_down_proc_name,
+			       S_IFREG | S_IRUGO | S_IWUSR, de))) {
+		a->proc_d_l1_down = (void *) pe;
+		pe->write_proc = write_d_l1_down;
+		pe->read_proc = read_d_l1_down;
+		pe->data = a;
+	}
+
+	DBG_TRC(("proc entry %s created", tmp));
+
+	return (1);
+}
+
+/* --------------------------------------------------------------------------
+    Remove adapter directory and files in proc file system
+   -------------------------------------------------------------------------- */
+void remove_adapter_proc(diva_os_xdi_adapter_t * a)
+{
+	char tmp[16];
+
+	if (a->proc_adapter_dir) {
+		if (a->proc_d_l1_down) {
+			remove_proc_entry(d_l1_down_proc_name,
+					  (struct proc_dir_entry *) a->proc_adapter_dir);
+		}
+		if (a->proc_grp_opt) {
+			remove_proc_entry(grp_opt_proc_name,
+					  (struct proc_dir_entry *) a->proc_adapter_dir);
+		}
+		if (a->proc_info) {
+			remove_proc_entry(info_proc_name,
+					  (struct proc_dir_entry *) a->proc_adapter_dir);
+		}
+		sprintf(tmp, "%s%d", adapter_dir_name, a->controller);
+		remove_proc_entry(tmp, proc_net_eicon);
+		DBG_TRC(("proc entry %s%d removed", adapter_dir_name,
+			 a->controller));
+	}
+}
diff --git a/drivers/isdn/hardware/eicon/divasync.h b/drivers/isdn/hardware/eicon/divasync.h
new file mode 100644
index 0000000..0a5be7f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasync.h
@@ -0,0 +1,490 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_SYNC__H  
+#define __DIVA_SYNC__H
+#define IDI_SYNC_REQ_REMOVE             0x00
+#define IDI_SYNC_REQ_GET_NAME           0x01
+#define IDI_SYNC_REQ_GET_SERIAL         0x02
+#define IDI_SYNC_REQ_SET_POSTCALL       0x03
+#define IDI_SYNC_REQ_GET_XLOG           0x04
+#define IDI_SYNC_REQ_GET_FEATURES       0x05
+#define IDI_SYNC_REQ_USB_REGISTER       0x06
+#define IDI_SYNC_REQ_USB_RELEASE        0x07
+#define IDI_SYNC_REQ_USB_ADD_DEVICE     0x08
+#define IDI_SYNC_REQ_USB_START_DEVICE   0x09
+#define IDI_SYNC_REQ_USB_STOP_DEVICE    0x0A
+#define IDI_SYNC_REQ_USB_REMOVE_DEVICE  0x0B
+#define IDI_SYNC_REQ_GET_CARDTYPE       0x0C
+#define IDI_SYNC_REQ_GET_DBG_XLOG       0x0D
+#define DIVA_USB
+#define DIVA_USB_REQ                    0xAC
+#define DIVA_USB_TEST                   0xAB
+#define DIVA_USB_ADD_ADAPTER            0xAC
+#define DIVA_USB_REMOVE_ADAPTER         0xAD
+#define IDI_SYNC_REQ_SERIAL_HOOK        0x80
+#define IDI_SYNC_REQ_XCHANGE_STATUS     0x81
+#define IDI_SYNC_REQ_USB_HOOK           0x82
+#define IDI_SYNC_REQ_PORTDRV_HOOK       0x83
+#define IDI_SYNC_REQ_SLI                0x84   /*  SLI request from 3signal modem drivers */
+#define IDI_SYNC_REQ_RECONFIGURE        0x85
+#define IDI_SYNC_REQ_RESET              0x86
+#define IDI_SYNC_REQ_GET_85X_DEVICE_DATA     0x87
+#define IDI_SYNC_REQ_LOCK_85X                   0x88
+#define IDI_SYNC_REQ_DIVA_85X_USB_DATA_EXCHANGE 0x99
+#define IDI_SYNC_REQ_DIPORT_EXCHANGE_REQ   0x98
+#define IDI_SYNC_REQ_GET_85X_EXT_PORT_TYPE      0xA0
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES  0x92
+/*
+   To receive XDI features:
+   1. set 'buffer_length_in_bytes' to length of you buffer
+   2. set 'features' to pointer to your buffer
+   3. issue synchronous request to XDI
+   4. Check that feature 'DIVA_XDI_EXTENDED_FEATURES_VALID' is present
+      after call. This feature does indicate that your request
+      was processed and XDI does support this synchronous request
+   5. if on return bit 31 (0x80000000) in 'buffer_length_in_bytes' is
+      set then provided buffer was too small, and bits 30-0 does
+      contain necessary length of buffer.
+      in this case only features that do find place in the buffer
+      are indicated to caller
+*/
+typedef struct _diva_xdi_get_extended_xdi_features {
+  dword buffer_length_in_bytes;
+  byte  *features;
+} diva_xdi_get_extended_xdi_features_t;
+/*
+   features[0]
+  */
+#define DIVA_XDI_EXTENDED_FEATURES_VALID          0x01
+#define DIVA_XDI_EXTENDED_FEATURE_CMA             0x02
+#define DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR       0x04
+#define DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS       0x08
+#define DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC    0x10
+#define DIVA_XDI_EXTENDED_FEATURE_RX_DMA          0x20
+#define DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA  0x40
+#define DIVA_XDI_EXTENDED_FEATURE_WIDE_ID         0x80
+#define DIVA_XDI_EXTENDED_FEATURES_MAX_SZ    1
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR   0x93
+typedef struct _diva_xdi_get_adapter_sdram_bar {
+ dword bar;
+} diva_xdi_get_adapter_sdram_bar_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS   0x94
+/*
+  CAPI Parameters will be written in the caller's buffer
+  */
+typedef struct _diva_xdi_get_capi_parameters {
+  dword structure_length;
+  byte flag_dynamic_l1_down;
+  byte group_optimization_enabled;
+} diva_xdi_get_capi_parameters_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER   0x95
+/*
+  Get logical adapter number, as assigned by XDI
+  'controller' is starting with zero 'sub' controller number
+  in case of one adapter that supports multiple interfaces
+  'controller' is zero for Master adapter (and adapter that supports
+  only one interface)
+  */
+typedef struct _diva_xdi_get_logical_adapter_number {
+  dword logical_adapter_number;
+  dword controller;
+  dword total_controllers;
+} diva_xdi_get_logical_adapter_number_s_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_UP1DM_OPERATION   0x96
+/******************************************************************************/
+#define IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION 0x97
+#define IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC     0x01
+#define IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE      0x02
+typedef struct _diva_xdi_dma_descriptor_operation {
+  int   operation;
+  int   descriptor_number;
+  void* descriptor_address;
+  dword descriptor_magic;
+} diva_xdi_dma_descriptor_operation_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY   0x01
+#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY     0x02
+#define IDI_SYNC_REQ_DIDD_ADD_ADAPTER               0x03
+#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER            0x04
+#define IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY        0x05
+#define IDI_SYNC_REQ_DIDD_GET_CFG_LIB_IFC           0x10
+typedef struct _diva_didd_adapter_notify {
+ dword handle; /* Notification handle */
+ void   * callback;
+ void   * context;
+} diva_didd_adapter_notify_t;
+typedef struct _diva_didd_add_adapter {
+ void   * descriptor;
+} diva_didd_add_adapter_t;
+typedef struct _diva_didd_remove_adapter {
+ IDI_CALL p_request;
+} diva_didd_remove_adapter_t;
+typedef struct _diva_didd_read_adapter_array {
+ void   * buffer;
+ dword length;
+} diva_didd_read_adapter_array_t;
+typedef struct _diva_didd_get_cfg_lib_ifc {
+ void* ifc;
+} diva_didd_get_cfg_lib_ifc_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_STREAM    0x91
+#define DIVA_XDI_SYNCHRONOUS_SERVICE   0x01
+#define DIVA_XDI_DMA_SERVICE           0x02
+#define DIVA_XDI_AUTO_SERVICE          0x03
+#define DIVA_ISTREAM_COMPLETE_NOTIFY   0
+#define DIVA_ISTREAM_COMPLETE_READ     1
+#define DIVA_ISTREAM_COMPLETE_WRITE    2
+typedef struct _diva_xdi_stream_interface {
+  unsigned char  Id;                 /* filled by XDI client */
+ unsigned char provided_service;    /* filled by XDI        */
+ unsigned char requested_service;   /* filled by XDI Client */
+ void* xdi_context;    /* filled by XDI     */
+ void* client_context;   /* filled by XDI client */
+ int (*write)(void* context,
+               int Id,
+               void* data,
+               int length,
+               int final,
+               byte usr1,
+               byte usr2);
+ int (*read)(void* context,
+              int Id,
+              void* data,
+              int max_length,
+              int* final,
+              byte* usr1,
+              byte* usr2);
+ int (*complete)(void* client_context,
+         int Id,
+          int what,
+         void* data,
+         int length,
+         int* final);
+} diva_xdi_stream_interface_t;
+/******************************************************************************/
+/*
+ * IDI_SYNC_REQ_SERIAL_HOOK - special interface for the DIVA Mobile card
+ */
+typedef struct
+{ unsigned char LineState;         /* Modem line state (STATUS_R) */
+#define SERIAL_GSM_CELL 0x01   /* GSM or CELL cable attached  */
+ unsigned char CardState;          /* PCMCIA card state (0 = down) */
+ unsigned char IsdnState;          /* ISDN layer 1 state (0 = down)*/
+ unsigned char HookState;          /* current logical hook state */
+#define SERIAL_ON_HOOK 0x02   /* set in DIVA CTRL_R register */
+} SERIAL_STATE;
+typedef int (  * SERIAL_INT_CB) (void *Context) ;
+typedef int (  * SERIAL_DPC_CB) (void *Context) ;
+typedef unsigned char (  * SERIAL_I_SYNC) (void *Context) ;
+typedef struct
+{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+ unsigned char Req;             /* request (must be always 0) */
+ unsigned char Rc;              /* return code (is the request) */
+ unsigned char Function;           /* private function code  */
+#define SERIAL_HOOK_ATTACH 0x81
+#define SERIAL_HOOK_STATUS 0x82
+#define SERIAL_HOOK_I_SYNC 0x83
+#define SERIAL_HOOK_NOECHO 0x84
+#define SERIAL_HOOK_RING 0x85
+#define SERIAL_HOOK_DETACH 0x8f
+ unsigned char Flags;           /* function refinements   */
+ /* parameters passed by the the ATTACH request      */
+ SERIAL_INT_CB InterruptHandler; /* called on each interrupt  */
+ SERIAL_DPC_CB DeferredHandler; /* called on hook state changes */
+ void   *HandlerContext; /* context for both handlers */
+ /* return values for both the ATTACH and the STATUS request   */
+ unsigned long IoBase;    /* IO port assigned to UART  */
+ SERIAL_STATE State;
+ /* parameters and return values for the I_SYNC function    */
+ SERIAL_I_SYNC SyncFunction;  /* to be called synchronized */
+ void   *SyncContext;  /* context for this function */
+ unsigned char SyncResult;   /* return value of function  */
+} SERIAL_HOOK;
+/*
+ * IDI_SYNC_REQ_XCHANGE_STATUS - exchange the status between IDI and WMP
+ * IDI_SYNC_REQ_RECONFIGURE - reconfiguration of IDI from WMP
+ */
+typedef struct
+{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+ unsigned char Req;             /* request (must be always 0) */
+ unsigned char Rc;              /* return code (is the request) */
+#define DRIVER_STATUS_BOOT  0xA1
+#define DRIVER_STATUS_INIT_DEV 0xA2
+#define DRIVER_STATUS_RUNNING 0xA3
+#define DRIVER_STATUS_SHUTDOWN 0xAF
+#define DRIVER_STATUS_TRAPPED 0xAE
+ unsigned char wmpStatus;          /* exported by WMP              */
+ unsigned char idiStatus;   /* exported by IDI              */
+ unsigned long wizProto ;   /* from WMP registry to IDI     */
+ /* the cardtype value is defined by cardtype.h */
+ unsigned long cardType ;   /* from IDI registry to WMP     */
+ unsigned long nt2 ;    /* from IDI registry to WMP     */
+ unsigned long permanent ;   /* from IDI registry to WMP     */
+ unsigned long stableL2 ;   /* from IDI registry to WMP     */
+ unsigned long tei ;    /* from IDI registry to WMP     */
+#define CRC4_MASK   0x00000003
+#define L1_TRISTATE_MASK 0x00000004
+#define WATCHDOG_MASK  0x00000008
+#define NO_ORDER_CHECK_MASK 0x00000010
+#define LOW_CHANNEL_MASK 0x00000020
+#define NO_HSCX30_MASK  0x00000040
+#define MODE_MASK   0x00000080
+#define SET_BOARD   0x00001000
+#define SET_CRC4   0x00030000
+#define SET_L1_TRISTATE  0x00040000
+#define SET_WATCHDOG  0x00080000
+#define SET_NO_ORDER_CHECK 0x00100000
+#define SET_LOW_CHANNEL  0x00200000
+#define SET_NO_HSCX30  0x00400000
+#define SET_MODE   0x00800000
+#define SET_PROTO   0x02000000
+#define SET_CARDTYPE  0x04000000
+#define SET_NT2    0x08000000
+#define SET_PERMANENT  0x10000000
+#define SET_STABLEL2  0x20000000
+#define SET_TEI    0x40000000
+#define SET_NUMBERLEN  0x80000000
+ unsigned long Flag ;  /* |31-Type-16|15-Mask-0| */
+ unsigned long NumberLen ; /* reconfiguration: union is empty */
+ union {
+  struct {    /* possible reconfiguration, but ... ; SET_BOARD */
+   unsigned long SerialNumber ;
+   char     *pCardname ; /* di_defs.h: BOARD_NAME_LENGTH */
+  } board ;
+  struct {      /* reset: need resources */
+   void * pRawResources ;
+   void * pXlatResources ;
+  } res ;
+  struct { /* reconfiguration: wizProto == PROTTYPE_RBSCAS */
+#define GLARE_RESOLVE_MASK 0x00000001
+#define DID_MASK   0x00000002
+#define BEARER_CAP_MASK  0x0000000c
+#define SET_GLARE_RESOLVE 0x00010000
+#define SET_DID    0x00020000
+#define SET_BEARER_CAP  0x000c0000
+   unsigned long Flag ;  /* |31-Type-16|15-VALUE-0| */
+   unsigned short DigitTimeout ;
+   unsigned short AnswerDelay ;
+  } rbs ;
+  struct { /* reconfiguration: wizProto == PROTTYPE_QSIG */
+#define CALL_REF_LENGTH1_MASK 0x00000001
+#define BRI_CHANNEL_ID_MASK  0x00000002
+#define SET_CALL_REF_LENGTH  0x00010000
+#define SET_BRI_CHANNEL_ID  0x00020000
+   unsigned long Flag ;  /* |31-Type-16|15-VALUE-0| */
+  } qsig ;
+  struct { /* reconfiguration: NumberLen != 0 */
+#define SET_SPID1   0x00010000
+#define SET_NUMBER1   0x00020000
+#define SET_SUBADDRESS1  0x00040000
+#define SET_SPID2   0x00100000
+#define SET_NUMBER2   0x00200000
+#define SET_SUBADDRESS2  0x00400000
+#define MASK_SET   0xffff0000
+   unsigned long Flag ;   /* |31-Type-16|15-Channel-0| */
+   unsigned char *pBuffer ; /* number value */
+  } isdnNo ;
+ }
+parms
+;
+} isdnProps ;
+/*
+ * IDI_SYNC_REQ_PORTDRV_HOOK - signal plug/unplug (Award Cardware only)
+ */
+typedef void (  * PORTDRV_HOOK_CB) (void *Context, int Plug) ;
+typedef struct
+{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+ unsigned char Req;             /* request (must be always 0) */
+ unsigned char Rc;              /* return code (is the request) */
+ unsigned char Function;           /* private function code  */
+ unsigned char Flags;           /* function refinements   */
+ PORTDRV_HOOK_CB Callback;   /* to be called on plug/unplug */
+ void   *Context;   /* context for callback   */
+ unsigned long Info;    /* more info if needed   */
+} PORTDRV_HOOK ;
+/*  Codes for the 'Rc' element in structure below. */
+#define SLI_INSTALL     (0xA1)
+#define SLI_UNINSTALL   (0xA2)
+typedef int ( * SLIENTRYPOINT)(void* p3SignalAPI, void* pContext);
+typedef struct
+{   /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+    unsigned char   Req;                /* request (must be always 0)   */
+    unsigned char   Rc;                 /* return code (is the request) */
+    unsigned char   Function;           /* private function code        */
+    unsigned char   Flags;              /* function refinements         */
+    SLIENTRYPOINT   Callback;           /* to be called on plug/unplug  */
+    void            *Context;           /* context for callback         */
+    unsigned long   Info;               /* more info if needed          */
+} SLIENTRYPOINT_REQ ;
+/******************************************************************************/
+/*
+ *  Definitions for DIVA USB
+ */
+typedef int  (  * USB_SEND_REQ) (unsigned char PipeIndex, unsigned char Type,void *Data, int sizeData);
+typedef int  (  * USB_START_DEV) (void *Adapter, void *Ipac) ;
+/* called from WDM */
+typedef void (  * USB_RECV_NOTIFY) (void *Ipac, void *msg) ;
+typedef void (  * USB_XMIT_NOTIFY) (void *Ipac, unsigned char PipeIndex) ;
+/******************************************************************************/
+/*
+ * Parameter description for synchronous requests.
+ *
+ * Sorry, must repeat some parts of di_defs.h here because
+ * they are not defined for all operating environments
+ */
+typedef union
+{ ENTITY Entity;
+ struct
+ { /* 'Req' and 'Rc' are at the same place as in the ENTITY struct */
+  unsigned char   Req; /* request (must be always 0) */
+  unsigned char   Rc;  /* return code (is the request) */
+ }   Request;
+ struct
+ { unsigned char   Req; /* request (must be always 0) */
+  unsigned char   Rc;  /* return code (0x01)   */
+  unsigned char   name[BOARD_NAME_LENGTH];
+ }   GetName;
+ struct
+ { unsigned char   Req; /* request (must be always 0) */
+  unsigned char   Rc;  /* return code (0x02)   */
+  unsigned long   serial; /* serial number    */
+ }   GetSerial;
+ struct
+ { unsigned char   Req; /* request (must be always 0) */
+  unsigned char   Rc;  /* return code (0x02)   */
+  unsigned long   lineIdx;/* line, 0 if card has only one */
+ }   GetLineIdx;
+ struct
+ { unsigned char  Req;     /* request (must be always 0) */
+  unsigned char  Rc;      /* return code (0x02)   */
+  unsigned long  cardtype;/* card type        */
+ }   GetCardType;
+ struct
+ { unsigned short command;/* command = 0x0300 */
+  unsigned short dummy; /* not used */
+  IDI_CALL       callback;/* routine to call back */
+  ENTITY      *contxt; /* ptr to entity to use */
+ }   PostCall;
+ struct
+ { unsigned char  Req;  /* request (must be always 0) */
+  unsigned char  Rc;   /* return code (0x04)   */
+  unsigned char  pcm[1]; /* buffer (a pc_maint struct) */
+ }   GetXlog;
+ struct
+ { unsigned char  Req;  /* request (must be always 0) */
+  unsigned char  Rc;   /* return code (0x05)   */
+  unsigned short features;/* feature defines see below */
+ }   GetFeatures;
+ SERIAL_HOOK  SerialHook;
+/* Added for DIVA USB */
+ struct
+ { unsigned char   Req;
+  unsigned char   Rc;
+  USB_SEND_REQ    UsbSendRequest; /* function in Diva Usb WDM driver in usb_os.c, */
+                                        /* called from usb_drv.c to send a message to our device */
+                                        /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0) ; */
+  USB_RECV_NOTIFY usb_recv;       /* called from usb_os.c to pass a received message and ptr to IPAC */
+                                        /* on to usb_drv.c by a call to usb_recv(). */
+  USB_XMIT_NOTIFY usb_xmit;       /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */
+                                        /* to usb_drv.c by a call to usb_xmit(). */
+  USB_START_DEV   UsbStartDevice; /* Start the USB Device, in usb_os.c */
+  IDI_CALL        callback;       /* routine to call back */
+  ENTITY          *contxt;     /* ptr to entity to use */
+  void            ** ipac_ptr;    /* pointer to struct IPAC in VxD */
+ } Usb_Msg_old;
+/* message used by WDM and VXD to pass pointers of function and IPAC* */
+ struct
+ { unsigned char Req;
+  unsigned char Rc;
+        USB_SEND_REQ    pUsbSendRequest;/* function in Diva Usb WDM driver in usb_os.c, */
+                                        /* called from usb_drv.c to send a message to our device */
+                                        /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0) ; */
+        USB_RECV_NOTIFY p_usb_recv;     /* called from usb_os.c to pass a received message and ptr to IPAC */
+                                        /* on to usb_drv.c by a call to usb_recv(). */
+        USB_XMIT_NOTIFY p_usb_xmit;     /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */
+                                        /* to usb_drv.c by a call to usb_xmit().*/
+  void            *ipac_ptr;      /* &Diva.ipac pointer to struct IPAC in VxD */
+ } Usb_Msg;
+ PORTDRV_HOOK PortdrvHook;
+    SLIENTRYPOINT_REQ   sliEntryPointReq;
+  struct {
+    unsigned char Req;
+    unsigned char Rc;
+    diva_xdi_stream_interface_t info;
+  } xdi_stream_info;
+  struct {
+    unsigned char Req;
+    unsigned char Rc;
+    diva_xdi_get_extended_xdi_features_t info;
+  } xdi_extended_features;
+ struct {
+    unsigned char Req;
+    unsigned char Rc;
+  diva_xdi_get_adapter_sdram_bar_t info;
+ } xdi_sdram_bar;
+  struct {
+    unsigned char Req;
+    unsigned char Rc;
+    diva_xdi_get_capi_parameters_t info;
+  } xdi_capi_prms;
+ struct {
+  ENTITY           e;
+  diva_didd_adapter_notify_t info;
+ } didd_notify;
+ struct {
+  ENTITY           e;
+  diva_didd_add_adapter_t   info;
+ } didd_add_adapter;
+ struct {
+  ENTITY           e;
+  diva_didd_remove_adapter_t info;
+ } didd_remove_adapter;
+ struct {
+  ENTITY             e;
+  diva_didd_read_adapter_array_t info;
+ } didd_read_adapter_array;
+ struct {
+  ENTITY             e;
+  diva_didd_get_cfg_lib_ifc_t     info;
+ } didd_get_cfg_lib_ifc;
+  struct {
+    unsigned char Req;
+    unsigned char Rc;
+    diva_xdi_get_logical_adapter_number_s_t info;
+  } xdi_logical_adapter_number;
+  struct {
+    unsigned char Req;
+    unsigned char Rc;
+    diva_xdi_dma_descriptor_operation_t info;
+  } xdi_dma_descriptor_operation;
+} IDI_SYNC_REQ;
+/******************************************************************************/
+#endif /* __DIVA_SYNC__H */  
diff --git a/drivers/isdn/hardware/eicon/dqueue.c b/drivers/isdn/hardware/eicon/dqueue.c
new file mode 100644
index 0000000..9822582
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dqueue.c
@@ -0,0 +1,110 @@
+/* $Id: dqueue.c,v 1.5 2003/04/12 21:40:49 schindler Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * User Mode IDI Interface
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "dqueue.h"
+
+int
+diva_data_q_init(diva_um_idi_data_queue_t * q,
+		 int max_length, int max_segments)
+{
+	int i;
+
+	q->max_length = max_length;
+	q->segments = max_segments;
+
+	for (i = 0; i < q->segments; i++) {
+		q->data[i] = NULL;
+		q->length[i] = 0;
+	}
+	q->read = q->write = q->count = q->segment_pending = 0;
+
+	for (i = 0; i < q->segments; i++) {
+		if (!(q->data[i] = diva_os_malloc(0, q->max_length))) {
+			diva_data_q_finit(q);
+			return (-1);
+		}
+	}
+
+	return (0);
+}
+
+int diva_data_q_finit(diva_um_idi_data_queue_t * q)
+{
+	int i;
+
+	for (i = 0; i < q->segments; i++) {
+		if (q->data[i]) {
+			diva_os_free(0, q->data[i]);
+		}
+		q->data[i] = NULL;
+		q->length[i] = 0;
+	}
+	q->read = q->write = q->count = q->segment_pending = 0;
+
+	return (0);
+}
+
+int diva_data_q_get_max_length(const diva_um_idi_data_queue_t * q)
+{
+	return (q->max_length);
+}
+
+void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t * q)
+{
+	if ((!q->segment_pending) && (q->count < q->segments)) {
+		q->segment_pending = 1;
+		return (q->data[q->write]);
+	}
+
+	return NULL;
+}
+
+void
+diva_data_q_ack_segment4write(diva_um_idi_data_queue_t * q, int length)
+{
+	if (q->segment_pending) {
+		q->length[q->write] = length;
+		q->count++;
+		q->write++;
+		if (q->write >= q->segments) {
+			q->write = 0;
+		}
+		q->segment_pending = 0;
+	}
+}
+
+const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t *
+					 q)
+{
+	if (q->count) {
+		return (q->data[q->read]);
+	}
+	return NULL;
+}
+
+int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t * q)
+{
+	return (q->length[q->read]);
+}
+
+void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t * q)
+{
+	if (q->count) {
+		q->length[q->read] = 0;
+		q->count--;
+		q->read++;
+		if (q->read >= q->segments) {
+			q->read = 0;
+		}
+	}
+}
diff --git a/drivers/isdn/hardware/eicon/dqueue.h b/drivers/isdn/hardware/eicon/dqueue.h
new file mode 100644
index 0000000..72d21c9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dqueue.h
@@ -0,0 +1,31 @@
+/* $Id: dqueue.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
+
+#ifndef _DIVA_USER_MODE_IDI_DATA_QUEUE_H__
+#define _DIVA_USER_MODE_IDI_DATA_QUEUE_H__
+
+#define DIVA_UM_IDI_MAX_MSGS 64
+
+typedef struct _diva_um_idi_data_queue {
+	int segments;
+	int max_length;
+	int read;
+	int write;
+	int count;
+	int segment_pending;
+	void *data[DIVA_UM_IDI_MAX_MSGS];
+	int length[DIVA_UM_IDI_MAX_MSGS];
+} diva_um_idi_data_queue_t;
+
+int diva_data_q_init(diva_um_idi_data_queue_t * q,
+		     int max_length, int max_segments);
+int diva_data_q_finit(diva_um_idi_data_queue_t * q);
+int diva_data_q_get_max_length(const diva_um_idi_data_queue_t * q);
+void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t * q);
+void diva_data_q_ack_segment4write(diva_um_idi_data_queue_t * q,
+				   int length);
+const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t *
+					 q);
+int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t * q);
+void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t * q);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/dsp_defs.h b/drivers/isdn/hardware/eicon/dsp_defs.h
new file mode 100644
index 0000000..b44950e
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsp_defs.h
@@ -0,0 +1,304 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 DSP_DEFS_H_  
+#define DSP_DEFS_H_
+#include "dspdids.h"
+/*---------------------------------------------------------------------------*/
+#define dsp_download_reserve_space(fp,length)
+/*****************************************************************************/
+/*
+ * OS file access abstraction layer
+ *
+ * I/O functions returns -1 on error, 0 on EOF
+ */
+#define OS_SEEK_SET 0
+#define OS_SEEK_CUR 1
+#define OS_SEEK_END 2
+struct _OsFileHandle_;
+typedef long (  * OsFileIo)  (struct _OsFileHandle_    *handle,
+                                void                     *buffer,
+                                long                       size) ;
+typedef long (  * OsFileSeek)(struct _OsFileHandle_    *handle,
+                                long                       position,
+                                int                        mode) ;
+typedef long (  * OsCardLoad)(struct _OsFileHandle_    *handle,
+                                long                       length,
+                                void                         *   *addr) ;
+typedef struct _OsFileHandle_
+{ void       *sysFileDesc ;
+ unsigned long sysFileSize ;
+ OsFileIo      sysFileRead ;
+ OsFileSeek    sysFileSeek ;
+ void       *sysLoadDesc ;
+ OsCardLoad    sysCardLoad ;
+} OsFileHandle ;
+extern OsFileHandle *OsOpenFile (char *path_name) ;
+extern void          OsCloseFile (OsFileHandle *fp) ;
+/*****************************************************************************/
+#define DSP_TELINDUS_FILE "dspdload.bin"
+/* special DSP file for BRI cards for Qsig and CornetN because of missing memory */
+#define DSP_QSIG_TELINDUS_FILE "dspdqsig.bin"
+#define DSP_MDM_TELINDUS_FILE "dspdvmdm.bin"
+#define DSP_FAX_TELINDUS_FILE "dspdvfax.bin"
+#define DSP_DIRECTORY_ENTRIES 64
+#define DSP_MEMORY_TYPE_EXTERNAL_DM         0
+#define DSP_MEMORY_TYPE_EXTERNAL_PM         1
+#define DSP_MEMORY_TYPE_INTERNAL_DM         2
+#define DSP_MEMORY_TYPE_INTERNAL_PM         3
+#define DSP_DOWNLOAD_FLAG_BOOTABLE          0x0001
+#define DSP_DOWNLOAD_FLAG_2181              0x0002
+#define DSP_DOWNLOAD_FLAG_TIMECRITICAL      0x0004
+#define DSP_DOWNLOAD_FLAG_COMPAND           0x0008
+#define DSP_MEMORY_BLOCK_COUNT              16
+#define DSP_SEGMENT_PM_FLAG                 0x0001
+#define DSP_SEGMENT_SHARED_FLAG             0x0002
+#define DSP_SEGMENT_EXTERNAL_DM             DSP_MEMORY_TYPE_EXTERNAL_DM
+#define DSP_SEGMENT_EXTERNAL_PM             DSP_MEMORY_TYPE_EXTERNAL_PM
+#define DSP_SEGMENT_INTERNAL_DM             DSP_MEMORY_TYPE_INTERNAL_DM
+#define DSP_SEGMENT_INTERNAL_PM             DSP_MEMORY_TYPE_INTERNAL_PM
+#define DSP_SEGMENT_FIRST_RELOCATABLE       4
+#define DSP_DATA_BLOCK_PM_FLAG              0x0001
+#define DSP_DATA_BLOCK_DWORD_FLAG           0x0002
+#define DSP_DATA_BLOCK_RESOLVE_FLAG         0x0004
+#define DSP_RELOC_NONE                      0x00
+#define DSP_RELOC_SEGMENT_MASK              0x3f
+#define DSP_RELOC_TYPE_MASK                 0xc0
+#define DSP_RELOC_TYPE_0                    0x00  /* relocation of address in DM word / high part of PM word */
+#define DSP_RELOC_TYPE_1                    0x40  /* relocation of address in low part of PM data word */
+#define DSP_RELOC_TYPE_2                    0x80  /* relocation of address in standard command */
+#define DSP_RELOC_TYPE_3                    0xc0  /* relocation of address in call/jump on flag in */
+#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_COMBIFILE_FORMAT_VERSION_BCD    0x0100
+#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_FILE_FORMAT_VERSION_BCD         0x0100
+typedef struct tag_dsp_combifile_header
+{
+  char                  format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE];
+  word                  format_version_bcd;
+  word                  header_size;
+  word                  combifile_description_size;
+  word                  directory_entries;
+  word                  directory_size;
+  word                  download_count;
+  word                  usage_mask_size;
+} t_dsp_combifile_header;
+typedef struct tag_dsp_combifile_directory_entry
+{
+  word                  card_type_number;
+  word                  file_set_number;
+} t_dsp_combifile_directory_entry;
+typedef struct tag_dsp_file_header
+{
+  char                  format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE];
+  word                  format_version_bcd;
+  word                  download_id;
+  word                  download_flags;
+  word                  required_processing_power;
+  word                  interface_channel_count;
+  word                  header_size;
+  word                  download_description_size;
+  word                  memory_block_table_size;
+  word                  memory_block_count;
+  word                  segment_table_size;
+  word                  segment_count;
+  word                  symbol_table_size;
+  word                  symbol_count;
+  word                  total_data_size_dm;
+  word                  data_block_count_dm;
+  word                  total_data_size_pm;
+  word                  data_block_count_pm;
+} t_dsp_file_header;
+typedef struct tag_dsp_memory_block_desc
+{
+  word                  alias_memory_block;
+  word                  memory_type;
+  word                  address;
+  word                  size;             /* DSP words */
+} t_dsp_memory_block_desc;
+typedef struct tag_dsp_segment_desc
+{
+  word                  memory_block;
+  word                  attributes;
+  word                  base;
+  word                  size;
+  word                  alignment;        /* ==0 -> no other legal start address than base */
+} t_dsp_segment_desc;
+typedef struct tag_dsp_symbol_desc
+{
+  word                  symbol_id;
+  word                  segment;
+  word                  offset;
+  word                  size;             /* DSP words */
+} t_dsp_symbol_desc;
+typedef struct tag_dsp_data_block_header
+{
+  word                  attributes;
+  word                  segment;
+  word                  offset;
+  word                  size;             /* DSP words */
+} t_dsp_data_block_header;
+typedef struct tag_dsp_download_desc
+{
+  word                  download_id;
+  word                  download_flags;
+  word                  required_processing_power;
+  word                  interface_channel_count;
+  word                  excess_header_size;
+  word                  memory_block_count;
+  word                  segment_count;
+  word                  symbol_count;
+  word                  data_block_count_dm;
+  word                  data_block_count_pm;
+  byte   *            p_excess_header_data;
+  char   *            p_download_description;
+  t_dsp_memory_block_desc   *p_memory_block_table;
+  t_dsp_segment_desc   *p_segment_table;
+  t_dsp_symbol_desc   *p_symbol_table;
+  word   *            p_data_blocks_dm;
+  word   *            p_data_blocks_pm;
+} t_dsp_desc;
+typedef struct tag_dsp_portable_download_desc /* be sure to keep native alignment for MAESTRA's */
+{
+  word                  download_id;
+  word                  download_flags;
+  word                  required_processing_power;
+  word                  interface_channel_count;
+  word                  excess_header_size;
+  word                  memory_block_count;
+  word                  segment_count;
+  word                  symbol_count;
+  word                  data_block_count_dm;
+  word                  data_block_count_pm;
+  dword                 p_excess_header_data;
+  dword                 p_download_description;
+  dword                 p_memory_block_table;
+  dword                 p_segment_table;
+  dword                 p_symbol_table;
+  dword                 p_data_blocks_dm;
+  dword                 p_data_blocks_pm;
+} t_dsp_portable_desc;
+#define DSP_DOWNLOAD_INDEX_KERNEL               0
+#define DSP30TX_DOWNLOAD_INDEX_KERNEL           1
+#define DSP30RX_DOWNLOAD_INDEX_KERNEL           2
+#define DSP_MAX_DOWNLOAD_COUNT                  64
+#define DSP_DOWNLOAD_MAX_SEGMENTS         16
+#define DSP_UDATA_REQUEST_RECONFIGURE     0
+/*
+parameters:
+  <word> reconfigure delay (in 8kHz samples)
+  <word> reconfigure code
+  <byte> reconfigure hdlc preamble flags
+*/
+#define DSP_RECONFIGURE_TX_FLAG           0x8000
+#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG  0x4000
+#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG 0x2000
+#define DSP_RECONFIGURE_HDLC_FLAG         0x1000
+#define DSP_RECONFIGURE_SYNC_FLAG         0x0800
+#define DSP_RECONFIGURE_PROTOCOL_MASK     0x00ff
+#define DSP_RECONFIGURE_IDLE              0
+#define DSP_RECONFIGURE_V25               1
+#define DSP_RECONFIGURE_V21_CH2           2
+#define DSP_RECONFIGURE_V27_2400          3
+#define DSP_RECONFIGURE_V27_4800          4
+#define DSP_RECONFIGURE_V29_7200          5
+#define DSP_RECONFIGURE_V29_9600          6
+#define DSP_RECONFIGURE_V33_12000         7
+#define DSP_RECONFIGURE_V33_14400         8
+#define DSP_RECONFIGURE_V17_7200          9
+#define DSP_RECONFIGURE_V17_9600          10
+#define DSP_RECONFIGURE_V17_12000         11
+#define DSP_RECONFIGURE_V17_14400         12
+/*
+data indications if transparent framer
+  <byte> data 0
+  <byte> data 1
+  ...
+data indications if HDLC framer
+  <byte> data 0
+  <byte> data 1
+  ...
+  <byte> CRC 0
+  <byte> CRC 1
+  <byte> preamble flags
+*/
+#define DSP_UDATA_INDICATION_SYNC         0
+/*
+returns:
+  <word> time of sync (sampled from counter at 8kHz)
+*/
+#define DSP_UDATA_INDICATION_DCD_OFF      1
+/*
+returns:
+  <word> time of DCD off (sampled from counter at 8kHz)
+*/
+#define DSP_UDATA_INDICATION_DCD_ON       2
+/*
+returns:
+  <word> time of DCD on (sampled from counter at 8kHz)
+  <byte> connected norm
+  <word> connected options
+  <dword> connected speed (bit/s)
+*/
+#define DSP_UDATA_INDICATION_CTS_OFF      3
+/*
+returns:
+  <word> time of CTS off (sampled from counter at 8kHz)
+*/
+#define DSP_UDATA_INDICATION_CTS_ON       4
+/*
+returns:
+  <word> time of CTS on (sampled from counter at 8kHz)
+  <byte> connected norm
+  <word> connected options
+  <dword> connected speed (bit/s)
+*/
+#define DSP_CONNECTED_NORM_UNSPECIFIED      0
+#define DSP_CONNECTED_NORM_V21              1
+#define DSP_CONNECTED_NORM_V23              2
+#define DSP_CONNECTED_NORM_V22              3
+#define DSP_CONNECTED_NORM_V22_BIS          4
+#define DSP_CONNECTED_NORM_V32_BIS          5
+#define DSP_CONNECTED_NORM_V34              6
+#define DSP_CONNECTED_NORM_V8               7
+#define DSP_CONNECTED_NORM_BELL_212A        8
+#define DSP_CONNECTED_NORM_BELL_103         9
+#define DSP_CONNECTED_NORM_V29_LEASED_LINE  10
+#define DSP_CONNECTED_NORM_V33_LEASED_LINE  11
+#define DSP_CONNECTED_NORM_TFAST            12
+#define DSP_CONNECTED_NORM_V21_CH2          13
+#define DSP_CONNECTED_NORM_V27_TER          14
+#define DSP_CONNECTED_NORM_V29              15
+#define DSP_CONNECTED_NORM_V33              16
+#define DSP_CONNECTED_NORM_V17              17
+#define DSP_CONNECTED_OPTION_TRELLIS        0x0001
+/*---------------------------------------------------------------------------*/
+extern char *dsp_read_file (OsFileHandle          *fp,
+                            word                     card_type_number,
+                            word                  *p_dsp_download_count,
+                            t_dsp_desc            *p_dsp_download_table,
+                            t_dsp_portable_desc   *p_dsp_portable_download_table) ;
+/*---------------------------------------------------------------------------*/
+#endif /* DSP_DEFS_H_ */  
diff --git a/drivers/isdn/hardware/eicon/dsp_tst.h b/drivers/isdn/hardware/eicon/dsp_tst.h
new file mode 100644
index 0000000..a6021e5b
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsp_tst.h
@@ -0,0 +1,47 @@
+/* $Id: dsp_tst.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
+
+#ifndef __DIVA_PRI_HOST_TEST_DSPS_H__
+#define __DIVA_PRI_HOST_TEST_DSPS_H__
+
+/*
+   DSP registers on maestra pri
+   */
+#define DSP1_PORT       (0x00)
+#define DSP2_PORT       (0x8)
+#define DSP3_PORT       (0x800)
+#define DSP4_PORT       (0x808)
+#define DSP5_PORT       (0x810)
+#define DSP6_PORT       (0x818)
+#define DSP7_PORT       (0x820)
+#define DSP8_PORT       (0x828)
+#define DSP9_PORT       (0x830)
+#define DSP10_PORT      (0x840)
+#define DSP11_PORT      (0x848)
+#define DSP12_PORT      (0x850)
+#define DSP13_PORT      (0x858)
+#define DSP14_PORT      (0x860)
+#define DSP15_PORT      (0x868)
+#define DSP16_PORT      (0x870)
+#define DSP17_PORT      (0x1000)
+#define DSP18_PORT      (0x1008)
+#define DSP19_PORT      (0x1010)
+#define DSP20_PORT      (0x1018)
+#define DSP21_PORT      (0x1020)
+#define DSP22_PORT      (0x1028)
+#define DSP23_PORT      (0x1030)
+#define DSP24_PORT      (0x1040)
+#define DSP25_PORT      (0x1048)
+#define DSP26_PORT      (0x1050)
+#define DSP27_PORT      (0x1058)
+#define DSP28_PORT      (0x1060)
+#define DSP29_PORT      (0x1068)
+#define DSP30_PORT      (0x1070)
+#define DSP_ADR_OFFS    0x80
+
+/*------------------------------------------------------------------
+		Dsp related definitions
+  ------------------------------------------------------------------ */
+#define DSP_SIGNATURE_PROBE_WORD 0x5a5a
+#define dsp_make_address_ex(pm,address) ((word)((pm) ? (address) : (address) + 0x4000))
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/dspdids.h b/drivers/isdn/hardware/eicon/dspdids.h
new file mode 100644
index 0000000..ebe131a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dspdids.h
@@ -0,0 +1,75 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 DSPDIDS_H_
+#define DSPDIDS_H_
+/*---------------------------------------------------------------------------*/
+#define DSP_DID_INVALID   0
+#define DSP_DID_DIVA   1
+#define DSP_DID_DIVA_PRO  2
+#define DSP_DID_DIVA_PRO_20  3
+#define DSP_DID_DIVA_PRO_PCCARD  4
+#define DSP_DID_DIVA_SERVER_BRI_1M 5
+#define DSP_DID_DIVA_SERVER_BRI_2M 6
+#define DSP_DID_DIVA_SERVER_PRI_2M_TX 7
+#define DSP_DID_DIVA_SERVER_PRI_2M_RX 8
+#define DSP_DID_DIVA_SERVER_PRI_30M 9
+#define DSP_DID_TASK_HSCX  100
+#define DSP_DID_TASK_HSCX_PRI_2M_TX 101
+#define DSP_DID_TASK_HSCX_PRI_2M_RX 102
+#define DSP_DID_TASK_V110KRNL  200
+#define DSP_DID_OVERLAY_V1100  201
+#define DSP_DID_OVERLAY_V1101  202
+#define DSP_DID_OVERLAY_V1102  203
+#define DSP_DID_OVERLAY_V1103  204
+#define DSP_DID_OVERLAY_V1104  205
+#define DSP_DID_OVERLAY_V1105  206
+#define DSP_DID_OVERLAY_V1106  207
+#define DSP_DID_OVERLAY_V1107  208
+#define DSP_DID_OVERLAY_V1108  209
+#define DSP_DID_OVERLAY_V1109  210
+#define DSP_DID_TASK_V110_PRI_2M_TX 220
+#define DSP_DID_TASK_V110_PRI_2M_RX 221
+#define DSP_DID_TASK_MODEM  300
+#define DSP_DID_TASK_FAX05  400
+#define DSP_DID_TASK_VOICE  500
+#define DSP_DID_TASK_TIKRNL81  600
+#define DSP_DID_OVERLAY_DIAL  601
+#define DSP_DID_OVERLAY_V22  602
+#define DSP_DID_OVERLAY_V32  603
+#define DSP_DID_OVERLAY_FSK  604
+#define DSP_DID_OVERLAY_FAX  605
+#define DSP_DID_OVERLAY_VXX  606
+#define DSP_DID_OVERLAY_V8  607
+#define DSP_DID_OVERLAY_INFO  608
+#define DSP_DID_OVERLAY_V34  609
+#define DSP_DID_OVERLAY_DFX  610
+#define DSP_DID_PARTIAL_OVERLAY_DIAL 611
+#define DSP_DID_PARTIAL_OVERLAY_FSK 612
+#define DSP_DID_PARTIAL_OVERLAY_FAX 613
+#define DSP_DID_TASK_TIKRNL05  700
+/*---------------------------------------------------------------------------*/
+#endif
+/*---------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/dsrv4bri.h b/drivers/isdn/hardware/eicon/dsrv4bri.h
new file mode 100644
index 0000000..732d22d
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsrv4bri.h
@@ -0,0 +1,40 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_XDI_DSRV_4_BRI_INC__
+#define __DIVA_XDI_DSRV_4_BRI_INC__
+/*
+ * Some special registers in the PLX 9054
+ */
+#define PLX9054_P2LDBELL    0x60
+#define PLX9054_L2PDBELL    0x64
+#define PLX9054_INTCSR      0x69
+#define PLX9054_INT_ENABLE  0x09
+#define PLX9054_SOFT_RESET 0x4000
+#define PLX9054_RELOAD_EEPROM 0x2000
+#define DIVA_4BRI_REVISION(__x__) (((__x__)->cardType == CARDTYPE_DIVASRV_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2F_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI))
+void diva_os_set_qBri_functions (PISDN_ADAPTER IoAdapter);
+void diva_os_set_qBri2_functions (PISDN_ADAPTER IoAdapter);
+#endif
diff --git a/drivers/isdn/hardware/eicon/dsrv_bri.h b/drivers/isdn/hardware/eicon/dsrv_bri.h
new file mode 100644
index 0000000..f38ebbe
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsrv_bri.h
@@ -0,0 +1,37 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_XDI_DSRV_BRI_INC__
+#define __DIVA_XDI_DSRV_BRI_INC__
+/*
+ Functions exported from os dependent part of
+ BRI card configuration and used in
+ OS independed part
+ */
+/*
+ Prepare OS dependent part of BRI functions
+ */
+void diva_os_prepare_maestra_functions (PISDN_ADAPTER IoAdapter);
+#endif
diff --git a/drivers/isdn/hardware/eicon/dsrv_pri.h b/drivers/isdn/hardware/eicon/dsrv_pri.h
new file mode 100644
index 0000000..8611826
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsrv_pri.h
@@ -0,0 +1,38 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_XDI_DSRV_PRI_INC__
+#define __DIVA_XDI_DSRV_PRI_INC__
+/*
+ Functions exported from os dependent part of
+ PRI card configuration and used in
+ OS independed part
+ */
+/*
+ Prepare OS dependent part of PRI/PRI Rev.2 functions
+ */
+void diva_os_prepare_pri_functions (PISDN_ADAPTER IoAdapter);
+void diva_os_prepare_pri2_functions (PISDN_ADAPTER IoAdapter);
+#endif
diff --git a/drivers/isdn/hardware/eicon/entity.h b/drivers/isdn/hardware/eicon/entity.h
new file mode 100644
index 0000000..16252cf
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/entity.h
@@ -0,0 +1,28 @@
+/* $Id: entity.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVAS_USER_MODE_IDI_ENTITY__
+#define __DIVAS_USER_MODE_IDI_ENTITY__
+
+#define DIVA_UM_IDI_RC_PENDING      0x00000001
+#define DIVA_UM_IDI_REMOVE_PENDING  0x00000002
+#define DIVA_UM_IDI_TX_FLOW_CONTROL 0x00000004
+#define DIVA_UM_IDI_REMOVED         0x00000008
+#define DIVA_UM_IDI_ASSIGN_PENDING  0x00000010
+
+typedef struct _divas_um_idi_entity {
+	struct list_head          link;
+	diva_um_idi_adapter_t*    adapter; /* Back to adapter */
+	ENTITY                    e;
+	void*                     os_ref;
+	dword                     status;
+	void*                     os_context;
+	int                       rc_count;
+	diva_um_idi_data_queue_t  data; /* definad by user 1 ... MAX */
+	diva_um_idi_data_queue_t  rc;   /* two entries */
+	BUFFERS                   XData;
+	BUFFERS                   RData;
+	byte                      buffer[2048+512];
+} divas_um_idi_entity_t;
+
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/helpers.h b/drivers/isdn/hardware/eicon/helpers.h
new file mode 100644
index 0000000..b212311
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/helpers.h
@@ -0,0 +1,51 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_XDI_CARD_CONFIG_HELPERS_INC__
+#define __DIVA_XDI_CARD_CONFIG_HELPERS_INC__
+dword diva_get_protocol_file_features  (byte* File,
+                      int offset,
+                      char *IdStringBuffer,
+                      dword IdBufferSize);
+void diva_configure_protocol (PISDN_ADAPTER IoAdapter);
+/*
+ Low level file access system abstraction
+ */
+/* -------------------------------------------------------------------------
+  Access to single file
+  Return pointer to the image of the requested file,
+  write image length to 'FileLength'
+  ------------------------------------------------------------------------- */
+void *xdiLoadFile (char *FileName, dword *FileLength, unsigned long MaxLoadSize) ;
+/* -------------------------------------------------------------------------
+  Dependent on the protocol settings does read return pointer
+  to the image of appropriate protocol file
+  ------------------------------------------------------------------------- */
+void *xdiLoadArchive (PISDN_ADAPTER IoAdapter, dword *FileLength, unsigned long MaxLoadSize) ;
+/* --------------------------------------------------------------------------
+  Free all system resources accessed by xdiLoadFile and xdiLoadArchive
+  -------------------------------------------------------------------------- */
+void xdiFreeFile (void* handle);
+#endif
diff --git a/drivers/isdn/hardware/eicon/idifunc.c b/drivers/isdn/hardware/eicon/idifunc.c
new file mode 100644
index 0000000..4cbc68c
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/idifunc.c
@@ -0,0 +1,267 @@
+/* $Id: idifunc.c,v 1.14.4.4 2004/08/28 20:03:53 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * User Mode IDI Interface 
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "um_xdi.h"
+#include "um_idi.h"
+
+#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+extern char *DRIVERRELEASE_IDI;
+
+extern void DIVA_DIDD_Read(void *, int);
+extern int diva_user_mode_idi_create_adapter(const DESCRIPTOR *, int);
+extern void diva_user_mode_idi_remove_adapter(int);
+
+static dword notify_handle;
+static DESCRIPTOR DAdapter;
+static DESCRIPTOR MAdapter;
+
+static void no_printf(unsigned char *x, ...)
+{
+	/* dummy debug function */
+}
+
+#include "debuglib.c"
+
+/*
+ * stop debug
+ */
+static void stop_dbg(void)
+{
+	DbgDeregister();
+	memset(&MAdapter, 0, sizeof(MAdapter));
+	dprintf = no_printf;
+}
+
+typedef struct _udiva_card {
+	struct list_head list;
+	int Id;
+	DESCRIPTOR d;
+} udiva_card;
+
+static LIST_HEAD(cards);
+static diva_os_spin_lock_t ll_lock;
+
+/*
+ * find card in list
+ */
+static udiva_card *find_card_in_list(DESCRIPTOR * d)
+{
+	udiva_card *card;
+	struct list_head *tmp;
+	diva_os_spin_lock_magic_t old_irql;
+
+	diva_os_enter_spin_lock(&ll_lock, &old_irql, "find card");
+	list_for_each(tmp, &cards) {
+		card = list_entry(tmp, udiva_card, list);
+		if (card->d.request == d->request) {
+			diva_os_leave_spin_lock(&ll_lock, &old_irql,
+						"find card");
+			return (card);
+		}
+	}
+	diva_os_leave_spin_lock(&ll_lock, &old_irql, "find card");
+	return ((udiva_card *) NULL);
+}
+
+/*
+ * new card
+ */
+static void um_new_card(DESCRIPTOR * d)
+{
+	int adapter_nr = 0;
+	udiva_card *card = NULL;
+	IDI_SYNC_REQ sync_req;
+	diva_os_spin_lock_magic_t old_irql;
+
+	if (!(card = diva_os_malloc(0, sizeof(udiva_card)))) {
+		DBG_ERR(("cannot get buffer for card"));
+		return;
+	}
+	memcpy(&card->d, d, sizeof(DESCRIPTOR));
+	sync_req.xdi_logical_adapter_number.Req = 0;
+	sync_req.xdi_logical_adapter_number.Rc =
+	    IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER;
+	card->d.request((ENTITY *) & sync_req);
+	adapter_nr =
+	    sync_req.xdi_logical_adapter_number.info.logical_adapter_number;
+	card->Id = adapter_nr;
+	if (!(diva_user_mode_idi_create_adapter(d, adapter_nr))) {
+		diva_os_enter_spin_lock(&ll_lock, &old_irql, "add card");
+		list_add_tail(&card->list, &cards);
+		diva_os_leave_spin_lock(&ll_lock, &old_irql, "add card");
+	} else {
+		DBG_ERR(("could not create user mode idi card %d",
+			 adapter_nr));
+	}
+}
+
+/*
+ * remove card
+ */
+static void um_remove_card(DESCRIPTOR * d)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	udiva_card *card = NULL;
+
+	if (!(card = find_card_in_list(d))) {
+		DBG_ERR(("cannot find card to remove"));
+		return;
+	}
+	diva_user_mode_idi_remove_adapter(card->Id);
+	diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove card");
+	list_del(&card->list);
+	diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove card");
+	DBG_LOG(("idi proc entry removed for card %d", card->Id));
+	diva_os_free(0, card);
+}
+
+/*
+ * remove all adapter
+ */
+static void DIVA_EXIT_FUNCTION remove_all_idi_proc(void)
+{
+	udiva_card *card;
+	diva_os_spin_lock_magic_t old_irql;
+
+rescan:
+	diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove all");
+	if (!list_empty(&cards)) {
+		card = list_entry(cards.next, udiva_card, list);
+		list_del(&card->list);
+		diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
+		diva_user_mode_idi_remove_adapter(card->Id);
+		diva_os_free(0, card);
+		goto rescan;
+	}
+	diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
+}
+
+/*
+ * DIDD notify callback
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+			   int removal)
+{
+	if (adapter->type == IDI_DADAPTER) {
+		DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
+		return (NULL);
+	} else if (adapter->type == IDI_DIMAINT) {
+		if (removal) {
+			stop_dbg();
+		} else {
+			memcpy(&MAdapter, adapter, sizeof(MAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
+		}
+	} else if ((adapter->type > 0) && (adapter->type < 16)) {	/* IDI Adapter */
+		if (removal) {
+			um_remove_card(adapter);
+		} else {
+			um_new_card(adapter);
+		}
+	}
+	return (NULL);
+}
+
+/*
+ * connect DIDD
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+	int x = 0;
+	int dadapter = 0;
+	IDI_SYNC_REQ req;
+	DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+	DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DADAPTER) {	/* DADAPTER found */
+			dadapter = 1;
+			memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			req.didd_notify.e.Req = 0;
+			req.didd_notify.e.Rc =
+			    IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+			req.didd_notify.info.callback = (void *)didd_callback;
+			req.didd_notify.info.context = NULL;
+			DAdapter.request((ENTITY *) & req);
+			if (req.didd_notify.e.Rc != 0xff) {
+				stop_dbg();
+				return (0);
+			}
+			notify_handle = req.didd_notify.info.handle;
+		} else if (DIDD_Table[x].type == IDI_DIMAINT) {	/* MAINT found */
+			memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
+		} else if ((DIDD_Table[x].type > 0)
+			   && (DIDD_Table[x].type < 16)) {	/* IDI Adapter found */
+			um_new_card(&DIDD_Table[x]);
+		}
+	}
+
+	if (!dadapter) {
+		stop_dbg();
+	}
+
+	return (dadapter);
+}
+
+/*
+ *  Disconnect from DIDD
+ */
+static void DIVA_EXIT_FUNCTION disconnect_didd(void)
+{
+	IDI_SYNC_REQ req;
+
+	stop_dbg();
+
+	req.didd_notify.e.Req = 0;
+	req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+	req.didd_notify.info.handle = notify_handle;
+	DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION idifunc_init(void)
+{
+	diva_os_initialize_spin_lock(&ll_lock, "idifunc");
+
+	if (diva_user_mode_idi_init()) {
+		DBG_ERR(("init: init failed."));
+		return (0);
+	}
+
+	if (!connect_didd()) {
+		diva_user_mode_idi_finit();
+		DBG_ERR(("init: failed to connect to DIDD."));
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * finit
+ */
+void DIVA_EXIT_FUNCTION idifunc_finit(void)
+{
+	diva_user_mode_idi_finit();
+	disconnect_didd();
+	remove_all_idi_proc();
+}
diff --git a/drivers/isdn/hardware/eicon/io.c b/drivers/isdn/hardware/eicon/io.c
new file mode 100644
index 0000000..4a27e23
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/io.c
@@ -0,0 +1,852 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "divasync.h"
+#define MIPS_SCOM
+#include "pkmaint.h" /* pc_main.h, packed in os-dependent fashion */
+#include "di.h"
+#include "mi_pc.h"
+#include "io.h"
+extern ADAPTER * adapter[MAX_ADAPTER];
+extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+void request (PISDN_ADAPTER, ENTITY *);
+static void pcm_req (PISDN_ADAPTER, ENTITY *);
+/* --------------------------------------------------------------------------
+  local functions
+  -------------------------------------------------------------------------- */
+#define ReqFunc(N) \
+static void Request##N(ENTITY *e) \
+{ if ( IoAdapters[N] ) (* IoAdapters[N]->DIRequest)(IoAdapters[N], e) ; }
+ReqFunc(0)
+ReqFunc(1)
+ReqFunc(2)
+ReqFunc(3)
+ReqFunc(4)
+ReqFunc(5)
+ReqFunc(6)
+ReqFunc(7)
+ReqFunc(8)
+ReqFunc(9)
+ReqFunc(10)
+ReqFunc(11)
+ReqFunc(12)
+ReqFunc(13)
+ReqFunc(14)
+ReqFunc(15)
+IDI_CALL Requests[MAX_ADAPTER] =
+{ &Request0, &Request1, &Request2, &Request3,
+ &Request4, &Request5, &Request6, &Request7,
+ &Request8, &Request9, &Request10, &Request11,
+ &Request12, &Request13, &Request14, &Request15
+};
+/*****************************************************************************/
+/*
+  This array should indicate all new services, that this version of XDI
+  is able to provide to his clients
+  */
+static byte extended_xdi_features[DIVA_XDI_EXTENDED_FEATURES_MAX_SZ+1] = {
+ (DIVA_XDI_EXTENDED_FEATURES_VALID       |
+  DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR    |
+  DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS    |
+#if defined(DIVA_IDI_RX_DMA)
+  DIVA_XDI_EXTENDED_FEATURE_CMA          |
+  DIVA_XDI_EXTENDED_FEATURE_RX_DMA       |
+  DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA |
+#endif
+  DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC),
+ 0
+};
+/*****************************************************************************/
+void
+dump_xlog_buffer (PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc)
+{
+ dword   logLen ;
+ word *Xlog   = xlogDesc->buf ;
+ word  logCnt = xlogDesc->cnt ;
+ word  logOut = xlogDesc->out / sizeof(*Xlog) ;
+ DBG_FTL(("%s: ************* XLOG recovery (%d) *************",
+          &IoAdapter->Name[0], (int)logCnt))
+ DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0]))
+ for ( ; logCnt > 0 ; --logCnt )
+ {
+  if ( !GET_WORD(&Xlog[logOut]) )
+  {
+   if ( --logCnt == 0 )
+    break ;
+   logOut = 0 ;
+  }
+  if ( GET_WORD(&Xlog[logOut]) <= (logOut * sizeof(*Xlog)) )
+  {
+   if ( logCnt > 2 )
+   {
+    DBG_FTL(("Possibly corrupted XLOG: %d entries left",
+             (int)logCnt))
+   }
+   break ;
+  }
+  logLen = (dword)(GET_WORD(&Xlog[logOut]) - (logOut * sizeof(*Xlog))) ;
+  DBG_FTL_MXLOG(( (char *)&Xlog[logOut + 1], (dword)(logLen - 2) ))
+  logOut = (GET_WORD(&Xlog[logOut]) + 1) / sizeof(*Xlog) ;
+ }
+ DBG_FTL(("%s: ***************** end of XLOG *****************",
+          &IoAdapter->Name[0]))
+}
+/*****************************************************************************/
+#if defined(XDI_USE_XLOG)
+static char *(ExceptionCauseTable[]) =
+{
+ "Interrupt",
+ "TLB mod /IBOUND",
+ "TLB load /DBOUND",
+ "TLB store",
+ "Address error load",
+ "Address error store",
+ "Instruction load bus error",
+ "Data load/store bus error",
+ "Syscall",
+ "Breakpoint",
+ "Reverd instruction",
+ "Coprocessor unusable",
+ "Overflow",
+ "TRAP",
+ "VCEI",
+ "Floating Point Exception",
+ "CP2",
+ "Reserved 17",
+ "Reserved 18",
+ "Reserved 19",
+ "Reserved 20",
+ "Reserved 21",
+ "Reserved 22",
+ "WATCH",
+ "Reserved 24",
+ "Reserved 25",
+ "Reserved 26",
+ "Reserved 27",
+ "Reserved 28",
+ "Reserved 29",
+ "Reserved 30",
+ "VCED"
+} ;
+#endif
+void
+dump_trap_frame (PISDN_ADAPTER IoAdapter, byte __iomem *exceptionFrame)
+{
+ MP_XCPTC __iomem *xcept = (MP_XCPTC __iomem *)exceptionFrame ;
+ dword    __iomem *regs;
+ regs  = &xcept->regs[0] ;
+ DBG_FTL(("%s: ***************** CPU TRAPPED *****************",
+          &IoAdapter->Name[0]))
+ DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0]))
+ DBG_FTL(("Cause: %s",
+          ExceptionCauseTable[(READ_DWORD(&xcept->cr) & 0x0000007c) >> 2]))
+ DBG_FTL(("sr    0x%08x cr    0x%08x epc   0x%08x vaddr 0x%08x",
+          READ_DWORD(&xcept->sr), READ_DWORD(&xcept->cr),
+					READ_DWORD(&xcept->epc), READ_DWORD(&xcept->vaddr)))
+ DBG_FTL(("zero  0x%08x at    0x%08x v0    0x%08x v1    0x%08x",
+          READ_DWORD(&regs[ 0]), READ_DWORD(&regs[ 1]),
+					READ_DWORD(&regs[ 2]), READ_DWORD(&regs[ 3])))
+ DBG_FTL(("a0    0x%08x a1    0x%08x a2    0x%08x a3    0x%08x",
+          READ_DWORD(&regs[ 4]), READ_DWORD(&regs[ 5]),
+					READ_DWORD(&regs[ 6]), READ_DWORD(&regs[ 7])))
+ DBG_FTL(("t0    0x%08x t1    0x%08x t2    0x%08x t3    0x%08x",
+          READ_DWORD(&regs[ 8]), READ_DWORD(&regs[ 9]),
+					READ_DWORD(&regs[10]), READ_DWORD(&regs[11])))
+ DBG_FTL(("t4    0x%08x t5    0x%08x t6    0x%08x t7    0x%08x",
+          READ_DWORD(&regs[12]), READ_DWORD(&regs[13]),
+					READ_DWORD(&regs[14]), READ_DWORD(&regs[15])))
+ DBG_FTL(("s0    0x%08x s1    0x%08x s2    0x%08x s3    0x%08x",
+          READ_DWORD(&regs[16]), READ_DWORD(&regs[17]),
+					READ_DWORD(&regs[18]), READ_DWORD(&regs[19])))
+ DBG_FTL(("s4    0x%08x s5    0x%08x s6    0x%08x s7    0x%08x",
+          READ_DWORD(&regs[20]), READ_DWORD(&regs[21]),
+					READ_DWORD(&regs[22]), READ_DWORD(&regs[23])))
+ DBG_FTL(("t8    0x%08x t9    0x%08x k0    0x%08x k1    0x%08x",
+          READ_DWORD(&regs[24]), READ_DWORD(&regs[25]),
+					READ_DWORD(&regs[26]), READ_DWORD(&regs[27])))
+ DBG_FTL(("gp    0x%08x sp    0x%08x s8    0x%08x ra    0x%08x",
+          READ_DWORD(&regs[28]), READ_DWORD(&regs[29]),
+					READ_DWORD(&regs[30]), READ_DWORD(&regs[31])))
+ DBG_FTL(("md    0x%08x|%08x         resvd 0x%08x class 0x%08x",
+          READ_DWORD(&xcept->mdhi), READ_DWORD(&xcept->mdlo),
+					READ_DWORD(&xcept->reseverd), READ_DWORD(&xcept->xclass)))
+}
+/* --------------------------------------------------------------------------
+  Real XDI Request function
+  -------------------------------------------------------------------------- */
+void request(PISDN_ADAPTER IoAdapter, ENTITY * e)
+{
+ byte i;
+ diva_os_spin_lock_magic_t irql;
+/*
+ * if the Req field in the entity structure is 0,
+ * we treat this request as a special function call
+ */
+ if ( !e->Req )
+ {
+  IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e ;
+  switch (e->Rc)
+  {
+#if defined(DIVA_IDI_RX_DMA)
+    case IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION: {
+      diva_xdi_dma_descriptor_operation_t* pI = \
+                                   &syncReq->xdi_dma_descriptor_operation.info;
+      if (!IoAdapter->dma_map) {
+        pI->operation         = -1;
+        pI->descriptor_number = -1;
+        return;
+      }
+      diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "dma_op");
+      if (pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC) {
+        pI->descriptor_number = diva_alloc_dma_map_entry (\
+                               (struct _diva_dma_map_entry*)IoAdapter->dma_map);
+        if (pI->descriptor_number >= 0) {
+          dword dma_magic;
+          void* local_addr;
+          diva_get_dma_map_entry (\
+                               (struct _diva_dma_map_entry*)IoAdapter->dma_map,
+                               pI->descriptor_number,
+                               &local_addr, &dma_magic);
+          pI->descriptor_address  = local_addr;
+          pI->descriptor_magic    = dma_magic;
+          pI->operation           = 0;
+        } else {
+          pI->operation           = -1;
+        }
+      } else if ((pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE) &&
+                 (pI->descriptor_number >= 0)) {
+        diva_free_dma_map_entry((struct _diva_dma_map_entry*)IoAdapter->dma_map,
+                                pI->descriptor_number);
+        pI->descriptor_number = -1;
+        pI->operation         = 0;
+      } else {
+        pI->descriptor_number = -1;
+        pI->operation         = -1;
+      }
+      diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "dma_op");
+    } return;
+#endif
+    case IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER: {
+      diva_xdi_get_logical_adapter_number_s_t *pI = \
+                                     &syncReq->xdi_logical_adapter_number.info;
+      pI->logical_adapter_number = IoAdapter->ANum;
+      pI->controller = IoAdapter->ControllerNumber;
+      pI->total_controllers = IoAdapter->Properties.Adapters;
+    } return;
+    case IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS: {
+       diva_xdi_get_capi_parameters_t prms, *pI = &syncReq->xdi_capi_prms.info;
+       memset (&prms, 0x00, sizeof(prms));
+       prms.structure_length = MIN(sizeof(prms), pI->structure_length);
+       memset (pI, 0x00, pI->structure_length);
+       prms.flag_dynamic_l1_down    = (IoAdapter->capi_cfg.cfg_1 & \
+         DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? 1 : 0;
+       prms.group_optimization_enabled = (IoAdapter->capi_cfg.cfg_1 & \
+         DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON) ? 1 : 0;
+       memcpy (pI, &prms, prms.structure_length);
+      } return;
+    case IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR:
+      syncReq->xdi_sdram_bar.info.bar = IoAdapter->sdram_bar;
+      return;
+    case IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES: {
+      dword i;
+      diva_xdi_get_extended_xdi_features_t* pI =\
+                                 &syncReq->xdi_extended_features.info;
+      pI->buffer_length_in_bytes &= ~0x80000000;
+      if (pI->buffer_length_in_bytes && pI->features) {
+        memset (pI->features, 0x00, pI->buffer_length_in_bytes);
+      }
+      for (i = 0; ((pI->features) && (i < pI->buffer_length_in_bytes) &&
+                   (i < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ)); i++) {
+        pI->features[i] = extended_xdi_features[i];
+      }
+      if ((pI->buffer_length_in_bytes < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ) ||
+          (!pI->features)) {
+        pI->buffer_length_in_bytes =\
+                           (0x80000000 | DIVA_XDI_EXTENDED_FEATURES_MAX_SZ);
+      }
+     } return;
+    case IDI_SYNC_REQ_XDI_GET_STREAM:
+      if (IoAdapter) {
+        diva_xdi_provide_istream_info (&IoAdapter->a,
+                                       &syncReq->xdi_stream_info.info);
+      } else {
+        syncReq->xdi_stream_info.info.provided_service = 0;
+      }
+      return;
+  case IDI_SYNC_REQ_GET_NAME:
+   if ( IoAdapter )
+   {
+    strcpy (&syncReq->GetName.name[0], IoAdapter->Name) ;
+    DBG_TRC(("xdi: Adapter %d / Name '%s'",
+             IoAdapter->ANum, IoAdapter->Name))
+    return ;
+   }
+   syncReq->GetName.name[0] = '\0' ;
+   break ;
+  case IDI_SYNC_REQ_GET_SERIAL:
+   if ( IoAdapter )
+   {
+    syncReq->GetSerial.serial = IoAdapter->serialNo ;
+    DBG_TRC(("xdi: Adapter %d / SerialNo %ld",
+             IoAdapter->ANum, IoAdapter->serialNo))
+    return ;
+   }
+   syncReq->GetSerial.serial = 0 ;
+   break ;
+  case IDI_SYNC_REQ_GET_CARDTYPE:
+   if ( IoAdapter )
+   {
+    syncReq->GetCardType.cardtype = IoAdapter->cardType ;
+    DBG_TRC(("xdi: Adapter %d / CardType %ld",
+             IoAdapter->ANum, IoAdapter->cardType))
+    return ;
+   }
+   syncReq->GetCardType.cardtype = 0 ;
+   break ;
+  case IDI_SYNC_REQ_GET_XLOG:
+   if ( IoAdapter )
+   {
+    pcm_req (IoAdapter, e) ;
+    return ;
+   }
+   e->Ind = 0 ;
+   break ;
+  case IDI_SYNC_REQ_GET_DBG_XLOG:
+   if ( IoAdapter )
+   {
+    pcm_req (IoAdapter, e) ;
+    return ;
+   }
+   e->Ind = 0 ;
+   break ;
+  case IDI_SYNC_REQ_GET_FEATURES:
+   if ( IoAdapter )
+   {
+    syncReq->GetFeatures.features =
+      (unsigned short)IoAdapter->features ;
+    return ;
+   }
+   syncReq->GetFeatures.features = 0 ;
+   break ;
+        case IDI_SYNC_REQ_PORTDRV_HOOK:
+            if ( IoAdapter )
+            {
+                DBG_TRC(("Xdi:IDI_SYNC_REQ_PORTDRV_HOOK - ignored"))
+                return ;
+            }
+            break;
+  }
+  if ( IoAdapter )
+  {
+   return ;
+  }
+ }
+ DBG_TRC(("xdi: Id 0x%x / Req 0x%x / Rc 0x%x", e->Id, e->Req, e->Rc))
+ if ( !IoAdapter )
+ {
+  DBG_FTL(("xdi: uninitialized Adapter used - ignore request"))
+  return ;
+ }
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+/*
+ * assign an entity
+ */
+ if ( !(e->Id &0x1f) )
+ {
+  if ( IoAdapter->e_count >= IoAdapter->e_max )
+  {
+   DBG_FTL(("xdi: all Ids in use (max=%d) --> Req ignored",
+            IoAdapter->e_max))
+   diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+   return ;
+  }
+/*
+ * find a new free id
+ */
+  for ( i = 1 ; IoAdapter->e_tbl[i].e ; ++i ) ;
+  IoAdapter->e_tbl[i].e = e ;
+  IoAdapter->e_count++ ;
+  e->No = (byte)i ;
+  e->More = 0 ;
+  e->RCurrent = 0xff ;
+ }
+ else
+ {
+  i = e->No ;
+ }
+/*
+ * if the entity is still busy, ignore the request call
+ */
+ if ( e->More & XBUSY )
+ {
+  DBG_FTL(("xdi: Id 0x%x busy --> Req 0x%x ignored", e->Id, e->Req))
+  if ( !IoAdapter->trapped && IoAdapter->trapFnc )
+  {
+   IoAdapter->trapFnc (IoAdapter) ;
+      /*
+        Firs trap, also notify user if supported
+       */
+      if (IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) {
+        (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum);
+      }
+  }
+  diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+  return ;
+ }
+/*
+ * initialize transmit status variables
+ */
+ e->More |= XBUSY ;
+ e->More &= ~XMOREF ;
+ e->XCurrent = 0 ;
+ e->XOffset = 0 ;
+/*
+ * queue this entity in the adapter request queue
+ */
+ IoAdapter->e_tbl[i].next = 0 ;
+ if ( IoAdapter->head )
+ {
+  IoAdapter->e_tbl[IoAdapter->tail].next = i ;
+  IoAdapter->tail = i ;
+ }
+ else
+ {
+  IoAdapter->head = i ;
+  IoAdapter->tail = i ;
+ }
+/*
+ * queue the DPC to process the request
+ */
+ diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr);
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+}
+/* ---------------------------------------------------------------------
+  Main DPC routine
+   --------------------------------------------------------------------- */
+void DIDpcRoutine (struct _diva_os_soft_isr* psoft_isr, void* Context) {
+ PISDN_ADAPTER IoAdapter  = (PISDN_ADAPTER)Context ;
+ ADAPTER* a        = &IoAdapter->a ;
+ diva_os_atomic_t* pin_dpc = &IoAdapter->in_dpc;
+ if (diva_os_atomic_increment (pin_dpc) == 1) {
+  do {
+   if ( IoAdapter->tst_irq (a) )
+   {
+    if ( !IoAdapter->Unavailable )
+     IoAdapter->dpc (a) ;
+    IoAdapter->clr_irq (a) ;
+   }
+   IoAdapter->out (a) ;
+  } while (diva_os_atomic_decrement (pin_dpc) > 0);
+  /* ----------------------------------------------------------------
+    Look for XLOG request (cards with indirect addressing)
+    ---------------------------------------------------------------- */
+  if (IoAdapter->pcm_pending) {
+   struct pc_maint *pcm;
+   diva_os_spin_lock_magic_t OldIrql ;
+   diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+                &OldIrql,
+                "data_dpc");
+   pcm = (struct pc_maint *)IoAdapter->pcm_data;
+   switch (IoAdapter->pcm_pending) {
+    case 1: /* ask card for XLOG */
+     a->ram_out (a, &IoAdapter->pcm->rc, 0) ;
+     a->ram_out (a, &IoAdapter->pcm->req, pcm->req) ;
+     IoAdapter->pcm_pending = 2;
+     break;
+    case 2: /* Try to get XLOG from the card */
+     if ((int)(a->ram_in (a, &IoAdapter->pcm->rc))) {
+      a->ram_in_buffer (a, IoAdapter->pcm, pcm, sizeof(*pcm)) ;
+      IoAdapter->pcm_pending = 3;
+     }
+     break;
+    case 3: /* let XDI recovery XLOG */
+     break;
+   }
+   diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+                &OldIrql,
+                "data_dpc");
+  }
+  /* ---------------------------------------------------------------- */
+ }
+}
+/* --------------------------------------------------------------------------
+  XLOG interface
+  -------------------------------------------------------------------------- */
+static void
+pcm_req (PISDN_ADAPTER IoAdapter, ENTITY *e)
+{
+ diva_os_spin_lock_magic_t OldIrql ;
+ int              i, rc ;
+ ADAPTER         *a = &IoAdapter->a ;
+ struct pc_maint *pcm = (struct pc_maint *)&e->Ind ;
+/*
+ * special handling of I/O based card interface
+ * the memory access isn't an atomic operation !
+ */
+ if ( IoAdapter->Properties.Card == CARD_MAE )
+ {
+  diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+               &OldIrql,
+               "data_pcm_1");
+  IoAdapter->pcm_data = (void *)pcm;
+  IoAdapter->pcm_pending = 1;
+  diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr);
+  diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+               &OldIrql,
+               "data_pcm_1");
+  for ( rc = 0, i = (IoAdapter->trapped ? 3000 : 250) ; !rc && (i > 0) ; --i )
+  {
+   diva_os_sleep (1) ;
+   if (IoAdapter->pcm_pending == 3) {
+    diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+                 &OldIrql,
+                 "data_pcm_3");
+    IoAdapter->pcm_pending = 0;
+    IoAdapter->pcm_data    = NULL ;
+    diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+                 &OldIrql,
+                 "data_pcm_3");
+    return ;
+   }
+   diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+                &OldIrql,
+                "data_pcm_2");
+   diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr);
+   diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+                &OldIrql,
+                "data_pcm_2");
+  }
+  diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+               &OldIrql,
+               "data_pcm_4");
+  IoAdapter->pcm_pending = 0;
+  IoAdapter->pcm_data    = NULL ;
+  diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+               &OldIrql,
+               "data_pcm_4");
+  goto Trapped ;
+ }
+/*
+ * memory based shared ram is accessible from different
+ * processors without disturbing concurrent processes.
+ */
+ a->ram_out (a, &IoAdapter->pcm->rc, 0) ;
+ a->ram_out (a, &IoAdapter->pcm->req, pcm->req) ;
+ for ( i = (IoAdapter->trapped ? 3000 : 250) ; --i > 0 ; )
+ {
+  diva_os_sleep (1) ;
+  rc = (int)(a->ram_in (a, &IoAdapter->pcm->rc)) ;
+  if ( rc )
+  {
+   a->ram_in_buffer (a, IoAdapter->pcm, pcm, sizeof(*pcm)) ;
+   return ;
+  }
+ }
+Trapped:
+ if ( IoAdapter->trapFnc )
+ {
+    int trapped = IoAdapter->trapped;
+  IoAdapter->trapFnc (IoAdapter) ;
+    /*
+      Firs trap, also notify user if supported
+     */
+    if (!trapped && IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) {
+      (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum);
+    }
+ }
+}
+/*------------------------------------------------------------------*/
+/* ram access functions for memory mapped cards                     */
+/*------------------------------------------------------------------*/
+byte mem_in (ADAPTER *a, void *addr)
+{
+ byte val;
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ val = READ_BYTE(Base + (unsigned long)addr);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+ return (val);
+}
+word mem_inw (ADAPTER *a, void *addr)
+{
+ word val;
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ val = READ_WORD((Base + (unsigned long)addr));
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+ return (val);
+}
+void mem_in_dw (ADAPTER *a, void *addr, dword* data, int dwords)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ while (dwords--) {
+  *data++ = READ_DWORD((Base + (unsigned long)addr));
+  addr+=4;
+ }
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_in_buffer (ADAPTER *a, void *addr, void *buffer, word length)
+{
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ memcpy_fromio(buffer, (Base + (unsigned long)addr), length);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_look_ahead (ADAPTER *a, PBUFFER *RBuffer, ENTITY *e)
+{
+ PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io ;
+ IoAdapter->RBuffer.length = mem_inw (a, &RBuffer->length) ;
+ mem_in_buffer (a, RBuffer->P, IoAdapter->RBuffer.P,
+                IoAdapter->RBuffer.length) ;
+ e->RBuffer = (DBUFFER *)&IoAdapter->RBuffer ;
+}
+void mem_out (ADAPTER *a, void *addr, byte data)
+{
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ WRITE_BYTE(Base + (unsigned long)addr, data);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_outw (ADAPTER *a, void *addr, word data)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ WRITE_WORD((Base + (unsigned long)addr), data);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_out_dw (ADAPTER *a, void *addr, const dword* data, int dwords)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ while (dwords--) {
+	WRITE_DWORD((Base + (unsigned long)addr), *data);
+  	addr+=4;
+	data++;
+ }
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_out_buffer (ADAPTER *a, void *addr, void *buffer, word length)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ memcpy_toio((Base + (unsigned long)addr), buffer, length) ;
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_inc (ADAPTER *a, void *addr)
+{
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ byte  x = READ_BYTE(Base + (unsigned long)addr);
+ WRITE_BYTE(Base + (unsigned long)addr, x + 1);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+/*------------------------------------------------------------------*/
+/* ram access functions for io-mapped cards                         */
+/*------------------------------------------------------------------*/
+byte io_in(ADAPTER * a, void * adr)
+{
+  byte val;
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  outppw(Port + 4, (word)(unsigned long)adr);
+  val = inpp(Port);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+  return(val);
+}
+word io_inw(ADAPTER * a, void * adr)
+{
+  word val;
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  outppw(Port + 4, (word)(unsigned long)adr);
+  val = inppw(Port);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+  return(val);
+}
+void io_in_buffer(ADAPTER * a, void * adr, void * buffer, word len)
+{
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  byte* P = (byte*)buffer;
+  if ((long)adr & 1) {
+    outppw(Port+4, (word)(unsigned long)adr);
+    *P = inpp(Port);
+    P++;
+    adr = ((byte *) adr) + 1;
+    len--;
+    if (!len) {
+	DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+	return;
+    }
+  }
+  outppw(Port+4, (word)(unsigned long)adr);
+  inppw_buffer (Port, P, len+1);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_look_ahead(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e)
+{
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  outppw(Port+4, (word)(unsigned long)RBuffer);
+  ((PISDN_ADAPTER)a->io)->RBuffer.length = inppw(Port);
+  inppw_buffer (Port, ((PISDN_ADAPTER)a->io)->RBuffer.P, ((PISDN_ADAPTER)a->io)->RBuffer.length + 1);
+  e->RBuffer = (DBUFFER *) &(((PISDN_ADAPTER)a->io)->RBuffer);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_out(ADAPTER * a, void * adr, byte data)
+{
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  outppw(Port+4, (word)(unsigned long)adr);
+  outpp(Port, data);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_outw(ADAPTER * a, void * adr, word data)
+{
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  outppw(Port+4, (word)(unsigned long)adr);
+  outppw(Port, data);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_out_buffer(ADAPTER * a, void * adr, void * buffer, word len)
+{
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  byte* P = (byte*)buffer;
+  if ((long)adr & 1) {
+    outppw(Port+4, (word)(unsigned long)adr);
+    outpp(Port, *P);
+    P++;
+    adr = ((byte *) adr) + 1;
+    len--;
+    if (!len) {
+	DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+	return;
+    }
+  }
+  outppw(Port+4, (word)(unsigned long)adr);
+  outppw_buffer (Port, P, len+1);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_inc(ADAPTER * a, void * adr)
+{
+  byte x;
+  byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+  outppw(Port+4, (word)(unsigned long)adr);
+  x = inpp(Port);
+  outppw(Port+4, (word)(unsigned long)adr);
+  outpp(Port, x+1);
+  DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+/*------------------------------------------------------------------*/
+/* OS specific functions related to queuing of entities             */
+/*------------------------------------------------------------------*/
+void free_entity(ADAPTER * a, byte e_no)
+{
+  PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+  IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_free");
+  IoAdapter->e_tbl[e_no].e = NULL;
+  IoAdapter->e_count--;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_free");
+}
+void assign_queue(ADAPTER * a, byte e_no, word ref)
+{
+  PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+  IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_assign");
+  IoAdapter->e_tbl[e_no].assign_ref = ref;
+  IoAdapter->e_tbl[e_no].next = (byte)IoAdapter->assign;
+  IoAdapter->assign = e_no;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_assign");
+}
+byte get_assign(ADAPTER * a, word ref)
+{
+  PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+  byte e_no;
+  IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+              &irql,
+              "data_assign_get");
+  for(e_no = (byte)IoAdapter->assign;
+      e_no && IoAdapter->e_tbl[e_no].assign_ref!=ref;
+      e_no = IoAdapter->e_tbl[e_no].next);
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+              &irql,
+              "data_assign_get");
+  return e_no;
+}
+void req_queue(ADAPTER * a, byte e_no)
+{
+  PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+  IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_q");
+  IoAdapter->e_tbl[e_no].next = 0;
+  if(IoAdapter->head) {
+    IoAdapter->e_tbl[IoAdapter->tail].next = e_no;
+    IoAdapter->tail = e_no;
+  }
+  else {
+    IoAdapter->head = e_no;
+    IoAdapter->tail = e_no;
+  }
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_q");
+}
+byte look_req(ADAPTER * a)
+{
+  PISDN_ADAPTER IoAdapter;
+  IoAdapter = (PISDN_ADAPTER) a->io;
+  return ((byte)IoAdapter->head) ;
+}
+void next_req(ADAPTER * a)
+{
+  PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+  IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_next");
+  IoAdapter->head = IoAdapter->e_tbl[IoAdapter->head].next;
+  if(!IoAdapter->head) IoAdapter->tail = 0;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_next");
+}
+/*------------------------------------------------------------------*/
+/* memory map functions                                             */
+/*------------------------------------------------------------------*/
+ENTITY * entity_ptr(ADAPTER * a, byte e_no)
+{
+  PISDN_ADAPTER IoAdapter;
+  IoAdapter = (PISDN_ADAPTER) a->io;
+  return (IoAdapter->e_tbl[e_no].e);
+}
+void * PTR_X(ADAPTER * a, ENTITY * e)
+{
+  return ((void *) e->X);
+}
+void * PTR_R(ADAPTER * a, ENTITY * e)
+{
+  return ((void *) e->R);
+}
+void * PTR_P(ADAPTER * a, ENTITY * e, void * P)
+{
+  return P;
+}
+void CALLBACK(ADAPTER * a, ENTITY * e)
+{
+ if ( e && e->callback )
+  e->callback (e) ;
+}
diff --git a/drivers/isdn/hardware/eicon/io.h b/drivers/isdn/hardware/eicon/io.h
new file mode 100644
index 0000000..0c6c650
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/io.h
@@ -0,0 +1,308 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_XDI_COMMON_IO_H_INC__ /* { */
+#define __DIVA_XDI_COMMON_IO_H_INC__
+/*
+ maximum = 16 adapters
+ */
+#define DI_MAX_LINKS    MAX_ADAPTER
+#define ISDN_MAX_NUM_LEN 60
+/* --------------------------------------------------------------------------
+  structure for quadro card management (obsolete for
+  systems that do provide per card load event)
+  -------------------------------------------------------------------------- */
+typedef struct {
+ dword         Num ;
+ DEVICE_NAME   DeviceName[4] ;
+ PISDN_ADAPTER QuadroAdapter[4] ;
+} ADAPTER_LIST_ENTRY, *PADAPTER_LIST_ENTRY ;
+/* --------------------------------------------------------------------------
+  Special OS memory support structures
+  -------------------------------------------------------------------------- */
+#define MAX_MAPPED_ENTRIES 8
+typedef struct {
+ void  * Address;
+ dword    Length;
+} ADAPTER_MEMORY ;
+/* --------------------------------------------------------------------------
+  Configuration of XDI clients carried by XDI
+  -------------------------------------------------------------------------- */
+#define DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON      0x01
+#define DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON 0x02
+typedef struct _diva_xdi_capi_cfg {
+  byte cfg_1;
+} diva_xdi_capi_cfg_t;
+/* --------------------------------------------------------------------------
+  Main data structure kept per adapter
+  -------------------------------------------------------------------------- */
+struct _ISDN_ADAPTER {
+ void             (* DIRequest)(PISDN_ADAPTER, ENTITY *) ;
+ int                 State ; /* from NT4 1.srv, a good idea, but  a poor achievment */
+ int                 Initialized ;
+ int         RegisteredWithDidd ;
+ int                 Unavailable ;  /* callback function possible? */
+ int         ResourcesClaimed ;
+ int         PnpBiosConfigUsed ;
+ dword        Logging ;
+ dword        features ;
+ char        ProtocolIdString[80] ;
+ /*
+  remember mapped memory areas
+ */
+ ADAPTER_MEMORY     MappedMemory[MAX_MAPPED_ENTRIES] ;
+ CARD_PROPERTIES     Properties ;
+ dword               cardType ;
+ dword               protocol_id ;       /* configured protocol identifier */
+ char                protocol_name[8] ;  /* readable name of protocol */
+ dword               BusType ;
+ dword               BusNumber ;
+ dword               slotNumber ;
+ dword               slotId ;
+ dword               ControllerNumber ;  /* for QUADRO cards only */
+ PISDN_ADAPTER       MultiMaster ;       /* for 4-BRI card only - use MultiMaster or QuadroList */
+ PADAPTER_LIST_ENTRY QuadroList ;        /* for QUADRO card  only */
+ PDEVICE_OBJECT      DeviceObject ;
+ dword               DeviceId ;
+ diva_os_adapter_irq_info_t irq_info;
+ dword volatile      IrqCount ;
+ int                 trapped ;
+ dword               DspCodeBaseAddr ;
+ dword               MaxDspCodeSize ;
+ dword               downloadAddr ;
+ dword               DspCodeBaseAddrTable[4] ; /* add. for MultiMaster */
+ dword               MaxDspCodeSizeTable[4] ; /* add. for MultiMaster */
+ dword               downloadAddrTable[4] ; /* add. for MultiMaster */
+ dword               MemoryBase ;
+ dword               MemorySize ;
+ byte                __iomem *Address ;
+ byte                __iomem *Config ;
+ byte                __iomem *Control ;
+ byte                __iomem *reset ;
+ byte                __iomem *port ;
+ byte                __iomem *ram ;
+ byte                __iomem *cfg ;
+ byte                __iomem *prom ;
+ byte                __iomem *ctlReg ;
+ struct pc_maint  *pcm ;
+ diva_os_dependent_devica_name_t os_name;
+ byte                Name[32] ;
+ dword               serialNo ;
+ dword               ANum ;
+ dword               ArchiveType ; /* ARCHIVE_TYPE_NONE ..._SINGLE ..._USGEN ..._MULTI */
+ char               *ProtocolSuffix ; /* internal protocolfile table */
+ char                Archive[32] ;
+ char                Protocol[32] ;
+ char                AddDownload[32] ; /* Dsp- or other additional download files */
+ char                Oad1[ISDN_MAX_NUM_LEN] ;
+ char                Osa1[ISDN_MAX_NUM_LEN] ;
+ char                Oad2[ISDN_MAX_NUM_LEN] ;
+ char                Osa2[ISDN_MAX_NUM_LEN] ;
+ char                Spid1[ISDN_MAX_NUM_LEN] ;
+ char                Spid2[ISDN_MAX_NUM_LEN] ;
+  byte                nosig ;
+  byte                BriLayer2LinkCount ; /* amount of TEI's that adapter will support in P2MP mode */
+ dword               Channels ;
+ dword               tei ;
+ dword               nt2 ;
+ dword               TerminalCount ;
+ dword               WatchDog ;
+ dword               Permanent ;
+ dword               BChMask ; /* B channel mask for unchannelized modes */
+ dword               StableL2 ;
+ dword               DidLen ;
+ dword               NoOrderCheck ;
+ dword               ForceLaw; /* VoiceCoding - default:0, a-law: 1, my-law: 2 */
+ dword               SigFlags ;
+ dword               LowChannel ;
+ dword               NoHscx30 ;
+ dword               ProtVersion ;
+ dword               crc4 ;
+ dword               L1TristateOrQsig ; /* enable Layer 1 Tristate (bit 2)Or Qsig params (bit 0,1)*/
+ dword               InitialDspInfo ;
+ dword               ModemGuardTone ;
+ dword               ModemMinSpeed ;
+ dword               ModemMaxSpeed ;
+ dword               ModemOptions ;
+ dword               ModemOptions2 ;
+ dword               ModemNegotiationMode ;
+ dword               ModemModulationsMask ;
+ dword               ModemTransmitLevel ;
+ dword               FaxOptions ;
+ dword               FaxMaxSpeed ;
+ dword               Part68LevelLimiter ;
+ dword               UsEktsNumCallApp ;
+ byte                UsEktsFeatAddConf ;
+ byte                UsEktsFeatRemoveConf ;
+ byte                UsEktsFeatCallTransfer ;
+ byte                UsEktsFeatMsgWaiting ;
+ byte                QsigDialect;
+ byte                ForceVoiceMailAlert;
+ byte                DisableAutoSpid;
+ byte                ModemCarrierWaitTimeSec;
+ byte                ModemCarrierLossWaitTimeTenthSec;
+ byte                PiafsLinkTurnaroundInFrames;
+ byte                DiscAfterProgress;
+ byte                AniDniLimiter[3];
+ byte                TxAttenuation;  /* PRI/E1 only: attenuate TX signal */
+ word                QsigFeatures;
+ dword               GenerateRingtone ;
+ dword               SupplementaryServicesFeatures;
+ dword               R2Dialect;
+ dword               R2CasOptions;
+ dword               FaxV34Options;
+ dword               DisabledDspMask;
+ dword               AdapterTestMask;
+ dword               DspImageLength;
+ word                AlertToIn20mSecTicks;
+ word                ModemEyeSetup;
+ byte                R2CtryLength;
+ byte                CCBSRelTimer;
+ byte               *PcCfgBufferFile;/* flexible parameter via file */
+ byte               *PcCfgBuffer ; /* flexible parameter via multistring */
+ diva_os_dump_file_t dump_file; /* dump memory to file at lowest irq level */
+ diva_os_board_trace_t board_trace ; /* traces from the board */
+ diva_os_spin_lock_t isr_spin_lock;
+ diva_os_spin_lock_t data_spin_lock;
+ diva_os_soft_isr_t req_soft_isr;
+ diva_os_soft_isr_t isr_soft_isr;
+ diva_os_atomic_t  in_dpc;
+ PBUFFER             RBuffer;        /* Copy of receive lookahead buffer */
+ word                e_max;
+ word                e_count;
+ E_INFO             *e_tbl;
+ word                assign;         /* list of pending ASSIGNs  */
+ word                head;           /* head of request queue    */
+ word                tail;           /* tail of request queue    */
+ ADAPTER             a ;             /* not a separate structure */
+ void        (* out)(ADAPTER * a) ;
+ byte        (* dpc)(ADAPTER * a) ;
+ byte        (* tst_irq)(ADAPTER * a) ;
+ void        (* clr_irq)(ADAPTER * a) ;
+ int         (* load)(PISDN_ADAPTER) ;
+ int         (* mapmem)(PISDN_ADAPTER) ;
+ int         (* chkIrq)(PISDN_ADAPTER) ;
+ void        (* disIrq)(PISDN_ADAPTER) ;
+ void        (* start)(PISDN_ADAPTER) ;
+ void        (* stop)(PISDN_ADAPTER) ;
+ void        (* rstFnc)(PISDN_ADAPTER) ;
+ void        (* trapFnc)(PISDN_ADAPTER) ;
+ dword            (* DetectDsps)(PISDN_ADAPTER) ;
+ void        (* os_trap_nfy_Fnc)(PISDN_ADAPTER, dword) ;
+ diva_os_isr_callback_t diva_isr_handler;
+ dword               sdram_bar;  /* must be 32 bit */
+ dword               fpga_features;
+ volatile int        pcm_pending;
+ volatile void *     pcm_data;
+ diva_xdi_capi_cfg_t capi_cfg;
+ dword               tasks;
+ void               *dma_map;
+ int             (*DivaAdapterTestProc)(PISDN_ADAPTER);
+ void               *AdapterTestMemoryStart;
+ dword               AdapterTestMemoryLength;
+ const byte* cfg_lib_memory_init;
+ dword       cfg_lib_memory_init_length;
+};
+/* ---------------------------------------------------------------------
+  Entity table
+   --------------------------------------------------------------------- */
+struct e_info_s {
+  ENTITY *      e;
+  byte          next;                   /* chaining index           */
+  word          assign_ref;             /* assign reference         */
+};
+/* ---------------------------------------------------------------------
+  S-cards shared ram structure for loading
+   --------------------------------------------------------------------- */
+struct s_load {
+ byte ctrl;
+ byte card;
+ byte msize;
+ byte fill0;
+ word ebit;
+ word elocl;
+ word eloch;
+ byte reserved[20];
+ word signature;
+ byte fill[224];
+ byte b[256];
+};
+#define PR_RAM  ((struct pr_ram *)0)
+#define RAM ((struct dual *)0)
+/* ---------------------------------------------------------------------
+  platform specific conversions
+   --------------------------------------------------------------------- */
+extern void * PTR_P(ADAPTER * a, ENTITY * e, void * P);
+extern void * PTR_X(ADAPTER * a, ENTITY * e);
+extern void * PTR_R(ADAPTER * a, ENTITY * e);
+extern void CALLBACK(ADAPTER * a, ENTITY * e);
+extern void set_ram(void * * adr_ptr);
+/* ---------------------------------------------------------------------
+  ram access functions for io mapped cards
+   --------------------------------------------------------------------- */
+byte io_in(ADAPTER * a, void * adr);
+word io_inw(ADAPTER * a, void * adr);
+void io_in_buffer(ADAPTER * a, void * adr, void * P, word length);
+void io_look_ahead(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e);
+void io_out(ADAPTER * a, void * adr, byte data);
+void io_outw(ADAPTER * a, void * adr, word data);
+void io_out_buffer(ADAPTER * a, void * adr, void * P, word length);
+void io_inc(ADAPTER * a, void * adr);
+void bri_in_buffer (PISDN_ADAPTER IoAdapter, dword Pos,
+                    void *Buf, dword Len);
+int bri_out_buffer (PISDN_ADAPTER IoAdapter, dword Pos,
+                    void *Buf, dword Len, int Verify);
+/* ---------------------------------------------------------------------
+  ram access functions for memory mapped cards
+   --------------------------------------------------------------------- */
+byte mem_in(ADAPTER * a, void * adr);
+word mem_inw(ADAPTER * a, void * adr);
+void mem_in_buffer(ADAPTER * a, void * adr, void * P, word length);
+void mem_look_ahead(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e);
+void mem_out(ADAPTER * a, void * adr, byte data);
+void mem_outw(ADAPTER * a, void * adr, word data);
+void mem_out_buffer(ADAPTER * a, void * adr, void * P, word length);
+void mem_inc(ADAPTER * a, void * adr);
+void mem_in_dw (ADAPTER *a, void *addr, dword* data, int dwords);
+void mem_out_dw (ADAPTER *a, void *addr, const dword* data, int dwords);
+/* ---------------------------------------------------------------------
+  functions exported by io.c
+   --------------------------------------------------------------------- */
+extern IDI_CALL Requests[MAX_ADAPTER] ;
+extern void     DIDpcRoutine (struct _diva_os_soft_isr* psoft_isr,
+               void* context);
+extern void     request (PISDN_ADAPTER, ENTITY *) ;
+/* ---------------------------------------------------------------------
+  trapFn helpers, used to recover debug trace from dead card
+   --------------------------------------------------------------------- */
+typedef struct {
+ word *buf ;
+ word  cnt ;
+ word  out ;
+} Xdesc ;
+extern void     dump_trap_frame  (PISDN_ADAPTER IoAdapter, byte __iomem *exception) ;
+extern void     dump_xlog_buffer (PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc) ;
+/* --------------------------------------------------------------------- */
+#endif  /* } __DIVA_XDI_COMMON_IO_H_INC__ */
diff --git a/drivers/isdn/hardware/eicon/istream.c b/drivers/isdn/hardware/eicon/istream.c
new file mode 100644
index 0000000..2313966
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/istream.c
@@ -0,0 +1,226 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#if defined(DIVA_ISTREAM) /* { */
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "di.h"
+#if !defined USE_EXTENDED_DEBUGS
+  #include "dimaint.h"
+#else
+  #define dprintf
+#endif
+#include "dfifo.h"
+int diva_istream_write (void* context,
+             int   Id,
+              void* data,
+             int length,
+             int final,
+            byte usr1,
+            byte usr2);
+int diva_istream_read (void* context,
+            int Id,
+            void* data,
+            int max_length,
+            int* final,
+            byte* usr1,
+            byte* usr2);
+/* -------------------------------------------------------------------
+  Does provide iStream interface to the client
+   ------------------------------------------------------------------- */
+void diva_xdi_provide_istream_info (ADAPTER* a,
+                  diva_xdi_stream_interface_t* pi) {
+  pi->provided_service = 0;
+}
+/* ------------------------------------------------------------------
+  Does write the data from caller's buffer to the card's
+  stream interface.
+  If synchronous service was requested, then function
+  does return amount of data written to stream.
+  'final' does indicate that pice of data to be written is
+  final part of frame (necessary only by structured datatransfer)
+  return  0 if zero lengh packet was written
+  return -1 if stream is full
+  ------------------------------------------------------------------ */
+int diva_istream_write (void* context,
+                int   Id,
+                  void* data,
+                 int length,
+                 int final,
+                byte usr1,
+                byte usr2) {
+ ADAPTER* a = (ADAPTER*)context;
+ int written = 0, to_write = -1;
+ char tmp[4];
+ byte* data_ptr = (byte*)data;
+ for (;;) {
+  a->ram_in_dw (a,
+#ifdef PLATFORM_GT_32BIT
+         ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]),
+#else
+         (void*)(a->tx_stream[Id] + a->tx_pos[Id]),
+#endif
+                  (dword*)&tmp[0],
+         1);
+  if (tmp[0] & DIVA_DFIFO_READY) { /* No free blocks more */
+   if (to_write < 0)
+    return (-1); /* was not able to write       */
+   break;     /* only part of message was written */
+  }
+  to_write = MIN(length, DIVA_DFIFO_DATA_SZ);
+  if (to_write) {
+   a->ram_out_buffer (a,
+#ifdef PLATFORM_GT_32BIT
+            ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]+4),
+#else
+              (void*)(a->tx_stream[Id] + a->tx_pos[Id] + 4),
+#endif
+                         data_ptr,
+             (word)to_write);
+   length  -= to_write;
+   written  += to_write;
+   data_ptr += to_write;
+  }
+  tmp[1] = (char)to_write;
+  tmp[0] = (tmp[0] & DIVA_DFIFO_WRAP) |
+       DIVA_DFIFO_READY |
+       ((!length && final) ? DIVA_DFIFO_LAST : 0);
+  if (tmp[0] & DIVA_DFIFO_LAST) {
+   tmp[2] = usr1;
+   tmp[3] = usr2;
+  }
+    a->ram_out_dw (a,
+#ifdef PLATFORM_GT_32BIT
+         ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]),
+#else
+           (void*)(a->tx_stream[Id] + a->tx_pos[Id]),
+#endif
+                   (dword*)&tmp[0],
+          1);
+  if (tmp[0] & DIVA_DFIFO_WRAP) {
+   a->tx_pos[Id]  = 0;
+  } else {
+   a->tx_pos[Id] += DIVA_DFIFO_STEP;
+  }
+  if (!length) {
+   break;
+  }
+ }
+ return (written);
+}
+/* -------------------------------------------------------------------
+  In case of SYNCRONOUS service:
+  Does write data from stream in caller's buffer.
+  Does return amount of data written to buffer
+  Final flag is set on return if last part of structured frame
+  was received
+  return 0  if zero packet was received
+  return -1 if stream is empty
+    return -2 if read buffer does not profide sufficient space
+              to accommodate entire segment
+  max_length should be at least 68 bytes
+  ------------------------------------------------------------------- */
+int diva_istream_read (void* context,
+                int Id,
+                void* data,
+                int max_length,
+                int* final,
+               byte* usr1,
+               byte* usr2) {
+ ADAPTER* a = (ADAPTER*)context;
+ int read = 0, to_read = -1;
+ char tmp[4];
+ byte* data_ptr = (byte*)data;
+ *final = 0;
+ for (;;) {
+  a->ram_in_dw (a,
+#ifdef PLATFORM_GT_32BIT
+         ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]),
+#else
+         (void*)(a->rx_stream[Id] + a->rx_pos[Id]),
+#endif
+                  (dword*)&tmp[0],
+         1);
+  if (tmp[1] > max_length) {
+   if (to_read < 0)
+    return (-2); /* was not able to read */
+   break;
+    }
+  if (!(tmp[0] & DIVA_DFIFO_READY)) {
+   if (to_read < 0)
+    return (-1); /* was not able to read */
+   break;
+  }
+  to_read = MIN(max_length, tmp[1]);
+  if (to_read) {
+   a->ram_in_buffer(a,
+#ifdef PLATFORM_GT_32BIT
+           ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id] + 4),
+#else
+            (void*)(a->rx_stream[Id] + a->rx_pos[Id] + 4),
+#endif
+                       data_ptr,
+            (word)to_read);
+   max_length -= to_read;
+   read     += to_read;
+   data_ptr  += to_read;
+  }
+  if (tmp[0] & DIVA_DFIFO_LAST) {
+   *final = 1;
+  }
+  tmp[0] &= DIVA_DFIFO_WRAP;
+    a->ram_out_dw(a,
+#ifdef PLATFORM_GT_32BIT
+         ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]),
+#else
+         (void*)(a->rx_stream[Id] + a->rx_pos[Id]),
+#endif
+         (dword*)&tmp[0],
+         1);
+  if (tmp[0] & DIVA_DFIFO_WRAP) {
+   a->rx_pos[Id]  = 0;
+  } else {
+   a->rx_pos[Id] += DIVA_DFIFO_STEP;
+  }
+  if (*final) {
+   if (usr1)
+    *usr1 = tmp[2];
+   if (usr2)
+    *usr2 = tmp[3];
+   break;
+  }
+ }
+ return (read);
+}
+/* ---------------------------------------------------------------------
+  Does check if one of streams had caused interrupt and does
+  wake up corresponding application
+   --------------------------------------------------------------------- */
+void pr_stream (ADAPTER * a) {
+}
+#endif /* } */
diff --git a/drivers/isdn/hardware/eicon/kst_ifc.h b/drivers/isdn/hardware/eicon/kst_ifc.h
new file mode 100644
index 0000000..203189a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/kst_ifc.h
@@ -0,0 +1,336 @@
+/*
+ *
+  Copyright (c) Eicon Networks, 2000.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    1.9
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_EICON_TRACE_API__
+#define __DIVA_EICON_TRACE_API__
+
+#define DIVA_TRACE_LINE_TYPE_LEN 64
+#define DIVA_TRACE_IE_LEN        64
+#define DIVA_TRACE_FAX_PRMS_LEN  128
+
+typedef struct _diva_trace_ie {
+	byte length;
+	byte data[DIVA_TRACE_IE_LEN];
+} diva_trace_ie_t;
+
+/*
+	Structure used to represent "State\\BX\\Modem" directory
+	to user.
+	*/
+typedef struct _diva_trace_modem_state {
+	dword	ChannelNumber;
+
+	dword	Event;
+
+	dword	Norm;
+
+	dword Options; /* Options received from Application */
+
+	dword	TxSpeed;
+	dword	RxSpeed;
+
+	dword RoundtripMsec;
+
+	dword SymbolRate;
+
+	int		RxLeveldBm;
+	int		EchoLeveldBm;
+
+	dword	SNRdb;
+	dword MAE;
+
+	dword LocalRetrains;
+	dword RemoteRetrains;
+	dword LocalResyncs;
+	dword RemoteResyncs;
+
+	dword DiscReason;
+
+} diva_trace_modem_state_t;
+
+/*
+	Representation of "State\\BX\\FAX" directory
+	*/
+typedef struct _diva_trace_fax_state {
+	dword	ChannelNumber;
+	dword Event;
+	dword Page_Counter;
+	dword Features;
+	char Station_ID[DIVA_TRACE_FAX_PRMS_LEN];
+	char Subaddress[DIVA_TRACE_FAX_PRMS_LEN];
+	char Password[DIVA_TRACE_FAX_PRMS_LEN];
+	dword Speed;
+	dword Resolution;
+	dword Paper_Width;
+	dword Paper_Length;
+	dword Scanline_Time;
+	dword Disc_Reason;
+	dword	dummy;
+} diva_trace_fax_state_t;
+
+/*
+	Structure used to represent Interface State in the abstract
+	and interface/D-channel protocol independent form.
+	*/
+typedef struct _diva_trace_interface_state {
+	char Layer1[DIVA_TRACE_LINE_TYPE_LEN];
+	char Layer2[DIVA_TRACE_LINE_TYPE_LEN];
+} diva_trace_interface_state_t;
+
+typedef struct _diva_incoming_call_statistics {
+	dword Calls;
+	dword Connected;
+	dword User_Busy;
+	dword Call_Rejected;
+	dword Wrong_Number;
+	dword Incompatible_Dst;
+	dword Out_of_Order;
+	dword Ignored;
+} diva_incoming_call_statistics_t;
+
+typedef struct _diva_outgoing_call_statistics {
+	dword Calls;
+	dword Connected;
+	dword User_Busy;
+	dword No_Answer;
+	dword Wrong_Number;
+	dword Call_Rejected;
+	dword Other_Failures;
+} diva_outgoing_call_statistics_t;
+
+typedef struct _diva_modem_call_statistics {
+	dword Disc_Normal;
+	dword Disc_Unspecified;
+	dword Disc_Busy_Tone;
+	dword Disc_Congestion;
+	dword Disc_Carr_Wait;
+	dword Disc_Trn_Timeout;
+	dword Disc_Incompat;
+	dword Disc_Frame_Rej;
+	dword Disc_V42bis;
+} diva_modem_call_statistics_t;
+
+typedef struct _diva_fax_call_statistics {
+	dword Disc_Normal;
+	dword Disc_Not_Ident;
+	dword Disc_No_Response;
+	dword Disc_Retries;
+	dword Disc_Unexp_Msg;
+	dword Disc_No_Polling;
+	dword Disc_Training;
+	dword Disc_Unexpected;
+	dword Disc_Application;
+	dword Disc_Incompat;
+	dword Disc_No_Command;
+	dword Disc_Long_Msg;
+	dword Disc_Supervisor;
+	dword Disc_SUB_SEP_PWD;
+	dword Disc_Invalid_Msg;
+	dword Disc_Page_Coding;
+	dword Disc_App_Timeout;
+	dword Disc_Unspecified;
+} diva_fax_call_statistics_t;
+
+typedef struct _diva_prot_statistics {
+	dword X_Frames;
+	dword X_Bytes;
+	dword X_Errors;
+	dword R_Frames;
+	dword R_Bytes;
+	dword R_Errors;
+} diva_prot_statistics_t;
+
+typedef struct _diva_ifc_statistics {
+	diva_incoming_call_statistics_t	inc;
+	diva_outgoing_call_statistics_t outg;
+	diva_modem_call_statistics_t		mdm;
+	diva_fax_call_statistics_t			fax;
+	diva_prot_statistics_t					b1;
+	diva_prot_statistics_t					b2;
+	diva_prot_statistics_t					d1;
+	diva_prot_statistics_t					d2;
+} diva_ifc_statistics_t;
+
+/*
+	Structure used to represent "State\\BX" directory
+	to user.
+	*/
+typedef struct _diva_trace_line_state {
+	dword	ChannelNumber;
+
+	char Line[DIVA_TRACE_LINE_TYPE_LEN];
+
+	char Framing[DIVA_TRACE_LINE_TYPE_LEN];
+
+	char Layer2[DIVA_TRACE_LINE_TYPE_LEN];
+	char Layer3[DIVA_TRACE_LINE_TYPE_LEN];
+
+	char RemoteAddress[DIVA_TRACE_LINE_TYPE_LEN];
+	char RemoteSubAddress[DIVA_TRACE_LINE_TYPE_LEN];
+
+	char LocalAddress[DIVA_TRACE_LINE_TYPE_LEN];
+	char LocalSubAddress[DIVA_TRACE_LINE_TYPE_LEN];
+
+	diva_trace_ie_t	call_BC;
+	diva_trace_ie_t	call_HLC;
+	diva_trace_ie_t	call_LLC;
+
+	dword Charges;
+
+	dword CallReference;
+
+	dword LastDisconnecCause;
+
+	char UserID[DIVA_TRACE_LINE_TYPE_LEN];
+
+	diva_trace_modem_state_t modem;
+	diva_trace_fax_state_t   fax;
+
+	diva_trace_interface_state_t* pInterface;
+
+	diva_ifc_statistics_t*				pInterfaceStat;
+
+} diva_trace_line_state_t;
+
+#define DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE             ('l')
+#define DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE            ('m')
+#define DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE              ('f')
+#define DIVA_SUPER_TRACE_INTERFACE_CHANGE               ('i')
+#define DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE             ('s')
+#define DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE         ('M')
+#define DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE         ('F')
+
+struct _diva_strace_library_interface;
+typedef void (*diva_trace_channel_state_change_proc_t)(void* user_context,
+							struct _diva_strace_library_interface* hLib,
+							int Adapter,
+							diva_trace_line_state_t* channel, int notify_subject);
+typedef void (*diva_trace_channel_trace_proc_t)(void* user_context,
+							struct _diva_strace_library_interface* hLib,
+							int Adapter, void* xlog_buffer, int length);
+typedef void (*diva_trace_error_proc_t)(void* user_context,
+							struct _diva_strace_library_interface* hLib,
+							int Adapter,
+							int error, const char* file, int line);
+
+/*
+	This structure creates interface from user to library
+	*/
+typedef struct _diva_trace_library_user_interface {
+	void*																		user_context;
+	diva_trace_channel_state_change_proc_t	notify_proc;
+	diva_trace_channel_trace_proc_t					trace_proc;
+	diva_trace_error_proc_t									error_notify_proc;
+} diva_trace_library_user_interface_t;
+
+/*
+	Interface from Library to User
+	*/
+typedef int   (*DivaSTraceLibraryStart_proc_t)(void* hLib);
+typedef int   (*DivaSTraceLibraryFinit_proc_t)(void* hLib);
+typedef int   (*DivaSTraceMessageInput_proc_t)(void* hLib);
+typedef void*	(*DivaSTraceGetHandle_proc_t)(void* hLib);
+
+/*
+	Turn Audio Tap trace on/off
+	Channel should be in the range 1 ... Number of Channels
+	*/
+typedef int (*DivaSTraceSetAudioTap_proc_t)(void* hLib, int Channel, int on);
+
+/*
+	Turn B-channel trace on/off
+	Channel should be in the range 1 ... Number of Channels
+	*/
+typedef int (*DivaSTraceSetBChannel_proc_t)(void* hLib, int Channel, int on);
+
+/*
+	Turn	D-channel (Layer1/Layer2/Layer3) trace on/off
+		Layer1 - All D-channel frames received/sent over the interface
+						 inclusive Layer 2 headers, Layer 2 frames and TEI management frames
+		Layer2 - Events from LAPD protocol instance with SAPI of signalling protocol
+		Layer3 - All D-channel frames addressed to assigned to the card TEI and
+						 SAPI of signalling protocol, and signalling protocol events.
+	*/
+typedef int (*DivaSTraceSetDChannel_proc_t)(void* hLib, int on);
+
+/*
+	Get overall card statistics
+	*/
+typedef int (*DivaSTraceGetOutgoingCallStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetIncomingCallStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetModemStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetFaxStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetBLayer1Statistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetBLayer2Statistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetDLayer1Statistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetDLayer2Statistics_proc_t)(void* hLib);
+
+/*
+	Call control
+	*/
+typedef int (*DivaSTraceClearCall_proc_t)(void* hLib, int Channel);
+
+typedef struct _diva_strace_library_interface {
+	void* hLib;
+  DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStart;
+  DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStop;
+	DivaSTraceLibraryFinit_proc_t DivaSTraceLibraryFinit;
+	DivaSTraceMessageInput_proc_t DivaSTraceMessageInput;
+	DivaSTraceGetHandle_proc_t    DivaSTraceGetHandle;
+	DivaSTraceSetAudioTap_proc_t  DivaSTraceSetAudioTap;
+	DivaSTraceSetBChannel_proc_t  DivaSTraceSetBChannel;
+	DivaSTraceSetDChannel_proc_t  DivaSTraceSetDChannel;
+	DivaSTraceSetDChannel_proc_t  DivaSTraceSetInfo;
+	DivaSTraceGetOutgoingCallStatistics_proc_t \
+																DivaSTraceGetOutgoingCallStatistics;
+	DivaSTraceGetIncomingCallStatistics_proc_t \
+																DivaSTraceGetIncomingCallStatistics;
+	DivaSTraceGetModemStatistics_proc_t \
+																DivaSTraceGetModemStatistics;
+	DivaSTraceGetFaxStatistics_proc_t \
+																DivaSTraceGetFaxStatistics;
+	DivaSTraceGetBLayer1Statistics_proc_t \
+																DivaSTraceGetBLayer1Statistics;
+	DivaSTraceGetBLayer2Statistics_proc_t \
+																DivaSTraceGetBLayer2Statistics;
+	DivaSTraceGetDLayer1Statistics_proc_t \
+																DivaSTraceGetDLayer1Statistics;
+	DivaSTraceGetDLayer2Statistics_proc_t \
+																DivaSTraceGetDLayer2Statistics;
+	DivaSTraceClearCall_proc_t    DivaSTraceClearCall;
+} diva_strace_library_interface_t;
+
+/*
+	Create and return Library interface
+	*/
+diva_strace_library_interface_t* DivaSTraceLibraryCreateInstance (int Adapter,
+													const diva_trace_library_user_interface_t* user_proc,
+                          byte* pmem);
+dword DivaSTraceGetMemotyRequirement (int channels);
+
+#define DIVA_MAX_ADAPTERS  64
+#define DIVA_MAX_LINES     32
+
+#endif
+
diff --git a/drivers/isdn/hardware/eicon/main_if.h b/drivers/isdn/hardware/eicon/main_if.h
new file mode 100644
index 0000000..0ea339a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/main_if.h
@@ -0,0 +1,50 @@
+/*
+ *
+  Copyright (c) Eicon Technology Corporation, 2000.
+ *
+  This source file is supplied for the use with Eicon
+  Technology Corporation's range of DIVA Server Adapters.
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the GNU General Public License for more details.
+ *
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+/*------------------------------------------------------------------*/
+/* file: main_if.h                                                  */
+/*------------------------------------------------------------------*/
+# ifndef MAIN_IF___H
+# define MAIN_IF___H
+
+# include "debug_if.h"
+
+void  DI_lock (void) ;
+void  DI_unlock (void) ;
+
+#ifdef NOT_YET_NEEDED
+void  DI_nttime (LARGE_INTEGER *NTtime) ;
+void  DI_ntlcltime(LARGE_INTEGER *NTtime, LARGE_INTEGER *lclNTtime) ;
+void  DI_nttimefields(LARGE_INTEGER *NTtime, TIME_FIELDS *TimeFields);
+unsigned long  DI_wintime(LARGE_INTEGER *NTtime) ;
+
+unsigned short  DiInsertProcessorNumber (int type) ;
+void DiProcessEventLog (unsigned short id, unsigned long msgID, va_list ap);
+
+void  StartIoctlTimer (void (*Handler)(void), unsigned long msec) ;
+void  StopIoctlTimer (void) ;
+void  UnpendIoctl (DbgRequest *pDbgReq) ;
+#endif
+
+void add_to_q(int, char* , unsigned int);
+# endif /* MAIN_IF___H */
+
diff --git a/drivers/isdn/hardware/eicon/maintidi.c b/drivers/isdn/hardware/eicon/maintidi.c
new file mode 100644
index 0000000..23960cb
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/maintidi.c
@@ -0,0 +1,2194 @@
+/*
+ *
+  Copyright (c) Eicon Networks, 2000.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    1.9
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "kst_ifc.h"
+#include "di_defs.h"
+#include "maintidi.h"
+#include "pc.h"
+#include "man_defs.h"
+
+
+extern void diva_mnt_internal_dprintf (dword drv_id, dword type, char* p, ...);
+
+#define MODEM_PARSE_ENTRIES  16 /* amount of variables of interest */
+#define FAX_PARSE_ENTRIES    12 /* amount of variables of interest */
+#define LINE_PARSE_ENTRIES   15 /* amount of variables of interest */
+#define STAT_PARSE_ENTRIES   70 /* amount of variables of interest */
+
+/*
+	LOCAL FUNCTIONS
+	*/
+static int DivaSTraceLibraryStart (void* hLib);
+static int DivaSTraceLibraryStop  (void* hLib);
+static int SuperTraceLibraryFinit (void* hLib);
+static void*	SuperTraceGetHandle (void* hLib);
+static int SuperTraceMessageInput (void* hLib);
+static int SuperTraceSetAudioTap  (void* hLib, int Channel, int on);
+static int SuperTraceSetBChannel  (void* hLib, int Channel, int on);
+static int SuperTraceSetDChannel  (void* hLib, int on);
+static int SuperTraceSetInfo      (void* hLib, int on);
+static int SuperTraceClearCall (void* hLib, int Channel);
+static int SuperTraceGetOutgoingCallStatistics (void* hLib);
+static int SuperTraceGetIncomingCallStatistics (void* hLib);
+static int SuperTraceGetModemStatistics (void* hLib);
+static int SuperTraceGetFaxStatistics (void* hLib);
+static int SuperTraceGetBLayer1Statistics (void* hLib);
+static int SuperTraceGetBLayer2Statistics (void* hLib);
+static int SuperTraceGetDLayer1Statistics (void* hLib);
+static int SuperTraceGetDLayer2Statistics (void* hLib);
+
+/*
+	LOCAL FUNCTIONS
+	*/
+static int ScheduleNextTraceRequest (diva_strace_context_t* pLib);
+static int process_idi_event (diva_strace_context_t* pLib,
+															diva_man_var_header_t* pVar);
+static int process_idi_info  (diva_strace_context_t* pLib,
+															diva_man_var_header_t* pVar);
+static int diva_modem_event (diva_strace_context_t* pLib, int Channel);
+static int diva_fax_event   (diva_strace_context_t* pLib, int Channel);
+static int diva_line_event (diva_strace_context_t* pLib, int Channel);
+static int diva_modem_info (diva_strace_context_t* pLib,
+														int Channel,
+														diva_man_var_header_t* pVar);
+static int diva_fax_info   (diva_strace_context_t* pLib,
+														int Channel,
+														diva_man_var_header_t* pVar);
+static int diva_line_info  (diva_strace_context_t* pLib,
+														int Channel,
+														diva_man_var_header_t* pVar);
+static int diva_ifc_statistics (diva_strace_context_t* pLib,
+																diva_man_var_header_t* pVar);
+static diva_man_var_header_t* get_next_var (diva_man_var_header_t* pVar);
+static diva_man_var_header_t* find_var (diva_man_var_header_t* pVar,
+																				const char* name);
+static int diva_strace_read_int  (diva_man_var_header_t* pVar, int* var);
+static int diva_strace_read_uint (diva_man_var_header_t* pVar, dword* var);
+static int diva_strace_read_asz  (diva_man_var_header_t* pVar, char* var);
+static int diva_strace_read_asc  (diva_man_var_header_t* pVar, char* var);
+static int  diva_strace_read_ie  (diva_man_var_header_t* pVar,
+																	diva_trace_ie_t* var);
+static void diva_create_parse_table (diva_strace_context_t* pLib);
+static void diva_trace_error (diva_strace_context_t* pLib,
+															int error, const char* file, int line);
+static void diva_trace_notify_user (diva_strace_context_t* pLib,
+														 int Channel,
+														 int notify_subject);
+static int diva_trace_read_variable (diva_man_var_header_t* pVar,
+																		 void* variable);
+
+/*
+	Initialize the library and return context
+	of the created trace object that will represent
+	the IDI adapter.
+	Return 0 on error.
+	*/
+diva_strace_library_interface_t* DivaSTraceLibraryCreateInstance (int Adapter,
+											const diva_trace_library_user_interface_t* user_proc,
+                      byte* pmem) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)pmem;
+	int i;
+
+	if (!pLib) {
+		return NULL;
+	}
+
+	pmem += sizeof(*pLib);
+	memset(pLib, 0x00, sizeof(*pLib));
+
+	pLib->Adapter  = Adapter;
+
+	/*
+		Set up Library Interface
+		*/
+	pLib->instance.hLib                                = pLib;
+  pLib->instance.DivaSTraceLibraryStart              = DivaSTraceLibraryStart;
+  pLib->instance.DivaSTraceLibraryStop               = DivaSTraceLibraryStop;
+	pLib->instance.DivaSTraceLibraryFinit              = SuperTraceLibraryFinit;
+	pLib->instance.DivaSTraceMessageInput              = SuperTraceMessageInput;
+	pLib->instance.DivaSTraceGetHandle                 = SuperTraceGetHandle;
+	pLib->instance.DivaSTraceSetAudioTap               = SuperTraceSetAudioTap;
+	pLib->instance.DivaSTraceSetBChannel               = SuperTraceSetBChannel;
+	pLib->instance.DivaSTraceSetDChannel               = SuperTraceSetDChannel;
+	pLib->instance.DivaSTraceSetInfo                   = SuperTraceSetInfo;
+	pLib->instance.DivaSTraceGetOutgoingCallStatistics = \
+																			SuperTraceGetOutgoingCallStatistics;
+	pLib->instance.DivaSTraceGetIncomingCallStatistics = \
+																			SuperTraceGetIncomingCallStatistics;
+	pLib->instance.DivaSTraceGetModemStatistics        = \
+																			SuperTraceGetModemStatistics;
+	pLib->instance.DivaSTraceGetFaxStatistics          = \
+																			SuperTraceGetFaxStatistics;
+	pLib->instance.DivaSTraceGetBLayer1Statistics      = \
+																			SuperTraceGetBLayer1Statistics;
+	pLib->instance.DivaSTraceGetBLayer2Statistics      = \
+																			SuperTraceGetBLayer2Statistics;
+	pLib->instance.DivaSTraceGetDLayer1Statistics      = \
+																			SuperTraceGetDLayer1Statistics;
+	pLib->instance.DivaSTraceGetDLayer2Statistics      = \
+																			SuperTraceGetDLayer2Statistics;
+	pLib->instance.DivaSTraceClearCall                 = SuperTraceClearCall;
+
+
+	if (user_proc) {
+		pLib->user_proc_table.user_context      = user_proc->user_context;
+		pLib->user_proc_table.notify_proc       = user_proc->notify_proc;
+		pLib->user_proc_table.trace_proc        = user_proc->trace_proc;
+		pLib->user_proc_table.error_notify_proc = user_proc->error_notify_proc;
+	}
+
+	if (!(pLib->hAdapter = SuperTraceOpenAdapter (Adapter))) {
+    diva_mnt_internal_dprintf (0, DLI_ERR, "Can not open XDI adapter");
+		return NULL;
+	}
+	pLib->Channels = SuperTraceGetNumberOfChannels (pLib->hAdapter);
+
+	/*
+		Calculate amount of parte table entites necessary to translate
+		information from all events of onterest
+		*/
+	pLib->parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \
+												 STAT_PARSE_ENTRIES + \
+												 LINE_PARSE_ENTRIES + 1) * pLib->Channels;
+	pLib->parse_table = (diva_strace_path2action_t*)pmem;
+
+	for (i = 0; i < 30; i++) {
+		pLib->lines[i].pInterface     = &pLib->Interface;
+		pLib->lines[i].pInterfaceStat = &pLib->InterfaceStat;
+	}
+
+  pLib->e.R = &pLib->RData;
+
+	pLib->req_busy = 1;
+	pLib->rc_ok    = ASSIGN_OK;
+
+	diva_create_parse_table (pLib);
+
+	return ((diva_strace_library_interface_t*)pLib);
+}
+
+static int DivaSTraceLibraryStart (void* hLib) {
+  diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+  return (SuperTraceASSIGN (pLib->hAdapter, pLib->buffer));
+}
+
+/*
+  Return (-1) on error
+  Return (0) if was initiated or pending
+  Return (1) if removal is complete
+  */
+static int DivaSTraceLibraryStop  (void* hLib) {
+  diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+  if (!pLib->e.Id) { /* Was never started/assigned */
+    return (1);
+  }
+
+  switch (pLib->removal_state) {
+    case 0:
+      pLib->removal_state = 1;
+      ScheduleNextTraceRequest(pLib);
+      break;
+
+    case 3:
+      return (1);
+  }
+
+  return (0);
+}
+
+static int SuperTraceLibraryFinit (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	if (pLib) {
+		if (pLib->hAdapter) {
+			SuperTraceCloseAdapter  (pLib->hAdapter);
+		}
+		return (0);
+	}
+	return (-1);
+}
+
+static void*	SuperTraceGetHandle (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+  return (&pLib->e);
+}
+
+/*
+	After library handle object is gone in signaled state
+	this function should be called and will pick up incoming
+	IDI messages (return codes and indications).
+	*/
+static int SuperTraceMessageInput (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	int ret = 0;
+  byte Rc, Ind;
+
+  if (pLib->e.complete == 255) {
+    /*
+      Process return code
+      */
+    pLib->req_busy = 0;
+    Rc             = pLib->e.Rc;
+    pLib->e.Rc     = 0;
+
+    if (pLib->removal_state == 2) {
+      pLib->removal_state = 3;
+      return (0);
+    }
+
+		if (Rc != pLib->rc_ok) {
+      int ignore = 0;
+      /*
+        Auto-detect amount of events/channels and features
+        */
+      if (pLib->general_b_ch_event == 1) {
+        pLib->general_b_ch_event = 2;
+        ignore = 1;
+      } else if (pLib->general_fax_event == 1) {
+        pLib->general_fax_event = 2;
+        ignore = 1;
+      } else if (pLib->general_mdm_event == 1) {
+        pLib->general_mdm_event = 2;
+        ignore = 1;
+      } else if ((pLib->ChannelsTraceActive < pLib->Channels) && pLib->ChannelsTraceActive) {
+        pLib->ChannelsTraceActive = pLib->Channels;
+        ignore = 1;
+      } else if (pLib->ModemTraceActive < pLib->Channels) {
+        pLib->ModemTraceActive = pLib->Channels;
+        ignore = 1;
+      } else if (pLib->FaxTraceActive < pLib->Channels) {
+        pLib->FaxTraceActive = pLib->Channels;
+        ignore = 1;
+      } else if (pLib->audio_trace_init == 2) {
+        ignore = 1;
+        pLib->audio_trace_init = 1;
+      } else if (pLib->eye_pattern_pending) {
+				pLib->eye_pattern_pending =  0;
+				ignore = 1;
+			} else if (pLib->audio_tap_pending) {
+				pLib->audio_tap_pending = 0;
+				ignore = 1;
+      }
+
+      if (!ignore) {
+        return (-1); /* request failed */
+      }
+    } else {
+      if (pLib->general_b_ch_event == 1) {
+        pLib->ChannelsTraceActive = pLib->Channels;
+        pLib->general_b_ch_event = 2;
+      } else if (pLib->general_fax_event == 1) {
+        pLib->general_fax_event = 2;
+        pLib->FaxTraceActive = pLib->Channels;
+      } else if (pLib->general_mdm_event == 1) {
+        pLib->general_mdm_event = 2;
+        pLib->ModemTraceActive = pLib->Channels;
+      }
+    }
+    if (pLib->audio_trace_init == 2) {
+      pLib->audio_trace_init = 1;
+    }
+    pLib->rc_ok = 0xff; /* default OK after assign was done */
+    if ((ret = ScheduleNextTraceRequest(pLib))) {
+      return (-1);
+    }
+  } else {
+    /*
+      Process indication
+      Always 'RNR' indication if return code is pending
+      */
+    Ind         = pLib->e.Ind;
+    pLib->e.Ind = 0;
+    if (pLib->removal_state) {
+      pLib->e.RNum	= 0;
+      pLib->e.RNR	= 2;
+    } else if (pLib->req_busy) {
+      pLib->e.RNum	= 0;
+      pLib->e.RNR	= 1;
+    } else {
+      if (pLib->e.complete != 0x02) {
+        /*
+          Look-ahead call, set up buffers
+          */
+        pLib->e.RNum       = 1;
+        pLib->e.R->P       = (byte*)&pLib->buffer[0];
+        pLib->e.R->PLength = (word)(sizeof(pLib->buffer) - 1);
+
+      } else {
+        /*
+          Indication reception complete, process it now
+          */
+        byte* p = (byte*)&pLib->buffer[0];
+        pLib->buffer[pLib->e.R->PLength] = 0; /* terminate I.E. with zero */
+
+        switch (Ind) {
+          case MAN_COMBI_IND: {
+            int total_length    = pLib->e.R->PLength;
+            word  this_ind_length;
+
+            while (total_length > 3 && *p) {
+              Ind = *p++;
+              this_ind_length = (word)p[0] | ((word)p[1] << 8);
+              p += 2;
+
+              switch (Ind) {
+                case MAN_INFO_IND:
+                  if (process_idi_info (pLib, (diva_man_var_header_t*)p)) {
+                    return (-1);
+                  }
+                  break;
+      					case MAN_EVENT_IND:
+                  if (process_idi_event (pLib, (diva_man_var_header_t*)p)) {
+                    return (-1);
+                  }
+                  break;
+                case MAN_TRACE_IND:
+                  if (pLib->trace_on == 1) {
+                    /*
+                      Ignore first trace event that is result of
+                      EVENT_ON operation
+                    */
+                    pLib->trace_on++;
+                  } else {
+                    /*
+                      Delivery XLOG buffer to application
+                      */
+                    if (pLib->user_proc_table.trace_proc) {
+                      (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context,
+                                                            &pLib->instance, pLib->Adapter,
+                                                            p, this_ind_length);
+                    }
+                  }
+                  break;
+                default:
+                  diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind (DMA mode): %02x", Ind);
+              }
+              p += (this_ind_length+1);
+              total_length -= (4 + this_ind_length);
+            }
+          } break;
+          case MAN_INFO_IND:
+            if (process_idi_info (pLib, (diva_man_var_header_t*)p)) {
+              return (-1);
+            }
+            break;
+					case MAN_EVENT_IND:
+            if (process_idi_event (pLib, (diva_man_var_header_t*)p)) {
+              return (-1);
+            }
+            break;
+          case MAN_TRACE_IND:
+            if (pLib->trace_on == 1) {
+              /*
+                Ignore first trace event that is result of
+                EVENT_ON operation
+              */
+              pLib->trace_on++;
+            } else {
+              /*
+                Delivery XLOG buffer to application
+                */
+              if (pLib->user_proc_table.trace_proc) {
+                (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context,
+                                                      &pLib->instance, pLib->Adapter,
+                                                      p, pLib->e.R->PLength);
+              }
+            }
+            break;
+          default:
+            diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind: %02x", Ind);
+        }
+      }
+    }
+  }
+
+	if ((ret = ScheduleNextTraceRequest(pLib))) {
+		return (-1);
+	}
+
+	return (ret);
+}
+
+/*
+	Internal state machine responsible for scheduling of requests
+	*/
+static int ScheduleNextTraceRequest (diva_strace_context_t* pLib) {
+	char name[64];
+	int ret = 0;
+	int i;
+
+	if (pLib->req_busy) {
+		return (0);
+	}
+
+  if (pLib->removal_state == 1) {
+		if (SuperTraceREMOVE (pLib->hAdapter)) {
+      pLib->removal_state = 3;
+    } else {
+      pLib->req_busy = 1;
+      pLib->removal_state = 2;
+    }
+    return (0);
+  }
+
+  if (pLib->removal_state) {
+    return (0);
+  }
+
+  if (!pLib->general_b_ch_event) {
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\B Event", pLib->buffer))) {
+      return (-1);
+    }
+    pLib->general_b_ch_event = 1;
+		pLib->req_busy = 1;
+		return (0);
+  }
+
+  if (!pLib->general_fax_event) {
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\FAX Event", pLib->buffer))) {
+      return (-1);
+    }
+    pLib->general_fax_event = 1;
+		pLib->req_busy = 1;
+		return (0);
+  }
+
+  if (!pLib->general_mdm_event) {
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\Modem Event", pLib->buffer))) {
+      return (-1);
+    }
+    pLib->general_mdm_event = 1;
+		pLib->req_busy = 1;
+		return (0);
+  }
+
+	if (pLib->ChannelsTraceActive < pLib->Channels) {
+		pLib->ChannelsTraceActive++;
+		sprintf (name, "State\\B%d\\Line", pLib->ChannelsTraceActive);
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+			pLib->ChannelsTraceActive--;
+			return (-1);
+		}
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->ModemTraceActive < pLib->Channels) {
+		pLib->ModemTraceActive++;
+		sprintf (name, "State\\B%d\\Modem\\Event", pLib->ModemTraceActive);
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+			pLib->ModemTraceActive--;
+			return (-1);
+		}
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->FaxTraceActive < pLib->Channels) {
+		pLib->FaxTraceActive++;
+		sprintf (name, "State\\B%d\\FAX\\Event", pLib->FaxTraceActive);
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+			pLib->FaxTraceActive--;
+			return (-1);
+		}
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->trace_mask_init) {
+		word tmp = 0x0000;
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\Event Enable",
+												 		&tmp,
+												 		0x87, /* MI_BITFLD */
+												 		sizeof(tmp))) {
+			return (-1);
+		}
+		pLib->trace_mask_init = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->audio_trace_init) {
+		dword tmp = 0x00000000;
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\AudioCh# Enable",
+												 		&tmp,
+												 		0x87, /* MI_BITFLD */
+												 		sizeof(tmp))) {
+			return (-1);
+		}
+		pLib->audio_trace_init = 2;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->bchannel_init) {
+		dword tmp = 0x00000000;
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\B-Ch# Enable",
+												 		&tmp,
+												 		0x87, /* MI_BITFLD */
+												 		sizeof(tmp))) {
+			return (-1);
+		}
+		pLib->bchannel_init = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->trace_length_init) {
+		word tmp = 30;
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\Max Log Length",
+												 		&tmp,
+														0x82, /* MI_UINT */
+												 		sizeof(tmp))) {
+			return (-1);
+		}
+		pLib->trace_length_init = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->trace_on) {
+		if (SuperTraceTraceOnRequest (pLib->hAdapter,
+																	"Trace\\Log Buffer",
+																	pLib->buffer)) {
+			return (-1);
+		}
+		pLib->trace_on = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->trace_event_mask != pLib->current_trace_event_mask) {
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\Event Enable",
+												 		&pLib->trace_event_mask,
+												 		0x87, /* MI_BITFLD */
+												 		sizeof(pLib->trace_event_mask))) {
+			return (-1);
+		}
+		pLib->current_trace_event_mask = pLib->trace_event_mask;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if ((pLib->audio_tap_pending >= 0) && (pLib->audio_tap_mask != pLib->current_audio_tap_mask)) {
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\AudioCh# Enable",
+												 		&pLib->audio_tap_mask,
+												 		0x87, /* MI_BITFLD */
+												 		sizeof(pLib->audio_tap_mask))) {
+			return (-1);
+		}
+		pLib->current_audio_tap_mask = pLib->audio_tap_mask;
+		pLib->audio_tap_pending = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if ((pLib->eye_pattern_pending >= 0) && (pLib->audio_tap_mask != pLib->current_eye_pattern_mask)) {
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\EyeCh# Enable",
+												 		&pLib->audio_tap_mask,
+												 		0x87, /* MI_BITFLD */
+												 		sizeof(pLib->audio_tap_mask))) {
+			return (-1);
+		}
+		pLib->current_eye_pattern_mask = pLib->audio_tap_mask;
+		pLib->eye_pattern_pending = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->bchannel_trace_mask != pLib->current_bchannel_trace_mask) {
+		if (SuperTraceWriteVar (pLib->hAdapter,
+														pLib->buffer,
+												 		"Trace\\B-Ch# Enable",
+												 		&pLib->bchannel_trace_mask,
+												 		0x87, /* MI_BITFLD */
+												 		sizeof(pLib->bchannel_trace_mask))) {
+			return (-1);
+		}
+		pLib->current_bchannel_trace_mask = pLib->bchannel_trace_mask;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->trace_events_down) {
+		if (SuperTraceTraceOnRequest (pLib->hAdapter,
+																	"Events Down",
+																	pLib->buffer)) {
+			return (-1);
+		}
+		pLib->trace_events_down = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->l1_trace) {
+		if (SuperTraceTraceOnRequest (pLib->hAdapter,
+																	"State\\Layer1",
+																	pLib->buffer)) {
+			return (-1);
+		}
+		pLib->l1_trace = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->l2_trace) {
+		if (SuperTraceTraceOnRequest (pLib->hAdapter,
+																	"State\\Layer2 No1",
+																	pLib->buffer)) {
+			return (-1);
+		}
+		pLib->l2_trace = 1;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	for (i = 0; i < 30; i++) {
+		if (pLib->pending_line_status & (1L << i)) {
+			sprintf (name, "State\\B%d", i+1);
+			if (SuperTraceReadRequest (pLib->hAdapter, name, pLib->buffer)) {
+				return (-1);
+			}
+			pLib->pending_line_status &= ~(1L << i);
+			pLib->req_busy = 1;
+			return (0);
+		}
+		if (pLib->pending_modem_status & (1L << i)) {
+			sprintf (name, "State\\B%d\\Modem", i+1);
+			if (SuperTraceReadRequest (pLib->hAdapter, name, pLib->buffer)) {
+				return (-1);
+			}
+			pLib->pending_modem_status &= ~(1L << i);
+			pLib->req_busy = 1;
+			return (0);
+		}
+		if (pLib->pending_fax_status & (1L << i)) {
+			sprintf (name, "State\\B%d\\FAX", i+1);
+			if (SuperTraceReadRequest (pLib->hAdapter, name, pLib->buffer)) {
+				return (-1);
+			}
+			pLib->pending_fax_status &= ~(1L << i);
+			pLib->req_busy = 1;
+			return (0);
+		}
+		if (pLib->clear_call_command & (1L << i)) {
+			sprintf (name, "State\\B%d\\Clear Call", i+1);
+			if (SuperTraceExecuteRequest (pLib->hAdapter, name, pLib->buffer)) {
+				return (-1);
+			}
+			pLib->clear_call_command &= ~(1L << i);
+			pLib->req_busy = 1;
+			return (0);
+		}
+	}
+
+	if (pLib->outgoing_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\Outgoing Calls",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->outgoing_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->incoming_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\Incoming Calls",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->incoming_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->modem_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\Modem",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->modem_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->fax_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\FAX",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->fax_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->b1_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\B-Layer1",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->b1_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->b2_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\B-Layer2",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->b2_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->d1_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\D-Layer1",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->d1_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (pLib->d2_ifc_stats) {
+		if (SuperTraceReadRequest (pLib->hAdapter,
+															 "Statistics\\D-Layer2",
+															 pLib->buffer)) {
+			return (-1);
+		}
+		pLib->d2_ifc_stats = 0;
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	if (!pLib->IncomingCallsCallsActive) {
+		pLib->IncomingCallsCallsActive = 1;
+		sprintf (name, "%s", "Statistics\\Incoming Calls\\Calls");
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+			pLib->IncomingCallsCallsActive = 0;
+			return (-1);
+		}
+		pLib->req_busy = 1;
+		return (0);
+	}
+	if (!pLib->IncomingCallsConnectedActive) {
+		pLib->IncomingCallsConnectedActive = 1;
+		sprintf (name, "%s", "Statistics\\Incoming Calls\\Connected");
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+			pLib->IncomingCallsConnectedActive = 0;
+			return (-1);
+		}
+		pLib->req_busy = 1;
+		return (0);
+	}
+	if (!pLib->OutgoingCallsCallsActive) {
+		pLib->OutgoingCallsCallsActive = 1;
+		sprintf (name, "%s", "Statistics\\Outgoing Calls\\Calls");
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+			pLib->OutgoingCallsCallsActive = 0;
+			return (-1);
+		}
+		pLib->req_busy = 1;
+		return (0);
+	}
+	if (!pLib->OutgoingCallsConnectedActive) {
+		pLib->OutgoingCallsConnectedActive = 1;
+		sprintf (name, "%s", "Statistics\\Outgoing Calls\\Connected");
+		if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+			pLib->OutgoingCallsConnectedActive = 0;
+			return (-1);
+		}
+		pLib->req_busy = 1;
+		return (0);
+	}
+
+	return (0);
+}
+
+static int process_idi_event (diva_strace_context_t* pLib,
+				diva_man_var_header_t* pVar) {
+	const char* path = (char*)&pVar->path_length+1;
+	char name[64];
+	int i;
+
+	if (!strncmp("State\\B Event", path, pVar->path_length)) {
+    dword ch_id;
+    if (!diva_trace_read_variable (pVar, &ch_id)) {
+      if (!pLib->line_init_event && !pLib->pending_line_status) {
+        for (i = 1; i <= pLib->Channels; i++) {
+          diva_line_event(pLib, i);
+        }
+        return (0);
+      } else if (ch_id && ch_id <= pLib->Channels) {
+        return (diva_line_event(pLib, (int)ch_id));
+      }
+      return (0);
+    }
+    return (-1);
+  }
+
+	if (!strncmp("State\\FAX Event", path, pVar->path_length)) {
+    dword ch_id;
+    if (!diva_trace_read_variable (pVar, &ch_id)) {
+      if (!pLib->pending_fax_status && !pLib->fax_init_event) {
+        for (i = 1; i <= pLib->Channels; i++) {
+          diva_fax_event(pLib, i);
+        }
+        return (0);
+      } else if (ch_id && ch_id <= pLib->Channels) {
+        return (diva_fax_event(pLib, (int)ch_id));
+      }
+      return (0);
+    }
+    return (-1);
+  }
+
+	if (!strncmp("State\\Modem Event", path, pVar->path_length)) {
+    dword ch_id;
+    if (!diva_trace_read_variable (pVar, &ch_id)) {
+      if (!pLib->pending_modem_status && !pLib->modem_init_event) {
+        for (i = 1; i <= pLib->Channels; i++) {
+          diva_modem_event(pLib, i);
+        }
+        return (0);
+      } else if (ch_id && ch_id <= pLib->Channels) {
+        return (diva_modem_event(pLib, (int)ch_id));
+      }
+      return (0);
+    }
+    return (-1);
+  }
+
+	/*
+		First look for Line Event
+		*/
+	for (i = 1; i <= pLib->Channels; i++) {
+		sprintf (name, "State\\B%d\\Line", i);
+		if (find_var (pVar, name)) {
+			return (diva_line_event(pLib, i));
+		}
+	}
+
+	/*
+		Look for Moden Progress Event
+		*/
+	for (i = 1; i <= pLib->Channels; i++) {
+		sprintf (name, "State\\B%d\\Modem\\Event", i);
+		if (find_var (pVar, name)) {
+			return (diva_modem_event (pLib, i));
+		}
+	}
+
+	/*
+		Look for Fax Event
+		*/
+	for (i = 1; i <= pLib->Channels; i++) {
+		sprintf (name, "State\\B%d\\FAX\\Event", i);
+		if (find_var (pVar, name)) {
+			return (diva_fax_event (pLib, i));
+		}
+	}
+
+	/*
+		Notification about loss of events
+		*/
+	if (!strncmp("Events Down", path, pVar->path_length)) {
+		if (pLib->trace_events_down == 1) {
+			pLib->trace_events_down = 2;
+		} else {
+			diva_trace_error (pLib, 1, "Events Down", 0);
+		}
+		return (0);
+	}
+
+	if (!strncmp("State\\Layer1", path, pVar->path_length)) {
+		diva_strace_read_asz  (pVar, &pLib->lines[0].pInterface->Layer1[0]);
+		if (pLib->l1_trace == 1) {
+			pLib->l1_trace = 2;
+		} else {
+			diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE);
+		}
+		return (0);
+	}
+	if (!strncmp("State\\Layer2 No1", path, pVar->path_length)) {
+		char* tmp = &pLib->lines[0].pInterface->Layer2[0];
+    dword l2_state;
+    diva_strace_read_uint (pVar, &l2_state);
+
+		switch (l2_state) {
+			case 0:
+				strcpy (tmp, "Idle");
+				break;
+			case 1:
+				strcpy (tmp, "Layer2 UP");
+				break;
+			case 2:
+				strcpy (tmp, "Layer2 Disconnecting");
+				break;
+			case 3:
+				strcpy (tmp, "Layer2 Connecting");
+				break;
+			case 4:
+				strcpy (tmp, "SPID Initializing");
+				break;
+			case 5:
+				strcpy (tmp, "SPID Initialised");
+				break;
+			case 6:
+				strcpy (tmp, "Layer2 Connecting");
+				break;
+
+			case  7:
+				strcpy (tmp, "Auto SPID Stopped");
+				break;
+
+			case  8:
+				strcpy (tmp, "Auto SPID Idle");
+				break;
+
+			case  9:
+				strcpy (tmp, "Auto SPID Requested");
+				break;
+
+			case  10:
+				strcpy (tmp, "Auto SPID Delivery");
+				break;
+
+			case 11:
+				strcpy (tmp, "Auto SPID Complete");
+				break;
+
+			default:
+				sprintf (tmp, "U:%d", (int)l2_state);
+		}
+		if (pLib->l2_trace == 1) {
+			pLib->l2_trace = 2;
+		} else {
+			diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE);
+		}
+		return (0);
+	}
+
+	if (!strncmp("Statistics\\Incoming Calls\\Calls", path, pVar->path_length) ||
+			!strncmp("Statistics\\Incoming Calls\\Connected", path, pVar->path_length)) {
+		return (SuperTraceGetIncomingCallStatistics (pLib));
+	}
+
+	if (!strncmp("Statistics\\Outgoing Calls\\Calls", path, pVar->path_length) ||
+			!strncmp("Statistics\\Outgoing Calls\\Connected", path, pVar->path_length)) {
+		return (SuperTraceGetOutgoingCallStatistics (pLib));
+	}
+
+	return (-1);
+}
+
+static int diva_line_event (diva_strace_context_t* pLib, int Channel) {
+	pLib->pending_line_status |= (1L << (Channel-1));
+	return (0);
+}
+
+static int diva_modem_event (diva_strace_context_t* pLib, int Channel) {
+	pLib->pending_modem_status |= (1L << (Channel-1));
+	return (0);
+}
+
+static int diva_fax_event (diva_strace_context_t* pLib, int Channel) {
+	pLib->pending_fax_status |= (1L << (Channel-1));
+	return (0);
+}
+
+/*
+	Process INFO indications that arrive from the card
+	Uses path of first I.E. to detect the source of the
+	infication
+	*/
+static int process_idi_info  (diva_strace_context_t* pLib,
+															diva_man_var_header_t* pVar) {
+	const char* path = (char*)&pVar->path_length+1;
+	char name[64];
+	int i, len;
+
+	/*
+		First look for Modem Status Info
+		*/
+	for (i = pLib->Channels; i > 0; i--) {
+		len = sprintf (name, "State\\B%d\\Modem", i);
+		if (!strncmp(name, path, len)) {
+			return (diva_modem_info (pLib, i, pVar));
+		}
+	}
+
+	/*
+		Look for Fax Status Info
+		*/
+	for (i = pLib->Channels; i > 0; i--) {
+		len = sprintf (name, "State\\B%d\\FAX", i);
+		if (!strncmp(name, path, len)) {
+			return (diva_fax_info (pLib, i, pVar));
+		}
+	}
+
+	/*
+		Look for Line Status Info
+		*/
+	for (i = pLib->Channels; i > 0; i--) {
+		len = sprintf (name, "State\\B%d", i);
+		if (!strncmp(name, path, len)) {
+			return (diva_line_info (pLib, i, pVar));
+		}
+	}
+
+	if (!diva_ifc_statistics (pLib, pVar)) {
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*
+	MODEM INSTANCE STATE UPDATE
+
+	Update Modem Status Information and issue notification to user,
+	that will inform about change in the state of modem instance, that is
+	associuated with this channel
+	*/
+static int diva_modem_info (diva_strace_context_t* pLib,
+														int Channel,
+														diva_man_var_header_t* pVar) {
+	diva_man_var_header_t* cur;
+	int i, nr = Channel - 1;
+
+	for (i  = pLib->modem_parse_entry_first[nr];
+			 i <= pLib->modem_parse_entry_last[nr]; i++) {
+		if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+			if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+				diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+				return (-1);
+			}
+		} else {
+			diva_trace_error (pLib, -2 , __FILE__, __LINE__);
+			return (-1);
+		}
+	}
+
+	/*
+		We do not use first event to notify user - this is the event that is
+		generated as result of EVENT ON operation and is used only to initialize
+		internal variables of application
+		*/
+	if (pLib->modem_init_event & (1L << nr)) {
+		diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE);
+	} else {
+		pLib->modem_init_event |= (1L << nr);
+	}
+
+	return (0);
+}
+
+static int diva_fax_info (diva_strace_context_t* pLib,
+													int Channel,
+													diva_man_var_header_t* pVar) {
+	diva_man_var_header_t* cur;
+	int i, nr = Channel - 1;
+
+	for (i  = pLib->fax_parse_entry_first[nr];
+			 i <= pLib->fax_parse_entry_last[nr]; i++) {
+		if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+			if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+				diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+				return (-1);
+			}
+		} else {
+			diva_trace_error (pLib, -2 , __FILE__, __LINE__);
+			return (-1);
+		}
+	}
+
+	/*
+		We do not use first event to notify user - this is the event that is
+		generated as result of EVENT ON operation and is used only to initialize
+		internal variables of application
+		*/
+	if (pLib->fax_init_event & (1L << nr)) {
+		diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE);
+	} else {
+		pLib->fax_init_event |= (1L << nr);
+	}
+
+	return (0);
+}
+
+/*
+	LINE STATE UPDATE
+	Update Line Status Information and issue notification to user,
+	that will inform about change in the line state.
+	*/
+static int diva_line_info  (diva_strace_context_t* pLib,
+														int Channel,
+														diva_man_var_header_t* pVar) {
+	diva_man_var_header_t* cur;
+	int i, nr = Channel - 1;
+
+	for (i  = pLib->line_parse_entry_first[nr];
+			 i <= pLib->line_parse_entry_last[nr]; i++) {
+		if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+			if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+				diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+				return (-1);
+			}
+		} else {
+			diva_trace_error (pLib, -2 , __FILE__, __LINE__);
+			return (-1);
+		}
+	}
+
+	/*
+		We do not use first event to notify user - this is the event that is
+		generated as result of EVENT ON operation and is used only to initialize
+		internal variables of application
+
+		Exception is is if the line is "online". In this case we have to notify
+		user about this confition.
+		*/
+	if (pLib->line_init_event & (1L << nr)) {
+		diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE);
+	} else {
+		pLib->line_init_event |= (1L << nr);
+		if (strcmp (&pLib->lines[nr].Line[0], "Idle")) {
+			diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE);
+		}
+	}
+
+	return (0);
+}
+
+/*
+	Move position to next vatianle in the chain
+	*/
+static diva_man_var_header_t* get_next_var (diva_man_var_header_t* pVar) {
+	byte* msg   = (byte*)pVar;
+	byte* start;
+	int msg_length;
+
+	if (*msg != ESC) return NULL;
+
+	start = msg + 2;
+	msg_length = *(msg+1);
+	msg = (start+msg_length);
+
+	if (*msg != ESC) return NULL;
+
+	return ((diva_man_var_header_t*)msg);
+}
+
+/*
+	Move position to variable with given name
+	*/
+static diva_man_var_header_t* find_var (diva_man_var_header_t* pVar,
+																				const char* name) {
+	const char* path;
+
+	do {
+		path = (char*)&pVar->path_length+1;
+
+		if (!strncmp (name, path, pVar->path_length)) {
+			break;
+		}
+	} while ((pVar = get_next_var (pVar)));
+
+	return (pVar);
+}
+
+static void diva_create_line_parse_table  (diva_strace_context_t* pLib,
+																					 int Channel) {
+	diva_trace_line_state_t* pLine = &pLib->lines[Channel];
+	int nr = Channel+1;
+
+	if ((pLib->cur_parse_entry + LINE_PARSE_ENTRIES) >= pLib->parse_entries) {
+		diva_trace_error (pLib, -1, __FILE__, __LINE__);
+		return;
+	}
+
+	pLine->ChannelNumber = nr;
+
+	pLib->line_parse_entry_first[Channel] = pLib->cur_parse_entry;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Framing", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Framing[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Line", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Line[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Layer2", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer2[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Layer3", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer3[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Remote Address", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																								&pLine->RemoteAddress[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Remote SubAddr", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																								&pLine->RemoteSubAddress[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Local Address", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																								&pLine->LocalAddress[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Local SubAddr", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																								&pLine->LocalSubAddress[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\BC", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_BC;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\HLC", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_HLC;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\LLC", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_LLC;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Charges", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Charges;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Call Reference", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->CallReference;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Last Disc Cause", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																										&pLine->LastDisconnecCause;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\User ID", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->UserID[0];
+
+	pLib->line_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
+}
+
+static void diva_create_fax_parse_table (diva_strace_context_t* pLib,
+																				 int Channel) {
+	diva_trace_fax_state_t* pFax = &pLib->lines[Channel].fax;
+	int nr = Channel+1;
+
+	if ((pLib->cur_parse_entry + FAX_PARSE_ENTRIES) >= pLib->parse_entries) {
+		diva_trace_error (pLib, -1, __FILE__, __LINE__);
+		return;
+	}
+	pFax->ChannelNumber = nr;
+
+	pLib->fax_parse_entry_first[Channel] = pLib->cur_parse_entry;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Event", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Event;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Page Counter", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Page_Counter;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Features", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Features;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Station ID", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Station_ID[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Subaddress", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Subaddress[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Password", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Password[0];
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Speed", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Speed;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Resolution", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Resolution;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Paper Width", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Width;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Paper Length", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Length;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Scanline Time", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Scanline_Time;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\FAX\\Disc Reason", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Disc_Reason;
+
+	pLib->fax_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
+}
+
+static void diva_create_modem_parse_table (diva_strace_context_t* pLib,
+																					 int Channel) {
+	diva_trace_modem_state_t* pModem = &pLib->lines[Channel].modem;
+	int nr = Channel+1;
+
+	if ((pLib->cur_parse_entry + MODEM_PARSE_ENTRIES) >= pLib->parse_entries) {
+		diva_trace_error (pLib, -1, __FILE__, __LINE__);
+		return;
+	}
+	pModem->ChannelNumber = nr;
+
+	pLib->modem_parse_entry_first[Channel] = pLib->cur_parse_entry;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Event", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Event;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Norm", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Norm;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Options", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Options;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\TX Speed", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->TxSpeed;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\RX Speed", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxSpeed;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Roundtrip ms", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RoundtripMsec;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Symbol Rate", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SymbolRate;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\RX Level dBm", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxLeveldBm;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Echo Level dBm", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->EchoLeveldBm;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\SNR dB", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SNRdb;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\MAE", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->MAE;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Local Retrains", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalRetrains;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Remote Retrains", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteRetrains;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Local Resyncs", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalResyncs;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Remote Resyncs", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteResyncs;
+
+	sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+					 "State\\B%d\\Modem\\Disc Reason", nr);
+	pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->DiscReason;
+
+	pLib->modem_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
+}
+
+static void diva_create_parse_table (diva_strace_context_t* pLib) {
+	int i;
+
+	for (i = 0; i < pLib->Channels; i++) {
+		diva_create_line_parse_table  (pLib, i);
+		diva_create_modem_parse_table (pLib, i);
+		diva_create_fax_parse_table   (pLib, i);
+	}
+
+	pLib->statistic_parse_first = pLib->cur_parse_entry;
+
+	/*
+		Outgoing Calls
+		*/
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Outgoing Calls\\Calls");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.outg.Calls;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Outgoing Calls\\Connected");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.outg.Connected;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Outgoing Calls\\User Busy");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.outg.User_Busy;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Outgoing Calls\\No Answer");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.outg.No_Answer;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Outgoing Calls\\Wrong Number");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.outg.Wrong_Number;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Outgoing Calls\\Call Rejected");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.outg.Call_Rejected;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Outgoing Calls\\Other Failures");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.outg.Other_Failures;
+
+	/*
+		Incoming Calls
+		*/
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\Calls");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.Calls;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\Connected");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.Connected;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\User Busy");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.User_Busy;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\Call Rejected");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.Call_Rejected;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\Wrong Number");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.Wrong_Number;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\Incompatible Dst");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.Incompatible_Dst;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\Out of Order");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.Out_of_Order;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Incoming Calls\\Ignored");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.inc.Ignored;
+
+	/*
+		Modem Statistics
+		*/
+	pLib->mdm_statistic_parse_first = pLib->cur_parse_entry;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Normal");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Normal;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Unspecified");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Unspecified;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Busy Tone");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Busy_Tone;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Congestion");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Congestion;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Carr. Wait");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Carr_Wait;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Trn Timeout");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Trn_Timeout;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Incompat.");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Incompat;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc Frame Rej.");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_Frame_Rej;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\Modem\\Disc V42bis");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.mdm.Disc_V42bis;
+
+	pLib->mdm_statistic_parse_last  = pLib->cur_parse_entry - 1;
+
+	/*
+		Fax Statistics
+		*/
+	pLib->fax_statistic_parse_first = pLib->cur_parse_entry;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Normal");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Normal;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Not Ident.");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Not_Ident;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc No Response");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_No_Response;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Retries");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Retries;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Unexp. Msg.");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Unexp_Msg;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc No Polling.");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_No_Polling;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Training");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Training;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Unexpected");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Unexpected;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Application");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Application;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Incompat.");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Incompat;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc No Command");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_No_Command;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Long Msg");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Long_Msg;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Supervisor");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Supervisor;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc SUB SEP PWD");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_SUB_SEP_PWD;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Invalid Msg");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Invalid_Msg;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Page Coding");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Page_Coding;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc App Timeout");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_App_Timeout;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\FAX\\Disc Unspecified");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.fax.Disc_Unspecified;
+
+	pLib->fax_statistic_parse_last  = pLib->cur_parse_entry - 1;
+
+	/*
+		B-Layer1"
+		*/
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer1\\X-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b1.X_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer1\\X-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b1.X_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer1\\X-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b1.X_Errors;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer1\\R-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b1.R_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer1\\R-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b1.R_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer1\\R-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b1.R_Errors;
+
+	/*
+		B-Layer2
+		*/
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer2\\X-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b2.X_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer2\\X-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b2.X_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer2\\X-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b2.X_Errors;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer2\\R-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b2.R_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer2\\R-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b2.R_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\B-Layer2\\R-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.b2.R_Errors;
+
+	/*
+		D-Layer1
+		*/
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer1\\X-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d1.X_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer1\\X-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d1.X_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer1\\X-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d1.X_Errors;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer1\\R-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d1.R_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer1\\R-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d1.R_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer1\\R-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d1.R_Errors;
+
+	/*
+		D-Layer2
+		*/
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer2\\X-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d2.X_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer2\\X-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d2.X_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer2\\X-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d2.X_Errors;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer2\\R-Frames");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d2.R_Frames;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer2\\R-Bytes");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d2.R_Bytes;
+
+	strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+					"Statistics\\D-Layer2\\R-Errors");
+	pLib->parse_table[pLib->cur_parse_entry++].variable = \
+																		&pLib->InterfaceStat.d2.R_Errors;
+
+
+	pLib->statistic_parse_last  = pLib->cur_parse_entry - 1;
+}
+
+static void diva_trace_error (diva_strace_context_t* pLib,
+															int error, const char* file, int line) {
+	if (pLib->user_proc_table.error_notify_proc) {
+		(*(pLib->user_proc_table.error_notify_proc))(\
+																						pLib->user_proc_table.user_context,
+																						&pLib->instance, pLib->Adapter,
+																						error, file, line);
+	}
+}
+
+/*
+	Delivery notification to user
+	*/
+static void diva_trace_notify_user (diva_strace_context_t* pLib,
+														 int Channel,
+														 int notify_subject) {
+	if (pLib->user_proc_table.notify_proc) {
+		(*(pLib->user_proc_table.notify_proc))(pLib->user_proc_table.user_context,
+																					 &pLib->instance,
+																					 pLib->Adapter,
+																					 &pLib->lines[Channel],
+																					 notify_subject);
+	}
+}
+
+/*
+	Read variable value to they destination based on the variable type
+	*/
+static int diva_trace_read_variable (diva_man_var_header_t* pVar,
+																		 void* variable) {
+	switch (pVar->type) {
+		case 0x03: /* MI_ASCIIZ - syting                               */
+			return (diva_strace_read_asz  (pVar, (char*)variable));
+		case 0x04: /* MI_ASCII  - string                               */
+			return (diva_strace_read_asc  (pVar, (char*)variable));
+		case 0x05: /* MI_NUMBER - counted sequence of bytes            */
+			return (diva_strace_read_ie  (pVar, (diva_trace_ie_t*)variable));
+		case 0x81: /* MI_INT    - signed integer                       */
+			return (diva_strace_read_int (pVar, (int*)variable));
+		case 0x82: /* MI_UINT   - unsigned integer                     */
+			return (diva_strace_read_uint (pVar, (dword*)variable));
+		case 0x83: /* MI_HINT   - unsigned integer, hex representetion */
+			return (diva_strace_read_uint (pVar, (dword*)variable));
+		case 0x87: /* MI_BITFLD - unsigned integer, bit representation */
+			return (diva_strace_read_uint (pVar, (dword*)variable));
+	}
+
+	/*
+		This type of variable is not handled, indicate error
+		Or one problem in management interface, or in application recodeing
+		table, or this application should handle it.
+		*/
+	return (-1);
+}
+
+/*
+	Read signed integer to destination
+	*/
+static int diva_strace_read_int  (diva_man_var_header_t* pVar, int* var) {
+	byte* ptr = (char*)&pVar->path_length;
+	int value;
+
+	ptr += (pVar->path_length + 1);
+
+	switch (pVar->value_length) {
+		case 1:
+			value = *(char*)ptr;
+			break;
+
+		case 2:
+			value = (short)GET_WORD(ptr);
+			break;
+
+		case 4:
+			value = (int)GET_DWORD(ptr);
+			break;
+
+		default:
+			return (-1);
+	}
+
+	*var = value;
+
+	return (0);
+}
+
+static int diva_strace_read_uint (diva_man_var_header_t* pVar, dword* var) {
+	byte* ptr = (char*)&pVar->path_length;
+	dword value;
+
+	ptr += (pVar->path_length + 1);
+
+	switch (pVar->value_length) {
+		case 1:
+			value = (byte)(*ptr);
+			break;
+
+		case 2:
+			value = (word)GET_WORD(ptr);
+			break;
+
+		case 3:
+			value  = (dword)GET_DWORD(ptr);
+			value &= 0x00ffffff;
+			break;
+
+		case 4:
+			value = (dword)GET_DWORD(ptr);
+			break;
+
+		default:
+			return (-1);
+	}
+
+	*var = value;
+
+	return (0);
+}
+
+/*
+	Read zero terminated ASCII string
+	*/
+static int diva_strace_read_asz  (diva_man_var_header_t* pVar, char* var) {
+	char* ptr = (char*)&pVar->path_length;
+	int length;
+
+	ptr += (pVar->path_length + 1);
+
+	if (!(length = pVar->value_length)) {
+		length = strlen (ptr);
+	}
+	memcpy (var, ptr, length);
+	var[length] = 0;
+
+	return (0);
+}
+
+/*
+	Read counted (with leading length byte) ASCII string
+	*/
+static int diva_strace_read_asc  (diva_man_var_header_t* pVar, char* var) {
+	char* ptr = (char*)&pVar->path_length;
+
+	ptr += (pVar->path_length + 1);
+	memcpy (var, ptr+1, *ptr);
+	var[(int)*ptr] = 0;
+
+	return (0);
+}
+
+/*
+		Read one information element - i.e. one string of byte values with
+		one length byte in front
+	*/
+static int  diva_strace_read_ie  (diva_man_var_header_t* pVar,
+																	diva_trace_ie_t* var) {
+	char* ptr = (char*)&pVar->path_length;
+
+	ptr += (pVar->path_length + 1);
+
+	var->length = *ptr;
+	memcpy (&var->data[0], ptr+1, *ptr);
+
+	return (0);
+}
+
+static int SuperTraceSetAudioTap  (void* hLib, int Channel, int on) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+	if ((Channel < 1) || (Channel > pLib->Channels)) {
+		return (-1);
+	}
+	Channel--;
+
+	if (on) {
+		pLib->audio_tap_mask |=  (1L << Channel);
+	} else {
+		pLib->audio_tap_mask &= ~(1L << Channel);
+	}
+
+  /*
+    EYE patterns have TM_M_DATA set as additional
+    condition
+    */
+  if (pLib->audio_tap_mask) {
+    pLib->trace_event_mask |= TM_M_DATA;
+  } else {
+    pLib->trace_event_mask &= ~TM_M_DATA;
+  }
+
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceSetBChannel  (void* hLib, int Channel, int on) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+	if ((Channel < 1) || (Channel > pLib->Channels)) {
+		return (-1);
+	}
+	Channel--;
+
+	if (on) {
+		pLib->bchannel_trace_mask |=  (1L << Channel);
+	} else {
+		pLib->bchannel_trace_mask &= ~(1L << Channel);
+	}
+
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceSetDChannel  (void* hLib, int on) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+	if (on) {
+		pLib->trace_event_mask |= (TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1);
+	} else {
+		pLib->trace_event_mask &= ~(TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1);
+	}
+
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceSetInfo (void* hLib, int on) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+	if (on) {
+		pLib->trace_event_mask |= TM_STRING;
+	} else {
+		pLib->trace_event_mask &= ~TM_STRING;
+	}
+
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceClearCall (void* hLib, int Channel) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+	if ((Channel < 1) || (Channel > pLib->Channels)) {
+		return (-1);
+	}
+	Channel--;
+
+	pLib->clear_call_command |= (1L << Channel);
+
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+/*
+	Parse and update cumulative statistice
+	*/
+static int diva_ifc_statistics (diva_strace_context_t* pLib,
+																diva_man_var_header_t* pVar) {
+	diva_man_var_header_t* cur;
+	int i, one_updated = 0, mdm_updated = 0, fax_updated = 0;
+
+	for (i  = pLib->statistic_parse_first; i <= pLib->statistic_parse_last; i++) {
+		if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+			if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+				diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+				return (-1);
+			}
+			one_updated = 1;
+      if ((i >= pLib->mdm_statistic_parse_first) && (i <= pLib->mdm_statistic_parse_last)) {
+        mdm_updated = 1;
+      }
+      if ((i >= pLib->fax_statistic_parse_first) && (i <= pLib->fax_statistic_parse_last)) {
+        fax_updated = 1;
+      }
+		}
+	}
+
+	/*
+		We do not use first event to notify user - this is the event that is
+		generated as result of EVENT ON operation and is used only to initialize
+		internal variables of application
+		*/
+  if (mdm_updated) {
+		diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE);
+  } else if (fax_updated) {
+		diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE);
+  } else if (one_updated) {
+		diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE);
+	}
+
+	return (one_updated ? 0 : -1);
+}
+
+static int SuperTraceGetOutgoingCallStatistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->outgoing_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetIncomingCallStatistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->incoming_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetModemStatistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->modem_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetFaxStatistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->fax_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetBLayer1Statistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->b1_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetBLayer2Statistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->b2_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetDLayer1Statistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->d1_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetDLayer2Statistics (void* hLib) {
+	diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+	pLib->d2_ifc_stats = 1;
+	return (ScheduleNextTraceRequest (pLib));
+}
+
+dword DivaSTraceGetMemotyRequirement (int channels) {
+  dword parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \
+												 STAT_PARSE_ENTRIES + \
+												 LINE_PARSE_ENTRIES + 1) * channels;
+  return (sizeof(diva_strace_context_t) + \
+          (parse_entries * sizeof(diva_strace_path2action_t)));
+}
+
diff --git a/drivers/isdn/hardware/eicon/maintidi.h b/drivers/isdn/hardware/eicon/maintidi.h
new file mode 100644
index 0000000..4f06294
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/maintidi.h
@@ -0,0 +1,172 @@
+/*
+ *
+  Copyright (c) Eicon Networks, 2000.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    1.9
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_EICON_TRACE_IDI_IFC_H__
+#define __DIVA_EICON_TRACE_IDI_IFC_H__
+
+void* SuperTraceOpenAdapter   (int AdapterNumber);
+int   SuperTraceCloseAdapter  (void* AdapterHandle);
+int   SuperTraceWrite         (void* AdapterHandle,
+                               const void* data, int length);
+int   SuperTraceReadRequest   (void* AdapterHandle,const char* name,byte* data);
+int   SuperTraceGetNumberOfChannels (void* AdapterHandle);
+int   SuperTraceASSIGN        (void* AdapterHandle, byte* data);
+int   SuperTraceREMOVE        (void* AdapterHandle);
+int   SuperTraceTraceOnRequest(void* hAdapter, const char* name, byte* data);
+int   SuperTraceWriteVar (void* AdapterHandle,
+												byte* data,
+										 		const char* name,
+										 		void* var,
+										 		byte type,
+										 		byte var_length);
+int   SuperTraceExecuteRequest (void* AdapterHandle,
+																const char* name,
+																byte* data);
+
+typedef struct _diva_strace_path2action {
+	char               path[64]; /* Full path to variable            */
+	void*							 variable; /* Variable that will receive value */
+} diva_strace_path2action_t;
+
+#define DIVA_MAX_MANAGEMENT_TRANSFER_SIZE 4096
+
+typedef struct _diva_strace_context {
+	diva_strace_library_interface_t	instance;
+
+	int   Adapter;
+	void* hAdapter;
+
+	int Channels;
+	int	req_busy;
+
+  ENTITY   e;
+  IDI_CALL request;
+  BUFFERS  XData;
+  BUFFERS  RData;
+	byte buffer[DIVA_MAX_MANAGEMENT_TRANSFER_SIZE + 1];
+  int removal_state;
+  int general_b_ch_event;
+  int general_fax_event;
+  int general_mdm_event;
+
+	byte	rc_ok;
+
+	/*
+		Initialization request state machine
+		*/
+	int ChannelsTraceActive;
+	int ModemTraceActive;
+	int FaxTraceActive;
+	int IncomingCallsCallsActive;
+	int IncomingCallsConnectedActive;
+	int OutgoingCallsCallsActive;
+	int OutgoingCallsConnectedActive;
+
+	int trace_mask_init;
+	int audio_trace_init;
+	int bchannel_init;
+	int trace_length_init;
+	int	trace_on;
+	int trace_events_down;
+	int l1_trace;
+	int l2_trace;
+
+	/*
+		Trace\Event Enable
+		*/
+	word trace_event_mask;
+	word current_trace_event_mask;
+
+	dword audio_tap_mask;
+	dword current_audio_tap_mask;
+	dword current_eye_pattern_mask;
+	int   audio_tap_pending;
+	int   eye_pattern_pending;
+
+	dword bchannel_trace_mask;
+	dword current_bchannel_trace_mask;
+
+
+	diva_trace_line_state_t lines[30];
+
+	int	parse_entries;
+	int	cur_parse_entry;
+	diva_strace_path2action_t* parse_table;
+
+	diva_trace_library_user_interface_t user_proc_table;
+
+	int line_parse_entry_first[30];
+	int line_parse_entry_last[30];
+
+	int modem_parse_entry_first[30];
+	int modem_parse_entry_last[30];
+
+	int fax_parse_entry_first[30];
+	int fax_parse_entry_last[30];
+
+	int statistic_parse_first;
+	int statistic_parse_last;
+
+	int mdm_statistic_parse_first;
+	int mdm_statistic_parse_last;
+
+	int fax_statistic_parse_first;
+	int fax_statistic_parse_last;
+
+	dword	line_init_event;
+	dword	modem_init_event;
+	dword	fax_init_event;
+
+	dword	pending_line_status;
+	dword	pending_modem_status;
+	dword	pending_fax_status;
+
+	dword clear_call_command;
+
+	int outgoing_ifc_stats;
+	int incoming_ifc_stats;
+	int modem_ifc_stats;
+	int fax_ifc_stats;
+	int b1_ifc_stats;
+	int b2_ifc_stats;
+	int d1_ifc_stats;
+	int d2_ifc_stats;
+
+	diva_trace_interface_state_t Interface;
+	diva_ifc_statistics_t				 InterfaceStat;
+} diva_strace_context_t;
+
+typedef struct _diva_man_var_header {
+	byte   escape;
+	byte   length;
+	byte   management_id;
+	byte   type;
+	byte   attribute;
+	byte   status;
+	byte   value_length;
+	byte	 path_length;
+} diva_man_var_header_t;
+
+#endif
+
diff --git a/drivers/isdn/hardware/eicon/man_defs.h b/drivers/isdn/hardware/eicon/man_defs.h
new file mode 100644
index 0000000..cb4ef4c
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/man_defs.h
@@ -0,0 +1,133 @@
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    1.9
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+/* Definitions for use with the Management Information Element      */
+
+/*------------------------------------------------------------------*/
+/* Management information element                                   */
+/* ----------------------------------------------------------       */
+/* Byte     Coding            Comment                               */
+/* ----------------------------------------------------------       */
+/*    0 | 0 1 1 1 1 1 1 1 | ESC                                     */
+/*    1 | 0 x x x x x x x | Length of information element (m-1)     */
+/*    2 | 1 0 0 0 0 0 0 0 | Management Information Id               */
+/*    3 | x x x x x x x x | Type                                    */
+/*    4 | x x x x x x x x | Attribute                               */
+/*    5 | x x x x x x x x | Status                                  */
+/*    6 | x x x x x x x x | Variable Value Length (m-n)             */
+/*    7 | x x x x x x x x | Path / Variable Name String Length (n-8)*/
+/* 8..n | x x x x x x x x | Path/Node Name String separated by '\'  */
+/* n..m | x x x x x x x x | Variable content                        */
+/*------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------*/
+/* Type Field                                                       */
+/*                                                                  */
+/* MAN_READ:      not used                                          */
+/* MAN_WRITE:     not used                                          */
+/* MAN_EVENT_ON:  not used                                          */
+/* MAN_EVENT_OFF: not used                                          */
+/* MAN_INFO_IND:  type of variable                                  */
+/* MAN_EVENT_IND: type of variable                                  */
+/* MAN_TRACE_IND  not used                                          */
+/*------------------------------------------------------------------*/
+#define MI_DIR          0x01  /* Directory string (zero terminated) */
+#define MI_EXECUTE      0x02  /* Executable function (has no value) */
+#define MI_ASCIIZ       0x03  /* Zero terminated string             */
+#define MI_ASCII        0x04  /* String, first byte is length       */
+#define MI_NUMBER       0x05  /* Number string, first byte is length*/
+#define MI_TRACE        0x06  /* Trace information, format see below*/
+
+#define MI_FIXED_LENGTH 0x80  /* get length from MAN_INFO max_len   */
+#define MI_INT          0x81  /* number to display as signed int    */
+#define MI_UINT         0x82  /* number to display as unsigned int  */
+#define MI_HINT         0x83  /* number to display in hex format    */
+#define MI_HSTR         0x84  /* number to display as a hex string  */
+#define MI_BOOLEAN      0x85  /* number to display as boolean       */
+#define MI_IP_ADDRESS   0x86  /* number to display as IP address    */
+#define MI_BITFLD       0x87  /* number to display as bit field     */
+#define MI_SPID_STATE   0x88  /* state# of SPID initialisation      */
+
+/*------------------------------------------------------------------*/
+/* Attribute Field                                                  */
+/*                                                                  */
+/* MAN_READ:      not used                                          */
+/* MAN_WRITE:     not used                                          */
+/* MAN_EVENT_ON:  not used                                          */
+/* MAN_EVENT_OFF: not used                                          */
+/* MAN_INFO_IND:  set according to capabilities of that variable    */
+/* MAN_EVENT_IND: not used                                          */
+/* MAN_TRACE_IND  not used                                          */
+/*------------------------------------------------------------------*/
+#define MI_WRITE        0x01  /* Variable is writeable              */
+#define MI_EVENT        0x02  /* Variable can indicate changes      */
+
+/*------------------------------------------------------------------*/
+/* Status Field                                                     */
+/*                                                                  */
+/* MAN_READ:      not used                                          */
+/* MAN_WRITE:     not used                                          */
+/* MAN_EVENT_ON:  not used                                          */
+/* MAN_EVENT_OFF: not used                                          */
+/* MAN_INFO_IND:  set according to the actual status                */
+/* MAN_EVENT_IND: set according to the actual statu                 */
+/* MAN_TRACE_IND  not used                                          */
+/*------------------------------------------------------------------*/
+#define MI_LOCKED       0x01  /* write protected by another instance*/
+#define MI_EVENT_ON     0x02  /* Event logging switched on          */
+#define MI_PROTECTED    0x04  /* write protected by this instance   */
+
+/*------------------------------------------------------------------*/
+/* Data Format used for MAN_TRACE_IND (no MI-element used)          */
+/*------------------------------------------------------------------*/
+typedef struct mi_xlog_hdr_s MI_XLOG_HDR;
+struct mi_xlog_hdr_s
+{
+  unsigned long  time;   /* Timestamp in msec units                 */
+  unsigned short size;   /* Size of data that follows               */
+  unsigned short code;   /* code of trace event                     */
+};                       /* unspecified data follows this header    */
+
+/*------------------------------------------------------------------*/
+/* Trace mask definitions for trace events except B channel and     */
+/* debug trace events                                               */
+/*------------------------------------------------------------------*/
+#define TM_D_CHAN   0x0001  /* D-Channel        (D-.) Code 3,4      */
+#define TM_L_LAYER  0x0002  /* Low Layer        (LL)  Code 6,7      */
+#define TM_N_LAYER  0x0004  /* Network Layer    (N)   Code 14,15    */
+#define TM_DL_ERR   0x0008  /* Data Link Error  (MDL) Code 9        */
+#define TM_LAYER1   0x0010  /* Layer 1                Code 20       */
+#define TM_C_COMM   0x0020  /* Call Comment     (SIG) Code 5,21,22  */
+#define TM_M_DATA   0x0040  /* Modulation Data  (EYE) Code 23       */
+#define TM_STRING   0x0080  /* Sting data             Code 24       */
+#define TM_N_USED2  0x0100  /* not used                             */
+#define TM_N_USED3  0x0200  /* not used                             */
+#define TM_N_USED4  0x0400  /* not used                             */
+#define TM_N_USED5  0x0800  /* not used                             */
+#define TM_N_USED6  0x1000  /* not used                             */
+#define TM_N_USED7  0x2000  /* not used                             */
+#define TM_N_USED8  0x4000  /* not used                             */
+#define TM_REST     0x8000  /* Codes 10,11,12,13,16,18,19,128,129   */
+
+/*------ End of file -----------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mdm_msg.h b/drivers/isdn/hardware/eicon/mdm_msg.h
new file mode 100644
index 0000000..7a737e1
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/mdm_msg.h
@@ -0,0 +1,346 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __EICON_MDM_MSG_H__
+#define __EICON_MDM_MSG_H__
+#define DSP_UDATA_INDICATION_DCD_OFF  0x01
+#define DSP_UDATA_INDICATION_DCD_ON  0x02
+#define DSP_UDATA_INDICATION_CTS_OFF  0x03
+#define DSP_UDATA_INDICATION_CTS_ON  0x04
+/* =====================================================================
+DCD_OFF Message:
+  <word> time of DCD off (sampled from counter at 8kHz)
+DCD_ON Message:
+  <word> time of DCD on (sampled from counter at 8kHz)
+  <byte> connected norm
+  <word> connected options
+  <dword> connected speed (bit/s, max of tx and rx speed)
+  <word> roundtrip delay (ms)
+  <dword> connected speed tx (bit/s)
+  <dword> connected speed rx (bit/s)
+  Size of this message == 19 bytes, but we will receive only 11
+  ===================================================================== */
+#define DSP_CONNECTED_NORM_UNSPECIFIED      0
+#define DSP_CONNECTED_NORM_V21              1
+#define DSP_CONNECTED_NORM_V23              2
+#define DSP_CONNECTED_NORM_V22              3
+#define DSP_CONNECTED_NORM_V22_BIS          4
+#define DSP_CONNECTED_NORM_V32_BIS          5
+#define DSP_CONNECTED_NORM_V34              6
+#define DSP_CONNECTED_NORM_V8               7
+#define DSP_CONNECTED_NORM_BELL_212A        8
+#define DSP_CONNECTED_NORM_BELL_103         9
+#define DSP_CONNECTED_NORM_V29_LEASED_LINE  10
+#define DSP_CONNECTED_NORM_V33_LEASED_LINE  11
+#define DSP_CONNECTED_NORM_V90              12
+#define DSP_CONNECTED_NORM_V21_CH2          13
+#define DSP_CONNECTED_NORM_V27_TER          14
+#define DSP_CONNECTED_NORM_V29              15
+#define DSP_CONNECTED_NORM_V33              16
+#define DSP_CONNECTED_NORM_V17              17
+#define DSP_CONNECTED_NORM_V32              18
+#define DSP_CONNECTED_NORM_K56_FLEX         19
+#define DSP_CONNECTED_NORM_X2               20
+#define DSP_CONNECTED_NORM_V18              21
+#define DSP_CONNECTED_NORM_V18_LOW_HIGH     22
+#define DSP_CONNECTED_NORM_V18_HIGH_LOW     23
+#define DSP_CONNECTED_NORM_V21_LOW_HIGH     24
+#define DSP_CONNECTED_NORM_V21_HIGH_LOW     25
+#define DSP_CONNECTED_NORM_BELL103_LOW_HIGH 26
+#define DSP_CONNECTED_NORM_BELL103_HIGH_LOW 27
+#define DSP_CONNECTED_NORM_V23_75_1200      28
+#define DSP_CONNECTED_NORM_V23_1200_75      29
+#define DSP_CONNECTED_NORM_EDT_110          30
+#define DSP_CONNECTED_NORM_BAUDOT_45        31
+#define DSP_CONNECTED_NORM_BAUDOT_47        32
+#define DSP_CONNECTED_NORM_BAUDOT_50        33
+#define DSP_CONNECTED_NORM_DTMF             34
+#define DSP_CONNECTED_NORM_V18_RESERVED_13  35
+#define DSP_CONNECTED_NORM_V18_RESERVED_14  36
+#define DSP_CONNECTED_NORM_V18_RESERVED_15  37
+#define DSP_CONNECTED_NORM_VOWN             38
+#define DSP_CONNECTED_NORM_V23_OFF_HOOK     39
+#define DSP_CONNECTED_NORM_V23_ON_HOOK      40
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_3  41
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_4  42
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_5  43
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_6  44
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_7  45
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_8  46
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_9  47
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_10 48
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_11 49
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_12 50
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_13 51
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_14 52
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_15 53
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_16 54
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_17 55
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_18 56
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_19 57
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_20 58
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_21 59
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_22 60
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_23 61
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_24 62
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_25 63
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_26 64
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_27 65
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_28 66
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_29 67
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_30 68
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_31 69
+#define DSP_CONNECTED_OPTION_TRELLIS             0x0001
+#define DSP_CONNECTED_OPTION_V42_TRANS           0x0002
+#define DSP_CONNECTED_OPTION_V42_LAPM            0x0004
+#define DSP_CONNECTED_OPTION_SHORT_TRAIN         0x0008
+#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010
+#define DSP_CONNECTED_OPTION_V42BIS              0x0020
+#define DSP_CONNECTED_OPTION_MNP2                0x0040
+#define DSP_CONNECTED_OPTION_MNP3                0x0080
+#define DSP_CONNECTED_OPTION_MNP4                0x00c0
+#define DSP_CONNECTED_OPTION_MNP5                0x0100
+#define DSP_CONNECTED_OPTION_MNP10               0x0200
+#define DSP_CONNECTED_OPTION_MASK_V42            0x0024
+#define DSP_CONNECTED_OPTION_MASK_MNP            0x03c0
+#define DSP_CONNECTED_OPTION_MASK_ERROR_CORRECT  0x03e4
+#define DSP_CONNECTED_OPTION_MASK_COMPRESSION    0x0320
+#define DSP_UDATA_INDICATION_DISCONNECT         5
+/*
+returns:
+  <byte> cause
+*/
+/* ==========================================================
+    DLC: B2 modem configuration
+   ========================================================== */
+/*
+Fields in assign DLC information element for modem protocol V.42/MNP:
+  <byte> length of information element
+  <word> information field length
+  <byte> address A       (not used, default 3)
+  <byte> address B       (not used, default 1)
+  <byte> modulo mode     (not used, default 7)
+  <byte> window size     (not used, default 7)
+  <word> XID length      (not used, default 0)
+  ...    XID information (not used, default empty)
+  <byte> modem protocol negotiation options
+  <byte> modem protocol options
+  <byte> modem protocol break configuration
+  <byte> modem protocol application options
+*/
+#define DLC_MODEMPROT_DISABLE_V42_V42BIS     0x01
+#define DLC_MODEMPROT_DISABLE_MNP_MNP5       0x02
+#define DLC_MODEMPROT_REQUIRE_PROTOCOL       0x04
+#define DLC_MODEMPROT_DISABLE_V42_DETECT     0x08
+#define DLC_MODEMPROT_DISABLE_COMPRESSION    0x10
+#define DLC_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x20
+#define DLC_MODEMPROT_NO_PROTOCOL_IF_1200    0x01
+#define DLC_MODEMPROT_BUFFER_IN_V42_DETECT   0x02
+#define DLC_MODEMPROT_DISABLE_V42_SREJ       0x04
+#define DLC_MODEMPROT_DISABLE_MNP3           0x08
+#define DLC_MODEMPROT_DISABLE_MNP4           0x10
+#define DLC_MODEMPROT_DISABLE_MNP10          0x20
+#define DLC_MODEMPROT_NO_PROTOCOL_IF_V22BIS  0x40
+#define DLC_MODEMPROT_NO_PROTOCOL_IF_V32BIS  0x80
+#define DLC_MODEMPROT_BREAK_DISABLED         0x00
+#define DLC_MODEMPROT_BREAK_NORMAL           0x01
+#define DLC_MODEMPROT_BREAK_EXPEDITED        0x02
+#define DLC_MODEMPROT_BREAK_DESTRUCTIVE      0x03
+#define DLC_MODEMPROT_BREAK_CONFIG_MASK      0x03
+#define DLC_MODEMPROT_APPL_EARLY_CONNECT     0x01
+#define DLC_MODEMPROT_APPL_PASS_INDICATIONS  0x02
+/* ==========================================================
+    CAI parameters used for the modem L1 configuration
+   ========================================================== */
+/*
+Fields in assign CAI information element:
+  <byte> length of information element
+  <byte> info field and B-channel hardware
+  <byte> rate adaptation bit rate
+  <byte> async framing parameters
+  <byte> reserved
+  <word> packet length
+  <byte> modem line taking options
+  <byte> modem modulation negotiation parameters
+  <byte> modem modulation options
+  <byte> modem disabled modulations mask low
+  <byte> modem disabled modulations mask high
+  <byte> modem enabled modulations mask
+  <word> modem min TX speed
+  <word> modem max TX speed
+  <word> modem min RX speed
+  <word> modem max RX speed
+  <byte> modem disabled symbol rates mask
+  <byte> modem info options mask
+  <byte> modem transmit level adjust
+  <byte> modem speaker parameters
+  <word> modem private debug config
+  <struct> modem reserved
+  <struct> v18 config parameters
+  <struct> v18 probing sequence
+  <struct> v18 probing message
+*/
+#define DSP_CAI_HARDWARE_HDLC_64K          0x05
+#define DSP_CAI_HARDWARE_HDLC_56K          0x08
+#define DSP_CAI_HARDWARE_TRANSP            0x09
+#define DSP_CAI_HARDWARE_V110_SYNC         0x0c
+#define DSP_CAI_HARDWARE_V110_ASYNC        0x0d
+#define DSP_CAI_HARDWARE_HDLC_128K         0x0f
+#define DSP_CAI_HARDWARE_FAX               0x10
+#define DSP_CAI_HARDWARE_MODEM_ASYNC       0x11
+#define DSP_CAI_HARDWARE_MODEM_SYNC        0x12
+#define DSP_CAI_HARDWARE_V110_HDLCA        0x13
+#define DSP_CAI_HARDWARE_ADVANCED_VOICE    0x14
+#define DSP_CAI_HARDWARE_TRANSP_DTMF       0x16
+#define DSP_CAI_HARDWARE_DTMF_VOICE_ISDN   0x17
+#define DSP_CAI_HARDWARE_DTMF_VOICE_LOCAL  0x18
+#define DSP_CAI_HARDWARE_MASK              0x3f
+#define DSP_CAI_ENABLE_INFO_INDICATIONS    0x80
+#define DSP_CAI_RATE_ADAPTATION_300        0x00
+#define DSP_CAI_RATE_ADAPTATION_600        0x01
+#define DSP_CAI_RATE_ADAPTATION_1200       0x02
+#define DSP_CAI_RATE_ADAPTATION_2400       0x03
+#define DSP_CAI_RATE_ADAPTATION_4800       0x04
+#define DSP_CAI_RATE_ADAPTATION_9600       0x05
+#define DSP_CAI_RATE_ADAPTATION_19200      0x06
+#define DSP_CAI_RATE_ADAPTATION_38400      0x07
+#define DSP_CAI_RATE_ADAPTATION_48000      0x08
+#define DSP_CAI_RATE_ADAPTATION_56000      0x09
+#define DSP_CAI_RATE_ADAPTATION_7200       0x0a
+#define DSP_CAI_RATE_ADAPTATION_14400      0x0b
+#define DSP_CAI_RATE_ADAPTATION_28800      0x0c
+#define DSP_CAI_RATE_ADAPTATION_12000      0x0d
+#define DSP_CAI_RATE_ADAPTATION_1200_75    0x0e
+#define DSP_CAI_RATE_ADAPTATION_75_1200    0x0f
+#define DSP_CAI_RATE_ADAPTATION_MASK       0x0f
+#define DSP_CAI_ASYNC_PARITY_ENABLE        0x01
+#define DSP_CAI_ASYNC_PARITY_SPACE         0x00
+#define DSP_CAI_ASYNC_PARITY_ODD           0x02
+#define DSP_CAI_ASYNC_PARITY_EVEN          0x04
+#define DSP_CAI_ASYNC_PARITY_MARK          0x06
+#define DSP_CAI_ASYNC_PARITY_MASK          0x06
+#define DSP_CAI_ASYNC_ONE_STOP_BIT         0x00
+#define DSP_CAI_ASYNC_TWO_STOP_BITS        0x20
+#define DSP_CAI_ASYNC_CHAR_LENGTH_8        0x00
+#define DSP_CAI_ASYNC_CHAR_LENGTH_7        0x40
+#define DSP_CAI_ASYNC_CHAR_LENGTH_6        0x80
+#define DSP_CAI_ASYNC_CHAR_LENGTH_5        0xc0
+#define DSP_CAI_ASYNC_CHAR_LENGTH_MASK     0xc0
+#define DSP_CAI_MODEM_LEASED_LINE_MODE     0x01
+#define DSP_CAI_MODEM_4_WIRE_OPERATION     0x02
+#define DSP_CAI_MODEM_DISABLE_BUSY_DETECT  0x04
+#define DSP_CAI_MODEM_DISABLE_CALLING_TONE 0x08
+#define DSP_CAI_MODEM_DISABLE_ANSWER_TONE  0x10
+#define DSP_CAI_MODEM_ENABLE_DIAL_TONE_DET 0x20
+#define DSP_CAI_MODEM_USE_POTS_INTERFACE   0x40
+#define DSP_CAI_MODEM_FORCE_RAY_TAYLOR_FAX 0x80
+#define DSP_CAI_MODEM_NEGOTIATE_HIGHEST    0x00
+#define DSP_CAI_MODEM_NEGOTIATE_DISABLED   0x01
+#define DSP_CAI_MODEM_NEGOTIATE_IN_CLASS   0x02
+#define DSP_CAI_MODEM_NEGOTIATE_V100       0x03
+#define DSP_CAI_MODEM_NEGOTIATE_V8         0x04
+#define DSP_CAI_MODEM_NEGOTIATE_V8BIS      0x05
+#define DSP_CAI_MODEM_NEGOTIATE_MASK       0x07
+#define DSP_CAI_MODEM_GUARD_TONE_NONE      0x00
+#define DSP_CAI_MODEM_GUARD_TONE_550HZ     0x40
+#define DSP_CAI_MODEM_GUARD_TONE_1800HZ    0x80
+#define DSP_CAI_MODEM_GUARD_TONE_MASK      0xc0
+#define DSP_CAI_MODEM_DISABLE_RETRAIN      0x01
+#define DSP_CAI_MODEM_DISABLE_STEPUPDOWN   0x02
+#define DSP_CAI_MODEM_DISABLE_SPLIT_SPEED  0x04
+#define DSP_CAI_MODEM_DISABLE_TRELLIS      0x08
+#define DSP_CAI_MODEM_ALLOW_RDL_TEST_LOOP  0x10
+#define DSP_CAI_MODEM_DISABLE_FLUSH_TIMER  0x40
+#define DSP_CAI_MODEM_REVERSE_DIRECTION    0x80
+#define DSP_CAI_MODEM_DISABLE_V21          0x01
+#define DSP_CAI_MODEM_DISABLE_V23          0x02
+#define DSP_CAI_MODEM_DISABLE_V22          0x04
+#define DSP_CAI_MODEM_DISABLE_V22BIS       0x08
+#define DSP_CAI_MODEM_DISABLE_V32          0x10
+#define DSP_CAI_MODEM_DISABLE_V32BIS       0x20
+#define DSP_CAI_MODEM_DISABLE_V34          0x40
+#define DSP_CAI_MODEM_DISABLE_V90          0x80
+#define DSP_CAI_MODEM_DISABLE_BELL103      0x01
+#define DSP_CAI_MODEM_DISABLE_BELL212A     0x02
+#define DSP_CAI_MODEM_DISABLE_VFC          0x04
+#define DSP_CAI_MODEM_DISABLE_K56FLEX      0x08
+#define DSP_CAI_MODEM_DISABLE_X2           0x10
+#define DSP_CAI_MODEM_ENABLE_V29FDX        0x01
+#define DSP_CAI_MODEM_ENABLE_V33           0x02
+#define DSP_CAI_MODEM_DISABLE_2400_SYMBOLS 0x01
+#define DSP_CAI_MODEM_DISABLE_2743_SYMBOLS 0x02
+#define DSP_CAI_MODEM_DISABLE_2800_SYMBOLS 0x04
+#define DSP_CAI_MODEM_DISABLE_3000_SYMBOLS 0x08
+#define DSP_CAI_MODEM_DISABLE_3200_SYMBOLS 0x10
+#define DSP_CAI_MODEM_DISABLE_3429_SYMBOLS 0x20
+#define DSP_CAI_MODEM_DISABLE_TX_REDUCTION 0x01
+#define DSP_CAI_MODEM_DISABLE_PRECODING    0x02
+#define DSP_CAI_MODEM_DISABLE_PREEMPHASIS  0x04
+#define DSP_CAI_MODEM_DISABLE_SHAPING      0x08
+#define DSP_CAI_MODEM_DISABLE_NONLINEAR_EN 0x10
+#define DSP_CAI_MODEM_SPEAKER_OFF          0x00
+#define DSP_CAI_MODEM_SPEAKER_DURING_TRAIN 0x01
+#define DSP_CAI_MODEM_SPEAKER_TIL_CONNECT  0x02
+#define DSP_CAI_MODEM_SPEAKER_ALWAYS_ON    0x03
+#define DSP_CAI_MODEM_SPEAKER_CONTROL_MASK 0x03
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_MIN   0x00
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_LOW   0x04
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_HIGH  0x08
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_MAX   0x0c
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_MASK  0x0c
+/* ==========================================================
+    DCD/CTS State
+   ========================================================== */
+#define MDM_WANT_CONNECT_B3_ACTIVE_I  0x01
+#define MDM_NCPI_VALID                0x02
+#define MDM_NCPI_CTS_ON_RECEIVED      0x04
+#define MDM_NCPI_DCD_ON_RECEIVED      0x08
+/* ==========================================================
+    CAPI NCPI Constants
+   ========================================================== */
+#define MDM_NCPI_ECM_V42              0x0001
+#define MDM_NCPI_ECM_MNP              0x0002
+#define MDM_NCPI_TRANSPARENT          0x0004
+#define MDM_NCPI_COMPRESSED           0x0010
+/* ==========================================================
+    CAPI B2 Config Constants
+   ========================================================== */
+#define MDM_B2_DISABLE_V42bis         0x0001
+#define MDM_B2_DISABLE_MNP            0x0002
+#define MDM_B2_DISABLE_TRANS          0x0004
+#define MDM_B2_DISABLE_V42            0x0008
+#define MDM_B2_DISABLE_COMP           0x0010
+/* ==========================================================
+    CAPI B1 Config Constants
+   ========================================================== */
+#define MDM_CAPI_DISABLE_RETRAIN      0x0001
+#define MDM_CAPI_DISABLE_RING_TONE    0x0002
+#define MDM_CAPI_GUARD_1800           0x0004
+#define MDM_CAPI_GUARD_550            0x0008
+#define MDM_CAPI_NEG_V8               0x0003
+#define MDM_CAPI_NEG_V100             0x0002
+#define MDM_CAPI_NEG_MOD_CLASS        0x0001
+#define MDM_CAPI_NEG_DISABLED         0x0000
+#endif
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
new file mode 100644
index 0000000..f9b00f1
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/message.c
@@ -0,0 +1,15047 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "capi20.h"
+#include "divacapi.h"
+#include "mdm_msg.h"
+#include "divasync.h"
+
+
+
+#define FILE_ "MESSAGE.C"
+#define dprintf
+
+
+
+
+
+
+
+
+
+/*------------------------------------------------------------------*/
+/* This is options supported for all adapters that are server by    */
+/* XDI driver. Allo it is not necessary to ask it from every adapter*/
+/* and it is not necessary to save it separate for every adapter    */
+/* Macrose defined here have only local meaning                     */
+/*------------------------------------------------------------------*/
+static dword diva_xdi_extended_features = 0;
+
+#define DIVA_CAPI_USE_CMA                 0x00000001
+#define DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR  0x00000002
+#define DIVA_CAPI_XDI_PROVIDES_NO_CANCEL  0x00000004
+#define DIVA_CAPI_XDI_PROVIDES_RX_DMA     0x00000008
+
+/*
+  CAPI can request to process all return codes self only if:
+  protocol code supports this && xdi supports this
+ */
+#define DIVA_CAPI_SUPPORTS_NO_CANCEL(__a__)   (((__a__)->manufacturer_features&MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)&&    ((__a__)->manufacturer_features & MANUFACTURER_FEATURE_OK_FC_LABEL) &&     (diva_xdi_extended_features   & DIVA_CAPI_XDI_PROVIDES_NO_CANCEL))
+
+/*------------------------------------------------------------------*/
+/* local function prototypes                                        */
+/*------------------------------------------------------------------*/
+
+static void group_optimization(DIVA_CAPI_ADAPTER   * a, PLCI   * plci);
+static void set_group_ind_mask (PLCI   *plci);
+static void clear_group_ind_mask_bit (PLCI   *plci, word b);
+static byte test_group_ind_mask_bit (PLCI   *plci, word b);
+void AutomaticLaw(DIVA_CAPI_ADAPTER   *);
+word CapiRelease(word);
+word CapiRegister(word);
+word api_put(APPL   *, CAPI_MSG   *);
+static word api_parse(byte   *, word, byte *, API_PARSE *);
+static void api_save_msg(API_PARSE   *in, byte *format, API_SAVE   *out);
+static void api_load_msg(API_SAVE   *in, API_PARSE   *out);
+
+word api_remove_start(void);
+void api_remove_complete(void);
+
+static void plci_remove(PLCI   *);
+static void diva_get_extended_adapter_features (DIVA_CAPI_ADAPTER  * a);
+static void diva_ask_for_xdi_sdram_bar (DIVA_CAPI_ADAPTER  *, IDI_SYNC_REQ  *);
+
+void   callback(ENTITY   *);
+
+static void control_rc(PLCI   *, byte, byte, byte, byte, byte);
+static void data_rc(PLCI   *, byte);
+static void data_ack(PLCI   *, byte);
+static void sig_ind(PLCI   *);
+static void SendInfo(PLCI   *, dword, byte   * *, byte);
+static void SendSetupInfo(APPL   *, PLCI   *, dword, byte   * *, byte);
+static void SendSSExtInd(APPL   *, PLCI   * plci, dword Id, byte   * * parms);
+
+static void VSwitchReqInd(PLCI   *plci, dword Id, byte   **parms);
+
+static void nl_ind(PLCI   *);
+
+static byte connect_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte connect_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte connect_a_res(dword,word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte disconnect_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte disconnect_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte listen_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte info_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte info_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte alert_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte facility_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte facility_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte connect_b3_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte connect_b3_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte connect_b3_a_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte disconnect_b3_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte disconnect_b3_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte data_b3_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte data_b3_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte reset_b3_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte reset_b3_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte connect_b3_t90_a_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte select_b_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte manufacturer_req(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+static byte manufacturer_res(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+
+static word get_plci(DIVA_CAPI_ADAPTER   *);
+static void add_p(PLCI   *, byte, byte   *);
+static void add_s(PLCI   * plci, byte code, API_PARSE * p);
+static void add_ss(PLCI   * plci, byte code, API_PARSE * p);
+static void add_ie(PLCI   * plci, byte code, byte   * p, word p_length);
+static void add_d(PLCI   *, word, byte   *);
+static void add_ai(PLCI   *, API_PARSE *);
+static word add_b1(PLCI   *, API_PARSE *, word, word);
+static word add_b23(PLCI   *, API_PARSE *);
+static word add_modem_b23 (PLCI  * plci, API_PARSE* bp_parms);
+static void sig_req(PLCI   *, byte, byte);
+static void nl_req_ncci(PLCI   *, byte, byte);
+static void send_req(PLCI   *);
+static void send_data(PLCI   *);
+static word plci_remove_check(PLCI   *);
+static void listen_check(DIVA_CAPI_ADAPTER   *);
+static byte AddInfo(byte   **, byte   **, byte   *, byte *);
+static byte getChannel(API_PARSE *);
+static void IndParse(PLCI   *, word *, byte   **, byte);
+static byte ie_compare(byte   *, byte *);
+static word find_cip(DIVA_CAPI_ADAPTER   *, byte   *, byte   *);
+static word CPN_filter_ok(byte   *cpn,DIVA_CAPI_ADAPTER   *,word);
+
+/*
+  XON protocol helpers
+  */
+static void channel_flow_control_remove (PLCI   * plci);
+static void channel_x_off (PLCI   * plci, byte ch, byte flag);
+static void channel_x_on (PLCI   * plci, byte ch);
+static void channel_request_xon (PLCI   * plci, byte ch);
+static void channel_xmit_xon (PLCI   * plci);
+static int channel_can_xon (PLCI   * plci, byte ch);
+static void channel_xmit_extended_xon (PLCI   * plci);
+
+static byte SendMultiIE(PLCI   * plci, dword Id, byte   * * parms, byte ie_type, dword info_mask, byte setupParse);
+static word AdvCodecSupport(DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, byte);
+static void CodecIdCheck(DIVA_CAPI_ADAPTER   *, PLCI   *);
+static void SetVoiceChannel(PLCI   *, byte   *, DIVA_CAPI_ADAPTER   * );
+static void VoiceChannelOff(PLCI   *plci);
+static void adv_voice_write_coefs (PLCI   *plci, word write_command);
+static void adv_voice_clear_config (PLCI   *plci);
+
+static word get_b1_facilities (PLCI   * plci, byte b1_resource);
+static byte add_b1_facilities (PLCI   * plci, byte b1_resource, word b1_facilities);
+static void adjust_b1_facilities (PLCI   *plci, byte new_b1_resource, word new_b1_facilities);
+static word adjust_b_process (dword Id, PLCI   *plci, byte Rc);
+static void adjust_b1_resource (dword Id, PLCI   *plci, API_SAVE   *bp_msg, word b1_facilities, word internal_command);
+static void adjust_b_restore (dword Id, PLCI   *plci, byte Rc);
+static void reset_b3_command (dword Id, PLCI   *plci, byte Rc);
+static void select_b_command (dword Id, PLCI   *plci, byte Rc);
+static void fax_connect_ack_command (dword Id, PLCI   *plci, byte Rc);
+static void fax_edata_ack_command (dword Id, PLCI   *plci, byte Rc);
+static void fax_connect_info_command (dword Id, PLCI   *plci, byte Rc);
+static void fax_adjust_b23_command (dword Id, PLCI   *plci, byte Rc);
+static void fax_disconnect_command (dword Id, PLCI   *plci, byte Rc);
+static void hold_save_command (dword Id, PLCI   *plci, byte Rc);
+static void retrieve_restore_command (dword Id, PLCI   *plci, byte Rc);
+static void init_b1_config (PLCI   *plci);
+static void clear_b1_config (PLCI   *plci);
+
+static void dtmf_command (dword Id, PLCI   *plci, byte Rc);
+static byte dtmf_request (dword Id, word Number, DIVA_CAPI_ADAPTER   *a, PLCI   *plci, APPL   *appl, API_PARSE *msg);
+static void dtmf_confirmation (dword Id, PLCI   *plci);
+static void dtmf_indication (dword Id, PLCI   *plci, byte   *msg, word length);
+static void dtmf_parameter_write (PLCI   *plci);
+
+
+static void mixer_set_bchannel_id_esc (PLCI   *plci, byte bchannel_id);
+static void mixer_set_bchannel_id (PLCI   *plci, byte   *chi);
+static void mixer_clear_config (PLCI   *plci);
+static void mixer_notify_update (PLCI   *plci, byte others);
+static void mixer_command (dword Id, PLCI   *plci, byte Rc);
+static byte mixer_request (dword Id, word Number, DIVA_CAPI_ADAPTER   *a, PLCI   *plci, APPL   *appl, API_PARSE *msg);
+static void mixer_indication_coefs_set (dword Id, PLCI   *plci);
+static void mixer_indication_xconnect_from (dword Id, PLCI   *plci, byte   *msg, word length);
+static void mixer_indication_xconnect_to (dword Id, PLCI   *plci, byte   *msg, word length);
+static void mixer_remove (PLCI   *plci);
+
+
+static void ec_command (dword Id, PLCI   *plci, byte Rc);
+static byte ec_request (dword Id, word Number, DIVA_CAPI_ADAPTER   *a, PLCI   *plci, APPL   *appl, API_PARSE *msg);
+static void ec_indication (dword Id, PLCI   *plci, byte   *msg, word length);
+
+
+static void rtp_connect_b3_req_command (dword Id, PLCI   *plci, byte Rc);
+static void rtp_connect_b3_res_command (dword Id, PLCI   *plci, byte Rc);
+
+
+static int  diva_get_dma_descriptor  (PLCI   *plci, dword   *dma_magic);
+static void diva_free_dma_descriptor (PLCI   *plci, int nr);
+
+/*------------------------------------------------------------------*/
+/* external function prototypes                                     */
+/*------------------------------------------------------------------*/
+
+extern byte MapController (byte);
+extern byte UnMapController (byte);
+#define MapId(Id) (((Id) & 0xffffff00L) | MapController ((byte)(Id)))
+#define UnMapId(Id) (((Id) & 0xffffff00L) | UnMapController ((byte)(Id)))
+
+void   sendf(APPL   *, word, dword, word, byte *, ...);
+void   * TransmitBufferSet(APPL   * appl, dword ref);
+void   * TransmitBufferGet(APPL   * appl, void   * p);
+void TransmitBufferFree(APPL   * appl, void   * p);
+void   * ReceiveBufferGet(APPL   * appl, int Num);
+
+int fax_head_line_time (char *buffer);
+
+
+/*------------------------------------------------------------------*/
+/* Global data definitions                                          */
+/*------------------------------------------------------------------*/
+extern byte max_adapter;
+extern byte max_appl;
+extern DIVA_CAPI_ADAPTER   * adapter;
+extern APPL   * application;
+
+
+
+
+
+
+
+static byte remove_started = FALSE;
+static PLCI dummy_plci;
+
+
+static struct _ftable {
+  word command;
+  byte * format;
+  byte (* function)(dword, word, DIVA_CAPI_ADAPTER   *, PLCI   *, APPL   *, API_PARSE *);
+} ftable[] = {
+  {_DATA_B3_R,                          "dwww",         data_b3_req},
+  {_DATA_B3_I|RESPONSE,                 "w",            data_b3_res},
+  {_INFO_R,                             "ss",           info_req},
+  {_INFO_I|RESPONSE,                    "",             info_res},
+  {_CONNECT_R,                          "wsssssssss",   connect_req},
+  {_CONNECT_I|RESPONSE,                 "wsssss",       connect_res},
+  {_CONNECT_ACTIVE_I|RESPONSE,          "",             connect_a_res},
+  {_DISCONNECT_R,                       "s",            disconnect_req},
+  {_DISCONNECT_I|RESPONSE,              "",             disconnect_res},
+  {_LISTEN_R,                           "dddss",        listen_req},
+  {_ALERT_R,                            "s",            alert_req},
+  {_FACILITY_R,                         "ws",           facility_req},
+  {_FACILITY_I|RESPONSE,                "ws",           facility_res},
+  {_CONNECT_B3_R,                       "s",            connect_b3_req},
+  {_CONNECT_B3_I|RESPONSE,              "ws",           connect_b3_res},
+  {_CONNECT_B3_ACTIVE_I|RESPONSE,       "",             connect_b3_a_res},
+  {_DISCONNECT_B3_R,                    "s",            disconnect_b3_req},
+  {_DISCONNECT_B3_I|RESPONSE,           "",             disconnect_b3_res},
+  {_RESET_B3_R,                         "s",            reset_b3_req},
+  {_RESET_B3_I|RESPONSE,                "",             reset_b3_res},
+  {_CONNECT_B3_T90_ACTIVE_I|RESPONSE,   "ws",           connect_b3_t90_a_res},
+  {_CONNECT_B3_T90_ACTIVE_I|RESPONSE,   "",             connect_b3_t90_a_res},
+  {_SELECT_B_REQ,                       "s",            select_b_req},
+  {_MANUFACTURER_R,                     "dws",          manufacturer_req},
+  {_MANUFACTURER_I|RESPONSE,            "dws",          manufacturer_res},
+  {_MANUFACTURER_I|RESPONSE,            "",             manufacturer_res}
+};
+
+static byte * cip_bc[29][2] = {
+  { "",                     ""                     }, /* 0 */
+  { "\x03\x80\x90\xa3",     "\x03\x80\x90\xa2"     }, /* 1 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 2 */
+  { "\x02\x89\x90",         "\x02\x89\x90"         }, /* 3 */
+  { "\x03\x90\x90\xa3",     "\x03\x90\x90\xa2"     }, /* 4 */
+  { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 5 */
+  { "\x02\x98\x90",         "\x02\x98\x90"         }, /* 6 */
+  { "\x04\x88\xc0\xc6\xe6", "\x04\x88\xc0\xc6\xe6" }, /* 7 */
+  { "\x04\x88\x90\x21\x8f", "\x04\x88\x90\x21\x8f" }, /* 8 */
+  { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 9 */
+  { "",                     ""                     }, /* 10 */
+  { "",                     ""                     }, /* 11 */
+  { "",                     ""                     }, /* 12 */
+  { "",                     ""                     }, /* 13 */
+  { "",                     ""                     }, /* 14 */
+  { "",                     ""                     }, /* 15 */
+
+  { "\x03\x80\x90\xa3",     "\x03\x80\x90\xa2"     }, /* 16 */
+  { "\x03\x90\x90\xa3",     "\x03\x90\x90\xa2"     }, /* 17 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 18 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 19 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 20 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 21 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 22 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 23 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 24 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }, /* 25 */
+  { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 26 */
+  { "\x03\x91\x90\xa5",     "\x03\x91\x90\xa5"     }, /* 27 */
+  { "\x02\x88\x90",         "\x02\x88\x90"         }  /* 28 */
+};
+
+static byte * cip_hlc[29] = {
+  "",                           /* 0 */
+  "",                           /* 1 */
+  "",                           /* 2 */
+  "",                           /* 3 */
+  "",                           /* 4 */
+  "",                           /* 5 */
+  "",                           /* 6 */
+  "",                           /* 7 */
+  "",                           /* 8 */
+  "",                           /* 9 */
+  "",                           /* 10 */
+  "",                           /* 11 */
+  "",                           /* 12 */
+  "",                           /* 13 */
+  "",                           /* 14 */
+  "",                           /* 15 */
+
+  "\x02\x91\x81",               /* 16 */
+  "\x02\x91\x84",               /* 17 */
+  "\x02\x91\xa1",               /* 18 */
+  "\x02\x91\xa4",               /* 19 */
+  "\x02\x91\xa8",               /* 20 */
+  "\x02\x91\xb1",               /* 21 */
+  "\x02\x91\xb2",               /* 22 */
+  "\x02\x91\xb5",               /* 23 */
+  "\x02\x91\xb8",               /* 24 */
+  "\x02\x91\xc1",               /* 25 */
+  "\x02\x91\x81",               /* 26 */
+  "\x03\x91\xe0\x01",           /* 27 */
+  "\x03\x91\xe0\x02"            /* 28 */
+};
+
+/*------------------------------------------------------------------*/
+
+#define V120_HEADER_LENGTH 1
+#define V120_HEADER_EXTEND_BIT  0x80
+#define V120_HEADER_BREAK_BIT   0x40
+#define V120_HEADER_C1_BIT      0x04
+#define V120_HEADER_C2_BIT      0x08
+#define V120_HEADER_FLUSH_COND  (V120_HEADER_BREAK_BIT | V120_HEADER_C1_BIT | V120_HEADER_C2_BIT)
+
+static byte v120_default_header[] =
+{
+
+  0x83                          /*  Ext, BR , res, res, C2 , C1 , B  , F   */
+
+};
+
+static byte v120_break_header[] =
+{
+
+  0xc3 | V120_HEADER_BREAK_BIT  /*  Ext, BR , res, res, C2 , C1 , B  , F   */
+
+};
+
+
+/*------------------------------------------------------------------*/
+/* API_PUT function                                                 */
+/*------------------------------------------------------------------*/
+
+word api_put(APPL   * appl, CAPI_MSG   * msg)
+{
+  word i, j, k, l, n;
+  word ret;
+  byte c;
+  byte controller;
+  DIVA_CAPI_ADAPTER   * a;
+  PLCI   * plci;
+  NCCI   * ncci_ptr;
+  word ncci;
+  CAPI_MSG   *m;
+    API_PARSE msg_parms[MAX_MSG_PARMS+1];
+
+  if (msg->header.length < sizeof (msg->header) ||
+      msg->header.length > MAX_MSG_SIZE) {
+    dbug(1,dprintf("bad len"));
+    return _BAD_MSG;
+  }
+
+  controller = (byte)((msg->header.controller &0x7f)-1);
+
+  /* controller starts with 0 up to (max_adapter - 1) */
+  if ( controller >= max_adapter )
+  {
+    dbug(1,dprintf("invalid ctrl"));
+    return _BAD_MSG;
+  }
+  
+  a = &adapter[controller];
+  plci = NULL;
+  if ((msg->header.plci != 0) && (msg->header.plci <= a->max_plci) && !a->adapter_disabled)
+  {
+    dbug(1,dprintf("plci=%x",msg->header.plci));
+    plci = &a->plci[msg->header.plci-1];
+    ncci = GET_WORD(&msg->header.ncci);
+    if (plci->Id
+     && (plci->appl
+      || (plci->State == INC_CON_PENDING)
+      || (plci->State == INC_CON_ALERT)
+      || (msg->header.command == (_DISCONNECT_I|RESPONSE)))
+     && ((ncci == 0)
+      || (msg->header.command == (_DISCONNECT_B3_I|RESPONSE))
+      || ((ncci < MAX_NCCI+1) && (a->ncci_plci[ncci] == plci->Id))))
+    {
+      i = plci->msg_in_read_pos;
+      j = plci->msg_in_write_pos;
+      if (j >= i)
+      {
+        if (j + msg->header.length + MSG_IN_OVERHEAD <= MSG_IN_QUEUE_SIZE)
+          i += MSG_IN_QUEUE_SIZE - j;
+        else
+          j = 0;
+      }
+      else
+      {
+
+        n = (((CAPI_MSG   *)(plci->msg_in_queue))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc;
+
+        if (i > MSG_IN_QUEUE_SIZE - n)
+          i = MSG_IN_QUEUE_SIZE - n + 1;
+        i -= j;
+      }
+
+      if (i <= ((msg->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc))
+
+      {
+        dbug(0,dprintf("Q-FULL1(msg) - len=%d write=%d read=%d wrap=%d free=%d",
+          msg->header.length, plci->msg_in_write_pos,
+          plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));
+
+        return _QUEUE_FULL;
+      }
+      c = FALSE;
+      if ((((byte   *) msg) < ((byte   *)(plci->msg_in_queue)))
+       || (((byte   *) msg) >= ((byte   *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+      {
+        if (plci->msg_in_write_pos != plci->msg_in_read_pos)
+          c = TRUE;
+      }
+      if (msg->header.command == _DATA_B3_R)
+      {
+        if (msg->header.length < 20)
+        {
+          dbug(1,dprintf("DATA_B3 REQ wrong length %d", msg->header.length));
+          return _BAD_MSG;
+        }
+        ncci_ptr = &(a->ncci[ncci]);
+        n = ncci_ptr->data_pending;
+        l = ncci_ptr->data_ack_pending;
+        k = plci->msg_in_read_pos;
+        while (k != plci->msg_in_write_pos)
+        {
+          if (k == plci->msg_in_wrap_pos)
+            k = 0;
+          if ((((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[k]))->header.command == _DATA_B3_R)
+           && (((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[k]))->header.ncci == ncci))
+          {
+            n++;
+            if (((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[k]))->info.data_b3_req.Flags & 0x0004)
+              l++;
+          }
+
+          k += (((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[k]))->header.length +
+            MSG_IN_OVERHEAD + 3) & 0xfffc;
+
+        }
+        if ((n >= MAX_DATA_B3) || (l >= MAX_DATA_ACK))
+        {
+          dbug(0,dprintf("Q-FULL2(data) - pending=%d/%d ack_pending=%d/%d",
+                          ncci_ptr->data_pending, n, ncci_ptr->data_ack_pending, l));
+
+          return _QUEUE_FULL;
+        }
+        if (plci->req_in || plci->internal_command)
+        {
+          if ((((byte   *) msg) >= ((byte   *)(plci->msg_in_queue)))
+           && (((byte   *) msg) < ((byte   *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+          {
+            dbug(0,dprintf("Q-FULL3(requeue)"));
+
+            return _QUEUE_FULL;
+          }
+          c = TRUE;
+        }
+      }
+      else
+      {
+        if (plci->req_in || plci->internal_command)
+          c = TRUE;
+        else
+        {
+          plci->command = msg->header.command;
+          plci->number = msg->header.number;
+        }
+      }
+      if (c)
+      {
+        dbug(1,dprintf("enqueue msg(0x%04x,0x%x,0x%x) - len=%d write=%d read=%d wrap=%d free=%d",
+          msg->header.command, plci->req_in, plci->internal_command,
+          msg->header.length, plci->msg_in_write_pos,
+          plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));
+        if (j == 0)
+          plci->msg_in_wrap_pos = plci->msg_in_write_pos;
+        m = (CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[j]);
+        for (i = 0; i < msg->header.length; i++)
+          ((byte   *)(plci->msg_in_queue))[j++] = ((byte   *) msg)[i];
+        if (m->header.command == _DATA_B3_R)
+        {
+
+          m->info.data_b3_req.Data = (dword)(TransmitBufferSet (appl, m->info.data_b3_req.Data));
+
+        }
+
+        j = (j + 3) & 0xfffc;
+
+        *((APPL   *   *)(&((byte   *)(plci->msg_in_queue))[j])) = appl;
+        plci->msg_in_write_pos = j + MSG_IN_OVERHEAD;
+        return 0;
+      }
+    }
+    else
+    {
+      plci = NULL;
+    }
+  }
+  dbug(1,dprintf("com=%x",msg->header.command));
+
+  for(j=0;j<MAX_MSG_PARMS+1;j++) msg_parms[j].length = 0;
+  for(i=0, ret = _BAD_MSG;
+      i<(sizeof(ftable)/sizeof(struct _ftable));
+      i++) {
+
+    if(ftable[i].command==msg->header.command) {
+      /* break loop if the message is correct, otherwise continue scan  */
+      /* (for example: CONNECT_B3_T90_ACT_RES has two specifications)   */
+      if(!api_parse(msg->info.b,(word)(msg->header.length-12),ftable[i].format,msg_parms)) {
+        ret = 0;
+        break;
+      }
+      for(j=0;j<MAX_MSG_PARMS+1;j++) msg_parms[j].length = 0;
+    }
+  }
+  if(ret) {
+    dbug(1,dprintf("BAD_MSG"));
+    if(plci) plci->command = 0;
+    return ret;
+  }
+
+
+  c = ftable[i].function(GET_DWORD(&msg->header.controller),
+                         msg->header.number,
+                         a,
+                         plci,
+                         appl,
+                         msg_parms);
+
+  channel_xmit_extended_xon (plci);
+
+  if(c==1) send_req(plci);
+  if(c==2 && plci) plci->req_in = plci->req_in_start = plci->req_out = 0;
+  if(plci && !plci->req_in) plci->command = 0;
+  return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* api_parse function, check the format of api messages             */
+/*------------------------------------------------------------------*/
+
+word api_parse(byte   * msg, word length, byte * format, API_PARSE * parms)
+{
+  word i;
+  word p;
+
+  for(i=0,p=0; format[i]; i++) {
+    if(parms)
+    {
+      parms[i].info = &msg[p];
+    }
+    switch(format[i]) {
+    case 'b':
+      p +=1;
+      break;
+    case 'w':
+      p +=2;
+      break;
+    case 'd':
+      p +=4;
+      break;
+    case 's':
+      if(msg[p]==0xff) {
+        parms[i].info +=2;
+        parms[i].length = msg[p+1] + (msg[p+2]<<8);
+        p +=(parms[i].length +3);
+      }
+      else {
+        parms[i].length = msg[p];
+        p +=(parms[i].length +1);
+      }
+      break;
+    }
+
+    if(p>length) return TRUE;
+  }
+  if(parms) parms[i].info = NULL;
+  return FALSE;
+}
+
+void api_save_msg(API_PARSE   *in, byte *format, API_SAVE   *out)
+{
+  word i, j, n = 0;
+  byte   *p;
+
+  p = out->info;
+  for (i = 0; format[i] != '\0'; i++)
+  {
+    out->parms[i].info = p;
+    out->parms[i].length = in[i].length;
+    switch (format[i])
+    {
+    case 'b':
+      n = 1;
+      break;
+    case 'w':
+      n = 2;
+      break;
+    case 'd':
+      n = 4;
+      break;
+    case 's':
+      n = in[i].length + 1;
+      break;
+    }
+    for (j = 0; j < n; j++)
+      *(p++) = in[i].info[j];
+  }
+  out->parms[i].info = NULL;
+  out->parms[i].length = 0;
+}
+
+void api_load_msg(API_SAVE   *in, API_PARSE   *out)
+{
+  word i;
+
+  i = 0;
+  do
+  {
+    out[i].info = in->parms[i].info;
+    out[i].length = in->parms[i].length;
+  } while (in->parms[i++].info);
+}
+
+
+/*------------------------------------------------------------------*/
+/* CAPI remove function                                             */
+/*------------------------------------------------------------------*/
+
+word api_remove_start(void)
+{
+  word i;
+  word j;
+
+  if(!remove_started) {
+    remove_started = TRUE;
+    for(i=0;i<max_adapter;i++) {
+      if(adapter[i].request) {
+        for(j=0;j<adapter[i].max_plci;j++) {
+          if(adapter[i].plci[j].Sig.Id) plci_remove(&adapter[i].plci[j]);
+        }
+      }
+    }
+    return 1;
+  }
+  else {
+    for(i=0;i<max_adapter;i++) {
+      if(adapter[i].request) {
+        for(j=0;j<adapter[i].max_plci;j++) {
+          if(adapter[i].plci[j].Sig.Id) return 1;
+        }
+      }
+    }
+  }
+  api_remove_complete();
+  return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* internal command queue                                           */
+/*------------------------------------------------------------------*/
+
+static void init_internal_command_queue (PLCI   *plci)
+{
+  word i;
+
+  dbug (1, dprintf ("%s,%d: init_internal_command_queue",
+    (char   *)(FILE_), __LINE__));
+
+  plci->internal_command = 0;
+  for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS; i++)
+    plci->internal_command_queue[i] = NULL;
+}
+
+
+static void start_internal_command (dword Id, PLCI   *plci, t_std_internal_command command_function)
+{
+  word i;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: start_internal_command",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  if (plci->internal_command == 0)
+  {
+    plci->internal_command_queue[0] = command_function;
+    (* command_function)(Id, plci, OK);
+  }
+  else
+  {
+    i = 1;
+    while (plci->internal_command_queue[i] != 0)
+      i++;
+    plci->internal_command_queue[i] = command_function;
+  }
+}
+
+
+static void next_internal_command (dword Id, PLCI   *plci)
+{
+  word i;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: next_internal_command",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  plci->internal_command = 0;
+  plci->internal_command_queue[0] = NULL;
+  while (plci->internal_command_queue[1] != 0)
+  {
+    for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS - 1; i++)
+      plci->internal_command_queue[i] = plci->internal_command_queue[i+1];
+    plci->internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS - 1] = NULL;
+    (*(plci->internal_command_queue[0]))(Id, plci, OK);
+    if (plci->internal_command != 0)
+      return;
+    plci->internal_command_queue[0] = NULL;
+  }
+}
+
+
+/*------------------------------------------------------------------*/
+/* NCCI allocate/remove function                                    */
+/*------------------------------------------------------------------*/
+
+static dword ncci_mapping_bug = 0;
+
+static word get_ncci (PLCI   *plci, byte ch, word force_ncci)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word ncci, i, j, k;
+
+  a = plci->adapter;
+  if (!ch || a->ch_ncci[ch])
+  {
+    ncci_mapping_bug++;
+    dbug(1,dprintf("NCCI mapping exists %ld %02x %02x %02x-%02x",
+      ncci_mapping_bug, ch, force_ncci, a->ncci_ch[a->ch_ncci[ch]], a->ch_ncci[ch]));
+    ncci = ch;
+  }
+  else
+  {
+    if (force_ncci)
+      ncci = force_ncci;
+    else
+    {
+      if ((ch < MAX_NCCI+1) && !a->ncci_ch[ch])
+        ncci = ch;
+      else
+      {
+        ncci = 1;
+        while ((ncci < MAX_NCCI+1) && a->ncci_ch[ncci])
+          ncci++;
+        if (ncci == MAX_NCCI+1)
+        {
+          ncci_mapping_bug++;
+          i = 1;
+          do
+          {
+            j = 1;
+            while ((j < MAX_NCCI+1) && (a->ncci_ch[j] != i))
+              j++;
+            k = j;
+            if (j < MAX_NCCI+1)
+            {
+              do
+              {
+                j++;
+              } while ((j < MAX_NCCI+1) && (a->ncci_ch[j] != i));
+            }
+          } while ((i < MAX_NL_CHANNEL+1) && (j < MAX_NCCI+1));
+          if (i < MAX_NL_CHANNEL+1)
+          {
+            dbug(1,dprintf("NCCI mapping overflow %ld %02x %02x %02x-%02x-%02x",
+              ncci_mapping_bug, ch, force_ncci, i, k, j));
+          }
+          else
+          {
+            dbug(1,dprintf("NCCI mapping overflow %ld %02x %02x",
+              ncci_mapping_bug, ch, force_ncci));
+          }
+          ncci = ch;
+        }
+      }
+      a->ncci_plci[ncci] = plci->Id;
+      a->ncci_state[ncci] = IDLE;
+      if (!plci->ncci_ring_list)
+        plci->ncci_ring_list = ncci;
+      else
+        a->ncci_next[ncci] = a->ncci_next[plci->ncci_ring_list];
+      a->ncci_next[plci->ncci_ring_list] = (byte) ncci;
+    }
+    a->ncci_ch[ncci] = ch;
+    a->ch_ncci[ch] = (byte) ncci;
+    dbug(1,dprintf("NCCI mapping established %ld %02x %02x %02x-%02x",
+      ncci_mapping_bug, ch, force_ncci, ch, ncci));
+  }
+  return (ncci);
+}
+
+
+static void ncci_free_receive_buffers (PLCI   *plci, word ncci)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  APPL   *appl;
+  word i, ncci_code;
+  dword Id;
+
+  a = plci->adapter;
+  Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
+  if (ncci)
+  {
+    if (a->ncci_plci[ncci] == plci->Id)
+    {
+      if (!plci->appl)
+      {
+        ncci_mapping_bug++;
+        dbug(1,dprintf("NCCI mapping appl expected %ld %08lx",
+          ncci_mapping_bug, Id));
+      }
+      else
+      {
+        appl = plci->appl;
+        ncci_code = ncci | (((word) a->Id) << 8);
+        for (i = 0; i < appl->MaxBuffer; i++)
+        {
+          if ((appl->DataNCCI[i] == ncci_code)
+           && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
+          {
+            appl->DataNCCI[i] = 0;
+          }
+        }
+      }
+    }
+  }
+  else
+  {
+    for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+    {
+      if (a->ncci_plci[ncci] == plci->Id)
+      {
+        if (!plci->appl)
+        {
+          ncci_mapping_bug++;
+          dbug(1,dprintf("NCCI mapping no appl %ld %08lx",
+            ncci_mapping_bug, Id));
+        }
+        else
+        {
+          appl = plci->appl;
+          ncci_code = ncci | (((word) a->Id) << 8);
+          for (i = 0; i < appl->MaxBuffer; i++)
+          {
+            if ((appl->DataNCCI[i] == ncci_code)
+             && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
+            {
+              appl->DataNCCI[i] = 0;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+
+static void cleanup_ncci_data (PLCI   *plci, word ncci)
+{
+  NCCI   *ncci_ptr;
+
+  if (ncci && (plci->adapter->ncci_plci[ncci] == plci->Id))
+  {
+    ncci_ptr = &(plci->adapter->ncci[ncci]);
+    if (plci->appl)
+    {
+      while (ncci_ptr->data_pending != 0)
+      {
+        if (!plci->data_sent || (ncci_ptr->DBuffer[ncci_ptr->data_out].P != plci->data_sent_ptr))
+          TransmitBufferFree (plci->appl, ncci_ptr->DBuffer[ncci_ptr->data_out].P);
+        (ncci_ptr->data_out)++;
+        if (ncci_ptr->data_out == MAX_DATA_B3)
+          ncci_ptr->data_out = 0;
+        (ncci_ptr->data_pending)--;
+      }
+    }
+    ncci_ptr->data_out = 0;
+    ncci_ptr->data_pending = 0;
+    ncci_ptr->data_ack_out = 0;
+    ncci_ptr->data_ack_pending = 0;
+  }
+}
+
+
+static void ncci_remove (PLCI   *plci, word ncci, byte preserve_ncci)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  dword Id;
+  word i;
+
+  a = plci->adapter;
+  Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
+  if (!preserve_ncci)
+    ncci_free_receive_buffers (plci, ncci);
+  if (ncci)
+  {
+    if (a->ncci_plci[ncci] != plci->Id)
+    {
+      ncci_mapping_bug++;
+      dbug(1,dprintf("NCCI mapping doesn't exist %ld %08lx %02x",
+        ncci_mapping_bug, Id, preserve_ncci));
+    }
+    else
+    {
+      cleanup_ncci_data (plci, ncci);
+      dbug(1,dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
+        ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
+      a->ch_ncci[a->ncci_ch[ncci]] = 0;
+      if (!preserve_ncci)
+      {
+        a->ncci_ch[ncci] = 0;
+        a->ncci_plci[ncci] = 0;
+        a->ncci_state[ncci] = IDLE;
+        i = plci->ncci_ring_list;
+        while ((i != 0) && (a->ncci_next[i] != plci->ncci_ring_list) && (a->ncci_next[i] != ncci))
+          i = a->ncci_next[i];
+        if ((i != 0) && (a->ncci_next[i] == ncci))
+        {
+          if (i == ncci)
+            plci->ncci_ring_list = 0;
+          else if (plci->ncci_ring_list == ncci)
+            plci->ncci_ring_list = i;
+          a->ncci_next[i] = a->ncci_next[ncci];
+        }
+        a->ncci_next[ncci] = 0;
+      }
+    }
+  }
+  else
+  {
+    for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+    {
+      if (a->ncci_plci[ncci] == plci->Id)
+      {
+        cleanup_ncci_data (plci, ncci);
+        dbug(1,dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
+          ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
+        a->ch_ncci[a->ncci_ch[ncci]] = 0;
+        if (!preserve_ncci)
+        {
+          a->ncci_ch[ncci] = 0;
+          a->ncci_plci[ncci] = 0;
+          a->ncci_state[ncci] = IDLE;
+          a->ncci_next[ncci] = 0;
+        }
+      }
+    }
+    if (!preserve_ncci)
+      plci->ncci_ring_list = 0;
+  }
+}
+
+
+/*------------------------------------------------------------------*/
+/* PLCI remove function                                             */
+/*------------------------------------------------------------------*/
+
+static void plci_free_msg_in_queue (PLCI   *plci)
+{
+  word i;
+
+  if (plci->appl)
+  {
+    i = plci->msg_in_read_pos;
+    while (i != plci->msg_in_write_pos)
+    {
+      if (i == plci->msg_in_wrap_pos)
+        i = 0;
+      if (((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[i]))->header.command == _DATA_B3_R)
+      {
+
+        TransmitBufferFree (plci->appl,
+          (byte   *)(((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[i]))->info.data_b3_req.Data));
+
+      }
+
+      i += (((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[i]))->header.length +
+        MSG_IN_OVERHEAD + 3) & 0xfffc;
+
+    }
+  }
+  plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
+  plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+  plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+}
+
+
+static void plci_remove(PLCI   * plci)
+{
+
+  if(!plci) {
+    dbug(1,dprintf("plci_remove(no plci)"));
+    return;
+  }
+  init_internal_command_queue (plci);
+  dbug(1,dprintf("plci_remove(%x,tel=%x)",plci->Id,plci->tel));
+  if(plci_remove_check(plci))
+  {
+    return;
+  }
+  if (plci->Sig.Id == 0xff)
+  {
+    dbug(1,dprintf("D-channel X.25 plci->NL.Id:%0x", plci->NL.Id));
+    if (plci->NL.Id && !plci->nl_remove_id)
+    {
+      nl_req_ncci(plci,REMOVE,0);
+      send_req(plci);
+    }
+  }
+  else
+  {
+    if (!plci->sig_remove_id
+     && (plci->Sig.Id
+      || (plci->req_in!=plci->req_out)
+      || (plci->nl_req || plci->sig_req)))
+    {
+      sig_req(plci,HANGUP,0);
+      send_req(plci);
+    }
+  }
+  ncci_remove (plci, 0, FALSE);
+  plci_free_msg_in_queue (plci);
+
+  plci->channels = 0;
+  plci->appl = NULL;
+  if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT))
+    plci->State = OUTG_DIS_PENDING;
+}
+
+/*------------------------------------------------------------------*/
+/* Application Group function helpers                               */
+/*------------------------------------------------------------------*/
+
+static void set_group_ind_mask (PLCI   *plci)
+{
+  word i;
+
+  for (i = 0; i < C_IND_MASK_DWORDS; i++)
+    plci->group_optimization_mask_table[i] = 0xffffffffL;
+}
+
+static void clear_group_ind_mask_bit (PLCI   *plci, word b)
+{
+  plci->group_optimization_mask_table[b >> 5] &= ~(1L << (b & 0x1f));
+}
+
+static byte test_group_ind_mask_bit (PLCI   *plci, word b)
+{
+  return ((plci->group_optimization_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0);
+}
+
+/*------------------------------------------------------------------*/
+/* c_ind_mask operations for arbitrary MAX_APPL                     */
+/*------------------------------------------------------------------*/
+
+static void clear_c_ind_mask (PLCI   *plci)
+{
+  word i;
+
+  for (i = 0; i < C_IND_MASK_DWORDS; i++)
+    plci->c_ind_mask_table[i] = 0;
+}
+
+static byte c_ind_mask_empty (PLCI   *plci)
+{
+  word i;
+
+  i = 0;
+  while ((i < C_IND_MASK_DWORDS) && (plci->c_ind_mask_table[i] == 0))
+    i++;
+  return (i == C_IND_MASK_DWORDS);
+}
+
+static void set_c_ind_mask_bit (PLCI   *plci, word b)
+{
+  plci->c_ind_mask_table[b >> 5] |= (1L << (b & 0x1f));
+}
+
+static void clear_c_ind_mask_bit (PLCI   *plci, word b)
+{
+  plci->c_ind_mask_table[b >> 5] &= ~(1L << (b & 0x1f));
+}
+
+static byte test_c_ind_mask_bit (PLCI   *plci, word b)
+{
+  return ((plci->c_ind_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0);
+}
+
+static void dump_c_ind_mask (PLCI   *plci)
+{
+static char hex_digit_table[0x10] =
+  {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+  word i, j, k;
+  dword d;
+    char *p;
+    char buf[40];
+
+  for (i = 0; i < C_IND_MASK_DWORDS; i += 4)
+  {
+    p = buf + 36;
+    *p = '\0';
+    for (j = 0; j < 4; j++)
+    {
+      if (i+j < C_IND_MASK_DWORDS)
+      {
+        d = plci->c_ind_mask_table[i+j];
+        for (k = 0; k < 8; k++)
+        {
+          *(--p) = hex_digit_table[d & 0xf];
+          d >>= 4;
+        }
+      }
+      else if (i != 0)
+      {
+        for (k = 0; k < 8; k++)
+          *(--p) = ' ';
+      }
+      *(--p) = ' ';
+    }
+    dbug(1,dprintf ("c_ind_mask =%s", (char   *) p));
+  }
+}
+
+
+
+
+
+#define dump_plcis(a)
+
+
+
+/*------------------------------------------------------------------*/
+/* translation function for each message                            */
+/*------------------------------------------------------------------*/
+
+byte connect_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word ch;
+  word i;
+  word Info;
+  word CIP;
+  byte LinkLayer;
+  API_PARSE * ai;
+  API_PARSE * bp;
+    API_PARSE ai_parms[5];
+  word channel = 0;
+  dword ch_mask;
+  byte m;
+  static byte esc_chi[35] = {0x02,0x18,0x01};
+  static byte lli[2] = {0x01,0x00};
+  byte noCh = 0;
+  word dir = 0;
+  byte   *p_chi = "";
+
+  for(i=0;i<5;i++) ai_parms[i].length = 0;
+
+  dbug(1,dprintf("connect_req(%d)",parms->length));
+  Info = _WRONG_IDENTIFIER;
+  if(a)
+  {
+    if(a->adapter_disabled)
+    {
+      dbug(1,dprintf("adapter disabled"));
+      Id = ((word)1<<8)|a->Id;
+      sendf(appl,_CONNECT_R|CONFIRM,Id,Number,"w",0);
+      sendf(appl, _DISCONNECT_I, Id, 0, "w", _L1_ERROR);
+      return FALSE;
+    }
+    Info = _OUT_OF_PLCI;
+    if((i=get_plci(a)))
+    {
+      Info = 0;
+      plci = &a->plci[i-1];
+      plci->appl = appl;
+      plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+      /* check 'external controller' bit for codec support */
+      if(Id & EXT_CONTROLLER)
+      {
+        if(AdvCodecSupport(a, plci, appl, 0) )
+        {
+          plci->Id = 0;
+          sendf(appl, _CONNECT_R|CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER);
+          return 2;
+        }
+      }
+      ai = &parms[9];
+      bp = &parms[5];
+      ch = 0;
+      if(bp->length)LinkLayer = bp->info[3];
+      else LinkLayer = 0;
+      if(ai->length)
+      {
+        ch=0xffff;
+        if(!api_parse(&ai->info[1],(word)ai->length,"ssss",ai_parms))
+        {
+          ch = 0;
+          if(ai_parms[0].length)
+          {
+            ch = GET_WORD(ai_parms[0].info+1);
+            if(ch>4) ch=0; /* safety -> ignore ChannelID */
+            if(ch==4) /* explizit CHI in message */
+            {
+              /* check length of B-CH struct */
+              if((ai_parms[0].info)[3]>=1)
+              {
+                if((ai_parms[0].info)[4]==CHI)
+                {
+                  p_chi = &((ai_parms[0].info)[5]);
+                }
+                else
+                {
+                  p_chi = &((ai_parms[0].info)[3]);
+                }
+                if(p_chi[0]>35) /* check length of channel ID */
+                {
+                  Info = _WRONG_MESSAGE_FORMAT;    
+                }
+              }
+              else Info = _WRONG_MESSAGE_FORMAT;    
+            }
+
+            if(ch==3 && ai_parms[0].length>=7 && ai_parms[0].length<=36)
+            {
+              dir = GET_WORD(ai_parms[0].info+3);
+              ch_mask = 0;
+              m = 0x3f;
+              for(i=0; i+5<=ai_parms[0].length; i++)
+              {
+                if(ai_parms[0].info[i+5]!=0)
+                {
+                  if((ai_parms[0].info[i+5] | m) != 0xff)
+                    Info = _WRONG_MESSAGE_FORMAT;
+                  else
+                  {
+                    if (ch_mask == 0)
+                      channel = i;
+                    ch_mask |= 1L << i;
+                  }
+                }
+                m = 0;
+              }
+              if (ch_mask == 0)
+                Info = _WRONG_MESSAGE_FORMAT;
+              if (!Info)
+              {
+                if ((ai_parms[0].length == 36) || (ch_mask != ((dword)(1L << channel))))
+                {
+                  esc_chi[0] = (byte)(ai_parms[0].length - 2);
+                  for(i=0; i+5<=ai_parms[0].length; i++)
+                    esc_chi[i+3] = ai_parms[0].info[i+5];
+                }
+                else
+                  esc_chi[0] = 2;
+                esc_chi[2] = (byte)channel;
+                plci->b_channel = (byte)channel; /* not correct for ETSI ch 17..31 */
+                add_p(plci,LLI,lli);
+                add_p(plci,ESC,esc_chi);
+                plci->State = LOCAL_CONNECT;
+                if(!dir) plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;     /* dir 0=DTE, 1=DCE */
+              }
+            }
+          }
+        }
+        else  Info = _WRONG_MESSAGE_FORMAT;
+      }
+
+      dbug(1,dprintf("ch=%x,dir=%x,p_ch=%d",ch,dir,channel));
+      plci->command = _CONNECT_R;
+      plci->number = Number;
+      /* x.31 or D-ch free SAPI in LinkLayer? */
+      if(ch==1 && LinkLayer!=3 && LinkLayer!=12) noCh = TRUE;
+      if((ch==0 || ch==2 || noCh || ch==3 || ch==4) && !Info)
+      {
+        /* B-channel used for B3 connections (ch==0), or no B channel    */
+        /* is used (ch==2) or perm. connection (3) is used  do a CALL    */
+        if(noCh) Info = add_b1(plci,&parms[5],2,0);    /* no resource    */
+        else     Info = add_b1(plci,&parms[5],ch,0); 
+        add_s(plci,OAD,&parms[2]);
+        add_s(plci,OSA,&parms[4]);
+        add_s(plci,BC,&parms[6]);
+        add_s(plci,LLC,&parms[7]);
+        add_s(plci,HLC,&parms[8]);
+        CIP = GET_WORD(parms[0].info);
+        if (a->Info_Mask[appl->Id-1] & 0x200)
+        {
+          /* early B3 connect (CIP mask bit 9) no release after a disc */
+          add_p(plci,LLI,"\x01\x01");
+        }
+        if(GET_WORD(parms[0].info)<29) {
+          add_p(plci,BC,cip_bc[GET_WORD(parms[0].info)][a->u_law]);
+          add_p(plci,HLC,cip_hlc[GET_WORD(parms[0].info)]);
+        }
+        add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+        sig_req(plci,ASSIGN,DSIG_ID);
+      }
+      else if(ch==1) {
+
+        /* D-Channel used for B3 connections */
+        plci->Sig.Id = 0xff;
+        Info = 0;
+      }
+
+      if(!Info && ch!=2 && !noCh ) {
+        Info = add_b23(plci,&parms[5]);
+        if(!Info) {
+          if(!(plci->tel && !plci->adv_nl))nl_req_ncci(plci,ASSIGN,0);
+        }
+      }
+
+      if(!Info)
+      {
+        if(ch==0 || ch==2 || ch==3 || noCh || ch==4)
+        {
+          if(plci->spoofed_msg==SPOOFING_REQUIRED)
+          {
+            api_save_msg(parms, "wsssssssss", &plci->saved_msg);
+            plci->spoofed_msg = CALL_REQ;
+            plci->internal_command = BLOCK_PLCI;
+            plci->command = 0;
+            dbug(1,dprintf("Spoof"));
+            send_req(plci);
+            return FALSE;
+          }
+          if(ch==4)add_p(plci,CHI,p_chi);
+          add_s(plci,CPN,&parms[1]);
+          add_s(plci,DSA,&parms[3]);
+          if(noCh) add_p(plci,ESC,"\x02\x18\xfd");  /* D-channel, no B-L3 */
+          add_ai(plci,&parms[9]);
+          if(!dir)sig_req(plci,CALL_REQ,0);
+          else
+          {
+            plci->command = PERM_LIST_REQ;
+            plci->appl = appl;
+            sig_req(plci,LISTEN_REQ,0);
+            send_req(plci);
+            return FALSE;
+          }
+        }
+        send_req(plci);
+        return FALSE;
+      }
+      plci->Id = 0;
+    }
+  }
+  sendf(appl,
+        _CONNECT_R|CONFIRM,
+        Id,
+        Number,
+        "w",Info);
+  return 2;
+}
+
+byte connect_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word i, Info;
+  word Reject;
+  static byte cau_t[] = {0,0,0x90,0x91,0xac,0x9d,0x86,0xd8,0x9b};
+  static byte esc_t[] = {0x03,0x08,0x00,0x00};
+  API_PARSE * ai;
+    API_PARSE ai_parms[5];
+  word ch=0;
+
+  if(!plci) {
+    dbug(1,dprintf("connect_res(no plci)"));
+    return 0;  /* no plci, no send */
+  }
+
+  dbug(1,dprintf("connect_res(State=0x%x)",plci->State));
+  for(i=0;i<5;i++) ai_parms[i].length = 0;
+  ai = &parms[5];
+  dbug(1,dprintf("ai->length=%d",ai->length));
+
+  if(ai->length)
+  {
+    if(!api_parse(&ai->info[1],(word)ai->length,"ssss",ai_parms))
+    {
+      dbug(1,dprintf("ai_parms[0].length=%d/0x%x",ai_parms[0].length,GET_WORD(ai_parms[0].info+1)));
+      ch = 0;
+      if(ai_parms[0].length)
+      {
+        ch = GET_WORD(ai_parms[0].info+1);
+        dbug(1,dprintf("BCH-I=0x%x",ch));
+      }
+    }
+  }
+
+  if(plci->State==INC_CON_CONNECTED_ALERT)
+  {
+    dbug(1,dprintf("Connected Alert Call_Res"));
+    if (a->Info_Mask[appl->Id-1] & 0x200)
+    {
+    /* early B3 connect (CIP mask bit 9) no release after a disc */
+      add_p(plci,LLI,"\x01\x01");
+    }
+    add_s(plci, CONN_NR, &parms[2]);
+    add_s(plci, LLC, &parms[4]);
+    add_ai(plci, &parms[5]);
+    plci->State = INC_CON_ACCEPT;
+    sig_req(plci, CALL_RES,0);
+    return 1;
+  }
+  else if(plci->State==INC_CON_PENDING || plci->State==INC_CON_ALERT) {
+    clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+    dump_c_ind_mask (plci);
+    Reject = GET_WORD(parms[0].info);
+    dbug(1,dprintf("Reject=0x%x",Reject));
+    if(Reject) 
+    {
+      if(c_ind_mask_empty (plci)) 
+      {
+        if((Reject&0xff00)==0x3400) 
+        {
+          esc_t[2] = ((byte)(Reject&0x00ff)) | 0x80;
+          add_p(plci,ESC,esc_t);
+          add_ai(plci, &parms[5]);
+          sig_req(plci,REJECT,0);
+        }      
+        else if(Reject==1 || Reject>9) 
+        {
+          add_ai(plci, &parms[5]);
+          sig_req(plci,HANGUP,0);
+        }
+        else 
+        {
+          esc_t[2] = cau_t[(Reject&0x000f)];
+          add_p(plci,ESC,esc_t);
+          add_ai(plci, &parms[5]);
+          sig_req(plci,REJECT,0);
+        }
+        plci->appl = appl;
+      }
+      else 
+      {
+        sendf(appl, _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
+      }
+    }
+    else {
+      plci->appl = appl;
+      if(Id & EXT_CONTROLLER){
+        if(AdvCodecSupport(a, plci, appl, 0)){
+          dbug(1,dprintf("connect_res(error from AdvCodecSupport)"));
+          sig_req(plci,HANGUP,0);
+          return 1;
+        }
+        if(plci->tel == ADV_VOICE && a->AdvCodecPLCI)
+        {
+          Info = add_b23(plci, &parms[1]);
+          if (Info)
+          {
+            dbug(1,dprintf("connect_res(error from add_b23)"));
+            sig_req(plci,HANGUP,0);
+            return 1;
+          }
+          if(plci->adv_nl)
+          {
+            nl_req_ncci(plci, ASSIGN, 0);
+          }
+        }
+      }
+      else
+      {
+        plci->tel = 0;
+        if(ch!=2)
+        {
+          Info = add_b23(plci, &parms[1]);
+          if (Info)
+          {
+            dbug(1,dprintf("connect_res(error from add_b23 2)"));
+            sig_req(plci,HANGUP,0);
+            return 1;
+          }
+        }
+        nl_req_ncci(plci, ASSIGN, 0);
+      }
+
+      if(plci->spoofed_msg==SPOOFING_REQUIRED)
+      {
+        api_save_msg(parms, "wsssss", &plci->saved_msg);
+        plci->spoofed_msg = CALL_RES;
+        plci->internal_command = BLOCK_PLCI;
+        plci->command = 0;
+        dbug(1,dprintf("Spoof"));
+      }
+      else
+      {
+        add_b1 (plci, &parms[1], ch, plci->B1_facilities);
+        if (a->Info_Mask[appl->Id-1] & 0x200)
+        {
+          /* early B3 connect (CIP mask bit 9) no release after a disc */
+          add_p(plci,LLI,"\x01\x01");
+        }
+        add_s(plci, CONN_NR, &parms[2]);
+        add_s(plci, LLC, &parms[4]);
+        add_ai(plci, &parms[5]);
+        plci->State = INC_CON_ACCEPT;
+        sig_req(plci, CALL_RES,0);
+      }
+
+      for(i=0; i<max_appl; i++) {
+        if(test_c_ind_mask_bit (plci, i)) {
+          sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
+        }
+      }
+    }
+  }
+  return 1;
+}
+
+byte connect_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  dbug(1,dprintf("connect_a_res"));
+  return FALSE;
+}
+
+byte disconnect_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  word Info;
+  word i;
+
+  dbug(1,dprintf("disconnect_req"));
+
+  Info = _WRONG_IDENTIFIER;
+
+  if(plci)
+  {
+    if(plci->State==INC_CON_PENDING || plci->State==INC_CON_ALERT)
+    {
+      clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+      plci->appl = appl;
+      for(i=0; i<max_appl; i++)
+      {
+        if(test_c_ind_mask_bit (plci, i))
+          sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
+      }
+      plci->State = OUTG_DIS_PENDING;
+    }
+    if(plci->Sig.Id && plci->appl)
+    {
+      Info = 0;
+        if(plci->Sig.Id!=0xff)
+        {
+          if(plci->State!=INC_DIS_PENDING)
+          {
+            add_ai(plci, &msg[0]);
+            sig_req(plci,HANGUP,0);
+            plci->State = OUTG_DIS_PENDING;
+            return 1;
+          }
+        }
+        else
+        {
+          if (plci->NL.Id && !plci->nl_remove_id)
+          {
+            mixer_remove (plci);
+            nl_req_ncci(plci,REMOVE,0);
+          sendf(appl,_DISCONNECT_R|CONFIRM,Id,Number,"w",0);
+          sendf(appl, _DISCONNECT_I, Id, 0, "w", 0);
+          plci->State = INC_DIS_PENDING;
+          }
+          return 1;
+        }
+      }
+    }
+
+  if(!appl)  return FALSE;
+  sendf(appl, _DISCONNECT_R|CONFIRM, Id, Number, "w",Info);
+  return FALSE;
+}
+
+byte disconnect_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  dbug(1,dprintf("disconnect_res"));
+  if(plci)
+  {
+        /* clear ind mask bit, just in case of collsion of          */
+        /* DISCONNECT_IND and CONNECT_RES                           */
+    clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+    ncci_free_receive_buffers (plci, 0);
+    if(plci_remove_check(plci))
+    {
+      return 0;
+    }
+    if(plci->State==INC_DIS_PENDING
+    || plci->State==SUSPENDING) {
+      if(c_ind_mask_empty (plci)) {
+        if(plci->State!=SUSPENDING)plci->State = IDLE;
+        dbug(1,dprintf("chs=%d",plci->channels));
+        if(!plci->channels) {
+          plci_remove(plci);
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+byte listen_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word Info;
+  byte i;
+
+  dbug(1,dprintf("listen_req(Appl=0x%x)",appl->Id));
+
+  Info = _WRONG_IDENTIFIER;
+  if(a) {
+    Info = 0;
+    a->Info_Mask[appl->Id-1] = GET_DWORD(parms[0].info);
+    a->CIP_Mask[appl->Id-1] = GET_DWORD(parms[1].info);
+    dbug(1,dprintf("CIP_MASK=0x%lx",GET_DWORD(parms[1].info)));
+    if (a->Info_Mask[appl->Id-1] & 0x200){ /* early B3 connect provides */
+      a->Info_Mask[appl->Id-1] |=  0x10;   /* call progression infos    */
+    }
+
+    /* check if external controller listen and switch listen on or off*/
+    if(Id&EXT_CONTROLLER && GET_DWORD(parms[1].info)){
+      if(a->profile.Global_Options & ON_BOARD_CODEC) {
+        dummy_plci.State = IDLE;
+        a->codec_listen[appl->Id-1] = &dummy_plci;
+        a->TelOAD[0] = (byte)(parms[3].length);
+        for(i=1;parms[3].length>=i && i<22;i++) {
+          a->TelOAD[i] = parms[3].info[i];
+        }
+        a->TelOAD[i] = 0;
+        a->TelOSA[0] = (byte)(parms[4].length);
+        for(i=1;parms[4].length>=i && i<22;i++) {
+          a->TelOSA[i] = parms[4].info[i];
+        }
+        a->TelOSA[i] = 0;
+      }
+      else Info = 0x2002; /* wrong controller, codec not supported */
+    }
+    else{               /* clear listen */
+      a->codec_listen[appl->Id-1] = (PLCI   *)0;
+    }
+  }
+  sendf(appl,
+        _LISTEN_R|CONFIRM,
+        Id,
+        Number,
+        "w",Info);
+
+  if (a) listen_check(a);
+  return FALSE;
+}
+
+byte info_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  word i;
+  API_PARSE * ai;
+  PLCI   * rc_plci = NULL;
+    API_PARSE ai_parms[5];
+  word Info = 0;
+
+  dbug(1,dprintf("info_req"));
+  for(i=0;i<5;i++) ai_parms[i].length = 0;
+
+  ai = &msg[1];
+
+  if(ai->length)
+  {
+    if(api_parse(&ai->info[1],(word)ai->length,"ssss",ai_parms))
+    {
+      dbug(1,dprintf("AddInfo wrong"));
+      Info = _WRONG_MESSAGE_FORMAT;
+    }
+  }
+  if(!a) Info = _WRONG_STATE;
+
+  if(!Info && plci)
+  {                /* no fac, with CPN, or KEY */
+    rc_plci = plci;
+    if(!ai_parms[3].length && plci->State && (msg[0].length || ai_parms[1].length) )
+    {
+      /* overlap sending option */
+      dbug(1,dprintf("OvlSnd"));
+      add_s(plci,CPN,&msg[0]);
+      add_s(plci,KEY,&ai_parms[1]);
+      sig_req(plci,INFO_REQ,0);
+      send_req(plci);
+      return FALSE;
+    }
+
+    if(plci->State && ai_parms[2].length)
+    {
+      /* User_Info option */
+      dbug(1,dprintf("UUI"));
+      add_s(plci,UUI,&ai_parms[2]);
+      sig_req(plci,USER_DATA,0);
+    }
+    else if(plci->State && ai_parms[3].length)
+    {
+      /* Facility option */
+      dbug(1,dprintf("FAC"));
+      add_s(plci,CPN,&msg[0]);
+      add_ai(plci, &msg[1]);
+      sig_req(plci,FACILITY_REQ,0);
+    }
+    else
+    {
+      Info = _WRONG_STATE;
+    }
+  }
+  else if((ai_parms[1].length || ai_parms[2].length || ai_parms[3].length) && !Info)
+  {
+    /* NCR_Facility option -> send UUI and Keypad too */
+    dbug(1,dprintf("NCR_FAC"));
+    if((i=get_plci(a)))
+    {
+      rc_plci = &a->plci[i-1];
+      appl->NullCREnable  = TRUE;
+      rc_plci->internal_command = C_NCR_FAC_REQ;
+      rc_plci->appl = appl;
+      add_p(rc_plci,CAI,"\x01\x80");
+      add_p(rc_plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+      sig_req(rc_plci,ASSIGN,DSIG_ID);
+      send_req(rc_plci);
+    }
+    else
+    {
+      Info = _OUT_OF_PLCI;
+    }
+
+    if(!Info)
+    {
+      add_s(rc_plci,CPN,&msg[0]);
+      add_ai(rc_plci, &msg[1]);
+      sig_req(rc_plci,NCR_FACILITY,0);
+      send_req(rc_plci);
+      return FALSE;
+     /* for application controlled supplementary services    */
+    }
+  }
+
+  if (!rc_plci)
+  {
+    Info = _WRONG_MESSAGE_FORMAT;
+  }
+
+  if(!Info)
+  {
+    send_req(rc_plci);
+  }
+  else
+  {  /* appl is not assigned to a PLCI or error condition */
+    dbug(1,dprintf("localInfoCon"));
+    sendf(appl,
+          _INFO_R|CONFIRM,
+          Id,
+          Number,
+          "w",Info);
+  }
+  return FALSE;
+}
+
+byte info_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  dbug(1,dprintf("info_res"));
+  return FALSE;
+}
+
+byte alert_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  word Info;
+  byte ret;
+
+  dbug(1,dprintf("alert_req"));
+
+  Info = _WRONG_IDENTIFIER;
+  ret = FALSE;
+  if(plci) {
+    Info = _ALERT_IGNORED;
+    if(plci->State!=INC_CON_ALERT) {
+      Info = _WRONG_STATE;
+      if(plci->State==INC_CON_PENDING) {
+        Info = 0;
+        plci->State=INC_CON_ALERT;
+        add_ai(plci, &msg[0]);
+        sig_req(plci,CALL_ALERT,0);
+        ret = 1;
+      }
+    }
+  }
+  sendf(appl,
+        _ALERT_R|CONFIRM,
+        Id,
+        Number,
+        "w",Info);
+  return ret;
+}
+
+byte facility_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  word Info = 0;
+  word i    = 0;
+
+  word selector;
+  word SSreq;
+  long relatedPLCIvalue;
+  DIVA_CAPI_ADAPTER   * relatedadapter;
+  byte * SSparms  = "";
+    byte RCparms[]  = "\x05\x00\x00\x02\x00\x00";
+    byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";
+  API_PARSE * parms;
+    API_PARSE ss_parms[11];
+  PLCI   *rplci;
+    byte cai[15];
+  dword d;
+    API_PARSE dummy;
+
+  dbug(1,dprintf("facility_req"));
+  for(i=0;i<9;i++) ss_parms[i].length = 0;
+
+  parms = &msg[1];
+
+  if(!a)
+  {
+    dbug(1,dprintf("wrong Ctrl"));
+    Info = _WRONG_IDENTIFIER;
+  }
+
+  selector = GET_WORD(msg[0].info);
+
+  if(!Info)
+  {
+    switch(selector)
+    {
+      case SELECTOR_HANDSET:
+        Info = AdvCodecSupport(a, plci, appl, HOOK_SUPPORT);
+        break;
+
+      case SELECTOR_SU_SERV:
+        if(!msg[1].length)
+        {
+          Info = _WRONG_MESSAGE_FORMAT;
+          break;
+        }
+        SSreq = GET_WORD(&(msg[1].info[1]));
+        PUT_WORD(&RCparms[1],SSreq);
+        SSparms = RCparms;
+        switch(SSreq)
+        {
+          case S_GET_SUPPORTED_SERVICES:
+            if((i=get_plci(a)))
+            {
+              rplci = &a->plci[i-1];
+              rplci->appl = appl;
+              add_p(rplci,CAI,"\x01\x80");
+              add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+              sig_req(rplci,ASSIGN,DSIG_ID);
+              send_req(rplci);
+            }
+            else
+            {
+              PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
+              SSparms = (byte *)SSstruct;
+              break;
+            }
+            rplci->internal_command = GETSERV_REQ_PEND;
+            rplci->number = Number;
+            rplci->appl = appl;
+            sig_req(rplci,S_SUPPORTED,0);
+            send_req(rplci);
+            return FALSE;
+            break;
+
+          case S_LISTEN:
+            if(parms->length==7)
+            {
+              if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+              {
+                dbug(1,dprintf("format wrong"));
+                Info = _WRONG_MESSAGE_FORMAT;
+                break;
+              }
+            }
+            else
+            {
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+            a->Notification_Mask[appl->Id-1] = GET_DWORD(ss_parms[2].info);
+            if(a->Notification_Mask[appl->Id-1] & SMASK_MWI) /* MWI active? */
+            {
+              if((i=get_plci(a)))
+              {
+                rplci = &a->plci[i-1];
+                rplci->appl = appl;
+                add_p(rplci,CAI,"\x01\x80");
+                add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+                sig_req(rplci,ASSIGN,DSIG_ID);
+                send_req(rplci);
+              }
+              else
+              {
+                break;
+              }
+              rplci->internal_command = GET_MWI_STATE;
+              rplci->number = Number;
+              sig_req(rplci,MWI_POLL,0);
+              send_req(rplci);
+            }
+            break;
+
+          case S_HOLD:
+            api_parse(&parms->info[1],(word)parms->length,"ws",ss_parms);
+            if(plci && plci->State && plci->SuppState==IDLE)
+            {
+              plci->SuppState = HOLD_REQUEST;
+              plci->command = C_HOLD_REQ;
+              add_s(plci,CAI,&ss_parms[1]);
+              sig_req(plci,CALL_HOLD,0);
+              send_req(plci);
+              return FALSE;
+            }
+            else Info = 0x3010;                    /* wrong state           */
+            break;
+          case S_RETRIEVE:
+            if(plci && plci->State && plci->SuppState==CALL_HELD)
+            {
+              if(Id & EXT_CONTROLLER)
+              {
+                if(AdvCodecSupport(a, plci, appl, 0))
+                {
+                  Info = 0x3010;                    /* wrong state           */
+                  break;
+                }
+              }
+              else plci->tel = 0;
+
+              plci->SuppState = RETRIEVE_REQUEST;
+              plci->command = C_RETRIEVE_REQ;
+              if(plci->spoofed_msg==SPOOFING_REQUIRED)
+              {
+                plci->spoofed_msg = CALL_RETRIEVE;
+                plci->internal_command = BLOCK_PLCI;
+                plci->command = 0;
+                dbug(1,dprintf("Spoof"));
+                return FALSE;
+              }
+              else
+              {
+                sig_req(plci,CALL_RETRIEVE,0);
+                send_req(plci);
+                return FALSE;
+              }
+            }
+            else Info = 0x3010;                    /* wrong state           */
+            break;
+          case S_SUSPEND:
+            if(parms->length)
+            {
+              if(api_parse(&parms->info[1],(word)parms->length,"wbs",ss_parms))
+              {
+                dbug(1,dprintf("format wrong"));
+                Info = _WRONG_MESSAGE_FORMAT;
+                break;
+              }
+            }
+            if(plci && plci->State)
+            {
+              add_s(plci,CAI,&ss_parms[2]);
+              plci->command = SUSPEND_REQ;
+              sig_req(plci,SUSPEND,0);
+              plci->State = SUSPENDING;
+              send_req(plci);
+            }
+            else Info = 0x3010;                    /* wrong state           */
+            break;
+
+          case S_RESUME:
+            if(!(i=get_plci(a)) )
+            {
+              Info = _OUT_OF_PLCI;
+              break;
+            }
+            rplci = &a->plci[i-1];
+            rplci->appl = appl;
+            rplci->number = Number;
+            rplci->tel = 0;
+            rplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+            /* check 'external controller' bit for codec support */
+            if(Id & EXT_CONTROLLER)
+            {
+              if(AdvCodecSupport(a, rplci, appl, 0) )
+              {
+                rplci->Id = 0;
+                Info = 0x300A;
+                break;
+              }
+            }
+            if(parms->length)
+            {
+              if(api_parse(&parms->info[1],(word)parms->length,"wbs",ss_parms))
+              {
+                dbug(1,dprintf("format wrong"));
+                rplci->Id = 0;
+                Info = _WRONG_MESSAGE_FORMAT;
+                break;
+              }
+            }
+            dummy.length = 0;
+            dummy.info = "\x00";
+            add_b1(rplci, &dummy, 0, 0);
+            if (a->Info_Mask[appl->Id-1] & 0x200)
+            {
+              /* early B3 connect (CIP mask bit 9) no release after a disc */
+              add_p(rplci,LLI,"\x01\x01");
+            }
+            add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+            sig_req(rplci,ASSIGN,DSIG_ID);
+            send_req(rplci);
+            add_s(rplci,CAI,&ss_parms[2]);
+            rplci->command = RESUME_REQ;
+            sig_req(rplci,RESUME,0);
+            rplci->State = RESUMING;
+            send_req(rplci);
+            break;
+
+          case S_CONF_BEGIN: /* Request */
+          case S_CONF_DROP:
+          case S_CONF_ISOLATE:
+          case S_CONF_REATTACH:
+            if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+            {
+              dbug(1,dprintf("format wrong"));
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+            if(plci && plci->State && ((plci->SuppState==IDLE)||(plci->SuppState==CALL_HELD)))
+            {
+              d = GET_DWORD(ss_parms[2].info);     
+              if(d>=0x80)
+              {
+                dbug(1,dprintf("format wrong"));
+                Info = _WRONG_MESSAGE_FORMAT;
+                break;
+              }
+              plci->ptyState = (byte)SSreq;
+              plci->command = 0;
+              cai[0] = 2;
+              switch(SSreq)
+              {
+              case S_CONF_BEGIN:
+                  cai[1] = CONF_BEGIN;
+                  plci->internal_command = CONF_BEGIN_REQ_PEND;
+                  break;
+              case S_CONF_DROP:
+                  cai[1] = CONF_DROP;
+                  plci->internal_command = CONF_DROP_REQ_PEND;
+                  break;
+              case S_CONF_ISOLATE:
+                  cai[1] = CONF_ISOLATE;
+                  plci->internal_command = CONF_ISOLATE_REQ_PEND;
+                  break;
+              case S_CONF_REATTACH:
+                  cai[1] = CONF_REATTACH;
+                  plci->internal_command = CONF_REATTACH_REQ_PEND;
+                  break;
+              }
+              cai[2] = (byte)d; /* Conference Size resp. PartyId */
+              add_p(plci,CAI,cai);
+              sig_req(plci,S_SERVICE,0);
+              send_req(plci);
+              return FALSE;
+            }
+            else Info = 0x3010;                    /* wrong state           */
+            break;
+
+          case S_ECT:
+          case S_3PTY_BEGIN:
+          case S_3PTY_END:
+          case S_CONF_ADD:
+            if(parms->length==7)
+            {
+              if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+              {
+                dbug(1,dprintf("format wrong"));
+                Info = _WRONG_MESSAGE_FORMAT;
+                break;
+              }
+            }
+            else if(parms->length==8) /* workaround for the T-View-S */
+            {
+              if(api_parse(&parms->info[1],(word)parms->length,"wbdb",ss_parms))
+              {
+                dbug(1,dprintf("format wrong"));
+                Info = _WRONG_MESSAGE_FORMAT;
+                break;
+              }
+            }
+            else
+            {
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+            if(!msg[1].length)
+            {
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+            if (!plci)
+            {
+              Info = _WRONG_IDENTIFIER;
+              break;
+            }
+            relatedPLCIvalue = GET_DWORD(ss_parms[2].info);
+            relatedPLCIvalue &= 0x0000FFFF;
+            dbug(1,dprintf("PTY/ECT/addCONF,relPLCI=%lx",relatedPLCIvalue));
+            /* controller starts with 0 up to (max_adapter - 1) */
+            if (((relatedPLCIvalue & 0x7f) == 0)
+             || (MapController ((byte)(relatedPLCIvalue & 0x7f)) == 0)
+             || (MapController ((byte)(relatedPLCIvalue & 0x7f)) > max_adapter))
+            {
+              if(SSreq==S_3PTY_END)
+              {
+                dbug(1, dprintf("wrong Controller use 2nd PLCI=PLCI"));
+                rplci = plci;
+              }
+              else
+              {
+                Info = 0x3010;                    /* wrong state           */
+                break;
+              }
+            }
+            else
+            {  
+              relatedadapter = &adapter[MapController ((byte)(relatedPLCIvalue & 0x7f))-1];
+              relatedPLCIvalue >>=8;
+              /* find PLCI PTR*/
+              for(i=0,rplci=NULL;i<relatedadapter->max_plci;i++)
+              {
+                if(relatedadapter->plci[i].Id == (byte)relatedPLCIvalue)
+                {
+                  rplci = &relatedadapter->plci[i];
+                }
+              }
+              if(!rplci || !relatedPLCIvalue)
+              {
+                if(SSreq==S_3PTY_END)
+                {
+                  dbug(1, dprintf("use 2nd PLCI=PLCI"));
+                  rplci = plci;
+                }
+                else
+                {
+                  Info = 0x3010;                    /* wrong state           */
+                  break;
+                }
+              }
+            }
+/*
+            dbug(1,dprintf("rplci:%x",rplci));
+            dbug(1,dprintf("plci:%x",plci));
+            dbug(1,dprintf("rplci->ptyState:%x",rplci->ptyState));
+            dbug(1,dprintf("plci->ptyState:%x",plci->ptyState));
+            dbug(1,dprintf("SSreq:%x",SSreq));
+            dbug(1,dprintf("rplci->internal_command:%x",rplci->internal_command));
+            dbug(1,dprintf("rplci->appl:%x",rplci->appl));
+            dbug(1,dprintf("rplci->Id:%x",rplci->Id));
+*/
+            /* send PTY/ECT req, cannot check all states because of US stuff */
+            if( !rplci->internal_command && rplci->appl )
+            {
+              plci->command = 0;
+              rplci->relatedPTYPLCI = plci;
+              plci->relatedPTYPLCI = rplci;
+              rplci->ptyState = (byte)SSreq;
+              if(SSreq==S_ECT)
+              {
+                rplci->internal_command = ECT_REQ_PEND;
+                cai[1] = ECT_EXECUTE;
+
+                rplci->vswitchstate=0;
+                rplci->vsprot=0;
+                rplci->vsprotdialect=0;
+                plci->vswitchstate=0;
+                plci->vsprot=0;
+                plci->vsprotdialect=0;
+
+              }
+              else if(SSreq==S_CONF_ADD)
+              {
+                rplci->internal_command = CONF_ADD_REQ_PEND;
+                cai[1] = CONF_ADD;
+              }
+              else
+              {
+                rplci->internal_command = PTY_REQ_PEND;
+                cai[1] = (byte)(SSreq-3);
+              }
+              rplci->number = Number;
+              if(plci!=rplci) /* explicit invocation */
+              {
+                cai[0] = 2;
+                cai[2] = plci->Sig.Id;
+                dbug(1,dprintf("explicit invocation"));
+              }
+              else
+              {
+                dbug(1,dprintf("implicit invocation"));
+                cai[0] = 1;
+              }
+              add_p(rplci,CAI,cai);
+              sig_req(rplci,S_SERVICE,0);
+              send_req(rplci);
+              return FALSE;
+            }
+            else
+            {
+              dbug(0,dprintf("Wrong line"));
+              Info = 0x3010;                    /* wrong state           */
+              break;
+            }
+            break;
+
+          case S_CALL_DEFLECTION:
+            if(api_parse(&parms->info[1],(word)parms->length,"wbwss",ss_parms))
+            {
+              dbug(1,dprintf("format wrong"));
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+            if (!plci)
+            {
+              Info = _WRONG_IDENTIFIER;
+              break;
+            }
+            /* reuse unused screening indicator */
+            ss_parms[3].info[3] = (byte)GET_WORD(&(ss_parms[2].info[0]));
+            plci->command = 0;
+            plci->internal_command = CD_REQ_PEND;
+            appl->CDEnable = TRUE;
+            cai[0] = 1;
+            cai[1] = CALL_DEFLECTION;
+            add_p(plci,CAI,cai);
+            add_p(plci,CPN,ss_parms[3].info);
+            sig_req(plci,S_SERVICE,0);
+            send_req(plci);
+            return FALSE;
+            break;
+
+          case S_CALL_FORWARDING_START:
+            if(api_parse(&parms->info[1],(word)parms->length,"wbdwwsss",ss_parms))
+            {
+              dbug(1,dprintf("format wrong"));
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+
+            if((i=get_plci(a)))
+            {
+              rplci = &a->plci[i-1];
+              rplci->appl = appl;
+              add_p(rplci,CAI,"\x01\x80");
+              add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+              sig_req(rplci,ASSIGN,DSIG_ID);
+              send_req(rplci);
+            }
+            else
+            {
+              Info = _OUT_OF_PLCI;
+              break;
+            }
+
+            /* reuse unused screening indicator */
+            rplci->internal_command = CF_START_PEND;
+            rplci->appl = appl;
+            rplci->number = Number;
+            appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
+            cai[0] = 2;
+            cai[1] = 0x70|(byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
+            cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
+            add_p(rplci,CAI,cai);
+            add_p(rplci,OAD,ss_parms[5].info);
+            add_p(rplci,CPN,ss_parms[6].info);
+            sig_req(rplci,S_SERVICE,0);
+            send_req(rplci);
+            return FALSE;
+            break;
+
+          case S_INTERROGATE_DIVERSION:
+          case S_INTERROGATE_NUMBERS:
+          case S_CALL_FORWARDING_STOP:
+          case S_CCBS_REQUEST:
+          case S_CCBS_DEACTIVATE:
+          case S_CCBS_INTERROGATE:
+            switch(SSreq)
+            {
+            case S_INTERROGATE_NUMBERS:
+                if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+                {
+                  dbug(0,dprintf("format wrong"));
+                  Info = _WRONG_MESSAGE_FORMAT;
+                }
+                break;
+            case S_CCBS_REQUEST:
+            case S_CCBS_DEACTIVATE:
+                if(api_parse(&parms->info[1],(word)parms->length,"wbdw",ss_parms))
+                {
+                  dbug(0,dprintf("format wrong"));
+                  Info = _WRONG_MESSAGE_FORMAT;
+                }
+                break;
+            case S_CCBS_INTERROGATE:
+                if(api_parse(&parms->info[1],(word)parms->length,"wbdws",ss_parms))
+                {
+                  dbug(0,dprintf("format wrong"));
+                  Info = _WRONG_MESSAGE_FORMAT;
+                }
+                break;
+            default:
+            if(api_parse(&parms->info[1],(word)parms->length,"wbdwws",ss_parms))
+            {
+              dbug(0,dprintf("format wrong"));
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+                break;
+            }
+
+            if(Info) break;
+            if((i=get_plci(a)))
+            {
+              rplci = &a->plci[i-1];
+              switch(SSreq)
+              {
+                case S_INTERROGATE_DIVERSION: /* use cai with S_SERVICE below */
+                  cai[1] = 0x60|(byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
+                  rplci->internal_command = INTERR_DIVERSION_REQ_PEND; /* move to rplci if assigned */
+                  break;
+                case S_INTERROGATE_NUMBERS: /* use cai with S_SERVICE below */
+                  cai[1] = DIVERSION_INTERROGATE_NUM; /* Function */
+                  rplci->internal_command = INTERR_NUMBERS_REQ_PEND; /* move to rplci if assigned */
+                  break;
+                case S_CALL_FORWARDING_STOP:
+                  rplci->internal_command = CF_STOP_PEND;
+                  cai[1] = 0x80|(byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
+                  break;
+                case S_CCBS_REQUEST:
+                  cai[1] = CCBS_REQUEST;
+                  rplci->internal_command = CCBS_REQUEST_REQ_PEND;
+                  break;
+                case S_CCBS_DEACTIVATE:
+                  cai[1] = CCBS_DEACTIVATE;
+                  rplci->internal_command = CCBS_DEACTIVATE_REQ_PEND;
+                  break;
+                case S_CCBS_INTERROGATE:
+                  cai[1] = CCBS_INTERROGATE;
+                  rplci->internal_command = CCBS_INTERROGATE_REQ_PEND;
+                  break;
+                default:
+                  cai[1] = 0;
+                break;
+              }
+              rplci->appl = appl;
+              rplci->number = Number;
+              add_p(rplci,CAI,"\x01\x80");
+              add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+              sig_req(rplci,ASSIGN,DSIG_ID);
+              send_req(rplci);
+            }
+            else
+            {
+              Info = _OUT_OF_PLCI;
+              break;
+            }
+
+            appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
+            switch(SSreq)
+            {
+            case S_INTERROGATE_NUMBERS:
+                cai[0] = 1;
+                add_p(rplci,CAI,cai);
+                break;
+            case S_CCBS_REQUEST:
+            case S_CCBS_DEACTIVATE:
+                cai[0] = 3;
+                PUT_WORD(&cai[2],GET_WORD(&(ss_parms[3].info[0])));
+                add_p(rplci,CAI,cai);
+                break;
+            case S_CCBS_INTERROGATE:
+                cai[0] = 3;
+                PUT_WORD(&cai[2],GET_WORD(&(ss_parms[3].info[0])));
+                add_p(rplci,CAI,cai);
+                add_p(rplci,OAD,ss_parms[4].info);
+                break;
+            default:
+            cai[0] = 2;
+            cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
+            add_p(rplci,CAI,cai);
+            add_p(rplci,OAD,ss_parms[5].info);
+                break;
+            }
+                        
+            sig_req(rplci,S_SERVICE,0);
+            send_req(rplci);
+            return FALSE;
+            break;
+
+          case S_MWI_ACTIVATE:
+            if(api_parse(&parms->info[1],(word)parms->length,"wbwdwwwssss",ss_parms))
+            {
+              dbug(1,dprintf("format wrong"));
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+            if(!plci)
+            {                               
+              if((i=get_plci(a)))
+              {
+                rplci = &a->plci[i-1];
+                rplci->appl = appl;
+                rplci->cr_enquiry=TRUE;
+                add_p(rplci,CAI,"\x01\x80");
+                add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+                sig_req(rplci,ASSIGN,DSIG_ID);
+                send_req(rplci);
+              }
+              else
+              {
+                Info = _OUT_OF_PLCI;
+                break;
+              }
+            }
+            else
+            {
+              rplci = plci;
+              rplci->cr_enquiry=FALSE;
+            }
+
+            rplci->command = 0;
+            rplci->internal_command = MWI_ACTIVATE_REQ_PEND;
+            rplci->appl = appl;
+            rplci->number = Number;
+
+            cai[0] = 13;
+            cai[1] = ACTIVATION_MWI; /* Function */
+            PUT_WORD(&cai[2],GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
+            PUT_DWORD(&cai[4],GET_DWORD(&(ss_parms[3].info[0]))); /* Number of Messages */
+            PUT_WORD(&cai[8],GET_WORD(&(ss_parms[4].info[0]))); /* Message Status */
+            PUT_WORD(&cai[10],GET_WORD(&(ss_parms[5].info[0]))); /* Message Reference */
+            PUT_WORD(&cai[12],GET_WORD(&(ss_parms[6].info[0]))); /* Invocation Mode */
+            add_p(rplci,CAI,cai);
+            add_p(rplci,CPN,ss_parms[7].info); /* Receiving User Number */
+            add_p(rplci,OAD,ss_parms[8].info); /* Controlling User Number */
+            add_p(rplci,OSA,ss_parms[9].info); /* Controlling User Provided Number */
+            add_p(rplci,UID,ss_parms[10].info); /* Time */
+            sig_req(rplci,S_SERVICE,0);
+            send_req(rplci);
+            return FALSE;
+
+          case S_MWI_DEACTIVATE:
+            if(api_parse(&parms->info[1],(word)parms->length,"wbwwss",ss_parms))
+            {
+              dbug(1,dprintf("format wrong"));
+              Info = _WRONG_MESSAGE_FORMAT;
+              break;
+            }
+            if(!plci)
+            {                               
+              if((i=get_plci(a)))
+              {
+                rplci = &a->plci[i-1];
+                rplci->appl = appl;
+                rplci->cr_enquiry=TRUE;
+                add_p(rplci,CAI,"\x01\x80");
+                add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+                sig_req(rplci,ASSIGN,DSIG_ID);
+                send_req(rplci);
+              }
+              else
+              {
+                Info = _OUT_OF_PLCI;
+                break;
+              }
+            }
+            else
+            {
+              rplci = plci;
+              rplci->cr_enquiry=FALSE;
+            }
+
+            rplci->command = 0;
+            rplci->internal_command = MWI_DEACTIVATE_REQ_PEND;
+            rplci->appl = appl;
+            rplci->number = Number;
+
+            cai[0] = 5;
+            cai[1] = DEACTIVATION_MWI; /* Function */
+            PUT_WORD(&cai[2],GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
+            PUT_WORD(&cai[4],GET_WORD(&(ss_parms[3].info[0]))); /* Invocation Mode */
+            add_p(rplci,CAI,cai);
+            add_p(rplci,CPN,ss_parms[4].info); /* Receiving User Number */
+            add_p(rplci,OAD,ss_parms[5].info); /* Controlling User Number */
+            sig_req(rplci,S_SERVICE,0);
+            send_req(rplci);
+            return FALSE;
+
+          default:
+            Info = 0x300E;  /* not supported */
+            break;
+        }
+        break; /* case SELECTOR_SU_SERV: end */
+
+
+      case SELECTOR_DTMF:
+        return (dtmf_request (Id, Number, a, plci, appl, msg));
+
+
+
+      case SELECTOR_LINE_INTERCONNECT:
+        return (mixer_request (Id, Number, a, plci, appl, msg));
+
+
+
+      case PRIV_SELECTOR_ECHO_CANCELLER:
+        appl->appl_flags |= APPL_FLAG_PRIV_EC_SPEC;
+        return (ec_request (Id, Number, a, plci, appl, msg));
+
+      case SELECTOR_ECHO_CANCELLER:
+        appl->appl_flags &= ~APPL_FLAG_PRIV_EC_SPEC;
+        return (ec_request (Id, Number, a, plci, appl, msg));
+
+
+      case SELECTOR_V42BIS:
+      default:
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+    } /* end of switch(selector) */
+  }
+
+  dbug(1,dprintf("SendFacRc"));
+  sendf(appl,
+        _FACILITY_R|CONFIRM,
+        Id,
+        Number,
+        "wws",Info,selector,SSparms);
+  return FALSE;
+}
+
+byte facility_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  dbug(1,dprintf("facility_res"));
+  return FALSE;
+}
+
+byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word Info = 0;
+  byte req;
+  byte len;
+  word w;
+  word fax_control_bits, fax_feature_bits, fax_info_change;
+  API_PARSE * ncpi;
+    byte pvc[2];
+
+    API_PARSE fax_parms[9];
+  word i;
+
+
+  dbug(1,dprintf("connect_b3_req"));
+  if(plci)
+  {
+    if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING)
+     || (plci->State == INC_DIS_PENDING) || (plci->SuppState != IDLE))
+    {
+      Info = _WRONG_STATE;
+    }
+    else
+    {
+      /* local reply if assign unsuccessfull
+         or B3 protocol allows only one layer 3 connection
+           and already connected
+             or B2 protocol not any LAPD
+               and connect_b3_req contradicts originate/answer direction */
+      if (!plci->NL.Id
+       || (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
+        && ((plci->channels != 0)
+         || (((plci->B2_prot != B2_SDLC) && (plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL))
+          && ((plci->call_dir & CALL_DIR_ANSWER) && !(plci->call_dir & CALL_DIR_FORCE_OUTG_NL))))))
+      {
+        dbug(1,dprintf("B3 already connected=%d or no NL.Id=0x%x, dir=%d sstate=0x%x",
+                       plci->channels,plci->NL.Id,plci->call_dir,plci->SuppState));
+        Info = _WRONG_STATE;
+        sendf(appl,                                                        
+              _CONNECT_B3_R|CONFIRM,
+              Id,
+              Number,
+              "w",Info);
+        return FALSE;
+      }
+      plci->requested_options_conn = 0;
+
+      req = N_CONNECT;
+      ncpi = &parms[0];
+      if(plci->B3_prot==2 || plci->B3_prot==3)
+      {
+        if(ncpi->length>2)
+        {
+          /* check for PVC */
+          if(ncpi->info[2] || ncpi->info[3])
+          {
+            pvc[0] = ncpi->info[3];
+            pvc[1] = ncpi->info[2];
+            add_d(plci,2,pvc);
+            req = N_RESET;
+          }
+          else
+          {
+            if(ncpi->info[1] &1) req = N_CONNECT | N_D_BIT;
+            add_d(plci,(word)(ncpi->length-3),&ncpi->info[4]);
+          }
+        }
+      }
+      else if(plci->B3_prot==5)
+      {
+        if (plci->NL.Id && !plci->nl_remove_id)
+        {
+          fax_control_bits = GET_WORD(&((T30_INFO   *)plci->fax_connect_info_buffer)->control_bits_low);
+          fax_feature_bits = GET_WORD(&((T30_INFO   *)plci->fax_connect_info_buffer)->feature_bits_low);
+          if (!(fax_control_bits & T30_CONTROL_BIT_MORE_DOCUMENTS)
+           || (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS))
+          {
+            len = (byte)(&(((T30_INFO *) 0)->universal_6));
+            fax_info_change = FALSE;
+            if (ncpi->length >= 4)
+            {
+              w = GET_WORD(&ncpi->info[3]);
+              if ((w & 0x0001) != ((word)(((T30_INFO   *)(plci->fax_connect_info_buffer))->resolution & 0x0001)))
+              {
+                ((T30_INFO   *)(plci->fax_connect_info_buffer))->resolution =
+                  (byte)((((T30_INFO   *)(plci->fax_connect_info_buffer))->resolution & ~T30_RESOLUTION_R8_0770_OR_200) |
+                  ((w & 0x0001) ? T30_RESOLUTION_R8_0770_OR_200 : 0));
+                fax_info_change = TRUE;
+              }
+              fax_control_bits &= ~(T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
+              if (w & 0x0002)  /* Fax-polling request */
+                fax_control_bits |= T30_CONTROL_BIT_REQUEST_POLLING;
+              if ((w & 0x0004) /* Request to send / poll another document */
+               && (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS))
+              {
+                fax_control_bits |= T30_CONTROL_BIT_MORE_DOCUMENTS;
+              }
+              if (ncpi->length >= 6)
+              {
+                w = GET_WORD(&ncpi->info[5]);
+                if (((byte) w) != ((T30_INFO   *)(plci->fax_connect_info_buffer))->data_format)
+                {
+                  ((T30_INFO   *)(plci->fax_connect_info_buffer))->data_format = (byte) w;
+                  fax_info_change = TRUE;
+                }
+
+                if ((a->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+                 && (GET_WORD(&ncpi->info[5]) & 0x8000)) /* Private SEP/SUB/PWD enable */
+                {
+                  plci->requested_options_conn |= (1L << PRIVATE_FAX_SUB_SEP_PWD);
+                }
+                if ((a->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
+                 && (GET_WORD(&ncpi->info[5]) & 0x4000)) /* Private non-standard facilities enable */
+                {
+                  plci->requested_options_conn |= (1L << PRIVATE_FAX_NONSTANDARD);
+                }
+                fax_control_bits &= ~(T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_SEL_POLLING |
+                  T30_CONTROL_BIT_ACCEPT_PASSWORD);
+                if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id-1])
+                  & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+                {
+                  if (api_parse (&ncpi->info[1], ncpi->length, "wwwwsss", fax_parms))
+                    Info = _WRONG_MESSAGE_FORMAT;
+                  else
+                  {
+                    if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id-1])
+                      & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+      {
+                    fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
+                    if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
+                      fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
+      }
+                    w = fax_parms[4].length;
+                    if (w > 20)
+                      w = 20;
+                    ((T30_INFO   *)(plci->fax_connect_info_buffer))->station_id_len = (byte) w;
+                    for (i = 0; i < w; i++)
+                      ((T30_INFO   *)(plci->fax_connect_info_buffer))->station_id[i] = fax_parms[4].info[1+i];
+                    ((T30_INFO   *)(plci->fax_connect_info_buffer))->head_line_len = 0;
+                    len = (byte)(((T30_INFO *) 0)->station_id + 20);
+                    w = fax_parms[5].length;
+                    if (w > 20)
+                      w = 20;
+                    plci->fax_connect_info_buffer[len++] = (byte) w;
+                    for (i = 0; i < w; i++)
+                      plci->fax_connect_info_buffer[len++] = fax_parms[5].info[1+i];
+                    w = fax_parms[6].length;
+                    if (w > 20)
+                      w = 20;
+                    plci->fax_connect_info_buffer[len++] = (byte) w;
+                    for (i = 0; i < w; i++)
+                      plci->fax_connect_info_buffer[len++] = fax_parms[6].info[1+i];
+                    if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id-1])
+                      & (1L << PRIVATE_FAX_NONSTANDARD))
+      {
+                      if (api_parse (&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
+        {
+                        dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+                        plci->fax_connect_info_buffer[len++] = 0;
+        }
+                      else
+                      {
+          if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
+            plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
+   plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
+          for (i = 0; i < fax_parms[7].length; i++)
+     plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1+i];
+                      }
+                    }
+                  }
+                }
+                else
+                {
+                  len = (byte)(&(((T30_INFO *) 0)->universal_6));
+                }
+                fax_info_change = TRUE;
+
+              }
+              if (fax_control_bits != GET_WORD(&((T30_INFO   *)plci->fax_connect_info_buffer)->control_bits_low))
+              {
+                PUT_WORD (&((T30_INFO   *)plci->fax_connect_info_buffer)->control_bits_low, fax_control_bits);
+                fax_info_change = TRUE;
+              }
+            }
+            if (Info == GOOD)
+            {
+              plci->fax_connect_info_length = len;
+              if (fax_info_change)
+              {
+                if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
+                {
+                  start_internal_command (Id, plci, fax_connect_info_command);
+                  return FALSE;
+                }
+                else
+                {
+                  start_internal_command (Id, plci, fax_adjust_b23_command);
+                  return FALSE;
+                }
+              }
+            }
+          }
+          else  Info = _WRONG_STATE;
+        }
+        else  Info = _WRONG_STATE;
+      }
+
+      else if (plci->B3_prot == B3_RTP)
+      {
+        plci->internal_req_buffer[0] = ncpi->length + 1;
+        plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
+        for (w = 0; w < ncpi->length; w++)
+          plci->internal_req_buffer[2+w] = ncpi->info[1+w];
+        start_internal_command (Id, plci, rtp_connect_b3_req_command);
+        return FALSE;
+      }
+
+      if(!Info)
+      {
+        nl_req_ncci(plci,req,0);
+        return 1;
+      }
+    }
+  }
+  else Info = _WRONG_IDENTIFIER;
+
+  sendf(appl,
+        _CONNECT_B3_R|CONFIRM,
+        Id,
+        Number,
+        "w",Info);
+  return FALSE;
+}
+
+byte connect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word ncci;
+  API_PARSE * ncpi;
+  byte req;
+
+  word w;
+
+
+    API_PARSE fax_parms[9];
+  word i;
+  byte len;
+
+
+  dbug(1,dprintf("connect_b3_res"));
+
+  ncci = (word)(Id>>16);
+  if(plci && ncci) {
+    if(a->ncci_state[ncci]==INC_CON_PENDING) {
+      if (GET_WORD (&parms[0].info[0]) != 0)
+      {
+        a->ncci_state[ncci] = OUTG_REJ_PENDING;
+        channel_request_xon (plci, a->ncci_ch[ncci]);
+        channel_xmit_xon (plci);
+        cleanup_ncci_data (plci, ncci);
+        nl_req_ncci(plci,N_DISC,(byte)ncci);
+        return 1;
+      }
+      a->ncci_state[ncci] = INC_ACT_PENDING;
+
+      req = N_CONNECT_ACK;
+      ncpi = &parms[1];
+      if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
+      {
+
+        if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1])
+          & (1L << PRIVATE_FAX_NONSTANDARD))
+ {
+   if (((plci->B3_prot == 4) || (plci->B3_prot == 5))
+    && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
+    && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
+   {
+            len = ((byte)(((T30_INFO *) 0)->station_id + 20));
+            if (plci->fax_connect_info_length < len)
+            {
+              ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
+              ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
+            }
+            if (api_parse (&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
+            {
+              dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+            }
+            else
+            {
+              if (plci->fax_connect_info_length <= len)
+                plci->fax_connect_info_buffer[len] = 0;
+              len += 1 + plci->fax_connect_info_buffer[len];
+              if (plci->fax_connect_info_length <= len)
+                plci->fax_connect_info_buffer[len] = 0;
+              len += 1 + plci->fax_connect_info_buffer[len];
+              if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
+                plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
+              plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
+              for (i = 0; i < fax_parms[7].length; i++)
+                plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1+i];
+            }
+            plci->fax_connect_info_length = len;
+            ((T30_INFO *)(plci->fax_connect_info_buffer))->code = 0;
+            start_internal_command (Id, plci, fax_connect_ack_command);
+     return FALSE;
+          }
+        }
+
+        nl_req_ncci(plci,req,(byte)ncci);
+        if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+         && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+        {
+          if (plci->B3_prot == 4)
+            sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+          else
+            sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+          plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+        }
+      }
+
+      else if (plci->B3_prot == B3_RTP)
+      {
+        plci->internal_req_buffer[0] = ncpi->length + 1;
+        plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
+        for (w = 0; w < ncpi->length; w++)
+          plci->internal_req_buffer[2+w] = ncpi->info[1+w];
+        start_internal_command (Id, plci, rtp_connect_b3_res_command);
+        return FALSE;
+      }
+
+      else
+      {
+        if(ncpi->length>2) {
+          if(ncpi->info[1] &1) req = N_CONNECT_ACK | N_D_BIT;
+          add_d(plci,(word)(ncpi->length-3),&ncpi->info[4]);
+        }
+        nl_req_ncci(plci,req,(byte)ncci);
+        sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+        if (plci->adjust_b_restore)
+        {
+          plci->adjust_b_restore = FALSE;
+          start_internal_command (Id, plci, adjust_b_restore);
+        }
+      }
+      return 1;
+    }
+  }
+  return FALSE;
+}
+
+byte connect_b3_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word ncci;
+
+  ncci = (word)(Id>>16);
+  dbug(1,dprintf("connect_b3_a_res(ncci=0x%x)",ncci));
+
+  if (plci && ncci && (plci->State != IDLE) && (plci->State != INC_DIS_PENDING)
+   && (plci->State != OUTG_DIS_PENDING))
+  {
+    if(a->ncci_state[ncci]==INC_ACT_PENDING) {
+      a->ncci_state[ncci] = CONNECTED;
+      if(plci->State!=INC_CON_CONNECTED_ALERT) plci->State = CONNECTED;
+      channel_request_xon (plci, a->ncci_ch[ncci]);
+      channel_xmit_xon (plci);
+    }
+  }
+  return FALSE;
+}
+
+byte disconnect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word Info;
+  word ncci;
+  API_PARSE * ncpi;
+
+  dbug(1,dprintf("disconnect_b3_req"));
+
+  Info = _WRONG_IDENTIFIER;
+  ncci = (word)(Id>>16);
+  if (plci && ncci)
+  {
+    Info = _WRONG_STATE;
+    if ((a->ncci_state[ncci] == CONNECTED)
+     || (a->ncci_state[ncci] == OUTG_CON_PENDING)
+     || (a->ncci_state[ncci] == INC_CON_PENDING)
+     || (a->ncci_state[ncci] == INC_ACT_PENDING))
+    {
+      a->ncci_state[ncci] = OUTG_DIS_PENDING;
+      channel_request_xon (plci, a->ncci_ch[ncci]);
+      channel_xmit_xon (plci);
+
+      if (a->ncci[ncci].data_pending
+       && ((plci->B3_prot == B3_TRANSPARENT)
+        || (plci->B3_prot == B3_T30)
+        || (plci->B3_prot == B3_T30_WITH_EXTENSIONS)))
+      {
+        plci->send_disc = (byte)ncci;
+        plci->command = 0;
+        return FALSE;
+      }
+      else
+      {
+        cleanup_ncci_data (plci, ncci);
+
+        if(plci->B3_prot==2 || plci->B3_prot==3)
+        {
+          ncpi = &parms[0];
+          if(ncpi->length>3)
+          {
+            add_d(plci, (word)(ncpi->length - 3) ,(byte   *)&(ncpi->info[4]));
+          }
+        }
+        nl_req_ncci(plci,N_DISC,(byte)ncci);
+      }
+      return 1;
+    }
+  }
+  sendf(appl,
+        _DISCONNECT_B3_R|CONFIRM,
+        Id,
+        Number,
+        "w",Info);
+  return FALSE;
+}
+
+byte disconnect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word ncci;
+  word i;
+
+  ncci = (word)(Id>>16);
+  dbug(1,dprintf("disconnect_b3_res(ncci=0x%x",ncci));
+  if(plci && ncci) {
+    plci->requested_options_conn = 0;
+    plci->fax_connect_info_length = 0;
+    plci->ncpi_state = 0x00;
+    if (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
+      && ((plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL)))
+    {
+      plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
+    }
+    for(i=0; i<MAX_CHANNELS_PER_PLCI && plci->inc_dis_ncci_table[i]!=(byte)ncci; i++);
+    if(i<MAX_CHANNELS_PER_PLCI) {
+      if(plci->channels)plci->channels--;
+      for(; i<MAX_CHANNELS_PER_PLCI-1; i++) plci->inc_dis_ncci_table[i] = plci->inc_dis_ncci_table[i+1];
+      plci->inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI-1] = 0;
+
+      ncci_free_receive_buffers (plci, ncci);
+
+      if((plci->State==IDLE || plci->State==SUSPENDING) && !plci->channels){
+        if(plci->State == SUSPENDING){
+          sendf(plci->appl,
+                _FACILITY_I,
+                Id & 0xffffL,
+                0,
+                "ws", (word)3, "\x03\x04\x00\x00");
+          sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
+        }
+        plci_remove(plci);
+        plci->State=IDLE;
+      }
+    }
+    else
+    {
+      if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+       && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
+       && (a->ncci_state[ncci] == INC_DIS_PENDING))
+      {
+        ncci_free_receive_buffers (plci, ncci);
+
+        nl_req_ncci(plci,N_EDATA,(byte)ncci);
+
+        plci->adapter->ncci_state[ncci] = IDLE;
+        start_internal_command (Id, plci, fax_disconnect_command);
+        return 1;
+      }
+    }
+  }
+  return FALSE;
+}
+
+byte data_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  NCCI   *ncci_ptr;
+  DATA_B3_DESC   *data;
+  word Info;
+  word ncci;
+  word i;
+
+  dbug(1,dprintf("data_b3_req"));
+
+  Info = _WRONG_IDENTIFIER;
+  ncci = (word)(Id>>16);
+  dbug(1,dprintf("ncci=0x%x, plci=0x%x",ncci,plci));
+
+  if (plci && ncci)
+  {
+    Info = _WRONG_STATE;
+    if ((a->ncci_state[ncci] == CONNECTED)
+     || (a->ncci_state[ncci] == INC_ACT_PENDING))
+    {
+        /* queue data */
+      ncci_ptr = &(a->ncci[ncci]);
+      i = ncci_ptr->data_out + ncci_ptr->data_pending;
+      if (i >= MAX_DATA_B3)
+        i -= MAX_DATA_B3;
+      data = &(ncci_ptr->DBuffer[i]);
+      data->Number = Number;
+      if ((((byte   *)(parms[0].info)) >= ((byte   *)(plci->msg_in_queue)))
+       && (((byte   *)(parms[0].info)) < ((byte   *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+      {
+
+        data->P = (byte   *)(*((dword   *)(parms[0].info)));
+
+      }
+      else
+        data->P = TransmitBufferSet(appl,*(dword *)parms[0].info);
+      data->Length = GET_WORD(parms[1].info);
+      data->Handle = GET_WORD(parms[2].info);
+      data->Flags = GET_WORD(parms[3].info);
+      (ncci_ptr->data_pending)++;
+
+        /* check for delivery confirmation */
+      if (data->Flags & 0x0004)
+      {
+        i = ncci_ptr->data_ack_out + ncci_ptr->data_ack_pending;
+        if (i >= MAX_DATA_ACK)
+          i -= MAX_DATA_ACK;
+        ncci_ptr->DataAck[i].Number = data->Number;
+        ncci_ptr->DataAck[i].Handle = data->Handle;
+        (ncci_ptr->data_ack_pending)++;
+      }
+
+      send_data(plci);
+      return FALSE;
+    }
+  }
+  if (appl)
+  {
+    if (plci)
+    {
+      if ((((byte   *)(parms[0].info)) >= ((byte   *)(plci->msg_in_queue)))
+       && (((byte   *)(parms[0].info)) < ((byte   *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+      {
+
+        TransmitBufferFree (appl, (byte   *)(*((dword   *)(parms[0].info))));
+
+      }
+    }
+    sendf(appl,
+          _DATA_B3_R|CONFIRM,
+          Id,
+          Number,
+          "ww",GET_WORD(parms[2].info),Info);
+  }
+  return FALSE;
+}
+
+byte data_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word n;
+  word ncci;
+  word NCCIcode;
+
+  dbug(1,dprintf("data_b3_res"));
+
+  ncci = (word)(Id>>16);
+  if(plci && ncci) {
+    n = GET_WORD(parms[0].info);
+    dbug(1,dprintf("free(%d)",n));
+    NCCIcode = ncci | (((word) a->Id) << 8);
+    if(n<appl->MaxBuffer &&
+       appl->DataNCCI[n]==NCCIcode &&
+       (byte)(appl->DataFlags[n]>>8)==plci->Id) {
+      dbug(1,dprintf("found"));
+      appl->DataNCCI[n] = 0;
+
+      if (channel_can_xon (plci, a->ncci_ch[ncci])) {
+        channel_request_xon (plci, a->ncci_ch[ncci]);
+      }
+      channel_xmit_xon (plci);
+
+      if(appl->DataFlags[n] &4) {
+        nl_req_ncci(plci,N_DATA_ACK,(byte)ncci);
+        return 1;
+      }
+    }
+  }
+  return FALSE;
+}
+
+byte reset_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word Info;
+  word ncci;
+
+  dbug(1,dprintf("reset_b3_req"));
+
+  Info = _WRONG_IDENTIFIER;
+  ncci = (word)(Id>>16);
+  if(plci && ncci)
+  {
+    Info = _WRONG_STATE;
+    switch (plci->B3_prot)
+    {
+    case B3_ISO8208:
+    case B3_X25_DCE:
+      if(a->ncci_state[ncci]==CONNECTED)
+      {
+        nl_req_ncci(plci,N_RESET,(byte)ncci);
+        send_req(plci);
+        Info = GOOD;
+      }
+      break;
+    case B3_TRANSPARENT:
+      if(a->ncci_state[ncci]==CONNECTED)
+      {
+        start_internal_command (Id, plci, reset_b3_command);
+        Info = GOOD;
+      }
+      break;
+    }
+  }
+  /* reset_b3 must result in a reset_b3_con & reset_b3_Ind */
+  sendf(appl,
+        _RESET_B3_R|CONFIRM,
+        Id,
+        Number,
+        "w",Info);
+  return FALSE;
+}
+
+byte reset_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word ncci;
+
+  dbug(1,dprintf("reset_b3_res"));
+
+  ncci = (word)(Id>>16);
+  if(plci && ncci) {
+    switch (plci->B3_prot)
+    {
+    case B3_ISO8208:
+    case B3_X25_DCE:
+      if(a->ncci_state[ncci]==INC_RES_PENDING)
+      {
+        a->ncci_state[ncci] = CONNECTED;
+        nl_req_ncci(plci,N_RESET_ACK,(byte)ncci);
+        return TRUE;
+      }
+    break;
+    }
+  }
+  return FALSE;
+}
+
+byte connect_b3_t90_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word ncci;
+  API_PARSE * ncpi;
+  byte req;
+
+  dbug(1,dprintf("connect_b3_t90_a_res"));
+
+  ncci = (word)(Id>>16);
+  if(plci && ncci) {
+    if(a->ncci_state[ncci]==INC_ACT_PENDING) {
+      a->ncci_state[ncci] = CONNECTED;
+    }
+    else if(a->ncci_state[ncci]==INC_CON_PENDING) {
+      a->ncci_state[ncci] = CONNECTED;
+
+      req = N_CONNECT_ACK;
+
+        /* parms[0]==0 for CAPI original message definition! */
+      if(parms[0].info) {
+        ncpi = &parms[1];
+        if(ncpi->length>2) {
+          if(ncpi->info[1] &1) req = N_CONNECT_ACK | N_D_BIT;
+          add_d(plci,(word)(ncpi->length-3),&ncpi->info[4]);
+        }
+      }
+      nl_req_ncci(plci,req,(byte)ncci);
+      return 1;
+    }
+  }
+  return FALSE;
+}
+
+
+byte select_b_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  word Info=0;
+  word i;
+  byte tel;
+    API_PARSE bp_parms[7];
+
+  if(!plci || !msg)
+  {
+    Info = _WRONG_IDENTIFIER;
+  }
+  else
+  {
+    dbug(1,dprintf("select_b_req[%d],PLCI=0x%x,Tel=0x%x,NL=0x%x,appl=0x%x,sstate=0x%x",
+                   msg->length,plci->Id,plci->tel,plci->NL.Id,plci->appl,plci->SuppState));
+    dbug(1,dprintf("PlciState=0x%x",plci->State));
+    for(i=0;i<7;i++) bp_parms[i].length = 0;
+
+    /* check if no channel is open, no B3 connected only */
+    if((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING) || (plci->State == INC_DIS_PENDING)
+     || (plci->SuppState != IDLE) || plci->channels || plci->nl_remove_id)
+    {
+      Info = _WRONG_STATE;
+    }
+    /* check message format and fill bp_parms pointer */
+    else if(msg->length && api_parse(&msg->info[1], (word)msg->length, "wwwsss", bp_parms))
+    {
+      Info = _WRONG_MESSAGE_FORMAT;
+    }
+    else
+    {
+      if((plci->State==INC_CON_PENDING) || (plci->State==INC_CON_ALERT)) /* send alert tone inband to the network, */
+      {                                                                  /* e.g. Qsig or RBS or Cornet-N or xess PRI */
+        if(Id & EXT_CONTROLLER)
+        {
+          sendf(appl, _SELECT_B_REQ|CONFIRM, Id, Number, "w", 0x2002); /* wrong controller */
+          return 0;
+        }
+        plci->State=INC_CON_CONNECTED_ALERT;
+        plci->appl = appl;
+        clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+        dump_c_ind_mask (plci);
+        for(i=0; i<max_appl; i++) /* disconnect the other appls */
+        {                         /* its quasi a connect        */
+          if(test_c_ind_mask_bit (plci, i))
+            sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
+        }
+      }
+
+      api_save_msg(msg, "s", &plci->saved_msg);
+      tel = plci->tel;
+      if(Id & EXT_CONTROLLER)
+      {
+        if(tel) /* external controller in use by this PLCI */
+        {
+          if(a->AdvSignalAppl && a->AdvSignalAppl!=appl)
+          {
+            dbug(1,dprintf("Ext_Ctrl in use 1"));
+            Info = _WRONG_STATE;
+          }
+        }
+        else  /* external controller NOT in use by this PLCI ? */
+        {
+          if(a->AdvSignalPLCI)
+          {
+            dbug(1,dprintf("Ext_Ctrl in use 2"));
+            Info = _WRONG_STATE;
+          }
+          else /* activate the codec */
+          {
+            dbug(1,dprintf("Ext_Ctrl start"));
+            if(AdvCodecSupport(a, plci, appl, 0) )
+            {
+              dbug(1,dprintf("Error in codec procedures"));
+              Info = _WRONG_STATE;
+            }
+            else if(plci->spoofed_msg==SPOOFING_REQUIRED) /* wait until codec is active */
+            {
+              plci->spoofed_msg = AWAITING_SELECT_B;
+              plci->internal_command = BLOCK_PLCI; /* lock other commands */
+              plci->command = 0;
+              dbug(1,dprintf("continue if codec loaded"));
+              return FALSE;
+            }
+          }
+        }
+      }
+      else /* external controller bit is OFF */
+      {
+        if(tel) /* external controller in use, need to switch off */
+        {
+          if(a->AdvSignalAppl==appl)
+          {
+            CodecIdCheck(a, plci);
+            plci->tel = 0;
+            plci->adv_nl = 0;
+            dbug(1,dprintf("Ext_Ctrl disable"));
+          }
+          else
+          {
+            dbug(1,dprintf("Ext_Ctrl not requested"));
+          }
+        }
+      }
+      if (!Info)
+      {
+        if (plci->call_dir & CALL_DIR_OUT)
+          plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+        else if (plci->call_dir & CALL_DIR_IN)
+          plci->call_dir = CALL_DIR_IN | CALL_DIR_ANSWER;
+        start_internal_command (Id, plci, select_b_command);
+        return FALSE;
+      }
+    }
+  }
+  sendf(appl, _SELECT_B_REQ|CONFIRM, Id, Number, "w", Info);
+  return FALSE;
+}
+
+byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * parms)
+{
+  word command;
+  word i;
+  word ncci;
+  API_PARSE * m;
+    API_PARSE m_parms[5];
+  word codec;
+  byte req;
+  byte ch;
+  byte dir;
+  static byte chi[2] = {0x01,0x00};
+  static byte lli[2] = {0x01,0x00};
+  static byte codec_cai[2] = {0x01,0x01};
+  static byte null_msg = {0};
+  static API_PARSE null_parms = { 0, &null_msg };
+  PLCI   * v_plci;
+  word Info=0;
+
+  dbug(1,dprintf("manufacturer_req"));
+  for(i=0;i<5;i++) m_parms[i].length = 0;
+
+  if(GET_DWORD(parms[0].info)!=_DI_MANU_ID) {
+    Info = _WRONG_MESSAGE_FORMAT;
+  }
+  command = GET_WORD(parms[1].info);
+  m = &parms[2];
+  if (!Info)
+  {
+    switch(command) {
+    case _DI_ASSIGN_PLCI:
+      if(api_parse(&m->info[1],(word)m->length,"wbbs",m_parms)) {
+        Info = _WRONG_MESSAGE_FORMAT;
+        break;
+      }
+      codec = GET_WORD(m_parms[0].info);
+      ch = m_parms[1].info[0];
+      dir = m_parms[2].info[0];
+      if((i=get_plci(a))) {
+        plci = &a->plci[i-1];
+        plci->appl = appl;
+        plci->command = _MANUFACTURER_R;
+        plci->m_command = command;
+        plci->number = Number;
+        plci->State = LOCAL_CONNECT;
+        Id = ( ((word)plci->Id<<8)|plci->adapter->Id|0x80);
+        dbug(1,dprintf("ManCMD,plci=0x%x",Id));
+
+        if((ch==1 || ch==2) && (dir<=2)) {
+          chi[1] = (byte)(0x80|ch);
+          lli[1] = 0;
+          plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+          switch(codec)
+          {
+          case 0:
+            Info = add_b1(plci,&m_parms[3],0,0);
+            break;
+          case 1:
+            add_p(plci,CAI,codec_cai);
+            break;
+          /* manual 'swich on' to the codec support without signalling */
+          /* first 'assign plci' with this function, then use */
+          case 2:
+            if(AdvCodecSupport(a, plci, appl, 0) ) {
+              Info = _RESOURCE_ERROR;
+            }
+            else {
+              Info = add_b1(plci,&null_parms,0,B1_FACILITY_LOCAL);
+              lli[1] = 0x10; /* local call codec stream */
+            }
+            break;
+          }
+
+          plci->State = LOCAL_CONNECT;
+          plci->manufacturer = TRUE;
+          plci->command = _MANUFACTURER_R;
+          plci->m_command = command;
+          plci->number = Number;
+
+          if(!Info)
+          {
+            add_p(plci,LLI,lli);
+            add_p(plci,CHI,chi);
+            add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+            sig_req(plci,ASSIGN,DSIG_ID);
+
+            if(!codec)
+            {
+              Info = add_b23(plci,&m_parms[3]);
+              if(!Info)
+              {
+                nl_req_ncci(plci,ASSIGN,0);
+                send_req(plci);
+              }
+            }
+            if(!Info)
+            {
+              dbug(1,dprintf("dir=0x%x,spoof=0x%x",dir,plci->spoofed_msg));
+              if (plci->spoofed_msg==SPOOFING_REQUIRED)
+              {
+                api_save_msg (m_parms, "wbbs", &plci->saved_msg);
+                plci->spoofed_msg = AWAITING_MANUF_CON;
+                plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
+                plci->command = 0;
+                send_req(plci);
+                return FALSE;
+              }
+              if(dir==1) {
+                sig_req(plci,CALL_REQ,0);
+              }
+              else if(!dir){
+                sig_req(plci,LISTEN_REQ,0);
+              }
+              send_req(plci);
+            }
+            else
+            {
+              sendf(appl,
+                    _MANUFACTURER_R|CONFIRM,
+                    Id,
+                    Number,
+                    "dww",_DI_MANU_ID,command,Info);
+              return 2;
+            }
+          }
+        }
+      }
+      else  Info = _OUT_OF_PLCI;
+      break;
+
+    case _DI_IDI_CTRL:
+      if(!plci)
+      {
+        Info = _WRONG_IDENTIFIER;
+        break;
+      }
+      if(api_parse(&m->info[1],(word)m->length,"bs",m_parms)) {
+        Info = _WRONG_MESSAGE_FORMAT;
+        break;
+      }
+      req = m_parms[0].info[0];
+      plci->command = _MANUFACTURER_R;
+      plci->m_command = command;
+      plci->number = Number;
+      if(req==CALL_REQ)
+      {
+        plci->b_channel = getChannel(&m_parms[1]);
+        mixer_set_bchannel_id_esc (plci, plci->b_channel);
+        if(plci->spoofed_msg==SPOOFING_REQUIRED)
+        {
+          plci->spoofed_msg = CALL_REQ | AWAITING_MANUF_CON;
+          plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
+          plci->command = 0;
+          break;
+        }
+      }
+      else if(req==LAW_REQ)
+      {
+        plci->cr_enquiry = TRUE;
+      }
+      add_ss(plci,FTY,&m_parms[1]);
+      sig_req(plci,req,0);
+      send_req(plci);
+      if(req==HANGUP)
+      {      
+        if (plci->NL.Id && !plci->nl_remove_id)
+        {
+          if (plci->channels)
+          {
+            for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+            {
+              if ((a->ncci_plci[ncci] == plci->Id) && (a->ncci_state[ncci] == CONNECTED))
+              {
+                a->ncci_state[ncci] = OUTG_DIS_PENDING;
+                cleanup_ncci_data (plci, ncci);
+                nl_req_ncci(plci,N_DISC,(byte)ncci);
+              }
+            }
+          }
+          mixer_remove (plci);
+          nl_req_ncci(plci,REMOVE,0);
+          send_req(plci);
+        }  
+      }
+      break;
+
+    case _DI_SIG_CTRL:
+    /* signalling control for loop activation B-channel */
+      if(!plci)
+      {
+        Info = _WRONG_IDENTIFIER;
+        break;
+      }
+      if(m->length){
+        plci->command = _MANUFACTURER_R;
+        plci->number = Number;
+        add_ss(plci,FTY,m);
+        sig_req(plci,SIG_CTRL,0);
+        send_req(plci);
+      }
+      else Info = _WRONG_MESSAGE_FORMAT;
+      break;
+
+    case _DI_RXT_CTRL:
+    /* activation control for receiver/transmitter B-channel */
+      if(!plci)
+      {
+        Info = _WRONG_IDENTIFIER;
+        break;
+      }
+      if(m->length){
+        plci->command = _MANUFACTURER_R;
+        plci->number = Number;
+        add_ss(plci,FTY,m);
+        sig_req(plci,DSP_CTRL,0);
+        send_req(plci);
+      }
+      else Info = _WRONG_MESSAGE_FORMAT;
+      break;
+
+    case _DI_ADV_CODEC:
+    case _DI_DSP_CTRL:
+      /* TEL_CTRL commands to support non standard adjustments: */
+      /* Ring on/off, Handset micro volume, external micro vol. */
+      /* handset+external speaker volume, receiver+transm. gain,*/
+      /* handsfree on (hookinfo off), set mixer command         */
+
+      if(command == _DI_ADV_CODEC)
+      {
+        if(!a->AdvCodecPLCI) {
+          Info = _WRONG_STATE;
+          break;
+        }
+        v_plci = a->AdvCodecPLCI;
+      }
+      else
+      {
+        if (plci
+         && (m->length >= 3)
+         && (m->info[1] == 0x1c)
+         && (m->info[2] >= 1))
+        {
+          if (m->info[3] == DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS)
+          {
+            if ((plci->tel != ADV_VOICE) || (plci != a->AdvSignalPLCI))
+            {
+              Info = _WRONG_STATE;
+              break;
+            }
+            a->adv_voice_coef_length = m->info[2] - 1;
+            if (a->adv_voice_coef_length > m->length - 3)
+              a->adv_voice_coef_length = (byte)(m->length - 3);
+            if (a->adv_voice_coef_length > ADV_VOICE_COEF_BUFFER_SIZE)
+              a->adv_voice_coef_length = ADV_VOICE_COEF_BUFFER_SIZE;
+            for (i = 0; i < a->adv_voice_coef_length; i++)
+              a->adv_voice_coef_buffer[i] = m->info[4 + i];
+            if (plci->B1_facilities & B1_FACILITY_VOICE)
+              adv_voice_write_coefs (plci, ADV_VOICE_WRITE_UPDATE);
+            break;
+          }
+          else if (m->info[3] == DSP_CTRL_SET_DTMF_PARAMETERS)
+          {
+            if (!(a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_PARAMETERS))
+            {
+              Info = _FACILITY_NOT_SUPPORTED;
+              break;
+            }
+
+            plci->dtmf_parameter_length = m->info[2] - 1;
+            if (plci->dtmf_parameter_length > m->length - 3)
+              plci->dtmf_parameter_length = (byte)(m->length - 3);
+            if (plci->dtmf_parameter_length > DTMF_PARAMETER_BUFFER_SIZE)
+              plci->dtmf_parameter_length = DTMF_PARAMETER_BUFFER_SIZE;
+            for (i = 0; i < plci->dtmf_parameter_length; i++)
+              plci->dtmf_parameter_buffer[i] = m->info[4+i];
+            if (plci->B1_facilities & B1_FACILITY_DTMFR)
+              dtmf_parameter_write (plci);
+            break;
+
+          }
+        }
+        v_plci = plci;
+      }
+
+      if(!v_plci)
+      {
+        Info = _WRONG_IDENTIFIER;
+        break;
+      }
+      if(m->length){
+        add_ss(v_plci,FTY,m);
+        sig_req(v_plci,TEL_CTRL,0);
+        send_req(v_plci);
+      }
+      else Info = _WRONG_MESSAGE_FORMAT;
+
+      break;
+
+    case _DI_OPTIONS_REQUEST:
+      if(api_parse(&m->info[1],(word)m->length,"d",m_parms)) {
+        Info = _WRONG_MESSAGE_FORMAT;
+        break;
+      }
+      if (GET_DWORD (m_parms[0].info) & ~a->man_profile.private_options)
+      {
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      a->requested_options_table[appl->Id-1] = GET_DWORD (m_parms[0].info);
+      break;
+
+
+
+    default:
+      Info = _WRONG_MESSAGE_FORMAT;
+      break;
+    }
+  }
+
+  sendf(appl,
+        _MANUFACTURER_R|CONFIRM,
+        Id,
+        Number,
+        "dww",_DI_MANU_ID,command,Info);
+  return FALSE;
+}
+
+
+byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER   * a, PLCI   * plci, APPL   * appl, API_PARSE * msg)
+{
+  word indication;
+
+    API_PARSE m_parms[3];
+  API_PARSE *ncpi;
+    API_PARSE fax_parms[9];
+  word i;
+  byte len;
+
+
+  dbug(1,dprintf("manufacturer_res"));
+
+  if ((msg[0].length == 0)
+   || (msg[1].length == 0)
+   || (GET_DWORD(msg[0].info)!=_DI_MANU_ID))
+  {
+    return FALSE;
+  }
+  indication = GET_WORD(msg[1].info);
+  switch (indication)
+  {
+
+  case _DI_NEGOTIATE_B3:
+    if(!plci)
+      break;
+    if (((plci->B3_prot != 4) && (plci->B3_prot != 5))
+     || !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
+    {
+      dbug(1,dprintf("wrong state for NEGOTIATE_B3 parameters"));
+      break;
+    }
+    if (api_parse (&msg[2].info[1], msg[2].length, "ws", m_parms))
+    {
+      dbug(1,dprintf("wrong format in NEGOTIATE_B3 parameters"));
+      break;
+    }
+    ncpi = &m_parms[1];
+    len = ((byte)(((T30_INFO *) 0)->station_id + 20));
+    if (plci->fax_connect_info_length < len)
+    {
+      ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
+      ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
+    }
+    if (api_parse (&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
+    {
+      dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+    }
+    else
+    {
+      if (plci->fax_connect_info_length <= len)
+        plci->fax_connect_info_buffer[len] = 0;
+      len += 1 + plci->fax_connect_info_buffer[len];
+      if (plci->fax_connect_info_length <= len)
+        plci->fax_connect_info_buffer[len] = 0;
+      len += 1 + plci->fax_connect_info_buffer[len];
+      if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
+        plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
+      plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
+      for (i = 0; i < fax_parms[7].length; i++)
+        plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1+i];
+    }
+    plci->fax_connect_info_length = len;
+    plci->fax_edata_ack_length = plci->fax_connect_info_length;
+    start_internal_command (Id, plci, fax_edata_ack_command);
+    break;
+
+  }
+  return FALSE;
+}
+
+/*------------------------------------------------------------------*/
+/* IDI callback function                                            */
+/*------------------------------------------------------------------*/
+
+void   callback(ENTITY   * e)
+{
+  DIVA_CAPI_ADAPTER   * a;
+  APPL   * appl;
+  PLCI   * plci;
+  CAPI_MSG   *m;
+  word i, j;
+  byte rc;
+  byte ch;
+  byte req;
+  byte global_req;
+  int no_cancel_rc;
+
+  dbug(1,dprintf("%x:CB(%x:Req=%x,Rc=%x,Ind=%x)",
+                 (e->user[0]+1)&0x7fff,e->Id,e->Req,e->Rc,e->Ind));
+
+  a = &(adapter[(byte)e->user[0]]);
+  plci = &(a->plci[e->user[1]]);
+  no_cancel_rc = DIVA_CAPI_SUPPORTS_NO_CANCEL(a);
+
+  /*
+     If new protocol code and new XDI is used then CAPI should work
+     fully in accordance with IDI cpec an look on callback field instead
+     of Rc field for return codes.
+   */
+  if (((e->complete == 0xff) && no_cancel_rc) ||
+      (e->Rc && !no_cancel_rc)) {
+    rc = e->Rc;
+    ch = e->RcCh;
+    req = e->Req;
+    e->Rc = 0;
+
+    if (e->user[0] & 0x8000)
+    {
+      /*
+         If REMOVE request was sent then we have to wait until
+         return code with Id set to zero arrives.
+         All other return codes should be ignored.
+         */
+      if (req == REMOVE)
+      {
+        if (e->Id)
+        {
+          dbug(1,dprintf("cancel RC in REMOVE state"));
+          return;
+        }
+        channel_flow_control_remove (plci);
+        for (i = 0; i < 256; i++)
+        {
+          if (a->FlowControlIdTable[i] == plci->nl_remove_id)
+            a->FlowControlIdTable[i] = 0;
+        }
+        plci->nl_remove_id = 0;
+        if (plci->rx_dma_descriptor > 0) {
+          diva_free_dma_descriptor (plci, plci->rx_dma_descriptor - 1);
+          plci->rx_dma_descriptor = 0;
+        }
+      }
+      if (rc == OK_FC)
+      {
+        a->FlowControlIdTable[ch] = e->Id;
+        a->FlowControlSkipTable[ch] = 0;
+
+        a->ch_flow_control[ch] |= N_OK_FC_PENDING;
+        a->ch_flow_plci[ch] = plci->Id;
+        plci->nl_req = 0;
+      }
+      else
+      {
+        /*
+          Cancel return codes self, if feature was requested
+          */
+        if (no_cancel_rc && (a->FlowControlIdTable[ch] == e->Id) && e->Id) {
+          a->FlowControlIdTable[ch] = 0;
+          if ((rc == OK) && a->FlowControlSkipTable[ch]) {
+            dbug(3,dprintf ("XDI CAPI: RC cancelled Id:0x02, Ch:%02x",                              e->Id, ch));
+            return;
+          }
+        }
+
+        if (a->ch_flow_control[ch] & N_OK_FC_PENDING)
+        {
+          a->ch_flow_control[ch] &= ~N_OK_FC_PENDING;
+          if (ch == e->ReqCh)
+            plci->nl_req = 0;
+        }
+        else
+          plci->nl_req = 0;
+      }
+      if (plci->nl_req)
+        control_rc (plci, 0, rc, ch, 0, TRUE);
+      else
+      {
+        if (req == N_XON)
+        {
+          channel_x_on (plci, ch);
+          if (plci->internal_command)
+            control_rc (plci, req, rc, ch, 0, TRUE);
+        }
+        else
+        {
+          if (plci->nl_global_req)
+          {
+            global_req = plci->nl_global_req;
+            plci->nl_global_req = 0;
+            if (rc != ASSIGN_OK) {
+              e->Id = 0;
+              if (plci->rx_dma_descriptor > 0) {
+                diva_free_dma_descriptor (plci, plci->rx_dma_descriptor - 1);
+                plci->rx_dma_descriptor = 0;
+              }
+            }
+            channel_xmit_xon (plci);
+            control_rc (plci, 0, rc, ch, global_req, TRUE);
+          }
+          else if (plci->data_sent)
+          {
+            channel_xmit_xon (plci);
+            plci->data_sent = FALSE;
+            plci->NL.XNum = 1;
+            data_rc (plci, ch);
+            if (plci->internal_command)
+              control_rc (plci, req, rc, ch, 0, TRUE);
+          }
+          else
+          {
+            channel_xmit_xon (plci);
+            control_rc (plci, req, rc, ch, 0, TRUE);
+          }
+        }
+      }
+    }
+    else
+    {
+      /*
+         If REMOVE request was sent then we have to wait until
+         return code with Id set to zero arrives.
+         All other return codes should be ignored.
+         */
+      if (req == REMOVE)
+      {
+        if (e->Id)
+        {
+          dbug(1,dprintf("cancel RC in REMOVE state"));
+          return;
+        }
+        plci->sig_remove_id = 0;
+      }
+      plci->sig_req = 0;
+      if (plci->sig_global_req)
+      {
+        global_req = plci->sig_global_req;
+        plci->sig_global_req = 0;
+        if (rc != ASSIGN_OK)
+          e->Id = 0;
+        channel_xmit_xon (plci);
+        control_rc (plci, 0, rc, ch, global_req, FALSE);
+      }
+      else
+      {
+        channel_xmit_xon (plci);
+        control_rc (plci, req, rc, ch, 0, FALSE);
+      }
+    }
+    /*
+      Again: in accordance with IDI spec Rc and Ind can't be delivered in the
+      same callback. Also if new XDI and protocol code used then jump
+      direct to finish.
+      */
+    if (no_cancel_rc) {
+      channel_xmit_xon(plci);
+      goto capi_callback_suffix;
+    }
+  }
+
+  channel_xmit_xon(plci);
+
+  if (e->Ind) {
+    if (e->user[0] &0x8000) {
+      byte Ind = e->Ind & 0x0f;
+      byte Ch = e->IndCh;
+      if (((Ind==N_DISC) || (Ind==N_DISC_ACK)) &&
+          (a->ch_flow_plci[Ch] == plci->Id)) {
+        if (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK) {
+          dbug(3,dprintf ("XDI CAPI: I: pending N-XON Ch:%02x", Ch));
+        }
+        a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
+      }
+      nl_ind(plci);
+      if ((e->RNR != 1) &&
+          (a->ch_flow_plci[Ch] == plci->Id) &&
+          (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK)) {
+        a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
+        dbug(3,dprintf ("XDI CAPI: I: remove faked N-XON Ch:%02x", Ch));
+      }
+    } else {
+      sig_ind(plci);
+    }
+    e->Ind = 0;
+  }
+
+capi_callback_suffix:
+
+  while (!plci->req_in
+   && !plci->internal_command
+   && (plci->msg_in_write_pos != plci->msg_in_read_pos))
+  {
+    j = (plci->msg_in_read_pos == plci->msg_in_wrap_pos) ? 0 : plci->msg_in_read_pos;
+
+    i = (((CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[j]))->header.length + 3) & 0xfffc;
+
+    m = (CAPI_MSG   *)(&((byte   *)(plci->msg_in_queue))[j]);
+    appl = *((APPL   *   *)(&((byte   *)(plci->msg_in_queue))[j+i]));
+    dbug(1,dprintf("dequeue msg(0x%04x) - write=%d read=%d wrap=%d",
+      m->header.command, plci->msg_in_write_pos, plci->msg_in_read_pos, plci->msg_in_wrap_pos));
+    if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
+    {
+      plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+      plci->msg_in_read_pos = i + MSG_IN_OVERHEAD;
+    }
+    else
+    {
+      plci->msg_in_read_pos = j + i + MSG_IN_OVERHEAD;
+    }
+    if (plci->msg_in_read_pos == plci->msg_in_write_pos)
+    {
+      plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
+      plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+    }
+    else if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
+    {
+      plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+      plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+    }
+    i = api_put (appl, m);
+    if (i != 0)
+    {
+      if (m->header.command == _DATA_B3_R)
+
+        TransmitBufferFree (appl, (byte   *)(m->info.data_b3_req.Data));
+
+      dbug(1,dprintf("Error 0x%04x from msg(0x%04x)", i, m->header.command));
+      break;
+    }
+
+    if (plci->li_notify_update)
+    {
+      plci->li_notify_update = FALSE;
+      mixer_notify_update (plci, FALSE);
+    }
+
+  }
+  send_data(plci);
+  send_req(plci);
+}
+
+
+void control_rc(PLCI   * plci, byte req, byte rc, byte ch, byte global_req, byte nl_rc)
+{
+  dword Id;
+  dword rId;
+  word Number;
+  word Info=0;
+  word i;
+  word ncci;
+  DIVA_CAPI_ADAPTER   * a;
+  APPL   * appl;
+  PLCI   * rplci;
+    byte SSparms[]  = "\x05\x00\x00\x02\x00\x00";
+    byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";
+
+  if (!plci) {
+    dbug(0,dprintf("A: control_rc, no plci %02x:%02x:%02x:%02x:%02x", req, rc, ch, global_req, nl_rc));
+    return;
+  }
+  dbug(1,dprintf("req0_in/out=%d/%d",plci->req_in,plci->req_out));
+  if(plci->req_in!=plci->req_out)
+  {
+    if (nl_rc || (global_req != ASSIGN) || (rc == ASSIGN_OK))
+    {
+      dbug(1,dprintf("req_1return"));
+      return;
+    }
+    /* cancel outstanding request on the PLCI after SIG ASSIGN failure */
+  }
+  plci->req_in = plci->req_in_start = plci->req_out = 0;
+  dbug(1,dprintf("control_rc"));
+
+  appl = plci->appl;
+  a = plci->adapter;
+  ncci = a->ch_ncci[ch];
+  if(appl)
+  {
+    Id = (((dword)(ncci ? ncci : ch)) << 16) | ((word)plci->Id << 8) | a->Id;
+    if(plci->tel && plci->SuppState!=CALL_HELD) Id|=EXT_CONTROLLER;
+    Number = plci->number;
+    dbug(1,dprintf("Contr_RC-Id=%08lx,plci=%x,tel=%x, entity=0x%x, command=0x%x, int_command=0x%x",Id,plci->Id,plci->tel,plci->Sig.Id,plci->command,plci->internal_command));
+    dbug(1,dprintf("channels=0x%x",plci->channels));
+    if (plci_remove_check(plci))
+      return;
+    if(req==REMOVE && rc==ASSIGN_OK)
+    {
+      sig_req(plci,HANGUP,0);
+      sig_req(plci,REMOVE,0);
+      send_req(plci);
+    }
+    if(plci->command)
+    {
+      switch(plci->command)
+      {
+      case C_HOLD_REQ:
+        dbug(1,dprintf("HoldRC=0x%x",rc));
+        SSparms[1] = (byte)S_HOLD;
+        if(rc!=OK)
+        {
+          plci->SuppState = IDLE;
+          Info = 0x2001;
+        }
+        sendf(appl,_FACILITY_R|CONFIRM,Id,Number,"wws",Info,3,SSparms);
+        break;
+
+      case C_RETRIEVE_REQ:
+        dbug(1,dprintf("RetrieveRC=0x%x",rc));
+        SSparms[1] = (byte)S_RETRIEVE;
+        if(rc!=OK)
+        {
+          plci->SuppState = CALL_HELD;
+          Info = 0x2001;
+        }
+        sendf(appl,_FACILITY_R|CONFIRM,Id,Number,"wws",Info,3,SSparms);
+        break;
+
+      case _INFO_R:
+        dbug(1,dprintf("InfoRC=0x%x",rc));
+        if(rc!=OK) Info=_WRONG_STATE;
+        sendf(appl,_INFO_R|CONFIRM,Id,Number,"w",Info);
+        break;
+
+      case _CONNECT_R:
+        dbug(1,dprintf("Connect_R=0x%x/0x%x/0x%x/0x%x",req,rc,global_req,nl_rc));
+        if (plci->State == INC_DIS_PENDING)
+          break;
+        if(plci->Sig.Id!=0xff)
+        {
+          if (((global_req == ASSIGN) && (rc != ASSIGN_OK))
+           || (!nl_rc && (req == CALL_REQ) && (rc != OK)))
+          {
+            dbug(1,dprintf("No more IDs/Call_Req failed"));
+            sendf(appl,_CONNECT_R|CONFIRM,Id&0xffL,Number,"w",_OUT_OF_PLCI);
+            plci_remove(plci);
+            plci->State = IDLE;
+            break;
+          }
+          if(plci->State!=LOCAL_CONNECT)plci->State = OUTG_CON_PENDING;
+          sendf(appl,_CONNECT_R|CONFIRM,Id,Number,"w",0);
+        }
+        else /* D-ch activation */
+        {
+          if (rc != ASSIGN_OK)
+          {
+            dbug(1,dprintf("No more IDs/X.25 Call_Req failed"));
+            sendf(appl,_CONNECT_R|CONFIRM,Id&0xffL,Number,"w",_OUT_OF_PLCI);
+            plci_remove(plci);
+            plci->State = IDLE;
+            break;
+          }
+          sendf(appl,_CONNECT_R|CONFIRM,Id,Number,"w",0);
+          sendf(plci->appl,_CONNECT_ACTIVE_I,Id,0,"sss","","","");
+          plci->State = INC_ACT_PENDING;
+        }
+        break;
+
+      case _CONNECT_I|RESPONSE:
+        if (plci->State != INC_DIS_PENDING)
+          plci->State = INC_CON_ACCEPT;
+        break;
+
+      case _DISCONNECT_R:
+        if (plci->State == INC_DIS_PENDING)
+          break;
+        if(plci->Sig.Id!=0xff)
+        {
+          plci->State = OUTG_DIS_PENDING;
+          sendf(appl,_DISCONNECT_R|CONFIRM,Id,Number,"w",0);
+        }
+        break;
+
+      case SUSPEND_REQ:
+        break;
+
+      case RESUME_REQ:
+        break;
+
+      case _CONNECT_B3_R:
+        if(rc!=OK)
+        {
+          sendf(appl,_CONNECT_B3_R|CONFIRM,Id,Number,"w",_WRONG_IDENTIFIER);
+          break;
+        }
+        ncci = get_ncci (plci, ch, 0);
+        Id = (Id & 0xffff) | (((dword) ncci) << 16);
+        plci->channels++;
+        if(req==N_RESET)
+        {
+          a->ncci_state[ncci] = INC_ACT_PENDING;
+          sendf(appl,_CONNECT_B3_R|CONFIRM,Id,Number,"w",0);
+          sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+        }
+        else
+        {
+          a->ncci_state[ncci] = OUTG_CON_PENDING;
+          sendf(appl,_CONNECT_B3_R|CONFIRM,Id,Number,"w",0);
+        }
+        break;
+
+      case _CONNECT_B3_I|RESPONSE:
+        break;
+
+      case _RESET_B3_R:
+/*        sendf(appl,_RESET_B3_R|CONFIRM,Id,Number,"w",0);*/
+        break;
+
+      case _DISCONNECT_B3_R:
+        sendf(appl,_DISCONNECT_B3_R|CONFIRM,Id,Number,"w",0);
+        break;
+
+      case _MANUFACTURER_R:
+        break;
+
+      case PERM_LIST_REQ:
+        if(rc!=OK)
+        {
+          Info = _WRONG_IDENTIFIER;
+          sendf(plci->appl,_CONNECT_R|CONFIRM,Id,Number,"w",Info);
+          plci_remove(plci);
+        }
+        else
+          sendf(plci->appl,_CONNECT_R|CONFIRM,Id,Number,"w",Info);
+        break;
+
+      default:
+        break;
+      }
+      plci->command = 0;
+    }
+    else if (plci->internal_command)
+    {
+      switch(plci->internal_command)
+      {
+      case BLOCK_PLCI:
+        return;
+
+      case GET_MWI_STATE:
+        if(rc==OK) /* command supported, wait for indication */
+        {
+          return;
+        }
+        plci_remove(plci);
+        break;
+
+        /* Get Supported Services */
+      case GETSERV_REQ_PEND:
+        if(rc==OK) /* command supported, wait for indication */
+        {
+          break;
+        }
+        PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
+        sendf(appl, _FACILITY_R|CONFIRM, Id, Number, "wws",0,3,SSstruct);
+        plci_remove(plci);
+        break;
+
+      case INTERR_DIVERSION_REQ_PEND:      /* Interrogate Parameters        */
+      case INTERR_NUMBERS_REQ_PEND:
+      case CF_START_PEND:                  /* Call Forwarding Start pending */
+      case CF_STOP_PEND:                   /* Call Forwarding Stop pending  */
+      case CCBS_REQUEST_REQ_PEND:
+      case CCBS_DEACTIVATE_REQ_PEND:
+      case CCBS_INTERROGATE_REQ_PEND:
+        switch(plci->internal_command)
+        {
+          case INTERR_DIVERSION_REQ_PEND:
+            SSparms[1] = S_INTERROGATE_DIVERSION;
+            break;
+          case INTERR_NUMBERS_REQ_PEND:
+            SSparms[1] = S_INTERROGATE_NUMBERS;
+            break;
+          case CF_START_PEND:
+            SSparms[1] = S_CALL_FORWARDING_START;
+            break;
+          case CF_STOP_PEND:
+            SSparms[1] = S_CALL_FORWARDING_STOP;
+            break;
+          case CCBS_REQUEST_REQ_PEND:
+            SSparms[1] = S_CCBS_REQUEST;
+            break;
+          case CCBS_DEACTIVATE_REQ_PEND:
+            SSparms[1] = S_CCBS_DEACTIVATE;
+            break;
+          case CCBS_INTERROGATE_REQ_PEND:
+            SSparms[1] = S_CCBS_INTERROGATE;
+            break;
+        }
+        if(global_req==ASSIGN)
+        {
+          dbug(1,dprintf("AssignDiversion_RC=0x%x/0x%x",req,rc));
+          return;
+        }
+        if(!plci->appl) break;
+        if(rc==ISDN_GUARD_REJ)
+        {
+          Info = _CAPI_GUARD_ERROR;
+        }
+        else if(rc!=OK)
+        {
+          Info = _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED;
+        }
+        sendf(plci->appl,_FACILITY_R|CONFIRM,Id&0x7,
+              plci->number,"wws",Info,(word)3,SSparms);
+        if(Info) plci_remove(plci);
+        break;
+
+        /* 3pty conference pending */
+      case PTY_REQ_PEND:
+        if(!plci->relatedPTYPLCI) break;
+        rplci = plci->relatedPTYPLCI;
+        SSparms[1] = plci->ptyState;
+        rId = ((word)rplci->Id<<8)|rplci->adapter->Id;
+        if(rplci->tel) rId|=EXT_CONTROLLER;
+        if(rc!=OK)
+        {
+          Info = 0x300E; /* not supported */
+          plci->relatedPTYPLCI = NULL;
+          plci->ptyState = 0;
+        }
+        sendf(rplci->appl,
+              _FACILITY_R|CONFIRM,
+              rId,
+              plci->number,
+              "wws",Info,(word)3,SSparms);
+        break;
+
+        /* Explicit Call Transfer pending */
+      case ECT_REQ_PEND:
+        dbug(1,dprintf("ECT_RC=0x%x/0x%x",req,rc));
+        if(!plci->relatedPTYPLCI) break;
+        rplci = plci->relatedPTYPLCI;
+        SSparms[1] = S_ECT;
+        rId = ((word)rplci->Id<<8)|rplci->adapter->Id;
+        if(rplci->tel) rId|=EXT_CONTROLLER;
+        if(rc!=OK)
+        {
+          Info = 0x300E; /* not supported */
+          plci->relatedPTYPLCI = NULL;
+          plci->ptyState = 0;
+        }
+        sendf(rplci->appl,
+              _FACILITY_R|CONFIRM,
+              rId,
+              plci->number,
+              "wws",Info,(word)3,SSparms);
+        break;
+
+      case _MANUFACTURER_R:
+        dbug(1,dprintf("_Manufacturer_R=0x%x/0x%x",req,rc));
+        if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
+        {
+          dbug(1,dprintf("No more IDs"));
+          sendf(appl,_MANUFACTURER_R|CONFIRM,Id,Number,"dww",_DI_MANU_ID,_MANUFACTURER_R,_OUT_OF_PLCI);
+          plci_remove(plci);  /* after codec init, internal codec commands pending */
+        }
+        break;
+
+      case _CONNECT_R:
+        dbug(1,dprintf("_Connect_R=0x%x/0x%x",req,rc));
+        if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
+        {
+          dbug(1,dprintf("No more IDs"));
+          sendf(appl,_CONNECT_R|CONFIRM,Id&0xffL,Number,"w",_OUT_OF_PLCI);
+          plci_remove(plci);  /* after codec init, internal codec commands pending */
+        }
+        break;
+
+      case PERM_COD_HOOK:                     /* finished with Hook_Ind */
+        return;
+
+      case PERM_COD_CALL:
+        dbug(1,dprintf("***Codec Connect_Pending A, Rc = 0x%x",rc));
+        plci->internal_command = PERM_COD_CONN_PEND;
+        return;
+
+      case PERM_COD_ASSIGN:
+        dbug(1,dprintf("***Codec Assign A, Rc = 0x%x",rc));
+        if(rc!=ASSIGN_OK) break;
+        sig_req(plci,CALL_REQ,0);
+        send_req(plci);
+        plci->internal_command = PERM_COD_CALL;
+        return;
+
+        /* Null Call Reference Request pending */
+      case C_NCR_FAC_REQ:
+        dbug(1,dprintf("NCR_FAC=0x%x/0x%x",req,rc));
+        if(global_req==ASSIGN)
+        {
+          if(rc==ASSIGN_OK)
+          {
+            return;
+          }
+          else
+          {
+            sendf(appl,_INFO_R|CONFIRM,Id&0xf,Number,"w",_WRONG_STATE);
+            appl->NullCREnable = FALSE;
+            plci_remove(plci);
+          }
+        }
+        else if(req==NCR_FACILITY)
+        {
+          if(rc==OK)
+          {
+            sendf(appl,_INFO_R|CONFIRM,Id&0xf,Number,"w",0);
+          }
+          else
+          {
+            sendf(appl,_INFO_R|CONFIRM,Id&0xf,Number,"w",_WRONG_STATE);
+            appl->NullCREnable = FALSE;
+          }
+          plci_remove(plci);
+        }
+        break;
+
+      case HOOK_ON_REQ:
+        if(plci->channels)
+        {
+          if(a->ncci_state[ncci]==CONNECTED)
+          {
+            a->ncci_state[ncci] = OUTG_DIS_PENDING;
+            cleanup_ncci_data (plci, ncci);
+            nl_req_ncci(plci,N_DISC,(byte)ncci);
+          }
+          break;
+        }
+        break;
+
+      case HOOK_OFF_REQ:
+        if (plci->State == INC_DIS_PENDING)
+          break;
+        sig_req(plci,CALL_REQ,0);
+        send_req(plci);
+        plci->State=OUTG_CON_PENDING;
+        break;
+
+
+      case MWI_ACTIVATE_REQ_PEND:
+      case MWI_DEACTIVATE_REQ_PEND:
+        if(global_req == ASSIGN && rc==ASSIGN_OK)
+        {
+          dbug(1,dprintf("MWI_REQ assigned"));
+          return;
+        }
+        else if(rc!=OK)
+        {                 
+          if(rc==WRONG_IE)
+          {
+            Info = 0x2007; /* Illegal message parameter coding */
+            dbug(1,dprintf("MWI_REQ invalid parameter"));
+          }
+          else
+          {
+            Info = 0x300B; /* not supported */                      
+            dbug(1,dprintf("MWI_REQ not supported"));
+          }
+          /* 0x3010: Request not allowed in this state */
+          PUT_WORD(&SSparms[4],0x300E); /* SS not supported */
+                    
+        }
+        if(plci->internal_command==MWI_ACTIVATE_REQ_PEND)
+        {
+          PUT_WORD(&SSparms[1],S_MWI_ACTIVATE);
+        }
+        else PUT_WORD(&SSparms[1],S_MWI_DEACTIVATE);
+
+        if(plci->cr_enquiry)
+        {
+          sendf(plci->appl,
+                _FACILITY_R|CONFIRM,
+                Id&0xf,
+                plci->number,
+                "wws",Info,(word)3,SSparms);
+          if(rc!=OK) plci_remove(plci);
+        }
+        else
+        {
+          sendf(plci->appl,
+                _FACILITY_R|CONFIRM,
+                Id,
+                plci->number,
+                "wws",Info,(word)3,SSparms);
+        }
+        break;
+
+      case CONF_BEGIN_REQ_PEND:
+      case CONF_ADD_REQ_PEND:
+      case CONF_SPLIT_REQ_PEND:
+      case CONF_DROP_REQ_PEND:
+      case CONF_ISOLATE_REQ_PEND:
+      case CONF_REATTACH_REQ_PEND:
+        dbug(1,dprintf("CONF_RC=0x%x/0x%x",req,rc));
+        if((plci->internal_command==CONF_ADD_REQ_PEND)&&(!plci->relatedPTYPLCI)) break;
+        rplci = plci;
+        rId = Id;
+        switch(plci->internal_command)
+        {
+          case CONF_BEGIN_REQ_PEND:
+            SSparms[1] = S_CONF_BEGIN;
+            break;
+          case CONF_ADD_REQ_PEND:
+            SSparms[1] = S_CONF_ADD;
+            rplci = plci->relatedPTYPLCI;
+            rId = ((word)rplci->Id<<8)|rplci->adapter->Id;
+            break;
+          case CONF_SPLIT_REQ_PEND:
+            SSparms[1] = S_CONF_SPLIT;
+            break;
+          case CONF_DROP_REQ_PEND:
+            SSparms[1] = S_CONF_DROP;
+            break;
+          case CONF_ISOLATE_REQ_PEND:
+            SSparms[1] = S_CONF_ISOLATE;
+            break;
+          case CONF_REATTACH_REQ_PEND:
+            SSparms[1] = S_CONF_REATTACH;
+            break;
+        }
+        
+        if(rc!=OK)
+        {
+          Info = 0x300E; /* not supported */
+          plci->relatedPTYPLCI = NULL;
+          plci->ptyState = 0;
+        }
+        sendf(rplci->appl,
+              _FACILITY_R|CONFIRM,
+              rId,
+              plci->number,
+              "wws",Info,(word)3,SSparms);
+        break;
+
+      case VSWITCH_REQ_PEND:
+        if(rc!=OK)
+        {
+          if(plci->relatedPTYPLCI)
+          {
+            plci->relatedPTYPLCI->vswitchstate=0;
+            plci->relatedPTYPLCI->vsprot=0;
+            plci->relatedPTYPLCI->vsprotdialect=0;    
+          }
+          plci->vswitchstate=0;
+          plci->vsprot=0;
+          plci->vsprotdialect=0;
+        }
+        else
+        {
+          if(plci->relatedPTYPLCI &&
+             plci->vswitchstate==1 &&
+             plci->relatedPTYPLCI->vswitchstate==3) /* join complete */
+            plci->vswitchstate=3;
+        }
+        break;
+
+  /* Call Deflection Request pending (SSCT) */
+      case CD_REQ_PEND:
+        SSparms[1] = S_CALL_DEFLECTION;
+        if(rc!=OK)
+        {
+          Info = 0x300E; /* not supported */
+          plci->appl->CDEnable = 0;
+        }  
+        sendf(plci->appl,_FACILITY_R|CONFIRM,Id,
+          plci->number,"wws",Info,(word)3,SSparms);
+        break;
+
+      case RTP_CONNECT_B3_REQ_COMMAND_2:
+        if (rc == OK)
+        {
+          ncci = get_ncci (plci, ch, 0);
+          Id = (Id & 0xffff) | (((dword) ncci) << 16);
+          plci->channels++;
+          a->ncci_state[ncci] = OUTG_CON_PENDING;
+        }
+
+      default:
+        if (plci->internal_command_queue[0])
+        {
+          (*(plci->internal_command_queue[0]))(Id, plci, rc);
+          if (plci->internal_command)
+            return;
+        }
+        break;
+      }
+      next_internal_command (Id, plci);
+    }
+  }
+  else /* appl==0 */
+  {
+    Id = ((word)plci->Id<<8)|plci->adapter->Id;
+    if(plci->tel) Id|=EXT_CONTROLLER;
+
+    switch(plci->internal_command)
+    {
+    case BLOCK_PLCI:
+      return;
+
+    case START_L1_SIG_ASSIGN_PEND:
+    case REM_L1_SIG_ASSIGN_PEND:
+      if(global_req == ASSIGN)
+      {
+        break;
+      }
+      else
+      {
+        dbug(1,dprintf("***L1 Req rem PLCI"));
+        plci->internal_command = 0;
+        sig_req(plci,REMOVE,0);
+        send_req(plci);
+      }
+      break;
+
+      /* Call Deflection Request pending, just no appl ptr assigned */
+    case CD_REQ_PEND:
+      SSparms[1] = S_CALL_DEFLECTION;
+      if(rc!=OK)
+      {
+        Info = 0x300E; /* not supported */
+      }
+      for(i=0; i<max_appl; i++)
+      {
+        if(application[i].CDEnable)
+        {
+          if(!application[i].Id) application[i].CDEnable = 0;
+          else
+          {
+            sendf(&application[i],_FACILITY_R|CONFIRM,Id,
+                  plci->number,"wws",Info,(word)3,SSparms);
+            if(Info) application[i].CDEnable = 0;
+          }
+        }
+      }
+      plci->internal_command = 0;
+      break;
+
+    case PERM_COD_HOOK:                   /* finished with Hook_Ind */
+      return;
+
+    case PERM_COD_CALL:
+      plci->internal_command = PERM_COD_CONN_PEND;
+      dbug(1,dprintf("***Codec Connect_Pending, Rc = 0x%x",rc));
+      return;
+
+    case PERM_COD_ASSIGN:
+      dbug(1,dprintf("***Codec Assign, Rc = 0x%x",rc));
+      plci->internal_command = 0;
+      if(rc!=ASSIGN_OK) break;
+      plci->internal_command = PERM_COD_CALL;
+      sig_req(plci,CALL_REQ,0);
+      send_req(plci);
+      return;
+
+    case LISTEN_SIG_ASSIGN_PEND:
+      if(rc == ASSIGN_OK)
+      {
+        plci->internal_command = 0;
+        dbug(1,dprintf("ListenCheck, new SIG_ID = 0x%x",plci->Sig.Id));
+        add_p(plci,ESC,"\x02\x18\x00");             /* support call waiting */
+        sig_req(plci,INDICATE_REQ,0);
+        send_req(plci);
+      }
+      else
+      {
+        dbug(1,dprintf("ListenCheck failed (assignRc=0x%x)",rc));
+        a->listen_active--;
+        plci_remove(plci);
+        plci->State = IDLE;
+      }
+      break;
+
+    case USELAW_REQ:
+      if(global_req == ASSIGN)
+      {
+        if (rc==ASSIGN_OK)
+      {
+        sig_req(plci,LAW_REQ,0);
+        send_req(plci);
+        dbug(1,dprintf("Auto-Law assigned"));
+        }
+        else
+        {
+          dbug(1,dprintf("Auto-Law assign failed"));
+          a->automatic_law = 3;
+          plci->internal_command = 0;
+          a->automatic_lawPLCI = NULL;
+        }
+        break;
+      }
+      else if(req == LAW_REQ && rc==OK)
+      {
+        dbug(1,dprintf("Auto-Law initiated"));
+        a->automatic_law = 2;
+        plci->internal_command = 0;
+      }
+      else
+      {
+        dbug(1,dprintf("Auto-Law not supported"));
+        a->automatic_law = 3;
+        plci->internal_command = 0;
+        sig_req(plci,REMOVE,0);
+        send_req(plci);
+        a->automatic_lawPLCI = NULL;
+      }
+      break;
+    }
+    plci_remove_check(plci);
+  }
+}
+
+void data_rc(PLCI   * plci, byte ch)
+{
+  dword Id;
+  DIVA_CAPI_ADAPTER   * a;
+  NCCI   *ncci_ptr;
+  DATA_B3_DESC   *data;
+  word ncci;
+
+  if (plci->appl)
+  {
+    TransmitBufferFree (plci->appl, plci->data_sent_ptr);
+    a = plci->adapter;
+    ncci = a->ch_ncci[ch];
+    if (ncci && (a->ncci_plci[ncci] == plci->Id))
+    {
+      ncci_ptr = &(a->ncci[ncci]);
+      dbug(1,dprintf("data_out=%d, data_pending=%d",ncci_ptr->data_out,ncci_ptr->data_pending));
+      if (ncci_ptr->data_pending)
+      {
+        data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
+        if (!(data->Flags &4) && a->ncci_state[ncci])
+        {
+          Id = (((dword)ncci)<<16)|((word)plci->Id<<8)|a->Id;
+          if(plci->tel) Id|=EXT_CONTROLLER;
+          sendf(plci->appl,_DATA_B3_R|CONFIRM,Id,data->Number,
+                "ww",data->Handle,0);
+        }
+        (ncci_ptr->data_out)++;
+        if (ncci_ptr->data_out == MAX_DATA_B3)
+          ncci_ptr->data_out = 0;
+        (ncci_ptr->data_pending)--;
+      }
+    }
+  }
+}
+
+void data_ack(PLCI   * plci, byte ch)
+{
+  dword Id;
+  DIVA_CAPI_ADAPTER   * a;
+  NCCI   *ncci_ptr;
+  word ncci;
+
+  a = plci->adapter;
+  ncci = a->ch_ncci[ch];
+  ncci_ptr = &(a->ncci[ncci]);
+  if (ncci_ptr->data_ack_pending)
+  {
+    if (a->ncci_state[ncci] && (a->ncci_plci[ncci] == plci->Id))
+    {
+      Id = (((dword)ncci)<<16)|((word)plci->Id<<8)|a->Id;
+      if(plci->tel) Id|=EXT_CONTROLLER;
+      sendf(plci->appl,_DATA_B3_R|CONFIRM,Id,ncci_ptr->DataAck[ncci_ptr->data_ack_out].Number,
+            "ww",ncci_ptr->DataAck[ncci_ptr->data_ack_out].Handle,0);
+    }
+    (ncci_ptr->data_ack_out)++;
+    if (ncci_ptr->data_ack_out == MAX_DATA_ACK)
+      ncci_ptr->data_ack_out = 0;
+    (ncci_ptr->data_ack_pending)--;
+  }
+}
+
+void sig_ind(PLCI   * plci)
+{
+  dword x_Id;
+  dword Id;
+  dword rId;
+  word Number = 0;
+  word i;
+  word cip;
+  dword cip_mask;
+  byte   *ie;
+  DIVA_CAPI_ADAPTER   * a;
+    API_PARSE saved_parms[MAX_MSG_PARMS+1];
+#define MAXPARMSIDS 31
+    byte   * parms[MAXPARMSIDS];
+    byte   * add_i[4];
+    byte   * multi_fac_parms[MAX_MULTI_IE];
+    byte   * multi_pi_parms [MAX_MULTI_IE];
+    byte   * multi_ssext_parms [MAX_MULTI_IE];
+    byte   * multi_CiPN_parms [MAX_MULTI_IE];
+
+    byte   * multi_vswitch_parms [MAX_MULTI_IE];
+
+  byte ai_len;
+    byte   *esc_chi = "";
+    byte   *esc_law = "";
+    byte   *pty_cai = "";
+    byte   *esc_cr  = "";
+    byte   *esc_profile = "";
+
+    byte facility[256];
+  PLCI   * tplci = NULL;
+  byte chi[] = "\x02\x18\x01";
+  byte voice_cai[]  = "\x06\x14\x00\x00\x00\x00\x08";
+    byte resume_cau[] = "\x05\x05\x00\x02\x00\x00";
+  /* ESC_MSGTYPE must be the last but one message, a new IE has to be */
+  /* included before the ESC_MSGTYPE and MAXPARMSIDS has to be incremented */
+  /* SMSG is situated at the end because its 0 (for compatibility reasons */
+  /* (see Info_Mask Bit 4, first IE. then the message type)           */
+    word parms_id[] =
+         {MAXPARMSIDS, CPN, 0xff, DSA, OSA, BC, LLC, HLC, ESC_CAUSE, DSP, DT, CHA,
+          UUI, CONG_RR, CONG_RNR, ESC_CHI, KEY, CHI, CAU, ESC_LAW,
+          RDN, RDX, CONN_NR, RIN, NI, CAI, ESC_CR,
+          CST, ESC_PROFILE, 0xff, ESC_MSGTYPE, SMSG};
+          /* 14 FTY repl by ESC_CHI */
+          /* 18 PI  repl by ESC_LAW */
+         /* removed OAD changed to 0xff for future use, OAD is multiIE now */
+     word multi_fac_id[] = {1, FTY};
+     word multi_pi_id[]  = {1, PI};
+     word multi_CiPN_id[]  = {1, OAD};
+     word multi_ssext_id[]  = {1, ESC_SSEXT};
+
+     word multi_vswitch_id[]  = {1, ESC_VSWITCH};
+
+  byte   * cau;
+  word ncci;
+    byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
+    byte CF_Ind[] = "\x09\x02\x00\x06\x00\x00\x00\x00\x00\x00";
+    byte Interr_Err_Ind[] = "\x0a\x02\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+    byte CONF_Ind[] = "\x09\x16\x00\x06\x00\x00\0x00\0x00\0x00\0x00";
+  byte force_mt_info = FALSE;
+  byte dir;
+  dword d;
+  word w;
+
+  a = plci->adapter;
+  Id = ((word)plci->Id<<8)|a->Id;
+  PUT_WORD(&SS_Ind[4],0x0000);
+
+  if (plci->sig_remove_id)
+  {
+    plci->Sig.RNR = 2; /* discard */
+    dbug(1,dprintf("SIG discard while remove pending"));
+    return;
+  }
+  if(plci->tel && plci->SuppState!=CALL_HELD) Id|=EXT_CONTROLLER;
+  dbug(1,dprintf("SigInd-Id=%08lx,plci=%x,tel=%x,state=0x%x,channels=%d,Discflowcl=%d",
+    Id,plci->Id,plci->tel,plci->State,plci->channels,plci->hangup_flow_ctrl_timer));
+  if(plci->Sig.Ind==CALL_HOLD_ACK && plci->channels)
+  {
+    plci->Sig.RNR = 1;
+    return;
+  }
+  if(plci->Sig.Ind==HANGUP && plci->channels)
+  {
+    plci->Sig.RNR = 1;
+    plci->hangup_flow_ctrl_timer++;
+    /* recover the network layer after timeout */
+    if(plci->hangup_flow_ctrl_timer==100)
+    {
+      dbug(1,dprintf("Exceptional disc"));
+      plci->Sig.RNR = 0;
+      plci->hangup_flow_ctrl_timer = 0;
+      for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+      {
+        if (a->ncci_plci[ncci] == plci->Id)
+        {
+          cleanup_ncci_data (plci, ncci);
+          if(plci->channels)plci->channels--;
+          if (plci->appl)
+            sendf(plci->appl,_DISCONNECT_B3_I, (((dword) ncci) << 16) | Id,0,"ws",0,"");
+        }
+      }
+      if (plci->appl)
+        sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
+      plci_remove(plci);
+      plci->State=IDLE;
+    }
+    return;
+  }
+
+  /* do first parse the info with no OAD in, because OAD will be converted */
+  /* first the multiple facility IE, then mult. progress ind.              */
+  /* then the parameters for the info_ind + conn_ind                       */
+  IndParse(plci,multi_fac_id,multi_fac_parms,MAX_MULTI_IE);
+  IndParse(plci,multi_pi_id,multi_pi_parms,MAX_MULTI_IE);
+  IndParse(plci,multi_ssext_id,multi_ssext_parms,MAX_MULTI_IE);
+
+  IndParse(plci,multi_vswitch_id,multi_vswitch_parms,MAX_MULTI_IE);
+
+  IndParse(plci,parms_id,parms,0);
+  IndParse(plci,multi_CiPN_id,multi_CiPN_parms,MAX_MULTI_IE);
+  esc_chi  = parms[14];
+  esc_law  = parms[18];
+  pty_cai  = parms[24];
+  esc_cr   = parms[25];
+  esc_profile = parms[27];
+  if(esc_cr[0] && plci)
+  {
+    if(plci->cr_enquiry && plci->appl)
+    {
+      plci->cr_enquiry = FALSE;
+      /* d = MANU_ID            */
+      /* w = m_command          */
+      /* b = total length       */
+      /* b = indication type    */
+      /* b = length of all IEs  */
+      /* b = IE1                */
+      /* S = IE1 length + cont. */
+      /* b = IE2                */
+      /* S = IE2 lenght + cont. */
+      sendf(plci->appl,
+        _MANUFACTURER_I,
+        Id,
+        0,
+        "dwbbbbSbS",_DI_MANU_ID,plci->m_command,
+        2+1+1+esc_cr[0]+1+1+esc_law[0],plci->Sig.Ind,1+1+esc_cr[0]+1+1+esc_law[0],ESC,esc_cr,ESC,esc_law);
+    }
+  }
+  /* create the additional info structure                                  */
+  add_i[1] = parms[15]; /* KEY of additional info */
+  add_i[2] = parms[11]; /* UUI of additional info */
+  ai_len = AddInfo(add_i,multi_fac_parms, esc_chi, facility);
+
+  /* the ESC_LAW indicates if u-Law or a-Law is actually used by the card  */
+  /* indication returns by the card if requested by the function           */
+  /* AutomaticLaw() after driver init                                      */
+  if (a->automatic_law<4)
+  {
+    if(esc_law[0]){
+      if(esc_law[2]){
+        dbug(0,dprintf("u-Law selected"));
+        a->u_law = 1;
+      }
+      else {
+        dbug(0,dprintf("a-Law selected"));
+        a->u_law = 0;
+      }
+      a->automatic_law = 4;
+      if(plci==a->automatic_lawPLCI) {
+        plci->internal_command = 0;
+        sig_req(plci,REMOVE,0);
+        send_req(plci);
+        a->automatic_lawPLCI = NULL;
+      }
+    }
+    if (esc_profile[0])
+    {
+      dbug (1, dprintf ("[%06x] CardProfile: %lx %lx %lx %lx %lx",
+        UnMapController (a->Id), GET_DWORD (&esc_profile[6]),
+        GET_DWORD (&esc_profile[10]), GET_DWORD (&esc_profile[14]),
+        GET_DWORD (&esc_profile[18]), GET_DWORD (&esc_profile[46])));
+
+      a->profile.Global_Options &= 0x000000ffL;
+      a->profile.B1_Protocols &= 0x000003ffL;
+      a->profile.B2_Protocols &= 0x00001fdfL;
+      a->profile.B3_Protocols &= 0x000000b7L;
+
+      a->profile.Global_Options &= GET_DWORD (&esc_profile[6]) |
+        GL_BCHANNEL_OPERATION_SUPPORTED;
+      a->profile.B1_Protocols &= GET_DWORD (&esc_profile[10]);
+      a->profile.B2_Protocols &= GET_DWORD (&esc_profile[14]);
+      a->profile.B3_Protocols &= GET_DWORD (&esc_profile[18]);
+      a->manufacturer_features = GET_DWORD (&esc_profile[46]);
+      a->man_profile.private_options = 0;
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_ECHO_CANCELLER)
+      {
+        a->man_profile.private_options |= 1L << PRIVATE_ECHO_CANCELLER;
+        a->profile.Global_Options |= GL_ECHO_CANCELLER_SUPPORTED;
+      }
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_RTP)
+        a->man_profile.private_options |= 1L << PRIVATE_RTP;
+      a->man_profile.rtp_primary_payloads = GET_DWORD (&esc_profile[50]);
+      a->man_profile.rtp_additional_payloads = GET_DWORD (&esc_profile[54]);
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_T38)
+        a->man_profile.private_options |= 1L << PRIVATE_T38;
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD)
+        a->man_profile.private_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_V18)
+        a->man_profile.private_options |= 1L << PRIVATE_V18;
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_TONE)
+        a->man_profile.private_options |= 1L << PRIVATE_DTMF_TONE;
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_PIAFS)
+        a->man_profile.private_options |= 1L << PRIVATE_PIAFS;
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+        a->man_profile.private_options |= 1L << PRIVATE_FAX_PAPER_FORMATS;
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_VOWN)
+        a->man_profile.private_options |= 1L << PRIVATE_VOWN;
+
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_NONSTANDARD)
+        a->man_profile.private_options |= 1L << PRIVATE_FAX_NONSTANDARD;
+
+    }
+    else
+    {
+      a->profile.Global_Options &= 0x0000007fL;
+      a->profile.B1_Protocols &= 0x000003dfL;
+      a->profile.B2_Protocols &= 0x00001adfL;
+      a->profile.B3_Protocols &= 0x000000b7L;
+      a->manufacturer_features &= MANUFACTURER_FEATURE_HARDDTMF;
+    }
+    if (a->manufacturer_features & (MANUFACTURER_FEATURE_HARDDTMF |
+      MANUFACTURER_FEATURE_SOFTDTMF_SEND | MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
+    {
+      a->profile.Global_Options |= GL_DTMF_SUPPORTED;
+    }
+    a->manufacturer_features &= ~MANUFACTURER_FEATURE_OOB_CHANNEL;
+    dbug (1, dprintf ("[%06x] Profile: %lx %lx %lx %lx %lx",
+      UnMapController (a->Id), a->profile.Global_Options,
+      a->profile.B1_Protocols, a->profile.B2_Protocols,
+      a->profile.B3_Protocols, a->manufacturer_features));
+  }
+  /* codec plci for the handset/hook state support is just an internal id  */
+  if(plci!=a->AdvCodecPLCI)
+  {
+    force_mt_info =  SendMultiIE(plci,Id,multi_fac_parms, FTY, 0x20, 0);
+    force_mt_info |= SendMultiIE(plci,Id,multi_pi_parms, PI, 0x210, 0);
+    SendSSExtInd(NULL,plci,Id,multi_ssext_parms);
+    SendInfo(plci,Id, parms, force_mt_info);
+
+    VSwitchReqInd(plci,Id,multi_vswitch_parms);
+
+  }
+
+  /* switch the codec to the b-channel                                     */
+  if(esc_chi[0] && plci && !plci->SuppState){
+    plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
+    mixer_set_bchannel_id_esc (plci, plci->b_channel);
+    dbug(1,dprintf("storeChannel=0x%x",plci->b_channel));
+    if(plci->tel==ADV_VOICE && plci->appl) {
+      SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
+    }
+  }
+
+  if(plci->appl) Number = plci->appl->Number++;
+
+  switch(plci->Sig.Ind) {
+  /* Response to Get_Supported_Services request */
+  case S_SUPPORTED:
+    dbug(1,dprintf("S_Supported"));
+    if(!plci->appl) break;
+    if(pty_cai[0]==4)
+    {
+      PUT_DWORD(&CF_Ind[6],GET_DWORD(&pty_cai[1]) );
+    }
+    else
+    {
+      PUT_DWORD(&CF_Ind[6],MASK_TERMINAL_PORTABILITY | MASK_HOLD_RETRIEVE);
+    }
+    PUT_WORD (&CF_Ind[1], 0);
+    PUT_WORD (&CF_Ind[4], 0);
+    sendf(plci->appl,_FACILITY_R|CONFIRM,Id&0x7,plci->number, "wws",0,3,CF_Ind);
+    plci_remove(plci);
+    break;
+                    
+  /* Supplementary Service rejected */
+  case S_SERVICE_REJ:
+    dbug(1,dprintf("S_Reject=0x%x",pty_cai[5]));
+    if(!pty_cai[0]) break;
+    switch (pty_cai[5])
+    {
+    case ECT_EXECUTE:
+    case THREE_PTY_END:
+    case THREE_PTY_BEGIN:
+      if(!plci->relatedPTYPLCI) break;
+      tplci = plci->relatedPTYPLCI;
+      rId = ( (word)tplci->Id<<8)|tplci->adapter->Id;
+      if(tplci->tel) rId|=EXT_CONTROLLER;
+      if(pty_cai[5]==ECT_EXECUTE)
+      {
+        PUT_WORD(&SS_Ind[1],S_ECT);
+
+        plci->vswitchstate=0;
+        plci->relatedPTYPLCI->vswitchstate=0;
+
+      }
+      else
+      {
+        PUT_WORD(&SS_Ind[1],pty_cai[5]+3);
+      }
+      if(pty_cai[2]!=0xff)
+      {
+        PUT_WORD(&SS_Ind[4],0x3600|(word)pty_cai[2]);
+      }
+      else
+      {
+        PUT_WORD(&SS_Ind[4],0x300E);
+      }
+      plci->relatedPTYPLCI = NULL;
+      plci->ptyState = 0;
+      sendf(tplci->appl,_FACILITY_I,rId,0,"ws",3, SS_Ind);
+      break;
+
+    case CALL_DEFLECTION:
+      if(pty_cai[2]!=0xff)
+      {
+        PUT_WORD(&SS_Ind[4],0x3600|(word)pty_cai[2]);
+      }
+      else
+      {
+        PUT_WORD(&SS_Ind[4],0x300E);
+      }
+      PUT_WORD(&SS_Ind[1],pty_cai[5]);
+      for(i=0; i<max_appl; i++)
+      {
+        if(application[i].CDEnable)
+        {
+          if(application[i].Id) sendf(&application[i],_FACILITY_I,Id,0,"ws",3, SS_Ind);
+          application[i].CDEnable = FALSE;
+        }
+      }
+      break;
+
+    case DEACTIVATION_DIVERSION:
+    case ACTIVATION_DIVERSION:
+    case DIVERSION_INTERROGATE_CFU:
+    case DIVERSION_INTERROGATE_CFB:
+    case DIVERSION_INTERROGATE_CFNR:
+    case DIVERSION_INTERROGATE_NUM:
+    case CCBS_REQUEST:
+    case CCBS_DEACTIVATE:
+    case CCBS_INTERROGATE:
+      if(!plci->appl) break;
+      if(pty_cai[2]!=0xff)
+      {
+        PUT_WORD(&Interr_Err_Ind[4],0x3600|(word)pty_cai[2]);
+      }
+      else
+      {
+        PUT_WORD(&Interr_Err_Ind[4],0x300E);
+      }
+      switch (pty_cai[5])
+      {
+        case DEACTIVATION_DIVERSION:
+          dbug(1,dprintf("Deact_Div"));
+          Interr_Err_Ind[0]=0x9;
+          Interr_Err_Ind[3]=0x6;
+          PUT_WORD(&Interr_Err_Ind[1],S_CALL_FORWARDING_STOP);
+          break;
+        case ACTIVATION_DIVERSION:
+          dbug(1,dprintf("Act_Div"));
+          Interr_Err_Ind[0]=0x9;
+          Interr_Err_Ind[3]=0x6;
+          PUT_WORD(&Interr_Err_Ind[1],S_CALL_FORWARDING_START);
+          break;
+        case DIVERSION_INTERROGATE_CFU:
+        case DIVERSION_INTERROGATE_CFB:
+        case DIVERSION_INTERROGATE_CFNR:
+          dbug(1,dprintf("Interr_Div"));
+          Interr_Err_Ind[0]=0xa;
+          Interr_Err_Ind[3]=0x7;
+          PUT_WORD(&Interr_Err_Ind[1],S_INTERROGATE_DIVERSION);
+          break;
+        case DIVERSION_INTERROGATE_NUM:
+          dbug(1,dprintf("Interr_Num"));
+          Interr_Err_Ind[0]=0xa;
+          Interr_Err_Ind[3]=0x7;
+          PUT_WORD(&Interr_Err_Ind[1],S_INTERROGATE_NUMBERS);
+          break;
+        case CCBS_REQUEST:
+          dbug(1,dprintf("CCBS Request"));
+          Interr_Err_Ind[0]=0xd;
+          Interr_Err_Ind[3]=0xa;
+          PUT_WORD(&Interr_Err_Ind[1],S_CCBS_REQUEST);
+          break;
+        case CCBS_DEACTIVATE:
+          dbug(1,dprintf("CCBS Deactivate"));
+          Interr_Err_Ind[0]=0x9;
+          Interr_Err_Ind[3]=0x6;
+          PUT_WORD(&Interr_Err_Ind[1],S_CCBS_DEACTIVATE);
+          break;
+        case CCBS_INTERROGATE:
+          dbug(1,dprintf("CCBS Interrogate"));
+          Interr_Err_Ind[0]=0xb;
+          Interr_Err_Ind[3]=0x8;
+          PUT_WORD(&Interr_Err_Ind[1],S_CCBS_INTERROGATE);
+          break;
+      }
+      PUT_DWORD(&Interr_Err_Ind[6],plci->appl->S_Handle);
+      sendf(plci->appl,_FACILITY_I,Id&0x7,0,"ws",3, Interr_Err_Ind);
+      plci_remove(plci);
+      break;
+    case ACTIVATION_MWI:      
+    case DEACTIVATION_MWI:
+      if(pty_cai[5]==ACTIVATION_MWI)
+      {
+        PUT_WORD(&SS_Ind[1],S_MWI_ACTIVATE);
+      }
+      else PUT_WORD(&SS_Ind[1],S_MWI_DEACTIVATE);
+      
+      if(pty_cai[2]!=0xff)
+      {
+        PUT_WORD(&SS_Ind[4],0x3600|(word)pty_cai[2]);
+      }
+      else
+      {
+        PUT_WORD(&SS_Ind[4],0x300E);
+      }
+
+      if(plci->cr_enquiry)
+      {
+        sendf(plci->appl,_FACILITY_I,Id&0xf,0,"ws",3, SS_Ind);
+        plci_remove(plci);
+      }
+      else
+      {
+        sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+      }
+      break;
+    case CONF_ADD: /* ERROR */
+    case CONF_BEGIN:
+    case CONF_DROP:
+    case CONF_ISOLATE:
+    case CONF_REATTACH:
+      CONF_Ind[0]=9;
+      CONF_Ind[3]=6;   
+      switch(pty_cai[5])
+      {
+      case CONF_BEGIN:
+          PUT_WORD(&CONF_Ind[1],S_CONF_BEGIN);
+          plci->ptyState = 0;
+          break;
+      case CONF_DROP:
+          CONF_Ind[0]=5;
+          CONF_Ind[3]=2;
+          PUT_WORD(&CONF_Ind[1],S_CONF_DROP);
+          plci->ptyState = CONNECTED;
+          break;
+      case CONF_ISOLATE:
+          CONF_Ind[0]=5;
+          CONF_Ind[3]=2;
+          PUT_WORD(&CONF_Ind[1],S_CONF_ISOLATE);
+          plci->ptyState = CONNECTED;
+          break;
+      case CONF_REATTACH:
+          CONF_Ind[0]=5;
+          CONF_Ind[3]=2;
+          PUT_WORD(&CONF_Ind[1],S_CONF_REATTACH);
+          plci->ptyState = CONNECTED;
+          break;
+      case CONF_ADD:
+          PUT_WORD(&CONF_Ind[1],S_CONF_ADD);
+          plci->relatedPTYPLCI = NULL;
+          tplci=plci->relatedPTYPLCI;
+          if(tplci) tplci->ptyState = CONNECTED;
+          plci->ptyState = CONNECTED;
+          break;
+      }
+          
+      if(pty_cai[2]!=0xff)
+      {
+        PUT_WORD(&CONF_Ind[4],0x3600|(word)pty_cai[2]);
+      }
+      else
+      {
+        PUT_WORD(&CONF_Ind[4],0x3303); /* Time-out: network did not respond
+                                            within the required time */
+      }
+
+      PUT_DWORD(&CONF_Ind[6],0x0);
+      sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, CONF_Ind);
+      break;
+    }
+    break;
+
+  /* Supplementary Service indicates success */
+  case S_SERVICE:
+    dbug(1,dprintf("Service_Ind"));
+    PUT_WORD (&CF_Ind[4], 0);
+    switch (pty_cai[5])
+    {
+    case THREE_PTY_END:
+    case THREE_PTY_BEGIN:
+    case ECT_EXECUTE:
+      if(!plci->relatedPTYPLCI) break;
+      tplci = plci->relatedPTYPLCI;
+      rId = ( (word)tplci->Id<<8)|tplci->adapter->Id;
+      if(tplci->tel) rId|=EXT_CONTROLLER;
+      if(pty_cai[5]==ECT_EXECUTE)
+      {
+        PUT_WORD(&SS_Ind[1],S_ECT);
+
+        if(plci->vswitchstate!=3)
+        {
+
+        plci->ptyState = IDLE;
+        plci->relatedPTYPLCI = NULL;
+        plci->ptyState = 0;
+
+        }
+
+        dbug(1,dprintf("ECT OK"));
+        sendf(tplci->appl,_FACILITY_I,rId,0,"ws",3, SS_Ind);
+
+
+
+      }
+      else
+      {
+        switch (plci->ptyState)
+        {
+        case S_3PTY_BEGIN:
+          plci->ptyState = CONNECTED;
+          dbug(1,dprintf("3PTY ON"));
+          break;
+
+        case S_3PTY_END:
+          plci->ptyState = IDLE;
+          plci->relatedPTYPLCI = NULL;
+          plci->ptyState = 0;
+          dbug(1,dprintf("3PTY OFF"));
+          break;
+        }
+        PUT_WORD(&SS_Ind[1],pty_cai[5]+3);
+        sendf(tplci->appl,_FACILITY_I,rId,0,"ws",3, SS_Ind);
+      }
+      break;
+
+    case CALL_DEFLECTION:
+      PUT_WORD(&SS_Ind[1],pty_cai[5]);
+      for(i=0; i<max_appl; i++)
+      {
+        if(application[i].CDEnable)
+        {
+          if(application[i].Id) sendf(&application[i],_FACILITY_I,Id,0,"ws",3, SS_Ind);
+          application[i].CDEnable = FALSE;
+        }
+      }
+      break;
+
+    case DEACTIVATION_DIVERSION:
+    case ACTIVATION_DIVERSION:
+      if(!plci->appl) break;
+      PUT_WORD(&CF_Ind[1],pty_cai[5]+2);
+      PUT_DWORD(&CF_Ind[6],plci->appl->S_Handle);
+      sendf(plci->appl,_FACILITY_I,Id&0x7,0,"ws",3, CF_Ind);
+      plci_remove(plci);
+      break;
+
+    case DIVERSION_INTERROGATE_CFU:
+    case DIVERSION_INTERROGATE_CFB:
+    case DIVERSION_INTERROGATE_CFNR:
+    case DIVERSION_INTERROGATE_NUM:
+    case CCBS_REQUEST:
+    case CCBS_DEACTIVATE:
+    case CCBS_INTERROGATE:
+      if(!plci->appl) break;
+      switch (pty_cai[5])
+      {
+        case DIVERSION_INTERROGATE_CFU:
+        case DIVERSION_INTERROGATE_CFB:
+        case DIVERSION_INTERROGATE_CFNR:
+          dbug(1,dprintf("Interr_Div"));
+          PUT_WORD(&pty_cai[1],S_INTERROGATE_DIVERSION);
+          pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+          break;
+        case DIVERSION_INTERROGATE_NUM:
+          dbug(1,dprintf("Interr_Num"));
+          PUT_WORD(&pty_cai[1],S_INTERROGATE_NUMBERS);
+          pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+          break;
+        case CCBS_REQUEST:
+          dbug(1,dprintf("CCBS Request"));
+          PUT_WORD(&pty_cai[1],S_CCBS_REQUEST);
+          pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+          break;
+        case CCBS_DEACTIVATE:
+          dbug(1,dprintf("CCBS Deactivate"));
+          PUT_WORD(&pty_cai[1],S_CCBS_DEACTIVATE);
+          pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+          break;
+        case CCBS_INTERROGATE:
+          dbug(1,dprintf("CCBS Interrogate"));
+          PUT_WORD(&pty_cai[1],S_CCBS_INTERROGATE);
+          pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+          break;
+      }
+      PUT_WORD(&pty_cai[4],0); /* Supplementary Service Reason */
+      PUT_DWORD(&pty_cai[6],plci->appl->S_Handle);
+      sendf(plci->appl,_FACILITY_I,Id&0x7,0,"wS",3, pty_cai);
+      plci_remove(plci);
+      break;
+
+    case ACTIVATION_MWI:
+    case DEACTIVATION_MWI:
+      if(pty_cai[5]==ACTIVATION_MWI)
+      {
+        PUT_WORD(&SS_Ind[1],S_MWI_ACTIVATE);
+      }
+      else PUT_WORD(&SS_Ind[1],S_MWI_DEACTIVATE);
+      if(plci->cr_enquiry)
+      {
+        sendf(plci->appl,_FACILITY_I,Id&0xf,0,"ws",3, SS_Ind);
+        plci_remove(plci);
+      }
+      else
+      {
+        sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+      }
+      break;
+    case MWI_INDICATION:
+      if(pty_cai[0]>=0x12)
+      {
+        PUT_WORD(&pty_cai[3],S_MWI_INDICATE);
+        pty_cai[2]=pty_cai[0]-2; /* len Parameter */
+        pty_cai[5]=pty_cai[0]-5; /* Supplementary Service-specific parameter len */
+        if(plci->appl && (a->Notification_Mask[plci->appl->Id-1]&SMASK_MWI))
+        {
+          if(plci->internal_command==GET_MWI_STATE) /* result on Message Waiting Listen */
+          {
+            sendf(plci->appl,_FACILITY_I,Id&0xf,0,"wS",3, &pty_cai[2]);
+            plci_remove(plci);
+            return;
+          }
+          else  sendf(plci->appl,_FACILITY_I,Id,0,"wS",3, &pty_cai[2]);
+          pty_cai[0]=0;
+        }
+        else
+        {
+          for(i=0; i<max_appl; i++)
+          {                     
+            if(a->Notification_Mask[i]&SMASK_MWI)
+            {
+              sendf(&application[i],_FACILITY_I,Id&0x7,0,"wS",3, &pty_cai[2]);
+              pty_cai[0]=0;
+            }
+          }
+        }
+
+        if(!pty_cai[0])
+        { /* acknowledge */
+          facility[2]= 0; /* returncode */
+        }
+        else facility[2]= 0xff;
+      }
+      else
+      {
+        /* reject */
+        facility[2]= 0xff; /* returncode */
+      }
+      facility[0]= 2;
+      facility[1]= MWI_RESPONSE; /* Function */
+      add_p(plci,CAI,facility);
+      add_p(plci,ESC,multi_ssext_parms[0]); /* remembered parameter -> only one possible */
+      sig_req(plci,S_SERVICE,0);
+      send_req(plci);
+      plci->command = 0;
+      next_internal_command (Id, plci);
+      break;
+    case CONF_ADD: /* OK */
+    case CONF_BEGIN:
+    case CONF_DROP:
+    case CONF_ISOLATE:
+    case CONF_REATTACH:
+    case CONF_PARTYDISC:
+      CONF_Ind[0]=9;
+      CONF_Ind[3]=6;
+      switch(pty_cai[5])
+      {
+      case CONF_BEGIN:
+          PUT_WORD(&CONF_Ind[1],S_CONF_BEGIN);
+          if(pty_cai[0]==6)
+          {
+              d=pty_cai[6];
+              PUT_DWORD(&CONF_Ind[6],d); /* PartyID */
+          }
+          else
+          {
+              PUT_DWORD(&CONF_Ind[6],0x0);
+          }
+          break;
+      case CONF_ISOLATE:
+          PUT_WORD(&CONF_Ind[1],S_CONF_ISOLATE);
+          CONF_Ind[0]=5;
+          CONF_Ind[3]=2;
+          break;
+      case CONF_REATTACH:
+          PUT_WORD(&CONF_Ind[1],S_CONF_REATTACH);
+          CONF_Ind[0]=5;
+          CONF_Ind[3]=2;
+          break;
+      case CONF_DROP:
+          PUT_WORD(&CONF_Ind[1],S_CONF_DROP);
+          CONF_Ind[0]=5;
+          CONF_Ind[3]=2;
+          break;
+      case CONF_ADD:
+          PUT_WORD(&CONF_Ind[1],S_CONF_ADD);
+          d=pty_cai[6];
+          PUT_DWORD(&CONF_Ind[6],d); /* PartyID */
+          tplci=plci->relatedPTYPLCI;
+          if(tplci) tplci->ptyState = CONNECTED;
+          break;
+      case CONF_PARTYDISC:
+          CONF_Ind[0]=7;
+          CONF_Ind[3]=4;          
+          PUT_WORD(&CONF_Ind[1],S_CONF_PARTYDISC);
+          d=pty_cai[6];
+          PUT_DWORD(&CONF_Ind[4],d); /* PartyID */
+          break;
+      }
+      plci->ptyState = CONNECTED;
+      sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, CONF_Ind);
+      break;
+    case CCBS_INFO_RETAIN:
+    case CCBS_ERASECALLLINKAGEID:
+    case CCBS_STOP_ALERTING:
+      CONF_Ind[0]=5;
+      CONF_Ind[3]=2;
+      switch(pty_cai[5])
+      {
+      case CCBS_INFO_RETAIN:
+        PUT_WORD(&CONF_Ind[1],S_CCBS_INFO_RETAIN);
+        break;
+      case CCBS_STOP_ALERTING:
+        PUT_WORD(&CONF_Ind[1],S_CCBS_STOP_ALERTING);
+    break;
+      case CCBS_ERASECALLLINKAGEID:
+        PUT_WORD(&CONF_Ind[1],S_CCBS_ERASECALLLINKAGEID);
+        CONF_Ind[0]=7;
+        CONF_Ind[3]=4;
+        CONF_Ind[6]=0;
+        CONF_Ind[7]=0;
+        break;
+      }      
+      w=pty_cai[6];
+      PUT_WORD(&CONF_Ind[4],w); /* PartyID */
+
+      if(plci->appl && (a->Notification_Mask[plci->appl->Id-1]&SMASK_CCBS))
+      {
+        sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, CONF_Ind);
+      }
+      else
+      {
+        for(i=0; i<max_appl; i++)
+            if(a->Notification_Mask[i]&SMASK_CCBS)
+                sendf(&application[i],_FACILITY_I,Id&0x7,0,"ws",3, CONF_Ind);
+      }
+      break;
+    }
+    break;
+  case CALL_HOLD_REJ:
+    cau = parms[7];
+    if(cau)
+    {
+      i = _L3_CAUSE | cau[2];
+      if(cau[2]==0) i = 0x3603;
+    }
+    else
+    {
+      i = 0x3603;
+    }
+    PUT_WORD(&SS_Ind[1],S_HOLD);
+    PUT_WORD(&SS_Ind[4],i);
+    if(plci->SuppState == HOLD_REQUEST)
+    {
+      plci->SuppState = IDLE;
+      sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+    }
+    break;
+
+  case CALL_HOLD_ACK:
+    if(plci->SuppState == HOLD_REQUEST)
+    {
+      plci->SuppState = CALL_HELD;
+      CodecIdCheck(a, plci);
+      start_internal_command (Id, plci, hold_save_command);
+    }
+    break;
+
+  case CALL_RETRIEVE_REJ:
+    cau = parms[7];
+    if(cau)
+    {
+      i = _L3_CAUSE | cau[2];
+      if(cau[2]==0) i = 0x3603;
+    }
+    else
+    {
+      i = 0x3603;
+    }
+    PUT_WORD(&SS_Ind[1],S_RETRIEVE);
+    PUT_WORD(&SS_Ind[4],i);
+    if(plci->SuppState == RETRIEVE_REQUEST)
+    {
+      plci->SuppState = CALL_HELD;
+      CodecIdCheck(a, plci);
+      sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+    }
+    break;
+
+  case CALL_RETRIEVE_ACK:
+    PUT_WORD(&SS_Ind[1],S_RETRIEVE);
+    if(plci->SuppState == RETRIEVE_REQUEST)
+    {
+      plci->SuppState = IDLE;
+      plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
+      plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
+      if(plci->tel)
+      {
+        mixer_set_bchannel_id_esc (plci, plci->b_channel);
+        dbug(1,dprintf("RetrChannel=0x%x",plci->b_channel));
+        SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
+        if(plci->B2_prot==B2_TRANSPARENT && plci->B3_prot==B3_TRANSPARENT)
+        {
+          dbug(1,dprintf("Get B-ch"));
+          start_internal_command (Id, plci, retrieve_restore_command);
+        }
+        else
+          sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+      }
+      else
+        start_internal_command (Id, plci, retrieve_restore_command);
+    }
+    break;
+
+  case INDICATE_IND:
+    if(plci->State != LISTENING) {
+      sig_req(plci,HANGUP,0);
+      send_req(plci);
+      break;
+    }
+    cip = find_cip(a,parms[4],parms[6]);
+    cip_mask = 1L<<cip;
+    dbug(1,dprintf("cip=%d,cip_mask=%lx",cip,cip_mask));
+    clear_c_ind_mask (plci);
+    if (!remove_started && !a->adapter_disabled)
+    {
+      set_c_ind_mask_bit (plci, MAX_APPL);
+      group_optimization(a, plci);
+      for(i=0; i<max_appl; i++) {
+        if(application[i].Id
+        && (a->CIP_Mask[i]&1 || a->CIP_Mask[i]&cip_mask)
+        && CPN_filter_ok(parms[0],a,i)
+        && test_group_ind_mask_bit (plci, i) ) {
+          dbug(1,dprintf("storedcip_mask[%d]=0x%lx",i,a->CIP_Mask[i] ));
+          set_c_ind_mask_bit (plci, i);
+          dump_c_ind_mask (plci);
+          plci->State = INC_CON_PENDING;
+          plci->call_dir = (plci->call_dir & ~(CALL_DIR_OUT | CALL_DIR_ORIGINATE)) |
+            CALL_DIR_IN | CALL_DIR_ANSWER;
+          if(esc_chi[0]) {
+            plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
+            mixer_set_bchannel_id_esc (plci, plci->b_channel);
+          }
+          /* if a listen on the ext controller is done, check if hook states */
+          /* are supported or if just a on board codec must be activated     */
+          if(a->codec_listen[i] && !a->AdvSignalPLCI) {
+            if(a->profile.Global_Options & HANDSET)
+              plci->tel = ADV_VOICE;
+            else if(a->profile.Global_Options & ON_BOARD_CODEC)
+              plci->tel = CODEC;
+            if(plci->tel) Id|=EXT_CONTROLLER;
+            a->codec_listen[i] = plci;
+          }
+
+          sendf(&application[i],_CONNECT_I,Id,0,
+                "wSSSSSSSbSSSSS", cip,    /* CIP                 */
+                             parms[0],    /* CalledPartyNumber   */
+                             multi_CiPN_parms[0],    /* CallingPartyNumber  */
+                             parms[2],    /* CalledPartySubad    */
+                             parms[3],    /* CallingPartySubad   */
+                             parms[4],    /* BearerCapability    */
+                             parms[5],    /* LowLC               */
+                             parms[6],    /* HighLC              */
+                             ai_len,      /* nested struct add_i */
+                             add_i[0],    /* B channel info    */
+                             add_i[1],    /* keypad facility   */
+                             add_i[2],    /* user user data    */
+                             add_i[3],    /* nested facility   */
+                             multi_CiPN_parms[1]    /* second CiPN(SCR)   */
+                             );
+          SendSSExtInd(&application[i],
+                        plci,
+                        Id,
+                        multi_ssext_parms);
+          SendSetupInfo(&application[i],
+                        plci,
+                        Id,
+                        parms,
+                        SendMultiIE(plci,Id,multi_pi_parms, PI, 0x210, TRUE));
+        }
+      }
+      clear_c_ind_mask_bit (plci, MAX_APPL);
+      dump_c_ind_mask (plci);
+    }
+    if(c_ind_mask_empty (plci)) {
+      sig_req(plci,HANGUP,0);
+      send_req(plci);
+      plci->State = IDLE;
+    }
+    plci->notifiedcall = 0;
+    a->listen_active--;
+    listen_check(a);
+    break;
+
+  case CALL_PEND_NOTIFY:
+    plci->notifiedcall = 1;
+    listen_check(a);
+    break;
+
+  case CALL_IND:
+  case CALL_CON:
+    if(plci->State==ADVANCED_VOICE_SIG || plci->State==ADVANCED_VOICE_NOSIG)
+    {
+      if(plci->internal_command==PERM_COD_CONN_PEND)
+      {
+        if(plci->State==ADVANCED_VOICE_NOSIG)
+        {
+          dbug(1,dprintf("***Codec OK"));
+          if(a->AdvSignalPLCI)
+          {
+            tplci = a->AdvSignalPLCI;
+            if(tplci->spoofed_msg)
+            {
+              dbug(1,dprintf("***Spoofed Msg(0x%x)",tplci->spoofed_msg));
+              tplci->command = 0;
+              tplci->internal_command = 0;
+              x_Id = ((word)tplci->Id<<8)|tplci->adapter->Id | 0x80;
+              switch (tplci->spoofed_msg)
+              {
+              case CALL_RES:
+                tplci->command = _CONNECT_I|RESPONSE;
+                api_load_msg (&tplci->saved_msg, saved_parms);
+                add_b1(tplci,&saved_parms[1],0,tplci->B1_facilities);
+                if (tplci->adapter->Info_Mask[tplci->appl->Id-1] & 0x200)
+                {
+                  /* early B3 connect (CIP mask bit 9) no release after a disc */
+                  add_p(tplci,LLI,"\x01\x01");
+                }
+                add_s(tplci, CONN_NR, &saved_parms[2]);
+                add_s(tplci, LLC, &saved_parms[4]);
+                add_ai(tplci, &saved_parms[5]);
+                tplci->State = INC_CON_ACCEPT;
+                sig_req(tplci, CALL_RES,0);
+                send_req(tplci);
+                break;
+
+              case AWAITING_SELECT_B:
+                dbug(1,dprintf("Select_B continue"));
+                start_internal_command (x_Id, tplci, select_b_command);
+                break;
+
+              case AWAITING_MANUF_CON: /* Get_Plci per Manufacturer_Req to ext controller */
+                if(!tplci->Sig.Id)
+                {
+                  dbug(1,dprintf("No SigID!"));
+                  sendf(tplci->appl, _MANUFACTURER_R|CONFIRM,x_Id,tplci->number, "dww",_DI_MANU_ID,_MANUFACTURER_R,_OUT_OF_PLCI);
+                  plci_remove(tplci);
+                  break;
+                }
+                tplci->command = _MANUFACTURER_R;
+                api_load_msg (&tplci->saved_msg, saved_parms);
+                dir = saved_parms[2].info[0];
+                if(dir==1) {
+                  sig_req(tplci,CALL_REQ,0);
+                }
+                else if(!dir){
+                  sig_req(tplci,LISTEN_REQ,0);
+                }
+                send_req(tplci);
+                sendf(tplci->appl, _MANUFACTURER_R|CONFIRM,x_Id,tplci->number, "dww",_DI_MANU_ID,_MANUFACTURER_R,0);
+                break;
+
+              case (CALL_REQ|AWAITING_MANUF_CON):
+                sig_req(tplci,CALL_REQ,0);
+                send_req(tplci);
+                break;
+
+              case CALL_REQ:
+                if(!tplci->Sig.Id)
+                {
+                  dbug(1,dprintf("No SigID!"));
+                  sendf(tplci->appl,_CONNECT_R|CONFIRM,tplci->adapter->Id,0,"w",_OUT_OF_PLCI);
+                  plci_remove(tplci);
+                  break;
+                }
+                tplci->command = _CONNECT_R;
+                api_load_msg (&tplci->saved_msg, saved_parms);
+                add_s(tplci,CPN,&saved_parms[1]);
+                add_s(tplci,DSA,&saved_parms[3]);
+                add_ai(tplci,&saved_parms[9]);
+                sig_req(tplci,CALL_REQ,0);
+                send_req(tplci);
+                break;
+
+              case CALL_RETRIEVE:
+                tplci->command = C_RETRIEVE_REQ;
+                sig_req(tplci,CALL_RETRIEVE,0);
+                send_req(tplci);
+                break;
+              }
+              tplci->spoofed_msg = 0;
+              if (tplci->internal_command == 0)
+                next_internal_command (x_Id, tplci);
+            }
+          }
+          next_internal_command (Id, plci);
+          break;
+        }
+        dbug(1,dprintf("***Codec Hook Init Req"));
+        plci->internal_command = PERM_COD_HOOK;
+        add_p(plci,FTY,"\x01\x09");             /* Get Hook State*/
+        sig_req(plci,TEL_CTRL,0);
+        send_req(plci);
+      }
+    }
+    else if(plci->command != _MANUFACTURER_R  /* old style permanent connect */
+    && plci->State!=INC_ACT_PENDING)
+    {
+      mixer_set_bchannel_id_esc (plci, plci->b_channel);
+      if(plci->tel == ADV_VOICE && plci->SuppState == IDLE) /* with permanent codec switch on immediately */
+      {
+        chi[2] = plci->b_channel;
+        SetVoiceChannel(a->AdvCodecPLCI, chi, a);
+      }
+      sendf(plci->appl,_CONNECT_ACTIVE_I,Id,0,"Sss",parms[21],"","");
+      plci->State = INC_ACT_PENDING;
+    }
+    break;
+
+  case TEL_CTRL:
+    Number = 0;
+    ie = multi_fac_parms[0]; /* inspect the facility hook indications */
+    if(plci->State==ADVANCED_VOICE_SIG && ie[0]){
+      switch (ie[1]&0x91) {
+        case 0x80:   /* hook off */
+        case 0x81:
+          if(plci->internal_command==PERM_COD_HOOK)
+          {
+            dbug(1,dprintf("init:hook_off"));
+            plci->hook_state = ie[1];
+            next_internal_command (Id, plci);
+            break;
+          }
+          else /* ignore doubled hook indications */
+          {
+            if( ((plci->hook_state)&0xf0)==0x80)
+            {
+              dbug(1,dprintf("ignore hook"));
+              break;
+            }
+            plci->hook_state = ie[1]&0x91;
+          }
+          /* check for incoming call pending */
+          /* and signal '+'.Appl must decide */
+          /* with connect_res if call must   */
+          /* accepted or not                 */
+          for(i=0, tplci=NULL;i<max_appl;i++){
+            if(a->codec_listen[i]
+            && (a->codec_listen[i]->State==INC_CON_PENDING
+              ||a->codec_listen[i]->State==INC_CON_ALERT) ){
+              tplci = a->codec_listen[i];
+              tplci->appl = &application[i];
+            }
+          }
+          /* no incoming call, do outgoing call */
+          /* and signal '+' if outg. setup   */
+          if(!a->AdvSignalPLCI && !tplci){
+            if((i=get_plci(a))) {
+              a->AdvSignalPLCI = &a->plci[i-1];
+              tplci = a->AdvSignalPLCI;
+              tplci->tel  = ADV_VOICE;
+              PUT_WORD(&voice_cai[5],a->AdvSignalAppl->MaxDataLength);
+              if (a->Info_Mask[a->AdvSignalAppl->Id-1] & 0x200){
+                /* early B3 connect (CIP mask bit 9) no release after a disc */
+                add_p(tplci,LLI,"\x01\x01");
+              }
+              add_p(tplci, CAI, voice_cai);
+              add_p(tplci, OAD, a->TelOAD);
+              add_p(tplci, OSA, a->TelOSA);
+              add_p(tplci,SHIFT|6,NULL);
+              add_p(tplci,SIN,"\x02\x01\x00");
+              add_p(tplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+              sig_req(tplci,ASSIGN,DSIG_ID);
+              a->AdvSignalPLCI->internal_command = HOOK_OFF_REQ;
+              a->AdvSignalPLCI->command = 0;
+              tplci->appl = a->AdvSignalAppl;
+              tplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+              send_req(tplci);
+            }
+
+          }
+
+          if(!tplci) break;
+          Id = ((word)tplci->Id<<8)|a->Id;
+          Id|=EXT_CONTROLLER;
+          sendf(tplci->appl,
+                _FACILITY_I,
+                Id,
+                0,
+                "ws", (word)0, "\x01+");
+          break;
+
+        case 0x90:   /* hook on  */
+        case 0x91:
+          if(plci->internal_command==PERM_COD_HOOK)
+          {
+            dbug(1,dprintf("init:hook_on"));
+            plci->hook_state = ie[1]&0x91;
+            next_internal_command (Id, plci);
+            break;
+          }
+          else /* ignore doubled hook indications */
+          {
+            if( ((plci->hook_state)&0xf0)==0x90) break;
+            plci->hook_state = ie[1]&0x91;
+          }
+          /* hangup the adv. voice call and signal '-' to the appl */
+          if(a->AdvSignalPLCI) {
+            Id = ((word)a->AdvSignalPLCI->Id<<8)|a->Id;
+            if(plci->tel) Id|=EXT_CONTROLLER;
+            sendf(a->AdvSignalAppl,
+                  _FACILITY_I,
+                  Id,
+                  0,
+                  "ws", (word)0, "\x01-");
+            a->AdvSignalPLCI->internal_command = HOOK_ON_REQ;
+            a->AdvSignalPLCI->command = 0;
+            sig_req(a->AdvSignalPLCI,HANGUP,0);
+            send_req(a->AdvSignalPLCI);
+          }
+          break;
+      }
+    }
+    break;
+
+  case RESUME:
+    clear_c_ind_mask_bit (plci, (word)(plci->appl->Id-1));
+    PUT_WORD(&resume_cau[4],GOOD);
+    sendf(plci->appl,_FACILITY_I,Id,0,"ws", (word)3, resume_cau);
+    break;
+
+  case SUSPEND:
+    clear_c_ind_mask (plci);
+
+    if (plci->NL.Id && !plci->nl_remove_id) {
+      mixer_remove (plci);
+      nl_req_ncci(plci,REMOVE,0);
+    }
+    if (!plci->sig_remove_id) {
+      plci->internal_command = 0;
+      sig_req(plci,REMOVE,0);
+    }
+    send_req(plci);
+    if(!plci->channels) {
+      sendf(plci->appl,_FACILITY_I,Id,0,"ws", (word)3, "\x05\x04\x00\x02\x00\x00");
+      sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
+    }
+    break;
+
+  case SUSPEND_REJ:
+    break;
+
+  case HANGUP:
+    plci->hangup_flow_ctrl_timer=0;
+    if(plci->manufacturer && plci->State==LOCAL_CONNECT) break;
+    cau = parms[7];
+    if(cau) {
+      i = _L3_CAUSE | cau[2];
+      if(cau[2]==0) i = 0;
+      else if(cau[2]==8) i = _L1_ERROR;
+      else if(cau[2]==9 || cau[2]==10) i = _L2_ERROR;
+      else if(cau[2]==5) i = _CAPI_GUARD_ERROR;
+    }
+    else {
+      i = _L3_ERROR;
+    }
+
+    if(plci->State==INC_CON_PENDING || plci->State==INC_CON_ALERT)
+    {
+      for(i=0; i<max_appl; i++)
+      {
+        if(test_c_ind_mask_bit (plci, i))
+          sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
+      }
+    }
+    else
+    {
+      clear_c_ind_mask (plci);
+    }
+    if(!plci->appl)
+    {
+      if (plci->State == LISTENING)
+      {
+        plci->notifiedcall=0;
+        a->listen_active--;
+      }
+      plci->State = INC_DIS_PENDING;
+      if(c_ind_mask_empty (plci))
+      {
+        plci->State = IDLE;
+        if (plci->NL.Id && !plci->nl_remove_id)
+        {
+          mixer_remove (plci);
+          nl_req_ncci(plci,REMOVE,0);
+        }
+        if (!plci->sig_remove_id)
+        {
+          plci->internal_command = 0;
+          sig_req(plci,REMOVE,0);
+        }
+        send_req(plci);
+      }
+    }
+    else
+    {
+        /* collision of DISCONNECT or CONNECT_RES with HANGUP can   */
+        /* result in a second HANGUP! Don't generate another        */
+        /* DISCONNECT                                               */
+      if(plci->State!=IDLE && plci->State!=INC_DIS_PENDING)
+      {
+        if(plci->State==RESUMING)
+        {
+          PUT_WORD(&resume_cau[4],i);
+          sendf(plci->appl,_FACILITY_I,Id,0,"ws", (word)3, resume_cau);
+        }
+        plci->State = INC_DIS_PENDING;
+        sendf(plci->appl,_DISCONNECT_I,Id,0,"w",i);
+      }
+    }
+    break;
+
+  case SSEXT_IND:
+    SendSSExtInd(NULL,plci,Id,multi_ssext_parms);
+    break;
+
+  case VSWITCH_REQ:
+    VSwitchReqInd(plci,Id,multi_vswitch_parms);
+    break;
+  case VSWITCH_IND:
+ if(plci->relatedPTYPLCI &&
+  plci->vswitchstate==3 &&
+  plci->relatedPTYPLCI->vswitchstate==3 &&
+  parms[MAXPARMSIDS-1][0])
+ {
+  add_p(plci->relatedPTYPLCI,SMSG,parms[MAXPARMSIDS-1]);
+  sig_req(plci->relatedPTYPLCI,VSWITCH_REQ,0);
+  send_req(plci->relatedPTYPLCI);
+ }
+    else VSwitchReqInd(plci,Id,multi_vswitch_parms);
+    break;
+
+  }
+}
+
+
+static void SendSetupInfo(APPL   * appl, PLCI   * plci, dword Id, byte   * * parms, byte Info_Sent_Flag)
+{
+  word i;
+  byte   * ie;
+  word Info_Number;
+  byte   * Info_Element;
+  word Info_Mask = 0;
+
+  dbug(1,dprintf("SetupInfo"));
+
+  for(i=0; i<MAXPARMSIDS; i++) {
+    ie = parms[i];
+    Info_Number = 0;
+    Info_Element = ie;
+    if(ie[0]) {
+      switch(i) {
+      case 0:
+        dbug(1,dprintf("CPN "));
+        Info_Number = 0x0070;
+        Info_Mask   = 0x80;
+        Info_Sent_Flag = TRUE;
+        break;
+      case 8:  /* display      */
+        dbug(1,dprintf("display(%d)",i));
+        Info_Number = 0x0028;
+        Info_Mask = 0x04;
+        Info_Sent_Flag = TRUE;
+        break;
+      case 16: /* Channel Id */
+        dbug(1,dprintf("CHI"));
+        Info_Number = 0x0018;
+        Info_Mask = 0x100;
+        Info_Sent_Flag = TRUE;
+        mixer_set_bchannel_id (plci, Info_Element);
+        break;
+      case 19: /* Redirected Number */
+        dbug(1,dprintf("RDN"));
+        Info_Number = 0x0074;
+        Info_Mask = 0x400;
+        Info_Sent_Flag = TRUE;
+        break;
+      case 20: /* Redirected Number extended */
+        dbug(1,dprintf("RDX"));
+        Info_Number = 0x0073;
+        Info_Mask = 0x400;
+        Info_Sent_Flag = TRUE;
+        break;
+      case 22: /* Redirecing Number  */
+        dbug(1,dprintf("RIN"));
+        Info_Number = 0x0076;
+        Info_Mask = 0x400;
+        Info_Sent_Flag = TRUE;
+        break;
+      default:
+        Info_Number = 0;
+        break;
+      }
+    }
+
+    if(i==MAXPARMSIDS-2){ /* to indicate the message type "Setup" */
+      Info_Number = 0x8000 |5;
+      Info_Mask = 0x10;
+      Info_Element = "";
+    }
+
+    if(Info_Sent_Flag && Info_Number){
+      if(plci->adapter->Info_Mask[appl->Id-1] & Info_Mask) {
+        sendf(appl,_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+      }
+    }
+  }
+}
+
+
+void SendInfo(PLCI   * plci, dword Id, byte   * * parms, byte iesent)
+{
+  word i;
+  word j;
+  word k;
+  byte   * ie;
+  word Info_Number;
+  byte   * Info_Element;
+  word Info_Mask = 0;
+  static byte charges[5] = {4,0,0,0,0};
+  static byte cause[] = {0x02,0x80,0x00};
+  APPL   *appl;
+
+  dbug(1,dprintf("InfoParse "));
+
+  if(
+        !plci->appl
+        && !plci->State
+        && plci->Sig.Ind!=NCR_FACILITY
+      )
+  {
+    dbug(1,dprintf("NoParse "));
+    return;
+  }
+  cause[2] = 0;
+  for(i=0; i<MAXPARMSIDS; i++) {
+    ie = parms[i];
+    Info_Number = 0;
+    Info_Element = ie;
+    if(ie[0]) {
+      switch(i) {
+      case 0:
+        dbug(1,dprintf("CPN "));
+        Info_Number = 0x0070;
+        Info_Mask   = 0x80;
+        break;
+      case 7: /* ESC_CAU */
+        dbug(1,dprintf("cau(0x%x)",ie[2]));
+        Info_Number = 0x0008;
+        Info_Mask = 0x00;
+        cause[2] = ie[2];
+        Info_Element = NULL;
+        break;
+      case 8:  /* display      */
+        dbug(1,dprintf("display(%d)",i));
+        Info_Number = 0x0028;
+        Info_Mask = 0x04;
+        break;
+      case 9:  /* Date display */
+        dbug(1,dprintf("date(%d)",i));
+        Info_Number = 0x0029;
+        Info_Mask = 0x02;
+        break;
+      case 10: /* charges */
+        for(j=0;j<4;j++) charges[1+j] = 0;
+        for(j=0; j<ie[0] && !(ie[1+j]&0x80); j++);
+        for(k=1,j++; j<ie[0] && k<=4; j++,k++) charges[k] = ie[1+j];
+        Info_Number = 0x4000;
+        Info_Mask = 0x40;
+        Info_Element = charges;
+        break;
+      case 11: /* user user info */
+        dbug(1,dprintf("uui"));
+        Info_Number = 0x007E;
+        Info_Mask = 0x08;
+        break;
+      case 12: /* congestion receiver ready */
+        dbug(1,dprintf("clRDY"));
+        Info_Number = 0x00B0;
+        Info_Mask = 0x08;
+        Info_Element = "";
+        break;
+      case 13: /* congestion receiver not ready */
+        dbug(1,dprintf("clNRDY"));
+        Info_Number = 0x00BF;
+        Info_Mask = 0x08;
+        Info_Element = "";
+        break;
+      case 15: /* Keypad Facility */
+        dbug(1,dprintf("KEY"));
+        Info_Number = 0x002C;
+        Info_Mask = 0x20;
+        break;
+      case 16: /* Channel Id */
+        dbug(1,dprintf("CHI"));
+        Info_Number = 0x0018;
+        Info_Mask = 0x100;
+        mixer_set_bchannel_id (plci, Info_Element);
+        break;
+      case 17: /* if no 1tr6 cause, send full cause, else esc_cause */
+        dbug(1,dprintf("q9cau(0x%x)",ie[2]));
+        if(!cause[2] || cause[2]<0x80) break;  /* eg. layer 1 error */
+        Info_Number = 0x0008;
+        Info_Mask = 0x01;
+        if(cause[2] != ie[2]) Info_Element = cause;
+        break;
+      case 19: /* Redirected Number */
+        dbug(1,dprintf("RDN"));
+        Info_Number = 0x0074;
+        Info_Mask = 0x400;
+        break;
+      case 22: /* Redirecing Number  */
+        dbug(1,dprintf("RIN"));
+        Info_Number = 0x0076;
+        Info_Mask = 0x400;
+        break;
+      case 23: /* Notification Indicator  */
+        dbug(1,dprintf("NI"));
+        Info_Number = (word)NI;
+        Info_Mask = 0x210;
+        break;
+      case 26: /* Call State  */
+        dbug(1,dprintf("CST"));
+        Info_Number = (word)CST;
+        Info_Mask = 0x01; /* do with cause i.e. for now */
+        break;
+      case MAXPARMSIDS-2:  /* Escape Message Type, must be the last indication */
+        dbug(1,dprintf("ESC/MT[0x%x]",ie[3]));
+        Info_Number = 0x8000 |ie[3];
+        if(iesent) Info_Mask = 0xffff;
+        else  Info_Mask = 0x10;
+        Info_Element = "";
+        break;
+      default:
+        Info_Number  = 0;
+        Info_Mask    = 0;
+        Info_Element = "";
+        break;
+      }
+    }
+
+    if(plci->Sig.Ind==NCR_FACILITY)           /* check controller broadcast */
+    {
+      for(j=0; j<max_appl; j++)
+      {
+        appl = &application[j];
+        if(Info_Number
+        && appl->Id
+        && plci->adapter->Info_Mask[appl->Id-1] &Info_Mask)
+        {
+          dbug(1,dprintf("NCR_Ind"));
+          iesent=TRUE;
+          sendf(&application[j],_INFO_I,Id&0x0f,0,"wS",Info_Number,Info_Element);
+        }
+      }
+    }
+    else if(!plci->appl)
+    { /* overlap receiving broadcast */
+      if(Info_Number==CPN
+      || Info_Number==KEY
+      || Info_Number==NI
+      || Info_Number==DSP
+      || Info_Number==UUI )
+      {
+        for(j=0; j<max_appl; j++)
+        {
+          if(test_c_ind_mask_bit (plci, j))
+          {
+            dbug(1,dprintf("Ovl_Ind"));
+            iesent=TRUE;
+            sendf(&application[j],_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+          }
+        }
+      }
+    }               /* all other signalling states */
+    else if(Info_Number
+    && plci->adapter->Info_Mask[plci->appl->Id-1] &Info_Mask)
+    {
+      dbug(1,dprintf("Std_Ind"));
+      iesent=TRUE;
+      sendf(plci->appl,_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+    }
+  }
+}
+
+
+byte SendMultiIE(PLCI   * plci, dword Id, byte   * * parms, byte ie_type, dword info_mask, byte setupParse)
+{
+  word i;
+  word j;
+  byte   * ie;
+  word Info_Number;
+  byte   * Info_Element;
+  APPL   *appl;
+  word Info_Mask = 0;
+  byte iesent=0;
+
+  if(
+      !plci->appl
+      && !plci->State
+      && plci->Sig.Ind!=NCR_FACILITY
+      && !setupParse
+      )
+  {
+    dbug(1,dprintf("NoM-IEParse "));
+    return 0;
+  }
+  dbug(1,dprintf("M-IEParse "));
+
+  for(i=0; i<MAX_MULTI_IE; i++)
+  {
+    ie = parms[i];
+    Info_Number = 0;
+    Info_Element = ie;
+    if(ie[0])
+    {
+      dbug(1,dprintf("[Ind0x%x]:IE=0x%x",plci->Sig.Ind,ie_type));
+      Info_Number = (word)ie_type;
+      Info_Mask = (word)info_mask;
+    }
+
+    if(plci->Sig.Ind==NCR_FACILITY)           /* check controller broadcast */
+    {
+      for(j=0; j<max_appl; j++)
+      {
+        appl = &application[j];
+        if(Info_Number
+        && appl->Id
+        && plci->adapter->Info_Mask[appl->Id-1] &Info_Mask)
+        {
+          iesent = TRUE;
+          dbug(1,dprintf("Mlt_NCR_Ind"));
+          sendf(&application[j],_INFO_I,Id&0x0f,0,"wS",Info_Number,Info_Element);
+        }
+      }
+    }
+    else if(!plci->appl && Info_Number)
+    {                                        /* overlap receiving broadcast */
+      for(j=0; j<max_appl; j++)
+      {
+        if(test_c_ind_mask_bit (plci, j))
+        {
+          iesent = TRUE;
+          dbug(1,dprintf("Mlt_Ovl_Ind"));
+          sendf(&application[j],_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+        }
+      }
+    }                                        /* all other signalling states */
+    else if(Info_Number
+    && plci->adapter->Info_Mask[plci->appl->Id-1] &Info_Mask)
+    {
+      iesent = TRUE;
+      dbug(1,dprintf("Mlt_Std_Ind"));
+      sendf(plci->appl,_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+    }
+  }
+  return iesent;
+}
+
+static void SendSSExtInd(APPL   * appl, PLCI   * plci, dword Id, byte   * * parms)
+{
+  word i;
+   /* Format of multi_ssext_parms[i][]:
+   0 byte length
+   1 byte SSEXTIE
+   2 byte SSEXT_REQ/SSEXT_IND
+   3 byte length
+   4 word SSExtCommand
+   6... Params
+   */
+  if(
+   plci
+   && plci->State
+   && plci->Sig.Ind!=NCR_FACILITY
+    )
+ for(i=0;i<MAX_MULTI_IE;i++)
+    {
+      if(parms[i][0]<6) continue;
+   if(parms[i][2]==SSEXT_REQ) continue;
+
+   if(appl)
+   {
+    parms[i][0]=0; /* kill it */
+    sendf(appl,_MANUFACTURER_I,
+    Id,
+    0,
+    "dwS",
+    _DI_MANU_ID,
+    _DI_SSEXT_CTRL,
+    &parms[i][3]);
+   }
+   else if(plci->appl)
+   {
+    parms[i][0]=0; /* kill it */
+    sendf(plci->appl,_MANUFACTURER_I,
+    Id,
+    0,
+    "dwS",
+    _DI_MANU_ID,
+    _DI_SSEXT_CTRL,
+    &parms[i][3]);
+   }
+    }
+};
+
+void nl_ind(PLCI   * plci)
+{
+  byte ch;
+  word ncci;
+  dword Id;
+  DIVA_CAPI_ADAPTER   * a;
+  word NCCIcode;
+  APPL   * APPLptr;
+  word count;
+  word Num;
+  word i, ncpi_state;
+  byte len, ncci_state;
+  word msg;
+  word info = 0;
+  word fax_feature_bits;
+  byte fax_send_edata_ack;
+  static byte v120_header_buffer[2 + 3];
+  static word fax_info[] = {
+    0,                     /* T30_SUCCESS                        */
+    _FAX_NO_CONNECTION,    /* T30_ERR_NO_DIS_RECEIVED            */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_NO_RESPONSE        */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_RESPONSE          */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_TOO_MANY_REPEATS           */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_UNEXPECTED_MESSAGE         */
+    _FAX_REMOTE_ABORT,     /* T30_ERR_UNEXPECTED_DCN             */
+    _FAX_LOCAL_ABORT,      /* T30_ERR_DTC_UNSUPPORTED            */
+    _FAX_TRAINING_ERROR,   /* T30_ERR_ALL_RATES_FAILED           */
+    _FAX_TRAINING_ERROR,   /* T30_ERR_TOO_MANY_TRAINS            */
+    _FAX_PARAMETER_ERROR,  /* T30_ERR_RECEIVE_CORRUPTED          */
+    _FAX_REMOTE_ABORT,     /* T30_ERR_UNEXPECTED_DISC            */
+    _FAX_LOCAL_ABORT,      /* T30_ERR_APPLICATION_DISC           */
+    _FAX_REMOTE_REJECT,    /* T30_ERR_INCOMPATIBLE_DIS           */
+    _FAX_LOCAL_ABORT,      /* T30_ERR_INCOMPATIBLE_DCS           */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_NO_COMMAND         */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_COMMAND           */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_COMMAND_TOO_LONG   */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_TIMEOUT_RESPONSE_TOO_LONG  */
+    _FAX_NO_CONNECTION,    /* T30_ERR_NOT_IDENTIFIED             */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_SUPERVISORY_TIMEOUT        */
+    _FAX_PARAMETER_ERROR,  /* T30_ERR_TOO_LONG_SCAN_LINE         */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_PAGE_AFTER_MPS    */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_PAGE_AFTER_CFR    */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_FTT     */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_EOM     */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCS_AFTER_MPS     */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCN_AFTER_MCF     */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_DCN_AFTER_RTN     */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_CFR               */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_EOP     */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_EOM     */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_RETRY_NO_MCF_AFTER_MPS     */
+    0x331d,                /* T30_ERR_SUB_SEP_UNSUPPORTED        */
+    0x331e,                /* T30_ERR_PWD_UNSUPPORTED            */
+    0x331f,                /* T30_ERR_SUB_SEP_PWD_UNSUPPORTED    */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_INVALID_COMMAND_FRAME      */
+    _FAX_PARAMETER_ERROR,  /* T30_ERR_UNSUPPORTED_PAGE_CODING    */
+    _FAX_PARAMETER_ERROR,  /* T30_ERR_INVALID_PAGE_CODING        */
+    _FAX_REMOTE_REJECT,    /* T30_ERR_INCOMPATIBLE_PAGE_CONFIG   */
+    _FAX_LOCAL_ABORT,      /* T30_ERR_TIMEOUT_FROM_APPLICATION   */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_NO_REACTION_ON_MARK */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_TRAINING_TIMEOUT    */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_UNEXPECTED_V21      */
+    _FAX_PROTOCOL_ERROR,   /* T30_ERR_V34FAX_PRIMARY_CTS_ON      */
+    _FAX_LOCAL_ABORT,      /* T30_ERR_V34FAX_TURNAROUND_POLLING  */
+    _FAX_LOCAL_ABORT       /* T30_ERR_V34FAX_V8_INCOMPATIBILITY  */
+  };
+
+    byte dtmf_code_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE + 1];
+
+
+  static word rtp_info[] = {
+    GOOD,                  /* RTP_SUCCESS                       */
+    0x3600                 /* RTP_ERR_SSRC_OR_PAYLOAD_CHANGE    */
+  };
+
+  static dword udata_forwarding_table[0x100 / sizeof(dword)] =
+  {
+    0x0020301e, 0x00000000, 0x00000000, 0x00000000,
+    0x00000000, 0x00000000, 0x00000000, 0x00000000
+  };
+
+  ch = plci->NL.IndCh;
+  a = plci->adapter;
+  ncci = a->ch_ncci[ch];
+  Id = (((dword)(ncci ? ncci : ch)) << 16) | (((word) plci->Id) << 8) | a->Id;
+  if(plci->tel) Id|=EXT_CONTROLLER;
+  APPLptr = plci->appl;
+  dbug(1,dprintf("NL_IND-Id(NL:0x%x)=0x%08lx,plci=%x,tel=%x,state=0x%x,ch=0x%x,chs=%d,Ind=%x",
+    plci->NL.Id,Id,plci->Id,plci->tel,plci->State,ch,plci->channels,plci->NL.Ind &0x0f));
+
+  /* in the case if no connect_active_Ind was sent to the appl we wait for */
+
+  if (plci->nl_remove_id)
+  {
+    plci->NL.RNR = 2; /* discard */
+    dbug(1,dprintf("NL discard while remove pending"));
+    return;
+  }
+  if((plci->NL.Ind &0x0f)==N_CONNECT)
+  {
+    if(plci->State==INC_DIS_PENDING
+    || plci->State==OUTG_DIS_PENDING
+    || plci->State==IDLE)
+    {
+      plci->NL.RNR = 2; /* discard */
+      dbug(1,dprintf("discard n_connect"));
+      return;
+    }
+    if(plci->State < INC_ACT_PENDING)
+    {
+      plci->NL.RNR = 1; /* flow control */
+      channel_x_off (plci, ch, N_XON_CONNECT_IND);
+      return;
+    }
+  }
+
+  if(!APPLptr)                         /* no application or invalid data */
+  {                                    /* while reloading the DSP        */
+    dbug(1,dprintf("discard1"));
+    plci->NL.RNR = 2;
+    return;
+  }
+
+  if (((plci->NL.Ind &0x0f) == N_UDATA)
+     && (((plci->B2_prot != B2_SDLC) && ((plci->B1_resource == 17) || (plci->B1_resource == 18)))
+        || (plci->B2_prot == 7)
+        || (plci->B3_prot == 7)) )
+  {
+    plci->ncpi_buffer[0] = 0;
+
+    ncpi_state = plci->ncpi_state;
+    if (plci->NL.complete == 1)
+    {
+      byte  * data = &plci->NL.RBuffer->P[0];
+
+      if ((plci->NL.RBuffer->length >= 12)
+        &&( (*data == DSP_UDATA_INDICATION_DCD_ON)
+          ||(*data == DSP_UDATA_INDICATION_CTS_ON)) )
+      {
+        word conn_opt, ncpi_opt = 0x00;
+/*      HexDump ("MDM N_UDATA:", plci->NL.RBuffer->length, data); */
+
+        if (*data == DSP_UDATA_INDICATION_DCD_ON)
+          plci->ncpi_state |= NCPI_MDM_DCD_ON_RECEIVED;
+        if (*data == DSP_UDATA_INDICATION_CTS_ON)
+          plci->ncpi_state |= NCPI_MDM_CTS_ON_RECEIVED;
+
+        data++;    /* indication code */
+        data += 2; /* timestamp */
+        if ((*data == DSP_CONNECTED_NORM_V18) || (*data == DSP_CONNECTED_NORM_VOWN))
+          ncpi_state &= ~(NCPI_MDM_DCD_ON_RECEIVED | NCPI_MDM_CTS_ON_RECEIVED);
+        data++;    /* connected norm */
+        conn_opt = GET_WORD(data);
+        data += 2; /* connected options */
+
+        PUT_WORD (&(plci->ncpi_buffer[1]), (word)(GET_DWORD(data) & 0x0000FFFF));
+
+        if (conn_opt & DSP_CONNECTED_OPTION_MASK_V42)
+        {
+          ncpi_opt |= MDM_NCPI_ECM_V42;
+        }
+        else if (conn_opt & DSP_CONNECTED_OPTION_MASK_MNP)
+        {
+          ncpi_opt |= MDM_NCPI_ECM_MNP;
+        }
+        else
+        {
+          ncpi_opt |= MDM_NCPI_TRANSPARENT;
+        }
+        if (conn_opt & DSP_CONNECTED_OPTION_MASK_COMPRESSION)
+        {
+          ncpi_opt |= MDM_NCPI_COMPRESSED;
+        }
+        PUT_WORD (&(plci->ncpi_buffer[3]), ncpi_opt);
+        plci->ncpi_buffer[0] = 4;
+
+        plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND | NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
+      }
+    }
+    if (plci->B3_prot == 7)
+    {
+      if (((a->ncci_state[ncci] == INC_ACT_PENDING) || (a->ncci_state[ncci] == OUTG_CON_PENDING))
+       && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+       && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+      {
+        a->ncci_state[ncci] = INC_ACT_PENDING;
+        sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+        plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+      }
+    }
+
+    if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+        & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
+     || !(ncpi_state & NCPI_MDM_DCD_ON_RECEIVED)
+     || !(ncpi_state & NCPI_MDM_CTS_ON_RECEIVED))
+
+    {
+      plci->NL.RNR = 2;
+      return;
+    }
+  }
+
+  if(plci->NL.complete == 2)
+    {
+    if (((plci->NL.Ind &0x0f) == N_UDATA)
+     && !(udata_forwarding_table[plci->RData[0].P[0] >> 5] & (1L << (plci->RData[0].P[0] & 0x1f))))
+    {
+      switch(plci->RData[0].P[0])
+      {
+
+      case DTMF_UDATA_INDICATION_FAX_CALLING_TONE:
+        if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
+          sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0,"ws", SELECTOR_DTMF, "\x01X");
+        break;
+      case DTMF_UDATA_INDICATION_ANSWER_TONE:
+        if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
+          sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0,"ws", SELECTOR_DTMF, "\x01Y");
+        break;
+      case DTMF_UDATA_INDICATION_DIGITS_RECEIVED:
+        dtmf_indication (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+        break;
+      case DTMF_UDATA_INDICATION_DIGITS_SENT:
+        dtmf_confirmation (Id, plci);
+        break;
+
+
+      case UDATA_INDICATION_MIXER_TAP_DATA:
+        capidtmf_recv_process_block (&(plci->capidtmf_state), plci->RData[0].P + 1, (word)(plci->RData[0].PLength - 1));
+ i = capidtmf_indication (&(plci->capidtmf_state), dtmf_code_buffer + 1);
+ if (i != 0)
+ {
+   dtmf_code_buffer[0] = DTMF_UDATA_INDICATION_DIGITS_RECEIVED;
+          dtmf_indication (Id, plci, dtmf_code_buffer, (word)(i + 1));
+ }
+        break;
+
+
+      case UDATA_INDICATION_MIXER_COEFS_SET:
+        mixer_indication_coefs_set (Id, plci);
+        break;
+      case UDATA_INDICATION_XCONNECT_FROM:
+        mixer_indication_xconnect_from (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+        break;
+      case UDATA_INDICATION_XCONNECT_TO:
+        mixer_indication_xconnect_to (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+        break;
+
+
+      case LEC_UDATA_INDICATION_DISABLE_DETECT:
+        ec_indication (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+        break;
+
+
+
+      default:
+        break;
+      }
+    }
+    else
+  {
+      if ((plci->RData[0].PLength != 0)
+     && ((plci->B2_prot == B2_V120_ASYNC)
+      || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
+      || (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
+    {
+
+      sendf(plci->appl,_DATA_B3_I,Id,0,
+            "dwww",
+            plci->RData[1].P,
+              (plci->NL.RNum < 2) ? 0 : plci->RData[1].PLength,
+            plci->RNum,
+            plci->RFlags);
+
+    }
+    else
+    {
+
+      sendf(plci->appl,_DATA_B3_I,Id,0,
+            "dwww",
+            plci->RData[0].P,
+            plci->RData[0].PLength,
+            plci->RNum,
+            plci->RFlags);
+
+    }
+    }
+    return;
+  }
+
+  fax_feature_bits = 0;
+  if((plci->NL.Ind &0x0f)==N_CONNECT ||
+     (plci->NL.Ind &0x0f)==N_CONNECT_ACK ||
+     (plci->NL.Ind &0x0f)==N_DISC ||
+     (plci->NL.Ind &0x0f)==N_EDATA ||
+     (plci->NL.Ind &0x0f)==N_DISC_ACK)
+  {
+    info = 0;
+    plci->ncpi_buffer[0] = 0;
+    switch (plci->B3_prot) {
+    case  0: /*XPARENT*/
+    case  1: /*T.90 NL*/
+      break;    /* no network control protocol info - jfr */
+    case  2: /*ISO8202*/
+    case  3: /*X25 DCE*/
+      for(i=0; i<plci->NL.RLength; i++) plci->ncpi_buffer[4+i] = plci->NL.RBuffer->P[i];
+      plci->ncpi_buffer[0] = (byte)(i+3);
+      plci->ncpi_buffer[1] = (byte)(plci->NL.Ind &N_D_BIT? 1:0);
+      plci->ncpi_buffer[2] = 0;
+      plci->ncpi_buffer[3] = 0;
+      break;
+    case  4: /*T.30 - FAX*/
+    case  5: /*T.30 - FAX*/
+      if(plci->NL.RLength>=sizeof(T30_INFO))
+      {
+        dbug(1,dprintf("FaxStatus %04x", ((T30_INFO   *)plci->NL.RBuffer->P)->code));
+        len = 9;
+        PUT_WORD(&(plci->ncpi_buffer[1]),((T30_INFO   *)plci->NL.RBuffer->P)->rate_div_2400 * 2400);
+        fax_feature_bits = GET_WORD(&((T30_INFO   *)plci->NL.RBuffer->P)->feature_bits_low);
+        i = (((T30_INFO   *)plci->NL.RBuffer->P)->resolution & T30_RESOLUTION_R8_0770_OR_200) ? 0x0001 : 0x0000;
+        if (plci->B3_prot == 5)
+        {
+          if (!(fax_feature_bits & T30_FEATURE_BIT_ECM))
+            i |= 0x8000; /* This is not an ECM connection */
+          if (fax_feature_bits & T30_FEATURE_BIT_T6_CODING)
+            i |= 0x4000; /* This is a connection with MMR compression */
+          if (fax_feature_bits & T30_FEATURE_BIT_2D_CODING)
+            i |= 0x2000; /* This is a connection with MR compression */
+          if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
+            i |= 0x0004; /* More documents */
+          if (fax_feature_bits & T30_FEATURE_BIT_POLLING)
+            i |= 0x0002; /* Fax-polling indication */
+        }
+        dbug(1,dprintf("FAX Options %04x %04x",fax_feature_bits,i));
+        PUT_WORD(&(plci->ncpi_buffer[3]),i);
+        PUT_WORD(&(plci->ncpi_buffer[5]),((T30_INFO   *)plci->NL.RBuffer->P)->data_format);
+        plci->ncpi_buffer[7] = ((T30_INFO   *)plci->NL.RBuffer->P)->pages_low;
+        plci->ncpi_buffer[8] = ((T30_INFO   *)plci->NL.RBuffer->P)->pages_high;
+        plci->ncpi_buffer[len] = 0;
+        if(((T30_INFO   *)plci->NL.RBuffer->P)->station_id_len)
+        {
+          plci->ncpi_buffer[len] = 20;
+          for (i = 0; i < 20; i++)
+            plci->ncpi_buffer[++len] = ((T30_INFO   *)plci->NL.RBuffer->P)->station_id[i];
+        }
+        if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
+        {
+          if (((T30_INFO   *)plci->NL.RBuffer->P)->code < sizeof(fax_info) / sizeof(fax_info[0]))
+            info = fax_info[((T30_INFO   *)plci->NL.RBuffer->P)->code];
+          else
+            info = _FAX_PROTOCOL_ERROR;
+        }
+
+        if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1])
+          & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+        {
+          i = ((word)(((T30_INFO *) 0)->station_id + 20)) + ((T30_INFO   *)plci->NL.RBuffer->P)->head_line_len;
+          while (i < plci->NL.RBuffer->length)
+            plci->ncpi_buffer[++len] = plci->NL.RBuffer->P[i++];
+        }
+
+        plci->ncpi_buffer[0] = len;
+        fax_feature_bits = GET_WORD(&((T30_INFO   *)plci->NL.RBuffer->P)->feature_bits_low);
+        PUT_WORD(&((T30_INFO   *)plci->fax_connect_info_buffer)->feature_bits_low, fax_feature_bits);
+
+        plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND;
+ if (((plci->NL.Ind &0x0f) == N_CONNECT_ACK)
+         || (((plci->NL.Ind &0x0f) == N_CONNECT)
+          && (fax_feature_bits & T30_FEATURE_BIT_POLLING))
+         || (((plci->NL.Ind &0x0f) == N_EDATA)
+          && ((((T30_INFO   *)plci->NL.RBuffer->P)->code == EDATA_T30_TRAIN_OK)
+           || (((T30_INFO   *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
+           || (((T30_INFO   *)plci->NL.RBuffer->P)->code == EDATA_T30_DTC))))
+ {
+          plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT;
+ }
+ if (((plci->NL.Ind &0x0f) == N_DISC)
+  || ((plci->NL.Ind &0x0f) == N_DISC_ACK)
+  || (((plci->NL.Ind &0x0f) == N_EDATA)
+   && (((T30_INFO   *)plci->NL.RBuffer->P)->code == EDATA_T30_EOP_CAPI)))
+ {
+          plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
+ }
+      }
+      break;
+
+    case B3_RTP:
+      if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
+      {
+        if (plci->NL.RLength != 0)
+        {
+          info = rtp_info[plci->NL.RBuffer->P[0]];
+          plci->ncpi_buffer[0] = plci->NL.RLength - 1;
+          for (i = 1; i < plci->NL.RLength; i++)
+            plci->ncpi_buffer[i] = plci->NL.RBuffer->P[i];
+        }
+      }
+      break;
+
+    }
+    plci->NL.RNR = 2;
+  }
+  switch(plci->NL.Ind &0x0f) {
+  case N_EDATA:
+    if ((plci->B3_prot == 4) || (plci->B3_prot == 5))
+    {
+      dbug(1,dprintf("EDATA ncci=0x%x state=%d code=%02x", ncci, a->ncci_state[ncci],
+        ((T30_INFO   *)plci->NL.RBuffer->P)->code));
+      fax_send_edata_ack = (((T30_INFO   *)(plci->fax_connect_info_buffer))->operating_mode == T30_OPERATING_MODE_CAPI_NEG);
+
+      if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
+       && (plci->nsf_control_bits & (T30_NSF_CONTROL_BIT_NEGOTIATE_IND | T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
+       && (((T30_INFO   *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
+       && (a->ncci_state[ncci] == OUTG_CON_PENDING)
+       && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+       && !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
+      {
+        ((T30_INFO   *)(plci->fax_connect_info_buffer))->code = ((T30_INFO   *)plci->NL.RBuffer->P)->code;
+        sendf(plci->appl,_MANUFACTURER_I,Id,0,"dwbS",_DI_MANU_ID,_DI_NEGOTIATE_B3,
+          (byte)(plci->ncpi_buffer[0] + 1), plci->ncpi_buffer);
+        plci->ncpi_state |= NCPI_NEGOTIATE_B3_SENT;
+ if (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)
+   fax_send_edata_ack = FALSE;
+      }
+
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+      {
+        switch (((T30_INFO   *)plci->NL.RBuffer->P)->code)
+        {
+        case EDATA_T30_DIS:
+          if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
+           && !(GET_WORD(&((T30_INFO   *)plci->fax_connect_info_buffer)->control_bits_low) & T30_CONTROL_BIT_REQUEST_POLLING)
+           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+          {
+            a->ncci_state[ncci] = INC_ACT_PENDING;
+            if (plci->B3_prot == 4)
+              sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+            else
+              sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+            plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+          }
+          break;
+
+        case EDATA_T30_TRAIN_OK:
+          if ((a->ncci_state[ncci] == INC_ACT_PENDING)
+           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+          {
+            if (plci->B3_prot == 4)
+              sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+            else
+              sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+            plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+          }
+          break;
+
+        case EDATA_T30_EOP_CAPI:
+          if (a->ncci_state[ncci] == CONNECTED)
+          {
+            sendf(plci->appl,_DISCONNECT_B3_I,Id,0,"wS",GOOD,plci->ncpi_buffer);
+            a->ncci_state[ncci] = INC_DIS_PENDING;
+            plci->ncpi_state = 0;
+     fax_send_edata_ack = FALSE;
+          }
+          break;
+        }
+      }
+      else
+      {
+        switch (((T30_INFO   *)plci->NL.RBuffer->P)->code)
+        {
+        case EDATA_T30_TRAIN_OK:
+          if ((a->ncci_state[ncci] == INC_ACT_PENDING)
+           && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+           && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+          {
+            if (plci->B3_prot == 4)
+              sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+            else
+              sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+            plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+          }
+          break;
+        }
+      }
+      if (fax_send_edata_ack)
+      {
+        ((T30_INFO   *)(plci->fax_connect_info_buffer))->code = ((T30_INFO   *)plci->NL.RBuffer->P)->code;
+ plci->fax_edata_ack_length = 1;
+        start_internal_command (Id, plci, fax_edata_ack_command);
+      }
+    }
+    else
+    {
+      dbug(1,dprintf("EDATA ncci=0x%x state=%d", ncci, a->ncci_state[ncci]));
+    }
+    break;
+  case N_CONNECT:
+    if (!a->ch_ncci[ch])
+    {
+      ncci = get_ncci (plci, ch, 0);
+      Id = (Id & 0xffff) | (((dword) ncci) << 16);
+    }
+    dbug(1,dprintf("N_CONNECT: ch=%d state=%d plci=%lx plci_Id=%lx plci_State=%d",
+      ch, a->ncci_state[ncci], a->ncci_plci[ncci], plci->Id, plci->State));
+
+    msg = _CONNECT_B3_I;
+    if (a->ncci_state[ncci] == IDLE)
+      plci->channels++;
+    else if (plci->B3_prot == 1)
+      msg = _CONNECT_B3_T90_ACTIVE_I;
+
+    a->ncci_state[ncci] = INC_CON_PENDING;
+    if(plci->B3_prot == 4)
+      sendf(plci->appl,msg,Id,0,"s","");
+    else
+      sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+    break;
+  case N_CONNECT_ACK:
+    dbug(1,dprintf("N_connect_Ack"));
+    if (plci->internal_command_queue[0]
+     && ((plci->adjust_b_state == ADJUST_B_CONNECT_2)
+      || (plci->adjust_b_state == ADJUST_B_CONNECT_3)
+      || (plci->adjust_b_state == ADJUST_B_CONNECT_4)))
+    {
+      (*(plci->internal_command_queue[0]))(Id, plci, 0);
+      if (!plci->internal_command)
+        next_internal_command (Id, plci);
+      break;
+    }
+    msg = _CONNECT_B3_ACTIVE_I;
+    if (plci->B3_prot == 1)
+    {
+      if (a->ncci_state[ncci] != OUTG_CON_PENDING)
+        msg = _CONNECT_B3_T90_ACTIVE_I;
+      a->ncci_state[ncci] = INC_ACT_PENDING;
+      sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+    }
+    else if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
+    {
+      if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
+       && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+       && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+      {
+        a->ncci_state[ncci] = INC_ACT_PENDING;
+        if (plci->B3_prot == 4)
+          sendf(plci->appl,msg,Id,0,"s","");
+        else
+          sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+        plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+      }
+    }
+    else
+    {
+      a->ncci_state[ncci] = INC_ACT_PENDING;
+      sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+    }
+    if (plci->adjust_b_restore)
+    {
+      plci->adjust_b_restore = FALSE;
+      start_internal_command (Id, plci, adjust_b_restore);
+    }
+    break;
+  case N_DISC:
+  case N_DISC_ACK:
+    if (plci->internal_command_queue[0]
+     && ((plci->internal_command == FAX_DISCONNECT_COMMAND_1)
+      || (plci->internal_command == FAX_DISCONNECT_COMMAND_2)
+      || (plci->internal_command == FAX_DISCONNECT_COMMAND_3)))
+    {
+      (*(plci->internal_command_queue[0]))(Id, plci, 0);
+      if (!plci->internal_command)
+        next_internal_command (Id, plci);
+    }
+    ncci_state = a->ncci_state[ncci];
+    ncci_remove (plci, ncci, FALSE);
+
+        /* with N_DISC or N_DISC_ACK the IDI frees the respective   */
+        /* channel, so we cannot store the state in ncci_state! The */
+        /* information which channel we received a N_DISC is thus   */
+        /* stored in the inc_dis_ncci_table buffer.                 */
+    for(i=0; plci->inc_dis_ncci_table[i]; i++);
+    plci->inc_dis_ncci_table[i] = (byte) ncci;
+
+      /* need a connect_b3_ind before a disconnect_b3_ind with FAX */
+    if (!plci->channels
+     && (plci->B1_resource == 16)
+     && (plci->State <= CONNECTED))
+    {
+      len = 9;
+      i = ((T30_INFO   *)plci->fax_connect_info_buffer)->rate_div_2400 * 2400;
+      PUT_WORD (&plci->ncpi_buffer[1], i);
+      PUT_WORD (&plci->ncpi_buffer[3], 0);
+      i = ((T30_INFO   *)plci->fax_connect_info_buffer)->data_format;
+      PUT_WORD (&plci->ncpi_buffer[5], i);
+      PUT_WORD (&plci->ncpi_buffer[7], 0);
+      plci->ncpi_buffer[len] = 0;
+      plci->ncpi_buffer[0] = len;
+      if(plci->B3_prot == 4)
+        sendf(plci->appl,_CONNECT_B3_I,Id,0,"s","");
+      else
+      {
+
+        if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1])
+          & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+        {
+          plci->ncpi_buffer[++len] = 0;
+          plci->ncpi_buffer[++len] = 0;
+          plci->ncpi_buffer[++len] = 0;
+          plci->ncpi_buffer[0] = len;
+        }
+
+        sendf(plci->appl,_CONNECT_B3_I,Id,0,"S",plci->ncpi_buffer);
+      }
+      sendf(plci->appl,_DISCONNECT_B3_I,Id,0,"wS",info,plci->ncpi_buffer);
+      plci->ncpi_state = 0;
+      sig_req(plci,HANGUP,0);
+      send_req(plci);
+      plci->State = OUTG_DIS_PENDING;
+      /* disc here */
+    }
+    else if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+     && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
+     && ((ncci_state == INC_DIS_PENDING) || (ncci_state == IDLE)))
+    {
+      if (ncci_state == IDLE)
+      {
+        if (plci->channels)
+          plci->channels--;
+        if((plci->State==IDLE || plci->State==SUSPENDING) && !plci->channels){
+          if(plci->State == SUSPENDING){
+            sendf(plci->appl,
+                  _FACILITY_I,
+                  Id & 0xffffL,
+                  0,
+                  "ws", (word)3, "\x03\x04\x00\x00");
+            sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
+          }
+          plci_remove(plci);
+          plci->State=IDLE;
+        }
+      }
+    }
+    else if (plci->channels)
+    {
+      sendf(plci->appl,_DISCONNECT_B3_I,Id,0,"wS",info,plci->ncpi_buffer);
+      plci->ncpi_state = 0;
+      if ((ncci_state == OUTG_REJ_PENDING)
+       && ((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE)))
+      {
+        sig_req(plci,HANGUP,0);
+        send_req(plci);
+        plci->State = OUTG_DIS_PENDING;
+      }
+    }
+    break;
+  case N_RESET:
+    a->ncci_state[ncci] = INC_RES_PENDING;
+    sendf(plci->appl,_RESET_B3_I,Id,0,"S",plci->ncpi_buffer);
+    break;
+  case N_RESET_ACK:
+    a->ncci_state[ncci] = CONNECTED;
+    sendf(plci->appl,_RESET_B3_I,Id,0,"S",plci->ncpi_buffer);
+    break;
+
+  case N_UDATA:
+    if (!(udata_forwarding_table[plci->NL.RBuffer->P[0] >> 5] & (1L << (plci->NL.RBuffer->P[0] & 0x1f))))
+    {
+      plci->RData[0].P = plci->internal_ind_buffer + (-((int)(plci->internal_ind_buffer)) & 3);
+      plci->RData[0].PLength = INTERNAL_IND_BUFFER_SIZE;
+      plci->NL.R = plci->RData;
+      plci->NL.RNum = 1;
+      return;
+    }
+  case N_BDATA:
+  case N_DATA:
+    if (((a->ncci_state[ncci] != CONNECTED) && (plci->B2_prot == 1)) /* transparent */
+     || (a->ncci_state[ncci] == IDLE)
+     || (a->ncci_state[ncci] == INC_DIS_PENDING))
+    {
+      plci->NL.RNR = 2;
+      break;
+    }
+    if ((a->ncci_state[ncci] != CONNECTED)
+     && (a->ncci_state[ncci] != OUTG_DIS_PENDING)
+     && (a->ncci_state[ncci] != OUTG_REJ_PENDING))
+    {
+      dbug(1,dprintf("flow control"));
+      plci->NL.RNR = 1; /* flow control  */
+      channel_x_off (plci, ch, 0);
+      break;
+    }
+
+    NCCIcode = ncci | (((word)a->Id) << 8);
+
+                /* count all buffers within the Application pool    */
+                /* belonging to the same NCCI. If this is below the */
+                /* number of buffers available per NCCI we accept   */
+                /* this packet, otherwise we reject it              */
+    count = 0;
+    Num = 0xffff;
+    for(i=0; i<APPLptr->MaxBuffer; i++) {
+      if(NCCIcode==APPLptr->DataNCCI[i]) count++;
+      if(!APPLptr->DataNCCI[i] && Num==0xffff) Num = i;
+    }
+
+    if(count>=APPLptr->MaxNCCIData || Num==0xffff)
+    {
+      dbug(3,dprintf("Flow-Control"));
+      plci->NL.RNR = 1;
+      if( ++(APPLptr->NCCIDataFlowCtrlTimer)>=
+       (word)((a->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL) ? 40 : 2000))
+      {
+        plci->NL.RNR = 2;
+        dbug(3,dprintf("DiscardData"));
+      } else {
+        channel_x_off (plci, ch, 0);
+      }
+      break;
+    }
+    else
+    {
+      APPLptr->NCCIDataFlowCtrlTimer = 0;
+    }
+
+    plci->RData[0].P = ReceiveBufferGet(APPLptr,Num);
+    if(!plci->RData[0].P) {
+      plci->NL.RNR = 1;
+      channel_x_off (plci, ch, 0);
+      break;
+    }
+
+    APPLptr->DataNCCI[Num] = NCCIcode;
+    APPLptr->DataFlags[Num] = (plci->Id<<8) | (plci->NL.Ind>>4);
+    dbug(3,dprintf("Buffer(%d), Max = %d",Num,APPLptr->MaxBuffer));
+
+    plci->RNum = Num;
+    plci->RFlags = plci->NL.Ind>>4;
+    plci->RData[0].PLength = APPLptr->MaxDataLength;
+    plci->NL.R = plci->RData;
+    if ((plci->NL.RLength != 0)
+     && ((plci->B2_prot == B2_V120_ASYNC)
+      || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
+      || (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
+    {
+      plci->RData[1].P = plci->RData[0].P;
+      plci->RData[1].PLength = plci->RData[0].PLength;
+      plci->RData[0].P = v120_header_buffer + (-((int) v120_header_buffer) & 3);
+      if ((plci->NL.RBuffer->P[0] & V120_HEADER_EXTEND_BIT) || (plci->NL.RLength == 1))
+        plci->RData[0].PLength = 1;
+      else
+        plci->RData[0].PLength = 2;
+      if (plci->NL.RBuffer->P[0] & V120_HEADER_BREAK_BIT)
+        plci->RFlags |= 0x0010;
+      if (plci->NL.RBuffer->P[0] & (V120_HEADER_C1_BIT | V120_HEADER_C2_BIT))
+        plci->RFlags |= 0x8000;
+      plci->NL.RNum = 2;
+    }
+    else
+    {
+      if((plci->NL.Ind &0x0f)==N_UDATA)
+        plci->RFlags |= 0x0010;
+
+      else if ((plci->B3_prot == B3_RTP) && ((plci->NL.Ind & 0x0f) == N_BDATA))
+        plci->RFlags |= 0x0001;
+
+      plci->NL.RNum = 1;
+    }
+    break;
+  case N_DATA_ACK:
+    data_ack (plci, ch);
+    break;
+  default:
+    plci->NL.RNR = 2;
+    break;
+  }
+}
+
+/*------------------------------------------------------------------*/
+/* find a free PLCI                                                 */
+/*------------------------------------------------------------------*/
+
+word get_plci(DIVA_CAPI_ADAPTER   * a)
+{
+  word i,j;
+  PLCI   * plci;
+
+  dump_plcis (a);
+  for(i=0;i<a->max_plci && a->plci[i].Id;i++);
+  if(i==a->max_plci) {
+    dbug(1,dprintf("get_plci: out of PLCIs"));
+    return 0;
+  }
+  plci = &a->plci[i];
+  plci->Id = (byte)(i+1);
+
+  plci->Sig.Id = 0;
+  plci->NL.Id = 0;
+  plci->sig_req = 0;
+  plci->nl_req = 0;
+
+  plci->appl = NULL;
+  plci->relatedPTYPLCI = NULL;
+  plci->State = IDLE;
+  plci->SuppState = IDLE;
+  plci->channels = 0;
+  plci->tel = 0;
+  plci->B1_resource = 0;
+  plci->B2_prot = 0;
+  plci->B3_prot = 0;
+
+  plci->command = 0;
+  plci->m_command = 0;
+  init_internal_command_queue (plci);
+  plci->number = 0;
+  plci->req_in_start = 0;
+  plci->req_in = 0;
+  plci->req_out = 0;
+  plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
+  plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+  plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+
+  plci->data_sent = FALSE;
+  plci->send_disc = 0;
+  plci->sig_global_req = 0;
+  plci->sig_remove_id = 0;
+  plci->nl_global_req = 0;
+  plci->nl_remove_id = 0;
+  plci->adv_nl = 0;
+  plci->manufacturer = FALSE;
+  plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+  plci->spoofed_msg = 0;
+  plci->ptyState = 0;
+  plci->cr_enquiry = FALSE;
+  plci->hangup_flow_ctrl_timer = 0;
+
+  plci->ncci_ring_list = 0;
+  for(j=0;j<MAX_CHANNELS_PER_PLCI;j++) plci->inc_dis_ncci_table[j] = 0;
+  clear_c_ind_mask (plci);
+  set_group_ind_mask (plci);
+  plci->fax_connect_info_length = 0;
+  plci->nsf_control_bits = 0;
+  plci->ncpi_state = 0x00;
+  plci->ncpi_buffer[0] = 0;
+
+  plci->requested_options_conn = 0;
+  plci->requested_options = 0;
+  plci->notifiedcall = 0;
+  plci->vswitchstate = 0;
+  plci->vsprot = 0;
+  plci->vsprotdialect = 0;
+  init_b1_config (plci);
+  dbug(1,dprintf("get_plci(%x)",plci->Id));
+  return i+1;
+}
+
+/*------------------------------------------------------------------*/
+/* put a parameter in the parameter buffer                          */
+/*------------------------------------------------------------------*/
+
+static void add_p(PLCI   * plci, byte code, byte   * p)
+{
+  word p_length;
+
+  p_length = 0;
+  if(p) p_length = p[0];
+  add_ie(plci, code, p, p_length);
+}
+
+/*------------------------------------------------------------------*/
+/* put a structure in the parameter buffer                          */
+/*------------------------------------------------------------------*/
+static void add_s(PLCI   * plci, byte code, API_PARSE * p)
+{
+  if(p) add_ie(plci, code, p->info, (word)p->length);
+}
+
+/*------------------------------------------------------------------*/
+/* put multiple structures in the parameter buffer                  */
+/*------------------------------------------------------------------*/
+static void add_ss(PLCI   * plci, byte code, API_PARSE * p)
+{
+  byte i;
+
+  if(p){
+    dbug(1,dprintf("add_ss(%x,len=%d)",code,p->length));
+    for(i=2;i<(byte)p->length;i+=p->info[i]+2){
+      dbug(1,dprintf("add_ss_ie(%x,len=%d)",p->info[i-1],p->info[i]));
+      add_ie(plci, p->info[i-1], (byte   *)&(p->info[i]), (word)p->info[i]);
+    }
+  }
+}
+
+/*------------------------------------------------------------------*/
+/* return the channel number sent by the application in a esc_chi   */
+/*------------------------------------------------------------------*/
+static byte getChannel(API_PARSE * p)
+{
+  byte i;
+
+  if(p){
+    for(i=2;i<(byte)p->length;i+=p->info[i]+2){
+      if(p->info[i]==2){
+        if(p->info[i-1]==ESC && p->info[i+1]==CHI) return (p->info[i+2]);
+      }
+    }
+  }
+  return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* put an information element in the parameter buffer               */
+/*------------------------------------------------------------------*/
+
+static void add_ie(PLCI   * plci, byte code, byte   * p, word p_length)
+{
+  word i;
+
+  if(!(code &0x80) && !p_length) return;
+
+  if(plci->req_in==plci->req_in_start) {
+    plci->req_in +=2;
+  }
+  else {
+    plci->req_in--;
+  }
+  plci->RBuffer[plci->req_in++] = code;
+
+  if(p) {
+    plci->RBuffer[plci->req_in++] = (byte)p_length;
+    for(i=0;i<p_length;i++) plci->RBuffer[plci->req_in++] = p[1+i];
+  }
+
+  plci->RBuffer[plci->req_in++] = 0;
+}
+
+/*------------------------------------------------------------------*/
+/* put a unstructured data into the buffer                          */
+/*------------------------------------------------------------------*/
+
+void add_d(PLCI   * plci, word length, byte   * p)
+{
+  word i;
+
+  if(plci->req_in==plci->req_in_start) {
+    plci->req_in +=2;
+  }
+  else {
+    plci->req_in--;
+  }
+  for(i=0;i<length;i++) plci->RBuffer[plci->req_in++] = p[i];
+}
+
+/*------------------------------------------------------------------*/
+/* put parameters from the Additional Info parameter in the         */
+/* parameter buffer                                                 */
+/*------------------------------------------------------------------*/
+
+void add_ai(PLCI   * plci, API_PARSE * ai)
+{
+  word i;
+    API_PARSE ai_parms[5];
+
+  for(i=0;i<5;i++) ai_parms[i].length = 0;
+
+  if(!ai->length)
+    return;
+  if(api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
+    return;
+
+  add_s (plci,KEY,&ai_parms[1]);
+  add_s (plci,UUI,&ai_parms[2]);
+  add_ss(plci,FTY,&ai_parms[3]);
+}
+
+/*------------------------------------------------------------------*/
+/* put parameter for b1 protocol in the parameter buffer            */
+/*------------------------------------------------------------------*/
+
+word add_b1(PLCI   * plci, API_PARSE * bp, word b_channel_info, word b1_facilities)
+{
+    API_PARSE bp_parms[8];
+    API_PARSE mdm_cfg[9];
+    API_PARSE global_config[2];
+    byte cai[256];
+  byte resource[] = {5,9,13,12,16,39,9,17,17,18};
+  byte voice_cai[] = "\x06\x14\x00\x00\x00\x00\x08";
+  word i;
+
+    API_PARSE mdm_cfg_v18[4];
+  word j, n, w;
+  dword d;
+
+
+  for(i=0;i<8;i++) bp_parms[i].length = 0;
+  for(i=0;i<2;i++) global_config[i].length = 0;
+
+  dbug(1,dprintf("add_b1"));
+  api_save_msg(bp, "s", &plci->B_protocol);
+
+  if(b_channel_info==2){
+    plci->B1_resource = 0;
+    adjust_b1_facilities (plci, plci->B1_resource, b1_facilities);
+    add_p(plci, CAI, "\x01\x00");
+    dbug(1,dprintf("Cai=1,0 (no resource)"));
+    return 0;
+  }
+
+  if(plci->tel == CODEC_PERMANENT) return 0;
+  else if(plci->tel == CODEC){
+    plci->B1_resource = 1;
+    adjust_b1_facilities (plci, plci->B1_resource, b1_facilities);
+    add_p(plci, CAI, "\x01\x01");
+    dbug(1,dprintf("Cai=1,1 (Codec)"));
+    return 0;
+  }
+  else if(plci->tel == ADV_VOICE){
+    plci->B1_resource = add_b1_facilities (plci, 9, (word)(b1_facilities | B1_FACILITY_VOICE));
+    adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities | B1_FACILITY_VOICE));
+    voice_cai[1] = plci->B1_resource;
+    PUT_WORD (&voice_cai[5], plci->appl->MaxDataLength);
+    add_p(plci, CAI, voice_cai);
+    dbug(1,dprintf("Cai=1,0x%x (AdvVoice)",voice_cai[1]));
+    return 0;
+  }
+  plci->call_dir &= ~(CALL_DIR_ORIGINATE | CALL_DIR_ANSWER);
+  if (plci->call_dir & CALL_DIR_OUT)
+    plci->call_dir |= CALL_DIR_ORIGINATE;
+  else if (plci->call_dir & CALL_DIR_IN)
+    plci->call_dir |= CALL_DIR_ANSWER;
+
+  if(!bp->length){
+    plci->B1_resource = 0x5;
+    adjust_b1_facilities (plci, plci->B1_resource, b1_facilities);
+    add_p(plci, CAI, "\x01\x05");
+    return 0;
+  }
+
+  dbug(1,dprintf("b_prot_len=%d",(word)bp->length));
+  if(bp->length>256) return _WRONG_MESSAGE_FORMAT;
+  if(api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
+  {
+    bp_parms[6].length = 0;
+    if(api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
+    {
+      dbug(1,dprintf("b-form.!"));
+      return _WRONG_MESSAGE_FORMAT;
+    }
+  }
+  else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
+  {
+    dbug(1,dprintf("b-form.!"));
+    return _WRONG_MESSAGE_FORMAT;
+  }
+
+  if(bp_parms[6].length)
+  {
+    if(api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
+    {
+      return _WRONG_MESSAGE_FORMAT;
+    }
+    switch(GET_WORD(global_config[0].info))
+    {
+    case 1:
+      plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
+      break;
+    case 2:
+      plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
+      break;
+    }
+  }
+  dbug(1,dprintf("call_dir=%04x", plci->call_dir));
+
+
+  if ((GET_WORD(bp_parms[0].info) == B1_RTP)
+   && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
+  {
+    plci->B1_resource = add_b1_facilities (plci, 31, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+    adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+    cai[1] = plci->B1_resource;
+    cai[2] = 0;
+    cai[3] = 0;
+    cai[4] = 0;
+    PUT_WORD(&cai[5],plci->appl->MaxDataLength);
+    for (i = 0; i < bp_parms[3].length; i++)
+      cai[7+i] = bp_parms[3].info[1+i];
+    cai[0] = 6 + bp_parms[3].length;
+    add_p(plci, CAI, cai);
+    return 0;
+  }
+
+
+  if ((GET_WORD(bp_parms[0].info) == B1_PIAFS)
+   && (plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))
+  {
+    plci->B1_resource = add_b1_facilities (plci, 35/* PIAFS HARDWARE FACILITY */, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+    adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+    cai[1] = plci->B1_resource;
+    cai[2] = 0;
+    cai[3] = 0;
+    cai[4] = 0;
+    PUT_WORD(&cai[5],plci->appl->MaxDataLength);
+    cai[0] = 6;
+    add_p(plci, CAI, cai);
+    return 0;
+  }
+
+
+  if ((GET_WORD(bp_parms[0].info) >= 32)
+   || (!((1L << GET_WORD(bp_parms[0].info)) & plci->adapter->profile.B1_Protocols)
+    && ((GET_WORD(bp_parms[0].info) != 3)
+     || !((1L << B1_HDLC) & plci->adapter->profile.B1_Protocols)
+     || ((bp_parms[3].length != 0) && (GET_WORD(&bp_parms[3].info[1]) != 0) && (GET_WORD(&bp_parms[3].info[1]) != 56000)))))
+  {
+    return _B1_NOT_SUPPORTED;
+  }
+  plci->B1_resource = add_b1_facilities (plci, resource[GET_WORD(bp_parms[0].info)],
+    (word)(b1_facilities & ~B1_FACILITY_VOICE));
+  adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+  cai[0] = 6;
+  cai[1] = plci->B1_resource;
+  for (i=2;i<sizeof(cai);i++) cai[i] = 0;
+
+  if ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
+   || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
+   || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC))
+  { /* B1 - modem */
+    for (i=0;i<7;i++) mdm_cfg[i].length = 0;
+
+    if (bp_parms[3].length)
+    {
+      if(api_parse(&bp_parms[3].info[1],(word)bp_parms[3].length,"wwwwww", mdm_cfg))
+      {
+        return (_WRONG_MESSAGE_FORMAT);
+      }
+        
+      cai[2] = 0; /* Bit rate for adaptation */
+
+      dbug(1,dprintf("MDM Max Bit Rate:<%d>", GET_WORD(mdm_cfg[0].info)));
+
+      PUT_WORD (&cai[13], 0);                          /* Min Tx speed */
+      PUT_WORD (&cai[15], GET_WORD(mdm_cfg[0].info)); /* Max Tx speed */
+      PUT_WORD (&cai[17], 0);                          /* Min Rx speed */
+      PUT_WORD (&cai[19], GET_WORD(mdm_cfg[0].info)); /* Max Rx speed */
+
+      cai[3] = 0; /* Async framing parameters */
+      switch (GET_WORD (mdm_cfg[2].info))
+      {       /* Parity     */
+      case 1: /* odd parity */
+        cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
+        dbug(1,dprintf("MDM: odd parity"));
+        break;
+
+      case 2: /* even parity */
+        cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
+        dbug(1,dprintf("MDM: even parity"));
+        break;
+
+      default:
+        dbug(1,dprintf("MDM: no parity"));
+        break;
+      }
+
+      switch (GET_WORD (mdm_cfg[3].info))
+      {       /* stop bits   */
+      case 1: /* 2 stop bits */
+        cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
+        dbug(1,dprintf("MDM: 2 stop bits"));
+        break;
+
+      default:
+        dbug(1,dprintf("MDM: 1 stop bit"));
+        break;
+      }
+
+      switch (GET_WORD (mdm_cfg[1].info))
+      {     /* char length */
+      case 5:
+        cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
+        dbug(1,dprintf("MDM: 5 bits"));
+        break;
+
+      case 6:
+        cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
+        dbug(1,dprintf("MDM: 6 bits"));
+        break;
+
+      case 7:
+        cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
+        dbug(1,dprintf("MDM: 7 bits"));
+        break;
+
+      default:
+        dbug(1,dprintf("MDM: 8 bits"));
+        break;
+      }
+
+      cai[7] = 0; /* Line taking options */
+      cai[8] = 0; /* Modulation negotiation options */
+      cai[9] = 0; /* Modulation options */
+
+      if (((plci->call_dir & CALL_DIR_ORIGINATE) != 0) ^ ((plci->call_dir & CALL_DIR_OUT) != 0))
+      {
+        cai[9] |= DSP_CAI_MODEM_REVERSE_DIRECTION;
+        dbug(1, dprintf("MDM: Reverse direction"));
+      }
+
+      if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_DISABLE_RETRAIN)
+      {
+        cai[9] |= DSP_CAI_MODEM_DISABLE_RETRAIN;
+        dbug(1, dprintf("MDM: Disable retrain"));
+      }
+
+      if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_DISABLE_RING_TONE)
+      {
+        cai[7] |= DSP_CAI_MODEM_DISABLE_CALLING_TONE | DSP_CAI_MODEM_DISABLE_ANSWER_TONE;
+        dbug(1, dprintf("MDM: Disable ring tone"));
+      }
+
+      if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_GUARD_1800)
+      {
+        cai[8] |= DSP_CAI_MODEM_GUARD_TONE_1800HZ;
+        dbug(1, dprintf("MDM: 1800 guard tone"));
+      }
+      else if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_GUARD_550 )
+      {
+        cai[8] |= DSP_CAI_MODEM_GUARD_TONE_550HZ;
+        dbug(1, dprintf("MDM: 550 guard tone"));
+      }
+
+      if ((GET_WORD (mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_V100)
+      {
+        cai[8] |= DSP_CAI_MODEM_NEGOTIATE_V100;
+        dbug(1, dprintf("MDM: V100"));
+      }
+      else if ((GET_WORD (mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_MOD_CLASS)
+      {
+        cai[8] |= DSP_CAI_MODEM_NEGOTIATE_IN_CLASS;
+        dbug(1, dprintf("MDM: IN CLASS"));
+      }
+      else if ((GET_WORD (mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_DISABLED)
+      {
+        cai[8] |= DSP_CAI_MODEM_NEGOTIATE_DISABLED;
+        dbug(1, dprintf("MDM: DISABLED"));
+      }
+      cai[0] = 20;
+
+      if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_V18))
+       && (GET_WORD(mdm_cfg[5].info) & 0x8000)) /* Private V.18 enable */
+      {
+        plci->requested_options |= 1L << PRIVATE_V18;
+      }
+      if (GET_WORD(mdm_cfg[5].info) & 0x4000) /* Private VOWN enable */
+        plci->requested_options |= 1L << PRIVATE_VOWN;
+
+      if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+        & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
+      {
+        if (!api_parse(&bp_parms[3].info[1],(word)bp_parms[3].length,"wwwwwws", mdm_cfg))
+        {
+          i = 27;
+          if (mdm_cfg[6].length >= 4)
+          {
+            d = GET_DWORD(&mdm_cfg[6].info[1]);
+            cai[7] |= (byte) d;          /* line taking options */
+            cai[9] |= (byte)(d >> 8);    /* modulation options */
+            cai[++i] = (byte)(d >> 16);  /* vown modulation options */
+            cai[++i] = (byte)(d >> 24);
+            if (mdm_cfg[6].length >= 8)
+            {
+              d = GET_DWORD(&mdm_cfg[6].info[5]);
+              cai[10] |= (byte) d;        /* disabled modulations mask */
+              cai[11] |= (byte)(d >> 8);
+              if (mdm_cfg[6].length >= 12)
+              {
+                d = GET_DWORD(&mdm_cfg[6].info[9]);
+                cai[12] = (byte) d;          /* enabled modulations mask */
+                cai[++i] = (byte)(d >> 8);   /* vown enabled modulations */
+                cai[++i] = (byte)(d >> 16);
+                cai[++i] = (byte)(d >> 24);
+                cai[++i] = 0;
+                if (mdm_cfg[6].length >= 14)
+                {
+                  w = GET_WORD(&mdm_cfg[6].info[13]);
+                  if (w != 0)
+                    PUT_WORD(&cai[13], w);  /* min tx speed */
+                  if (mdm_cfg[6].length >= 16)
+                  {
+                    w = GET_WORD(&mdm_cfg[6].info[15]);
+                    if (w != 0)
+                      PUT_WORD(&cai[15], w);  /* max tx speed */
+                    if (mdm_cfg[6].length >= 18)
+                    {
+                      w = GET_WORD(&mdm_cfg[6].info[17]);
+                      if (w != 0)
+                        PUT_WORD(&cai[17], w);  /* min rx speed */
+                      if (mdm_cfg[6].length >= 20)
+                      {
+                        w = GET_WORD(&mdm_cfg[6].info[19]);
+                        if (w != 0)
+                          PUT_WORD(&cai[19], w);  /* max rx speed */
+                        if (mdm_cfg[6].length >= 22)
+                        {
+                          w = GET_WORD(&mdm_cfg[6].info[21]);
+                          cai[23] = (byte)(-((short) w));  /* transmit level */
+                          if (mdm_cfg[6].length >= 24)
+                          {
+                            w = GET_WORD(&mdm_cfg[6].info[23]);
+                            cai[22] |= (byte) w;        /* info options mask */
+                            cai[21] |= (byte)(w >> 8);  /* disabled symbol rates */
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+          cai[27] = i - 27;
+          i++;
+          if (!api_parse(&bp_parms[3].info[1],(word)bp_parms[3].length,"wwwwwwss", mdm_cfg))
+          {
+            if (!api_parse(&mdm_cfg[7].info[1],(word)mdm_cfg[7].length,"sss", mdm_cfg_v18))
+            {
+              for (n = 0; n < 3; n++)
+              {
+                cai[i] = (byte)(mdm_cfg_v18[n].length);
+                for (j = 1; j < ((word)(cai[i] + 1)); j++)
+                  cai[i+j] = mdm_cfg_v18[n].info[j];
+                i += cai[i] + 1;
+              }
+            }
+          }
+          cai[0] = (byte)(i - 1);
+        }
+      }
+
+    }
+  }
+  if(GET_WORD(bp_parms[0].info)==2 ||                         /* V.110 async */
+     GET_WORD(bp_parms[0].info)==3 )                          /* V.110 sync */
+  {
+    if(bp_parms[3].length){
+      dbug(1,dprintf("V.110,%d",GET_WORD(&bp_parms[3].info[1])));
+      switch(GET_WORD(&bp_parms[3].info[1])){                 /* Rate */
+        case 0:
+        case 56000:
+          if(GET_WORD(bp_parms[0].info)==3){                  /* V.110 sync 56k */
+            dbug(1,dprintf("56k sync HSCX"));
+            cai[1] = 8;
+            cai[2] = 0;
+            cai[3] = 0;
+          }
+          else if(GET_WORD(bp_parms[0].info)==2){
+            dbug(1,dprintf("56k async DSP"));
+            cai[2] = 9;
+          }
+          break;
+        case 50:     cai[2] = 1;  break;
+        case 75:     cai[2] = 1;  break;
+        case 110:    cai[2] = 1;  break;
+        case 150:    cai[2] = 1;  break;
+        case 200:    cai[2] = 1;  break;
+        case 300:    cai[2] = 1;  break;
+        case 600:    cai[2] = 1;  break;
+        case 1200:   cai[2] = 2;  break;
+        case 2400:   cai[2] = 3;  break;
+        case 4800:   cai[2] = 4;  break;
+        case 7200:   cai[2] = 10; break;
+        case 9600:   cai[2] = 5;  break;
+        case 12000:  cai[2] = 13; break;
+        case 24000:  cai[2] = 0;  break;
+        case 14400:  cai[2] = 11; break;
+        case 19200:  cai[2] = 6;  break;
+        case 28800:  cai[2] = 12; break;
+        case 38400:  cai[2] = 7;  break;
+        case 48000:  cai[2] = 8;  break;
+        case 76:     cai[2] = 15; break;  /* 75/1200     */
+        case 1201:   cai[2] = 14; break;  /* 1200/75     */
+        case 56001:  cai[2] = 9;  break;  /* V.110 56000 */
+
+        default:
+          return _B1_PARM_NOT_SUPPORTED;
+      }
+      cai[3] = 0;
+      if (cai[1] == 13)                                        /* v.110 async */
+      {
+        if (bp_parms[3].length >= 8)
+        {
+          switch (GET_WORD (&bp_parms[3].info[3]))
+          {       /* char length */
+          case 5:
+            cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
+            break;
+          case 6:
+            cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
+            break;
+          case 7:
+            cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
+            break;
+          }
+          switch (GET_WORD (&bp_parms[3].info[5]))
+          {       /* Parity     */
+          case 1: /* odd parity */
+            cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
+            break;
+          case 2: /* even parity */
+            cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
+            break;
+          }
+          switch (GET_WORD (&bp_parms[3].info[7]))
+          {       /* stop bits   */
+          case 1: /* 2 stop bits */
+            cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
+            break;
+          }
+        }
+      }
+    }
+    else if(cai[1]==8 || GET_WORD(bp_parms[0].info)==3 ){
+      dbug(1,dprintf("V.110 default 56k sync"));
+      cai[1] = 8;
+      cai[2] = 0;
+      cai[3] = 0;
+    }
+    else {
+      dbug(1,dprintf("V.110 default 9600 async"));
+      cai[2] = 5;
+    }
+  }
+  PUT_WORD(&cai[5],plci->appl->MaxDataLength);
+  dbug(1,dprintf("CAI[%d]=%x,%x,%x,%x,%x,%x", cai[0], cai[1], cai[2], cai[3], cai[4], cai[5], cai[6]));
+/* HexDump ("CAI", sizeof(cai), &cai[0]); */
+
+  add_p(plci, CAI, cai);
+  return 0;
+}
+
+/*------------------------------------------------------------------*/
+/* put parameter for b2 and B3  protocol in the parameter buffer    */
+/*------------------------------------------------------------------*/
+
+word add_b23(PLCI   * plci, API_PARSE * bp)
+{
+  word i, fax_control_bits;
+  byte pos, len;
+  byte SAPI = 0x40;  /* default SAPI 16 for x.31 */
+    API_PARSE bp_parms[8];
+  API_PARSE * b1_config;
+  API_PARSE * b2_config;
+    API_PARSE b2_config_parms[8];
+  API_PARSE * b3_config;
+    API_PARSE b3_config_parms[6];
+    API_PARSE global_config[2];
+
+  static byte llc[3] = {2,0,0};
+  static byte dlc[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+  static byte nlc[256];
+  static byte lli[12] = {1,1};
+
+  const byte llc2_out[] = {1,2,4,6,2,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};
+  const byte llc2_in[]  = {1,3,4,6,3,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};
+
+  const byte llc3[] = {4,3,2,2,6,6,0};
+  const byte header[] = {0,2,3,3,0,0,0};
+
+  for(i=0;i<8;i++) bp_parms[i].length = 0;
+  for(i=0;i<6;i++) b2_config_parms[i].length = 0;
+  for(i=0;i<5;i++) b3_config_parms[i].length = 0;
+
+  lli[0] = 1;
+  lli[1] = 1;
+  if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
+    lli[1] |= 2;
+  if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
+    lli[1] |= 4;
+
+  if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
+    lli[1] |= 0x10;
+    if (plci->rx_dma_descriptor <= 0) {
+      plci->rx_dma_descriptor=diva_get_dma_descriptor(plci,&plci->rx_dma_magic);
+      if (plci->rx_dma_descriptor >= 0)
+        plci->rx_dma_descriptor++;
+    }
+    if (plci->rx_dma_descriptor > 0) {
+      lli[0] = 6;
+      lli[1] |= 0x40;
+      lli[2] = (byte)(plci->rx_dma_descriptor - 1);
+      lli[3] = (byte)plci->rx_dma_magic;
+      lli[4] = (byte)(plci->rx_dma_magic >>  8);
+      lli[5] = (byte)(plci->rx_dma_magic >> 16);
+      lli[6] = (byte)(plci->rx_dma_magic >> 24);
+    }
+  }
+
+  if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
+    lli[1] |= 0x20;
+  }
+
+  dbug(1,dprintf("add_b23"));
+  api_save_msg(bp, "s", &plci->B_protocol);
+
+  if(!bp->length && plci->tel)
+  {
+    plci->adv_nl = TRUE;
+    dbug(1,dprintf("Default adv.Nl"));
+    add_p(plci,LLI,lli);
+    plci->B2_prot = 1 /*XPARENT*/;
+    plci->B3_prot = 0 /*XPARENT*/;
+    llc[1] = 2;
+    llc[2] = 4;
+    add_p(plci, LLC, llc);
+    dlc[0] = 2;
+    PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+    add_p(plci, DLC, dlc);
+    return 0;
+  }
+
+  if(!bp->length) /*default*/
+  {   
+    dbug(1,dprintf("ret default"));
+    add_p(plci,LLI,lli);
+    plci->B2_prot = 0 /*X.75   */;
+    plci->B3_prot = 0 /*XPARENT*/;
+    llc[1] = 1;
+    llc[2] = 4;
+    add_p(plci, LLC, llc);
+    dlc[0] = 2;
+    PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+    add_p(plci, DLC, dlc);
+    return 0;
+  }
+  dbug(1,dprintf("b_prot_len=%d",(word)bp->length));
+  if((word)bp->length > 256)    return _WRONG_MESSAGE_FORMAT;
+
+  if(api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
+  {
+    bp_parms[6].length = 0;
+    if(api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
+    {
+      dbug(1,dprintf("b-form.!"));
+      return _WRONG_MESSAGE_FORMAT;
+    }
+  }
+  else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
+  {
+    dbug(1,dprintf("b-form.!"));
+    return _WRONG_MESSAGE_FORMAT;
+  }
+
+  if(plci->tel==ADV_VOICE) /* transparent B on advanced voice */
+  {  
+    if(GET_WORD(bp_parms[1].info)!=1
+    || GET_WORD(bp_parms[2].info)!=0) return _B2_NOT_SUPPORTED;
+    plci->adv_nl = TRUE;
+  }
+  else if(plci->tel) return _B2_NOT_SUPPORTED;
+
+
+  if ((GET_WORD(bp_parms[1].info) == B2_RTP)
+   && (GET_WORD(bp_parms[2].info) == B3_RTP)
+   && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
+  {
+    add_p(plci,LLI,lli);
+    plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
+    plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
+    llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ? 14 : 13;
+    llc[2] = 4;
+    add_p(plci, LLC, llc);
+    dlc[0] = 2;
+    PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+    dlc[3] = 3; /* Addr A */
+    dlc[4] = 1; /* Addr B */
+    dlc[5] = 7; /* modulo mode */
+    dlc[6] = 7; /* window size */
+    dlc[7] = 0; /* XID len Lo  */
+    dlc[8] = 0; /* XID len Hi  */
+    for (i = 0; i < bp_parms[4].length; i++)
+      dlc[9+i] = bp_parms[4].info[1+i];
+    dlc[0] = (byte)(8 + bp_parms[4].length);
+    add_p(plci, DLC, dlc);
+    for (i = 0; i < bp_parms[5].length; i++)
+      nlc[1+i] = bp_parms[5].info[1+i];
+    nlc[0] = (byte)(bp_parms[5].length);
+    add_p(plci, NLC, nlc);
+    return 0;
+  }
+
+
+
+  if ((GET_WORD(bp_parms[1].info) >= 32)
+   || (!((1L << GET_WORD(bp_parms[1].info)) & plci->adapter->profile.B2_Protocols)
+    && ((GET_WORD(bp_parms[1].info) != B2_PIAFS)
+     || !(plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))))
+
+  {
+    return _B2_NOT_SUPPORTED;
+  }
+  if ((GET_WORD(bp_parms[2].info) >= 32)
+   || !((1L << GET_WORD(bp_parms[2].info)) & plci->adapter->profile.B3_Protocols))
+  {
+    return _B3_NOT_SUPPORTED;
+  }
+  if ((GET_WORD(bp_parms[1].info) != B2_SDLC)
+   && ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
+    || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
+    || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC)))
+  {
+    return (add_modem_b23 (plci, bp_parms));
+  }
+
+  add_p(plci,LLI,lli);
+
+  plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
+  plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
+  if(plci->B2_prot==12) SAPI = 0; /* default SAPI D-channel */
+
+  if(bp_parms[6].length)
+  {
+    if(api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
+    {
+      return _WRONG_MESSAGE_FORMAT;
+    }
+    switch(GET_WORD(global_config[0].info))
+    {
+    case 1:
+      plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
+      break;
+    case 2:
+      plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
+      break;
+    }
+  }
+  dbug(1,dprintf("call_dir=%04x", plci->call_dir));
+
+
+  if (plci->B2_prot == B2_PIAFS)
+    llc[1] = PIAFS_CRC;
+  else
+/* IMPLEMENT_PIAFS */
+  {
+    llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
+             llc2_out[GET_WORD(bp_parms[1].info)] : llc2_in[GET_WORD(bp_parms[1].info)];
+  }
+  llc[2] = llc3[GET_WORD(bp_parms[2].info)];
+
+  add_p(plci, LLC, llc);
+
+  dlc[0] = 2;
+  PUT_WORD(&dlc[1], plci->appl->MaxDataLength +
+                      header[GET_WORD(bp_parms[2].info)]);
+
+  b1_config = &bp_parms[3];
+  nlc[0] = 0;
+  if(plci->B3_prot == 4
+  || plci->B3_prot == 5)
+  {
+    for (i=0;i<sizeof(T30_INFO);i++) nlc[i] = 0;
+    nlc[0] = sizeof(T30_INFO);
+    if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+      ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI;
+    ((T30_INFO *)&nlc[1])->rate_div_2400 = 0xff;
+    if(b1_config->length>=2)
+    {
+      ((T30_INFO *)&nlc[1])->rate_div_2400 = (byte)(GET_WORD(&b1_config->info[1])/2400);
+    }
+  }
+  b2_config = &bp_parms[4];
+
+
+  if (llc[1] == PIAFS_CRC)
+  {
+    if (plci->B3_prot != B3_TRANSPARENT)
+    {
+      return _B_STACK_NOT_SUPPORTED;
+    }
+    if(b2_config->length && api_parse(&b2_config->info[1], (word)b2_config->length, "bwww", b2_config_parms)) {
+      return _WRONG_MESSAGE_FORMAT;
+    }
+    PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+    dlc[3] = 0; /* Addr A */
+    dlc[4] = 0; /* Addr B */
+    dlc[5] = 0; /* modulo mode */
+    dlc[6] = 0; /* window size */
+    if (b2_config->length >= 7){
+      dlc[ 7] = 7; 
+      dlc[ 8] = 0; 
+      dlc[ 9] = b2_config_parms[0].info[0]; /* PIAFS protocol Speed configuration */
+      dlc[10] = b2_config_parms[1].info[0]; /* V.42bis P0 */
+      dlc[11] = b2_config_parms[1].info[1]; /* V.42bis P0 */
+      dlc[12] = b2_config_parms[2].info[0]; /* V.42bis P1 */
+      dlc[13] = b2_config_parms[2].info[1]; /* V.42bis P1 */
+      dlc[14] = b2_config_parms[3].info[0]; /* V.42bis P2 */
+      dlc[15] = b2_config_parms[3].info[1]; /* V.42bis P2 */
+      dlc[ 0] = 15;
+      if(b2_config->length >= 8) { /* PIAFS control abilities */
+        dlc[ 7] = 10; 
+        dlc[16] = 2; /* Length of PIAFS extention */
+        dlc[17] = PIAFS_UDATA_ABILITIES; /* control (UDATA) ability */
+        dlc[18] = b2_config_parms[4].info[0]; /* value */
+        dlc[ 0] = 18;
+      }
+    }
+    else /* default values, 64K, variable, no compression */
+    {
+      dlc[ 7] = 7; 
+      dlc[ 8] = 0; 
+      dlc[ 9] = 0x03; /* PIAFS protocol Speed configuration */
+      dlc[10] = 0x03; /* V.42bis P0 */
+      dlc[11] = 0;    /* V.42bis P0 */
+      dlc[12] = 0;    /* V.42bis P1 */
+      dlc[13] = 0;    /* V.42bis P1 */
+      dlc[14] = 0;    /* V.42bis P2 */
+      dlc[15] = 0;    /* V.42bis P2 */
+    dlc[ 0] = 15;
+    }
+    add_p(plci, DLC, dlc);
+  }
+  else
+
+  if ((llc[1] == V120_L2) || (llc[1] == V120_V42BIS))
+  {
+    if (plci->B3_prot != B3_TRANSPARENT)
+      return _B_STACK_NOT_SUPPORTED;
+
+    dlc[0] = 6;
+    PUT_WORD (&dlc[1], GET_WORD (&dlc[1]) + 2);
+    dlc[3] = 0x08;
+    dlc[4] = 0x01;
+    dlc[5] = 127;
+    dlc[6] = 7;
+    if (b2_config->length != 0)
+    {
+      if((llc[1]==V120_V42BIS) && api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms)) {
+        return _WRONG_MESSAGE_FORMAT;
+      }
+      dlc[3] = (byte)((b2_config->info[2] << 3) | ((b2_config->info[1] >> 5) & 0x04));
+      dlc[4] = (byte)((b2_config->info[1] << 1) | 0x01);
+      if (b2_config->info[3] != 128)
+      {
+        dbug(1,dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
+        return _B2_PARM_NOT_SUPPORTED;
+      }
+      dlc[5] = (byte)(b2_config->info[3] - 1);
+      dlc[6] = b2_config->info[4];
+      if(llc[1]==V120_V42BIS){
+        if (b2_config->length >= 10){
+          dlc[ 7] = 6; 
+          dlc[ 8] = 0; 
+          dlc[ 9] = b2_config_parms[4].info[0];
+          dlc[10] = b2_config_parms[4].info[1];
+          dlc[11] = b2_config_parms[5].info[0];
+          dlc[12] = b2_config_parms[5].info[1];
+          dlc[13] = b2_config_parms[6].info[0];
+          dlc[14] = b2_config_parms[6].info[1];
+          dlc[ 0] = 14;
+          dbug(1,dprintf("b2_config_parms[4].info[0] [1]:  %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
+          dbug(1,dprintf("b2_config_parms[5].info[0] [1]:  %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
+          dbug(1,dprintf("b2_config_parms[6].info[0] [1]:  %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
+        }
+        else {
+          dlc[ 6] = 14;
+        }
+      }
+    }
+  }
+  else
+  {
+    if(b2_config->length)
+    {
+      dbug(1,dprintf("B2-Config"));
+      if(llc[1]==X75_V42BIS){
+        if(api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms))
+        {
+          return _WRONG_MESSAGE_FORMAT;
+        }
+      }
+      else {
+        if(api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbs", b2_config_parms))
+        {
+          return _WRONG_MESSAGE_FORMAT;
+        }
+      }
+          /* if B2 Protocol is LAPD, b2_config structure is different */
+      if(llc[1]==6)
+      {
+        dlc[0] = 4;
+        if(b2_config->length>=1) dlc[2] = b2_config->info[1];      /* TEI */
+        else dlc[2] = 0x01;
+        if( (b2_config->length>=2) && (plci->B2_prot==12) )
+        {
+          SAPI = b2_config->info[2];    /* SAPI */
+        }
+        dlc[1] = SAPI;
+        if( (b2_config->length>=3) && (b2_config->info[3]==128) )
+        {
+          dlc[3] = 127;      /* Mode */
+        }
+        else
+        {
+          dlc[3] = 7;        /* Mode */
+        }
+   
+        if(b2_config->length>=4) dlc[4] = b2_config->info[4];      /* Window */
+        else dlc[4] = 1;
+        dbug(1,dprintf("D-dlc[%d]=%x,%x,%x,%x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
+        if(b2_config->length>5) return _B2_PARM_NOT_SUPPORTED;
+      }
+      else
+      {
+        dlc[0] = (byte)(b2_config_parms[4].length+6);
+        dlc[3] = b2_config->info[1];
+        dlc[4] = b2_config->info[2];
+        if(b2_config->info[3]!=8 && b2_config->info[3]!=128){
+          dbug(1,dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
+          return _B2_PARM_NOT_SUPPORTED;
+        }
+
+        dlc[5] = (byte)(b2_config->info[3]-1);
+        dlc[6] = b2_config->info[4];
+        if(dlc[6]>dlc[5]){
+          dbug(1,dprintf("2D-dlc= %x %x %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4], dlc[5], dlc[6]));
+          return _B2_PARM_NOT_SUPPORTED;
+        }
+ 
+        if(llc[1]==X75_V42BIS) {
+          if (b2_config->length >= 10){
+            dlc[ 7] = 6; 
+            dlc[ 8] = 0; 
+            dlc[ 9] = b2_config_parms[4].info[0];
+            dlc[10] = b2_config_parms[4].info[1];
+            dlc[11] = b2_config_parms[5].info[0];
+            dlc[12] = b2_config_parms[5].info[1];
+            dlc[13] = b2_config_parms[6].info[0];
+            dlc[14] = b2_config_parms[6].info[1];
+            dlc[ 0] = 14;
+            dbug(1,dprintf("b2_config_parms[4].info[0] [1]:  %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
+            dbug(1,dprintf("b2_config_parms[5].info[0] [1]:  %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
+            dbug(1,dprintf("b2_config_parms[6].info[0] [1]:  %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
+          }
+          else {
+            dlc[ 6] = 14;
+          }
+
+        }
+        else {
+          PUT_WORD(&dlc[7], (word)b2_config_parms[4].length);
+          for(i=0; i<b2_config_parms[4].length; i++)
+            dlc[11+i] = b2_config_parms[4].info[1+i];
+        }
+      }
+    }
+  }
+  add_p(plci, DLC, dlc);
+
+  b3_config = &bp_parms[5];
+  if(b3_config->length)
+  {
+    if(plci->B3_prot == 4 
+    || plci->B3_prot == 5)
+    {
+      if(api_parse(&b3_config->info[1], (word)b3_config->length, "wwss", b3_config_parms))
+      {
+        return _WRONG_MESSAGE_FORMAT;
+      }
+      i = GET_WORD((byte   *)(b3_config_parms[0].info));
+      ((T30_INFO *)&nlc[1])->resolution = (byte)(((i & 0x0001) ||
+        ((plci->B3_prot == 4) && (((byte)(GET_WORD((byte   *)b3_config_parms[1].info))) != 5))) ? T30_RESOLUTION_R8_0770_OR_200 : 0);
+      ((T30_INFO *)&nlc[1])->data_format = (byte)(GET_WORD((byte   *)b3_config_parms[1].info));
+      fax_control_bits = T30_CONTROL_BIT_ALL_FEATURES;
+      if ((((T30_INFO *)&nlc[1])->rate_div_2400 != 0) && (((T30_INFO *)&nlc[1])->rate_div_2400 <= 6))
+        fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_V34FAX;
+      if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+      {
+
+        if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+          & (1L << PRIVATE_FAX_PAPER_FORMATS))
+        {
+          ((T30_INFO *)&nlc[1])->resolution |= T30_RESOLUTION_R8_1540 |
+            T30_RESOLUTION_R16_1540_OR_400 | T30_RESOLUTION_300_300 |
+            T30_RESOLUTION_INCH_BASED | T30_RESOLUTION_METRIC_BASED;
+        }
+
+ ((T30_INFO *)&nlc[1])->recording_properties =
+   T30_RECORDING_WIDTH_ISO_A3 |
+   (T30_RECORDING_LENGTH_UNLIMITED << 2) |
+   (T30_MIN_SCANLINE_TIME_00_00_00 << 4);
+      }
+      if(plci->B3_prot == 5)
+      {
+        if (i & 0x0002) /* Accept incoming fax-polling requests */
+          fax_control_bits |= T30_CONTROL_BIT_ACCEPT_POLLING;
+        if (i & 0x2000) /* Do not use MR compression */
+          fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_2D_CODING;
+        if (i & 0x4000) /* Do not use MMR compression */
+          fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_T6_CODING;
+        if (i & 0x8000) /* Do not use ECM */
+          fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_ECM;
+        if (plci->fax_connect_info_length != 0)
+        {
+          ((T30_INFO *)&nlc[1])->resolution = ((T30_INFO   *)plci->fax_connect_info_buffer)->resolution;
+          ((T30_INFO *)&nlc[1])->data_format = ((T30_INFO   *)plci->fax_connect_info_buffer)->data_format;
+          ((T30_INFO *)&nlc[1])->recording_properties = ((T30_INFO   *)plci->fax_connect_info_buffer)->recording_properties;
+          fax_control_bits |= GET_WORD(&((T30_INFO   *)plci->fax_connect_info_buffer)->control_bits_low) &
+            (T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
+        }
+      }
+      /* copy station id to NLC */
+      for(i=0; i<20; i++)
+      {
+        if(i<b3_config_parms[2].length)
+        {
+          ((T30_INFO *)&nlc[1])->station_id[i] = ((byte   *)b3_config_parms[2].info)[1+i];
+        }
+        else
+        {
+          ((T30_INFO *)&nlc[1])->station_id[i] = ' ';
+        }
+      }
+      ((T30_INFO *)&nlc[1])->station_id_len = 20;
+      /* copy head line to NLC */
+      if(b3_config_parms[3].length)
+      {
+
+        pos = (byte)(fax_head_line_time (&(((T30_INFO *)&nlc[1])->station_id[20])));
+        if (pos != 0)
+        {
+          if (CAPI_MAX_DATE_TIME_LENGTH + 2 + b3_config_parms[3].length > CAPI_MAX_HEAD_LINE_SPACE)
+            pos = 0;
+          else
+          {
+            ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+            ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+            len = (byte)b3_config_parms[2].length;
+            if (len > 20)
+              len = 20;
+            if (CAPI_MAX_DATE_TIME_LENGTH + 2 + len + 2 + b3_config_parms[3].length <= CAPI_MAX_HEAD_LINE_SPACE)
+            {
+              for (i = 0; i < len; i++)
+                ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ((byte   *)b3_config_parms[2].info)[1+i];
+              ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+              ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+            }
+          }
+        }
+
+        len = (byte)b3_config_parms[3].length;
+        if (len > CAPI_MAX_HEAD_LINE_SPACE - pos)
+          len = (byte)(CAPI_MAX_HEAD_LINE_SPACE - pos);
+        ((T30_INFO *)&nlc[1])->head_line_len = (byte)(pos + len);
+        nlc[0] += (byte)(pos + len);
+        for (i = 0; i < len; i++)
+          ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ((byte   *)b3_config_parms[3].info)[1+i];
+        }
+      else
+        ((T30_INFO *)&nlc[1])->head_line_len = 0;
+
+      plci->nsf_control_bits = 0;
+      if(plci->B3_prot == 5)
+      {
+        if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+         && (GET_WORD((byte   *)b3_config_parms[1].info) & 0x8000)) /* Private SUB/SEP/PWD enable */
+        {
+          plci->requested_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;
+        }
+        if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
+         && (GET_WORD((byte   *)b3_config_parms[1].info) & 0x4000)) /* Private non-standard facilities enable */
+        {
+          plci->requested_options |= 1L << PRIVATE_FAX_NONSTANDARD;
+        }
+        if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+          & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+        {
+        if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+          & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+        {
+          fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
+          if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
+            fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
+          }
+            len = nlc[0];
+          pos = ((byte)(((T30_INFO *) 0)->station_id + 20));
+   if (pos < plci->fax_connect_info_length)
+   {
+     for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
+              nlc[++len] = plci->fax_connect_info_buffer[pos++];
+          }
+   else
+     nlc[++len] = 0;
+   if (pos < plci->fax_connect_info_length)
+   {
+     for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
+              nlc[++len] = plci->fax_connect_info_buffer[pos++];
+          }
+   else
+     nlc[++len] = 0;
+          if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+            & (1L << PRIVATE_FAX_NONSTANDARD))
+          {
+     if ((pos < plci->fax_connect_info_length) && (plci->fax_connect_info_buffer[pos] != 0))
+     {
+              if ((plci->fax_connect_info_buffer[pos] >= 3) && (plci->fax_connect_info_buffer[pos+1] >= 2))
+                plci->nsf_control_bits = GET_WORD(&plci->fax_connect_info_buffer[pos+2]);
+       for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
+                nlc[++len] = plci->fax_connect_info_buffer[pos++];
+            }
+     else
+     {
+              if(api_parse(&b3_config->info[1], (word)b3_config->length, "wwsss", b3_config_parms))
+              {
+                dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+                nlc[++len] = 0;
+              }
+       else
+       {
+                if ((b3_config_parms[4].length >= 3) && (b3_config_parms[4].info[1] >= 2))
+                  plci->nsf_control_bits = GET_WORD(&b3_config_parms[4].info[2]);
+         nlc[++len] = (byte)(b3_config_parms[4].length);
+         for (i = 0; i < b3_config_parms[4].length; i++)
+    nlc[++len] = b3_config_parms[4].info[1+i];
+       }
+            }
+          }
+            nlc[0] = len;
+   if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
+    && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
+   {
+            ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI_NEG;
+          }
+        }
+      }
+
+      PUT_WORD(&(((T30_INFO *)&nlc[1])->control_bits_low), fax_control_bits);
+      len = ((byte)(((T30_INFO *) 0)->station_id + 20));
+      for (i = 0; i < len; i++)
+        plci->fax_connect_info_buffer[i] = nlc[1+i];
+      ((T30_INFO   *) plci->fax_connect_info_buffer)->head_line_len = 0;
+      i += ((T30_INFO *)&nlc[1])->head_line_len;
+      while (i < nlc[0])
+        plci->fax_connect_info_buffer[len++] = nlc[++i];
+      plci->fax_connect_info_length = len;
+    }
+    else
+    {
+      nlc[0] = 14;
+      if(b3_config->length!=16)
+        return _B3_PARM_NOT_SUPPORTED;
+      for(i=0; i<12; i++) nlc[1+i] = b3_config->info[1+i];
+      if(GET_WORD(&b3_config->info[13])!=8 && GET_WORD(&b3_config->info[13])!=128)
+        return _B3_PARM_NOT_SUPPORTED;
+      nlc[13] = b3_config->info[13];
+      if(GET_WORD(&b3_config->info[15])>=nlc[13])
+        return _B3_PARM_NOT_SUPPORTED;
+      nlc[14] = b3_config->info[15];
+    }
+  }
+  else
+  {
+    if (plci->B3_prot == 4 
+     || plci->B3_prot == 5 /*T.30 - FAX*/ ) return _B3_PARM_NOT_SUPPORTED;
+  }
+  add_p(plci, NLC, nlc);
+  return 0;
+}
+
+/*----------------------------------------------------------------*/
+/*      make the same as add_b23, but only for the modem related  */
+/*      L2 and L3 B-Chan protocol.                                */
+/*                                                                */
+/*      Enabled L2 and L3 Configurations:                         */
+/*        If L1 == Modem all negotiation                          */
+/*          only L2 == Modem with full negotiation is allowed     */
+/*        If L1 == Modem async or sync                            */
+/*          only L2 == Transparent is allowed                     */
+/*        L3 == Modem or L3 == Transparent are allowed            */
+/*      B2 Configuration for modem:                               */
+/*          word : enable/disable compression, bitoptions         */
+/*      B3 Configuration for modem:                               */
+/*          empty                                                 */
+/*----------------------------------------------------------------*/
+static word add_modem_b23 (PLCI  * plci, API_PARSE* bp_parms)
+{
+  static byte lli[12] = {1,1};
+  static byte llc[3] = {2,0,0};
+  static byte dlc[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+    API_PARSE mdm_config[2];
+  word i;
+  word b2_config = 0;
+
+  for(i=0;i<2;i++) mdm_config[i].length = 0;
+  for(i=0;i<sizeof(dlc);i++) dlc[i] = 0;
+
+  if (((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
+    && (GET_WORD(bp_parms[1].info) != B2_MODEM_EC_COMPRESSION))
+   || ((GET_WORD(bp_parms[0].info) != B1_MODEM_ALL_NEGOTIATE)
+    && (GET_WORD(bp_parms[1].info) != B2_TRANSPARENT)))
+  {
+    return (_B_STACK_NOT_SUPPORTED);
+  }
+  if ((GET_WORD(bp_parms[2].info) != B3_MODEM)
+   && (GET_WORD(bp_parms[2].info) != B3_TRANSPARENT))
+  {
+    return (_B_STACK_NOT_SUPPORTED);
+  }
+
+  plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
+  plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
+
+  if ((GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION) && bp_parms[4].length)
+  {
+    if (api_parse (&bp_parms[4].info[1],
+                  (word)bp_parms[4].length, "w",
+                  mdm_config))
+    {
+      return (_WRONG_MESSAGE_FORMAT);
+    }
+    b2_config = GET_WORD(mdm_config[0].info);
+  }
+
+  /* OK, L2 is modem */
+
+  lli[0] = 1;
+  lli[1] = 1;
+  if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
+    lli[1] |= 2;
+  if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
+    lli[1] |= 4;
+
+  if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
+    lli[1] |= 0x10;
+    if (plci->rx_dma_descriptor <= 0) {
+      plci->rx_dma_descriptor=diva_get_dma_descriptor(plci,&plci->rx_dma_magic);
+      if (plci->rx_dma_descriptor >= 0)
+        plci->rx_dma_descriptor++;
+    }
+    if (plci->rx_dma_descriptor > 0) {
+      lli[1] |= 0x40;
+      lli[0] = 6;
+      lli[2] = (byte)(plci->rx_dma_descriptor - 1);
+      lli[3] = (byte)plci->rx_dma_magic;
+      lli[4] = (byte)(plci->rx_dma_magic >>  8);
+      lli[5] = (byte)(plci->rx_dma_magic >> 16);
+      lli[6] = (byte)(plci->rx_dma_magic >> 24);
+    }
+  }
+
+  if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
+    lli[1] |= 0x20;
+  }
+
+  llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
+    /*V42*/ 10 : /*V42_IN*/ 9;
+  llc[2] = 4;                      /* pass L3 always transparent */
+  add_p(plci, LLI, lli);
+  add_p(plci, LLC, llc);
+  i =  1;
+  PUT_WORD (&dlc[i], plci->appl->MaxDataLength);
+  i += 2;
+  if (GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION)
+  {
+    if (bp_parms[4].length)
+  {
+    dbug(1, dprintf("MDM b2_config=%02x", b2_config));
+    dlc[i++] = 3; /* Addr A */
+    dlc[i++] = 1; /* Addr B */
+    dlc[i++] = 7; /* modulo mode */
+    dlc[i++] = 7; /* window size */
+    dlc[i++] = 0; /* XID len Lo  */
+    dlc[i++] = 0; /* XID len Hi  */
+
+    if (b2_config & MDM_B2_DISABLE_V42bis)
+    {
+      dlc[i] |= DLC_MODEMPROT_DISABLE_V42_V42BIS;
+    }
+    if (b2_config & MDM_B2_DISABLE_MNP)
+    {
+      dlc[i] |= DLC_MODEMPROT_DISABLE_MNP_MNP5;
+    }
+    if (b2_config & MDM_B2_DISABLE_TRANS)
+    {
+      dlc[i] |= DLC_MODEMPROT_REQUIRE_PROTOCOL;
+    }
+    if (b2_config & MDM_B2_DISABLE_V42)
+    {
+      dlc[i] |= DLC_MODEMPROT_DISABLE_V42_DETECT;
+    }
+    if (b2_config & MDM_B2_DISABLE_COMP)
+    {
+      dlc[i] |= DLC_MODEMPROT_DISABLE_COMPRESSION;
+    }
+    i++;
+  }
+  }
+  else
+  {
+    dlc[i++] = 3; /* Addr A */
+    dlc[i++] = 1; /* Addr B */
+    dlc[i++] = 7; /* modulo mode */
+    dlc[i++] = 7; /* window size */
+    dlc[i++] = 0; /* XID len Lo  */
+    dlc[i++] = 0; /* XID len Hi  */
+    dlc[i++] = DLC_MODEMPROT_DISABLE_V42_V42BIS |
+               DLC_MODEMPROT_DISABLE_MNP_MNP5 |
+               DLC_MODEMPROT_DISABLE_V42_DETECT |
+               DLC_MODEMPROT_DISABLE_COMPRESSION;
+  }
+  dlc[0] = (byte)(i - 1);
+/* HexDump ("DLC", sizeof(dlc), &dlc[0]); */
+  add_p(plci, DLC, dlc);
+  return (0);
+}
+
+
+/*------------------------------------------------------------------*/
+/* send a request for the signaling entity                          */
+/*------------------------------------------------------------------*/
+
+void sig_req(PLCI   * plci, byte req, byte Id)
+{
+  if(!plci) return;
+  if(plci->adapter->adapter_disabled) return;
+  dbug(1,dprintf("sig_req(%x)",req));
+  if (req == REMOVE)
+    plci->sig_remove_id = plci->Sig.Id;
+  if(plci->req_in==plci->req_in_start) {
+    plci->req_in +=2;
+    plci->RBuffer[plci->req_in++] = 0;
+  }
+  PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start-2);
+  plci->RBuffer[plci->req_in++] = Id;   /* sig/nl flag */
+  plci->RBuffer[plci->req_in++] = req;  /* request */
+  plci->RBuffer[plci->req_in++] = 0;    /* channel */
+  plci->req_in_start = plci->req_in;
+}
+
+/*------------------------------------------------------------------*/
+/* send a request for the network layer entity                      */
+/*------------------------------------------------------------------*/
+
+void nl_req_ncci(PLCI   * plci, byte req, byte ncci)
+{
+  if(!plci) return;
+  if(plci->adapter->adapter_disabled) return;
+  dbug(1,dprintf("nl_req %02x %02x %02x", plci->Id, req, ncci));
+  if (req == REMOVE)
+  {
+    plci->nl_remove_id = plci->NL.Id;
+    ncci_remove (plci, 0, (byte)(ncci != 0));
+    ncci = 0;
+  }
+  if(plci->req_in==plci->req_in_start) {
+    plci->req_in +=2;
+    plci->RBuffer[plci->req_in++] = 0;
+  }
+  PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start-2);
+  plci->RBuffer[plci->req_in++] = 1;    /* sig/nl flag */
+  plci->RBuffer[plci->req_in++] = req;  /* request */
+  plci->RBuffer[plci->req_in++] = plci->adapter->ncci_ch[ncci];   /* channel */
+  plci->req_in_start = plci->req_in;
+}
+
+void send_req(PLCI   * plci)
+{
+  ENTITY   * e;
+  word l;
+/*  word i; */
+
+  if(!plci) return;
+  if(plci->adapter->adapter_disabled) return;
+  channel_xmit_xon (plci);
+
+        /* if nothing to do, return */
+  if(plci->req_in==plci->req_out) return;
+  dbug(1,dprintf("send_req(in=%d,out=%d)",plci->req_in,plci->req_out));
+
+  if(plci->nl_req || plci->sig_req) return;
+
+  l = GET_WORD(&plci->RBuffer[plci->req_out]);
+  plci->req_out += 2;
+  plci->XData[0].P = &plci->RBuffer[plci->req_out];
+  plci->req_out += l;
+  if(plci->RBuffer[plci->req_out]==1)
+  {
+    e = &plci->NL;
+    plci->req_out++;
+    e->Req = plci->nl_req = plci->RBuffer[plci->req_out++];
+    e->ReqCh = plci->RBuffer[plci->req_out++];
+    if(!(e->Id & 0x1f))
+    {
+      e->Id = NL_ID;
+      plci->RBuffer[plci->req_out-4] = CAI;
+      plci->RBuffer[plci->req_out-3] = 1;
+      plci->RBuffer[plci->req_out-2] = (plci->Sig.Id==0xff) ? 0 : plci->Sig.Id;
+      plci->RBuffer[plci->req_out-1] = 0;
+      l+=3;
+      plci->nl_global_req = plci->nl_req;
+    }
+    dbug(1,dprintf("%x:NLREQ(%x:%x:%x)",plci->adapter->Id,e->Id,e->Req,e->ReqCh));
+  }
+  else
+  {
+    e = &plci->Sig;
+    if(plci->RBuffer[plci->req_out])
+      e->Id = plci->RBuffer[plci->req_out];
+    plci->req_out++;
+    e->Req = plci->sig_req = plci->RBuffer[plci->req_out++];
+    e->ReqCh = plci->RBuffer[plci->req_out++];
+    if(!(e->Id & 0x1f))
+      plci->sig_global_req = plci->sig_req;
+    dbug(1,dprintf("%x:SIGREQ(%x:%x:%x)",plci->adapter->Id,e->Id,e->Req,e->ReqCh));
+  }
+  plci->XData[0].PLength = l;
+  e->X = plci->XData;
+  plci->adapter->request(e);
+  dbug(1,dprintf("send_ok"));
+}
+
+void send_data(PLCI   * plci)
+{
+  DIVA_CAPI_ADAPTER   * a;
+  DATA_B3_DESC   * data;
+  NCCI   *ncci_ptr;
+  word ncci;
+
+  if (!plci->nl_req && plci->ncci_ring_list)
+  {
+    a = plci->adapter;
+    ncci = plci->ncci_ring_list;
+    do
+    {
+      ncci = a->ncci_next[ncci];
+      ncci_ptr = &(a->ncci[ncci]);
+      if (!(a->ncci_ch[ncci]
+         && (a->ch_flow_control[a->ncci_ch[ncci]] & N_OK_FC_PENDING)))
+      {
+        if (ncci_ptr->data_pending)
+        {
+          if ((a->ncci_state[ncci] == CONNECTED)
+           || (a->ncci_state[ncci] == INC_ACT_PENDING)
+           || (plci->send_disc == ncci))
+          {
+            data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
+            if ((plci->B2_prot == B2_V120_ASYNC)
+             || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
+             || (plci->B2_prot == B2_V120_BIT_TRANSPARENT))
+            {
+              plci->NData[1].P = TransmitBufferGet (plci->appl, data->P);
+              plci->NData[1].PLength = data->Length;
+              if (data->Flags & 0x10)
+                plci->NData[0].P = v120_break_header;
+              else
+                plci->NData[0].P = v120_default_header;
+              plci->NData[0].PLength = 1 ;
+              plci->NL.XNum = 2;
+              plci->NL.Req = plci->nl_req = (byte)((data->Flags&0x07)<<4 |N_DATA);
+            }
+            else
+            {
+              plci->NData[0].P = TransmitBufferGet (plci->appl, data->P);
+              plci->NData[0].PLength = data->Length;
+              if (data->Flags & 0x10)
+                plci->NL.Req = plci->nl_req = (byte)N_UDATA;
+
+              else if ((plci->B3_prot == B3_RTP) && (data->Flags & 0x01))
+                plci->NL.Req = plci->nl_req = (byte)N_BDATA;
+
+              else
+                plci->NL.Req = plci->nl_req = (byte)((data->Flags&0x07)<<4 |N_DATA);
+            }
+            plci->NL.X = plci->NData;
+            plci->NL.ReqCh = a->ncci_ch[ncci];
+            dbug(1,dprintf("%x:DREQ(%x:%x)",a->Id,plci->NL.Id,plci->NL.Req));
+            plci->data_sent = TRUE;
+            plci->data_sent_ptr = data->P;
+            a->request(&plci->NL);
+          }
+          else {
+            cleanup_ncci_data (plci, ncci);
+          }
+        }
+        else if (plci->send_disc == ncci)
+        {
+          /* dprintf("N_DISC"); */
+          plci->NData[0].PLength = 0;
+          plci->NL.ReqCh = a->ncci_ch[ncci];
+          plci->NL.Req = plci->nl_req = N_DISC;
+          a->request(&plci->NL);
+          plci->command = _DISCONNECT_B3_R;
+          plci->send_disc = 0;
+        }
+      }
+    } while (!plci->nl_req && (ncci != plci->ncci_ring_list));
+    plci->ncci_ring_list = ncci;
+  }
+}
+
+void listen_check(DIVA_CAPI_ADAPTER   * a)
+{
+  word i,j;
+  PLCI   * plci;
+  byte activnotifiedcalls = 0;
+
+  dbug(1,dprintf("listen_check(%d,%d)",a->listen_active,a->max_listen));
+  if (!remove_started && !a->adapter_disabled)
+  {
+    for(i=0;i<a->max_plci;i++)
+    {
+      plci = &(a->plci[i]);
+      if(plci->notifiedcall) activnotifiedcalls++;
+    }
+    dbug(1,dprintf("listen_check(%d)",activnotifiedcalls));
+
+    for(i=a->listen_active; i < ((word)(a->max_listen+activnotifiedcalls)); i++) {
+      if((j=get_plci(a))) {
+        a->listen_active++;
+        plci = &a->plci[j-1];
+        plci->State = LISTENING;
+
+        add_p(plci,OAD,"\x01\xfd");
+
+        add_p(plci,KEY,"\x04\x43\x41\x32\x30");
+
+        add_p(plci,CAI,"\x01\xc0");
+        add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+        add_p(plci,LLI,"\x01\xc4");                  /* support Dummy CR FAC + MWI + SpoofNotify */       
+        add_p(plci,SHIFT|6,NULL);
+        add_p(plci,SIN,"\x02\x00\x00");
+        plci->internal_command = LISTEN_SIG_ASSIGN_PEND;     /* do indicate_req if OK  */
+        sig_req(plci,ASSIGN,DSIG_ID);
+        send_req(plci);
+      }
+    }
+  }
+}
+
+/*------------------------------------------------------------------*/
+/* functions for all parameters sent in INDs                        */
+/*------------------------------------------------------------------*/
+
+void IndParse(PLCI   * plci, word * parms_id, byte   ** parms, byte multiIEsize)
+{
+  word ploc;            /* points to current location within packet */
+  byte w;
+  byte wlen;
+  byte codeset,lock;
+  byte   * in;
+  word i;
+  word code;
+  word mIEindex = 0;
+  ploc = 0;
+  codeset = 0;
+  lock = 0;
+
+  in = plci->Sig.RBuffer->P;
+  for(i=0; i<parms_id[0]; i++)   /* multiIE parms_id contains just the 1st */
+  {                            /* element but parms array is larger      */
+    parms[i] = (byte   *)"";
+  }
+  for(i=0; i<multiIEsize; i++)
+  {
+    parms[i] = (byte   *)"";
+  }
+
+  while(ploc<plci->Sig.RBuffer->length-1) {
+
+        /* read information element id and length                   */
+    w = in[ploc];
+
+    if(w & 0x80) {
+/*    w &=0xf0; removed, cannot detect congestion levels */
+/*    upper 4 bit masked with w==SHIFT now               */
+      wlen = 0;
+    }
+    else {
+      wlen = (byte)(in[ploc+1]+1);
+    }
+        /* check if length valid (not exceeding end of packet)      */
+    if((ploc+wlen) > 270) return ;
+    if(lock & 0x80) lock &=0x7f;
+    else codeset = lock;
+
+    if((w&0xf0)==SHIFT) {
+      codeset = in[ploc];
+      if(!(codeset & 0x08)) lock = (byte)(codeset & 7);
+      codeset &=7;
+      lock |=0x80;
+    }
+    else {
+      if(w==ESC && wlen>=3) code = in[ploc+2] |0x800;
+      else code = w;
+      code |= (codeset<<8);
+
+      for(i=1; i<parms_id[0]+1 && parms_id[i]!=code; i++);
+
+      if(i<parms_id[0]+1) {
+        if(!multiIEsize) { /* with multiIEs use next field index,          */
+          mIEindex = i-1;    /* with normal IEs use same index like parms_id */
+        }
+
+        parms[mIEindex] = &in[ploc+1];
+        dbug(1,dprintf("mIE[%d]=0x%x",*parms[mIEindex],in[ploc]));
+        if(parms_id[i]==OAD
+        || parms_id[i]==CONN_NR
+        || parms_id[i]==CAD) {
+          if(in[ploc+2] &0x80) {
+            in[ploc+0] = (byte)(in[ploc+1]+1);
+            in[ploc+1] = (byte)(in[ploc+2] &0x7f);
+            in[ploc+2] = 0x80;
+            parms[mIEindex] = &in[ploc];
+          }
+        }
+        mIEindex++;       /* effects multiIEs only */
+      }
+    }
+
+    ploc +=(wlen+1);
+  }
+  return ;
+}
+
+/*------------------------------------------------------------------*/
+/* try to match a cip from received BC and HLC                      */
+/*------------------------------------------------------------------*/
+
+byte ie_compare(byte   * ie1, byte * ie2)
+{
+  word i;
+  if(!ie1 || ! ie2) return FALSE;
+  if(!ie1[0]) return FALSE;
+  for(i=0;i<(word)(ie1[0]+1);i++) if(ie1[i]!=ie2[i]) return FALSE;
+  return TRUE;
+}
+
+word find_cip(DIVA_CAPI_ADAPTER   * a, byte   * bc, byte   * hlc)
+{
+  word i;
+  word j;
+
+  for(i=9;i && !ie_compare(bc,cip_bc[i][a->u_law]);i--);
+
+  for(j=16;j<29 &&
+           (!ie_compare(bc,cip_bc[j][a->u_law]) || !ie_compare(hlc,cip_hlc[j])); j++);
+  if(j==29) return i;
+  return j;
+}
+
+
+static byte AddInfo(byte   **add_i,
+                    byte   **fty_i,
+                    byte   *esc_chi,
+                    byte *facility)
+{
+  byte i;
+  byte j;
+  byte k;
+  byte flen;
+  byte len=0;
+   /* facility is a nested structure */
+   /* FTY can be more than once      */
+
+  if(esc_chi[0] && !(esc_chi[esc_chi[0]])&0x7f )
+  {
+    add_i[0] = (byte   *)"\x02\x02\x00"; /* use neither b nor d channel */
+  }
+
+  else
+  {
+    add_i[0] = (byte   *)"";
+  }
+  if(!fty_i[0][0])
+  {
+    add_i[3] = (byte   *)"";
+  }
+  else
+  {    /* facility array found  */
+    for(i=0,j=1;i<MAX_MULTI_IE && fty_i[i][0];i++)
+    {
+      dbug(1,dprintf("AddIFac[%d]",fty_i[i][0]));
+      len += fty_i[i][0];
+      len += 2;
+      flen=fty_i[i][0];
+      facility[j++]=0x1c; /* copy fac IE */
+      for(k=0;k<=flen;k++,j++)
+      {
+        facility[j]=fty_i[i][k];
+/*      dbug(1,dprintf("%x ",facility[j])); */
+      }
+    }
+    facility[0] = len;
+    add_i[3] = facility;
+  }
+/*  dbug(1,dprintf("FacArrLen=%d ",len)); */
+  len = add_i[0][0]+add_i[1][0]+add_i[2][0]+add_i[3][0];
+  len += 4;                          /* calculate length of all */
+  return(len);
+}
+
+/*------------------------------------------------------------------*/
+/* voice and codec features                                         */
+/*------------------------------------------------------------------*/
+
+void SetVoiceChannel(PLCI   *plci, byte   *chi, DIVA_CAPI_ADAPTER   * a)
+{
+  byte voice_chi[] = "\x02\x18\x01";
+  byte channel;
+
+  channel = chi[chi[0]]&0x3;
+  dbug(1,dprintf("ExtDevON(Ch=0x%x)",channel));
+  voice_chi[2] = (channel) ? channel : 1;
+  add_p(plci,FTY,"\x02\x01\x07");             /* B On, default on 1 */
+  add_p(plci,ESC,voice_chi);                  /* Channel */
+  sig_req(plci,TEL_CTRL,0);
+  send_req(plci);
+  if(a->AdvSignalPLCI)
+  {
+    adv_voice_write_coefs (a->AdvSignalPLCI, ADV_VOICE_WRITE_ACTIVATION);
+  }
+}
+
+void VoiceChannelOff(PLCI   *plci)
+{
+  dbug(1,dprintf("ExtDevOFF"));
+  add_p(plci,FTY,"\x02\x01\x08");             /* B Off */
+  sig_req(plci,TEL_CTRL,0);
+  send_req(plci);
+  if(plci->adapter->AdvSignalPLCI)
+  {
+    adv_voice_clear_config (plci->adapter->AdvSignalPLCI);
+  }
+}
+
+
+word AdvCodecSupport(DIVA_CAPI_ADAPTER   *a, PLCI   *plci, APPL   *appl, byte hook_listen)
+{
+  word j;
+  PLCI   *splci;
+
+  /* check if hardware supports handset with hook states (adv.codec) */
+  /* or if just a on board codec is supported                        */
+  /* the advanced codec plci is just for internal use                */
+
+  /* diva Pro with on-board codec:                                   */
+  if(a->profile.Global_Options & HANDSET)
+  {
+    /* new call, but hook states are already signalled */
+    if(a->AdvCodecFLAG)
+    {
+      if(a->AdvSignalAppl!=appl || a->AdvSignalPLCI)
+      {
+        dbug(1,dprintf("AdvSigPlci=0x%x",a->AdvSignalPLCI));
+        return 0x2001; /* codec in use by another application */
+      }
+      if(plci!=0)
+      {
+        a->AdvSignalPLCI = plci;
+        plci->tel=ADV_VOICE;
+      }
+      return 0;                      /* adv codec still used */
+    }
+    if((j=get_plci(a)))
+    {
+      splci = &a->plci[j-1];
+      splci->tel = CODEC_PERMANENT;
+      /* hook_listen indicates if a facility_req with handset/hook support */
+      /* was sent. Otherwise if just a call on an external device was made */
+      /* the codec will be used but the hook info will be discarded (just  */
+      /* the external controller is in use                                 */
+      if(hook_listen) splci->State = ADVANCED_VOICE_SIG;
+      else
+      {
+        splci->State = ADVANCED_VOICE_NOSIG;
+        if(plci)
+        {
+          plci->spoofed_msg = SPOOFING_REQUIRED;
+        }
+                                               /* indicate D-ch connect if  */
+      }                                        /* codec is connected OK     */
+      if(plci!=0)
+      {
+        a->AdvSignalPLCI = plci;
+        plci->tel=ADV_VOICE;
+      }
+      a->AdvSignalAppl = appl;
+      a->AdvCodecFLAG = TRUE;
+      a->AdvCodecPLCI = splci;
+      add_p(splci,CAI,"\x01\x15");
+      add_p(splci,LLI,"\x01\x00");
+      add_p(splci,ESC,"\x02\x18\x00");
+      add_p(splci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+      splci->internal_command = PERM_COD_ASSIGN;
+      dbug(1,dprintf("Codec Assign"));
+      sig_req(splci,ASSIGN,DSIG_ID);
+      send_req(splci);
+    }
+    else
+    {
+      return 0x2001; /* wrong state, no more plcis */
+    }
+  }
+  else if(a->profile.Global_Options & ON_BOARD_CODEC)
+  {
+    if(hook_listen) return 0x300B;               /* Facility not supported */
+                                                 /* no hook with SCOM      */
+    if(plci!=0) plci->tel = CODEC;
+    dbug(1,dprintf("S/SCOM codec"));
+    /* first time we use the scom-s codec we must shut down the internal   */
+    /* handset application of the card. This can be done by an assign with */
+    /* a cai with the 0x80 bit set. Assign return code is 'out of resource'*/
+    if(!a->scom_appl_disable){
+      if((j=get_plci(a))) {
+        splci = &a->plci[j-1];
+        add_p(splci,CAI,"\x01\x80");
+        add_p(splci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+        sig_req(splci,ASSIGN,0xC0);  /* 0xc0 is the TEL_ID */
+        send_req(splci);
+        a->scom_appl_disable = TRUE;
+      }
+      else{
+        return 0x2001; /* wrong state, no more plcis */
+      }
+    }
+  }
+  else return 0x300B;               /* Facility not supported */
+
+  return 0;
+}
+
+
+void CodecIdCheck(DIVA_CAPI_ADAPTER   *a, PLCI   *plci)
+{
+
+  dbug(1,dprintf("CodecIdCheck"));
+
+  if(a->AdvSignalPLCI == plci)
+  {
+    dbug(1,dprintf("PLCI owns codec"));
+    VoiceChannelOff(a->AdvCodecPLCI);
+    if(a->AdvCodecPLCI->State == ADVANCED_VOICE_NOSIG)
+    {
+      dbug(1,dprintf("remove temp codec PLCI"));
+      plci_remove(a->AdvCodecPLCI);
+      a->AdvCodecFLAG  = 0;
+      a->AdvCodecPLCI  = NULL;
+      a->AdvSignalAppl = NULL;
+    }
+    a->AdvSignalPLCI = NULL;
+  }
+}
+
+/* -------------------------------------------------------------------
+    Ask for physical address of card on PCI bus
+   ------------------------------------------------------------------- */
+static void diva_ask_for_xdi_sdram_bar (DIVA_CAPI_ADAPTER  * a,
+                                        IDI_SYNC_REQ  * preq) {
+  a->sdram_bar = 0;
+  if (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR) {
+    ENTITY   * e = (ENTITY   *)preq;
+
+    e->user[0] = a->Id - 1;
+    preq->xdi_sdram_bar.info.bar    = 0;
+    preq->xdi_sdram_bar.Req         = 0;
+    preq->xdi_sdram_bar.Rc           = IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR;
+
+    (*(a->request))(e);
+
+    a->sdram_bar = preq->xdi_sdram_bar.info.bar;
+    dbug(3,dprintf("A(%d) SDRAM BAR = %08x", a->Id, a->sdram_bar));
+  }
+}
+
+/* -------------------------------------------------------------------
+     Ask XDI about extended features
+   ------------------------------------------------------------------- */
+static void diva_get_extended_adapter_features (DIVA_CAPI_ADAPTER  * a) {
+  IDI_SYNC_REQ   * preq;
+    char buffer[              ((sizeof(preq->xdi_extended_features)+4) > sizeof(ENTITY)) ?                     (sizeof(preq->xdi_extended_features)+4) : sizeof(ENTITY)];
+
+    char features[4];
+  preq = (IDI_SYNC_REQ   *)&buffer[0];
+
+  if (!diva_xdi_extended_features) {
+    ENTITY   * e = (ENTITY   *)preq;
+    diva_xdi_extended_features |= 0x80000000;
+
+    e->user[0] = a->Id - 1;
+    preq->xdi_extended_features.Req = 0;
+    preq->xdi_extended_features.Rc  = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES;
+    preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features);
+    preq->xdi_extended_features.info.features = &features[0];
+
+    (*(a->request))(e);
+
+    if (features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) {
+      /*
+         Check features located in the byte '0'
+         */
+      if (features[0] & DIVA_XDI_EXTENDED_FEATURE_CMA) {
+        diva_xdi_extended_features |= DIVA_CAPI_USE_CMA;
+      }
+      if (features[0] & DIVA_XDI_EXTENDED_FEATURE_RX_DMA) {
+        diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_RX_DMA;
+        dbug(1,dprintf("XDI provides RxDMA"));
+      }
+      if (features[0] & DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR) {
+        diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR;
+      }
+      if (features[0] & DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC) {
+        diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_NO_CANCEL;
+        dbug(3,dprintf("XDI provides NO_CANCEL_RC feature"));
+      }
+
+    }
+  }
+
+  diva_ask_for_xdi_sdram_bar (a, preq);
+}
+
+/*------------------------------------------------------------------*/
+/* automatic law                                                    */
+/*------------------------------------------------------------------*/
+/* called from OS specific part after init time to get the Law              */
+/* a-law (Euro) and u-law (us,japan) use different BCs in the Setup message */
+void AutomaticLaw(DIVA_CAPI_ADAPTER   *a)
+{
+  word j;
+  PLCI   *splci;
+
+  if(a->automatic_law) {
+    return;
+  }
+  if((j=get_plci(a))) {
+    diva_get_extended_adapter_features (a);
+    splci = &a->plci[j-1];
+    a->automatic_lawPLCI = splci;
+    a->automatic_law = 1;
+    add_p(splci,CAI,"\x01\x80");
+    add_p(splci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+    splci->internal_command = USELAW_REQ;
+    splci->command = 0;
+    splci->number = 0;
+    sig_req(splci,ASSIGN,DSIG_ID);
+    send_req(splci);
+  }
+}
+
+/* called from OS specific part if an application sends an Capi20Release */
+word CapiRelease(word Id)
+{
+  word i, j, appls_found;
+  PLCI   *plci;
+  APPL   *this;
+  DIVA_CAPI_ADAPTER   *a;
+
+  if (!Id)
+  {
+    dbug(0,dprintf("A: CapiRelease(Id==0)"));
+    return (_WRONG_APPL_ID);
+  }
+
+  this = &application[Id-1];               /* get application pointer */
+
+  for(i=0,appls_found=0; i<max_appl; i++)
+  {
+    if(application[i].Id)       /* an application has been found        */
+    {
+      appls_found++;
+    }
+  }
+
+  for(i=0; i<max_adapter; i++)             /* scan all adapters...    */
+  {
+    a = &adapter[i];
+    if (a->request)
+    {
+      a->Info_Mask[Id-1] = 0;
+      a->CIP_Mask[Id-1] = 0;
+      a->Notification_Mask[Id-1] = 0;
+      a->codec_listen[Id-1] = NULL;
+      a->requested_options_table[Id-1] = 0;
+      for(j=0; j<a->max_plci; j++)           /* and all PLCIs connected */
+      {                                      /* with this application   */
+        plci = &a->plci[j];
+        if(plci->Id)                         /* if plci owns no application */
+        {                                    /* it may be not jet connected */
+          if(plci->State==INC_CON_PENDING
+          || plci->State==INC_CON_ALERT)
+          {
+            if(test_c_ind_mask_bit (plci, (word)(Id-1)))
+            {
+              clear_c_ind_mask_bit (plci, (word)(Id-1));
+              if(c_ind_mask_empty (plci))
+              {
+                sig_req(plci,HANGUP,0);
+                send_req(plci);
+                plci->State = OUTG_DIS_PENDING;
+              }
+            }
+          }
+          if(test_c_ind_mask_bit (plci, (word)(Id-1)))
+          {
+            clear_c_ind_mask_bit (plci, (word)(Id-1));
+            if(c_ind_mask_empty (plci))
+            {
+              if(!plci->appl)
+              {
+                plci_remove(plci);
+                plci->State = IDLE;
+              }
+            }
+          }
+          if(plci->appl==this)
+          {
+            plci->appl = NULL;
+            plci_remove(plci);
+            plci->State = IDLE;
+          }
+        }
+      }
+      listen_check(a);
+
+      if(a->flag_dynamic_l1_down)
+      {
+        if(appls_found==1)            /* last application does a capi release */
+        {
+          if((j=get_plci(a)))
+          {
+            plci = &a->plci[j-1];
+            plci->command = 0;
+            add_p(plci,OAD,"\x01\xfd");
+            add_p(plci,CAI,"\x01\x80");
+            add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+            add_p(plci,SHIFT|6,NULL);
+            add_p(plci,SIN,"\x02\x00\x00");
+            plci->internal_command = REM_L1_SIG_ASSIGN_PEND;
+            sig_req(plci,ASSIGN,DSIG_ID);
+            add_p(plci,FTY,"\x02\xff\x06"); /* l1 down */
+            sig_req(plci,SIG_CTRL,0);
+            send_req(plci);
+          }
+        }
+      }
+      if(a->AdvSignalAppl==this)
+      {
+        this->NullCREnable = FALSE;
+        if (a->AdvCodecPLCI)
+        {
+          plci_remove(a->AdvCodecPLCI);
+          a->AdvCodecPLCI->tel = 0;
+          a->AdvCodecPLCI->adv_nl = 0;
+        }
+        a->AdvSignalAppl = NULL;
+        a->AdvSignalPLCI = NULL;
+        a->AdvCodecFLAG = 0;
+        a->AdvCodecPLCI = NULL;
+      }
+    }
+  }
+
+  this->Id = 0;
+
+  return GOOD;
+}
+
+static word plci_remove_check(PLCI   *plci)
+{
+  if(!plci) return TRUE;
+  if(!plci->NL.Id && c_ind_mask_empty (plci))
+  {
+    if(plci->Sig.Id == 0xff)
+      plci->Sig.Id = 0;
+    if(!plci->Sig.Id)
+    {
+      dbug(1,dprintf("plci_remove_complete(%x)",plci->Id));
+      dbug(1,dprintf("tel=0x%x,Sig=0x%x",plci->tel,plci->Sig.Id));
+      if (plci->Id)
+      {
+        CodecIdCheck(plci->adapter, plci);
+        clear_b1_config (plci);
+        ncci_remove (plci, 0, FALSE);
+        plci_free_msg_in_queue (plci);
+        channel_flow_control_remove (plci);
+        plci->Id = 0;
+        plci->State = IDLE;
+        plci->channels = 0;
+        plci->appl = NULL;
+        plci->notifiedcall = 0;
+      }
+      listen_check(plci->adapter);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+
+/*------------------------------------------------------------------*/
+
+static byte plci_nl_busy (PLCI   *plci)
+{
+  /* only applicable for non-multiplexed protocols */
+  return (plci->nl_req
+    || (plci->ncci_ring_list
+     && plci->adapter->ncci_ch[plci->ncci_ring_list]
+     && (plci->adapter->ch_flow_control[plci->adapter->ncci_ch[plci->ncci_ring_list]] & N_OK_FC_PENDING)));
+}
+
+
+/*------------------------------------------------------------------*/
+/* DTMF facilities                                                  */
+/*------------------------------------------------------------------*/
+
+
+static struct
+{
+  byte send_mask;
+  byte listen_mask;
+  byte character;
+  byte code;
+} dtmf_digit_map[] =
+{
+  { 0x01, 0x01, 0x23, DTMF_DIGIT_TONE_CODE_HASHMARK },
+  { 0x01, 0x01, 0x2a, DTMF_DIGIT_TONE_CODE_STAR },
+  { 0x01, 0x01, 0x30, DTMF_DIGIT_TONE_CODE_0 },
+  { 0x01, 0x01, 0x31, DTMF_DIGIT_TONE_CODE_1 },
+  { 0x01, 0x01, 0x32, DTMF_DIGIT_TONE_CODE_2 },
+  { 0x01, 0x01, 0x33, DTMF_DIGIT_TONE_CODE_3 },
+  { 0x01, 0x01, 0x34, DTMF_DIGIT_TONE_CODE_4 },
+  { 0x01, 0x01, 0x35, DTMF_DIGIT_TONE_CODE_5 },
+  { 0x01, 0x01, 0x36, DTMF_DIGIT_TONE_CODE_6 },
+  { 0x01, 0x01, 0x37, DTMF_DIGIT_TONE_CODE_7 },
+  { 0x01, 0x01, 0x38, DTMF_DIGIT_TONE_CODE_8 },
+  { 0x01, 0x01, 0x39, DTMF_DIGIT_TONE_CODE_9 },
+  { 0x01, 0x01, 0x41, DTMF_DIGIT_TONE_CODE_A },
+  { 0x01, 0x01, 0x42, DTMF_DIGIT_TONE_CODE_B },
+  { 0x01, 0x01, 0x43, DTMF_DIGIT_TONE_CODE_C },
+  { 0x01, 0x01, 0x44, DTMF_DIGIT_TONE_CODE_D },
+  { 0x01, 0x00, 0x61, DTMF_DIGIT_TONE_CODE_A },
+  { 0x01, 0x00, 0x62, DTMF_DIGIT_TONE_CODE_B },
+  { 0x01, 0x00, 0x63, DTMF_DIGIT_TONE_CODE_C },
+  { 0x01, 0x00, 0x64, DTMF_DIGIT_TONE_CODE_D },
+
+  { 0x04, 0x04, 0x80, DTMF_SIGNAL_NO_TONE },
+  { 0x00, 0x04, 0x81, DTMF_SIGNAL_UNIDENTIFIED_TONE },
+  { 0x04, 0x04, 0x82, DTMF_SIGNAL_DIAL_TONE },
+  { 0x04, 0x04, 0x83, DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE },
+  { 0x04, 0x04, 0x84, DTMF_SIGNAL_SPECIAL_DIAL_TONE },
+  { 0x04, 0x04, 0x85, DTMF_SIGNAL_SECOND_DIAL_TONE },
+  { 0x04, 0x04, 0x86, DTMF_SIGNAL_RINGING_TONE },
+  { 0x04, 0x04, 0x87, DTMF_SIGNAL_SPECIAL_RINGING_TONE },
+  { 0x04, 0x04, 0x88, DTMF_SIGNAL_BUSY_TONE },
+  { 0x04, 0x04, 0x89, DTMF_SIGNAL_CONGESTION_TONE },
+  { 0x04, 0x04, 0x8a, DTMF_SIGNAL_SPECIAL_INFORMATION_TONE },
+  { 0x04, 0x04, 0x8b, DTMF_SIGNAL_COMFORT_TONE },
+  { 0x04, 0x04, 0x8c, DTMF_SIGNAL_HOLD_TONE },
+  { 0x04, 0x04, 0x8d, DTMF_SIGNAL_RECORD_TONE },
+  { 0x04, 0x04, 0x8e, DTMF_SIGNAL_CALLER_WAITING_TONE },
+  { 0x04, 0x04, 0x8f, DTMF_SIGNAL_CALL_WAITING_TONE },
+  { 0x04, 0x04, 0x90, DTMF_SIGNAL_PAY_TONE },
+  { 0x04, 0x04, 0x91, DTMF_SIGNAL_POSITIVE_INDICATION_TONE },
+  { 0x04, 0x04, 0x92, DTMF_SIGNAL_NEGATIVE_INDICATION_TONE },
+  { 0x04, 0x04, 0x93, DTMF_SIGNAL_WARNING_TONE },
+  { 0x04, 0x04, 0x94, DTMF_SIGNAL_INTRUSION_TONE },
+  { 0x04, 0x04, 0x95, DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE },
+  { 0x04, 0x04, 0x96, DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE },
+  { 0x04, 0x04, 0x97, DTMF_SIGNAL_CPE_ALERTING_SIGNAL },
+  { 0x04, 0x04, 0x98, DTMF_SIGNAL_OFF_HOOK_WARNING_TONE },
+  { 0x04, 0x04, 0xbf, DTMF_SIGNAL_INTERCEPT_TONE },
+  { 0x04, 0x04, 0xc0, DTMF_SIGNAL_MODEM_CALLING_TONE },
+  { 0x04, 0x04, 0xc1, DTMF_SIGNAL_FAX_CALLING_TONE },
+  { 0x04, 0x04, 0xc2, DTMF_SIGNAL_ANSWER_TONE },
+  { 0x04, 0x04, 0xc3, DTMF_SIGNAL_REVERSED_ANSWER_TONE },
+  { 0x04, 0x04, 0xc4, DTMF_SIGNAL_ANSAM_TONE },
+  { 0x04, 0x04, 0xc5, DTMF_SIGNAL_REVERSED_ANSAM_TONE },
+  { 0x04, 0x04, 0xc6, DTMF_SIGNAL_BELL103_ANSWER_TONE },
+  { 0x04, 0x04, 0xc7, DTMF_SIGNAL_FAX_FLAGS },
+  { 0x04, 0x04, 0xc8, DTMF_SIGNAL_G2_FAX_GROUP_ID },
+  { 0x00, 0x04, 0xc9, DTMF_SIGNAL_HUMAN_SPEECH },
+  { 0x04, 0x04, 0xca, DTMF_SIGNAL_ANSWERING_MACHINE_390 },
+  { 0x02, 0x02, 0xf1, DTMF_MF_DIGIT_TONE_CODE_1 },
+  { 0x02, 0x02, 0xf2, DTMF_MF_DIGIT_TONE_CODE_2 },
+  { 0x02, 0x02, 0xf3, DTMF_MF_DIGIT_TONE_CODE_3 },
+  { 0x02, 0x02, 0xf4, DTMF_MF_DIGIT_TONE_CODE_4 },
+  { 0x02, 0x02, 0xf5, DTMF_MF_DIGIT_TONE_CODE_5 },
+  { 0x02, 0x02, 0xf6, DTMF_MF_DIGIT_TONE_CODE_6 },
+  { 0x02, 0x02, 0xf7, DTMF_MF_DIGIT_TONE_CODE_7 },
+  { 0x02, 0x02, 0xf8, DTMF_MF_DIGIT_TONE_CODE_8 },
+  { 0x02, 0x02, 0xf9, DTMF_MF_DIGIT_TONE_CODE_9 },
+  { 0x02, 0x02, 0xfa, DTMF_MF_DIGIT_TONE_CODE_0 },
+  { 0x02, 0x02, 0xfb, DTMF_MF_DIGIT_TONE_CODE_K1 },
+  { 0x02, 0x02, 0xfc, DTMF_MF_DIGIT_TONE_CODE_K2 },
+  { 0x02, 0x02, 0xfd, DTMF_MF_DIGIT_TONE_CODE_KP },
+  { 0x02, 0x02, 0xfe, DTMF_MF_DIGIT_TONE_CODE_S1 },
+  { 0x02, 0x02, 0xff, DTMF_MF_DIGIT_TONE_CODE_ST },
+
+};
+
+#define DTMF_DIGIT_MAP_ENTRIES (sizeof(dtmf_digit_map) / sizeof(dtmf_digit_map[0]))
+
+
+static void dtmf_enable_receiver (PLCI   *plci, byte enable_mask)
+{
+  word min_digit_duration, min_gap_duration;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_enable_receiver %02x",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, enable_mask));
+
+  if (enable_mask != 0)
+  {
+    min_digit_duration = (plci->dtmf_rec_pulse_ms == 0) ? 40 : plci->dtmf_rec_pulse_ms;
+    min_gap_duration = (plci->dtmf_rec_pause_ms == 0) ? 40 : plci->dtmf_rec_pause_ms;
+    plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_ENABLE_RECEIVER;
+    PUT_WORD (&plci->internal_req_buffer[1], min_digit_duration);
+    PUT_WORD (&plci->internal_req_buffer[3], min_gap_duration);
+    plci->NData[0].PLength = 5;
+
+    PUT_WORD (&plci->internal_req_buffer[5], INTERNAL_IND_BUFFER_SIZE);
+    plci->NData[0].PLength += 2;
+    capidtmf_recv_enable (&(plci->capidtmf_state), min_digit_duration, min_gap_duration);
+
+  }
+  else
+  {
+    plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_DISABLE_RECEIVER;
+    plci->NData[0].PLength = 1;
+
+    capidtmf_recv_disable (&(plci->capidtmf_state));
+
+  }
+  plci->NData[0].P = plci->internal_req_buffer;
+  plci->NL.X = plci->NData;
+  plci->NL.ReqCh = 0;
+  plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+  plci->adapter->request (&plci->NL);
+}
+
+
+static void dtmf_send_digits (PLCI   *plci, byte   *digit_buffer, word digit_count)
+{
+  word w, i;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_send_digits %d",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, digit_count));
+
+  plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_SEND_DIGITS;
+  w = (plci->dtmf_send_pulse_ms == 0) ? 40 : plci->dtmf_send_pulse_ms;
+  PUT_WORD (&plci->internal_req_buffer[1], w);
+  w = (plci->dtmf_send_pause_ms == 0) ? 40 : plci->dtmf_send_pause_ms;
+  PUT_WORD (&plci->internal_req_buffer[3], w);
+  for (i = 0; i < digit_count; i++)
+  {
+    w = 0;
+    while ((w < DTMF_DIGIT_MAP_ENTRIES)
+      && (digit_buffer[i] != dtmf_digit_map[w].character))
+    {
+      w++;
+    }
+    plci->internal_req_buffer[5+i] = (w < DTMF_DIGIT_MAP_ENTRIES) ?
+      dtmf_digit_map[w].code : DTMF_DIGIT_TONE_CODE_STAR;
+  }
+  plci->NData[0].PLength = 5 + digit_count;
+  plci->NData[0].P = plci->internal_req_buffer;
+  plci->NL.X = plci->NData;
+  plci->NL.ReqCh = 0;
+  plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+  plci->adapter->request (&plci->NL);
+}
+
+
+static void dtmf_rec_clear_config (PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_rec_clear_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  plci->dtmf_rec_active = 0;
+  plci->dtmf_rec_pulse_ms = 0;
+  plci->dtmf_rec_pause_ms = 0;
+
+  capidtmf_init (&(plci->capidtmf_state), plci->adapter->u_law);
+
+}
+
+
+static void dtmf_send_clear_config (PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_send_clear_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  plci->dtmf_send_requests = 0;
+  plci->dtmf_send_pulse_ms = 0;
+  plci->dtmf_send_pause_ms = 0;
+}
+
+
+static void dtmf_prepare_switch (dword Id, PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_prepare_switch",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  while (plci->dtmf_send_requests != 0)
+    dtmf_confirmation (Id, plci);
+}
+
+
+static word dtmf_save_config (dword Id, PLCI   *plci, byte Rc)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_save_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  return (GOOD);
+}
+
+
+static word dtmf_restore_config (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_restore_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  Info = GOOD;
+  if (plci->B1_facilities & B1_FACILITY_DTMFR)
+  {
+    switch (plci->adjust_b_state)
+    {
+    case ADJUST_B_RESTORE_DTMF_1:
+      plci->internal_command = plci->adjust_b_command;
+      if (plci_nl_busy (plci))
+      {
+        plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+        break;
+      }
+      dtmf_enable_receiver (plci, plci->dtmf_rec_active);
+      plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_2;
+      break;
+    case ADJUST_B_RESTORE_DTMF_2:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Reenable DTMF receiver failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _WRONG_STATE;
+        break;
+      }
+      break;
+    }
+  }
+  return (Info);
+}
+
+
+static void dtmf_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word internal_command, Info;
+  byte mask;
+    byte result[4];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_command %02x %04x %04x %d %d %d %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command,
+    plci->dtmf_cmd, plci->dtmf_rec_pulse_ms, plci->dtmf_rec_pause_ms,
+    plci->dtmf_send_pulse_ms, plci->dtmf_send_pause_ms));
+
+  Info = GOOD;
+  result[0] = 2;
+  PUT_WORD (&result[1], DTMF_SUCCESS);
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  mask = 0x01;
+  switch (plci->dtmf_cmd)
+  {
+
+  case DTMF_LISTEN_TONE_START:
+    mask <<= 1;
+  case DTMF_LISTEN_MF_START:
+    mask <<= 1;
+
+  case DTMF_LISTEN_START:
+    switch (internal_command)
+    {
+    default:
+      adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+        B1_FACILITY_DTMFR), DTMF_COMMAND_1);
+    case DTMF_COMMAND_1:
+      if (adjust_b_process (Id, plci, Rc) != GOOD)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Load DTMF failed",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      if (plci->internal_command)
+        return;
+    case DTMF_COMMAND_2:
+      if (plci_nl_busy (plci))
+      {
+        plci->internal_command = DTMF_COMMAND_2;
+        return;
+      }
+      plci->internal_command = DTMF_COMMAND_3;
+      dtmf_enable_receiver (plci, (byte)(plci->dtmf_rec_active | mask));
+      return;
+    case DTMF_COMMAND_3:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Enable DTMF receiver failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+
+      plci->tone_last_indication_code = DTMF_SIGNAL_NO_TONE;
+
+      plci->dtmf_rec_active |= mask;
+      break;
+    }
+    break;
+
+
+  case DTMF_LISTEN_TONE_STOP:
+    mask <<= 1;
+  case DTMF_LISTEN_MF_STOP:
+    mask <<= 1;
+
+  case DTMF_LISTEN_STOP:
+    switch (internal_command)
+    {
+    default:
+      plci->dtmf_rec_active &= ~mask;
+      if (plci->dtmf_rec_active)
+        break;
+/*
+    case DTMF_COMMAND_1:
+      if (plci->dtmf_rec_active)
+      {
+        if (plci_nl_busy (plci))
+        {
+          plci->internal_command = DTMF_COMMAND_1;
+          return;
+        }
+        plci->dtmf_rec_active &= ~mask;
+        plci->internal_command = DTMF_COMMAND_2;
+        dtmf_enable_receiver (plci, FALSE);
+        return;
+      }
+      Rc = OK;
+    case DTMF_COMMAND_2:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Disable DTMF receiver failed %02x",
+          UnMapId (Id), (char far *)(FILE_), __LINE__, Rc));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+*/
+      adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities &
+        ~(B1_FACILITY_DTMFX | B1_FACILITY_DTMFR)), DTMF_COMMAND_3);
+    case DTMF_COMMAND_3:
+      if (adjust_b_process (Id, plci, Rc) != GOOD)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Unload DTMF failed",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      if (plci->internal_command)
+        return;
+      break;
+    }
+    break;
+
+
+  case DTMF_SEND_TONE:
+    mask <<= 1;
+  case DTMF_SEND_MF:
+    mask <<= 1;
+
+  case DTMF_DIGITS_SEND:
+    switch (internal_command)
+    {
+    default:
+      adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+        ((plci->dtmf_parameter_length != 0) ? B1_FACILITY_DTMFX | B1_FACILITY_DTMFR : B1_FACILITY_DTMFX)),
+        DTMF_COMMAND_1);
+    case DTMF_COMMAND_1:
+      if (adjust_b_process (Id, plci, Rc) != GOOD)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Load DTMF failed",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      if (plci->internal_command)
+        return;
+    case DTMF_COMMAND_2:
+      if (plci_nl_busy (plci))
+      {
+        plci->internal_command = DTMF_COMMAND_2;
+        return;
+      }
+      plci->dtmf_msg_number_queue[(plci->dtmf_send_requests)++] = plci->number;
+      plci->internal_command = DTMF_COMMAND_3;
+      dtmf_send_digits (plci, &plci->saved_msg.parms[3].info[1], plci->saved_msg.parms[3].length);
+      return;
+    case DTMF_COMMAND_3:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Send DTMF digits failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        if (plci->dtmf_send_requests != 0)
+          (plci->dtmf_send_requests)--;
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      return;
+    }
+    break;
+  }
+  sendf (plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
+    "wws", Info, SELECTOR_DTMF, result);
+}
+
+
+static byte dtmf_request (dword Id, word Number, DIVA_CAPI_ADAPTER   *a, PLCI   *plci, APPL   *appl, API_PARSE *msg)
+{
+  word Info;
+  word i, j;
+  byte mask;
+    API_PARSE dtmf_parms[5];
+    byte result[40];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_request",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  Info = GOOD;
+  result[0] = 2;
+  PUT_WORD (&result[1], DTMF_SUCCESS);
+  if (!(a->profile.Global_Options & GL_DTMF_SUPPORTED))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    Info = _FACILITY_NOT_SUPPORTED;
+  }
+  else if (api_parse (&msg[1].info[1], msg[1].length, "w", dtmf_parms))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    Info = _WRONG_MESSAGE_FORMAT;
+  }
+
+  else if ((GET_WORD (dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
+    || (GET_WORD (dtmf_parms[0].info) == DTMF_GET_SUPPORTED_SEND_CODES))
+  {
+    if (!((a->requested_options_table[appl->Id-1])
+        & (1L << PRIVATE_DTMF_TONE)))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, GET_WORD (dtmf_parms[0].info)));
+      PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+    }
+    else
+    {
+      for (i = 0; i < 32; i++)
+        result[4 + i] = 0;
+      if (GET_WORD (dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
+      {
+        for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
+        {
+          if (dtmf_digit_map[i].listen_mask != 0)
+            result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
+        }
+      }
+      else
+      {
+        for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
+        {
+          if (dtmf_digit_map[i].send_mask != 0)
+            result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
+        }
+      }
+      result[0] = 3 + 32;
+      result[3] = 32;
+    }
+  }
+
+  else if (plci == NULL)
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    Info = _WRONG_IDENTIFIER;
+  }
+  else
+  {
+    if (!plci->State
+     || !plci->NL.Id || plci->nl_remove_id)
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+        UnMapId (Id), (char   *)(FILE_), __LINE__));
+      Info = _WRONG_STATE;
+    }
+    else
+    {
+      plci->command = 0;
+      plci->dtmf_cmd = GET_WORD (dtmf_parms[0].info);
+      mask = 0x01;
+      switch (plci->dtmf_cmd)
+      {
+
+      case DTMF_LISTEN_TONE_START:
+      case DTMF_LISTEN_TONE_STOP:
+        mask <<= 1;
+      case DTMF_LISTEN_MF_START:
+      case DTMF_LISTEN_MF_STOP:
+        mask <<= 1;
+        if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id-1])
+          & (1L << PRIVATE_DTMF_TONE)))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+            UnMapId (Id), (char   *)(FILE_), __LINE__, GET_WORD (dtmf_parms[0].info)));
+          PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+          break;
+        }
+
+      case DTMF_LISTEN_START:
+      case DTMF_LISTEN_STOP:
+        if (!(a->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
+         && !(a->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _FACILITY_NOT_SUPPORTED;
+          break;
+        }
+        if (mask & DTMF_LISTEN_ACTIVE_FLAG)
+        {
+          if (api_parse (&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
+          {
+            plci->dtmf_rec_pulse_ms = 0;
+            plci->dtmf_rec_pause_ms = 0;
+          }
+          else
+          {
+            plci->dtmf_rec_pulse_ms = GET_WORD (dtmf_parms[1].info);
+            plci->dtmf_rec_pause_ms = GET_WORD (dtmf_parms[2].info);
+          }
+        }
+        start_internal_command (Id, plci, dtmf_command);
+        return (FALSE);
+
+
+      case DTMF_SEND_TONE:
+        mask <<= 1;
+      case DTMF_SEND_MF:
+        mask <<= 1;
+        if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id-1])
+          & (1L << PRIVATE_DTMF_TONE)))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+            UnMapId (Id), (char   *)(FILE_), __LINE__, GET_WORD (dtmf_parms[0].info)));
+          PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+          break;
+        }
+
+      case DTMF_DIGITS_SEND:
+        if (api_parse (&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_MESSAGE_FORMAT;
+          break;
+        }
+        if (mask & DTMF_LISTEN_ACTIVE_FLAG)
+        {
+          plci->dtmf_send_pulse_ms = GET_WORD (dtmf_parms[1].info);
+          plci->dtmf_send_pause_ms = GET_WORD (dtmf_parms[2].info);
+        }
+        i = 0;
+        j = 0;
+        while ((i < dtmf_parms[3].length) && (j < DTMF_DIGIT_MAP_ENTRIES))
+        {
+          j = 0;
+          while ((j < DTMF_DIGIT_MAP_ENTRIES)
+            && ((dtmf_parms[3].info[i+1] != dtmf_digit_map[j].character)
+             || ((dtmf_digit_map[j].send_mask & mask) == 0)))
+          {
+            j++;
+          }
+          i++;
+        }
+        if (j == DTMF_DIGIT_MAP_ENTRIES)
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Incorrect DTMF digit %02x",
+            UnMapId (Id), (char   *)(FILE_), __LINE__, dtmf_parms[3].info[i]));
+          PUT_WORD (&result[1], DTMF_INCORRECT_DIGIT);
+          break;
+        }
+        if (plci->dtmf_send_requests >=
+          sizeof(plci->dtmf_msg_number_queue) / sizeof(plci->dtmf_msg_number_queue[0]))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: DTMF request overrun",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_STATE;
+          break;
+        }
+        api_save_msg (dtmf_parms, "wwws", &plci->saved_msg);
+        start_internal_command (Id, plci, dtmf_command);
+        return (FALSE);
+
+      default:
+        dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, plci->dtmf_cmd));
+        PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+      }
+    }
+  }
+  sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+    "wws", Info, SELECTOR_DTMF, result);
+  return (FALSE);
+}
+
+
+static void dtmf_confirmation (dword Id, PLCI   *plci)
+{
+  word Info;
+  word i;
+    byte result[4];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_confirmation",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  Info = GOOD;
+  result[0] = 2;
+  PUT_WORD (&result[1], DTMF_SUCCESS);
+  if (plci->dtmf_send_requests != 0)
+  {
+    sendf (plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->dtmf_msg_number_queue[0],
+      "wws", GOOD, SELECTOR_DTMF, result);
+    (plci->dtmf_send_requests)--;
+    for (i = 0; i < plci->dtmf_send_requests; i++)
+      plci->dtmf_msg_number_queue[i] = plci->dtmf_msg_number_queue[i+1];      
+  }
+}
+
+
+static void dtmf_indication (dword Id, PLCI   *plci, byte   *msg, word length)
+{
+  word i, j, n;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_indication",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  n = 0;
+  for (i = 1; i < length; i++)
+  {
+    j = 0;
+    while ((j < DTMF_DIGIT_MAP_ENTRIES)
+      && ((msg[i] != dtmf_digit_map[j].code)
+       || ((dtmf_digit_map[j].listen_mask & plci->dtmf_rec_active) == 0)))
+    {
+      j++;
+    }
+    if (j < DTMF_DIGIT_MAP_ENTRIES)
+    {
+
+      if ((dtmf_digit_map[j].listen_mask & DTMF_TONE_LISTEN_ACTIVE_FLAG)
+       && (plci->tone_last_indication_code == DTMF_SIGNAL_NO_TONE)
+       && (dtmf_digit_map[j].character != DTMF_SIGNAL_UNIDENTIFIED_TONE))
+      {
+        if (n + 1 == i)
+        {
+          for (i = length; i > n + 1; i--)
+            msg[i] = msg[i - 1];
+          length++;
+          i++;
+        }
+        msg[++n] = DTMF_SIGNAL_UNIDENTIFIED_TONE;
+      }
+      plci->tone_last_indication_code = dtmf_digit_map[j].character;
+
+      msg[++n] = dtmf_digit_map[j].character;
+    }
+  }
+  if (n != 0)
+  {
+    msg[0] = (byte) n;
+    sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "wS", SELECTOR_DTMF, msg);
+  }
+}
+
+
+/*------------------------------------------------------------------*/
+/* DTMF parameters                                                  */
+/*------------------------------------------------------------------*/
+
+static void dtmf_parameter_write (PLCI   *plci)
+{
+  word i;
+    byte parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE + 2];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_write",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  parameter_buffer[0] = plci->dtmf_parameter_length + 1;
+  parameter_buffer[1] = DSP_CTRL_SET_DTMF_PARAMETERS;
+  for (i = 0; i < plci->dtmf_parameter_length; i++)
+    parameter_buffer[2+i] = plci->dtmf_parameter_buffer[i];
+  add_p (plci, FTY, parameter_buffer);
+  sig_req (plci, TEL_CTRL, 0);
+  send_req (plci);
+}
+
+
+static void dtmf_parameter_clear_config (PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_clear_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  plci->dtmf_parameter_length = 0;
+}
+
+
+static void dtmf_parameter_prepare_switch (dword Id, PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_prepare_switch",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+}
+
+
+static word dtmf_parameter_save_config (dword Id, PLCI   *plci, byte Rc)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_save_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  return (GOOD);
+}
+
+
+static word dtmf_parameter_restore_config (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_restore_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  Info = GOOD;
+  if ((plci->B1_facilities & B1_FACILITY_DTMFR)
+   && (plci->dtmf_parameter_length != 0))
+  {
+    switch (plci->adjust_b_state)
+    {
+    case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
+      plci->internal_command = plci->adjust_b_command;
+      if (plci->sig_req)
+      {
+        plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
+        break;
+      }
+      dtmf_parameter_write (plci);
+      plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_2;
+      break;
+    case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Restore DTMF parameters failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _WRONG_STATE;
+        break;
+      }
+      break;
+    }
+  }
+  return (Info);
+}
+
+
+/*------------------------------------------------------------------*/
+/* Line interconnect facilities                                     */
+/*------------------------------------------------------------------*/
+
+
+LI_CONFIG   *li_config_table;
+word li_total_channels;
+
+
+/*------------------------------------------------------------------*/
+/* translate a CHI information element to a channel number          */
+/* returns 0xff - any channel                                       */
+/*         0xfe - chi wrong coding                                  */
+/*         0xfd - D-channel                                         */
+/*         0x00 - no channel                                        */
+/*         else channel number / PRI: timeslot                      */
+/* if channels is provided we accept more than one channel.         */
+/*------------------------------------------------------------------*/
+
+static byte chi_to_channel (byte   *chi, dword *pchannelmap)
+{
+  int p;
+  int i;
+  dword map;
+  byte excl;
+  byte ofs;
+  byte ch;
+
+  if (pchannelmap) *pchannelmap = 0;
+  if(!chi[0]) return 0xff;
+  excl = 0;
+
+  if(chi[1] & 0x20) {
+    if(chi[0]==1 && chi[1]==0xac) return 0xfd; /* exclusive d-channel */
+    for(i=1; i<chi[0] && !(chi[i] &0x80); i++);
+    if(i==chi[0] || !(chi[i] &0x80)) return 0xfe;
+    if((chi[1] |0xc8)!=0xe9) return 0xfe;
+    if(chi[1] &0x08) excl = 0x40;
+
+        /* int. id present */
+    if(chi[1] &0x40) {
+      p=i+1;
+      for(i=p; i<chi[0] && !(chi[i] &0x80); i++);
+      if(i==chi[0] || !(chi[i] &0x80)) return 0xfe;
+    }
+
+        /* coding standard, Number/Map, Channel Type */
+    p=i+1;
+    for(i=p; i<chi[0] && !(chi[i] &0x80); i++);
+    if(i==chi[0] || !(chi[i] &0x80)) return 0xfe;
+    if((chi[p]|0xd0)!=0xd3) return 0xfe;
+
+        /* Number/Map */
+    if(chi[p] &0x10) {
+
+        /* map */
+      if((chi[0]-p)==4) ofs = 0;
+      else if((chi[0]-p)==3) ofs = 1;
+      else return 0xfe;
+      ch = 0;
+      map = 0;
+      for(i=0; i<4 && p<chi[0]; i++) {
+        p++;
+        ch += 8;
+        map <<= 8;
+        if(chi[p]) {
+          for (ch=0; !(chi[p] & (1 << ch)); ch++);
+          map |= chi[p];
+        }
+      }
+      ch += ofs;
+      map <<= ofs;
+    }
+    else {
+
+        /* number */
+      p=i+1;
+      ch = chi[p] &0x3f;
+      if(pchannelmap) {
+        if((byte)(chi[0]-p)>30) return 0xfe;
+        map = 0;
+        for(i=p; i<=chi[0]; i++) {
+          if ((chi[i] &0x7f) > 31) return 0xfe;
+          map |= (1L << (chi[i] &0x7f));
+        }
+      }
+      else {
+        if(p!=chi[0]) return 0xfe;
+        if (ch > 31) return 0xfe;
+        map = (1L << ch);
+      }
+      if(chi[p] &0x40) return 0xfe;
+    }
+    if (pchannelmap) *pchannelmap = map;
+    else if (map != ((dword)(1L << ch))) return 0xfe;
+    return (byte)(excl | ch);
+  }
+  else {  /* not PRI */
+    for(i=1; i<chi[0] && !(chi[i] &0x80); i++);
+    if(i!=chi[0] || !(chi[i] &0x80)) return 0xfe;
+    if(chi[1] &0x08) excl = 0x40;
+
+    switch(chi[1] |0x98) {
+    case 0x98: return 0;
+    case 0x99:
+      if (pchannelmap) *pchannelmap = 2;
+      return excl |1;
+    case 0x9a:
+      if (pchannelmap) *pchannelmap = 4;
+      return excl |2;
+    case 0x9b: return 0xff;
+    case 0x9c: return 0xfd; /* d-ch */
+    default: return 0xfe;
+    }
+  }
+}
+
+
+static void mixer_set_bchannel_id_esc (PLCI   *plci, byte bchannel_id)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  PLCI   *splci;
+  byte old_id;
+
+  a = plci->adapter;
+  old_id = plci->li_bchannel_id;
+  if (a->li_pri)
+  {
+    if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+      li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+    plci->li_bchannel_id = (bchannel_id & 0x1f) + 1;
+    if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+      li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+  }
+  else
+  {
+    if (((bchannel_id & 0x03) == 1) || ((bchannel_id & 0x03) == 2))
+    {
+      if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+        li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+      plci->li_bchannel_id = bchannel_id & 0x03;
+      if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+      {
+        splci = a->AdvSignalPLCI;
+        if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
+        {
+          if ((splci->li_bchannel_id != 0)
+           && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
+          {
+            li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
+          }
+          splci->li_bchannel_id = 3 - plci->li_bchannel_id;
+          li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
+          dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id_esc %d",
+            (dword)((splci->Id << 8) | UnMapController (splci->adapter->Id)),
+            (char   *)(FILE_), __LINE__, splci->li_bchannel_id));
+        }
+      }
+      if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+        li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+    }
+  }
+  if ((old_id == 0) && (plci->li_bchannel_id != 0)
+   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+  {
+    mixer_clear_config (plci);
+  }
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_set_bchannel_id_esc %d %d",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, bchannel_id, plci->li_bchannel_id));
+}
+
+
+static void mixer_set_bchannel_id (PLCI   *plci, byte   *chi)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  PLCI   *splci;
+  byte ch, old_id;
+
+  a = plci->adapter;
+  old_id = plci->li_bchannel_id;
+  ch = chi_to_channel (chi, NULL);
+  if (!(ch & 0x80))
+  {
+    if (a->li_pri)
+    {
+      if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+        li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+      plci->li_bchannel_id = (ch & 0x1f) + 1;
+      if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+        li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+    }
+    else
+    {
+      if (((ch & 0x1f) == 1) || ((ch & 0x1f) == 2))
+      {
+        if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+          li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+        plci->li_bchannel_id = ch & 0x1f;
+        if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+        {
+          splci = a->AdvSignalPLCI;
+          if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
+          {
+            if ((splci->li_bchannel_id != 0)
+             && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
+            {
+              li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
+            }
+            splci->li_bchannel_id = 3 - plci->li_bchannel_id;
+            li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
+            dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
+              (dword)((splci->Id << 8) | UnMapController (splci->adapter->Id)),
+              (char   *)(FILE_), __LINE__, splci->li_bchannel_id));
+          }
+        }
+        if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+          li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+      }
+    }
+  }
+  if ((old_id == 0) && (plci->li_bchannel_id != 0)
+   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+  {
+    mixer_clear_config (plci);
+  }
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_set_bchannel_id %02x %d",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, ch, plci->li_bchannel_id));
+}
+
+
+#define MIXER_MAX_DUMP_CHANNELS 34
+
+static void mixer_calculate_coefs (DIVA_CAPI_ADAPTER   *a)
+{
+static char hex_digit_table[0x10] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+  word n, i, j;
+  char *p;
+    char hex_line[2 * MIXER_MAX_DUMP_CHANNELS + MIXER_MAX_DUMP_CHANNELS / 8 + 4];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_calculate_coefs",
+    (dword)(UnMapController (a->Id)), (char   *)(FILE_), __LINE__));
+
+  for (i = 0; i < li_total_channels; i++)
+  {
+    li_config_table[i].channel &= LI_CHANNEL_ADDRESSES_SET;
+    if (li_config_table[i].chflags != 0)
+      li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
+    else
+    {
+      for (j = 0; j < li_total_channels; j++)
+      {
+        if (((li_config_table[i].flag_table[j]) != 0)
+         || ((li_config_table[j].flag_table[i]) != 0))
+        {
+          li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
+        }
+        if (((li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE) != 0)
+         || ((li_config_table[j].flag_table[i] & LI_FLAG_CONFERENCE) != 0))
+        {
+          li_config_table[i].channel |= LI_CHANNEL_CONFERENCE;
+        }
+      }
+    }
+  }
+  for (i = 0; i < li_total_channels; i++)
+  {
+    for (j = 0; j < li_total_channels; j++)
+    {
+      li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC);
+      if (li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE)
+        li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
+    }
+  }
+  for (n = 0; n < li_total_channels; n++)
+  {
+    if (li_config_table[n].channel & LI_CHANNEL_CONFERENCE)
+    {
+      for (i = 0; i < li_total_channels; i++)
+      {
+        if (li_config_table[i].channel & LI_CHANNEL_CONFERENCE)
+        {
+          for (j = 0; j < li_total_channels; j++)
+          {
+            li_config_table[i].coef_table[j] |=
+              li_config_table[i].coef_table[n] & li_config_table[n].coef_table[j];
+          }
+        }
+      }
+    }
+  }
+  for (i = 0; i < li_total_channels; i++)
+  {
+    if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+    {
+      li_config_table[i].coef_table[i] &= ~LI_COEF_CH_CH;
+      for (j = 0; j < li_total_channels; j++)
+      {
+        if (li_config_table[i].coef_table[j] & LI_COEF_CH_CH)
+          li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE;
+      }
+      if (li_config_table[i].flag_table[i] & LI_FLAG_CONFERENCE)
+        li_config_table[i].coef_table[i] |= LI_COEF_CH_CH;
+    }
+  }
+  for (i = 0; i < li_total_channels; i++)
+  {
+    if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+    {
+      for (j = 0; j < li_total_channels; j++)
+      {
+        if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+          li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
+        if (li_config_table[i].flag_table[j] & LI_FLAG_MONITOR)
+          li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
+        if (li_config_table[i].flag_table[j] & LI_FLAG_MIX)
+          li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
+        if (li_config_table[i].flag_table[j] & LI_FLAG_PCCONNECT)
+          li_config_table[i].coef_table[j] |= LI_COEF_PC_PC;
+      }
+      if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
+      {
+        for (j = 0; j < li_total_channels; j++)
+        {
+          if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+          {
+            li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
+            if (li_config_table[j].chflags & LI_CHFLAG_MIX)
+              li_config_table[i].coef_table[j] |= LI_COEF_PC_CH | LI_COEF_PC_PC;
+          }
+        }
+      }
+      if (li_config_table[i].chflags & LI_CHFLAG_MIX)
+      {
+        for (j = 0; j < li_total_channels; j++)
+        {
+          if (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT)
+            li_config_table[j].coef_table[i] |= LI_COEF_PC_CH;
+        }
+      }
+      if (li_config_table[i].chflags & LI_CHFLAG_LOOP)
+      {
+        for (j = 0; j < li_total_channels; j++)
+        {
+          if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+          {
+            for (n = 0; n < li_total_channels; n++)
+            {
+              if (li_config_table[n].flag_table[i] & LI_FLAG_INTERCONNECT)
+              {
+                li_config_table[n].coef_table[j] |= LI_COEF_CH_CH;
+                if (li_config_table[j].chflags & LI_CHFLAG_MIX)
+                {
+                  li_config_table[n].coef_table[j] |= LI_COEF_PC_CH;
+                  if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
+                    li_config_table[n].coef_table[j] |= LI_COEF_CH_PC | LI_COEF_PC_PC;
+                }
+                else if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
+                  li_config_table[n].coef_table[j] |= LI_COEF_CH_PC;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  for (i = 0; i < li_total_channels; i++)
+  {
+    if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+    {
+      if (li_config_table[i].chflags & (LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP))
+        li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
+      if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
+        li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
+      if (li_config_table[i].chflags & LI_CHFLAG_MIX)
+        li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
+      for (j = 0; j < li_total_channels; j++)
+      {
+        if ((li_config_table[i].flag_table[j] &
+          (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_MONITOR))
+         || (li_config_table[j].flag_table[i] &
+          (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX)))
+        {
+          li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
+        }
+        if (li_config_table[i].flag_table[j] & (LI_FLAG_PCCONNECT | LI_FLAG_MONITOR))
+          li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
+        if (li_config_table[j].flag_table[i] & (LI_FLAG_PCCONNECT | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX))
+          li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
+      }
+      if (!(li_config_table[i].channel & LI_CHANNEL_ACTIVE))
+      {
+        li_config_table[i].coef_table[i] |= LI_COEF_PC_CH | LI_COEF_CH_PC;
+        li_config_table[i].channel |= LI_CHANNEL_TX_DATA | LI_CHANNEL_RX_DATA;
+      }
+    }
+  }
+  for (i = 0; i < li_total_channels; i++)
+  {
+    if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+    {
+      j = 0;
+      while ((j < li_total_channels) && !(li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT))
+        j++;
+      if (j < li_total_channels)
+      {
+        for (j = 0; j < li_total_channels; j++)
+        {
+          li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_PC_CH);
+          if (li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT)
+            li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
+        }
+      }
+    }
+  }
+  n = li_total_channels;
+  if (n > MIXER_MAX_DUMP_CHANNELS)
+    n = MIXER_MAX_DUMP_CHANNELS;
+  p = hex_line;
+  for (j = 0; j < n; j++)
+  {
+    if ((j & 0x7) == 0)
+      *(p++) = ' ';
+    *(p++) = hex_digit_table[li_config_table[j].curchnl >> 4];
+    *(p++) = hex_digit_table[li_config_table[j].curchnl & 0xf];
+  }
+  *p = '\0';
+  dbug (1, dprintf ("[%06lx] CURRENT %s",
+    (dword)(UnMapController (a->Id)), (char   *) hex_line));
+  p = hex_line;
+  for (j = 0; j < n; j++)
+  {
+    if ((j & 0x7) == 0)
+      *(p++) = ' ';
+    *(p++) = hex_digit_table[li_config_table[j].channel >> 4];
+    *(p++) = hex_digit_table[li_config_table[j].channel & 0xf];
+  }
+  *p = '\0';
+  dbug (1, dprintf ("[%06lx] CHANNEL %s",
+    (dword)(UnMapController (a->Id)), (char   *) hex_line));
+  p = hex_line;
+  for (j = 0; j < n; j++)
+  {
+    if ((j & 0x7) == 0)
+      *(p++) = ' ';
+    *(p++) = hex_digit_table[li_config_table[j].chflags >> 4];
+    *(p++) = hex_digit_table[li_config_table[j].chflags & 0xf];
+  }
+  *p = '\0';
+  dbug (1, dprintf ("[%06lx] CHFLAG  %s",
+    (dword)(UnMapController (a->Id)), (char   *) hex_line));
+  for (i = 0; i < n; i++)
+  {
+    p = hex_line;
+    for (j = 0; j < n; j++)
+    {
+      if ((j & 0x7) == 0)
+        *(p++) = ' ';
+      *(p++) = hex_digit_table[li_config_table[i].flag_table[j] >> 4];
+      *(p++) = hex_digit_table[li_config_table[i].flag_table[j] & 0xf];
+    }
+    *p = '\0';
+    dbug (1, dprintf ("[%06lx] FLAG[%02x]%s",
+      (dword)(UnMapController (a->Id)), i, (char   *) hex_line));
+  }
+  for (i = 0; i < n; i++)
+  {
+    p = hex_line;
+    for (j = 0; j < n; j++)
+    {
+      if ((j & 0x7) == 0)
+        *(p++) = ' ';
+      *(p++) = hex_digit_table[li_config_table[i].coef_table[j] >> 4];
+      *(p++) = hex_digit_table[li_config_table[i].coef_table[j] & 0xf];
+    }
+    *p = '\0';
+    dbug (1, dprintf ("[%06lx] COEF[%02x]%s",
+      (dword)(UnMapController (a->Id)), i, (char   *) hex_line));
+  }
+}
+
+
+static struct
+{
+  byte mask;
+  byte line_flags;
+} mixer_write_prog_pri[] =
+{
+  { LI_COEF_CH_CH, 0 },
+  { LI_COEF_CH_PC, MIXER_COEF_LINE_TO_PC_FLAG },
+  { LI_COEF_PC_CH, MIXER_COEF_LINE_FROM_PC_FLAG },
+  { LI_COEF_PC_PC, MIXER_COEF_LINE_TO_PC_FLAG | MIXER_COEF_LINE_FROM_PC_FLAG }
+};
+
+static struct
+{
+  byte from_ch;
+  byte to_ch;
+  byte mask;
+  byte xconnect_override;
+} mixer_write_prog_bri[] =
+{
+  { 0, 0, LI_COEF_CH_CH, 0x01 },  /* B      to B      */
+  { 1, 0, LI_COEF_CH_CH, 0x01 },  /* Alt B  to B      */
+  { 0, 0, LI_COEF_PC_CH, 0x80 },  /* PC     to B      */
+  { 1, 0, LI_COEF_PC_CH, 0x01 },  /* Alt PC to B      */
+  { 2, 0, LI_COEF_CH_CH, 0x00 },  /* IC     to B      */
+  { 3, 0, LI_COEF_CH_CH, 0x00 },  /* Alt IC to B      */
+  { 0, 0, LI_COEF_CH_PC, 0x80 },  /* B      to PC     */
+  { 1, 0, LI_COEF_CH_PC, 0x01 },  /* Alt B  to PC     */
+  { 0, 0, LI_COEF_PC_PC, 0x01 },  /* PC     to PC     */
+  { 1, 0, LI_COEF_PC_PC, 0x01 },  /* Alt PC to PC     */
+  { 2, 0, LI_COEF_CH_PC, 0x00 },  /* IC     to PC     */
+  { 3, 0, LI_COEF_CH_PC, 0x00 },  /* Alt IC to PC     */
+  { 0, 2, LI_COEF_CH_CH, 0x00 },  /* B      to IC     */
+  { 1, 2, LI_COEF_CH_CH, 0x00 },  /* Alt B  to IC     */
+  { 0, 2, LI_COEF_PC_CH, 0x00 },  /* PC     to IC     */
+  { 1, 2, LI_COEF_PC_CH, 0x00 },  /* Alt PC to IC     */
+  { 2, 2, LI_COEF_CH_CH, 0x00 },  /* IC     to IC     */
+  { 3, 2, LI_COEF_CH_CH, 0x00 },  /* Alt IC to IC     */
+  { 1, 1, LI_COEF_CH_CH, 0x01 },  /* Alt B  to Alt B  */
+  { 0, 1, LI_COEF_CH_CH, 0x01 },  /* B      to Alt B  */
+  { 1, 1, LI_COEF_PC_CH, 0x80 },  /* Alt PC to Alt B  */
+  { 0, 1, LI_COEF_PC_CH, 0x01 },  /* PC     to Alt B  */
+  { 3, 1, LI_COEF_CH_CH, 0x00 },  /* Alt IC to Alt B  */
+  { 2, 1, LI_COEF_CH_CH, 0x00 },  /* IC     to Alt B  */
+  { 1, 1, LI_COEF_CH_PC, 0x80 },  /* Alt B  to Alt PC */
+  { 0, 1, LI_COEF_CH_PC, 0x01 },  /* B      to Alt PC */
+  { 1, 1, LI_COEF_PC_PC, 0x01 },  /* Alt PC to Alt PC */
+  { 0, 1, LI_COEF_PC_PC, 0x01 },  /* PC     to Alt PC */
+  { 3, 1, LI_COEF_CH_PC, 0x00 },  /* Alt IC to Alt PC */
+  { 2, 1, LI_COEF_CH_PC, 0x00 },  /* IC     to Alt PC */
+  { 1, 3, LI_COEF_CH_CH, 0x00 },  /* Alt B  to Alt IC */
+  { 0, 3, LI_COEF_CH_CH, 0x00 },  /* B      to Alt IC */
+  { 1, 3, LI_COEF_PC_CH, 0x00 },  /* Alt PC to Alt IC */
+  { 0, 3, LI_COEF_PC_CH, 0x00 },  /* PC     to Alt IC */
+  { 3, 3, LI_COEF_CH_CH, 0x00 },  /* Alt IC to Alt IC */
+  { 2, 3, LI_COEF_CH_CH, 0x00 }   /* IC     to Alt IC */
+};
+
+static byte mixer_swapped_index_bri[] =
+{
+  18,  /* B      to B      */
+  19,  /* Alt B  to B      */
+  20,  /* PC     to B      */
+  21,  /* Alt PC to B      */
+  22,  /* IC     to B      */
+  23,  /* Alt IC to B      */
+  24,  /* B      to PC     */
+  25,  /* Alt B  to PC     */
+  26,  /* PC     to PC     */
+  27,  /* Alt PC to PC     */
+  28,  /* IC     to PC     */
+  29,  /* Alt IC to PC     */
+  30,  /* B      to IC     */
+  31,  /* Alt B  to IC     */
+  32,  /* PC     to IC     */
+  33,  /* Alt PC to IC     */
+  34,  /* IC     to IC     */
+  35,  /* Alt IC to IC     */
+  0,   /* Alt B  to Alt B  */
+  1,   /* B      to Alt B  */
+  2,   /* Alt PC to Alt B  */
+  3,   /* PC     to Alt B  */
+  4,   /* Alt IC to Alt B  */
+  5,   /* IC     to Alt B  */
+  6,   /* Alt B  to Alt PC */
+  7,   /* B      to Alt PC */
+  8,   /* Alt PC to Alt PC */
+  9,   /* PC     to Alt PC */
+  10,  /* Alt IC to Alt PC */
+  11,  /* IC     to Alt PC */
+  12,  /* Alt B  to Alt IC */
+  13,  /* B      to Alt IC */
+  14,  /* Alt PC to Alt IC */
+  15,  /* PC     to Alt IC */
+  16,  /* Alt IC to Alt IC */
+  17   /* IC     to Alt IC */
+};
+
+static struct
+{
+  byte mask;
+  byte from_pc;
+  byte to_pc;
+} xconnect_write_prog[] =
+{
+  { LI_COEF_CH_CH, FALSE, FALSE },
+  { LI_COEF_CH_PC, FALSE, TRUE },
+  { LI_COEF_PC_CH, TRUE, FALSE },
+  { LI_COEF_PC_PC, TRUE, TRUE }
+};
+
+
+static void xconnect_query_addresses (PLCI   *plci)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word w, ch;
+  byte   *p;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: xconnect_query_addresses",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  a = plci->adapter;
+  if (a->li_pri && ((plci->li_bchannel_id == 0)
+   || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci)))
+  {
+    dbug (1, dprintf ("[%06x] %s,%d: Channel id wiped out",
+      (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+      (char   *)(FILE_), __LINE__));
+    return;
+  }
+  p = plci->internal_req_buffer;
+  ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
+  *(p++) = UDATA_REQUEST_XCONNECT_FROM;
+  w = ch;
+  *(p++) = (byte) w;
+  *(p++) = (byte)(w >> 8);
+  w = ch | XCONNECT_CHANNEL_PORT_PC;
+  *(p++) = (byte) w;
+  *(p++) = (byte)(w >> 8);
+  plci->NData[0].P = plci->internal_req_buffer;
+  plci->NData[0].PLength = p - plci->internal_req_buffer;
+  plci->NL.X = plci->NData;
+  plci->NL.ReqCh = 0;
+  plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+  plci->adapter->request (&plci->NL);
+}
+
+
+static void xconnect_write_coefs (PLCI   *plci, word internal_command)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: xconnect_write_coefs %04x",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, internal_command));
+
+  plci->li_write_command = internal_command;
+  plci->li_write_channel = 0;
+}
+
+
+static byte xconnect_write_coefs_process (dword Id, PLCI   *plci, byte Rc)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word w, n, i, j, r, s, to_ch;
+  dword d;
+  byte   *p;
+  struct xconnect_transfer_address_s   *transfer_address;
+  byte ch_map[MIXER_CHANNELS_BRI];
+
+  dbug (1, dprintf ("[%06x] %s,%d: xconnect_write_coefs_process %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->li_write_channel));
+
+  a = plci->adapter;
+  if ((plci->li_bchannel_id == 0)
+   || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
+  {
+    dbug (1, dprintf ("[%06x] %s,%d: Channel id wiped out",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    return (TRUE);
+  }
+  i = a->li_base + (plci->li_bchannel_id - 1);
+  j = plci->li_write_channel;
+  p = plci->internal_req_buffer;
+  if (j != 0)
+  {
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: LI write coefs failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      return (FALSE);
+    }
+  }
+  if (li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+  {
+    r = 0;
+    s = 0;
+    if (j < li_total_channels)
+    {
+      if (li_config_table[i].channel & LI_CHANNEL_ADDRESSES_SET)
+      {
+        s = ((li_config_table[i].send_b.card_address.low | li_config_table[i].send_b.card_address.high) ?
+            (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_PC | LI_COEF_PC_PC)) &
+          ((li_config_table[i].send_pc.card_address.low | li_config_table[i].send_pc.card_address.high) ?
+            (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_PC_CH));
+      }
+      r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+      while ((j < li_total_channels)
+        && ((r == 0)
+         || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
+         || (!li_config_table[j].adapter->li_pri
+          && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
+         || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
+           || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
+          && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
+           || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
+         || ((li_config_table[j].adapter->li_base != a->li_base)
+          && !(r & s &
+            ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
+              (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
+            ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
+              (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))))
+      {
+        j++;
+        if (j < li_total_channels)
+          r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+      }
+    }
+    if (j < li_total_channels)
+    {
+      plci->internal_command = plci->li_write_command;
+      if (plci_nl_busy (plci))
+        return (TRUE);
+      to_ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
+      *(p++) = UDATA_REQUEST_XCONNECT_TO;
+      do
+      {
+        if (li_config_table[j].adapter->li_base != a->li_base)
+        {
+          r &= s &
+            ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
+              (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
+            ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
+              (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC));
+        }
+        n = 0;
+        do
+        {
+          if (r & xconnect_write_prog[n].mask)
+          {
+            if (xconnect_write_prog[n].from_pc)
+              transfer_address = &(li_config_table[j].send_pc);
+            else
+              transfer_address = &(li_config_table[j].send_b);
+            d = transfer_address->card_address.low;
+            *(p++) = (byte) d;
+            *(p++) = (byte)(d >> 8);
+            *(p++) = (byte)(d >> 16);
+            *(p++) = (byte)(d >> 24);
+            d = transfer_address->card_address.high;
+            *(p++) = (byte) d;
+            *(p++) = (byte)(d >> 8);
+            *(p++) = (byte)(d >> 16);
+            *(p++) = (byte)(d >> 24);
+            d = transfer_address->offset;
+            *(p++) = (byte) d;
+            *(p++) = (byte)(d >> 8);
+            *(p++) = (byte)(d >> 16);
+            *(p++) = (byte)(d >> 24);
+            w = xconnect_write_prog[n].to_pc ? to_ch | XCONNECT_CHANNEL_PORT_PC : to_ch;
+            *(p++) = (byte) w;
+            *(p++) = (byte)(w >> 8);
+            w = ((li_config_table[i].coef_table[j] & xconnect_write_prog[n].mask) == 0) ? 0x01 :
+              (li_config_table[i].adapter->u_law ?
+                 (li_config_table[j].adapter->u_law ? 0x80 : 0x86) :
+                 (li_config_table[j].adapter->u_law ? 0x7a : 0x80));
+            *(p++) = (byte) w;
+            *(p++) = (byte) 0;
+            li_config_table[i].coef_table[j] ^= xconnect_write_prog[n].mask << 4;
+          }
+          n++;
+        } while ((n < sizeof(xconnect_write_prog) / sizeof(xconnect_write_prog[0]))
+          && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
+        if (n == sizeof(xconnect_write_prog) / sizeof(xconnect_write_prog[0]))
+        {
+          do
+          {
+            j++;
+            if (j < li_total_channels)
+              r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+          } while ((j < li_total_channels)
+            && ((r == 0)
+             || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
+             || (!li_config_table[j].adapter->li_pri
+              && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
+             || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
+               || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
+              && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
+               || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
+             || ((li_config_table[j].adapter->li_base != a->li_base)
+              && !(r & s &
+                ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
+                  (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
+                ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
+                  (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))));
+        }
+      } while ((j < li_total_channels)
+        && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
+    }
+    else if (j == li_total_channels)
+    {
+      plci->internal_command = plci->li_write_command;
+      if (plci_nl_busy (plci))
+        return (TRUE);
+      if (a->li_pri)
+      {
+        *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
+        w = 0;
+        if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+          w |= MIXER_FEATURE_ENABLE_TX_DATA;
+        if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+          w |= MIXER_FEATURE_ENABLE_RX_DATA;
+        *(p++) = (byte) w;
+        *(p++) = (byte)(w >> 8);
+      }
+      else
+      {
+        *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
+        w = 0;
+        if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
+         && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
+        {
+          w = GET_WORD (a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
+        }
+        if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+          w |= MIXER_FEATURE_ENABLE_TX_DATA;
+        if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+          w |= MIXER_FEATURE_ENABLE_RX_DATA;
+        *(p++) = (byte) w;
+        *(p++) = (byte)(w >> 8);
+        for (j = 0; j < sizeof(ch_map); j += 2)
+        {
+          if (plci->li_bchannel_id == 2)
+          {
+            ch_map[j] = (byte)(j+1);
+            ch_map[j+1] = (byte) j;
+          }
+          else
+          {
+            ch_map[j] = (byte) j;
+            ch_map[j+1] = (byte)(j+1);
+          }
+        }
+        for (n = 0; n < sizeof(mixer_write_prog_bri) / sizeof(mixer_write_prog_bri[0]); n++)
+        {
+          i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
+          j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
+          if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
+          {
+            *p = (mixer_write_prog_bri[n].xconnect_override != 0) ?
+              mixer_write_prog_bri[n].xconnect_override :
+              ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
+            if ((i >= a->li_base + MIXER_BCHANNELS_BRI) || (j >= a->li_base + MIXER_BCHANNELS_BRI))
+            {
+              w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+              li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
+            }
+          }
+          else
+          {
+            *p = 0x00;
+            if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+            {
+              w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
+              if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
+                *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
+            }
+          }
+          p++;
+        }
+      }
+      j = li_total_channels + 1;
+    }
+  }
+  else
+  {
+    if (j <= li_total_channels)
+    {
+      plci->internal_command = plci->li_write_command;
+      if (plci_nl_busy (plci))
+        return (TRUE);
+      if (j < a->li_base)
+        j = a->li_base;
+      if (a->li_pri)
+      {
+        *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
+        w = 0;
+        if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+          w |= MIXER_FEATURE_ENABLE_TX_DATA;
+        if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+          w |= MIXER_FEATURE_ENABLE_RX_DATA;
+        *(p++) = (byte) w;
+        *(p++) = (byte)(w >> 8);
+        for (n = 0; n < sizeof(mixer_write_prog_pri) / sizeof(mixer_write_prog_pri[0]); n++)
+        {
+          *(p++) = (byte)((plci->li_bchannel_id - 1) | mixer_write_prog_pri[n].line_flags);
+          for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
+          {
+            w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+            if (w & mixer_write_prog_pri[n].mask)
+            {
+              *(p++) = (li_config_table[i].coef_table[j] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
+              li_config_table[i].coef_table[j] ^= mixer_write_prog_pri[n].mask << 4;
+            }
+            else
+              *(p++) = 0x00;
+          }
+          *(p++) = (byte)((plci->li_bchannel_id - 1) | MIXER_COEF_LINE_ROW_FLAG | mixer_write_prog_pri[n].line_flags);
+          for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
+          {
+            w = ((li_config_table[j].coef_table[i] & 0xf) ^ (li_config_table[j].coef_table[i] >> 4));
+            if (w & mixer_write_prog_pri[n].mask)
+            {
+              *(p++) = (li_config_table[j].coef_table[i] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
+              li_config_table[j].coef_table[i] ^= mixer_write_prog_pri[n].mask << 4;
+            }
+            else
+              *(p++) = 0x00;
+          }
+        }
+      }
+      else
+      {
+        *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
+        w = 0;
+        if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
+         && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
+        {
+          w = GET_WORD (a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
+        }
+        if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+          w |= MIXER_FEATURE_ENABLE_TX_DATA;
+        if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+          w |= MIXER_FEATURE_ENABLE_RX_DATA;
+        *(p++) = (byte) w;
+        *(p++) = (byte)(w >> 8);
+        for (j = 0; j < sizeof(ch_map); j += 2)
+        {
+          if (plci->li_bchannel_id == 2)
+          {
+            ch_map[j] = (byte)(j+1);
+            ch_map[j+1] = (byte) j;
+          }
+          else
+          {
+            ch_map[j] = (byte) j;
+            ch_map[j+1] = (byte)(j+1);
+          }
+        }
+        for (n = 0; n < sizeof(mixer_write_prog_bri) / sizeof(mixer_write_prog_bri[0]); n++)
+        {
+          i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
+          j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
+          if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
+          {
+            *p = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
+            w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+            li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
+          }
+          else
+          {
+            *p = 0x00;
+            if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+            {
+              w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
+              if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
+                *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
+            }
+          }
+          p++;
+        }
+      }
+      j = li_total_channels + 1;
+    }
+  }
+  plci->li_write_channel = j;
+  if (p != plci->internal_req_buffer)
+  {
+    plci->NData[0].P = plci->internal_req_buffer;
+    plci->NData[0].PLength = p - plci->internal_req_buffer;
+    plci->NL.X = plci->NData;
+    plci->NL.ReqCh = 0;
+    plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+    plci->adapter->request (&plci->NL);
+  }
+  return (TRUE);
+}
+
+
+static void mixer_notify_update (PLCI   *plci, byte others)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word i, w;
+  PLCI   *notify_plci;
+    byte msg[sizeof(CAPI_MSG_HEADER) + 6];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_notify_update %d",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, others));
+
+  a = plci->adapter;
+  if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
+  {
+    if (others)
+      plci->li_notify_update = TRUE;
+    i = 0;
+    do
+    {
+      notify_plci = NULL;
+      if (others)
+      {
+        while ((i < li_total_channels) && (li_config_table[i].plci == NULL))
+          i++;
+        if (i < li_total_channels)
+          notify_plci = li_config_table[i++].plci;
+      }
+      else
+      {
+        if ((plci->li_bchannel_id != 0)
+         && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+        {
+          notify_plci = plci;
+        }
+      }
+      if ((notify_plci != NULL)
+       && !notify_plci->li_notify_update
+       && (notify_plci->appl != NULL)
+       && (notify_plci->State)
+       && notify_plci->NL.Id && !notify_plci->nl_remove_id)
+      {
+        notify_plci->li_notify_update = TRUE;
+        ((CAPI_MSG *) msg)->header.length = 18;
+        ((CAPI_MSG *) msg)->header.appl_id = notify_plci->appl->Id;
+        ((CAPI_MSG *) msg)->header.command = _FACILITY_R;
+        ((CAPI_MSG *) msg)->header.number = 0;
+        ((CAPI_MSG *) msg)->header.controller = notify_plci->adapter->Id;
+        ((CAPI_MSG *) msg)->header.plci = notify_plci->Id;
+        ((CAPI_MSG *) msg)->header.ncci = 0;
+        ((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT;
+        ((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3;
+        PUT_WORD (&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE);
+        ((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0;
+        w = api_put (notify_plci->appl, (CAPI_MSG *) msg);
+        if (w != _QUEUE_FULL)
+        {
+          if (w != 0)
+          {
+            dbug (1, dprintf ("[%06lx] %s,%d: Interconnect notify failed %06x %d",
+              (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+              (char   *)(FILE_), __LINE__,
+              (dword)((notify_plci->Id << 8) | UnMapController (notify_plci->adapter->Id)), w));
+          }
+          notify_plci->li_notify_update = FALSE;
+        }
+      }
+    } while (others && (notify_plci != NULL));
+    if (others)
+      plci->li_notify_update = FALSE;
+  }
+}
+
+
+static void mixer_clear_config (PLCI   *plci)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word i, j;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_clear_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  plci->li_notify_update = FALSE;
+  plci->li_plci_b_write_pos = 0;
+  plci->li_plci_b_read_pos = 0;
+  plci->li_plci_b_req_pos = 0;
+  a = plci->adapter;
+  if ((plci->li_bchannel_id != 0)
+   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+  {
+    i = a->li_base + (plci->li_bchannel_id - 1);
+    li_config_table[i].curchnl = 0;
+    li_config_table[i].channel = 0;
+    li_config_table[i].chflags = 0;
+    for (j = 0; j < li_total_channels; j++)
+    {
+      li_config_table[j].flag_table[i] = 0;
+      li_config_table[i].flag_table[j] = 0;
+      li_config_table[i].coef_table[j] = 0;
+      li_config_table[j].coef_table[i] = 0;
+    }
+    if (!a->li_pri)
+    {
+      li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
+      if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+      {
+        i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+        li_config_table[i].curchnl = 0;
+        li_config_table[i].channel = 0;
+        li_config_table[i].chflags = 0;
+        for (j = 0; j < li_total_channels; j++)
+        {
+          li_config_table[i].flag_table[j] = 0;
+          li_config_table[j].flag_table[i] = 0;
+          li_config_table[i].coef_table[j] = 0;
+          li_config_table[j].coef_table[i] = 0;
+        }
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+        {
+          i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+          li_config_table[i].curchnl = 0;
+          li_config_table[i].channel = 0;
+          li_config_table[i].chflags = 0;
+          for (j = 0; j < li_total_channels; j++)
+          {
+            li_config_table[i].flag_table[j] = 0;
+            li_config_table[j].flag_table[i] = 0;
+            li_config_table[i].coef_table[j] = 0;
+            li_config_table[j].coef_table[i] = 0;
+          }
+        }
+      }
+    }
+  }
+}
+
+
+static void mixer_prepare_switch (dword Id, PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_prepare_switch",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  do
+  {
+    mixer_indication_coefs_set (Id, plci);
+  } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
+}
+
+
+static word mixer_save_config (dword Id, PLCI   *plci, byte Rc)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word i, j;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_save_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  a = plci->adapter;
+  if ((plci->li_bchannel_id != 0)
+   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+  {
+    i = a->li_base + (plci->li_bchannel_id - 1);
+    for (j = 0; j < li_total_channels; j++)
+    {
+      li_config_table[i].coef_table[j] &= 0xf;
+      li_config_table[j].coef_table[i] &= 0xf;
+    }
+    if (!a->li_pri)
+      li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
+  }
+  return (GOOD);
+}
+
+
+static word mixer_restore_config (dword Id, PLCI   *plci, byte Rc)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word Info;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_restore_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  Info = GOOD;
+  a = plci->adapter;
+  if ((plci->B1_facilities & B1_FACILITY_MIXER)
+   && (plci->li_bchannel_id != 0)
+   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+  {
+    switch (plci->adjust_b_state)
+    {
+    case ADJUST_B_RESTORE_MIXER_1:
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+      {
+        plci->internal_command = plci->adjust_b_command;
+        if (plci_nl_busy (plci))
+        {
+          plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
+          break;
+        }
+        xconnect_query_addresses (plci);
+        plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_2;
+        break;
+      }
+      plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
+      Rc = OK;
+    case ADJUST_B_RESTORE_MIXER_2:
+    case ADJUST_B_RESTORE_MIXER_3:
+    case ADJUST_B_RESTORE_MIXER_4:
+      if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Adjust B query addresses failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _WRONG_STATE;
+        break;
+      }
+      if (Rc == OK)
+      {
+        if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
+          plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_3;
+        else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)
+          plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
+      }
+      else if (Rc == 0)
+      {
+        if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
+          plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_4;
+        else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
+          plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
+      }
+      if (plci->adjust_b_state != ADJUST_B_RESTORE_MIXER_5)
+      {
+        plci->internal_command = plci->adjust_b_command;
+        break;
+      }
+    case ADJUST_B_RESTORE_MIXER_5:
+      xconnect_write_coefs (plci, plci->adjust_b_command);
+      plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_6;
+      Rc = OK;
+    case ADJUST_B_RESTORE_MIXER_6:
+      if (!xconnect_write_coefs_process (Id, plci, Rc))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Write mixer coefs failed",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      if (plci->internal_command)
+        break;
+      plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_7;
+    case ADJUST_B_RESTORE_MIXER_7:
+      break;
+    }
+  }
+  return (Info);
+}
+
+
+static void mixer_command (dword Id, PLCI   *plci, byte Rc)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word i, internal_command, Info;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_command %02x %04x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command,
+    plci->li_cmd));
+
+  Info = GOOD;
+  a = plci->adapter;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (plci->li_cmd)
+  {
+  case LI_REQ_CONNECT:
+  case LI_REQ_DISCONNECT:
+  case LI_REQ_SILENT_UPDATE:
+    switch (internal_command)
+    {
+    default:
+      if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+      {
+        adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+          B1_FACILITY_MIXER), MIXER_COMMAND_1);
+      }
+    case MIXER_COMMAND_1:
+      if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+      {
+        if (adjust_b_process (Id, plci, Rc) != GOOD)
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Load mixer failed",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _FACILITY_NOT_SUPPORTED;
+          break;
+        }
+        if (plci->internal_command)
+          return;
+      }
+      plci->li_plci_b_req_pos = plci->li_plci_b_write_pos;
+      if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+       || ((get_b1_facilities (plci, plci->B1_resource) & B1_FACILITY_MIXER)
+        && (add_b1_facilities (plci, plci->B1_resource, (word)(plci->B1_facilities &
+         ~B1_FACILITY_MIXER)) == plci->B1_resource)))
+      {
+        xconnect_write_coefs (plci, MIXER_COMMAND_2);
+      }
+      else
+      {
+        do
+        {
+          mixer_indication_coefs_set (Id, plci);
+        } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
+      }
+    case MIXER_COMMAND_2:
+      if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+       || ((get_b1_facilities (plci, plci->B1_resource) & B1_FACILITY_MIXER)
+        && (add_b1_facilities (plci, plci->B1_resource, (word)(plci->B1_facilities &
+         ~B1_FACILITY_MIXER)) == plci->B1_resource)))
+      {
+        if (!xconnect_write_coefs_process (Id, plci, Rc))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Write mixer coefs failed",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          if (plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
+          {
+            do
+            {
+              plci->li_plci_b_write_pos = (plci->li_plci_b_write_pos == 0) ?
+                LI_PLCI_B_QUEUE_ENTRIES-1 : plci->li_plci_b_write_pos - 1;
+              i = (plci->li_plci_b_write_pos == 0) ?
+                LI_PLCI_B_QUEUE_ENTRIES-1 : plci->li_plci_b_write_pos - 1;
+            } while ((plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
+              && !(plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG));
+          }
+          Info = _FACILITY_NOT_SUPPORTED;
+          break;
+        }
+        if (plci->internal_command)
+          return;
+      }
+      if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
+      {
+        adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities &
+          ~B1_FACILITY_MIXER), MIXER_COMMAND_3);
+      }
+    case MIXER_COMMAND_3:
+      if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
+      {
+        if (adjust_b_process (Id, plci, Rc) != GOOD)
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Unload mixer failed",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _FACILITY_NOT_SUPPORTED;
+          break;
+        }
+        if (plci->internal_command)
+          return;
+      }
+      break;
+    }
+    break;
+  }
+  if ((plci->li_bchannel_id == 0)
+   || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
+  {
+    dbug (1, dprintf ("[%06x] %s,%d: Channel id wiped out %d",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, (int)(plci->li_bchannel_id)));
+  }
+  else
+  {
+    i = a->li_base + (plci->li_bchannel_id - 1);
+    li_config_table[i].curchnl = plci->li_channel_bits;
+    if (!a->li_pri && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+    {
+      i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+      li_config_table[i].curchnl = plci->li_channel_bits;
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+      {
+        i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+        li_config_table[i].curchnl = plci->li_channel_bits;
+      }
+    }
+  }
+}
+
+
+static void li_update_connect (dword Id, DIVA_CAPI_ADAPTER   *a, PLCI   *plci,
+  dword plci_b_id, byte connect, dword li_flags)
+{
+  word i, ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
+  PLCI   *plci_b;
+  DIVA_CAPI_ADAPTER   *a_b;
+
+  a_b = &(adapter[MapController ((byte)(plci_b_id & 0x7f)) - 1]);
+  plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
+  ch_a = a->li_base + (plci->li_bchannel_id - 1);
+  if (!a->li_pri && (plci->tel == ADV_VOICE)
+   && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
+  {
+    ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
+    ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+      a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
+  }
+  else
+  {
+    ch_a_v = ch_a;
+    ch_a_s = ch_a;
+  }
+  ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
+  if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
+   && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
+  {
+    ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
+    ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+      a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
+  }
+  else
+  {
+    ch_b_v = ch_b;
+    ch_b_s = ch_b;
+  }
+  if (connect)
+  {
+    li_config_table[ch_a].flag_table[ch_a_v] &= ~LI_FLAG_MONITOR;
+    li_config_table[ch_a].flag_table[ch_a_s] &= ~LI_FLAG_MONITOR;
+    li_config_table[ch_a_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+    li_config_table[ch_a_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+  }
+  li_config_table[ch_a].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
+  li_config_table[ch_a].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
+  li_config_table[ch_b_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+  li_config_table[ch_b_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+  if (ch_a_v == ch_b_v)
+  {
+    li_config_table[ch_a_v].flag_table[ch_b_v] &= ~LI_FLAG_CONFERENCE;
+    li_config_table[ch_a_s].flag_table[ch_b_s] &= ~LI_FLAG_CONFERENCE;
+  }
+  else
+  {
+    if (li_config_table[ch_a_v].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
+    {
+      for (i = 0; i < li_total_channels; i++)
+      {
+        if (i != ch_a_v)
+          li_config_table[ch_a_v].flag_table[i] &= ~LI_FLAG_CONFERENCE;
+      }
+    }
+    if (li_config_table[ch_a_s].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
+    {
+      for (i = 0; i < li_total_channels; i++)
+      {
+        if (i != ch_a_s)
+          li_config_table[ch_a_s].flag_table[i] &= ~LI_FLAG_CONFERENCE;
+      }
+    }
+    if (li_config_table[ch_b_v].flag_table[ch_a_v] & LI_FLAG_CONFERENCE)
+    {
+      for (i = 0; i < li_total_channels; i++)
+      {
+        if (i != ch_a_v)
+          li_config_table[i].flag_table[ch_a_v] &= ~LI_FLAG_CONFERENCE;
+      }
+    }
+    if (li_config_table[ch_b_v].flag_table[ch_a_s] & LI_FLAG_CONFERENCE)
+    {
+      for (i = 0; i < li_total_channels; i++)
+      {
+        if (i != ch_a_s)
+          li_config_table[i].flag_table[ch_a_s] &= ~LI_FLAG_CONFERENCE;
+      }
+    }
+  }
+  if (li_flags & LI_FLAG_CONFERENCE_A_B)
+  {
+    li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+  }
+  if (li_flags & LI_FLAG_CONFERENCE_B_A)
+  {
+    li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+  }
+  if (li_flags & LI_FLAG_MONITOR_A)
+  {
+    li_config_table[ch_a].flag_table[ch_a_v] |= LI_FLAG_MONITOR;
+    li_config_table[ch_a].flag_table[ch_a_s] |= LI_FLAG_MONITOR;
+  }
+  if (li_flags & LI_FLAG_MONITOR_B)
+  {
+    li_config_table[ch_a].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
+    li_config_table[ch_a].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
+  }
+  if (li_flags & LI_FLAG_ANNOUNCEMENT_A)
+  {
+    li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+    li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+  }
+  if (li_flags & LI_FLAG_ANNOUNCEMENT_B)
+  {
+    li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+    li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+  }
+  if (li_flags & LI_FLAG_MIX_A)
+  {
+    li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_MIX;
+    li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_MIX;
+  }
+  if (li_flags & LI_FLAG_MIX_B)
+  {
+    li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_MIX;
+    li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_MIX;
+  }
+  if (ch_a_v != ch_a_s)
+  {
+    li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+  }
+  if (ch_b_v != ch_b_s)
+  {
+    li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+  }
+}
+
+
+static void li2_update_connect (dword Id, DIVA_CAPI_ADAPTER   *a, PLCI   *plci,
+  dword plci_b_id, byte connect, dword li_flags)
+{
+  word ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
+  PLCI   *plci_b;
+  DIVA_CAPI_ADAPTER   *a_b;
+
+  a_b = &(adapter[MapController ((byte)(plci_b_id & 0x7f)) - 1]);
+  plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
+  ch_a = a->li_base + (plci->li_bchannel_id - 1);
+  if (!a->li_pri && (plci->tel == ADV_VOICE)
+   && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
+  {
+    ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
+    ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+      a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
+  }
+  else
+  {
+    ch_a_v = ch_a;
+    ch_a_s = ch_a;
+  }
+  ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
+  if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
+   && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
+  {
+    ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
+    ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+      a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
+  }
+  else
+  {
+    ch_b_v = ch_b;
+    ch_b_s = ch_b;
+  }
+  if (connect)
+  {
+    li_config_table[ch_b].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
+    li_config_table[ch_b].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
+    li_config_table[ch_b_v].flag_table[ch_b] &= ~LI_FLAG_MIX;
+    li_config_table[ch_b_s].flag_table[ch_b] &= ~LI_FLAG_MIX;
+    li_config_table[ch_b].flag_table[ch_b] &= ~LI_FLAG_PCCONNECT;
+    li_config_table[ch_b].chflags &= ~(LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP);
+  }
+  li_config_table[ch_b_v].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  li_config_table[ch_b_s].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  li_config_table[ch_b_v].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  li_config_table[ch_b_s].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  li_config_table[ch_a_v].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  li_config_table[ch_a_v].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  li_config_table[ch_a_s].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  li_config_table[ch_a_s].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+  if (li_flags & LI2_FLAG_INTERCONNECT_A_B)
+  {
+    li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
+  }
+  if (li_flags & LI2_FLAG_INTERCONNECT_B_A)
+  {
+    li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+  }
+  if (li_flags & LI2_FLAG_MONITOR_B)
+  {
+    li_config_table[ch_b].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
+    li_config_table[ch_b].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
+  }
+  if (li_flags & LI2_FLAG_MIX_B)
+  {
+    li_config_table[ch_b_v].flag_table[ch_b] |= LI_FLAG_MIX;
+    li_config_table[ch_b_s].flag_table[ch_b] |= LI_FLAG_MIX;
+  }
+  if (li_flags & LI2_FLAG_MONITOR_X)
+    li_config_table[ch_b].chflags |= LI_CHFLAG_MONITOR;
+  if (li_flags & LI2_FLAG_MIX_X)
+    li_config_table[ch_b].chflags |= LI_CHFLAG_MIX;
+  if (li_flags & LI2_FLAG_LOOP_B)
+  {
+    li_config_table[ch_b_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+    li_config_table[ch_b_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+  }
+  if (li_flags & LI2_FLAG_LOOP_PC)
+    li_config_table[ch_b].flag_table[ch_b] |= LI_FLAG_PCCONNECT;
+  if (li_flags & LI2_FLAG_LOOP_X)
+    li_config_table[ch_b].chflags |= LI_CHFLAG_LOOP;
+  if (li_flags & LI2_FLAG_PCCONNECT_A_B)
+    li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_PCCONNECT;
+  if (li_flags & LI2_FLAG_PCCONNECT_B_A)
+    li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_PCCONNECT;
+  if (ch_a_v != ch_a_s)
+  {
+    li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+  }
+  if (ch_b_v != ch_b_s)
+  {
+    li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+    li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+  }
+}
+
+
+static word li_check_main_plci (dword Id, PLCI   *plci)
+{
+  if (plci == NULL)
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    return (_WRONG_IDENTIFIER);
+  }
+  if (!plci->State
+   || !plci->NL.Id || plci->nl_remove_id
+   || (plci->li_bchannel_id == 0))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    return (_WRONG_STATE);
+  }
+  li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+  return (GOOD);
+}
+
+
+static PLCI   *li_check_plci_b (dword Id, PLCI   *plci,
+  dword plci_b_id, word plci_b_write_pos, byte   *p_result)
+{
+  byte ctlr_b;
+  PLCI   *plci_b;
+
+  if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+    LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    PUT_WORD (p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
+    return (NULL);
+  }
+  ctlr_b = 0;
+  if ((plci_b_id & 0x7f) != 0)
+  {
+    ctlr_b = MapController ((byte)(plci_b_id & 0x7f));
+    if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
+      ctlr_b = 0;
+  }
+  if ((ctlr_b == 0)
+   || (((plci_b_id >> 8) & 0xff) == 0)
+   || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI invalid second PLCI %08lx",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b_id));
+    PUT_WORD (p_result, _WRONG_IDENTIFIER);
+    return (NULL);
+  }
+  plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
+  if (!plci_b->State
+   || !plci_b->NL.Id || plci_b->nl_remove_id
+   || (plci_b->li_bchannel_id == 0))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI peer in wrong state %08lx",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b_id));
+    PUT_WORD (p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
+    return (NULL);
+  }
+  li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci = plci_b;
+  if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
+    ((byte)(UnMapController (plci->adapter->Id) & ~EXT_CONTROLLER))
+   && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+    || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI not on same ctrl %08lx",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b_id));
+    PUT_WORD (p_result, _WRONG_IDENTIFIER);
+    return (NULL);
+  }
+  if (!(get_b1_facilities (plci_b, add_b1_facilities (plci_b, plci_b->B1_resource,
+    (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Interconnect peer cannot mix %d",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b->B1_resource));
+    PUT_WORD (p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
+    return (NULL);
+  }
+  return (plci_b);
+}
+
+
+static PLCI   *li2_check_plci_b (dword Id, PLCI   *plci,
+  dword plci_b_id, word plci_b_write_pos, byte   *p_result)
+{
+  byte ctlr_b;
+  PLCI   *plci_b;
+
+  if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+    LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    PUT_WORD (p_result, _WRONG_STATE);
+    return (NULL);
+  }
+  ctlr_b = 0;
+  if ((plci_b_id & 0x7f) != 0)
+  {
+    ctlr_b = MapController ((byte)(plci_b_id & 0x7f));
+    if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
+      ctlr_b = 0;
+  }
+  if ((ctlr_b == 0)
+   || (((plci_b_id >> 8) & 0xff) == 0)
+   || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI invalid second PLCI %08lx",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b_id));
+    PUT_WORD (p_result, _WRONG_IDENTIFIER);
+    return (NULL);
+  }
+  plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
+  if (!plci_b->State
+   || !plci_b->NL.Id || plci_b->nl_remove_id
+   || (plci_b->li_bchannel_id == 0)
+   || (li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci != plci_b))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI peer in wrong state %08lx",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b_id));
+    PUT_WORD (p_result, _WRONG_STATE);
+    return (NULL);
+  }
+  if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
+    ((byte)(UnMapController (plci->adapter->Id) & ~EXT_CONTROLLER))
+   && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+    || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI not on same ctrl %08lx",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b_id));
+    PUT_WORD (p_result, _WRONG_IDENTIFIER);
+    return (NULL);
+  }
+  if (!(get_b1_facilities (plci_b, add_b1_facilities (plci_b, plci_b->B1_resource,
+    (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Interconnect peer cannot mix %d",
+      UnMapId (Id), (char   *)(FILE_), __LINE__, plci_b->B1_resource));
+    PUT_WORD (p_result, _WRONG_STATE);
+    return (NULL);
+  }
+  return (plci_b);
+}
+
+
+static byte mixer_request (dword Id, word Number, DIVA_CAPI_ADAPTER   *a, PLCI   *plci, APPL   *appl, API_PARSE *msg)
+{
+  word Info;
+  word i;
+  dword d, li_flags, plci_b_id;
+  PLCI   *plci_b;
+    API_PARSE li_parms[3];
+    API_PARSE li_req_parms[3];
+    API_PARSE li_participant_struct[2];
+    API_PARSE li_participant_parms[3];
+  word participant_parms_pos;
+  byte result_buffer[32];
+  byte   *result;
+  word result_pos;
+  word plci_b_write_pos;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_request",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  Info = GOOD;
+  result = result_buffer;
+  result_buffer[0] = 0;
+  if (!(a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    Info = _FACILITY_NOT_SUPPORTED;
+  }
+  else if (api_parse (&msg[1].info[1], msg[1].length, "ws", li_parms))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    Info = _WRONG_MESSAGE_FORMAT;
+  }
+  else
+  {
+    result_buffer[0] = 3;
+    PUT_WORD (&result_buffer[1], GET_WORD (li_parms[0].info));
+    result_buffer[3] = 0;
+    switch (GET_WORD (li_parms[0].info))
+    {
+    case LI_GET_SUPPORTED_SERVICES:
+      if (appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
+      {
+        result_buffer[0] = 17;
+        result_buffer[3] = 14;
+        PUT_WORD (&result_buffer[4], GOOD);
+        d = 0;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_CH)
+          d |= LI_CONFERENCING_SUPPORTED;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
+          d |= LI_MONITORING_SUPPORTED;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
+          d |= LI_ANNOUNCEMENTS_SUPPORTED | LI_MIXING_SUPPORTED;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+          d |= LI_CROSS_CONTROLLER_SUPPORTED;
+        PUT_DWORD (&result_buffer[6], d);
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+        {
+          d = 0;
+          for (i = 0; i < li_total_channels; i++)
+          {
+            if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+             && (li_config_table[i].adapter->li_pri
+              || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
+            {
+              d++;
+            }
+          }
+        }
+        else
+        {
+          d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
+        }
+        PUT_DWORD (&result_buffer[10], d / 2);
+        PUT_DWORD (&result_buffer[14], d);
+      }
+      else
+      {
+        result_buffer[0] = 25;
+        result_buffer[3] = 22;
+        PUT_WORD (&result_buffer[4], GOOD);
+        d = LI2_ASYMMETRIC_SUPPORTED | LI2_B_LOOPING_SUPPORTED | LI2_X_LOOPING_SUPPORTED;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
+          d |= LI2_MONITORING_SUPPORTED | LI2_REMOTE_MONITORING_SUPPORTED;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
+          d |= LI2_MIXING_SUPPORTED | LI2_REMOTE_MIXING_SUPPORTED;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_PC)
+          d |= LI2_PC_LOOPING_SUPPORTED;
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+          d |= LI2_CROSS_CONTROLLER_SUPPORTED;
+        PUT_DWORD (&result_buffer[6], d);
+        d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
+        PUT_DWORD (&result_buffer[10], d / 2);
+        PUT_DWORD (&result_buffer[14], d - 1);
+        if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+        {
+          d = 0;
+          for (i = 0; i < li_total_channels; i++)
+          {
+            if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+             && (li_config_table[i].adapter->li_pri
+              || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
+            {
+              d++;
+            }
+          }
+        }
+        PUT_DWORD (&result_buffer[18], d / 2);
+        PUT_DWORD (&result_buffer[22], d - 1);
+      }
+      break;
+
+    case LI_REQ_CONNECT:
+      if (li_parms[1].length == 8)
+      {
+        appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
+        if (api_parse (&li_parms[1].info[1], li_parms[1].length, "dd", li_req_parms))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_MESSAGE_FORMAT;
+          break;
+        }
+        plci_b_id = GET_DWORD (li_req_parms[0].info) & 0xffff;
+        li_flags = GET_DWORD (li_req_parms[1].info);
+        Info = li_check_main_plci (Id, plci);
+        result_buffer[0] = 9;
+        result_buffer[3] = 6;
+        PUT_DWORD (&result_buffer[4], plci_b_id);
+        PUT_WORD (&result_buffer[8], GOOD);
+        if (Info != GOOD)
+          break;
+        result = plci->saved_msg.info;
+        for (i = 0; i <= result_buffer[0]; i++)
+          result[i] = result_buffer[i];
+        plci_b_write_pos = plci->li_plci_b_write_pos;
+        plci_b = li_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
+        if (plci_b == NULL)
+          break;
+        li_update_connect (Id, a, plci, plci_b_id, TRUE, li_flags);
+        plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_LAST_FLAG;
+        plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+        plci->li_plci_b_write_pos = plci_b_write_pos;
+      }
+      else
+      {
+        appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
+        if (api_parse (&li_parms[1].info[1], li_parms[1].length, "ds", li_req_parms))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_MESSAGE_FORMAT;
+          break;
+        }
+        li_flags = GET_DWORD (li_req_parms[0].info) & ~(LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A);
+        Info = li_check_main_plci (Id, plci);
+        result_buffer[0] = 7;
+        result_buffer[3] = 4;
+        PUT_WORD (&result_buffer[4], Info);
+        result_buffer[6] = 0;
+        if (Info != GOOD)
+          break;
+        result = plci->saved_msg.info;
+        for (i = 0; i <= result_buffer[0]; i++)
+          result[i] = result_buffer[i];
+        plci_b_write_pos = plci->li_plci_b_write_pos;
+        participant_parms_pos = 0;
+        result_pos = 7;
+        li2_update_connect (Id, a, plci, UnMapId (Id), TRUE, li_flags);
+        while (participant_parms_pos < li_req_parms[1].length)
+        {
+          result[result_pos] = 6;
+          result_pos += 7;
+          PUT_DWORD (&result[result_pos - 6], 0);
+          PUT_WORD (&result[result_pos - 2], GOOD);
+          if (api_parse (&li_req_parms[1].info[1 + participant_parms_pos],
+            (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
+          {
+            dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+              UnMapId (Id), (char   *)(FILE_), __LINE__));
+            PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+            break;
+          }
+          if (api_parse (&li_participant_struct[0].info[1],
+            li_participant_struct[0].length, "dd", li_participant_parms))
+          {
+            dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+              UnMapId (Id), (char   *)(FILE_), __LINE__));
+            PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+            break;
+          }
+          plci_b_id = GET_DWORD (li_participant_parms[0].info) & 0xffff;
+          li_flags = GET_DWORD (li_participant_parms[1].info);
+          PUT_DWORD (&result[result_pos - 6], plci_b_id);
+          if (sizeof(result) - result_pos < 7)
+          {
+            dbug (1, dprintf ("[%06lx] %s,%d: LI result overrun",
+              UnMapId (Id), (char   *)(FILE_), __LINE__));
+            PUT_WORD (&result[result_pos - 2], _WRONG_STATE);
+            break;
+          }
+          plci_b = li2_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
+          if (plci_b != NULL)
+          {
+            li2_update_connect (Id, a, plci, plci_b_id, TRUE, li_flags);
+            plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id |
+              ((li_flags & (LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A |
+              LI2_FLAG_PCCONNECT_A_B | LI2_FLAG_PCCONNECT_B_A)) ? 0 : LI_PLCI_B_DISC_FLAG);
+            plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+          }
+          participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
+            (&li_req_parms[1].info[1]));
+        }
+        result[0] = (byte)(result_pos - 1);
+        result[3] = (byte)(result_pos - 4);
+        result[6] = (byte)(result_pos - 7);
+        i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES-1 : plci_b_write_pos - 1;
+        if ((plci_b_write_pos == plci->li_plci_b_read_pos)
+         || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
+        {
+          plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
+          plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+        }
+        else
+          plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
+        plci->li_plci_b_write_pos = plci_b_write_pos;
+      }
+      mixer_calculate_coefs (a);
+      plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
+      mixer_notify_update (plci, TRUE);
+      sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+        "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
+      plci->command = 0;
+      plci->li_cmd = GET_WORD (li_parms[0].info);
+      start_internal_command (Id, plci, mixer_command);
+      return (FALSE);
+
+    case LI_REQ_DISCONNECT:
+      if (li_parms[1].length == 4)
+      {
+        appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
+        if (api_parse (&li_parms[1].info[1], li_parms[1].length, "d", li_req_parms))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_MESSAGE_FORMAT;
+          break;
+        }
+        plci_b_id = GET_DWORD (li_req_parms[0].info) & 0xffff;
+        Info = li_check_main_plci (Id, plci);
+        result_buffer[0] = 9;
+        result_buffer[3] = 6;
+        PUT_DWORD (&result_buffer[4], GET_DWORD (li_req_parms[0].info));
+        PUT_WORD (&result_buffer[8], GOOD);
+        if (Info != GOOD)
+          break;
+        result = plci->saved_msg.info;
+        for (i = 0; i <= result_buffer[0]; i++)
+          result[i] = result_buffer[i];
+        plci_b_write_pos = plci->li_plci_b_write_pos;
+        plci_b = li_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
+        if (plci_b == NULL)
+          break;
+        li_update_connect (Id, a, plci, plci_b_id, FALSE, 0);
+        plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG | LI_PLCI_B_LAST_FLAG;
+        plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+        plci->li_plci_b_write_pos = plci_b_write_pos;
+      }
+      else
+      {
+        appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
+        if (api_parse (&li_parms[1].info[1], li_parms[1].length, "s", li_req_parms))
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_MESSAGE_FORMAT;
+          break;
+        }
+        Info = li_check_main_plci (Id, plci);
+        result_buffer[0] = 7;
+        result_buffer[3] = 4;
+        PUT_WORD (&result_buffer[4], Info);
+        result_buffer[6] = 0;
+        if (Info != GOOD)
+          break;
+        result = plci->saved_msg.info;
+        for (i = 0; i <= result_buffer[0]; i++)
+          result[i] = result_buffer[i];
+        plci_b_write_pos = plci->li_plci_b_write_pos;
+        participant_parms_pos = 0;
+        result_pos = 7;
+        while (participant_parms_pos < li_req_parms[0].length)
+        {
+          result[result_pos] = 6;
+          result_pos += 7;
+          PUT_DWORD (&result[result_pos - 6], 0);
+          PUT_WORD (&result[result_pos - 2], GOOD);
+          if (api_parse (&li_req_parms[0].info[1 + participant_parms_pos],
+            (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
+          {
+            dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+              UnMapId (Id), (char   *)(FILE_), __LINE__));
+            PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+            break;
+          }
+          if (api_parse (&li_participant_struct[0].info[1],
+            li_participant_struct[0].length, "d", li_participant_parms))
+          {
+            dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+              UnMapId (Id), (char   *)(FILE_), __LINE__));
+            PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+            break;
+          }
+          plci_b_id = GET_DWORD (li_participant_parms[0].info) & 0xffff;
+          PUT_DWORD (&result[result_pos - 6], plci_b_id);
+          if (sizeof(result) - result_pos < 7)
+          {
+            dbug (1, dprintf ("[%06lx] %s,%d: LI result overrun",
+              UnMapId (Id), (char   *)(FILE_), __LINE__));
+            PUT_WORD (&result[result_pos - 2], _WRONG_STATE);
+            break;
+          }
+          plci_b = li2_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
+          if (plci_b != NULL)
+          {
+            li2_update_connect (Id, a, plci, plci_b_id, FALSE, 0);
+            plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
+            plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+          }
+          participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
+            (&li_req_parms[0].info[1]));
+        }
+        result[0] = (byte)(result_pos - 1);
+        result[3] = (byte)(result_pos - 4);
+        result[6] = (byte)(result_pos - 7);
+        i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES-1 : plci_b_write_pos - 1;
+        if ((plci_b_write_pos == plci->li_plci_b_read_pos)
+         || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
+        {
+          plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
+          plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+        }
+        else
+          plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
+        plci->li_plci_b_write_pos = plci_b_write_pos;
+      }
+      mixer_calculate_coefs (a);
+      plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
+      mixer_notify_update (plci, TRUE);
+      sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+        "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
+      plci->command = 0;
+      plci->li_cmd = GET_WORD (li_parms[0].info);
+      start_internal_command (Id, plci, mixer_command);
+      return (FALSE);
+
+    case LI_REQ_SILENT_UPDATE:
+      if (!plci || !plci->State
+       || !plci->NL.Id || plci->nl_remove_id
+       || (plci->li_bchannel_id == 0)
+       || (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci != plci))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        return (FALSE);
+      }
+      plci_b_write_pos = plci->li_plci_b_write_pos;
+      if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+        LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        return (FALSE);
+      }
+      i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES-1 : plci_b_write_pos - 1;
+      if ((plci_b_write_pos == plci->li_plci_b_read_pos)
+       || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
+      {
+        plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
+        plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+      }
+      else
+        plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
+      plci->li_plci_b_write_pos = plci_b_write_pos;
+      plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
+      plci->command = 0;
+      plci->li_cmd = GET_WORD (li_parms[0].info);
+      start_internal_command (Id, plci, mixer_command);
+      return (FALSE);
+
+    default:
+      dbug (1, dprintf ("[%06lx] %s,%d: LI unknown request %04x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, GET_WORD (li_parms[0].info)));
+      Info = _FACILITY_NOT_SUPPORTED;
+    }
+  }
+  sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+    "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
+  return (FALSE);
+}
+
+
+static void mixer_indication_coefs_set (dword Id, PLCI   *plci)
+{
+  dword d;
+  DIVA_CAPI_ADAPTER   *a;
+    byte result[12];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_indication_coefs_set",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  a = plci->adapter;
+  if (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos)
+  {
+    do
+    {
+      d = plci->li_plci_b_queue[plci->li_plci_b_read_pos];
+      if (!(d & LI_PLCI_B_SKIP_FLAG))
+      {
+        if (plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
+        {
+          if (d & LI_PLCI_B_DISC_FLAG)
+          {
+            result[0] = 5;
+            PUT_WORD (&result[1], LI_IND_DISCONNECT);
+            result[3] = 2;
+            PUT_WORD (&result[4], _LI_USER_INITIATED);
+          }
+          else
+          {
+            result[0] = 7;
+            PUT_WORD (&result[1], LI_IND_CONNECT_ACTIVE);
+            result[3] = 4;
+            PUT_DWORD (&result[4], d & ~LI_PLCI_B_FLAG_MASK);
+          }
+        }
+        else
+        {
+          if (d & LI_PLCI_B_DISC_FLAG)
+          {
+            result[0] = 9;
+            PUT_WORD (&result[1], LI_IND_DISCONNECT);
+            result[3] = 6;
+            PUT_DWORD (&result[4], d & ~LI_PLCI_B_FLAG_MASK);
+            PUT_WORD (&result[8], _LI_USER_INITIATED);
+          }
+          else
+          {
+            result[0] = 7;
+            PUT_WORD (&result[1], LI_IND_CONNECT_ACTIVE);
+            result[3] = 4;
+            PUT_DWORD (&result[4], d & ~LI_PLCI_B_FLAG_MASK);
+          }
+        }
+        sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0,
+          "ws", SELECTOR_LINE_INTERCONNECT, result);
+      }
+      plci->li_plci_b_read_pos = (plci->li_plci_b_read_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ?
+        0 : plci->li_plci_b_read_pos + 1;
+    } while (!(d & LI_PLCI_B_LAST_FLAG) && (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos));
+  }
+}
+
+
+static void mixer_indication_xconnect_from (dword Id, PLCI   *plci, byte   *msg, word length)
+{
+  word i, j, ch;
+  struct xconnect_transfer_address_s s,   *p;
+  DIVA_CAPI_ADAPTER   *a;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_indication_xconnect_from %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, (int) length));
+
+  a = plci->adapter;
+  i = 1;
+  for (i = 1; i < length; i += 16)
+  {
+    s.card_address.low = msg[i] | (msg[i+1] << 8) | (((dword)(msg[i+2])) << 16) | (((dword)(msg[i+3])) << 24);
+    s.card_address.high = msg[i+4] | (msg[i+5] << 8) | (((dword)(msg[i+6])) << 16) | (((dword)(msg[i+7])) << 24);
+    s.offset = msg[i+8] | (msg[i+9] << 8) | (((dword)(msg[i+10])) << 16) | (((dword)(msg[i+11])) << 24);
+    ch = msg[i+12] | (msg[i+13] << 8);
+    j = ch & XCONNECT_CHANNEL_NUMBER_MASK;
+    if (!a->li_pri && (plci->li_bchannel_id == 2))
+      j = 1 - j;
+    j += a->li_base;
+    if (ch & XCONNECT_CHANNEL_PORT_PC)
+      p = &(li_config_table[j].send_pc);
+    else
+      p = &(li_config_table[j].send_b);
+    p->card_address.low = s.card_address.low;
+    p->card_address.high = s.card_address.high;
+    p->offset = s.offset;
+    li_config_table[j].channel |= LI_CHANNEL_ADDRESSES_SET;
+  }
+  if (plci->internal_command_queue[0]
+   && ((plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
+    || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
+    || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)))
+  {
+    (*(plci->internal_command_queue[0]))(Id, plci, 0);
+    if (!plci->internal_command)
+      next_internal_command (Id, plci);
+  }
+  mixer_notify_update (plci, TRUE);
+}
+
+
+static void mixer_indication_xconnect_to (dword Id, PLCI   *plci, byte   *msg, word length)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_indication_xconnect_to %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, (int) length));
+
+}
+
+
+static byte mixer_notify_source_removed (PLCI   *plci, dword plci_b_id)
+{
+  word plci_b_write_pos;
+
+  plci_b_write_pos = plci->li_plci_b_write_pos;
+  if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+    LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 1)
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+      (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+      (char   *)(FILE_), __LINE__));
+    return (FALSE);
+  }
+  plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
+  plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+  plci->li_plci_b_write_pos = plci_b_write_pos;
+  return (TRUE);
+}
+
+
+static void mixer_remove (PLCI   *plci)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  PLCI   *notify_plci;
+  dword plci_b_id;
+  word i, j;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: mixer_remove",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  a = plci->adapter;
+  plci_b_id = (plci->Id << 8) | UnMapController (plci->adapter->Id);
+  if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
+  {
+    if ((plci->li_bchannel_id != 0)
+     && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+    {
+      i = a->li_base + (plci->li_bchannel_id - 1);
+      if ((li_config_table[i].curchnl | li_config_table[i].channel) & LI_CHANNEL_INVOLVED)
+      {
+        for (j = 0; j < li_total_channels; j++)
+        {
+          if ((li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+           || (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT))
+          {
+            notify_plci = li_config_table[j].plci;
+            if ((notify_plci != NULL)
+             && (notify_plci != plci)
+             && (notify_plci->appl != NULL)
+             && !(notify_plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
+             && (notify_plci->State)
+             && notify_plci->NL.Id && !notify_plci->nl_remove_id)
+            {
+              mixer_notify_source_removed (notify_plci, plci_b_id);
+            }
+          }
+        }
+        mixer_clear_config (plci);
+        mixer_calculate_coefs (a);
+        mixer_notify_update (plci, TRUE);
+      }
+      li_config_table[i].plci = NULL;
+      plci->li_bchannel_id = 0;
+    }
+  }
+}
+
+
+/*------------------------------------------------------------------*/
+/* Echo canceller facilities                                        */
+/*------------------------------------------------------------------*/
+
+
+static void ec_write_parameters (PLCI   *plci)
+{
+  word w;
+    byte parameter_buffer[6];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_write_parameters",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  parameter_buffer[0] = 5;
+  parameter_buffer[1] = DSP_CTRL_SET_LEC_PARAMETERS;
+  PUT_WORD (&parameter_buffer[2], plci->ec_idi_options);
+  plci->ec_idi_options &= ~LEC_RESET_COEFFICIENTS;
+  w = (plci->ec_tail_length == 0) ? 128 : plci->ec_tail_length;
+  PUT_WORD (&parameter_buffer[4], w);
+  add_p (plci, FTY, parameter_buffer);
+  sig_req (plci, TEL_CTRL, 0);
+  send_req (plci);
+}
+
+
+static void ec_clear_config (PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_clear_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
+    LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING;
+  plci->ec_tail_length = 0;
+}
+
+
+static void ec_prepare_switch (dword Id, PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_prepare_switch",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+}
+
+
+static word ec_save_config (dword Id, PLCI   *plci, byte Rc)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_save_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  return (GOOD);
+}
+
+
+static word ec_restore_config (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_restore_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  Info = GOOD;
+  if (plci->B1_facilities & B1_FACILITY_EC)
+  {
+    switch (plci->adjust_b_state)
+    {
+    case ADJUST_B_RESTORE_EC_1:
+      plci->internal_command = plci->adjust_b_command;
+      if (plci->sig_req)
+      {
+        plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
+        break;
+      }
+      ec_write_parameters (plci);
+      plci->adjust_b_state = ADJUST_B_RESTORE_EC_2;
+      break;
+    case ADJUST_B_RESTORE_EC_2:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Restore EC failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _WRONG_STATE;
+        break;
+      }
+      break;
+    }
+  }
+  return (Info);
+}
+
+
+static void ec_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word internal_command, Info;
+    byte result[8];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_command %02x %04x %04x %04x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command,
+    plci->ec_cmd, plci->ec_idi_options, plci->ec_tail_length));
+
+  Info = GOOD;
+  if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
+  {
+    result[0] = 2;
+    PUT_WORD (&result[1], EC_SUCCESS);
+  }
+  else
+  {
+    result[0] = 5;
+    PUT_WORD (&result[1], plci->ec_cmd);
+    result[3] = 2;
+    PUT_WORD (&result[4], GOOD);
+  }
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (plci->ec_cmd)
+  {
+  case EC_ENABLE_OPERATION:
+  case EC_FREEZE_COEFFICIENTS:
+  case EC_RESUME_COEFFICIENT_UPDATE:
+  case EC_RESET_COEFFICIENTS:
+    switch (internal_command)
+    {
+    default:
+      adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+        B1_FACILITY_EC), EC_COMMAND_1);
+    case EC_COMMAND_1:
+      if (adjust_b_process (Id, plci, Rc) != GOOD)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Load EC failed",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      if (plci->internal_command)
+        return;
+    case EC_COMMAND_2:
+      if (plci->sig_req)
+      {
+        plci->internal_command = EC_COMMAND_2;
+        return;
+      }
+      plci->internal_command = EC_COMMAND_3;
+      ec_write_parameters (plci);
+      return;
+    case EC_COMMAND_3:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Enable EC failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      break;
+    }
+    break;
+
+  case EC_DISABLE_OPERATION:
+    switch (internal_command)
+    {
+    default:
+    case EC_COMMAND_1:
+      if (plci->B1_facilities & B1_FACILITY_EC)
+      {
+        if (plci->sig_req)
+        {
+          plci->internal_command = EC_COMMAND_1;
+          return;
+        }
+        plci->internal_command = EC_COMMAND_2;
+        ec_write_parameters (plci);
+        return;
+      }
+      Rc = OK;
+    case EC_COMMAND_2:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Disable EC failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities &
+        ~B1_FACILITY_EC), EC_COMMAND_3);
+    case EC_COMMAND_3:
+      if (adjust_b_process (Id, plci, Rc) != GOOD)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Unload EC failed",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _FACILITY_NOT_SUPPORTED;
+        break;
+      }
+      if (plci->internal_command)
+        return;
+      break;
+    }
+    break;
+  }
+  sendf (plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
+    "wws", Info, (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
+    PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
+}
+
+
+static byte ec_request (dword Id, word Number, DIVA_CAPI_ADAPTER   *a, PLCI   *plci, APPL   *appl, API_PARSE *msg)
+{
+  word Info;
+  word opt;
+    API_PARSE ec_parms[3];
+    byte result[16];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_request",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  Info = GOOD;
+  result[0] = 0;
+  if (!(a->man_profile.private_options & (1L << PRIVATE_ECHO_CANCELLER)))
+  {
+    dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+    Info = _FACILITY_NOT_SUPPORTED;
+  }
+  else
+  {
+    if (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
+    {
+      if (api_parse (&msg[1].info[1], msg[1].length, "w", ec_parms))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _WRONG_MESSAGE_FORMAT;
+      }
+      else
+      {
+        if (plci == NULL)
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_IDENTIFIER;
+        }
+        else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_STATE;
+        }
+        else
+        {
+          plci->command = 0;
+          plci->ec_cmd = GET_WORD (ec_parms[0].info);
+          plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
+          result[0] = 2;
+          PUT_WORD (&result[1], EC_SUCCESS);
+          if (msg[1].length >= 4)
+          {
+            opt = GET_WORD (&ec_parms[0].info[2]);
+            plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
+              LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
+            if (!(opt & EC_DISABLE_NON_LINEAR_PROCESSING))
+              plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
+            if (opt & EC_DETECT_DISABLE_TONE)
+              plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
+            if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
+              plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
+            if (msg[1].length >= 6)
+            {
+              plci->ec_tail_length = GET_WORD (&ec_parms[0].info[4]);
+            }
+          }
+          switch (plci->ec_cmd)
+          {
+          case EC_ENABLE_OPERATION:
+            plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
+            start_internal_command (Id, plci, ec_command);
+            return (FALSE);
+
+          case EC_DISABLE_OPERATION:
+            plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
+              LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
+              LEC_RESET_COEFFICIENTS;
+            start_internal_command (Id, plci, ec_command);
+            return (FALSE);
+
+          case EC_FREEZE_COEFFICIENTS:
+            plci->ec_idi_options |= LEC_FREEZE_COEFFICIENTS;
+            start_internal_command (Id, plci, ec_command);
+            return (FALSE);
+
+          case EC_RESUME_COEFFICIENT_UPDATE:
+            plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
+            start_internal_command (Id, plci, ec_command);
+            return (FALSE);
+
+          case EC_RESET_COEFFICIENTS:
+            plci->ec_idi_options |= LEC_RESET_COEFFICIENTS;
+            start_internal_command (Id, plci, ec_command);
+            return (FALSE);
+
+          default:
+            dbug (1, dprintf ("[%06lx] %s,%d: EC unknown request %04x",
+              UnMapId (Id), (char   *)(FILE_), __LINE__, plci->ec_cmd));
+            PUT_WORD (&result[1], EC_UNSUPPORTED_OPERATION);
+          }
+        }
+      }
+    }
+    else
+    {
+      if (api_parse (&msg[1].info[1], msg[1].length, "ws", ec_parms))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+          UnMapId (Id), (char   *)(FILE_), __LINE__));
+        Info = _WRONG_MESSAGE_FORMAT;
+      }
+      else
+      {
+        if (GET_WORD (ec_parms[0].info) == EC_GET_SUPPORTED_SERVICES)
+        {
+          result[0] = 11;
+          PUT_WORD (&result[1], EC_GET_SUPPORTED_SERVICES);
+          result[3] = 8;
+          PUT_WORD (&result[4], GOOD);
+          PUT_WORD (&result[6], 0x0007);
+          PUT_WORD (&result[8], LEC_MAX_SUPPORTED_TAIL_LENGTH);
+          PUT_WORD (&result[10], 0);
+        }
+        else if (plci == NULL)
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_IDENTIFIER;
+        }
+        else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
+        {
+          dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+            UnMapId (Id), (char   *)(FILE_), __LINE__));
+          Info = _WRONG_STATE;
+        }
+        else
+        {
+          plci->command = 0;
+          plci->ec_cmd = GET_WORD (ec_parms[0].info);
+          plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
+          result[0] = 5;
+          PUT_WORD (&result[1], plci->ec_cmd);
+          result[3] = 2;
+          PUT_WORD (&result[4], GOOD);
+          plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
+            LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
+          plci->ec_tail_length = 0;
+          if (ec_parms[1].length >= 2)
+          {
+            opt = GET_WORD (&ec_parms[1].info[1]);
+            if (opt & EC_ENABLE_NON_LINEAR_PROCESSING)
+              plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
+            if (opt & EC_DETECT_DISABLE_TONE)
+              plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
+            if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
+              plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
+            if (ec_parms[1].length >= 4)
+            {
+              plci->ec_tail_length = GET_WORD (&ec_parms[1].info[3]);
+            }
+          }
+          switch (plci->ec_cmd)
+          {
+          case EC_ENABLE_OPERATION:
+            plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
+            start_internal_command (Id, plci, ec_command);
+            return (FALSE);
+
+          case EC_DISABLE_OPERATION:
+            plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
+              LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
+              LEC_RESET_COEFFICIENTS;
+            start_internal_command (Id, plci, ec_command);
+            return (FALSE);
+
+          default:
+            dbug (1, dprintf ("[%06lx] %s,%d: EC unknown request %04x",
+              UnMapId (Id), (char   *)(FILE_), __LINE__, plci->ec_cmd));
+            PUT_WORD (&result[4], _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP);
+          }
+        }
+      }
+    }
+  }
+  sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+    "wws", Info, (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
+    PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
+  return (FALSE);
+}
+
+
+static void ec_indication (dword Id, PLCI   *plci, byte   *msg, word length)
+{
+    byte result[8];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: ec_indication",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+  if (!(plci->ec_idi_options & LEC_MANUAL_DISABLE))
+  {
+    if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
+    {
+      result[0] = 2;
+      PUT_WORD (&result[1], 0);
+      switch (msg[1])
+      {
+      case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
+        PUT_WORD (&result[1], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
+        break;
+      case LEC_DISABLE_TYPE_REVERSED_2100HZ:
+        PUT_WORD (&result[1], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
+        break;
+      case LEC_DISABLE_RELEASED:
+        PUT_WORD (&result[1], EC_BYPASS_RELEASED);
+        break;
+      }
+    }
+    else
+    {
+      result[0] = 5;
+      PUT_WORD (&result[1], EC_BYPASS_INDICATION);
+      result[3] = 2;
+      PUT_WORD (&result[4], 0);
+      switch (msg[1])
+      {
+      case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
+        PUT_WORD (&result[4], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
+        break;
+      case LEC_DISABLE_TYPE_REVERSED_2100HZ:
+        PUT_WORD (&result[4], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
+        break;
+      case LEC_DISABLE_RELEASED:
+        PUT_WORD (&result[4], EC_BYPASS_RELEASED);
+        break;
+      }
+    }
+    sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
+      PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
+  }
+}
+
+
+
+/*------------------------------------------------------------------*/
+/* Advanced voice                                                   */
+/*------------------------------------------------------------------*/
+
+static void adv_voice_write_coefs (PLCI   *plci, word write_command)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word i;
+  byte *p;
+
+  word w, n, j, k;
+  byte ch_map[MIXER_CHANNELS_BRI];
+
+    byte coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE + 2];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_write_coefs %d",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, write_command));
+
+  a = plci->adapter;
+  p = coef_buffer + 1;
+  *(p++) = DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS;
+  i = 0;
+  while (i + sizeof(word) <= a->adv_voice_coef_length)
+  {
+    PUT_WORD (p, GET_WORD (a->adv_voice_coef_buffer + i));
+    p += 2;
+    i += 2;
+  }
+  while (i < ADV_VOICE_OLD_COEF_COUNT * sizeof(word))
+  {
+    PUT_WORD (p, 0x8000);
+    p += 2;
+    i += 2;
+  }
+
+  if (!a->li_pri && (plci->li_bchannel_id == 0))
+  {
+    if ((li_config_table[a->li_base].plci == NULL) && (li_config_table[a->li_base + 1].plci != NULL))
+    {
+      plci->li_bchannel_id = 1;
+      li_config_table[a->li_base].plci = plci;
+      dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
+        (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+        (char   *)(FILE_), __LINE__, plci->li_bchannel_id));
+    }
+    else if ((li_config_table[a->li_base].plci != NULL) && (li_config_table[a->li_base + 1].plci == NULL))
+    {
+      plci->li_bchannel_id = 2;
+      li_config_table[a->li_base + 1].plci = plci;
+      dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
+        (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+        (char   *)(FILE_), __LINE__, plci->li_bchannel_id));
+    }
+  }
+  if (!a->li_pri && (plci->li_bchannel_id != 0)
+   && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+  {
+    i = a->li_base + (plci->li_bchannel_id - 1);
+    switch (write_command)
+    {
+    case ADV_VOICE_WRITE_ACTIVATION:
+      j = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+      k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+      if (!(plci->B1_facilities & B1_FACILITY_MIXER))
+      {
+        li_config_table[j].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
+        li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
+      }
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+      {
+        li_config_table[k].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
+        li_config_table[i].flag_table[k] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
+        li_config_table[k].flag_table[j] |= LI_FLAG_CONFERENCE;
+        li_config_table[j].flag_table[k] |= LI_FLAG_CONFERENCE;
+      }
+      mixer_calculate_coefs (a);
+      li_config_table[i].curchnl = li_config_table[i].channel;
+      li_config_table[j].curchnl = li_config_table[j].channel;
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+        li_config_table[k].curchnl = li_config_table[k].channel;
+      break;
+
+    case ADV_VOICE_WRITE_DEACTIVATION:
+      for (j = 0; j < li_total_channels; j++)
+      {
+        li_config_table[i].flag_table[j] = 0;
+        li_config_table[j].flag_table[i] = 0;
+      }
+      k = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+      for (j = 0; j < li_total_channels; j++)
+      {
+        li_config_table[k].flag_table[j] = 0;
+        li_config_table[j].flag_table[k] = 0;
+      }
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+      {
+        k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+        for (j = 0; j < li_total_channels; j++)
+        {
+          li_config_table[k].flag_table[j] = 0;
+          li_config_table[j].flag_table[k] = 0;
+        }
+      }
+      mixer_calculate_coefs (a);
+      break;
+    }
+    if (plci->B1_facilities & B1_FACILITY_MIXER)
+    {
+      w = 0;
+      if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length)
+        w = GET_WORD (a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
+      if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+        w |= MIXER_FEATURE_ENABLE_TX_DATA;
+      if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+        w |= MIXER_FEATURE_ENABLE_RX_DATA;
+      *(p++) = (byte) w;
+      *(p++) = (byte)(w >> 8);
+      for (j = 0; j < sizeof(ch_map); j += 2)
+      {
+        ch_map[j] = (byte)(j + (plci->li_bchannel_id - 1));
+        ch_map[j+1] = (byte)(j + (2 - plci->li_bchannel_id));
+      }
+      for (n = 0; n < sizeof(mixer_write_prog_bri) / sizeof(mixer_write_prog_bri[0]); n++)
+      {
+        i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
+        j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
+        if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
+        {
+          *(p++) = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
+          w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+          li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
+        }
+        else
+        {
+          *(p++) = (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n < a->adv_voice_coef_length) ?
+            a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n] : 0x00;
+        }
+      }
+    }
+    else
+    {
+      for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
+        *(p++) = a->adv_voice_coef_buffer[i];
+    }
+  }
+  else
+
+  {
+    for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
+      *(p++) = a->adv_voice_coef_buffer[i];
+  }
+  coef_buffer[0] = (p - coef_buffer) - 1;
+  add_p (plci, FTY, coef_buffer);
+  sig_req (plci, TEL_CTRL, 0);
+  send_req (plci);
+}
+
+
+static void adv_voice_clear_config (PLCI   *plci)
+{
+  DIVA_CAPI_ADAPTER   *a;
+
+  word i, j;
+
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_clear_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  a = plci->adapter;
+  if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+  {
+    a->adv_voice_coef_length = 0;
+
+    if (!a->li_pri && (plci->li_bchannel_id != 0)
+     && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+    {
+      i = a->li_base + (plci->li_bchannel_id - 1);
+      li_config_table[i].curchnl = 0;
+      li_config_table[i].channel = 0;
+      li_config_table[i].chflags = 0;
+      for (j = 0; j < li_total_channels; j++)
+      {
+        li_config_table[i].flag_table[j] = 0;
+        li_config_table[j].flag_table[i] = 0;
+        li_config_table[i].coef_table[j] = 0;
+        li_config_table[j].coef_table[i] = 0;
+      }
+      li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
+      i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+      li_config_table[i].curchnl = 0;
+      li_config_table[i].channel = 0;
+      li_config_table[i].chflags = 0;
+      for (j = 0; j < li_total_channels; j++)
+      {
+        li_config_table[i].flag_table[j] = 0;
+        li_config_table[j].flag_table[i] = 0;
+        li_config_table[i].coef_table[j] = 0;
+        li_config_table[j].coef_table[i] = 0;
+      }
+      if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+      {
+        i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+        li_config_table[i].curchnl = 0;
+        li_config_table[i].channel = 0;
+        li_config_table[i].chflags = 0;
+        for (j = 0; j < li_total_channels; j++)
+        {
+          li_config_table[i].flag_table[j] = 0;
+          li_config_table[j].flag_table[i] = 0;
+          li_config_table[i].coef_table[j] = 0;
+          li_config_table[j].coef_table[i] = 0;
+        }
+      }
+    }
+
+  }
+}
+
+
+static void adv_voice_prepare_switch (dword Id, PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_prepare_switch",
+    UnMapId (Id), (char   *)(FILE_), __LINE__));
+
+}
+
+
+static word adv_voice_save_config (dword Id, PLCI   *plci, byte Rc)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_save_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  return (GOOD);
+}
+
+
+static word adv_voice_restore_config (dword Id, PLCI   *plci, byte Rc)
+{
+  DIVA_CAPI_ADAPTER   *a;
+  word Info;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_restore_config %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  Info = GOOD;
+  a = plci->adapter;
+  if ((plci->B1_facilities & B1_FACILITY_VOICE)
+   && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+  {
+    switch (plci->adjust_b_state)
+    {
+    case ADJUST_B_RESTORE_VOICE_1:
+      plci->internal_command = plci->adjust_b_command;
+      if (plci->sig_req)
+      {
+        plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
+        break;
+      }
+      adv_voice_write_coefs (plci, ADV_VOICE_WRITE_UPDATE);
+      plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_2;
+      break;
+    case ADJUST_B_RESTORE_VOICE_2:
+      if ((Rc != OK) && (Rc != OK_FC))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Restore voice config failed %02x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+        Info = _WRONG_STATE;
+        break;
+      }
+      break;
+    }
+  }
+  return (Info);
+}
+
+
+
+
+/*------------------------------------------------------------------*/
+/* B1 resource switching                                            */
+/*------------------------------------------------------------------*/
+
+static byte b1_facilities_table[] =
+{
+  0x00,  /* 0  No bchannel resources      */
+  0x00,  /* 1  Codec (automatic law)      */
+  0x00,  /* 2  Codec (A-law)              */
+  0x00,  /* 3  Codec (y-law)              */
+  0x00,  /* 4  HDLC for X.21              */
+  0x00,  /* 5  HDLC                       */
+  0x00,  /* 6  External Device 0          */
+  0x00,  /* 7  External Device 1          */
+  0x00,  /* 8  HDLC 56k                   */
+  0x00,  /* 9  Transparent                */
+  0x00,  /* 10 Loopback to network        */
+  0x00,  /* 11 Test pattern to net        */
+  0x00,  /* 12 Rate adaptation sync       */
+  0x00,  /* 13 Rate adaptation async      */
+  0x00,  /* 14 R-Interface                */
+  0x00,  /* 15 HDLC 128k leased line      */
+  0x00,  /* 16 FAX                        */
+  0x00,  /* 17 Modem async                */
+  0x00,  /* 18 Modem sync HDLC            */
+  0x00,  /* 19 V.110 async HDLC           */
+  0x12,  /* 20 Adv voice (Trans,mixer)    */
+  0x00,  /* 21 Codec connected to IC      */
+  0x0c,  /* 22 Trans,DTMF                 */
+  0x1e,  /* 23 Trans,DTMF+mixer           */
+  0x1f,  /* 24 Trans,DTMF+mixer+local     */
+  0x13,  /* 25 Trans,mixer+local          */
+  0x12,  /* 26 HDLC,mixer                 */
+  0x12,  /* 27 HDLC 56k,mixer             */
+  0x2c,  /* 28 Trans,LEC+DTMF             */
+  0x3e,  /* 29 Trans,LEC+DTMF+mixer       */
+  0x3f,  /* 30 Trans,LEC+DTMF+mixer+local */
+  0x2c,  /* 31 RTP,LEC+DTMF               */
+  0x3e,  /* 32 RTP,LEC+DTMF+mixer         */
+  0x3f,  /* 33 RTP,LEC+DTMF+mixer+local   */
+  0x00,  /* 34 Signaling task             */
+  0x00,  /* 35 PIAFS                      */
+  0x0c,  /* 36 Trans,DTMF+TONE            */
+  0x1e,  /* 37 Trans,DTMF+TONE+mixer      */
+  0x1f   /* 38 Trans,DTMF+TONE+mixer+local*/
+};
+
+
+static word get_b1_facilities (PLCI   * plci, byte b1_resource)
+{
+  word b1_facilities;
+
+  b1_facilities = b1_facilities_table[b1_resource];
+  if ((b1_resource == 9) || (b1_resource == 20) || (b1_resource == 25))
+  {
+
+    if (!(((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
+       || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id-1] & (1L << PRIVATE_DTMF_TONE)))))
+
+    {
+      if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND)
+        b1_facilities |= B1_FACILITY_DTMFX;
+      if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)
+        b1_facilities |= B1_FACILITY_DTMFR;
+    }
+  }
+  if ((b1_resource == 17) || (b1_resource == 18))
+  {
+    if (plci->adapter->manufacturer_features & (MANUFACTURER_FEATURE_V18 | MANUFACTURER_FEATURE_VOWN))
+      b1_facilities |= B1_FACILITY_DTMFX | B1_FACILITY_DTMFR;
+  }
+/*
+  dbug (1, dprintf ("[%06lx] %s,%d: get_b1_facilities %d %04x",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char far *)(FILE_), __LINE__, b1_resource, b1_facilites));
+*/
+  return (b1_facilities);
+}
+
+
+static byte add_b1_facilities (PLCI   * plci, byte b1_resource, word b1_facilities)
+{
+  byte b;
+
+  switch (b1_resource)
+  {
+  case 5:
+  case 26:
+    if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+      b = 26;
+    else
+      b = 5;
+    break;
+
+  case 8:
+  case 27:
+    if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+      b = 27;
+    else
+      b = 8;
+    break;
+
+  case 9:
+  case 20:
+  case 22:
+  case 23:
+  case 24:
+  case 25:
+  case 28:
+  case 29:
+  case 30:
+  case 36:
+  case 37:
+  case 38:
+    if (b1_facilities & B1_FACILITY_EC)
+    {
+      if (b1_facilities & B1_FACILITY_LOCAL)
+        b = 30;
+      else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+        b = 29;
+      else
+        b = 28;
+    }
+
+    else if ((b1_facilities & (B1_FACILITY_DTMFX | B1_FACILITY_DTMFR | B1_FACILITY_MIXER))
+      && (((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
+       || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id-1] & (1L << PRIVATE_DTMF_TONE)))))
+    {
+      if (b1_facilities & B1_FACILITY_LOCAL)
+        b = 38;
+      else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+        b = 37;
+      else
+        b = 36;
+    }
+
+    else if (((plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
+      && !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
+     || ((b1_facilities & B1_FACILITY_DTMFR)
+      && ((b1_facilities & B1_FACILITY_MIXER)
+       || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)))
+     || ((b1_facilities & B1_FACILITY_DTMFX)
+      && ((b1_facilities & B1_FACILITY_MIXER)
+       || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND))))
+    {
+      if (b1_facilities & B1_FACILITY_LOCAL)
+        b = 24;
+      else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+        b = 23;
+      else
+        b = 22;
+    }
+    else
+    {
+      if (b1_facilities & B1_FACILITY_LOCAL)
+        b = 25;
+      else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+        b = 20;
+      else
+        b = 9;
+    }
+    break;
+
+  case 31:
+  case 32:
+  case 33:
+    if (b1_facilities & B1_FACILITY_LOCAL)
+      b = 33;
+    else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+      b = 32;
+    else
+      b = 31;
+    break;
+
+  default:
+    b = b1_resource;
+  }
+  dbug (1, dprintf ("[%06lx] %s,%d: add_b1_facilities %d %04x %d %04x",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__,
+    b1_resource, b1_facilities, b, get_b1_facilities (plci, b)));
+  return (b);
+}
+
+
+static void adjust_b1_facilities (PLCI   *plci, byte new_b1_resource, word new_b1_facilities)
+{
+  word removed_facilities;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adjust_b1_facilities %d %04x %04x",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__, new_b1_resource, new_b1_facilities,
+    new_b1_facilities & get_b1_facilities (plci, new_b1_resource)));
+
+  new_b1_facilities &= get_b1_facilities (plci, new_b1_resource);
+  removed_facilities = plci->B1_facilities & ~new_b1_facilities;
+
+  if (removed_facilities & B1_FACILITY_EC)
+    ec_clear_config (plci);
+
+
+  if (removed_facilities & B1_FACILITY_DTMFR)
+  {
+    dtmf_rec_clear_config (plci);
+    dtmf_parameter_clear_config (plci);
+  }
+  if (removed_facilities & B1_FACILITY_DTMFX)
+    dtmf_send_clear_config (plci);
+
+
+  if (removed_facilities & B1_FACILITY_MIXER)
+    mixer_clear_config (plci);
+
+  if (removed_facilities & B1_FACILITY_VOICE)
+    adv_voice_clear_config (plci);
+  plci->B1_facilities = new_b1_facilities;
+}
+
+
+static void adjust_b_clear (PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adjust_b_clear",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  plci->adjust_b_restore = FALSE;
+}
+
+
+static word adjust_b_process (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  byte b1_resource;
+  NCCI   * ncci_ptr;
+    API_PARSE bp[2];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adjust_b_process %02x %d",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+  Info = GOOD;
+  switch (plci->adjust_b_state)
+  {
+  case ADJUST_B_START:
+    if ((plci->adjust_b_parms_msg == NULL)
+     && (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
+     && ((plci->adjust_b_mode & ~(ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 |
+      ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_RESTORE)) == 0))
+    {
+      b1_resource = (plci->adjust_b_mode == ADJUST_B_MODE_NO_RESOURCE) ?
+        0 : add_b1_facilities (plci, plci->B1_resource, plci->adjust_b_facilities);
+      if (b1_resource == plci->B1_resource)
+      {
+        adjust_b1_facilities (plci, b1_resource, plci->adjust_b_facilities);
+        break;
+      }
+      if (plci->adjust_b_facilities & ~get_b1_facilities (plci, b1_resource))
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Adjust B nonsupported facilities %d %d %04x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__,
+          plci->B1_resource, b1_resource, plci->adjust_b_facilities));
+        Info = _WRONG_STATE;
+        break;
+      }
+    }
+    if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+    {
+
+      mixer_prepare_switch (Id, plci);
+
+
+      dtmf_prepare_switch (Id, plci);
+      dtmf_parameter_prepare_switch (Id, plci);
+
+
+      ec_prepare_switch (Id, plci);
+
+      adv_voice_prepare_switch (Id, plci);
+    }
+    plci->adjust_b_state = ADJUST_B_SAVE_MIXER_1;
+    Rc = OK;
+  case ADJUST_B_SAVE_MIXER_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+    {
+
+      Info = mixer_save_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_SAVE_DTMF_1;
+    Rc = OK;
+  case ADJUST_B_SAVE_DTMF_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+    {
+
+      Info = dtmf_save_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_REMOVE_L23_1;
+  case ADJUST_B_REMOVE_L23_1:
+    if ((plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
+     && plci->NL.Id && !plci->nl_remove_id)
+    {
+      plci->internal_command = plci->adjust_b_command;
+      if (plci->adjust_b_ncci != 0)
+      {
+        ncci_ptr = &(plci->adapter->ncci[plci->adjust_b_ncci]);
+        while (ncci_ptr->data_pending)
+        {
+          plci->data_sent_ptr = ncci_ptr->DBuffer[ncci_ptr->data_out].P;
+          data_rc (plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
+        }
+        while (ncci_ptr->data_ack_pending)
+          data_ack (plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
+      }
+      nl_req_ncci (plci, REMOVE,
+        (byte)((plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) ? plci->adjust_b_ncci : 0));
+      send_req (plci);
+      plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
+      break;
+    }
+    plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
+    Rc = OK;
+  case ADJUST_B_REMOVE_L23_2:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Adjust B remove failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      Info = _WRONG_STATE;
+      break;
+    }
+    if (plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
+    {
+      if (plci_nl_busy (plci))
+      {
+        plci->internal_command = plci->adjust_b_command;
+        break;
+      }
+    }
+    plci->adjust_b_state = ADJUST_B_SAVE_EC_1;
+    Rc = OK;
+  case ADJUST_B_SAVE_EC_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+    {
+
+      Info = ec_save_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_SAVE_DTMF_PARAMETER_1;
+    Rc = OK;
+  case ADJUST_B_SAVE_DTMF_PARAMETER_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+    {
+
+      Info = dtmf_parameter_save_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_SAVE_VOICE_1;
+    Rc = OK;
+  case ADJUST_B_SAVE_VOICE_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+    {
+      Info = adv_voice_save_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+    }
+    plci->adjust_b_state = ADJUST_B_SWITCH_L1_1;
+  case ADJUST_B_SWITCH_L1_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
+    {
+      if (plci->sig_req)
+      {
+        plci->internal_command = plci->adjust_b_command;
+        break;
+      }
+      if (plci->adjust_b_parms_msg != NULL)
+        api_load_msg (plci->adjust_b_parms_msg, bp);
+      else
+        api_load_msg (&plci->B_protocol, bp);
+      Info = add_b1 (plci, bp,
+        (word)((plci->adjust_b_mode & ADJUST_B_MODE_NO_RESOURCE) ? 2 : 0),
+        plci->adjust_b_facilities);
+      if (Info != GOOD)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Adjust B invalid L1 parameters %d %04x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__,
+          plci->B1_resource, plci->adjust_b_facilities));
+        break;
+      }
+      plci->internal_command = plci->adjust_b_command;
+      sig_req (plci, RESOURCES, 0);
+      send_req (plci);
+      plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
+      break;
+    }
+    plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
+    Rc = OK;
+  case ADJUST_B_SWITCH_L1_2:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Adjust B switch failed %02x %d %04x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__,
+        Rc, plci->B1_resource, plci->adjust_b_facilities));
+      Info = _WRONG_STATE;
+      break;
+    }
+    plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
+    Rc = OK;
+  case ADJUST_B_RESTORE_VOICE_1:
+  case ADJUST_B_RESTORE_VOICE_2:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+    {
+      Info = adv_voice_restore_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+    }
+    plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
+    Rc = OK;
+  case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
+  case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+    {
+
+      Info = dtmf_parameter_restore_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
+    Rc = OK;
+  case ADJUST_B_RESTORE_EC_1:
+  case ADJUST_B_RESTORE_EC_2:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+    {
+
+      Info = ec_restore_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_ASSIGN_L23_1;
+  case ADJUST_B_ASSIGN_L23_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
+    {
+      if (plci_nl_busy (plci))
+      {
+        plci->internal_command = plci->adjust_b_command;
+        break;
+      }
+      if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
+        plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
+      if (plci->adjust_b_parms_msg != NULL)
+        api_load_msg (plci->adjust_b_parms_msg, bp);
+      else
+        api_load_msg (&plci->B_protocol, bp);
+      Info = add_b23 (plci, bp);
+      if (Info != GOOD)
+      {
+        dbug (1, dprintf ("[%06lx] %s,%d: Adjust B invalid L23 parameters %04x",
+          UnMapId (Id), (char   *)(FILE_), __LINE__, Info));
+        break;
+      }
+      plci->internal_command = plci->adjust_b_command;
+      nl_req_ncci (plci, ASSIGN, 0);
+      send_req (plci);
+      plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
+      break;
+    }
+    plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
+    Rc = ASSIGN_OK;
+  case ADJUST_B_ASSIGN_L23_2:
+    if ((Rc != OK) && (Rc != OK_FC) && (Rc != ASSIGN_OK))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Adjust B assign failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      Info = _WRONG_STATE;
+      break;
+    }
+    if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
+    {
+      if (Rc != ASSIGN_OK)
+      {
+        plci->internal_command = plci->adjust_b_command;
+        break;
+      }
+    }
+    if (plci->adjust_b_mode & ADJUST_B_MODE_USER_CONNECT)
+    {
+      plci->adjust_b_restore = TRUE;
+      break;
+    }
+    plci->adjust_b_state = ADJUST_B_CONNECT_1;
+  case ADJUST_B_CONNECT_1:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
+    {
+      plci->internal_command = plci->adjust_b_command;
+      if (plci_nl_busy (plci))
+        break;
+      nl_req_ncci (plci, N_CONNECT, 0);
+      send_req (plci);
+      plci->adjust_b_state = ADJUST_B_CONNECT_2;
+      break;
+    }
+    plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+    Rc = OK;
+  case ADJUST_B_CONNECT_2:
+  case ADJUST_B_CONNECT_3:
+  case ADJUST_B_CONNECT_4:
+    if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Adjust B connect failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      Info = _WRONG_STATE;
+      break;
+    }
+    if (Rc == OK)
+    {
+      if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
+      {
+        get_ncci (plci, (byte)(Id >> 16), plci->adjust_b_ncci);
+        Id = (Id & 0xffff) | (((dword)(plci->adjust_b_ncci)) << 16);
+      }
+      if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
+        plci->adjust_b_state = ADJUST_B_CONNECT_3;
+      else if (plci->adjust_b_state == ADJUST_B_CONNECT_4)
+        plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+    }
+    else if (Rc == 0)
+    {
+      if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
+        plci->adjust_b_state = ADJUST_B_CONNECT_4;
+      else if (plci->adjust_b_state == ADJUST_B_CONNECT_3)
+        plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+    }
+    if (plci->adjust_b_state != ADJUST_B_RESTORE_DTMF_1)
+    {
+      plci->internal_command = plci->adjust_b_command;
+      break;
+    }
+    Rc = OK;
+  case ADJUST_B_RESTORE_DTMF_1:
+  case ADJUST_B_RESTORE_DTMF_2:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+    {
+
+      Info = dtmf_restore_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
+    Rc = OK;
+  case ADJUST_B_RESTORE_MIXER_1:
+  case ADJUST_B_RESTORE_MIXER_2:
+  case ADJUST_B_RESTORE_MIXER_3:
+  case ADJUST_B_RESTORE_MIXER_4:
+  case ADJUST_B_RESTORE_MIXER_5:
+  case ADJUST_B_RESTORE_MIXER_6:
+  case ADJUST_B_RESTORE_MIXER_7:
+    if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+    {
+
+      Info = mixer_restore_config (Id, plci, Rc);
+      if ((Info != GOOD) || plci->internal_command)
+        break;
+
+    }
+    plci->adjust_b_state = ADJUST_B_END;
+  case ADJUST_B_END:
+    break;
+  }
+  return (Info);
+}
+
+
+static void adjust_b1_resource (dword Id, PLCI   *plci, API_SAVE   *bp_msg, word b1_facilities, word internal_command)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adjust_b1_resource %d %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__,
+    plci->B1_resource, b1_facilities));
+
+  plci->adjust_b_parms_msg = bp_msg;
+  plci->adjust_b_facilities = b1_facilities;
+  plci->adjust_b_command = internal_command;
+  plci->adjust_b_ncci = (word)(Id >> 16);
+  if ((bp_msg == NULL) && (plci->B1_resource == 0))
+    plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_SWITCH_L1;
+  else
+    plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 | ADJUST_B_MODE_RESTORE;
+  plci->adjust_b_state = ADJUST_B_START;
+  dbug (1, dprintf ("[%06lx] %s,%d: Adjust B1 resource %d %04x...",
+    UnMapId (Id), (char   *)(FILE_), __LINE__,
+    plci->B1_resource, b1_facilities));
+}
+
+
+static void adjust_b_restore (dword Id, PLCI   *plci, byte Rc)
+{
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: adjust_b_restore %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+    if (plci->req_in != 0)
+    {
+      plci->internal_command = ADJUST_B_RESTORE_1;
+      break;
+    }
+    Rc = OK;
+  case ADJUST_B_RESTORE_1:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Adjust B enqueued failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+    }
+    plci->adjust_b_parms_msg = NULL;
+    plci->adjust_b_facilities = plci->B1_facilities;
+    plci->adjust_b_command = ADJUST_B_RESTORE_2;
+    plci->adjust_b_ncci = (word)(Id >> 16);
+    plci->adjust_b_mode = ADJUST_B_MODE_RESTORE;
+    plci->adjust_b_state = ADJUST_B_START;
+    dbug (1, dprintf ("[%06lx] %s,%d: Adjust B restore...",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+  case ADJUST_B_RESTORE_2:
+    if (adjust_b_process (Id, plci, Rc) != GOOD)
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Adjust B restore failed",
+        UnMapId (Id), (char   *)(FILE_), __LINE__));
+    }
+    if (plci->internal_command)
+      break;
+    break;
+  }
+}
+
+
+static void reset_b3_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: reset_b3_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+    plci->adjust_b_parms_msg = NULL;
+    plci->adjust_b_facilities = plci->B1_facilities;
+    plci->adjust_b_command = RESET_B3_COMMAND_1;
+    plci->adjust_b_ncci = (word)(Id >> 16);
+    plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_CONNECT;
+    plci->adjust_b_state = ADJUST_B_START;
+    dbug (1, dprintf ("[%06lx] %s,%d: Reset B3...",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+  case RESET_B3_COMMAND_1:
+    Info = adjust_b_process (Id, plci, Rc);
+    if (Info != GOOD)
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Reset failed",
+        UnMapId (Id), (char   *)(FILE_), __LINE__));
+      break;
+    }
+    if (plci->internal_command)
+      return;
+    break;
+  }
+/*  sendf (plci->appl, _RESET_B3_R | CONFIRM, Id, plci->number, "w", Info);*/
+  sendf(plci->appl,_RESET_B3_I,Id,0,"s","");
+}
+
+
+static void select_b_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+  byte esc_chi[3];
+
+  dbug (1, dprintf ("[%06lx] %s,%d: select_b_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+    plci->adjust_b_parms_msg = &plci->saved_msg;
+    if ((plci->tel == ADV_VOICE) && (plci == plci->adapter->AdvSignalPLCI))
+      plci->adjust_b_facilities = plci->B1_facilities | B1_FACILITY_VOICE;
+    else
+      plci->adjust_b_facilities = plci->B1_facilities & ~B1_FACILITY_VOICE;
+    plci->adjust_b_command = SELECT_B_COMMAND_1;
+    plci->adjust_b_ncci = (word)(Id >> 16);
+    if (plci->saved_msg.parms[0].length == 0)
+    {
+      plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
+        ADJUST_B_MODE_NO_RESOURCE;
+    }
+    else
+    {
+      plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
+        ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
+    }
+    plci->adjust_b_state = ADJUST_B_START;
+    dbug (1, dprintf ("[%06lx] %s,%d: Select B protocol...",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+  case SELECT_B_COMMAND_1:
+    Info = adjust_b_process (Id, plci, Rc);
+    if (Info != GOOD)
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: Select B protocol failed",
+        UnMapId (Id), (char   *)(FILE_), __LINE__));
+      break;
+    }
+    if (plci->internal_command)
+      return;
+    if (plci->tel == ADV_VOICE)
+    {
+      esc_chi[0] = 0x02;
+      esc_chi[1] = 0x18;
+      esc_chi[2] = plci->b_channel;
+      SetVoiceChannel (plci->adapter->AdvCodecPLCI, esc_chi, plci->adapter);
+    }
+    break;
+  }
+  sendf (plci->appl, _SELECT_B_REQ | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void fax_connect_ack_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: fax_connect_ack_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+  case FAX_CONNECT_ACK_COMMAND_1:
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = FAX_CONNECT_ACK_COMMAND_1;
+      return;
+    }
+    plci->internal_command = FAX_CONNECT_ACK_COMMAND_2;
+    plci->NData[0].P = plci->fax_connect_info_buffer;
+    plci->NData[0].PLength = plci->fax_connect_info_length;
+    plci->NL.X = plci->NData;
+    plci->NL.ReqCh = 0;
+    plci->NL.Req = plci->nl_req = (byte) N_CONNECT_ACK;
+    plci->adapter->request (&plci->NL);
+    return;
+  case FAX_CONNECT_ACK_COMMAND_2:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: FAX issue CONNECT ACK failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      break;
+    }
+  }
+  if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+   && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+  {
+    if (plci->B3_prot == 4)
+      sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+    else
+      sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+    plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+  }
+}
+
+
+static void fax_edata_ack_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: fax_edata_ack_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+  case FAX_EDATA_ACK_COMMAND_1:
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = FAX_EDATA_ACK_COMMAND_1;
+      return;
+    }
+    plci->internal_command = FAX_EDATA_ACK_COMMAND_2;
+    plci->NData[0].P = plci->fax_connect_info_buffer;
+    plci->NData[0].PLength = plci->fax_edata_ack_length;
+    plci->NL.X = plci->NData;
+    plci->NL.ReqCh = 0;
+    plci->NL.Req = plci->nl_req = (byte) N_EDATA;
+    plci->adapter->request (&plci->NL);
+    return;
+  case FAX_EDATA_ACK_COMMAND_2:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: FAX issue EDATA ACK failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      break;
+    }
+  }
+}
+
+
+static void fax_connect_info_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: fax_connect_info_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+  case FAX_CONNECT_INFO_COMMAND_1:
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = FAX_CONNECT_INFO_COMMAND_1;
+      return;
+    }
+    plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
+    plci->NData[0].P = plci->fax_connect_info_buffer;
+    plci->NData[0].PLength = plci->fax_connect_info_length;
+    plci->NL.X = plci->NData;
+    plci->NL.ReqCh = 0;
+    plci->NL.Req = plci->nl_req = (byte) N_EDATA;
+    plci->adapter->request (&plci->NL);
+    return;
+  case FAX_CONNECT_INFO_COMMAND_2:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: FAX setting connect info failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      Info = _WRONG_STATE;
+      break;
+    }
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
+      return;
+    }
+    plci->command = _CONNECT_B3_R;
+    nl_req_ncci (plci, N_CONNECT, 0);
+    send_req (plci);
+    return;
+  }
+  sendf (plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void fax_adjust_b23_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: fax_adjust_b23_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+    plci->adjust_b_parms_msg = NULL;
+    plci->adjust_b_facilities = plci->B1_facilities;
+    plci->adjust_b_command = FAX_ADJUST_B23_COMMAND_1;
+    plci->adjust_b_ncci = (word)(Id >> 16);
+    plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23;
+    plci->adjust_b_state = ADJUST_B_START;
+    dbug (1, dprintf ("[%06lx] %s,%d: FAX adjust B23...",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+  case FAX_ADJUST_B23_COMMAND_1:
+    Info = adjust_b_process (Id, plci, Rc);
+    if (Info != GOOD)
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: FAX adjust failed",
+        UnMapId (Id), (char   *)(FILE_), __LINE__));
+      break;
+    }
+    if (plci->internal_command)
+      return;
+  case FAX_ADJUST_B23_COMMAND_2:
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = FAX_ADJUST_B23_COMMAND_2;
+      return;
+    }
+    plci->command = _CONNECT_B3_R;
+    nl_req_ncci (plci, N_CONNECT, 0);
+    send_req (plci);
+    return;
+  }
+  sendf (plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void fax_disconnect_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: fax_disconnect_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+    plci->internal_command = FAX_DISCONNECT_COMMAND_1;
+    return;
+  case FAX_DISCONNECT_COMMAND_1:
+  case FAX_DISCONNECT_COMMAND_2:
+  case FAX_DISCONNECT_COMMAND_3:
+    if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: FAX disconnect EDATA failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      break;
+    }
+    if (Rc == OK)
+    {
+      if ((internal_command == FAX_DISCONNECT_COMMAND_1)
+       || (internal_command == FAX_DISCONNECT_COMMAND_2))
+      {
+        plci->internal_command = FAX_DISCONNECT_COMMAND_2;
+      }
+    }
+    else if (Rc == 0)
+    {
+      if (internal_command == FAX_DISCONNECT_COMMAND_1)
+        plci->internal_command = FAX_DISCONNECT_COMMAND_3;
+    }
+    return;
+  }
+}
+
+
+
+static void rtp_connect_b3_req_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: rtp_connect_b3_req_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+  case RTP_CONNECT_B3_REQ_COMMAND_1:
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_1;
+      return;
+    }
+    plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
+    nl_req_ncci (plci, N_CONNECT, 0);
+    send_req (plci);
+    return;
+  case RTP_CONNECT_B3_REQ_COMMAND_2:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: RTP setting connect info failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      Info = _WRONG_STATE;
+      break;
+    }
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
+      return;
+    }
+    plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_3;
+    plci->NData[0].PLength = plci->internal_req_buffer[0];
+    plci->NData[0].P = plci->internal_req_buffer + 1;
+    plci->NL.X = plci->NData;
+    plci->NL.ReqCh = 0;
+    plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+    plci->adapter->request (&plci->NL);
+    break;
+  case RTP_CONNECT_B3_REQ_COMMAND_3:
+    return;
+  }
+  sendf (plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void rtp_connect_b3_res_command (dword Id, PLCI   *plci, byte Rc)
+{
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: rtp_connect_b3_res_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+  case RTP_CONNECT_B3_RES_COMMAND_1:
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_1;
+      return;
+    }
+    plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
+    nl_req_ncci (plci, N_CONNECT_ACK, (byte)(Id >> 16));
+    send_req (plci);
+    return;
+  case RTP_CONNECT_B3_RES_COMMAND_2:
+    if ((Rc != OK) && (Rc != OK_FC))
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: RTP setting connect resp info failed %02x",
+        UnMapId (Id), (char   *)(FILE_), __LINE__, Rc));
+      Info = _WRONG_STATE;
+      break;
+    }
+    if (plci_nl_busy (plci))
+    {
+      plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
+      return;
+    }
+    sendf (plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
+    plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_3;
+    plci->NData[0].PLength = plci->internal_req_buffer[0];
+    plci->NData[0].P = plci->internal_req_buffer + 1;
+    plci->NL.X = plci->NData;
+    plci->NL.ReqCh = 0;
+    plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+    plci->adapter->request (&plci->NL);
+    return;
+  case RTP_CONNECT_B3_RES_COMMAND_3:
+    return;
+  }
+}
+
+
+
+static void hold_save_command (dword Id, PLCI   *plci, byte Rc)
+{
+    byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: hold_save_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    if (!plci->NL.Id)
+      break;
+    plci->command = 0;
+    plci->adjust_b_parms_msg = NULL;
+    plci->adjust_b_facilities = plci->B1_facilities;
+    plci->adjust_b_command = HOLD_SAVE_COMMAND_1;
+    plci->adjust_b_ncci = (word)(Id >> 16);
+    plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23;
+    plci->adjust_b_state = ADJUST_B_START;
+    dbug (1, dprintf ("[%06lx] %s,%d: HOLD save...",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+  case HOLD_SAVE_COMMAND_1:
+    Info = adjust_b_process (Id, plci, Rc);
+    if (Info != GOOD)
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: HOLD save failed",
+        UnMapId (Id), (char   *)(FILE_), __LINE__));
+      break;
+    }
+    if (plci->internal_command)
+      return;
+  }
+  sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
+}
+
+
+static void retrieve_restore_command (dword Id, PLCI   *plci, byte Rc)
+{
+    byte SS_Ind[] = "\x05\x03\x00\x02\x00\x00"; /* Retrieve_Ind struct*/
+  word Info;
+  word internal_command;
+
+  dbug (1, dprintf ("[%06lx] %s,%d: retrieve_restore_command %02x %04x",
+    UnMapId (Id), (char   *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+  Info = GOOD;
+  internal_command = plci->internal_command;
+  plci->internal_command = 0;
+  switch (internal_command)
+  {
+  default:
+    plci->command = 0;
+    plci->adjust_b_parms_msg = NULL;
+    plci->adjust_b_facilities = plci->B1_facilities;
+    plci->adjust_b_command = RETRIEVE_RESTORE_COMMAND_1;
+    plci->adjust_b_ncci = (word)(Id >> 16);
+    plci->adjust_b_mode = ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
+    plci->adjust_b_state = ADJUST_B_START;
+    dbug (1, dprintf ("[%06lx] %s,%d: RETRIEVE restore...",
+      UnMapId (Id), (char   *)(FILE_), __LINE__));
+  case RETRIEVE_RESTORE_COMMAND_1:
+    Info = adjust_b_process (Id, plci, Rc);
+    if (Info != GOOD)
+    {
+      dbug (1, dprintf ("[%06lx] %s,%d: RETRIEVE restore failed",
+        UnMapId (Id), (char   *)(FILE_), __LINE__));
+      break;
+    }
+    if (plci->internal_command)
+      return;
+  }
+  sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
+}
+
+
+static void init_b1_config (PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: init_b1_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  plci->B1_resource = 0;
+  plci->B1_facilities = 0;
+
+  plci->li_bchannel_id = 0;
+  mixer_clear_config (plci);
+
+
+  ec_clear_config (plci);
+
+
+  dtmf_rec_clear_config (plci);
+  dtmf_send_clear_config (plci);
+  dtmf_parameter_clear_config (plci);
+
+  adv_voice_clear_config (plci);
+  adjust_b_clear (plci);
+}
+
+
+static void clear_b1_config (PLCI   *plci)
+{
+
+  dbug (1, dprintf ("[%06lx] %s,%d: clear_b1_config",
+    (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+    (char   *)(FILE_), __LINE__));
+
+  adv_voice_clear_config (plci);
+  adjust_b_clear (plci);
+
+  ec_clear_config (plci);
+
+
+  dtmf_rec_clear_config (plci);
+  dtmf_send_clear_config (plci);
+  dtmf_parameter_clear_config (plci);
+
+
+  if ((plci->li_bchannel_id != 0)
+   && (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+  {
+    mixer_clear_config (plci);
+    li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = NULL;
+    plci->li_bchannel_id = 0;
+  }
+
+  plci->B1_resource = 0;
+  plci->B1_facilities = 0;
+}
+
+
+/* -----------------------------------------------------------------
+                XON protocol local helpers
+   ----------------------------------------------------------------- */
+static void channel_flow_control_remove (PLCI   * plci) {
+  DIVA_CAPI_ADAPTER   * a = plci->adapter;
+  word i;
+  for(i=1;i<MAX_NL_CHANNEL+1;i++) {
+    if (a->ch_flow_plci[i] == plci->Id) {
+      a->ch_flow_plci[i] = 0;
+      a->ch_flow_control[i] = 0;
+    }
+  }
+}
+
+static void channel_x_on (PLCI   * plci, byte ch) {
+  DIVA_CAPI_ADAPTER   * a = plci->adapter;
+  if (a->ch_flow_control[ch] & N_XON_SENT) {
+    a->ch_flow_control[ch] &= ~N_XON_SENT;
+  }
+}
+
+static void channel_x_off (PLCI   * plci, byte ch, byte flag) {
+  DIVA_CAPI_ADAPTER   * a = plci->adapter;
+  if ((a->ch_flow_control[ch] & N_RX_FLOW_CONTROL_MASK) == 0) {
+    a->ch_flow_control[ch] |= (N_CH_XOFF | flag);
+    a->ch_flow_plci[ch] = plci->Id;
+    a->ch_flow_control_pending++;
+  }
+}
+
+static void channel_request_xon (PLCI   * plci, byte ch) {
+  DIVA_CAPI_ADAPTER   * a = plci->adapter;
+
+  if (a->ch_flow_control[ch] & N_CH_XOFF) {
+    a->ch_flow_control[ch] |= N_XON_REQ;
+    a->ch_flow_control[ch] &= ~N_CH_XOFF;
+    a->ch_flow_control[ch] &= ~N_XON_CONNECT_IND;
+  }
+}
+
+static void channel_xmit_extended_xon (PLCI   * plci) {
+  DIVA_CAPI_ADAPTER   * a;
+  int max_ch = sizeof(a->ch_flow_control)/sizeof(a->ch_flow_control[0]);
+  int i, one_requested = 0;
+
+  if ((!plci) || (!plci->Id) || ((a = plci->adapter) == 0)) {
+    return;
+  }
+
+  for (i = 0; i < max_ch; i++) {
+    if ((a->ch_flow_control[i] & N_CH_XOFF) &&
+        (a->ch_flow_control[i] & N_XON_CONNECT_IND) &&
+        (plci->Id == a->ch_flow_plci[i])) {
+      channel_request_xon (plci, (byte)i);
+      one_requested = 1;
+    }
+  }
+
+  if (one_requested) {
+    channel_xmit_xon (plci);
+  }
+}
+
+/*
+  Try to xmit next X_ON
+  */
+static int find_channel_with_pending_x_on (DIVA_CAPI_ADAPTER   * a, PLCI   * plci) {
+  int max_ch = sizeof(a->ch_flow_control)/sizeof(a->ch_flow_control[0]);
+  int i;
+
+  if (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)) {
+    return (0);
+  }
+
+  if (a->last_flow_control_ch >= max_ch) {
+    a->last_flow_control_ch = 1;
+  }
+  for (i=a->last_flow_control_ch; i < max_ch; i++) {
+    if ((a->ch_flow_control[i] & N_XON_REQ) &&
+        (plci->Id == a->ch_flow_plci[i])) {
+      a->last_flow_control_ch = i+1;
+      return (i);
+    }
+  }
+
+  for (i = 1; i < a->last_flow_control_ch; i++) {
+    if ((a->ch_flow_control[i] & N_XON_REQ) &&
+        (plci->Id == a->ch_flow_plci[i])) {
+      a->last_flow_control_ch = i+1;
+      return (i);
+    }
+  }
+
+  return (0);
+}
+
+static void channel_xmit_xon (PLCI   * plci) {
+  DIVA_CAPI_ADAPTER   * a = plci->adapter;
+  byte ch;
+
+  if (plci->nl_req || !plci->NL.Id || plci->nl_remove_id) {
+    return;
+  }
+  if ((ch = (byte)find_channel_with_pending_x_on (a, plci)) == 0) {
+    return;
+  }
+  a->ch_flow_control[ch] &= ~N_XON_REQ;
+  a->ch_flow_control[ch] |= N_XON_SENT;
+
+  plci->NL.Req = plci->nl_req = (byte)N_XON;
+  plci->NL.ReqCh         = ch;
+  plci->NL.X             = plci->NData;
+  plci->NL.XNum          = 1;
+  plci->NData[0].P       = &plci->RBuffer[0];
+  plci->NData[0].PLength = 0;
+
+  plci->adapter->request(&plci->NL);
+}
+
+static int channel_can_xon (PLCI   * plci, byte ch) {
+  APPL   * APPLptr;
+  DIVA_CAPI_ADAPTER   * a;
+  word NCCIcode;
+  dword count;
+  word Num;
+  word i;
+
+  APPLptr = plci->appl;
+  a = plci->adapter;
+
+  if (!APPLptr)
+    return (0);
+
+  NCCIcode = a->ch_ncci[ch] | (((word) a->Id) << 8);
+
+                /* count all buffers within the Application pool    */
+                /* belonging to the same NCCI. XON if a first is    */
+                /* used.                                            */
+  count = 0;
+  Num = 0xffff;
+  for(i=0; i<APPLptr->MaxBuffer; i++) {
+    if(NCCIcode==APPLptr->DataNCCI[i]) count++;
+    if(!APPLptr->DataNCCI[i] && Num==0xffff) Num = i;
+  }
+  if ((count > 2) || (Num == 0xffff)) {
+    return (0);
+  }
+  return (1);
+}
+
+
+/*------------------------------------------------------------------*/
+
+static word CPN_filter_ok(byte   *cpn,DIVA_CAPI_ADAPTER   * a,word offset)
+{
+  return 1;
+}
+
+
+
+/**********************************************************************************/
+/* function groups the listening applications according to the CIP mask and the   */
+/* Info_Mask. Each group gets just one Connect_Ind. Some application manufacturer */
+/* are not multi-instance capable, so they start e.g. 30 applications what causes */
+/* big problems on application level (one call, 30 Connect_Ind, ect). The         */
+/* function must be enabled by setting "a->group_optimization_enabled" from the   */
+/* OS specific part (per adapter).                                                */
+/**********************************************************************************/
+static void group_optimization(DIVA_CAPI_ADAPTER   * a, PLCI   * plci)
+{
+  word i,j,k,busy,group_found;
+  dword info_mask_group[MAX_CIP_TYPES];
+  dword cip_mask_group[MAX_CIP_TYPES];
+  word appl_number_group_type[MAX_APPL];
+  PLCI   *auxplci;
+
+  set_group_ind_mask (plci); /* all APPLs within this inc. call are allowed to dial in */
+
+  if(!a->group_optimization_enabled)
+  {
+    dbug(1,dprintf("No group optimization"));
+    return;
+  }
+
+  dbug(1,dprintf("Group optimization = 0x%x...", a->group_optimization_enabled));
+
+  for(i=0;i<MAX_CIP_TYPES;i++)
+  {
+    info_mask_group[i] = 0;
+    cip_mask_group [i] = 0;
+  }
+  for(i=0;i<MAX_APPL;i++)
+  {
+    appl_number_group_type[i] = 0;
+  }
+  for(i=0; i<max_appl; i++) /* check if any multi instance capable application is present */
+  {  /* group_optimization set to 1 means not to optimize multi-instance capable applications (default) */
+    if(application[i].Id && (application[i].MaxNCCI) > 1 && (a->CIP_Mask[i])  && (a->group_optimization_enabled ==1) )
+    {
+      dbug(1,dprintf("Multi-Instance capable, no optimization required"));
+      return; /* allow good application unfiltered access */
+    }
+  }
+  for(i=0; i<max_appl; i++) /* Build CIP Groups */
+  {
+    if(application[i].Id && a->CIP_Mask[i] )
+    {
+      for(k=0,busy=FALSE; k<a->max_plci; k++)
+      {
+        if(a->plci[k].Id) 
+        {
+          auxplci = &a->plci[k];
+          if(auxplci->appl == &application[i]) /* application has a busy PLCI */
+          {
+            busy = TRUE;
+            dbug(1,dprintf("Appl 0x%x is busy",i+1));
+          }
+          else if(test_c_ind_mask_bit (auxplci, i)) /* application has an incoming call pending */
+          {
+            busy = TRUE;
+            dbug(1,dprintf("Appl 0x%x has inc. call pending",i+1));
+          }
+        }
+      }
+
+      for(j=0,group_found=0; j<=(MAX_CIP_TYPES) && !busy &&!group_found; j++)     /* build groups with free applications only */
+      {
+        if(j==MAX_CIP_TYPES)       /* all groups are in use but group still not found */
+        {                           /* the MAX_CIP_TYPES group enables all calls because of field overflow */
+          appl_number_group_type[i] = MAX_CIP_TYPES;
+          group_found=TRUE;
+          dbug(1,dprintf("Field overflow appl 0x%x",i+1));
+        }
+        else if( (info_mask_group[j]==a->CIP_Mask[i]) && (cip_mask_group[j]==a->Info_Mask[i]) )  
+        {                                      /* is group already present ?                  */
+          appl_number_group_type[i] = j|0x80;  /* store the group number for each application */
+          group_found=TRUE;
+          dbug(1,dprintf("Group 0x%x found with appl 0x%x, CIP=0x%lx",appl_number_group_type[i],i+1,info_mask_group[j]));
+        }
+        else if(!info_mask_group[j])
+        {                                      /* establish a new group                       */
+          appl_number_group_type[i] = j|0x80;  /* store the group number for each application */
+          info_mask_group[j] = a->CIP_Mask[i]; /* store the new CIP mask for the new group    */
+          cip_mask_group[j] = a->Info_Mask[i]; /* store the new Info_Mask for this new group  */
+          group_found=TRUE;
+          dbug(1,dprintf("New Group 0x%x established with appl 0x%x, CIP=0x%lx",appl_number_group_type[i],i+1,info_mask_group[j]));
+        }
+      }
+    }
+  }
+        
+  for(i=0; i<max_appl; i++) /* Build group_optimization_mask_table */
+  {
+    if(appl_number_group_type[i]) /* application is free, has listens and is member of a group */
+    {
+      if(appl_number_group_type[i] == MAX_CIP_TYPES)
+      {
+        dbug(1,dprintf("OverflowGroup 0x%x, valid appl = 0x%x, call enabled",appl_number_group_type[i],i+1));
+      }
+      else
+      {
+        dbug(1,dprintf("Group 0x%x, valid appl = 0x%x",appl_number_group_type[i],i+1));
+        for(j=i+1; j<max_appl; j++)   /* search other group members and mark them as busy        */
+        {
+          if(appl_number_group_type[i] == appl_number_group_type[j]) 
+          {
+            dbug(1,dprintf("Appl 0x%x is member of group 0x%x, no call",j+1,appl_number_group_type[j]));
+            clear_group_ind_mask_bit (plci, j);           /* disable call on other group members */
+            appl_number_group_type[j] = 0;       /* remove disabled group member from group list */
+          }
+        }
+      }
+    }
+    else                                                 /* application should not get a call */
+    {
+      clear_group_ind_mask_bit (plci, i);
+    }
+  }
+
+}
+
+
+
+/* OS notifies the driver about a application Capi_Register */
+word CapiRegister(word id)
+{
+  word i,j,appls_found;
+
+  PLCI   *plci;
+  DIVA_CAPI_ADAPTER   *a;
+
+  for(i=0,appls_found=0; i<max_appl; i++)
+  {
+    if( application[i].Id && (application[i].Id!=id) )
+    {
+      appls_found++;                       /* an application has been found */
+    }
+  }
+
+  if(appls_found) return TRUE;
+  for(i=0; i<max_adapter; i++)                   /* scan all adapters...    */
+  {
+    a = &adapter[i];
+    if(a->request)
+    {
+      if(a->flag_dynamic_l1_down)  /* remove adapter from L1 tristate (Huntgroup) */
+      {
+        if(!appls_found)           /* first application does a capi register   */
+        {
+          if((j=get_plci(a)))                    /* activate L1 of all adapters */
+          {
+            plci = &a->plci[j-1];
+            plci->command = 0;
+            add_p(plci,OAD,"\x01\xfd");
+            add_p(plci,CAI,"\x01\x80");
+            add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+            add_p(plci,SHIFT|6,NULL);
+            add_p(plci,SIN,"\x02\x00\x00");
+            plci->internal_command = START_L1_SIG_ASSIGN_PEND;
+            sig_req(plci,ASSIGN,DSIG_ID);
+            add_p(plci,FTY,"\x02\xff\x07"); /* l1 start */
+            sig_req(plci,SIG_CTRL,0);
+            send_req(plci);
+          }
+        }
+      }
+    }
+  }
+  return FALSE;
+}
+
+/*------------------------------------------------------------------*/
+
+/* Functions for virtual Switching e.g. Transfer by join, Conference */
+
+static void VSwitchReqInd(PLCI   *plci, dword Id, byte   **parms)
+{
+ word i;
+ /* Format of vswitch_t:
+ 0 byte length
+ 1 byte VSWITCHIE
+ 2 byte VSWITCH_REQ/VSWITCH_IND
+ 3 byte reserved
+ 4 word VSwitchcommand
+ 6 word returnerror
+ 8... Params
+ */
+ if(!plci ||
+  !plci->appl ||
+  !plci->State ||
+  plci->Sig.Ind==NCR_FACILITY
+  )
+  return;
+ 
+ for(i=0;i<MAX_MULTI_IE;i++)
+ {
+        if(!parms[i][0]) continue;
+  if(parms[i][0]<7)
+  {
+   parms[i][0]=0; /* kill it */
+   continue;
+  }
+  dbug(1,dprintf("VSwitchReqInd(%d)",parms[i][4]));
+  switch(parms[i][4])
+  {
+  case VSJOIN:
+   if(!plci->relatedPTYPLCI ||
+    (plci->ptyState!=S_ECT && plci->relatedPTYPLCI->ptyState!=S_ECT))
+   { /* Error */
+    break;
+   }
+   /* remember all necessary informations */
+   if(parms[i][0]!=11 || parms[i][8]!=3) /* Length Test */
+   {
+    break;
+   }
+   if(parms[i][2]==VSWITCH_IND && parms[i][9]==1)
+   {   /* first indication after ECT-Request on Consultation Call */
+    plci->vswitchstate=parms[i][9];
+    parms[i][9]=2; /* State */
+    /* now ask first Call to join */
+   }
+   else if(parms[i][2]==VSWITCH_REQ && parms[i][9]==3)
+   { /* Answer of VSWITCH_REQ from first Call */
+    plci->vswitchstate=parms[i][9];
+    /* tell consultation call to join
+    and the protocol capabilities of the first call */
+   }
+   else
+   { /* Error */
+    break;
+   }    
+   plci->vsprot=parms[i][10]; /* protocol */
+   plci->vsprotdialect=parms[i][11]; /* protocoldialect */
+   /* send join request to related PLCI */
+   parms[i][1]=VSWITCHIE;
+   parms[i][2]=VSWITCH_REQ;
+   
+   plci->relatedPTYPLCI->command = 0;
+   plci->relatedPTYPLCI->internal_command = VSWITCH_REQ_PEND;
+   add_p(plci->relatedPTYPLCI,ESC,&parms[i][0]);
+   sig_req(plci->relatedPTYPLCI,VSWITCH_REQ,0);
+   send_req(plci->relatedPTYPLCI);
+   break;
+  case VSTRANSPORT:
+  default:
+   if(plci->relatedPTYPLCI &&
+    plci->vswitchstate==3 &&
+    plci->relatedPTYPLCI->vswitchstate==3)
+   {
+    add_p(plci->relatedPTYPLCI,ESC,&parms[i][0]);
+    sig_req(plci->relatedPTYPLCI,VSWITCH_REQ,0);
+    send_req(plci->relatedPTYPLCI);
+   }
+   break;
+  }  
+  parms[i][0]=0; /* kill it */
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+
+static int diva_get_dma_descriptor (PLCI   *plci, dword   *dma_magic) {
+  ENTITY e;
+  IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+  if (!(diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_RX_DMA)) {
+    return (-1);
+  }
+
+  pReq->xdi_dma_descriptor_operation.Req = 0;
+  pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+  pReq->xdi_dma_descriptor_operation.info.operation =     IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_number  = -1;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
+
+  e.user[0] = plci->adapter->Id - 1;
+  plci->adapter->request((ENTITY*)pReq);
+
+  if (!pReq->xdi_dma_descriptor_operation.info.operation &&
+      (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) &&
+      pReq->xdi_dma_descriptor_operation.info.descriptor_magic) {
+    *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic;
+    dbug(3,dprintf("dma_alloc, a:%d (%d-%08x)",
+         plci->adapter->Id,
+         pReq->xdi_dma_descriptor_operation.info.descriptor_number,
+         *dma_magic));
+    return (pReq->xdi_dma_descriptor_operation.info.descriptor_number);
+  } else {
+    dbug(1,dprintf("dma_alloc failed"));
+    return (-1);
+  }
+}
+
+static void diva_free_dma_descriptor (PLCI   *plci, int nr) {
+  ENTITY e;
+  IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+  if (nr < 0) {
+    return;
+  }
+
+  pReq->xdi_dma_descriptor_operation.Req = 0;
+  pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+  pReq->xdi_dma_descriptor_operation.info.operation =                                                IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_number  = nr;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+  pReq->xdi_dma_descriptor_operation.info.descriptor_magic   = 0;
+
+  e.user[0] = plci->adapter->Id - 1;
+  plci->adapter->request((ENTITY*)pReq);
+
+  if (!pReq->xdi_dma_descriptor_operation.info.operation) {
+    dbug(1,dprintf("dma_free(%d)", nr));
+  } else {
+    dbug(1,dprintf("dma_free failed (%d)", nr));
+  }
+}
+
+/*------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mi_pc.h b/drivers/isdn/hardware/eicon/mi_pc.h
new file mode 100644
index 0000000..a861dac
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/mi_pc.h
@@ -0,0 +1,204 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+/*----------------------------------------------------------------------------
+// MAESTRA ISA PnP */
+#define BRI_MEMORY_BASE                 0x1f700000
+#define BRI_MEMORY_SIZE                 0x00100000  /* 1MB on the BRI                         */
+#define BRI_SHARED_RAM_SIZE             0x00010000  /* 64k shared RAM                         */
+#define BRI_RAY_TAYLOR_DSP_CODE_SIZE    0x00020000  /* max 128k DSP-Code (Ray Taylor's code)  */
+#define BRI_ORG_MAX_DSP_CODE_SIZE       0x00050000  /* max 320k DSP-Code (Telindus)           */
+#define BRI_V90D_MAX_DSP_CODE_SIZE      0x00060000  /* max 384k DSP-Code if V.90D included    */
+#define BRI_CACHED_ADDR(x)              (((x) & 0x1fffffffL) | 0x80000000L)
+#define BRI_UNCACHED_ADDR(x)            (((x) & 0x1fffffffL) | 0xa0000000L)
+#define ADDR  4
+#define ADDRH 6
+#define DATA  0
+#define RESET 7
+#define DEFAULT_ADDRESS 0x240
+#define DEFAULT_IRQ     3
+#define M_PCI_ADDR   0x04  /* MAESTRA BRI PCI */
+#define M_PCI_ADDRH  0x0c  /* MAESTRA BRI PCI */
+#define M_PCI_DATA   0x00  /* MAESTRA BRI PCI */
+#define M_PCI_RESET  0x10  /* MAESTRA BRI PCI */
+/*----------------------------------------------------------------------------
+// MAESTRA PRI PCI */
+#define MP_IRQ_RESET                    0xc18       /* offset of isr in the CONFIG memory bar */
+#define MP_IRQ_RESET_VAL                0xfe        /* value to clear an interrupt            */
+#define MP_MEMORY_SIZE                  0x00400000  /* 4MB on standard PRI                    */
+#define MP2_MEMORY_SIZE                 0x00800000  /* 8MB on PRI Rev. 2                      */
+#define MP_SHARED_RAM_OFFSET            0x00001000  /* offset of shared RAM base in the DRAM memory bar */
+#define MP_SHARED_RAM_SIZE              0x00010000  /* 64k shared RAM                         */
+#define MP_PROTOCOL_OFFSET              (MP_SHARED_RAM_OFFSET + MP_SHARED_RAM_SIZE)
+#define MP_RAY_TAYLOR_DSP_CODE_SIZE     0x00040000  /* max 256k DSP-Code (Ray Taylor's code)  */
+#define MP_ORG_MAX_DSP_CODE_SIZE        0x00060000  /* max 384k DSP-Code (Telindus)           */
+#define MP_V90D_MAX_DSP_CODE_SIZE       0x00070000  /* max 448k DSP-Code if V.90D included)   */
+#define MP_VOIP_MAX_DSP_CODE_SIZE       0x00090000  /* max 576k DSP-Code if voice over IP included */
+#define MP_CACHED_ADDR(x)               (((x) & 0x1fffffffL) | 0x80000000L)
+#define MP_UNCACHED_ADDR(x)             (((x) & 0x1fffffffL) | 0xa0000000L)
+#define MP_RESET         0x20        /* offset of RESET register in the DEVICES memory bar */
+/* RESET register bits */
+#define _MP_S2M_RESET    0x10        /* active lo   */
+#define _MP_LED2         0x08        /* 1 = on      */
+#define _MP_LED1         0x04        /* 1 = on      */
+#define _MP_DSP_RESET    0x02        /* active lo   */
+#define _MP_RISC_RESET   0x81        /* active hi, bit 7 for compatibility with old boards */
+/* CPU exception context structure in MP shared ram after trap */
+typedef struct mp_xcptcontext_s MP_XCPTC;
+struct mp_xcptcontext_s {
+    dword       sr;
+    dword       cr;
+    dword       epc;
+    dword       vaddr;
+    dword       regs[32];
+    dword       mdlo;
+    dword       mdhi;
+    dword       reseverd;
+    dword       xclass;
+};
+/* boot interface structure for PRI */
+struct mp_load {
+  dword     volatile cmd;
+  dword     volatile addr;
+  dword     volatile len;
+  dword     volatile err;
+  dword     volatile live;
+  dword     volatile res1[0x1b];
+  dword     volatile TrapId;    /* has value 0x999999XX on a CPU trap */
+  dword     volatile res2[0x03];
+  MP_XCPTC  volatile xcpt;      /* contains register dump */
+  dword     volatile rest[((0x1020>>2)-6) - 0x1b - 1 - 0x03 - (sizeof(MP_XCPTC)>>2)];
+  dword     volatile signature;
+  dword data[60000]; /* real interface description */
+};
+/*----------------------------------------------------------------------------*/
+/* SERVER 4BRI (Quattro PCI)                                                  */
+#define MQ_BOARD_REG_OFFSET             0x800000    /* PC relative On board registers offset  */
+#define MQ_BREG_RISC                    0x1200      /* RISC Reset ect                         */
+#define MQ_RISC_COLD_RESET_MASK         0x0001      /* RISC Cold reset                        */
+#define MQ_RISC_WARM_RESET_MASK         0x0002      /* RISC Warm reset                        */
+#define MQ_BREG_IRQ_TEST                0x0608      /* Interrupt request, no CPU interaction  */
+#define MQ_IRQ_REQ_ON                   0x1
+#define MQ_IRQ_REQ_OFF                  0x0
+#define MQ_BOARD_DSP_OFFSET             0xa00000    /* PC relative On board DSP regs offset   */
+#define MQ_DSP1_ADDR_OFFSET             0x0008      /* Addr register offset DSP 1 subboard 1  */
+#define MQ_DSP2_ADDR_OFFSET             0x0208      /* Addr register offset DSP 2 subboard 1  */
+#define MQ_DSP1_DATA_OFFSET             0x0000      /* Data register offset DSP 1 subboard 1  */
+#define MQ_DSP2_DATA_OFFSET             0x0200      /* Data register offset DSP 2 subboard 1  */
+#define MQ_DSP_JUNK_OFFSET              0x0400      /* DSP Data/Addr regs subboard offset     */
+#define MQ_ISAC_DSP_RESET               0x0028      /* ISAC and DSP reset address offset      */
+#define MQ_BOARD_ISAC_DSP_RESET         0x800028    /* ISAC and DSP reset address offset      */
+#define MQ_INSTANCE_COUNT               4           /* 4BRI consists of four instances        */
+#define MQ_MEMORY_SIZE                  0x00400000  /* 4MB on standard 4BRI                   */
+#define MQ_CTRL_SIZE                    0x00002000  /* 8K memory mapped registers             */
+#define MQ_SHARED_RAM_SIZE              0x00010000  /* 64k shared RAM                         */
+#define MQ_ORG_MAX_DSP_CODE_SIZE        0x00050000  /* max 320k DSP-Code (Telindus) */
+#define MQ_V90D_MAX_DSP_CODE_SIZE       0x00060000  /* max 384K DSP-Code if V.90D included */
+#define MQ_VOIP_MAX_DSP_CODE_SIZE       0x00028000  /* max 4*160k = 640K DSP-Code if voice over IP included */
+#define MQ_CACHED_ADDR(x)               (((x) & 0x1fffffffL) | 0x80000000L)
+#define MQ_UNCACHED_ADDR(x)             (((x) & 0x1fffffffL) | 0xa0000000L)
+/*--------------------------------------------------------------------------------------------*/
+/* Additional definitions reflecting the different address map of the  SERVER 4BRI V2          */
+#define MQ2_BREG_RISC                   0x0200      /* RISC Reset ect                         */
+#define MQ2_BREG_IRQ_TEST               0x0400      /* Interrupt request, no CPU interaction  */
+#define MQ2_BOARD_DSP_OFFSET            0x800000    /* PC relative On board DSP regs offset   */
+#define MQ2_DSP1_DATA_OFFSET            0x1800      /* Data register offset DSP 1 subboard 1  */
+#define MQ2_DSP1_ADDR_OFFSET            0x1808      /* Addr register offset DSP 1 subboard 1  */
+#define MQ2_DSP2_DATA_OFFSET            0x1810      /* Data register offset DSP 2 subboard 1  */
+#define MQ2_DSP2_ADDR_OFFSET            0x1818      /* Addr register offset DSP 2 subboard 1  */
+#define MQ2_DSP_JUNK_OFFSET             0x1000      /* DSP Data/Addr regs subboard offset     */
+#define MQ2_ISAC_DSP_RESET              0x0000      /* ISAC and DSP reset address offset      */
+#define MQ2_BOARD_ISAC_DSP_RESET        0x800000    /* ISAC and DSP reset address offset      */
+#define MQ2_IPACX_CONFIG                0x0300      /* IPACX Configuration TE(0)/NT(1)        */
+#define MQ2_BOARD_IPACX_CONFIG          0x800300    /*     ""                                 */
+#define MQ2_MEMORY_SIZE                 0x01000000  /* 16MB code/data memory                  */
+#define MQ2_CTRL_SIZE                   0x00008000  /* 32K memory mapped registers            */
+/*----------------------------------------------------------------------------*/
+/* SERVER BRI 2M/2F as derived from 4BRI V2                                   */
+#define BRI2_MEMORY_SIZE                0x00800000  /* 8MB code/data memory                   */
+#define BRI2_PROTOCOL_MEMORY_SIZE       (MQ2_MEMORY_SIZE >> 2) /*  same as one 4BRI Rev.2 task */
+#define BRI2_CTRL_SIZE                  0x00008000  /* 32K memory mapped registers            */
+#define M_INSTANCE_COUNT                1           /*  BRI consists of one instance          */
+/*
+ * Some useful constants for proper initialization of the GT6401x
+ */
+#define ID_REG        0x0000      /*Pci reg-contain the Dev&Ven ID of the card*/
+#define RAS0_BASEREG  0x0010      /*Ras0 register - contain the base addr Ras0*/
+#define RAS2_BASEREG  0x0014
+#define CS_BASEREG    0x0018
+#define BOOT_BASEREG  0x001c
+#define GTREGS_BASEREG 0x0024   /*GTRegsBase reg-contain the base addr where*/
+                                /*the GT64010 internal regs where mapped    */
+/*
+ *  GT64010 internal registers
+ */
+        /* DRAM device coding  */
+#define LOW_RAS0_DREG 0x0400    /*Ras0 low decode address*/
+#define HI_RAS0_DREG  0x0404    /*Ras0 high decode address*/
+#define LOW_RAS1_DREG 0x0408    /*Ras1 low decode address*/
+#define HI_RAS1_DREG  0x040c    /*Ras1 high decode address*/
+#define LOW_RAS2_DREG 0x0410    /*Ras2 low decode address*/
+#define HI_RAS2_DREG  0x0414    /*Ras2 high decode address*/
+#define LOW_RAS3_DREG 0x0418    /*Ras3 low decode address*/
+#define HI_RAS3_DREG  0x041c    /*Ras3 high decode address*/
+        /* I/O CS device coding  */
+#define LOW_CS0_DREG  0x0420 /* CS0* low decode register */
+#define HI_CS0_DREG   0x0424 /* CS0* high decode register */
+#define LOW_CS1_DREG  0x0428 /* CS1* low decode register */
+#define HI_CS1_DREG   0x042c /* CS1* high decode register */
+#define LOW_CS2_DREG  0x0430 /* CS2* low decode register */
+#define HI_CS2_DREG   0x0434 /* CS2* high decode register */
+#define LOW_CS3_DREG  0x0438 /* CS3* low decode register */
+#define HI_CS3_DREG   0x043c /* CS3* high decode register */
+        /* Boot PROM device coding */
+#define LOW_BOOTCS_DREG 0x0440 /* Boot CS low decode register */
+#define HI_BOOTCS_DREG 0x0444 /* Boot CS High decode register */
+        /* DRAM group coding (for CPU)  */
+#define LO_RAS10_GREG 0x0008    /*Ras1..0 group low decode address*/
+#define HI_RAS10_GREG 0x0010    /*Ras1..0 group high decode address*/
+#define LO_RAS32_GREG 0x0018    /*Ras3..2 group low decode address  */
+#define HI_RAS32_GREG 0x0020    /*Ras3..2 group high decode address  */
+        /* I/O CS group coding for (CPU)  */
+#define LO_CS20_GREG  0x0028 /* CS2..0 group low decode register */
+#define HI_CS20_GREG  0x0030 /* CS2..0 group high decode register */
+#define LO_CS3B_GREG  0x0038 /* CS3 & PROM group low decode register */
+#define HI_CS3B_GREG  0x0040 /* CS3 & PROM group high decode register */
+        /* Galileo specific PCI config. */
+#define PCI_TIMEOUT_RET 0x0c04 /* Time Out and retry register */
+#define RAS10_BANKSIZE 0x0c08 /* RAS 1..0 group PCI bank size */
+#define RAS32_BANKSIZE 0x0c0c /* RAS 3..2 group PCI bank size */
+#define CS20_BANKSIZE 0x0c10 /* CS 2..0 group PCI bank size */
+#define CS3B_BANKSIZE 0x0c14 /* CS 3 & Boot group PCI bank size */
+#define DRAM_SIZE     0x0001      /*Dram size in mega bytes*/
+#define PROM_SIZE     0x08000     /*Prom size in bytes*/
+/*--------------------------------------------------------------------------*/
+#define OFFS_DIVA_INIT_TASK_COUNT 0x68
+#define OFFS_DSP_CODE_BASE_ADDR   0x6c
+#define OFFS_XLOG_BUF_ADDR        0x70
+#define OFFS_XLOG_COUNT_ADDR      0x74
+#define OFFS_XLOG_OUT_ADDR        0x78
+#define OFFS_PROTOCOL_END_ADDR    0x7c
+#define OFFS_PROTOCOL_ID_STRING   0x80
+/*--------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mntfunc.c b/drivers/isdn/hardware/eicon/mntfunc.c
new file mode 100644
index 0000000..a564b75
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/mntfunc.c
@@ -0,0 +1,370 @@
+/* $Id: mntfunc.c,v 1.19.6.4 2005/01/31 12:22:20 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * Maint module
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "debug_if.h"
+
+extern char *DRIVERRELEASE_MNT;
+
+#define DBG_MINIMUM  (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT  (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+extern void DIVA_DIDD_Read(void *, int);
+
+static dword notify_handle;
+static DESCRIPTOR DAdapter;
+static DESCRIPTOR MAdapter;
+static DESCRIPTOR MaintDescriptor =
+    { IDI_DIMAINT, 0, 0, (IDI_CALL) diva_maint_prtComp };
+
+extern int diva_os_copy_to_user(void *os_handle, void __user *dst,
+				const void *src, int length);
+extern int diva_os_copy_from_user(void *os_handle, void *dst,
+				  const void __user *src, int length);
+
+static void no_printf(unsigned char *x, ...)
+{
+	/* dummy debug function */
+}
+
+#include "debuglib.c"
+
+/*
+ *  DIDD callback function
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+			   int removal)
+{
+	if (adapter->type == IDI_DADAPTER) {
+		DBG_ERR(("cb: Change in DAdapter ? Oops ?."));
+	} else if (adapter->type == IDI_DIMAINT) {
+		if (removal) {
+			DbgDeregister();
+			memset(&MAdapter, 0, sizeof(MAdapter));
+			dprintf = no_printf;
+		} else {
+			memcpy(&MAdapter, adapter, sizeof(MAdapter));
+			dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+			DbgRegister("MAINT", DRIVERRELEASE_MNT, DBG_DEFAULT);
+		}
+	} else if ((adapter->type > 0) && (adapter->type < 16)) {
+		if (removal) {
+			diva_mnt_remove_xdi_adapter(adapter);
+		} else {
+			diva_mnt_add_xdi_adapter(adapter);
+		}
+	}
+	return (NULL);
+}
+
+/*
+ * connect to didd
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+	int x = 0;
+	int dadapter = 0;
+	IDI_SYNC_REQ req;
+	DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+	DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+	for (x = 0; x < MAX_DESCRIPTORS; x++) {
+		if (DIDD_Table[x].type == IDI_DADAPTER) {	/* DADAPTER found */
+			dadapter = 1;
+			memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+			req.didd_notify.e.Req = 0;
+			req.didd_notify.e.Rc =
+			    IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+			req.didd_notify.info.callback = (void *)didd_callback;
+			req.didd_notify.info.context = NULL;
+			DAdapter.request((ENTITY *) & req);
+			if (req.didd_notify.e.Rc != 0xff)
+				return (0);
+			notify_handle = req.didd_notify.info.handle;
+			/* Register MAINT (me) */
+			req.didd_add_adapter.e.Req = 0;
+			req.didd_add_adapter.e.Rc =
+			    IDI_SYNC_REQ_DIDD_ADD_ADAPTER;
+			req.didd_add_adapter.info.descriptor =
+			    (void *) &MaintDescriptor;
+			DAdapter.request((ENTITY *) & req);
+			if (req.didd_add_adapter.e.Rc != 0xff)
+				return (0);
+		} else if ((DIDD_Table[x].type > 0)
+			   && (DIDD_Table[x].type < 16)) {
+			diva_mnt_add_xdi_adapter(&DIDD_Table[x]);
+		}
+	}
+	return (dadapter);
+}
+
+/*
+ * disconnect from didd
+ */
+static void DIVA_EXIT_FUNCTION disconnect_didd(void)
+{
+	IDI_SYNC_REQ req;
+
+	req.didd_notify.e.Req = 0;
+	req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+	req.didd_notify.info.handle = notify_handle;
+	DAdapter.request((ENTITY *) & req);
+
+	req.didd_remove_adapter.e.Req = 0;
+	req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER;
+	req.didd_remove_adapter.info.p_request =
+	    (IDI_CALL) MaintDescriptor.request;
+	DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * read/write maint
+ */
+int maint_read_write(void __user *buf, int count)
+{
+	byte data[128];
+	dword cmd, id, mask;
+	int ret = 0;
+
+	if (count < (3 * sizeof(dword)))
+		return (-EFAULT);
+
+	if (diva_os_copy_from_user(NULL, (void *) &data[0],
+				   buf, 3 * sizeof(dword))) {
+		return (-EFAULT);
+	}
+
+	cmd = *(dword *) & data[0];	/* command */
+	id = *(dword *) & data[4];	/* driver id */
+	mask = *(dword *) & data[8];	/* mask or size */
+
+	switch (cmd) {
+	case DITRACE_CMD_GET_DRIVER_INFO:
+		if ((ret = diva_get_driver_info(id, data, sizeof(data))) > 0) {
+			if ((count < ret) || diva_os_copy_to_user
+			    (NULL, buf, (void *) &data[0], ret))
+				ret = -EFAULT;
+		} else {
+			ret = -EINVAL;
+		}
+		break;
+
+	case DITRACE_READ_DRIVER_DBG_MASK:
+		if ((ret = diva_get_driver_dbg_mask(id, (byte *) data)) > 0) {
+			if ((count < ret) || diva_os_copy_to_user
+			    (NULL, buf, (void *) &data[0], ret))
+				ret = -EFAULT;
+		} else {
+			ret = -ENODEV;
+		}
+		break;
+
+	case DITRACE_WRITE_DRIVER_DBG_MASK:
+		if ((ret = diva_set_driver_dbg_mask(id, mask)) <= 0) {
+			ret = -ENODEV;
+		}
+		break;
+
+    /*
+       Filter commands will ignore the ID due to fact that filtering affects
+       the B- channel and Audio Tap trace levels only. Also MAINT driver will
+       select the right trace ID by itself
+       */
+	case DITRACE_WRITE_SELECTIVE_TRACE_FILTER:
+		if (!mask) {
+			ret = diva_set_trace_filter (1, "*");
+		} else if (mask < sizeof(data)) {
+			if (diva_os_copy_from_user(NULL, data, (char __user *)buf+12, mask)) {
+				ret = -EFAULT;
+			} else {
+				ret = diva_set_trace_filter ((int)mask, data);
+			}
+		} else {
+			ret = -EINVAL;
+		}
+		break;
+
+	case DITRACE_READ_SELECTIVE_TRACE_FILTER:
+		if ((ret = diva_get_trace_filter (sizeof(data), data)) > 0) {
+			if (diva_os_copy_to_user (NULL, buf, data, ret))
+				ret = -EFAULT;
+		} else {
+			ret = -ENODEV;
+		}
+		break;
+
+	case DITRACE_READ_TRACE_ENTRY:{
+			diva_os_spin_lock_magic_t old_irql;
+			word size;
+			diva_dbg_entry_head_t *pmsg;
+			byte *pbuf;
+
+			if (!(pbuf = diva_os_malloc(0, mask))) {
+				return (-ENOMEM);
+			}
+
+			for(;;) {
+				if (!(pmsg =
+				    diva_maint_get_message(&size, &old_irql))) {
+					break;
+				}
+				if (size > mask) {
+					diva_maint_ack_message(0, &old_irql);
+					ret = -EINVAL;
+					break;
+				}
+				ret = size;
+				memcpy(pbuf, pmsg, size);
+				diva_maint_ack_message(1, &old_irql);
+				if ((count < size) ||
+				     diva_os_copy_to_user (NULL, buf, (void *) pbuf, size))
+							ret = -EFAULT;
+				break;
+			}
+			diva_os_free(0, pbuf);
+		}
+		break;
+
+	case DITRACE_READ_TRACE_ENTRYS:{
+			diva_os_spin_lock_magic_t old_irql;
+			word size;
+			diva_dbg_entry_head_t *pmsg;
+			byte *pbuf = NULL;
+			int written = 0;
+
+			if (mask < 4096) {
+				ret = -EINVAL;
+				break;
+			}
+			if (!(pbuf = diva_os_malloc(0, mask))) {
+				return (-ENOMEM);
+			}
+
+			for (;;) {
+				if (!(pmsg =
+				     diva_maint_get_message(&size, &old_irql))) {
+					break;
+				}
+				if ((size + 8) > mask) {
+					diva_maint_ack_message(0, &old_irql);
+					break;
+				}
+				/*
+				   Write entry length
+				 */
+				pbuf[written++] = (byte) size;
+				pbuf[written++] = (byte) (size >> 8);
+				pbuf[written++] = 0;
+				pbuf[written++] = 0;
+				/*
+				   Write message
+				 */
+				memcpy(&pbuf[written], pmsg, size);
+				diva_maint_ack_message(1, &old_irql);
+				written += size;
+				mask -= (size + 4);
+			}
+			pbuf[written++] = 0;
+			pbuf[written++] = 0;
+			pbuf[written++] = 0;
+			pbuf[written++] = 0;
+
+			if ((count < written) || diva_os_copy_to_user(NULL, buf, (void *) pbuf, written)) {
+				ret = -EFAULT;
+			} else {
+				ret = written;
+			}
+			diva_os_free(0, pbuf);
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+	return (ret);
+}
+
+/*
+ *  init
+ */
+int DIVA_INIT_FUNCTION mntfunc_init(int *buffer_length, void **buffer,
+				    unsigned long diva_dbg_mem)
+{
+	if (*buffer_length < 64) {
+		*buffer_length = 64;
+	}
+	if (*buffer_length > 512) {
+		*buffer_length = 512;
+	}
+	*buffer_length *= 1024;
+
+	if (diva_dbg_mem) {
+		*buffer = (void *) diva_dbg_mem;
+	} else {
+		while ((*buffer_length >= (64 * 1024))
+		       &&
+		       (!(*buffer = diva_os_malloc (0, *buffer_length)))) {
+			*buffer_length -= 1024;
+		}
+
+		if (!*buffer) {
+			DBG_ERR(("init: Can not alloc trace buffer"));
+			return (0);
+		}
+	}
+
+	if (diva_maint_init(*buffer, *buffer_length, (diva_dbg_mem == 0))) {
+		if (!diva_dbg_mem) {
+			diva_os_free (0, *buffer);
+		}
+		DBG_ERR(("init: maint init failed"));
+		return (0);
+	}
+
+	if (!connect_didd()) {
+		DBG_ERR(("init: failed to connect to DIDD."));
+		diva_maint_finit();
+		if (!diva_dbg_mem) {
+			diva_os_free (0, *buffer);
+		}
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ *  exit
+ */
+void DIVA_EXIT_FUNCTION mntfunc_finit(void)
+{
+	void *buffer;
+	int i = 100;
+
+	DbgDeregister();
+
+	while (diva_mnt_shutdown_xdi_adapters() && i--) {
+		diva_os_sleep(10);
+	}
+
+	disconnect_didd();
+
+	if ((buffer = diva_maint_finit())) {
+		diva_os_free (0, buffer);
+	}
+
+	memset(&MAdapter, 0, sizeof(MAdapter));
+	dprintf = no_printf;
+}
diff --git a/drivers/isdn/hardware/eicon/os_4bri.c b/drivers/isdn/hardware/eicon/os_4bri.c
new file mode 100644
index 0000000..cccfabc
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_4bri.c
@@ -0,0 +1,1131 @@
+/* $Id: os_4bri.c,v 1.28.4.4 2005/02/11 19:40:25 armin Exp $ */
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "dsp_defs.h"
+#include "di.h"
+#include "io.h"
+
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "os_4bri.h"
+#include "diva_pci.h"
+#include "mi_pc.h"
+#include "dsrv4bri.h"
+
+static void *diva_xdiLoadFileFile = NULL;
+static dword diva_xdiLoadFileLength = 0;
+
+/*
+**  IMPORTS
+*/
+extern void prepare_qBri_functions(PISDN_ADAPTER IoAdapter);
+extern void prepare_qBri2_functions(PISDN_ADAPTER IoAdapter);
+extern void diva_xdi_display_adapter_features(int card);
+extern void diva_add_slave_adapter(diva_os_xdi_adapter_t * a);
+
+extern int qBri_FPGA_download(PISDN_ADAPTER IoAdapter);
+extern void start_qBri_hardware(PISDN_ADAPTER IoAdapter);
+
+extern int diva_card_read_xlog(diva_os_xdi_adapter_t * a);
+
+/*
+**  LOCALS
+*/
+static unsigned long _4bri_bar_length[4] = {
+	0x100,
+	0x100,			/* I/O */
+	MQ_MEMORY_SIZE,
+	0x2000
+};
+static unsigned long _4bri_v2_bar_length[4] = {
+	0x100,
+	0x100,			/* I/O */
+	MQ2_MEMORY_SIZE,
+	0x10000
+};
+static unsigned long _4bri_v2_bri_bar_length[4] = {
+	0x100,
+	0x100,			/* I/O */
+	BRI2_MEMORY_SIZE,
+	0x10000
+};
+
+
+static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t * a);
+static int _4bri_get_serial_number(diva_os_xdi_adapter_t * a);
+static int diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+				   diva_xdi_um_cfg_cmd_t * cmd,
+				   int length);
+static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t * a);
+static int diva_4bri_write_fpga_image(diva_os_xdi_adapter_t * a,
+				      byte * data, dword length);
+static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter);
+static int diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+				       dword address,
+				       const byte * data,
+				       dword length, dword limit);
+static int diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter,
+				   dword start_address, dword features);
+static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter);
+static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t * a);
+
+static int _4bri_is_rev_2_card(int card_ordinal)
+{
+	switch (card_ordinal) {
+	case CARDTYPE_DIVASRV_Q_8M_V2_PCI:
+	case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI:
+	case CARDTYPE_DIVASRV_B_2M_V2_PCI:
+	case CARDTYPE_DIVASRV_B_2F_PCI:
+	case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
+		return (1);
+	}
+	return (0);
+}
+
+static int _4bri_is_rev_2_bri_card(int card_ordinal)
+{
+	switch (card_ordinal) {
+	case CARDTYPE_DIVASRV_B_2M_V2_PCI:
+	case CARDTYPE_DIVASRV_B_2F_PCI:
+	case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
+		return (1);
+	}
+	return (0);
+}
+
+static void diva_4bri_set_addresses(diva_os_xdi_adapter_t *a)
+{
+	dword offset = a->resources.pci.qoffset;
+	dword c_offset = offset * a->xdi_adapter.ControllerNumber;
+
+	a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 2;
+	a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2;
+	a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2;
+	a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 0;
+	a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 3;
+	a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 0;
+
+	/*
+	   Set up hardware related pointers
+	 */
+	a->xdi_adapter.Address = a->resources.pci.addr[2];	/* BAR2 SDRAM  */
+	a->xdi_adapter.Address += c_offset;
+
+	a->xdi_adapter.Control = a->resources.pci.addr[2];	/* BAR2 SDRAM  */
+
+	a->xdi_adapter.ram = a->resources.pci.addr[2];	/* BAR2 SDRAM  */
+	a->xdi_adapter.ram += c_offset + (offset - MQ_SHARED_RAM_SIZE);
+	
+	a->xdi_adapter.reset = a->resources.pci.addr[0];	/* BAR0 CONFIG */
+	/*
+	   ctlReg contains the register address for the MIPS CPU reset control
+	 */
+	a->xdi_adapter.ctlReg = a->resources.pci.addr[3];	/* BAR3 CNTRL  */
+	/*
+	   prom contains the register address for FPGA and EEPROM programming
+	 */
+	a->xdi_adapter.prom = &a->xdi_adapter.reset[0x6E];
+}
+
+/*
+**  BAR0 - MEM - 0x100    - CONFIG MEM
+**  BAR1 - I/O - 0x100    - UNUSED
+**  BAR2 - MEM - MQ_MEMORY_SIZE (MQ2_MEMORY_SIZE on Rev.2) - SDRAM
+**  BAR3 - MEM - 0x2000 (0x10000 on Rev.2)   - CNTRL
+**
+**  Called by master adapter, that will initialize and add slave adapters
+*/
+int diva_4bri_init_card(diva_os_xdi_adapter_t * a)
+{
+	int bar, i;
+	byte __iomem *p;
+	PADAPTER_LIST_ENTRY quadro_list;
+	diva_os_xdi_adapter_t *diva_current;
+	diva_os_xdi_adapter_t *adapter_list[4];
+	PISDN_ADAPTER Slave;
+	unsigned long bar_length[sizeof(_4bri_bar_length) /
+				 sizeof(_4bri_bar_length[0])];
+	int v2 = _4bri_is_rev_2_card(a->CardOrdinal);
+	int tasks = _4bri_is_rev_2_bri_card(a->CardOrdinal) ? 1 : MQ_INSTANCE_COUNT;
+	int factor = (tasks == 1) ? 1 : 2;
+
+	if (v2) {
+		if (_4bri_is_rev_2_bri_card(a->CardOrdinal)) {
+			memcpy(bar_length, _4bri_v2_bri_bar_length,
+			       sizeof(bar_length));
+		} else {
+			memcpy(bar_length, _4bri_v2_bar_length,
+			       sizeof(bar_length));
+		}
+	} else {
+		memcpy(bar_length, _4bri_bar_length, sizeof(bar_length));
+	}
+	DBG_TRC(("SDRAM_LENGTH=%08x, tasks=%d, factor=%d",
+		 bar_length[2], tasks, factor))
+
+	/*
+	   Get Serial Number
+	   The serial number of 4BRI is accessible in accordance with PCI spec
+	   via command register located in configuration space, also we do not
+	   have to map any BAR before we can access it
+	 */
+	if (!_4bri_get_serial_number(a)) {
+		DBG_ERR(("A: 4BRI can't get Serial Number"))
+		diva_4bri_cleanup_adapter(a);
+		return (-1);
+	}
+
+	/*
+	   Set properties
+	 */
+	a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
+	DBG_LOG(("Load %s, SN:%ld, bus:%02x, func:%02x",
+		 a->xdi_adapter.Properties.Name,
+		 a->xdi_adapter.serialNo,
+		 a->resources.pci.bus, a->resources.pci.func))
+
+	/*
+	   First initialization step: get and check hardware resoures.
+	   Do not map resources and do not access card at this step
+	 */
+	for (bar = 0; bar < 4; bar++) {
+		a->resources.pci.bar[bar] =
+		    divasa_get_pci_bar(a->resources.pci.bus,
+				       a->resources.pci.func, bar,
+				       a->resources.pci.hdev);
+		if (!a->resources.pci.bar[bar]
+		    || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) {
+			DBG_ERR(
+				("A: invalid bar[%d]=%08x", bar,
+				 a->resources.pci.bar[bar]))
+			return (-1);
+		}
+	}
+	a->resources.pci.irq =
+	    (byte) divasa_get_pci_irq(a->resources.pci.bus,
+				      a->resources.pci.func,
+				      a->resources.pci.hdev);
+	if (!a->resources.pci.irq) {
+		DBG_ERR(("A: invalid irq"));
+		return (-1);
+	}
+
+	a->xdi_adapter.sdram_bar = a->resources.pci.bar[2];
+
+	/*
+	   Map all MEMORY BAR's
+	 */
+	for (bar = 0; bar < 4; bar++) {
+		if (bar != 1) {	/* ignore I/O */
+			a->resources.pci.addr[bar] =
+			    divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar],
+						 bar_length[bar]);
+			if (!a->resources.pci.addr[bar]) {
+				DBG_ERR(("A: 4BRI: can't map bar[%d]", bar))
+				diva_4bri_cleanup_adapter(a);
+				return (-1);
+			}
+		}
+	}
+
+	/*
+	   Register I/O port
+	 */
+	sprintf(&a->port_name[0], "DIVA 4BRI %ld", (long) a->xdi_adapter.serialNo);
+
+	if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1],
+				     bar_length[1], &a->port_name[0], 1)) {
+		DBG_ERR(("A: 4BRI: can't register bar[1]"))
+		diva_4bri_cleanup_adapter(a);
+		return (-1);
+	}
+
+	a->resources.pci.addr[1] =
+		(void *) (unsigned long) a->resources.pci.bar[1];
+
+	/*
+	   Set cleanup pointer for base adapter only, so slave adapter
+	   will be unable to get cleanup
+	 */
+	a->interface.cleanup_adapter_proc = diva_4bri_cleanup_adapter;
+
+	/*
+	   Create slave adapters
+	 */
+	if (tasks > 1) {
+		if (!(a->slave_adapters[0] =
+		     (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
+		{
+			diva_4bri_cleanup_adapter(a);
+			return (-1);
+		}
+		if (!(a->slave_adapters[1] =
+		     (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
+		{
+			diva_os_free(0, a->slave_adapters[0]);
+			a->slave_adapters[0] = NULL;
+			diva_4bri_cleanup_adapter(a);
+			return (-1);
+		}
+		if (!(a->slave_adapters[2] =
+		     (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
+		{
+			diva_os_free(0, a->slave_adapters[0]);
+			diva_os_free(0, a->slave_adapters[1]);
+			a->slave_adapters[0] = NULL;
+			a->slave_adapters[1] = NULL;
+			diva_4bri_cleanup_adapter(a);
+			return (-1);
+		}
+		memset(a->slave_adapters[0], 0x00, sizeof(*a));
+		memset(a->slave_adapters[1], 0x00, sizeof(*a));
+		memset(a->slave_adapters[2], 0x00, sizeof(*a));
+	}
+
+	adapter_list[0] = a;
+	adapter_list[1] = a->slave_adapters[0];
+	adapter_list[2] = a->slave_adapters[1];
+	adapter_list[3] = a->slave_adapters[2];
+
+	/*
+	   Allocate slave list
+	 */
+	quadro_list =
+	    (PADAPTER_LIST_ENTRY) diva_os_malloc(0, sizeof(*quadro_list));
+	if (!(a->slave_list = quadro_list)) {
+		for (i = 0; i < (tasks - 1); i++) {
+			diva_os_free(0, a->slave_adapters[i]);
+			a->slave_adapters[i] = NULL;
+		}
+		diva_4bri_cleanup_adapter(a);
+		return (-1);
+	}
+	memset(quadro_list, 0x00, sizeof(*quadro_list));
+
+	/*
+	   Set interfaces
+	 */
+	a->xdi_adapter.QuadroList = quadro_list;
+	for (i = 0; i < tasks; i++) {
+		adapter_list[i]->xdi_adapter.ControllerNumber = i;
+		adapter_list[i]->xdi_adapter.tasks = tasks;
+		quadro_list->QuadroAdapter[i] =
+		    &adapter_list[i]->xdi_adapter;
+	}
+
+	for (i = 0; i < tasks; i++) {
+		diva_current = adapter_list[i];
+
+		diva_current->dsp_mask = 0x00000003;
+
+		diva_current->xdi_adapter.a.io =
+		    &diva_current->xdi_adapter;
+		diva_current->xdi_adapter.DIRequest = request;
+		diva_current->interface.cmd_proc = diva_4bri_cmd_card_proc;
+		diva_current->xdi_adapter.Properties =
+		    CardProperties[a->CardOrdinal];
+		diva_current->CardOrdinal = a->CardOrdinal;
+
+		diva_current->xdi_adapter.Channels =
+		    CardProperties[a->CardOrdinal].Channels;
+		diva_current->xdi_adapter.e_max =
+		    CardProperties[a->CardOrdinal].E_info;
+		diva_current->xdi_adapter.e_tbl =
+		    diva_os_malloc(0,
+				   diva_current->xdi_adapter.e_max *
+				   sizeof(E_INFO));
+
+		if (!diva_current->xdi_adapter.e_tbl) {
+			diva_4bri_cleanup_slave_adapters(a);
+			diva_4bri_cleanup_adapter(a);
+			for (i = 1; i < (tasks - 1); i++) {
+				diva_os_free(0, adapter_list[i]);
+			}
+			return (-1);
+		}
+		memset(diva_current->xdi_adapter.e_tbl, 0x00,
+		       diva_current->xdi_adapter.e_max * sizeof(E_INFO));
+
+		if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.isr_spin_lock, "isr")) {
+			diva_4bri_cleanup_slave_adapters(a);
+			diva_4bri_cleanup_adapter(a);
+			for (i = 1; i < (tasks - 1); i++) {
+				diva_os_free(0, adapter_list[i]);
+			}
+			return (-1);
+		}
+		if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.data_spin_lock, "data")) {
+			diva_4bri_cleanup_slave_adapters(a);
+			diva_4bri_cleanup_adapter(a);
+			for (i = 1; i < (tasks - 1); i++) {
+				diva_os_free(0, adapter_list[i]);
+			}
+			return (-1);
+		}
+
+		strcpy(diva_current->xdi_adapter.req_soft_isr. dpc_thread_name, "kdivas4brid");
+
+		if (diva_os_initialize_soft_isr (&diva_current->xdi_adapter.req_soft_isr, DIDpcRoutine,
+		     &diva_current->xdi_adapter)) {
+			diva_4bri_cleanup_slave_adapters(a);
+			diva_4bri_cleanup_adapter(a);
+			for (i = 1; i < (tasks - 1); i++) {
+				diva_os_free(0, adapter_list[i]);
+			}
+			return (-1);
+		}
+
+		/*
+		   Do not initialize second DPC - only one thread will be created
+		 */
+		diva_current->xdi_adapter.isr_soft_isr.object =
+		    diva_current->xdi_adapter.req_soft_isr.object;
+	}
+
+	if (v2) {
+		prepare_qBri2_functions(&a->xdi_adapter);
+	} else {
+		prepare_qBri_functions(&a->xdi_adapter);
+	}
+
+	for (i = 0; i < tasks; i++) {
+		diva_current = adapter_list[i];
+		if (i)
+			memcpy(&diva_current->resources, &a->resources, sizeof(divas_card_resources_t));
+		diva_current->resources.pci.qoffset = (a->xdi_adapter.MemorySize >> factor); 
+	}
+
+	/*
+	   Set up hardware related pointers
+	 */
+	a->xdi_adapter.cfg = (void *) (unsigned long) a->resources.pci.bar[0];	/* BAR0 CONFIG */
+	a->xdi_adapter.port = (void *) (unsigned long) a->resources.pci.bar[1];	/* BAR1        */
+	a->xdi_adapter.ctlReg = (void *) (unsigned long) a->resources.pci.bar[3];	/* BAR3 CNTRL  */
+
+	for (i = 0; i < tasks; i++) {
+		diva_current = adapter_list[i];
+		diva_4bri_set_addresses(diva_current);
+		Slave = a->xdi_adapter.QuadroList->QuadroAdapter[i];
+		Slave->MultiMaster = &a->xdi_adapter;
+		Slave->sdram_bar = a->xdi_adapter.sdram_bar;
+		if (i) {
+			Slave->serialNo = ((dword) (Slave->ControllerNumber << 24)) |
+					a->xdi_adapter.serialNo;
+			Slave->cardType = a->xdi_adapter.cardType;
+		}
+	}
+
+	/*
+	   reset contains the base address for the PLX 9054 register set
+	 */
+	p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+	WRITE_BYTE(&p[PLX9054_INTCSR], 0x00);	/* disable PCI interrupts */
+	DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+
+	/*
+	   Set IRQ handler
+	 */
+	a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
+	sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA 4BRI %ld",
+		(long) a->xdi_adapter.serialNo);
+
+	if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
+				 a->xdi_adapter.irq_info.irq_name)) {
+		diva_4bri_cleanup_slave_adapters(a);
+		diva_4bri_cleanup_adapter(a);
+		for (i = 1; i < (tasks - 1); i++) {
+			diva_os_free(0, adapter_list[i]);
+		}
+		return (-1);
+	}
+
+	a->xdi_adapter.irq_info.registered = 1;
+
+	/*
+	   Add three slave adapters
+	 */
+	if (tasks > 1) {
+		diva_add_slave_adapter(adapter_list[1]);
+		diva_add_slave_adapter(adapter_list[2]);
+		diva_add_slave_adapter(adapter_list[3]);
+	}
+
+	diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
+		      a->resources.pci.irq, a->xdi_adapter.serialNo);
+
+	return (0);
+}
+
+/*
+**  Cleanup function will be called for master adapter only
+**  this is garanteed by design: cleanup callback is set
+**  by master adapter only
+*/
+static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t * a)
+{
+	int bar;
+
+	/*
+	   Stop adapter if running
+	 */
+	if (a->xdi_adapter.Initialized) {
+		diva_4bri_stop_adapter(a);
+	}
+
+	/*
+	   Remove IRQ handler
+	 */
+	if (a->xdi_adapter.irq_info.registered) {
+		diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
+	}
+	a->xdi_adapter.irq_info.registered = 0;
+
+	/*
+	   Free DPC's and spin locks on all adapters
+	 */
+	diva_4bri_cleanup_slave_adapters(a);
+
+	/*
+	   Unmap all BARS
+	 */
+	for (bar = 0; bar < 4; bar++) {
+		if (bar != 1) {
+			if (a->resources.pci.bar[bar]
+			    && a->resources.pci.addr[bar]) {
+				divasa_unmap_pci_bar(a->resources.pci.addr[bar]);
+				a->resources.pci.bar[bar] = 0;
+				a->resources.pci.addr[bar] = NULL;
+			}
+		}
+	}
+
+	/*
+	   Unregister I/O
+	 */
+	if (a->resources.pci.bar[1] && a->resources.pci.addr[1]) {
+		diva_os_register_io_port(a, 0, a->resources.pci.bar[1],
+					 _4bri_is_rev_2_card(a->
+							     CardOrdinal) ?
+					 _4bri_v2_bar_length[1] :
+					 _4bri_bar_length[1],
+					 &a->port_name[0], 1);
+		a->resources.pci.bar[1] = 0;
+		a->resources.pci.addr[1] = NULL;
+	}
+
+	if (a->slave_list) {
+		diva_os_free(0, a->slave_list);
+		a->slave_list = NULL;
+	}
+
+	return (0);
+}
+
+static int _4bri_get_serial_number(diva_os_xdi_adapter_t * a)
+{
+	dword data[64];
+	dword serNo;
+	word addr, status, i, j;
+	byte Bus, Slot;
+	void *hdev;
+
+	Bus = a->resources.pci.bus;
+	Slot = a->resources.pci.func;
+	hdev = a->resources.pci.hdev;
+
+	for (i = 0; i < 64; ++i) {
+		addr = i * 4;
+		for (j = 0; j < 5; ++j) {
+			PCIwrite(Bus, Slot, 0x4E, &addr, sizeof(addr),
+				 hdev);
+			diva_os_wait(1);
+			PCIread(Bus, Slot, 0x4E, &status, sizeof(status),
+				hdev);
+			if (status & 0x8000)
+				break;
+		}
+		if (j >= 5) {
+			DBG_ERR(("EEPROM[%d] read failed (0x%x)", i * 4, addr))
+			return (0);
+		}
+		PCIread(Bus, Slot, 0x50, &data[i], sizeof(data[i]), hdev);
+	}
+	DBG_BLK(((char *) &data[0], sizeof(data)))
+
+	serNo = data[32];
+	if (serNo == 0 || serNo == 0xffffffff)
+		serNo = data[63];
+
+	if (!serNo) {
+		DBG_LOG(("W: Serial Number == 0, create one serial number"));
+		serNo = a->resources.pci.bar[1] & 0xffff0000;
+		serNo |= a->resources.pci.bus << 8;
+		serNo |= a->resources.pci.func;
+	}
+
+	a->xdi_adapter.serialNo = serNo;
+
+	DBG_REG(("Serial No.          : %ld", a->xdi_adapter.serialNo))
+
+	return (serNo);
+}
+
+/*
+**  Release resources of slave adapters
+*/
+static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t * a)
+{
+	diva_os_xdi_adapter_t *adapter_list[4];
+	diva_os_xdi_adapter_t *diva_current;
+	int i;
+
+	adapter_list[0] = a;
+	adapter_list[1] = a->slave_adapters[0];
+	adapter_list[2] = a->slave_adapters[1];
+	adapter_list[3] = a->slave_adapters[2];
+
+	for (i = 0; i < a->xdi_adapter.tasks; i++) {
+		diva_current = adapter_list[i];
+		if (diva_current) {
+			diva_os_destroy_spin_lock(&diva_current->
+						  xdi_adapter.
+						  isr_spin_lock, "unload");
+			diva_os_destroy_spin_lock(&diva_current->
+						  xdi_adapter.
+						  data_spin_lock,
+						  "unload");
+
+			diva_os_cancel_soft_isr(&diva_current->xdi_adapter.
+						req_soft_isr);
+			diva_os_cancel_soft_isr(&diva_current->xdi_adapter.
+						isr_soft_isr);
+
+			diva_os_remove_soft_isr(&diva_current->xdi_adapter.
+						req_soft_isr);
+			diva_current->xdi_adapter.isr_soft_isr.object = NULL;
+
+			if (diva_current->xdi_adapter.e_tbl) {
+				diva_os_free(0,
+					     diva_current->xdi_adapter.
+					     e_tbl);
+			}
+			diva_current->xdi_adapter.e_tbl = NULL;
+			diva_current->xdi_adapter.e_max = 0;
+			diva_current->xdi_adapter.e_count = 0;
+		}
+	}
+
+	return (0);
+}
+
+static int
+diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+			diva_xdi_um_cfg_cmd_t * cmd, int length)
+{
+	int ret = -1;
+
+	if (cmd->adapter != a->controller) {
+		DBG_ERR(("A: 4bri_cmd, invalid controller=%d != %d",
+			 cmd->adapter, a->controller))
+		return (-1);
+	}
+
+	switch (cmd->command) {
+	case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			*(dword *) a->xdi_mbox.data =
+			    (dword) a->CardOrdinal;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			*(dword *) a->xdi_mbox.data =
+			    (dword) a->xdi_adapter.serialNo;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
+		if (!a->xdi_adapter.ControllerNumber) {
+			/*
+			   Only master adapter can access hardware config
+			 */
+			a->xdi_mbox.data_length = sizeof(dword) * 9;
+			a->xdi_mbox.data =
+			    diva_os_malloc(0, a->xdi_mbox.data_length);
+			if (a->xdi_mbox.data) {
+				int i;
+				dword *data = (dword *) a->xdi_mbox.data;
+
+				for (i = 0; i < 8; i++) {
+					*data++ = a->resources.pci.bar[i];
+				}
+				*data++ = (dword) a->resources.pci.irq;
+				a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+				ret = 0;
+			}
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_CARD_STATE:
+		if (!a->xdi_adapter.ControllerNumber) {
+			a->xdi_mbox.data_length = sizeof(dword);
+			a->xdi_mbox.data =
+			    diva_os_malloc(0, a->xdi_mbox.data_length);
+			if (a->xdi_mbox.data) {
+				dword *data = (dword *) a->xdi_mbox.data;
+				if (!a->xdi_adapter.ram
+				    || !a->xdi_adapter.reset
+				    || !a->xdi_adapter.cfg) {
+					*data = 3;
+				} else if (a->xdi_adapter.trapped) {
+					*data = 2;
+				} else if (a->xdi_adapter.Initialized) {
+					*data = 1;
+				} else {
+					*data = 0;
+				}
+				a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+				ret = 0;
+			}
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_WRITE_FPGA:
+		if (!a->xdi_adapter.ControllerNumber) {
+			ret =
+			    diva_4bri_write_fpga_image(a,
+						       (byte *) & cmd[1],
+						       cmd->command_data.
+						       write_fpga.
+						       image_length);
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_RESET_ADAPTER:
+		if (!a->xdi_adapter.ControllerNumber) {
+			ret = diva_4bri_reset_adapter(&a->xdi_adapter);
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
+		if (!a->xdi_adapter.ControllerNumber) {
+			ret = diva_4bri_write_sdram_block(&a->xdi_adapter,
+							  cmd->
+							  command_data.
+							  write_sdram.
+							  offset,
+							  (byte *) &
+							  cmd[1],
+							  cmd->
+							  command_data.
+							  write_sdram.
+							  length,
+							  a->xdi_adapter.
+							  MemorySize);
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_START_ADAPTER:
+		if (!a->xdi_adapter.ControllerNumber) {
+			ret = diva_4bri_start_adapter(&a->xdi_adapter,
+						      cmd->command_data.
+						      start.offset,
+						      cmd->command_data.
+						      start.features);
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
+		if (!a->xdi_adapter.ControllerNumber) {
+			a->xdi_adapter.features =
+			    cmd->command_data.features.features;
+			a->xdi_adapter.a.protocol_capabilities =
+			    a->xdi_adapter.features;
+			DBG_TRC(("Set raw protocol features (%08x)",
+				 a->xdi_adapter.features))
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_STOP_ADAPTER:
+		if (!a->xdi_adapter.ControllerNumber) {
+			ret = diva_4bri_stop_adapter(a);
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
+		ret = diva_card_read_xlog(a);
+		break;
+
+	case DIVA_XDI_UM_CMD_READ_SDRAM:
+		if (!a->xdi_adapter.ControllerNumber
+		    && a->xdi_adapter.Address) {
+			if (
+			    (a->xdi_mbox.data_length =
+			     cmd->command_data.read_sdram.length)) {
+				if (
+				    (a->xdi_mbox.data_length +
+				     cmd->command_data.read_sdram.offset) <
+				    a->xdi_adapter.MemorySize) {
+					a->xdi_mbox.data =
+					    diva_os_malloc(0,
+							   a->xdi_mbox.
+							   data_length);
+					if (a->xdi_mbox.data) {
+						byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter);
+						byte __iomem *src = p;
+						byte *dst = a->xdi_mbox.data;
+						dword len = a->xdi_mbox.data_length;
+
+						src += cmd->command_data.read_sdram.offset;
+
+						while (len--) {
+							*dst++ = READ_BYTE(src++);
+						}
+						DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p);
+						a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+						ret = 0;
+					}
+				}
+			}
+		}
+		break;
+
+	default:
+		DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller,
+			 cmd->command))
+	}
+
+	return (ret);
+}
+
+void *xdiLoadFile(char *FileName, unsigned long *FileLength,
+		  unsigned long lim)
+{
+	void *ret = diva_xdiLoadFileFile;
+
+	if (FileLength) {
+		*FileLength = diva_xdiLoadFileLength;
+	}
+	diva_xdiLoadFileFile = NULL;
+	diva_xdiLoadFileLength = 0;
+
+	return (ret);
+}
+
+void diva_os_set_qBri_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+void diva_os_set_qBri2_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+static int
+diva_4bri_write_fpga_image(diva_os_xdi_adapter_t * a, byte * data,
+			   dword length)
+{
+	int ret;
+
+	diva_xdiLoadFileFile = data;
+	diva_xdiLoadFileLength = length;
+
+	ret = qBri_FPGA_download(&a->xdi_adapter);
+
+	diva_xdiLoadFileFile = NULL;
+	diva_xdiLoadFileLength = 0;
+
+	return (ret ? 0 : -1);
+}
+
+static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter)
+{
+	PISDN_ADAPTER Slave;
+	int i;
+
+	if (!IoAdapter->Address || !IoAdapter->reset) {
+		return (-1);
+	}
+	if (IoAdapter->Initialized) {
+		DBG_ERR(("A: A(%d) can't reset 4BRI adapter - please stop first",
+			 IoAdapter->ANum))
+		return (-1);
+	}
+
+	/*
+	   Forget all entities on all adapters
+	 */
+	for (i = 0; ((i < IoAdapter->tasks) && IoAdapter->QuadroList); i++) {
+		Slave = IoAdapter->QuadroList->QuadroAdapter[i];
+		Slave->e_count = 0;
+		if (Slave->e_tbl) {
+			memset(Slave->e_tbl, 0x00,
+			       Slave->e_max * sizeof(E_INFO));
+		}
+		Slave->head = 0;
+		Slave->tail = 0;
+		Slave->assign = 0;
+		Slave->trapped = 0;
+
+		memset(&Slave->a.IdTable[0], 0x00,
+		       sizeof(Slave->a.IdTable));
+		memset(&Slave->a.IdTypeTable[0], 0x00,
+		       sizeof(Slave->a.IdTypeTable));
+		memset(&Slave->a.FlowControlIdTable[0], 0x00,
+		       sizeof(Slave->a.FlowControlIdTable));
+		memset(&Slave->a.FlowControlSkipTable[0], 0x00,
+		       sizeof(Slave->a.FlowControlSkipTable));
+		memset(&Slave->a.misc_flags_table[0], 0x00,
+		       sizeof(Slave->a.misc_flags_table));
+		memset(&Slave->a.rx_stream[0], 0x00,
+		       sizeof(Slave->a.rx_stream));
+		memset(&Slave->a.tx_stream[0], 0x00,
+		       sizeof(Slave->a.tx_stream));
+		memset(&Slave->a.tx_pos[0], 0x00, sizeof(Slave->a.tx_pos));
+		memset(&Slave->a.rx_pos[0], 0x00, sizeof(Slave->a.rx_pos));
+	}
+
+	return (0);
+}
+
+
+static int
+diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+			    dword address,
+			    const byte * data, dword length, dword limit)
+{
+	byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+	byte __iomem *mem = p;
+
+	if (((address + length) >= limit) || !mem) {
+		DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+		DBG_ERR(("A: A(%d) write 4BRI address=0x%08lx",
+			 IoAdapter->ANum, address + length))
+		return (-1);
+	}
+	mem += address;
+
+	while (length--) {
+		WRITE_BYTE(mem++, *data++);
+	}
+
+	DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+	return (0);
+}
+
+static int
+diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter,
+			dword start_address, dword features)
+{
+	volatile word __iomem *signature;
+	int started = 0;
+	int i;
+	byte __iomem *p;
+
+	/*
+	   start adapter
+	 */
+	start_qBri_hardware(IoAdapter);
+
+	p = DIVA_OS_MEM_ATTACH_RAM(IoAdapter);
+	/*
+	   wait for signature in shared memory (max. 3 seconds)
+	 */
+	signature = (volatile word __iomem *) (&p[0x1E]);
+
+	for (i = 0; i < 300; ++i) {
+		diva_os_wait(10);
+		if (READ_WORD(&signature[0]) == 0x4447) {
+			DBG_TRC(("Protocol startup time %d.%02d seconds",
+				 (i / 100), (i % 100)))
+			started = 1;
+			break;
+		}
+	}
+
+	for (i = 1; i < IoAdapter->tasks; i++) {
+		IoAdapter->QuadroList->QuadroAdapter[i]->features =
+		    IoAdapter->features;
+		IoAdapter->QuadroList->QuadroAdapter[i]->a.
+		    protocol_capabilities = IoAdapter->features;
+	}
+
+	if (!started) {
+		DBG_FTL(("%s: Adapter selftest failed, signature=%04x",
+			 IoAdapter->Properties.Name,
+			 READ_WORD(&signature[0])))
+		DIVA_OS_MEM_DETACH_RAM(IoAdapter, p);
+		(*(IoAdapter->trapFnc)) (IoAdapter);
+		IoAdapter->stop(IoAdapter);
+		return (-1);
+	}
+	DIVA_OS_MEM_DETACH_RAM(IoAdapter, p);
+
+	for (i = 0; i < IoAdapter->tasks; i++) {
+		IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 1;
+		IoAdapter->QuadroList->QuadroAdapter[i]->IrqCount = 0;
+	}
+
+	if (check_qBri_interrupt(IoAdapter)) {
+		DBG_ERR(("A: A(%d) interrupt test failed",
+			 IoAdapter->ANum))
+		for (i = 0; i < IoAdapter->tasks; i++) {
+			IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0;
+		}
+		IoAdapter->stop(IoAdapter);
+		return (-1);
+	}
+
+	IoAdapter->Properties.Features = (word) features;
+	diva_xdi_display_adapter_features(IoAdapter->ANum);
+
+	for (i = 0; i < IoAdapter->tasks; i++) {
+		DBG_LOG(("A(%d) %s adapter successfull started",
+			 IoAdapter->QuadroList->QuadroAdapter[i]->ANum,
+			 (IoAdapter->tasks == 1) ? "BRI 2.0" : "4BRI"))
+		diva_xdi_didd_register_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum);
+		IoAdapter->QuadroList->QuadroAdapter[i]->Properties.Features = (word) features;
+	}
+
+	return (0);
+}
+
+static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter)
+{
+#ifdef	SUPPORT_INTERRUPT_TEST_ON_4BRI
+	int i;
+	ADAPTER *a = &IoAdapter->a;
+	byte __iomem *p;
+
+	IoAdapter->IrqCount = 0;
+
+	if (IoAdapter->ControllerNumber > 0)
+		return (-1);
+
+	p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+	WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE);
+	DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+	/*
+	   interrupt test
+	 */
+	a->ReadyInt = 1;
+	a->ram_out(a, &PR_RAM->ReadyInt, 1);
+
+	for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10));
+
+	return ((IoAdapter->IrqCount > 0) ? 0 : -1);
+#else
+	dword volatile __iomem *qBriIrq;
+	byte __iomem *p;
+	/*
+	   Reset on-board interrupt register
+	 */
+	IoAdapter->IrqCount = 0;
+	p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	qBriIrq = (dword volatile __iomem *) (&p[_4bri_is_rev_2_card
+				       (IoAdapter->
+					cardType) ? (MQ2_BREG_IRQ_TEST)
+				       : (MQ_BREG_IRQ_TEST)]);
+
+	WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF);
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+	p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+	WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE);
+	DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+
+	diva_os_wait(100);
+
+	return (0);
+#endif				/* SUPPORT_INTERRUPT_TEST_ON_4BRI */
+}
+
+static void diva_4bri_clear_interrupts(diva_os_xdi_adapter_t * a)
+{
+	PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+
+	/*
+	   clear any pending interrupt
+	 */
+	IoAdapter->disIrq(IoAdapter);
+
+	IoAdapter->tst_irq(&IoAdapter->a);
+	IoAdapter->clr_irq(&IoAdapter->a);
+	IoAdapter->tst_irq(&IoAdapter->a);
+
+	/*
+	   kill pending dpcs
+	 */
+	diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
+	diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
+}
+
+static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t * a)
+{
+	PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+	int i;
+
+	if (!IoAdapter->ram) {
+		return (-1);
+	}
+
+	if (!IoAdapter->Initialized) {
+		DBG_ERR(("A: A(%d) can't stop PRI adapter - not running",
+			 IoAdapter->ANum))
+		return (-1);	/* nothing to stop */
+	}
+
+	for (i = 0; i < IoAdapter->tasks; i++) {
+		IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0;
+	}
+
+	/*
+	   Disconnect Adapters from DIDD
+	 */
+	for (i = 0; i < IoAdapter->tasks; i++) {
+		diva_xdi_didd_remove_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum);
+	}
+
+	i = 100;
+
+	/*
+	   Stop interrupts
+	 */
+	a->clear_interrupts_proc = diva_4bri_clear_interrupts;
+	IoAdapter->a.ReadyInt = 1;
+	IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
+	do {
+		diva_os_sleep(10);
+	} while (i-- && a->clear_interrupts_proc);
+
+	if (a->clear_interrupts_proc) {
+		diva_4bri_clear_interrupts(a);
+		a->clear_interrupts_proc = NULL;
+		DBG_ERR(("A: A(%d) no final interrupt from 4BRI adapter",
+			 IoAdapter->ANum))
+	}
+	IoAdapter->a.ReadyInt = 0;
+
+	/*
+	   Stop and reset adapter
+	 */
+	IoAdapter->stop(IoAdapter);
+
+	return (0);
+}
diff --git a/drivers/isdn/hardware/eicon/os_4bri.h b/drivers/isdn/hardware/eicon/os_4bri.h
new file mode 100644
index 0000000..665f0af
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_4bri.h
@@ -0,0 +1,8 @@
+/* $Id: os_4bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
+
+#ifndef __DIVA_OS_4_BRI_H__
+#define __DIVA_OS_4_BRI_H__
+
+int diva_4bri_init_card(diva_os_xdi_adapter_t * a);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/os_bri.c b/drivers/isdn/hardware/eicon/os_bri.c
new file mode 100644
index 0000000..4cc44a5
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_bri.c
@@ -0,0 +1,813 @@
+/* $Id: os_bri.c,v 1.21 2004/03/21 17:26:01 armin Exp $ */
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "dsp_defs.h"
+#include "di.h"
+#include "io.h"
+
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "os_bri.h"
+#include "diva_pci.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+
+/*
+**  IMPORTS
+*/
+extern void prepare_maestra_functions(PISDN_ADAPTER IoAdapter);
+extern void diva_xdi_display_adapter_features(int card);
+extern int diva_card_read_xlog(diva_os_xdi_adapter_t * a);
+
+/*
+**  LOCALS
+*/
+static int bri_bar_length[3] = {
+	0x80,
+	0x80,
+	0x20
+};
+static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t * a);
+static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t * a);
+static int diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+				  diva_xdi_um_cfg_cmd_t * cmd, int length);
+static int diva_bri_reregister_io(diva_os_xdi_adapter_t * a);
+static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter);
+static int diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+				      dword address,
+				      const byte * data, dword length);
+static int diva_bri_start_adapter(PISDN_ADAPTER IoAdapter,
+				  dword start_address, dword features);
+static int diva_bri_stop_adapter(diva_os_xdi_adapter_t * a);
+
+static void diva_bri_set_addresses(diva_os_xdi_adapter_t * a)
+{
+	a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0;
+	a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 1;
+	a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2;
+	a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 1;
+	a->resources.pci.mem_type_id[MEM_TYPE_PORT] = 2;
+	a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 2;
+	
+	a->xdi_adapter.ram = a->resources.pci.addr[0];
+	a->xdi_adapter.cfg = a->resources.pci.addr[1];
+	a->xdi_adapter.Address = a->resources.pci.addr[2];
+
+	a->xdi_adapter.reset = a->xdi_adapter.cfg;
+	a->xdi_adapter.port = a->xdi_adapter.Address;
+
+	a->xdi_adapter.ctlReg = a->xdi_adapter.port + M_PCI_RESET;
+
+	a->xdi_adapter.reset += 0x4C;	/* PLX 9050 !! */
+}
+
+/*
+**  BAR0 - MEM Addr  - 0x80  - NOT USED
+**  BAR1 - I/O Addr  - 0x80
+**  BAR2 - I/O Addr  - 0x20
+*/
+int diva_bri_init_card(diva_os_xdi_adapter_t * a)
+{
+	int bar;
+	dword bar2 = 0, bar2_length = 0xffffffff;
+	word cmd = 0, cmd_org;
+	byte Bus, Slot;
+	void *hdev;
+	byte __iomem *p;
+
+	/*
+	   Set properties
+	 */
+	a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
+	DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name))
+
+	    /*
+	       Get resources
+	     */
+	    for (bar = 0; bar < 3; bar++) {
+		a->resources.pci.bar[bar] =
+		    divasa_get_pci_bar(a->resources.pci.bus,
+				       a->resources.pci.func, bar,
+				       a->resources.pci.hdev);
+		if (!a->resources.pci.bar[bar]) {
+			DBG_ERR(("A: can't get BAR[%d]", bar))
+			return (-1);
+		}
+	}
+
+	a->resources.pci.irq =
+	    (byte) divasa_get_pci_irq(a->resources.pci.bus,
+				      a->resources.pci.func,
+				      a->resources.pci.hdev);
+	if (!a->resources.pci.irq) {
+		DBG_ERR(("A: invalid irq"));
+		return (-1);
+	}
+
+	/*
+	   Get length of I/O bar 2 - it is different by older
+	   EEPROM version
+	 */
+	Bus = a->resources.pci.bus;
+	Slot = a->resources.pci.func;
+	hdev = a->resources.pci.hdev;
+
+	/*
+	   Get plain original values of the BAR2 CDM registers
+	 */
+	PCIread(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev);
+	PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+	/*
+	   Disable device and get BAR2 length
+	 */
+	PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev);
+	PCIwrite(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev);
+	PCIread(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev);
+	/*
+	   Restore BAR2 and CMD registers
+	 */
+	PCIwrite(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev);
+	PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+
+	/*
+	   Calculate BAR2 length
+	 */
+	bar2_length = (~(bar2_length & ~7)) + 1;
+	DBG_LOG(("BAR[2] length=%lx", bar2_length))
+
+	    /*
+	       Map and register resources
+	     */
+	    if (!(a->resources.pci.addr[0] =
+		 divasa_remap_pci_bar(a, 0, a->resources.pci.bar[0],
+				      bri_bar_length[0]))) {
+		DBG_ERR(("A: BRI, can't map BAR[0]"))
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+
+	sprintf(&a->port_name[0], "BRI %02x:%02x",
+		a->resources.pci.bus, a->resources.pci.func);
+
+	if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1],
+				     bri_bar_length[1], &a->port_name[0], 1)) {
+		DBG_ERR(("A: BRI, can't register BAR[1]"))
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+	a->resources.pci.addr[1] = (void *) (unsigned long) a->resources.pci.bar[1];
+	a->resources.pci.length[1] = bri_bar_length[1];
+
+	if (diva_os_register_io_port(a, 1, a->resources.pci.bar[2],
+				     bar2_length, &a->port_name[0], 2)) {
+		DBG_ERR(("A: BRI, can't register BAR[2]"))
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+	a->resources.pci.addr[2] = (void *) (unsigned long) a->resources.pci.bar[2];
+	a->resources.pci.length[2] = bar2_length;
+
+	/*
+	   Set all memory areas
+	 */
+	diva_bri_set_addresses(a);
+
+	/*
+	   Get Serial Number
+	 */
+	a->xdi_adapter.serialNo = diva_bri_get_serial_number(a);
+
+	/*
+	   Register I/O ports with correct name now
+	 */
+	if (diva_bri_reregister_io(a)) {
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+
+	/*
+	   Initialize OS dependent objects
+	 */
+	if (diva_os_initialize_spin_lock
+	    (&a->xdi_adapter.isr_spin_lock, "isr")) {
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+	if (diva_os_initialize_spin_lock
+	    (&a->xdi_adapter.data_spin_lock, "data")) {
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+
+	strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasbrid");
+
+	if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr,
+					DIDpcRoutine, &a->xdi_adapter)) {
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+	/*
+	   Do not initialize second DPC - only one thread will be created
+	 */
+	a->xdi_adapter.isr_soft_isr.object = a->xdi_adapter.req_soft_isr.object;
+
+	/*
+	   Create entity table
+	 */
+	a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels;
+	a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info;
+	a->xdi_adapter.e_tbl = diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO));
+	if (!a->xdi_adapter.e_tbl) {
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+	memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO));
+
+	/*
+	   Set up interface
+	 */
+	a->xdi_adapter.a.io = &a->xdi_adapter;
+	a->xdi_adapter.DIRequest = request;
+	a->interface.cleanup_adapter_proc = diva_bri_cleanup_adapter;
+	a->interface.cmd_proc = diva_bri_cmd_card_proc;
+
+	p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+	outpp(p, 0x41);
+	DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+
+	prepare_maestra_functions(&a->xdi_adapter);
+
+	a->dsp_mask = 0x00000003;
+
+	/*
+	   Set IRQ handler
+	 */
+	a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
+	sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA BRI %ld",
+		(long) a->xdi_adapter.serialNo);
+	if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
+				 a->xdi_adapter.irq_info.irq_name)) {
+		diva_bri_cleanup_adapter(a);
+		return (-1);
+	}
+	a->xdi_adapter.irq_info.registered = 1;
+
+	diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
+		      a->resources.pci.irq, a->xdi_adapter.serialNo);
+
+	return (0);
+}
+
+
+static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t * a)
+{
+	int i;
+
+	if (a->xdi_adapter.Initialized) {
+		diva_bri_stop_adapter(a);
+	}
+
+	/*
+	   Remove ISR Handler
+	 */
+	if (a->xdi_adapter.irq_info.registered) {
+		diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
+	}
+	a->xdi_adapter.irq_info.registered = 0;
+
+	if (a->resources.pci.addr[0] && a->resources.pci.bar[0]) {
+		divasa_unmap_pci_bar(a->resources.pci.addr[0]);
+		a->resources.pci.addr[0] = NULL;
+		a->resources.pci.bar[0] = 0;
+	}
+
+	for (i = 1; i < 3; i++) {
+		if (a->resources.pci.addr[i] && a->resources.pci.bar[i]) {
+			diva_os_register_io_port(a, 0,
+						 a->resources.pci.bar[i],
+						 a->resources.pci.
+						 length[i],
+						 &a->port_name[0], i);
+			a->resources.pci.addr[i] = NULL;
+			a->resources.pci.bar[i] = 0;
+		}
+	}
+
+	/*
+	   Free OS objects
+	 */
+	diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr);
+	diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr);
+
+	diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr);
+	a->xdi_adapter.isr_soft_isr.object = NULL;
+
+	diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm");
+	diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm");
+
+	/*
+	   Free memory
+	 */
+	if (a->xdi_adapter.e_tbl) {
+		diva_os_free(0, a->xdi_adapter.e_tbl);
+		a->xdi_adapter.e_tbl = NULL;
+	}
+
+	return (0);
+}
+
+void diva_os_prepare_maestra_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+/*
+**  Get serial number
+*/
+static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t * a)
+{
+	dword serNo = 0;
+	byte __iomem *confIO;
+	word serHi, serLo;
+	word __iomem *confMem;
+
+	confIO = DIVA_OS_MEM_ATTACH_CFG(&a->xdi_adapter);
+	serHi = (word) (inppw(&confIO[0x22]) & 0x0FFF);
+	serLo = (word) (inppw(&confIO[0x26]) & 0x0FFF);
+	serNo = ((dword) serHi << 16) | (dword) serLo;
+	DIVA_OS_MEM_DETACH_CFG(&a->xdi_adapter, confIO);
+
+	if ((serNo == 0) || (serNo == 0xFFFFFFFF)) {
+		DBG_FTL(("W: BRI use BAR[0] to get card serial number"))
+
+		confMem = (word __iomem *)DIVA_OS_MEM_ATTACH_RAM(&a->xdi_adapter);
+		serHi = (word) (READ_WORD(&confMem[0x11]) & 0x0FFF);
+		serLo = (word) (READ_WORD(&confMem[0x13]) & 0x0FFF);
+		serNo = (((dword) serHi) << 16) | ((dword) serLo);
+		DIVA_OS_MEM_DETACH_RAM(&a->xdi_adapter, confMem);
+	}
+
+	DBG_LOG(("Serial Number=%ld", serNo))
+
+	return (serNo);
+}
+
+/*
+**  Unregister I/O and register it with new name,
+**  based on Serial Number
+*/
+static int diva_bri_reregister_io(diva_os_xdi_adapter_t * a)
+{
+	int i;
+
+	for (i = 1; i < 3; i++) {
+		diva_os_register_io_port(a, 0, a->resources.pci.bar[i],
+					 a->resources.pci.length[i],
+					 &a->port_name[0], i);
+		a->resources.pci.addr[i] = NULL;
+	}
+
+	sprintf(a->port_name, "DIVA BRI %ld",
+		(long) a->xdi_adapter.serialNo);
+
+	for (i = 1; i < 3; i++) {
+		if (diva_os_register_io_port(a, 1, a->resources.pci.bar[i],
+					     a->resources.pci.length[i],
+					     &a->port_name[0], i)) {
+			DBG_ERR(("A: failed to reregister BAR[%d]", i))
+			return (-1);
+		}
+		a->resources.pci.addr[i] =
+		    (void *) (unsigned long) a->resources.pci.bar[i];
+	}
+
+	return (0);
+}
+
+/*
+**  Process command from user mode
+*/
+static int
+diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+		       diva_xdi_um_cfg_cmd_t * cmd, int length)
+{
+	int ret = -1;
+
+	if (cmd->adapter != a->controller) {
+		DBG_ERR(("A: pri_cmd, invalid controller=%d != %d",
+			 cmd->adapter, a->controller))
+		return (-1);
+	}
+
+	switch (cmd->command) {
+	case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			*(dword *) a->xdi_mbox.data =
+			    (dword) a->CardOrdinal;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			*(dword *) a->xdi_mbox.data =
+			    (dword) a->xdi_adapter.serialNo;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
+		a->xdi_mbox.data_length = sizeof(dword) * 9;
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			int i;
+			dword *data = (dword *) a->xdi_mbox.data;
+
+			for (i = 0; i < 8; i++) {
+				*data++ = a->resources.pci.bar[i];
+			}
+			*data++ = (dword) a->resources.pci.irq;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_CARD_STATE:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			dword *data = (dword *) a->xdi_mbox.data;
+			if (!a->xdi_adapter.port) {
+				*data = 3;
+			} else if (a->xdi_adapter.trapped) {
+				*data = 2;
+			} else if (a->xdi_adapter.Initialized) {
+				*data = 1;
+			} else {
+				*data = 0;
+			}
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_RESET_ADAPTER:
+		ret = diva_bri_reset_adapter(&a->xdi_adapter);
+		break;
+
+	case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
+		ret = diva_bri_write_sdram_block(&a->xdi_adapter,
+						 cmd->command_data.
+						 write_sdram.offset,
+						 (byte *) & cmd[1],
+						 cmd->command_data.
+						 write_sdram.length);
+		break;
+
+	case DIVA_XDI_UM_CMD_START_ADAPTER:
+		ret = diva_bri_start_adapter(&a->xdi_adapter,
+					     cmd->command_data.start.
+					     offset,
+					     cmd->command_data.start.
+					     features);
+		break;
+
+	case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
+		a->xdi_adapter.features =
+		    cmd->command_data.features.features;
+		a->xdi_adapter.a.protocol_capabilities =
+		    a->xdi_adapter.features;
+		DBG_TRC(
+			("Set raw protocol features (%08x)",
+			 a->xdi_adapter.features)) ret = 0;
+		break;
+
+	case DIVA_XDI_UM_CMD_STOP_ADAPTER:
+		ret = diva_bri_stop_adapter(a);
+		break;
+
+	case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
+		ret = diva_card_read_xlog(a);
+		break;
+
+	default:
+		DBG_ERR(
+			("A: A(%d) invalid cmd=%d", a->controller,
+			 cmd->command))}
+
+	return (ret);
+}
+
+static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter)
+{
+	byte __iomem *addrHi, *addrLo, *ioaddr;
+	dword i;
+	byte __iomem *Port;
+
+	if (!IoAdapter->port) {
+		return (-1);
+	}
+	if (IoAdapter->Initialized) {
+		DBG_ERR(("A: A(%d) can't reset BRI adapter - please stop first",
+			 IoAdapter->ANum)) return (-1);
+	}
+	(*(IoAdapter->rstFnc)) (IoAdapter);
+	diva_os_wait(100);
+	Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+	addrHi = Port +
+	    ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+	addrLo = Port + ADDR;
+	ioaddr = Port + DATA;
+	/*
+	   recover
+	 */
+	outpp(addrHi, (byte) 0);
+	outppw(addrLo, (word) 0);
+	outppw(ioaddr, (word) 0);
+	/*
+	   clear shared memory
+	 */
+	outpp(addrHi,
+	      (byte) (
+		      (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+		       BRI_SHARED_RAM_SIZE) >> 16));
+	outppw(addrLo, 0);
+	for (i = 0; i < 0x8000; outppw(ioaddr, 0), ++i);
+	diva_os_wait(100);
+
+	/*
+	   clear signature
+	 */
+	outpp(addrHi,
+	      (byte) (
+		      (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+		       BRI_SHARED_RAM_SIZE) >> 16));
+	outppw(addrLo, 0x1e);
+	outpp(ioaddr, 0);
+	outpp(ioaddr, 0);
+
+	outpp(addrHi, (byte) 0);
+	outppw(addrLo, (word) 0);
+	outppw(ioaddr, (word) 0);
+
+	DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+
+	/*
+	   Forget all outstanding entities
+	 */
+	IoAdapter->e_count = 0;
+	if (IoAdapter->e_tbl) {
+		memset(IoAdapter->e_tbl, 0x00,
+		       IoAdapter->e_max * sizeof(E_INFO));
+	}
+	IoAdapter->head = 0;
+	IoAdapter->tail = 0;
+	IoAdapter->assign = 0;
+	IoAdapter->trapped = 0;
+
+	memset(&IoAdapter->a.IdTable[0], 0x00,
+	       sizeof(IoAdapter->a.IdTable));
+	memset(&IoAdapter->a.IdTypeTable[0], 0x00,
+	       sizeof(IoAdapter->a.IdTypeTable));
+	memset(&IoAdapter->a.FlowControlIdTable[0], 0x00,
+	       sizeof(IoAdapter->a.FlowControlIdTable));
+	memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00,
+	       sizeof(IoAdapter->a.FlowControlSkipTable));
+	memset(&IoAdapter->a.misc_flags_table[0], 0x00,
+	       sizeof(IoAdapter->a.misc_flags_table));
+	memset(&IoAdapter->a.rx_stream[0], 0x00,
+	       sizeof(IoAdapter->a.rx_stream));
+	memset(&IoAdapter->a.tx_stream[0], 0x00,
+	       sizeof(IoAdapter->a.tx_stream));
+	memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos));
+	memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos));
+
+	return (0);
+}
+
+static int
+diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+			   dword address, const byte * data, dword length)
+{
+	byte __iomem *addrHi, *addrLo, *ioaddr;
+	byte __iomem *Port;
+
+	if (!IoAdapter->port) {
+		return (-1);
+	}
+
+	Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+	addrHi = Port +
+	    ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+	addrLo = Port + ADDR;
+	ioaddr = Port + DATA;
+
+	while (length--) {
+		outpp(addrHi, (word) (address >> 16));
+		outppw(addrLo, (word) (address & 0x0000ffff));
+		outpp(ioaddr, *data++);
+		address++;
+	}
+
+	DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+	return (0);
+}
+
+static int
+diva_bri_start_adapter(PISDN_ADAPTER IoAdapter,
+		       dword start_address, dword features)
+{
+	byte __iomem *Port;
+	dword i, test;
+	byte __iomem *addrHi, *addrLo, *ioaddr;
+	int started = 0;
+	ADAPTER *a = &IoAdapter->a;
+
+	if (IoAdapter->Initialized) {
+		DBG_ERR(
+			("A: A(%d) bri_start_adapter, adapter already running",
+			 IoAdapter->ANum)) return (-1);
+	}
+	if (!IoAdapter->port) {
+		DBG_ERR(("A: A(%d) bri_start_adapter, adapter not mapped",
+			 IoAdapter->ANum)) return (-1);
+	}
+
+	sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum);
+	DBG_LOG(("A(%d) start BRI", IoAdapter->ANum))
+
+	Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+	addrHi = Port +
+	    ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+	addrLo = Port + ADDR;
+	ioaddr = Port + DATA;
+
+	outpp(addrHi,
+	      (byte) (
+		      (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+		       BRI_SHARED_RAM_SIZE) >> 16));
+	outppw(addrLo, 0x1e);
+	outppw(ioaddr, 0x00);
+	DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+
+	/*
+	   start the protocol code
+	 */
+	Port = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	outpp(Port, 0x08);
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, Port);
+
+	Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+	addrHi = Port +
+	    ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+	addrLo = Port + ADDR;
+	ioaddr = Port + DATA;
+	/*
+	   wait for signature (max. 3 seconds)
+	 */
+	for (i = 0; i < 300; ++i) {
+		diva_os_wait(10);
+		outpp(addrHi,
+		      (byte) (
+			      (IoAdapter->MemoryBase +
+			       IoAdapter->MemorySize -
+			       BRI_SHARED_RAM_SIZE) >> 16));
+		outppw(addrLo, 0x1e);
+		test = (dword) inppw(ioaddr);
+		if (test == 0x4447) {
+			DBG_LOG(
+				("Protocol startup time %d.%02d seconds",
+				 (i / 100), (i % 100)))
+			started = 1;
+			break;
+		}
+	}
+	DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+
+	if (!started) {
+		DBG_FTL(("A: A(%d) %s: Adapter selftest failed 0x%04X",
+			 IoAdapter->ANum, IoAdapter->Properties.Name,
+			 test))
+		(*(IoAdapter->trapFnc)) (IoAdapter);
+		return (-1);
+	}
+
+	IoAdapter->Initialized = 1;
+
+	/*
+	   Check Interrupt
+	 */
+	IoAdapter->IrqCount = 0;
+	a->ReadyInt = 1;
+
+	if (IoAdapter->reset) {
+		Port = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+		outpp(Port, 0x41);
+		DIVA_OS_MEM_DETACH_RESET(IoAdapter, Port);
+	}
+
+	a->ram_out(a, &PR_RAM->ReadyInt, 1);
+	for (i = 0; ((!IoAdapter->IrqCount) && (i < 100)); i++) {
+		diva_os_wait(10);
+	}
+	if (!IoAdapter->IrqCount) {
+		DBG_ERR(
+			("A: A(%d) interrupt test failed",
+			 IoAdapter->ANum))
+		IoAdapter->Initialized = 0;
+		IoAdapter->stop(IoAdapter);
+		return (-1);
+	}
+
+	IoAdapter->Properties.Features = (word) features;
+	diva_xdi_display_adapter_features(IoAdapter->ANum);
+	DBG_LOG(("A(%d) BRI adapter successfull started", IoAdapter->ANum))
+	    /*
+	       Register with DIDD
+	     */
+	diva_xdi_didd_register_adapter(IoAdapter->ANum);
+
+	return (0);
+}
+
+static void diva_bri_clear_interrupts(diva_os_xdi_adapter_t * a)
+{
+	PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+
+	/*
+	   clear any pending interrupt
+	 */
+	IoAdapter->disIrq(IoAdapter);
+
+	IoAdapter->tst_irq(&IoAdapter->a);
+	IoAdapter->clr_irq(&IoAdapter->a);
+	IoAdapter->tst_irq(&IoAdapter->a);
+
+	/*
+	   kill pending dpcs
+	 */
+	diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
+	diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
+}
+
+/*
+**  Stop card
+*/
+static int diva_bri_stop_adapter(diva_os_xdi_adapter_t * a)
+{
+	PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+	int i = 100;
+
+	if (!IoAdapter->port) {
+		return (-1);
+	}
+	if (!IoAdapter->Initialized) {
+		DBG_ERR(("A: A(%d) can't stop BRI adapter - not running",
+			 IoAdapter->ANum))
+		return (-1);	/* nothing to stop */
+	}
+	IoAdapter->Initialized = 0;
+
+	/*
+	   Disconnect Adapter from DIDD
+	 */
+	diva_xdi_didd_remove_adapter(IoAdapter->ANum);
+
+	/*
+	   Stop interrupts
+	 */
+	a->clear_interrupts_proc = diva_bri_clear_interrupts;
+	IoAdapter->a.ReadyInt = 1;
+	IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
+	do {
+		diva_os_sleep(10);
+	} while (i-- && a->clear_interrupts_proc);
+	if (a->clear_interrupts_proc) {
+		diva_bri_clear_interrupts(a);
+		a->clear_interrupts_proc = NULL;
+		DBG_ERR(("A: A(%d) no final interrupt from BRI adapter",
+			 IoAdapter->ANum))
+	}
+	IoAdapter->a.ReadyInt = 0;
+
+	/*
+	   Stop and reset adapter
+	 */
+	IoAdapter->stop(IoAdapter);
+
+	return (0);
+}
diff --git a/drivers/isdn/hardware/eicon/os_bri.h b/drivers/isdn/hardware/eicon/os_bri.h
new file mode 100644
index 0000000..a54f0ce
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_bri.h
@@ -0,0 +1,8 @@
+/* $Id: os_bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
+
+#ifndef __DIVA_OS_BRI_REV_1_H__
+#define __DIVA_OS_BRI_REV_1_H__
+
+int diva_bri_init_card(diva_os_xdi_adapter_t * a);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/os_capi.h b/drivers/isdn/hardware/eicon/os_capi.h
new file mode 100644
index 0000000..726f915
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_capi.h
@@ -0,0 +1,21 @@
+/* $Id: os_capi.h,v 1.7 2003/04/12 21:40:49 schindler Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface OS include files 
+ * 
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de) 
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef __OS_CAPI_H__ 
+#define __OS_CAPI_H__
+
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+
+#endif /* __OS_CAPI_H__ */
diff --git a/drivers/isdn/hardware/eicon/os_pri.c b/drivers/isdn/hardware/eicon/os_pri.c
new file mode 100644
index 0000000..8ac207f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_pri.c
@@ -0,0 +1,1051 @@
+/* $Id: os_pri.c,v 1.32 2004/03/21 17:26:01 armin Exp $ */
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "dsp_defs.h"
+#include "di.h"
+#include "io.h"
+
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "os_pri.h"
+#include "diva_pci.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "dsp_tst.h"
+#include "diva_dma.h"
+
+/* --------------------------------------------------------------------------
+   OS Dependent part of XDI driver for DIVA PRI Adapter
+
+   DSP detection/validation by Anthony Booth (Eicon Networks, www.eicon.com)
+-------------------------------------------------------------------------- */
+
+#define DIVA_PRI_NO_PCI_BIOS_WORKAROUND 1
+
+extern int diva_card_read_xlog(diva_os_xdi_adapter_t * a);
+
+/*
+**  IMPORTS
+*/
+extern void prepare_pri_functions(PISDN_ADAPTER IoAdapter);
+extern void prepare_pri2_functions(PISDN_ADAPTER IoAdapter);
+extern void diva_xdi_display_adapter_features(int card);
+
+static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t * a);
+static int diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+				  diva_xdi_um_cfg_cmd_t * cmd, int length);
+static int pri_get_serial_number(diva_os_xdi_adapter_t * a);
+static int diva_pri_stop_adapter(diva_os_xdi_adapter_t * a);
+static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t * a);
+
+/*
+**  Check card revision
+*/
+static int pri_is_rev_2_card(int card_ordinal)
+{
+	switch (card_ordinal) {
+	case CARDTYPE_DIVASRV_P_30M_V2_PCI:
+	case CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI:
+		return (1);
+	}
+	return (0);
+}
+
+static void diva_pri_set_addresses(diva_os_xdi_adapter_t * a)
+{
+	a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 0;
+	a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2;
+	a->resources.pci.mem_type_id[MEM_TYPE_CONFIG] = 4;
+	a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0;
+	a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 2;
+	a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 4;
+	a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 3;
+	
+	a->xdi_adapter.Address = a->resources.pci.addr[0];
+	a->xdi_adapter.Control = a->resources.pci.addr[2];
+	a->xdi_adapter.Config = a->resources.pci.addr[4];
+
+	a->xdi_adapter.ram = a->resources.pci.addr[0];
+	a->xdi_adapter.ram += MP_SHARED_RAM_OFFSET;
+
+	a->xdi_adapter.reset = a->resources.pci.addr[2];
+	a->xdi_adapter.reset += MP_RESET;
+
+	a->xdi_adapter.cfg = a->resources.pci.addr[4];
+	a->xdi_adapter.cfg += MP_IRQ_RESET;
+
+	a->xdi_adapter.sdram_bar = a->resources.pci.bar[0];
+
+	a->xdi_adapter.prom = a->resources.pci.addr[3];
+}
+
+/*
+**  BAR0 - SDRAM, MP_MEMORY_SIZE, MP2_MEMORY_SIZE by Rev.2
+**  BAR1 - DEVICES,				0x1000
+**  BAR2 - CONTROL (REG), 0x2000
+**  BAR3 - FLASH (REG),		0x8000
+**  BAR4 - CONFIG (CFG),	0x1000
+*/
+int diva_pri_init_card(diva_os_xdi_adapter_t * a)
+{
+	int bar = 0;
+	int pri_rev_2;
+	unsigned long bar_length[5] = {
+		MP_MEMORY_SIZE,
+		0x1000,
+		0x2000,
+		0x8000,
+		0x1000
+	};
+
+	pri_rev_2 = pri_is_rev_2_card(a->CardOrdinal);
+
+	if (pri_rev_2) {
+		bar_length[0] = MP2_MEMORY_SIZE;
+	}
+	/*
+	   Set properties
+	 */
+	a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
+	DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name))
+
+	/*
+	   First initialization step: get and check hardware resoures.
+	   Do not map resources and do not acecess card at this step
+	 */
+	for (bar = 0; bar < 5; bar++) {
+		a->resources.pci.bar[bar] =
+		    divasa_get_pci_bar(a->resources.pci.bus,
+				       a->resources.pci.func, bar,
+				       a->resources.pci.hdev);
+		if (!a->resources.pci.bar[bar]
+		    || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) {
+			DBG_ERR(("A: invalid bar[%d]=%08x", bar,
+				 a->resources.pci.bar[bar]))
+			return (-1);
+		}
+	}
+	a->resources.pci.irq =
+	    (byte) divasa_get_pci_irq(a->resources.pci.bus,
+				      a->resources.pci.func,
+				      a->resources.pci.hdev);
+	if (!a->resources.pci.irq) {
+		DBG_ERR(("A: invalid irq"));
+		return (-1);
+	}
+
+	/*
+	   Map all BAR's
+	 */
+	for (bar = 0; bar < 5; bar++) {
+		a->resources.pci.addr[bar] =
+		    divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar],
+					 bar_length[bar]);
+		if (!a->resources.pci.addr[bar]) {
+			DBG_ERR(("A: A(%d), can't map bar[%d]",
+				 a->controller, bar))
+			diva_pri_cleanup_adapter(a);
+			return (-1);
+		}
+	}
+
+	/*
+	   Set all memory areas
+	 */
+	diva_pri_set_addresses(a);
+
+	/*
+	   Get Serial Number of this adapter
+	 */
+	if (pri_get_serial_number(a)) {
+		dword serNo;
+		serNo = a->resources.pci.bar[1] & 0xffff0000;
+		serNo |= ((dword) a->resources.pci.bus) << 8;
+		serNo += (a->resources.pci.func + a->controller + 1);
+		a->xdi_adapter.serialNo = serNo & ~0xFF000000;
+		DBG_ERR(("A: A(%d) can't get Serial Number, generated serNo=%ld",
+			 a->controller, a->xdi_adapter.serialNo))
+	}
+
+
+	/*
+	   Initialize os objects
+	 */
+	if (diva_os_initialize_spin_lock(&a->xdi_adapter.isr_spin_lock, "isr")) {
+		diva_pri_cleanup_adapter(a);
+		return (-1);
+	}
+	if (diva_os_initialize_spin_lock
+	    (&a->xdi_adapter.data_spin_lock, "data")) {
+		diva_pri_cleanup_adapter(a);
+		return (-1);
+	}
+
+	strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasprid");
+
+	if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr,
+					DIDpcRoutine, &a->xdi_adapter)) {
+		diva_pri_cleanup_adapter(a);
+		return (-1);
+	}
+
+	/*
+	   Do not initialize second DPC - only one thread will be created
+	 */
+	a->xdi_adapter.isr_soft_isr.object =
+	    a->xdi_adapter.req_soft_isr.object;
+
+	/*
+	   Next step of card initialization:
+	   set up all interface pointers
+	 */
+	a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels;
+	a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info;
+
+	a->xdi_adapter.e_tbl =
+	    diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO));
+	if (!a->xdi_adapter.e_tbl) {
+		diva_pri_cleanup_adapter(a);
+		return (-1);
+	}
+	memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO));
+
+	a->xdi_adapter.a.io = &a->xdi_adapter;
+	a->xdi_adapter.DIRequest = request;
+	a->interface.cleanup_adapter_proc = diva_pri_cleanup_adapter;
+	a->interface.cmd_proc = diva_pri_cmd_card_proc;
+
+	if (pri_rev_2) {
+		prepare_pri2_functions(&a->xdi_adapter);
+	} else {
+		prepare_pri_functions(&a->xdi_adapter);
+	}
+
+	a->dsp_mask = diva_pri_detect_dsps(a);
+
+	/*
+	   Allocate DMA map
+	 */
+	if (pri_rev_2) {
+		diva_init_dma_map(a->resources.pci.hdev,
+				  (struct _diva_dma_map_entry **) &a->xdi_adapter.dma_map, 32);
+	}
+
+	/*
+	   Set IRQ handler
+	 */
+	a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
+	sprintf(a->xdi_adapter.irq_info.irq_name,
+		"DIVA PRI %ld", (long) a->xdi_adapter.serialNo);
+
+	if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
+				 a->xdi_adapter.irq_info.irq_name)) {
+		diva_pri_cleanup_adapter(a);
+		return (-1);
+	}
+	a->xdi_adapter.irq_info.registered = 1;
+
+	diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
+		      a->resources.pci.irq, a->xdi_adapter.serialNo);
+
+	return (0);
+}
+
+static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t * a)
+{
+	int bar = 0;
+
+	/*
+	   Stop Adapter if adapter is running
+	 */
+	if (a->xdi_adapter.Initialized) {
+		diva_pri_stop_adapter(a);
+	}
+
+	/*
+	   Remove ISR Handler
+	 */
+	if (a->xdi_adapter.irq_info.registered) {
+		diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
+	}
+	a->xdi_adapter.irq_info.registered = 0;
+
+	/*
+	   Step 1: unmap all BAR's, if any was mapped
+	 */
+	for (bar = 0; bar < 5; bar++) {
+		if (a->resources.pci.bar[bar]
+		    && a->resources.pci.addr[bar]) {
+			divasa_unmap_pci_bar(a->resources.pci.addr[bar]);
+			a->resources.pci.bar[bar] = 0;
+			a->resources.pci.addr[bar] = NULL;
+		}
+	}
+
+	/*
+	   Free OS objects
+	 */
+	diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr);
+	diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr);
+
+	diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr);
+	a->xdi_adapter.isr_soft_isr.object = NULL;
+
+	diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm");
+	diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm");
+
+	/*
+	   Free memory accupied by XDI adapter
+	 */
+	if (a->xdi_adapter.e_tbl) {
+		diva_os_free(0, a->xdi_adapter.e_tbl);
+		a->xdi_adapter.e_tbl = NULL;
+	}
+	a->xdi_adapter.Channels = 0;
+	a->xdi_adapter.e_max = 0;
+
+
+	/*
+	   Free adapter DMA map
+	 */
+	diva_free_dma_map(a->resources.pci.hdev,
+			  (struct _diva_dma_map_entry *) a->xdi_adapter.
+			  dma_map);
+	a->xdi_adapter.dma_map = NULL;
+
+
+	/*
+	   Detach this adapter from debug driver
+	 */
+
+	return (0);
+}
+
+/*
+**  Activate On Board Boot Loader
+*/
+static int diva_pri_reset_adapter(PISDN_ADAPTER IoAdapter)
+{
+	dword i;
+	struct mp_load __iomem *boot;
+
+	if (!IoAdapter->Address || !IoAdapter->reset) {
+		return (-1);
+	}
+	if (IoAdapter->Initialized) {
+		DBG_ERR(("A: A(%d) can't reset PRI adapter - please stop first",
+			 IoAdapter->ANum))
+		return (-1);
+	}
+
+	boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+	WRITE_DWORD(&boot->err, 0);
+	DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+
+	IoAdapter->rstFnc(IoAdapter);
+
+	diva_os_wait(10);
+
+	boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+	i = READ_DWORD(&boot->live);
+
+	diva_os_wait(10);
+	if (i == READ_DWORD(&boot->live)) {
+		DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+		DBG_ERR(("A: A(%d) CPU on PRI %ld is not alive!",
+			 IoAdapter->ANum, IoAdapter->serialNo))
+		return (-1);
+	}
+	if (READ_DWORD(&boot->err)) {
+		DBG_ERR(("A: A(%d) PRI %ld Board Selftest failed, error=%08lx",
+			 IoAdapter->ANum, IoAdapter->serialNo,
+			 READ_DWORD(&boot->err)))
+		DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+		return (-1);
+	}
+	DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+
+	/*
+	   Forget all outstanding entities
+	 */
+	IoAdapter->e_count = 0;
+	if (IoAdapter->e_tbl) {
+		memset(IoAdapter->e_tbl, 0x00,
+		       IoAdapter->e_max * sizeof(E_INFO));
+	}
+	IoAdapter->head = 0;
+	IoAdapter->tail = 0;
+	IoAdapter->assign = 0;
+	IoAdapter->trapped = 0;
+
+	memset(&IoAdapter->a.IdTable[0], 0x00,
+	       sizeof(IoAdapter->a.IdTable));
+	memset(&IoAdapter->a.IdTypeTable[0], 0x00,
+	       sizeof(IoAdapter->a.IdTypeTable));
+	memset(&IoAdapter->a.FlowControlIdTable[0], 0x00,
+	       sizeof(IoAdapter->a.FlowControlIdTable));
+	memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00,
+	       sizeof(IoAdapter->a.FlowControlSkipTable));
+	memset(&IoAdapter->a.misc_flags_table[0], 0x00,
+	       sizeof(IoAdapter->a.misc_flags_table));
+	memset(&IoAdapter->a.rx_stream[0], 0x00,
+	       sizeof(IoAdapter->a.rx_stream));
+	memset(&IoAdapter->a.tx_stream[0], 0x00,
+	       sizeof(IoAdapter->a.tx_stream));
+	memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos));
+	memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos));
+
+	return (0);
+}
+
+static int
+diva_pri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+			   dword address,
+			   const byte * data, dword length, dword limit)
+{
+	byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+	byte __iomem *mem = p;
+
+	if (((address + length) >= limit) || !mem) {
+		DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+		DBG_ERR(("A: A(%d) write PRI address=0x%08lx",
+			 IoAdapter->ANum, address + length))
+		return (-1);
+	}
+	mem += address;
+
+	/* memcpy_toio(), maybe? */
+	while (length--) {
+		WRITE_BYTE(mem++, *data++);
+	}
+
+	DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+	return (0);
+}
+
+static int
+diva_pri_start_adapter(PISDN_ADAPTER IoAdapter,
+		       dword start_address, dword features)
+{
+	dword i;
+	int started = 0;
+	byte __iomem *p;
+	struct mp_load __iomem *boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+	ADAPTER *a = &IoAdapter->a;
+
+	if (IoAdapter->Initialized) {
+		DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+		DBG_ERR(("A: A(%d) pri_start_adapter, adapter already running",
+			 IoAdapter->ANum))
+		return (-1);
+	}
+	if (!boot) {
+		DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+		DBG_ERR(("A: PRI %ld can't start, adapter not mapped",
+			 IoAdapter->serialNo))
+		return (-1);
+	}
+
+	sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum);
+	DBG_LOG(("A(%d) start PRI at 0x%08lx", IoAdapter->ANum,
+		 start_address))
+
+	WRITE_DWORD(&boot->addr, start_address);
+	WRITE_DWORD(&boot->cmd, 3);
+
+	for (i = 0; i < 300; ++i) {
+		diva_os_wait(10);
+		if ((READ_DWORD(&boot->signature) >> 16) == 0x4447) {
+			DBG_LOG(("A(%d) Protocol startup time %d.%02d seconds",
+				 IoAdapter->ANum, (i / 100), (i % 100)))
+			started = 1;
+			break;
+		}
+	}
+
+	if (!started) {
+		byte __iomem *p = (byte __iomem *)boot;
+		dword TrapId;
+		dword debug;
+		TrapId = READ_DWORD(&p[0x80]);
+		debug = READ_DWORD(&p[0x1c]);
+		DBG_ERR(("A(%d) Adapter start failed 0x%08lx, TrapId=%08lx, debug=%08lx",
+			 IoAdapter->ANum, READ_DWORD(&boot->signature),
+			 TrapId, debug))
+		DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+		if (IoAdapter->trapFnc) {
+			(*(IoAdapter->trapFnc)) (IoAdapter);
+		}
+		IoAdapter->stop(IoAdapter);
+		return (-1);
+	}
+	DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+
+	IoAdapter->Initialized = TRUE;
+
+	/*
+	   Check Interrupt
+	 */
+	IoAdapter->IrqCount = 0;
+	p = DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+	WRITE_DWORD(p, (dword) ~ 0x03E00000);
+	DIVA_OS_MEM_DETACH_CFG(IoAdapter, p);
+	a->ReadyInt = 1;
+	a->ram_out(a, &PR_RAM->ReadyInt, 1);
+
+	for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10));
+
+	if (!IoAdapter->IrqCount) {
+		DBG_ERR(("A: A(%d) interrupt test failed",
+			 IoAdapter->ANum))
+		IoAdapter->Initialized = FALSE;
+		IoAdapter->stop(IoAdapter);
+		return (-1);
+	}
+
+	IoAdapter->Properties.Features = (word) features;
+
+	diva_xdi_display_adapter_features(IoAdapter->ANum);
+
+	DBG_LOG(("A(%d) PRI adapter successfull started", IoAdapter->ANum))
+	/*
+	   Register with DIDD
+	 */
+	diva_xdi_didd_register_adapter(IoAdapter->ANum);
+
+	return (0);
+}
+
+static void diva_pri_clear_interrupts(diva_os_xdi_adapter_t * a)
+{
+	PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+
+	/*
+	   clear any pending interrupt
+	 */
+	IoAdapter->disIrq(IoAdapter);
+
+	IoAdapter->tst_irq(&IoAdapter->a);
+	IoAdapter->clr_irq(&IoAdapter->a);
+	IoAdapter->tst_irq(&IoAdapter->a);
+
+	/*
+	   kill pending dpcs
+	 */
+	diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
+	diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
+}
+
+/*
+**  Stop Adapter, but do not unmap/unregister - adapter
+**  will be restarted later
+*/
+static int diva_pri_stop_adapter(diva_os_xdi_adapter_t * a)
+{
+	PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+	int i = 100;
+
+	if (!IoAdapter->ram) {
+		return (-1);
+	}
+	if (!IoAdapter->Initialized) {
+		DBG_ERR(("A: A(%d) can't stop PRI adapter - not running",
+			 IoAdapter->ANum))
+		return (-1);	/* nothing to stop */
+	}
+	IoAdapter->Initialized = 0;
+
+	/*
+	   Disconnect Adapter from DIDD
+	 */
+	diva_xdi_didd_remove_adapter(IoAdapter->ANum);
+
+	/*
+	   Stop interrupts
+	 */
+	a->clear_interrupts_proc = diva_pri_clear_interrupts;
+	IoAdapter->a.ReadyInt = 1;
+	IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
+	do {
+		diva_os_sleep(10);
+	} while (i-- && a->clear_interrupts_proc);
+
+	if (a->clear_interrupts_proc) {
+		diva_pri_clear_interrupts(a);
+		a->clear_interrupts_proc = NULL;
+		DBG_ERR(("A: A(%d) no final interrupt from PRI adapter",
+			 IoAdapter->ANum))
+	}
+	IoAdapter->a.ReadyInt = 0;
+
+	/*
+	   Stop and reset adapter
+	 */
+	IoAdapter->stop(IoAdapter);
+
+	return (0);
+}
+
+/*
+**  Process commands form configuration/download framework and from
+**  user mode
+**
+**  return 0 on success
+*/
+static int
+diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+		       diva_xdi_um_cfg_cmd_t * cmd, int length)
+{
+	int ret = -1;
+
+	if (cmd->adapter != a->controller) {
+		DBG_ERR(("A: pri_cmd, invalid controller=%d != %d",
+			 cmd->adapter, a->controller))
+		return (-1);
+	}
+
+	switch (cmd->command) {
+	case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			*(dword *) a->xdi_mbox.data =
+			    (dword) a->CardOrdinal;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			*(dword *) a->xdi_mbox.data =
+			    (dword) a->xdi_adapter.serialNo;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
+		a->xdi_mbox.data_length = sizeof(dword) * 9;
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			int i;
+			dword *data = (dword *) a->xdi_mbox.data;
+
+			for (i = 0; i < 8; i++) {
+				*data++ = a->resources.pci.bar[i];
+			}
+			*data++ = (dword) a->resources.pci.irq;
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_RESET_ADAPTER:
+		ret = diva_pri_reset_adapter(&a->xdi_adapter);
+		break;
+
+	case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
+		ret = diva_pri_write_sdram_block(&a->xdi_adapter,
+						 cmd->command_data.
+						 write_sdram.offset,
+						 (byte *) & cmd[1],
+						 cmd->command_data.
+						 write_sdram.length,
+						 pri_is_rev_2_card(a->
+								   CardOrdinal)
+						 ? MP2_MEMORY_SIZE :
+						 MP_MEMORY_SIZE);
+		break;
+
+	case DIVA_XDI_UM_CMD_STOP_ADAPTER:
+		ret = diva_pri_stop_adapter(a);
+		break;
+
+	case DIVA_XDI_UM_CMD_START_ADAPTER:
+		ret = diva_pri_start_adapter(&a->xdi_adapter,
+					     cmd->command_data.start.
+					     offset,
+					     cmd->command_data.start.
+					     features);
+		break;
+
+	case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
+		a->xdi_adapter.features =
+		    cmd->command_data.features.features;
+		a->xdi_adapter.a.protocol_capabilities =
+		    a->xdi_adapter.features;
+		DBG_TRC(("Set raw protocol features (%08x)",
+			 a->xdi_adapter.features))
+		ret = 0;
+		break;
+
+	case DIVA_XDI_UM_CMD_GET_CARD_STATE:
+		a->xdi_mbox.data_length = sizeof(dword);
+		a->xdi_mbox.data =
+		    diva_os_malloc(0, a->xdi_mbox.data_length);
+		if (a->xdi_mbox.data) {
+			dword *data = (dword *) a->xdi_mbox.data;
+			if (!a->xdi_adapter.ram ||
+				!a->xdi_adapter.reset ||
+			    !a->xdi_adapter.cfg) {
+				*data = 3;
+			} else if (a->xdi_adapter.trapped) {
+				*data = 2;
+			} else if (a->xdi_adapter.Initialized) {
+				*data = 1;
+			} else {
+				*data = 0;
+			}
+			a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+			ret = 0;
+		}
+		break;
+
+	case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
+		ret = diva_card_read_xlog(a);
+		break;
+
+	case DIVA_XDI_UM_CMD_READ_SDRAM:
+		if (a->xdi_adapter.Address) {
+			if (
+			    (a->xdi_mbox.data_length =
+			     cmd->command_data.read_sdram.length)) {
+				if (
+				    (a->xdi_mbox.data_length +
+				     cmd->command_data.read_sdram.offset) <
+				    a->xdi_adapter.MemorySize) {
+					a->xdi_mbox.data =
+					    diva_os_malloc(0,
+							   a->xdi_mbox.
+							   data_length);
+					if (a->xdi_mbox.data) {
+						byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter);
+						byte __iomem *src = p;
+						byte *dst = a->xdi_mbox.data;
+						dword len = a->xdi_mbox.data_length;
+
+						src += cmd->command_data.read_sdram.offset;
+
+						while (len--) {
+							*dst++ = READ_BYTE(src++);
+						}
+						a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+						DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p);
+						ret = 0;
+					}
+				}
+			}
+		}
+		break;
+
+	default:
+		DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller,
+			 cmd->command))
+	}
+
+	return (ret);
+}
+
+/*
+**  Get Serial Number
+*/
+static int pri_get_serial_number(diva_os_xdi_adapter_t * a)
+{
+	byte data[64];
+	int i;
+	dword len = sizeof(data);
+	volatile byte __iomem *config;
+	volatile byte __iomem *flash;
+	byte c;
+
+/*
+ *  First set some GT6401x config registers before accessing the BOOT-ROM
+ */
+ 	config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+	c = READ_BYTE(&config[0xc3c]);
+	if (!(c & 0x08)) {
+		WRITE_BYTE(&config[0xc3c], c);	/* Base Address enable register */
+	}
+	WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0x00);
+	WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF);
+ 	DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+/*
+ *  Read only the last 64 bytes of manufacturing data
+ */
+	memset(data, '\0', len);
+ 	flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter);
+	for (i = 0; i < len; i++) {
+		data[i] = READ_BYTE(&flash[0x8000 - len + i]);
+	}
+ 	DIVA_OS_MEM_DETACH_PROM(&a->xdi_adapter, flash);
+
+ 	config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+	WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0xFC);	/* Disable FLASH EPROM access */
+	WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF);
+ 	DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+
+	if (memcmp(&data[48], "DIVAserverPR", 12)) {
+#if !defined(DIVA_PRI_NO_PCI_BIOS_WORKAROUND)	/* { */
+		word cmd = 0, cmd_org;
+		void *addr;
+		dword addr1, addr3, addr4;
+		byte Bus, Slot;
+		void *hdev;
+		addr4 = a->resources.pci.bar[4];
+		addr3 = a->resources.pci.bar[3];	/* flash  */
+		addr1 = a->resources.pci.bar[1];	/* unused */
+
+		DBG_ERR(("A: apply Compaq BIOS workaround"))
+		DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+			     data[0], data[1], data[2], data[3],
+			     data[4], data[5], data[6], data[7]))
+
+		Bus = a->resources.pci.bus;
+		Slot = a->resources.pci.func;
+		hdev = a->resources.pci.hdev;
+		PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+		PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev);
+
+		PCIwrite(Bus, Slot, 0x14, &addr4, sizeof(addr4), hdev);
+		PCIwrite(Bus, Slot, 0x20, &addr1, sizeof(addr1), hdev);
+
+		PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+
+		addr = a->resources.pci.addr[1];
+		a->resources.pci.addr[1] = a->resources.pci.addr[4];
+		a->resources.pci.addr[4] = addr;
+
+		addr1 = a->resources.pci.bar[1];
+		a->resources.pci.bar[1] = a->resources.pci.bar[4];
+		a->resources.pci.bar[4] = addr1;
+
+		/*
+		   Try to read Flash again
+		 */
+		len = sizeof(data);
+
+ 		config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+		if (!(config[0xc3c] & 0x08)) {
+			config[0xc3c] |= 0x08;	/* Base Address enable register */
+		}
+		config[LOW_BOOTCS_DREG] = 0x00;
+		config[HI_BOOTCS_DREG] = 0xFF;
+ 		DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+
+		memset(data, '\0', len);
+ 		flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter);
+		for (i = 0; i < len; i++) {
+			data[i] = flash[0x8000 - len + i];
+		}
+ 		DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter, flash);
+ 		config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+		config[LOW_BOOTCS_DREG] = 0xFC;
+		config[HI_BOOTCS_DREG] = 0xFF;
+ 		DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+
+		if (memcmp(&data[48], "DIVAserverPR", 12)) {
+			DBG_ERR(("A: failed to read serial number"))
+			DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+				     data[0], data[1], data[2], data[3],
+				     data[4], data[5], data[6], data[7]))
+			return (-1);
+		}
+#else				/* } { */
+		DBG_ERR(("A: failed to read DIVA signature word"))
+		DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+			     data[0], data[1], data[2], data[3],
+			     data[4], data[5], data[6], data[7]))
+		DBG_LOG(("%02x:%02x:%02x:%02x", data[47], data[46],
+			     data[45], data[44]))
+#endif				/* } */
+	}
+
+	a->xdi_adapter.serialNo =
+	    (data[47] << 24) | (data[46] << 16) | (data[45] << 8) |
+	    data[44];
+	if (!a->xdi_adapter.serialNo
+	    || (a->xdi_adapter.serialNo == 0xffffffff)) {
+		a->xdi_adapter.serialNo = 0;
+		DBG_ERR(("A: failed to read serial number"))
+		return (-1);
+	}
+
+	DBG_LOG(("Serial No.          : %ld", a->xdi_adapter.serialNo))
+	DBG_TRC(("Board Revision      : %d.%02d", (int) data[41],
+		     (int) data[40]))
+	DBG_TRC(("PLD revision        : %d.%02d", (int) data[33],
+		     (int) data[32]))
+	DBG_TRC(("Boot loader version : %d.%02d", (int) data[37],
+		     (int) data[36]))
+
+	DBG_TRC(("Manufacturing Date  : %d/%02d/%02d  (yyyy/mm/dd)",
+		     (int) ((data[28] > 90) ? 1900 : 2000) +
+		     (int) data[28], (int) data[29], (int) data[30]))
+
+	return (0);
+}
+
+void diva_os_prepare_pri2_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+void diva_os_prepare_pri_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+/*
+**  Checks presence of DSP on board
+*/
+static int
+dsp_check_presence(volatile byte __iomem * addr, volatile byte __iomem * data, int dsp)
+{
+	word pattern;
+
+	WRITE_WORD(addr, 0x4000);
+	WRITE_WORD(data, DSP_SIGNATURE_PROBE_WORD);
+
+	WRITE_WORD(addr, 0x4000);
+	pattern = READ_WORD(data);
+
+	if (pattern != DSP_SIGNATURE_PROBE_WORD) {
+		DBG_TRC(("W: DSP[%d] %04x(is) != %04x(should)",
+			 dsp, pattern, DSP_SIGNATURE_PROBE_WORD))
+		return (-1);
+	}
+
+	WRITE_WORD(addr, 0x4000);
+	WRITE_WORD(data, ~DSP_SIGNATURE_PROBE_WORD);
+
+	WRITE_WORD(addr, 0x4000);
+	pattern = READ_WORD(data);
+
+	if (pattern != (word) ~ DSP_SIGNATURE_PROBE_WORD) {
+		DBG_ERR(("A: DSP[%d] %04x(is) != %04x(should)",
+			 dsp, pattern, (word) ~ DSP_SIGNATURE_PROBE_WORD))
+		return (-2);
+	}
+
+	DBG_TRC(("DSP[%d] present", dsp))
+
+	return (0);
+}
+
+
+/*
+**  Check if DSP's are present and operating
+**  Information about detected DSP's is returned as bit mask
+**  Bit 0  - DSP1
+**  ...
+**  ...
+**  ...
+**  Bit 29 - DSP30
+*/
+static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t * a)
+{
+	byte __iomem *base;
+	byte __iomem *p;
+	dword ret = 0;
+	dword row_offset[7] = {
+		0x00000000,
+		0x00000800,	/* 1 - ROW 1 */
+		0x00000840,	/* 2 - ROW 2 */
+		0x00001000,	/* 3 - ROW 3 */
+		0x00001040,	/* 4 - ROW 4 */
+		0x00000000	/* 5 - ROW 0 */
+	};
+
+	byte __iomem *dsp_addr_port;
+	byte __iomem *dsp_data_port;
+	byte row_state;
+	int dsp_row = 0, dsp_index, dsp_num;
+
+	if (!a->xdi_adapter.Control || !a->xdi_adapter.reset) {
+		return (0);
+	}
+
+	p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+	WRITE_BYTE(p, _MP_RISC_RESET | _MP_DSP_RESET);
+	DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+	diva_os_wait(5);
+
+	base = DIVA_OS_MEM_ATTACH_CONTROL(&a->xdi_adapter);
+
+	for (dsp_num = 0; dsp_num < 30; dsp_num++) {
+		dsp_row = dsp_num / 7 + 1;
+		dsp_index = dsp_num % 7;
+
+		dsp_data_port = base;
+		dsp_addr_port = base;
+
+		dsp_data_port += row_offset[dsp_row];
+		dsp_addr_port += row_offset[dsp_row];
+
+		dsp_data_port += (dsp_index * 8);
+		dsp_addr_port += (dsp_index * 8) + 0x80;
+
+		if (!dsp_check_presence
+		    (dsp_addr_port, dsp_data_port, dsp_num + 1)) {
+			ret |= (1 << dsp_num);
+		}
+	}
+	DIVA_OS_MEM_DETACH_CONTROL(&a->xdi_adapter, base);
+
+	p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+	WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
+	DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+	diva_os_wait(5);
+
+	/*
+	   Verify modules
+	 */
+	for (dsp_row = 0; dsp_row < 4; dsp_row++) {
+		row_state = ((ret >> (dsp_row * 7)) & 0x7F);
+		if (row_state && (row_state != 0x7F)) {
+			for (dsp_index = 0; dsp_index < 7; dsp_index++) {
+				if (!(row_state & (1 << dsp_index))) {
+					DBG_ERR(("A: MODULE[%d]-DSP[%d] failed",
+						 dsp_row + 1,
+						 dsp_index + 1))
+				}
+			}
+		}
+	}
+
+	if (!(ret & 0x10000000)) {
+		DBG_ERR(("A: ON BOARD-DSP[1] failed"))
+	}
+	if (!(ret & 0x20000000)) {
+		DBG_ERR(("A: ON BOARD-DSP[2] failed"))
+	}
+
+	/*
+	   Print module population now
+	 */
+	DBG_LOG(("+-----------------------+"))
+	DBG_LOG(("| DSP MODULE POPULATION |"))
+	DBG_LOG(("+-----------------------+"))
+	DBG_LOG(("|  1  |  2  |  3  |  4  |"))
+	DBG_LOG(("+-----------------------+"))
+	DBG_LOG(("|  %s  |  %s  |  %s  |  %s  |",
+		 ((ret >> (0 * 7)) & 0x7F) ? "Y" : "N",
+		 ((ret >> (1 * 7)) & 0x7F) ? "Y" : "N",
+		 ((ret >> (2 * 7)) & 0x7F) ? "Y" : "N",
+		 ((ret >> (3 * 7)) & 0x7F) ? "Y" : "N"))
+	DBG_LOG(("+-----------------------+"))
+
+	DBG_LOG(("DSP's(present-absent):%08x-%08x", ret,
+		 ~ret & 0x3fffffff))
+
+	return (ret);
+}
diff --git a/drivers/isdn/hardware/eicon/os_pri.h b/drivers/isdn/hardware/eicon/os_pri.h
new file mode 100644
index 0000000..a7c42f9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_pri.h
@@ -0,0 +1,8 @@
+/* $Id: os_pri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
+
+#ifndef __DIVA_OS_PRI_REV_1_H__
+#define __DIVA_OS_PRI_REV_1_H__
+
+int diva_pri_init_card(diva_os_xdi_adapter_t * a);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/pc.h b/drivers/isdn/hardware/eicon/pc.h
new file mode 100644
index 0000000..1c69457
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pc.h
@@ -0,0 +1,738 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 PC_H_INCLUDED  /* { */
+#define PC_H_INCLUDED
+/*------------------------------------------------------------------*/
+/* buffer definition                                                */
+/*------------------------------------------------------------------*/
+typedef struct {
+  word length;          /* length of data/parameter field           */
+  byte P[270];          /* data/parameter field                     */
+} PBUFFER;
+/*------------------------------------------------------------------*/
+/* dual port ram structure                                          */
+/*------------------------------------------------------------------*/
+struct dual
+{
+  byte Req;             /* request register                         */
+  byte ReqId;           /* request task/entity identification       */
+  byte Rc;              /* return code register                     */
+  byte RcId;            /* return code task/entity identification   */
+  byte Ind;             /* Indication register                      */
+  byte IndId;           /* Indication task/entity identification    */
+  byte IMask;           /* Interrupt Mask Flag                      */
+  byte RNR;             /* Receiver Not Ready (set by PC)           */
+  byte XLock;           /* XBuffer locked Flag                      */
+  byte Int;             /* ISDN-S interrupt                         */
+  byte ReqCh;           /* Channel field for layer-3 Requests       */
+  byte RcCh;            /* Channel field for layer-3 Returncodes    */
+  byte IndCh;           /* Channel field for layer-3 Indications    */
+  byte MInd;            /* more data indication field               */
+  word MLength;         /* more data total packet length            */
+  byte ReadyInt;        /* request field for ready interrupt        */
+  byte SWReg;           /* Software register for special purposes   */
+  byte Reserved[11];    /* reserved space                           */
+  byte InterfaceType;   /* interface type 1=16K interface           */
+  word Signature;       /* ISDN-S adapter Signature (GD)            */
+  PBUFFER XBuffer;      /* Transmit Buffer                          */
+  PBUFFER RBuffer;      /* Receive Buffer                           */
+};
+/*------------------------------------------------------------------*/
+/* SWReg Values (0 means no command)                                */
+/*------------------------------------------------------------------*/
+#define SWREG_DIE_WITH_LEDON  0x01
+#define SWREG_HALT_CPU        0x02 /* Push CPU into a while(1) loop */
+/*------------------------------------------------------------------*/
+/* Id Fields Coding                                                 */
+/*------------------------------------------------------------------*/
+#define ID_MASK 0xe0    /* Mask for the ID field                    */
+#define GL_ERR_ID 0x1f  /* ID for error reporting on global requests*/
+#define DSIG_ID  0x00   /* ID for D-channel signaling               */
+#define NL_ID    0x20   /* ID for network-layer access (B or D)     */
+#define BLLC_ID  0x60   /* ID for B-channel link level access       */
+#define TASK_ID  0x80   /* ID for dynamic user tasks                */
+#define TIMER_ID 0xa0   /* ID for timer task                        */
+#define TEL_ID   0xc0   /* ID for telephone support                 */
+#define MAN_ID   0xe0   /* ID for management                        */
+/*------------------------------------------------------------------*/
+/* ASSIGN and REMOVE requests are the same for all entities         */
+/*------------------------------------------------------------------*/
+#define ASSIGN  0x01
+#define UREMOVE 0xfe /* without return code */
+#define REMOVE  0xff
+/*------------------------------------------------------------------*/
+/* Timer Interrupt Task Interface                                   */
+/*------------------------------------------------------------------*/
+#define ASSIGN_TIM 0x01
+#define REMOVE_TIM 0xff
+/*------------------------------------------------------------------*/
+/* dynamic user task interface                                      */
+/*------------------------------------------------------------------*/
+#define ASSIGN_TSK 0x01
+#define REMOVE_TSK 0xff
+#define LOAD 0xf0
+#define RELOCATE 0xf1
+#define START 0xf2
+#define LOAD2 0xf3
+#define RELOCATE2 0xf4
+/*------------------------------------------------------------------*/
+/* dynamic user task messages                                       */
+/*------------------------------------------------------------------*/
+#define TSK_B2          0x0000
+#define TSK_WAKEUP      0x2000
+#define TSK_TIMER       0x4000
+#define TSK_TSK         0x6000
+#define TSK_PC          0xe000
+/*------------------------------------------------------------------*/
+/* LL management primitives                                         */
+/*------------------------------------------------------------------*/
+#define ASSIGN_LL 1     /* assign logical link                      */
+#define REMOVE_LL 0xff  /* remove logical link                      */
+/*------------------------------------------------------------------*/
+/* LL service primitives                                            */
+/*------------------------------------------------------------------*/
+#define LL_UDATA 1      /* link unit data request/indication        */
+#define LL_ESTABLISH 2  /* link establish request/indication        */
+#define LL_RELEASE 3    /* link release request/indication          */
+#define LL_DATA 4       /* data request/indication                  */
+#define LL_LOCAL 5      /* switch to local operation (COM only)     */
+#define LL_DATA_PEND 5  /* data pending indication (SDLC SHM only)  */
+#define LL_REMOTE 6     /* switch to remote operation (COM only)    */
+#define LL_TEST 8       /* link test request                        */
+#define LL_MDATA 9      /* more data request/indication             */
+#define LL_BUDATA 10    /* broadcast unit data request/indication   */
+#define LL_XID 12       /* XID command request/indication           */
+#define LL_XID_R 13     /* XID response request/indication          */
+/*------------------------------------------------------------------*/
+/* NL service primitives                                            */
+/*------------------------------------------------------------------*/
+#define N_MDATA         1       /* more data to come REQ/IND        */
+#define N_CONNECT       2       /* OSI N-CONNECT REQ/IND            */
+#define N_CONNECT_ACK   3       /* OSI N-CONNECT CON/RES            */
+#define N_DISC          4       /* OSI N-DISC REQ/IND               */
+#define N_DISC_ACK      5       /* OSI N-DISC CON/RES               */
+#define N_RESET         6       /* OSI N-RESET REQ/IND              */
+#define N_RESET_ACK     7       /* OSI N-RESET CON/RES              */
+#define N_DATA          8       /* OSI N-DATA REQ/IND               */
+#define N_EDATA         9       /* OSI N-EXPEDITED DATA REQ/IND     */
+#define N_UDATA         10      /* OSI D-UNIT-DATA REQ/IND          */
+#define N_BDATA         11      /* BROADCAST-DATA REQ/IND           */
+#define N_DATA_ACK      12      /* data ack ind for D-bit procedure */
+#define N_EDATA_ACK     13      /* data ack ind for INTERRUPT       */
+#define N_XON           15      /* clear RNR state */
+#define N_COMBI_IND     N_XON   /* combined indication              */
+#define N_Q_BIT         0x10    /* Q-bit for req/ind                */
+#define N_M_BIT         0x20    /* M-bit for req/ind                */
+#define N_D_BIT         0x40    /* D-bit for req/ind                */
+/*------------------------------------------------------------------*/
+/* Signaling management primitives                                  */
+/*------------------------------------------------------------------*/
+#define ASSIGN_SIG  1    /* assign signaling task                    */
+#define UREMOVE_SIG 0xfe /* remove signaling task without return code*/
+#define REMOVE_SIG  0xff /* remove signaling task                    */
+/*------------------------------------------------------------------*/
+/* Signaling service primitives                                     */
+/*------------------------------------------------------------------*/
+#define CALL_REQ 1      /* call request                             */
+#define CALL_CON 1      /* call confirmation                        */
+#define CALL_IND 2      /* incoming call connected                  */
+#define LISTEN_REQ 2    /* listen request                           */
+#define HANGUP 3        /* hangup request/indication                */
+#define SUSPEND 4       /* call suspend request/confirm             */
+#define RESUME 5        /* call resume request/confirm              */
+#define SUSPEND_REJ 6   /* suspend rejected indication              */
+#define USER_DATA 8     /* user data for user to user signaling     */
+#define CONGESTION 9    /* network congestion indication            */
+#define INDICATE_REQ 10 /* request to indicate an incoming call     */
+#define INDICATE_IND 10 /* indicates that there is an incoming call */
+#define CALL_RES 11     /* accept an incoming call                  */
+#define CALL_ALERT 12   /* send ALERT for incoming call             */
+#define INFO_REQ 13     /* INFO request                             */
+#define INFO_IND 13     /* INFO indication                          */
+#define REJECT 14       /* reject an incoming call                  */
+#define RESOURCES 15    /* reserve B-Channel hardware resources     */
+#define HW_CTRL 16      /* B-Channel hardware IOCTL req/ind         */
+#define TEL_CTRL 16     /* Telephone control request/indication     */
+#define STATUS_REQ 17   /* Request D-State (returned in INFO_IND)   */
+#define FAC_REG_REQ 18  /* 1TR6 connection independent fac reg      */
+#define FAC_REG_ACK 19  /* 1TR6 fac registration acknowledge        */
+#define FAC_REG_REJ 20  /* 1TR6 fac registration reject             */
+#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call       */
+#define SW_CTRL 22      /* extended software features               */
+#define REGISTER_REQ 23 /* Q.931 connection independent reg req     */
+#define REGISTER_IND 24 /* Q.931 connection independent reg ind     */
+#define FACILITY_REQ 25 /* Q.931 connection independent fac req     */
+#define FACILITY_IND 26 /* Q.931 connection independent fac ind     */
+#define NCR_INFO_REQ 27 /* INFO_REQ with NULL CR                    */
+#define GCR_MIM_REQ 28  /* MANAGEMENT_INFO_REQ with global CR       */
+#define SIG_CTRL    29  /* Control for Signalling Hardware          */
+#define DSP_CTRL    30  /* Control for DSPs                         */
+#define LAW_REQ      31 /* Law config request for (returns info_i)  */
+#define SPID_CTRL    32 /* Request/indication SPID related          */
+#define NCR_FACILITY 33 /* Request/indication with NULL/DUMMY CR    */
+#define CALL_HOLD    34 /* Request/indication to hold a CALL        */
+#define CALL_RETRIEVE 35 /* Request/indication to retrieve a CALL   */
+#define CALL_HOLD_ACK 36 /* OK of                hold a CALL        */
+#define CALL_RETRIEVE_ACK 37 /* OK of             retrieve a CALL   */
+#define CALL_HOLD_REJ 38 /* Reject of            hold a CALL        */
+#define CALL_RETRIEVE_REJ 39 /* Reject of         retrieve a call   */
+#define GCR_RESTART   40 /* Send/Receive Restart message            */
+#define S_SERVICE     41 /* Send/Receive Supplementary Service      */
+#define S_SERVICE_REJ 42 /* Reject Supplementary Service indication */
+#define S_SUPPORTED   43 /* Req/Ind to get Supported Services       */
+#define STATUS_ENQ    44 /* Req to send the D-ch request if !state0 */
+#define CALL_GUARD    45 /* Req/Ind to use the FLAGS_CALL_OUTCHECK  */
+#define CALL_GUARD_HP 46 /* Call Guard function to reject a call    */
+#define CALL_GUARD_IF 47 /* Call Guard function, inform the appl    */
+#define SSEXT_REQ     48 /* Supplem.Serv./QSIG specific request     */
+#define SSEXT_IND     49 /* Supplem.Serv./QSIG specific indication  */
+/* reserved commands for the US protocols */
+#define INT_3PTY_NIND 50 /* US       specific indication            */
+#define INT_CF_NIND   51 /* US       specific indication            */
+#define INT_3PTY_DROP 52 /* US       specific indication            */
+#define INT_MOVE_CONF 53 /* US       specific indication            */
+#define INT_MOVE_RC   54 /* US       specific indication            */
+#define INT_MOVE_FLIPPED_CONF 55 /* US specific indication          */
+#define INT_X5NI_OK   56 /* internal transfer OK indication         */
+#define INT_XDMS_START 57 /* internal transfer OK indication        */
+#define INT_XDMS_STOP 58 /* internal transfer finish indication     */
+#define INT_XDMS_STOP2 59 /* internal transfer send FA              */
+#define INT_CUSTCONF_REJ 60 /* internal conference reject           */
+#define INT_CUSTXFER 61 /* internal transfer request                */
+#define INT_CUSTX_NIND 62 /* internal transfer ack                  */
+#define INT_CUSTXREJ_NIND 63 /* internal transfer rej               */
+#define INT_X5NI_CF_XFER  64 /* internal transfer OK indication     */
+#define VSWITCH_REQ 65        /* communication between protocol and */
+#define VSWITCH_IND 66        /* capifunctions for D-CH-switching   */
+#define MWI_POLL 67     /* Message Waiting Status Request fkt */
+#define CALL_PEND_NOTIFY 68 /* notify capi to set new listen        */
+#define DO_NOTHING 69       /* dont do somethin if you get this     */
+#define INT_CT_REJ 70       /* ECT rejected internal command        */
+#define CALL_HOLD_COMPLETE 71 /* In NT Mode indicate hold complete  */
+#define CALL_RETRIEVE_COMPLETE 72 /* In NT Mode indicate retrieve complete  */
+/*------------------------------------------------------------------*/
+/* management service primitives                                    */
+/*------------------------------------------------------------------*/
+#define MAN_READ        2
+#define MAN_WRITE       3
+#define MAN_EXECUTE     4
+#define MAN_EVENT_ON    5
+#define MAN_EVENT_OFF   6
+#define MAN_LOCK        7
+#define MAN_UNLOCK      8
+#define MAN_INFO_IND    2
+#define MAN_EVENT_IND   3
+#define MAN_TRACE_IND   4
+#define MAN_COMBI_IND   9
+#define MAN_ESC         0x80
+/*------------------------------------------------------------------*/
+/* return code coding                                               */
+/*------------------------------------------------------------------*/
+#define UNKNOWN_COMMAND         0x01    /* unknown command          */
+#define WRONG_COMMAND           0x02    /* wrong command            */
+#define WRONG_ID                0x03    /* unknown task/entity id   */
+#define WRONG_CH                0x04    /* wrong task/entity id     */
+#define UNKNOWN_IE              0x05    /* unknown information el.  */
+#define WRONG_IE                0x06    /* wrong information el.    */
+#define OUT_OF_RESOURCES        0x07    /* ISDN-S card out of res.  */
+#define ISDN_GUARD_REJ          0x09    /* ISDN-Guard SuppServ rej  */
+#define N_FLOW_CONTROL          0x10    /* Flow-Control, retry      */
+#define ASSIGN_RC               0xe0    /* ASSIGN acknowledgement   */
+#define ASSIGN_OK               0xef    /* ASSIGN OK                */
+#define OK_FC                   0xfc    /* Flow-Control RC          */
+#define READY_INT               0xfd    /* Ready interrupt          */
+#define TIMER_INT               0xfe    /* timer interrupt          */
+#define OK                      0xff    /* command accepted         */
+/*------------------------------------------------------------------*/
+/* information elements                                             */
+/*------------------------------------------------------------------*/
+#define SHIFT 0x90              /* codeset shift                    */
+#define MORE 0xa0               /* more data                        */
+#define SDNCMPL 0xa1            /* sending complete                 */
+#define CL 0xb0                 /* congestion level                 */
+        /* codeset 0                                                */
+#define SMSG 0x00               /* segmented message                */
+#define BC  0x04                /* Bearer Capability                */
+#define CAU 0x08                /* cause                            */
+#define CAD 0x0c                /* Connected address                */
+#define CAI 0x10                /* call identity                    */
+#define CHI 0x18                /* channel identification           */
+#define LLI 0x19                /* logical link id                  */
+#define CHA 0x1a                /* charge advice                    */
+#define FTY 0x1c                /* Facility                         */
+#define DT  0x29                /* ETSI date/time                   */
+#define KEY 0x2c                /* keypad information element       */
+#define UID 0x2d                /* User id information element      */
+#define DSP 0x28                /* display                          */
+#define SIG 0x34                /* signalling hardware control      */
+#define OAD 0x6c                /* origination address              */
+#define OSA 0x6d                /* origination sub-address          */
+#define CPN 0x70                /* called party number              */
+#define DSA 0x71                /* destination sub-address          */
+#define RDX 0x73                /* redirecting number extended      */
+#define RDN 0x74                /* redirecting number               */
+#define RIN 0x76                /* redirection number               */
+#define IUP 0x76                /* VN6 rerouter->PCS (codeset 6)    */
+#define IPU 0x77                /* VN6 PCS->rerouter (codeset 6)    */
+#define RI  0x79                /* restart indicator                */
+#define MIE 0x7a                /* management info element          */
+#define LLC 0x7c                /* low layer compatibility          */
+#define HLC 0x7d                /* high layer compatibility         */
+#define UUI 0x7e                /* user user information            */
+#define ESC 0x7f                /* escape extension                 */
+#define DLC 0x20                /* data link layer configuration    */
+#define NLC 0x21                /* network layer configuration      */
+#define REDIRECT_IE     0x22    /* redirection request/indication data */
+#define REDIRECT_NET_IE 0x23    /* redirection network override data   */
+        /* codeset 6                                                */
+#define SIN 0x01                /* service indicator                */
+#define CIF 0x02                /* charging information             */
+#define DATE 0x03               /* date                             */
+#define CPS 0x07                /* called party status              */
+/*------------------------------------------------------------------*/
+/* ESC information elements                                         */
+/*------------------------------------------------------------------*/
+#define MSGTYPEIE        0x7a   /* Messagetype info element         */
+#define CRIE             0x7b   /* INFO info element                */
+#define CODESET6IE       0xec   /* Tunnel for Codeset 6 IEs         */
+#define VSWITCHIE        0xed   /* VSwitch info element             */
+#define SSEXTIE          0xee   /* Supplem. Service info element    */
+#define PROFILEIE        0xef   /* Profile info element             */
+/*------------------------------------------------------------------*/
+/* TEL_CTRL contents                                                */
+/*------------------------------------------------------------------*/
+#define RING_ON         0x01
+#define RING_OFF        0x02
+#define HANDS_FREE_ON   0x03
+#define HANDS_FREE_OFF  0x04
+#define ON_HOOK         0x80
+#define OFF_HOOK        0x90
+/* operation values used by ETSI supplementary services */
+#define THREE_PTY_BEGIN           0x04
+#define THREE_PTY_END             0x05
+#define ECT_EXECUTE               0x06
+#define ACTIVATION_DIVERSION      0x07
+#define DEACTIVATION_DIVERSION    0x08
+#define CALL_DEFLECTION           0x0D
+#define INTERROGATION_DIVERSION   0x0B
+#define INTERROGATION_SERV_USR_NR 0x11
+#define ACTIVATION_MWI            0x20
+#define DEACTIVATION_MWI          0x21
+#define MWI_INDICATION            0x22
+#define MWI_RESPONSE              0x23
+#define CONF_BEGIN                0x28
+#define CONF_ADD                  0x29
+#define CONF_SPLIT                0x2a
+#define CONF_DROP                 0x2b
+#define CONF_ISOLATE              0x2c
+#define CONF_REATTACH             0x2d
+#define CONF_PARTYDISC            0x2e
+#define CCBS_INFO_RETAIN          0x2f
+#define CCBS_ERASECALLLINKAGEID   0x30
+#define CCBS_STOP_ALERTING        0x31
+#define CCBS_REQUEST              0x32
+#define CCBS_DEACTIVATE           0x33
+#define CCBS_INTERROGATE          0x34
+#define CCBS_STATUS               0x35
+#define CCBS_ERASE                0x36
+#define CCBS_B_FREE               0x37
+#define CCNR_INFO_RETAIN          0x38
+#define CCBS_REMOTE_USER_FREE     0x39
+#define CCNR_REQUEST              0x3a
+#define CCNR_INTERROGATE          0x3b
+#define GET_SUPPORTED_SERVICES    0xff
+#define DIVERSION_PROCEDURE_CFU     0x70
+#define DIVERSION_PROCEDURE_CFB     0x71
+#define DIVERSION_PROCEDURE_CFNR    0x72
+#define DIVERSION_DEACTIVATION_CFU  0x80
+#define DIVERSION_DEACTIVATION_CFB  0x81
+#define DIVERSION_DEACTIVATION_CFNR 0x82
+#define DIVERSION_INTERROGATE_NUM   0x11
+#define DIVERSION_INTERROGATE_CFU   0x60
+#define DIVERSION_INTERROGATE_CFB   0x61
+#define DIVERSION_INTERROGATE_CFNR  0x62
+/* Service Masks */
+#define SMASK_HOLD_RETRIEVE        0x00000001
+#define SMASK_TERMINAL_PORTABILITY 0x00000002
+#define SMASK_ECT                  0x00000004
+#define SMASK_3PTY                 0x00000008
+#define SMASK_CALL_FORWARDING      0x00000010
+#define SMASK_CALL_DEFLECTION      0x00000020
+#define SMASK_MCID                 0x00000040
+#define SMASK_CCBS                 0x00000080
+#define SMASK_MWI                  0x00000100
+#define SMASK_CCNR                 0x00000200
+#define SMASK_CONF                 0x00000400
+/* ----------------------------------------------
+    Types of transfers used to transfer the
+    information in the 'struct RC->Reserved2[8]'
+    The information is transferred as 2 dwords
+    (2 4Byte unsigned values)
+    First of them is the transfer type.
+    2^32-1 possible messages are possible in this way.
+    The context of the second one had no meaning
+   ---------------------------------------------- */
+#define DIVA_RC_TYPE_NONE              0x00000000
+#define DIVA_RC_TYPE_REMOVE_COMPLETE   0x00000008
+#define DIVA_RC_TYPE_STREAM_PTR        0x00000009
+#define DIVA_RC_TYPE_CMA_PTR           0x0000000a
+#define DIVA_RC_TYPE_OK_FC             0x0000000b
+#define DIVA_RC_TYPE_RX_DMA            0x0000000c
+/* ------------------------------------------------------
+      IO Control codes for IN BAND SIGNALING
+   ------------------------------------------------------ */
+#define CTRL_L1_SET_SIG_ID        5
+#define CTRL_L1_SET_DAD           6
+#define CTRL_L1_RESOURCES         7
+/* ------------------------------------------------------ */
+/* ------------------------------------------------------
+      Layer 2 types
+   ------------------------------------------------------ */
+#define X75T            1       /* x.75 for ttx                     */
+#define TRF             2       /* transparent with hdlc framing    */
+#define TRF_IN          3       /* transparent with hdlc fr. inc.   */
+#define SDLC            4       /* sdlc, sna layer-2                */
+#define X75             5       /* x.75 for btx                     */
+#define LAPD            6       /* lapd (Q.921)                     */
+#define X25_L2          7       /* x.25 layer-2                     */
+#define V120_L2         8       /* V.120 layer-2 protocol           */
+#define V42_IN          9       /* V.42 layer-2 protocol, incomming */
+#define V42            10       /* V.42 layer-2 protocol            */
+#define MDM_ATP        11       /* AT Parser built in the L2        */
+#define X75_V42BIS     12       /* x.75 with V.42bis                */
+#define RTPL2_IN       13       /* RTP layer-2 protocol, incomming  */
+#define RTPL2          14       /* RTP layer-2 protocol             */
+#define V120_V42BIS    15       /* V.120 asynchronous mode supporting V.42bis compression */
+#define LISTENER       27       /* Layer 2 to listen line */
+#define MTP2           28       /* MTP2 Layer 2 */
+#define PIAFS_CRC      29       /* PIAFS Layer 2 with CRC calculation at L2 */
+/* ------------------------------------------------------
+   PIAFS DLC DEFINITIONS
+   ------------------------------------------------------ */
+#define PIAFS_64K            0x01
+#define PIAFS_VARIABLE_SPEED 0x02
+#define PIAFS_CHINESE_SPEED    0x04
+#define PIAFS_UDATA_ABILITY_ID    0x80
+#define PIAFS_UDATA_ABILITY_DCDON 0x01
+#define PIAFS_UDATA_ABILITY_DDI   0x80
+/*
+DLC of PIAFS :
+Byte | 8 7 6 5 4 3 2 1
+-----+--------------------------------------------------------
+   0 | 0 0 1 0 0 0 0 0  Data Link Configuration
+   1 | X X X X X X X X  Length of IE (at least 15 Bytes)
+   2 | 0 0 0 0 0 0 0 0  max. information field, LOW  byte (not used, fix 73 Bytes)
+   3 | 0 0 0 0 0 0 0 0  max. information field, HIGH byte (not used, fix 73 Bytes)
+   4 | 0 0 0 0 0 0 0 0  address A (not used)
+   5 | 0 0 0 0 0 0 0 0  address B (not used)
+   6 | 0 0 0 0 0 0 0 0  Mode (not used, fix 128)
+   7 | 0 0 0 0 0 0 0 0  Window Size (not used, fix 127)
+   8 | X X X X X X X X  XID Length, Low Byte (at least 7 Bytes)
+   9 | X X X X X X X X  XID Length, High Byte
+  10 | 0 0 0 0 0 C V S  PIAFS Protocol Speed configuration -> Note(1)
+     |                  S = 0 -> Protocol Speed is 32K
+     |                  S = 1 -> Protocol Speed is 64K
+     |                  V = 0 -> Protocol Speed is fixed
+     |                  V = 1 -> Protocol Speed is variable
+     |                  C = 0 -> speed setting according to standard
+     |                  C = 1 -> speed setting for chinese implementation
+  11 | 0 0 0 0 0 0 R T  P0 - V42bis Compression enable/disable, Low Byte
+     |                  T = 0 -> Transmit Direction enable
+     |                  T = 1 -> Transmit Direction disable
+     |                  R = 0 -> Receive  Direction enable
+     |                  R = 1 -> Receive  Direction disable
+  13 | 0 0 0 0 0 0 0 0  P0 - V42bis Compression enable/disable, High Byte
+  14 | X X X X X X X X  P1 - V42bis Dictionary Size, Low Byte
+  15 | X X X X X X X X  P1 - V42bis Dictionary Size, High Byte
+  16 | X X X X X X X X  P2 - V42bis String Length, Low Byte
+  17 | X X X X X X X X  P2 - V42bis String Length, High Byte
+  18 | X X X X X X X X  PIAFS extension length
+  19 | 1 0 0 0 0 0 0 0  PIAFS extension Id (0x80) - UDATA abilities
+  20 | U 0 0 0 0 0 0 D  UDATA abilities -> Note (2)
+     |                  up to now the following Bits are defined:
+     |                  D - signal DCD ON
+     |                  U - use extensive UDATA control communication
+     |                      for DDI test application
++ Note (1): ----------+------+-----------------------------------------+
+| PIAFS Protocol      | Bit  |                                         |
+| Speed configuration |    S | Bit 1 - Protocol Speed                  |
+|                     |      |         0 - 32K                         |
+|                     |      |         1 - 64K (default)               |
+|                     |    V | Bit 2 - Variable Protocol Speed         |
+|                     |      |         0 - Speed is fix                |
+|                     |      |         1 - Speed is variable (default) |
+|                     |      |             OVERWRITES 32k Bit 1        |
+|                     |    C | Bit 3   0 - Speed Settings according to |
+|                     |      |             PIAFS specification         |
+|                     |      |         1 - Speed setting for chinese   |
+|                     |      |             PIAFS implementation        |
+|                     |      | Explanation for chinese speed settings: |
+|                     |      |         if Bit 3 is set the following   |
+|                     |      |         rules apply:                    |
+|                     |      |         Bit1=0 Bit2=0: 32k fix          |
+|                     |      |         Bit1=1 Bit2=0: 64k fix          |
+|                     |      |         Bit1=0 Bit2=1: PIAFS is trying  |
+|                     |      |             to negotiate 32k is that is |
+|                     |      |             not possible it tries to    |
+|                     |      |             negotiate 64k               |
+|                     |      |         Bit1=1 Bit2=1: PIAFS is trying  |
+|                     |      |             to negotiate 64k is that is |
+|                     |      |             not possible it tries to    |
+|                     |      |             negotiate 32k               |
++ Note (2): ----------+------+-----------------------------------------+
+| PIAFS               | Bit  | this byte defines the usage of UDATA    |
+| Implementation      |      | control communication                   |
+| UDATA usage         |    D | Bit 1 - DCD-ON signalling               |
+|                     |      |         0 - no DCD-ON is signalled      |
+|                     |      |             (default)                   |
+|                     |      |         1 - DCD-ON will be signalled    |
+|                     |    U | Bit 8 - DDI test application UDATA      |
+|                     |      |         control communication           |
+|                     |      |         0 - no UDATA control            |
+|                     |      |             communication (default)     |
+|                     |      |             sets as well the DCD-ON     |
+|                     |      |             signalling                  |
+|                     |      |         1 - UDATA control communication |
+|                     |      |             ATTENTION: Do not use these |
+|                     |      |                        setting if you   |
+|                     |      |                        are not really   |
+|                     |      |                        that you need it |
+|                     |      |                        and you know     |
+|                     |      |                        exactly what you |
+|                     |      |                        are doing.       |
+|                     |      |                        You can easily   |
+|                     |      |                        disable any      |
+|                     |      |                        data transfer.   |
++---------------------+------+-----------------------------------------+
+*/
+/* ------------------------------------------------------
+   LISTENER DLC DEFINITIONS
+   ------------------------------------------------------ */
+#define LISTENER_FEATURE_MASK_CUMMULATIVE            0x0001
+/* ------------------------------------------------------
+   LISTENER META-FRAME CODE/PRIMITIVE DEFINITIONS
+   ------------------------------------------------------ */
+#define META_CODE_LL_UDATA_RX 0x01
+#define META_CODE_LL_UDATA_TX 0x02
+#define META_CODE_LL_DATA_RX  0x03
+#define META_CODE_LL_DATA_TX  0x04
+#define META_CODE_LL_MDATA_RX 0x05
+#define META_CODE_LL_MDATA_TX 0x06
+#define META_CODE_EMPTY       0x10
+#define META_CODE_LOST_FRAMES 0x11
+#define META_FLAG_TRUNCATED   0x0001
+/*------------------------------------------------------------------*/
+/* CAPI-like profile to indicate features on LAW_REQ                */
+/*------------------------------------------------------------------*/
+#define GL_INTERNAL_CONTROLLER_SUPPORTED     0x00000001L
+#define GL_EXTERNAL_EQUIPMENT_SUPPORTED      0x00000002L
+#define GL_HANDSET_SUPPORTED                 0x00000004L
+#define GL_DTMF_SUPPORTED                    0x00000008L
+#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED  0x00000010L
+#define GL_CHANNEL_ALLOCATION_SUPPORTED      0x00000020L
+#define GL_BCHANNEL_OPERATION_SUPPORTED      0x00000040L
+#define GL_LINE_INTERCONNECT_SUPPORTED       0x00000080L
+#define B1_HDLC_SUPPORTED                    0x00000001L
+#define B1_TRANSPARENT_SUPPORTED             0x00000002L
+#define B1_V110_ASYNC_SUPPORTED              0x00000004L
+#define B1_V110_SYNC_SUPPORTED               0x00000008L
+#define B1_T30_SUPPORTED                     0x00000010L
+#define B1_HDLC_INVERTED_SUPPORTED           0x00000020L
+#define B1_TRANSPARENT_R_SUPPORTED           0x00000040L
+#define B1_MODEM_ALL_NEGOTIATE_SUPPORTED     0x00000080L
+#define B1_MODEM_ASYNC_SUPPORTED             0x00000100L
+#define B1_MODEM_SYNC_HDLC_SUPPORTED         0x00000200L
+#define B2_X75_SUPPORTED                     0x00000001L
+#define B2_TRANSPARENT_SUPPORTED             0x00000002L
+#define B2_SDLC_SUPPORTED                    0x00000004L
+#define B2_LAPD_SUPPORTED                    0x00000008L
+#define B2_T30_SUPPORTED                     0x00000010L
+#define B2_PPP_SUPPORTED                     0x00000020L
+#define B2_TRANSPARENT_NO_CRC_SUPPORTED      0x00000040L
+#define B2_MODEM_EC_COMPRESSION_SUPPORTED    0x00000080L
+#define B2_X75_V42BIS_SUPPORTED              0x00000100L
+#define B2_V120_ASYNC_SUPPORTED              0x00000200L
+#define B2_V120_ASYNC_V42BIS_SUPPORTED       0x00000400L
+#define B2_V120_BIT_TRANSPARENT_SUPPORTED    0x00000800L
+#define B2_LAPD_FREE_SAPI_SEL_SUPPORTED      0x00001000L
+#define B3_TRANSPARENT_SUPPORTED             0x00000001L
+#define B3_T90NL_SUPPORTED                   0x00000002L
+#define B3_ISO8208_SUPPORTED                 0x00000004L
+#define B3_X25_DCE_SUPPORTED                 0x00000008L
+#define B3_T30_SUPPORTED                     0x00000010L
+#define B3_T30_WITH_EXTENSIONS_SUPPORTED     0x00000020L
+#define B3_RESERVED_SUPPORTED                0x00000040L
+#define B3_MODEM_SUPPORTED                   0x00000080L
+#define MANUFACTURER_FEATURE_SLAVE_CODEC          0x00000001L
+#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS   0x00000002L
+#define MANUFACTURER_FEATURE_HARDDTMF             0x00000004L
+#define MANUFACTURER_FEATURE_SOFTDTMF_SEND        0x00000008L
+#define MANUFACTURER_FEATURE_DTMF_PARAMETERS      0x00000010L
+#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE     0x00000020L
+#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD      0x00000040L
+#define MANUFACTURER_FEATURE_V18                  0x00000080L
+#define MANUFACTURER_FEATURE_MIXER_CH_CH          0x00000100L
+#define MANUFACTURER_FEATURE_MIXER_CH_PC          0x00000200L
+#define MANUFACTURER_FEATURE_MIXER_PC_CH          0x00000400L
+#define MANUFACTURER_FEATURE_MIXER_PC_PC          0x00000800L
+#define MANUFACTURER_FEATURE_ECHO_CANCELLER       0x00001000L
+#define MANUFACTURER_FEATURE_RTP                  0x00002000L
+#define MANUFACTURER_FEATURE_T38                  0x00004000L
+#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L
+#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL  0x00010000L
+#define MANUFACTURER_FEATURE_OOB_CHANNEL          0x00020000L
+#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL      0x00040000L
+#define MANUFACTURER_FEATURE_IN_BAND_FEATURE      0x00080000L
+#define MANUFACTURER_FEATURE_PIAFS                0x00100000L
+#define MANUFACTURER_FEATURE_DTMF_TONE            0x00200000L
+#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS    0x00400000L
+#define MANUFACTURER_FEATURE_OK_FC_LABEL          0x00800000L
+#define MANUFACTURER_FEATURE_VOWN                 0x01000000L
+#define MANUFACTURER_FEATURE_XCONNECT             0x02000000L
+#define MANUFACTURER_FEATURE_DMACONNECT           0x04000000L
+#define MANUFACTURER_FEATURE_AUDIO_TAP            0x08000000L
+#define MANUFACTURER_FEATURE_FAX_NONSTANDARD      0x10000000L
+#define MANUFACTURER_FEATURE_SS7                  0x20000000L
+#define MANUFACTURER_FEATURE_MADAPTER             0x40000000L
+#define MANUFACTURER_FEATURE_MEASURE              0x80000000L
+#define MANUFACTURER_FEATURE2_LISTENING           0x00000001L
+#define MANUFACTURER_FEATURE2_SS_DIFFCONTPOSSIBLE 0x00000002L
+#define MANUFACTURER_FEATURE2_GENERIC_TONE        0x00000004L
+#define MANUFACTURER_FEATURE2_COLOR_FAX           0x00000008L
+#define MANUFACTURER_FEATURE2_SS_ECT_DIFFCONTPOSSIBLE 0x00000010L
+#define RTP_PRIM_PAYLOAD_PCMU_8000     0
+#define RTP_PRIM_PAYLOAD_1016_8000     1
+#define RTP_PRIM_PAYLOAD_G726_32_8000  2
+#define RTP_PRIM_PAYLOAD_GSM_8000      3
+#define RTP_PRIM_PAYLOAD_G723_8000     4
+#define RTP_PRIM_PAYLOAD_DVI4_8000     5
+#define RTP_PRIM_PAYLOAD_DVI4_16000    6
+#define RTP_PRIM_PAYLOAD_LPC_8000      7
+#define RTP_PRIM_PAYLOAD_PCMA_8000     8
+#define RTP_PRIM_PAYLOAD_G722_16000    9
+#define RTP_PRIM_PAYLOAD_QCELP_8000    12
+#define RTP_PRIM_PAYLOAD_G728_8000     14
+#define RTP_PRIM_PAYLOAD_G729_8000     18
+#define RTP_PRIM_PAYLOAD_GSM_HR_8000   30
+#define RTP_PRIM_PAYLOAD_GSM_EFR_8000  31
+#define RTP_ADD_PAYLOAD_BASE           32
+#define RTP_ADD_PAYLOAD_RED            32
+#define RTP_ADD_PAYLOAD_CN_8000        33
+#define RTP_ADD_PAYLOAD_DTMF           34
+#define RTP_PRIM_PAYLOAD_PCMU_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_PCMU_8000)
+#define RTP_PRIM_PAYLOAD_1016_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_1016_8000)
+#define RTP_PRIM_PAYLOAD_G726_32_8000_SUPPORTED  (1L << RTP_PRIM_PAYLOAD_G726_32_8000)
+#define RTP_PRIM_PAYLOAD_GSM_8000_SUPPORTED      (1L << RTP_PRIM_PAYLOAD_GSM_8000)
+#define RTP_PRIM_PAYLOAD_G723_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_G723_8000)
+#define RTP_PRIM_PAYLOAD_DVI4_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_DVI4_8000)
+#define RTP_PRIM_PAYLOAD_DVI4_16000_SUPPORTED    (1L << RTP_PRIM_PAYLOAD_DVI4_16000)
+#define RTP_PRIM_PAYLOAD_LPC_8000_SUPPORTED      (1L << RTP_PRIM_PAYLOAD_LPC_8000)
+#define RTP_PRIM_PAYLOAD_PCMA_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_PCMA_8000)
+#define RTP_PRIM_PAYLOAD_G722_16000_SUPPORTED    (1L << RTP_PRIM_PAYLOAD_G722_16000)
+#define RTP_PRIM_PAYLOAD_QCELP_8000_SUPPORTED    (1L << RTP_PRIM_PAYLOAD_QCELP_8000)
+#define RTP_PRIM_PAYLOAD_G728_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_G728_8000)
+#define RTP_PRIM_PAYLOAD_G729_8000_SUPPORTED     (1L << RTP_PRIM_PAYLOAD_G729_8000)
+#define RTP_PRIM_PAYLOAD_GSM_HR_8000_SUPPORTED   (1L << RTP_PRIM_PAYLOAD_GSM_HR_8000)
+#define RTP_PRIM_PAYLOAD_GSM_EFR_8000_SUPPORTED  (1L << RTP_PRIM_PAYLOAD_GSM_EFR_8000)
+#define RTP_ADD_PAYLOAD_RED_SUPPORTED            (1L << (RTP_ADD_PAYLOAD_RED - RTP_ADD_PAYLOAD_BASE))
+#define RTP_ADD_PAYLOAD_CN_8000_SUPPORTED        (1L << (RTP_ADD_PAYLOAD_CN_8000 - RTP_ADD_PAYLOAD_BASE))
+#define RTP_ADD_PAYLOAD_DTMF_SUPPORTED           (1L << (RTP_ADD_PAYLOAD_DTMF - RTP_ADD_PAYLOAD_BASE))
+/* virtual switching definitions */
+#define VSJOIN         1
+#define VSTRANSPORT    2
+#define VSGETPARAMS    3
+#define VSCAD          1
+#define VSRXCPNAME     2
+#define VSCALLSTAT     3
+#define VSINVOKEID    4
+#define VSCLMRKS       5
+#define VSTBCTIDENT    6
+#define VSETSILINKID   7
+#define VSSAMECONTROLLER 8
+/* Errorcodes for VSETSILINKID begin */
+#define VSETSILINKIDRRWC      1
+#define VSETSILINKIDREJECT    2
+#define VSETSILINKIDTIMEOUT   3
+#define VSETSILINKIDFAILCOUNT 4
+#define VSETSILINKIDERROR     5
+/* Errorcodes for VSETSILINKID end */
+/* -----------------------------------------------------------**
+** The PROTOCOL_FEATURE_STRING in feature.h (included         **
+** in prstart.sx and astart.sx) defines capabilities and      **
+** features of the actual protocol code. It's used as a bit   **
+** mask.                                                      **
+** The following Bits are defined:                            **
+** -----------------------------------------------------------*/
+#define PROTCAP_TELINDUS  0x0001  /* Telindus Variant of protocol code   */
+#define PROTCAP_MAN_IF    0x0002  /* Management interface implemented    */
+#define PROTCAP_V_42      0x0004  /* V42 implemented                     */
+#define PROTCAP_V90D      0x0008  /* V.90D (implies up to 384k DSP code) */
+#define PROTCAP_EXTD_FAX  0x0010  /* Extended FAX (ECM, 2D, T6, Polling) */
+#define PROTCAP_EXTD_RXFC 0x0020  /* RxFC (Extd Flow Control), OOB Chnl  */
+#define PROTCAP_VOIP      0x0040  /* VoIP (implies up to 512k DSP code)  */
+#define PROTCAP_CMA_ALLPR 0x0080  /* CMA support for all NL primitives   */
+#define PROTCAP_FREE8     0x0100  /* not used                            */
+#define PROTCAP_FREE9     0x0200  /* not used                            */
+#define PROTCAP_FREE10    0x0400  /* not used                            */
+#define PROTCAP_FREE11    0x0800  /* not used                            */
+#define PROTCAP_FREE12    0x1000  /* not used                            */
+#define PROTCAP_FREE13    0x2000  /* not used                            */
+#define PROTCAP_FREE14    0x4000  /* not used                            */
+#define PROTCAP_EXTENSION 0x8000  /* used for future extentions          */
+/* -----------------------------------------------------------* */
+/* Onhook data transmission ETS30065901 */
+/* Message Type */
+/*#define RESERVED4                 0x4*/
+#define CALL_SETUP                0x80
+#define MESSAGE_WAITING_INDICATOR 0x82
+/*#define RESERVED84                0x84*/
+/*#define RESERVED85                0x85*/
+#define ADVICE_OF_CHARGE          0x86
+/*1111 0001
+to
+1111 1111
+F1H - Reserved for network operator use
+to
+FFH*/
+/* Parameter Types */
+#define DATE_AND_TIME                                           1
+#define CLI_PARAMETER_TYPE                                      2
+#define CALLED_DIRECTORY_NUMBER_PARAMETER_TYPE                  3
+#define REASON_FOR_ABSENCE_OF_CLI_PARAMETER_TYPE                4
+#define NAME_PARAMETER_TYPE                                     7
+#define REASON_FOR_ABSENCE_OF_CALLING_PARTY_NAME_PARAMETER_TYPE 8
+#define VISUAL_INDICATOR_PARAMETER_TYPE                         0xb
+#define COMPLEMENTARY_CLI_PARAMETER_TYPE                        0x10
+#define CALL_TYPE_PARAMETER_TYPE                                0x11
+#define FIRST_CALLED_LINE_DIRECTORY_NUMBER_PARAMETER_TYPE       0x12
+#define NETWORK_MESSAGE_SYSTEM_STATUS_PARAMETER_TYPE            0x13
+#define FORWARDED_CALL_TYPE_PARAMETER_TYPE                      0x15
+#define TYPE_OF_CALLING_USER_PARAMETER_TYPE                     0x16
+#define REDIRECTING_NUMBER_PARAMETER_TYPE                       0x1a
+#define EXTENSION_FOR_NETWORK_OPERATOR_USE_PARAMETER_TYPE       0xe0
+/* -----------------------------------------------------------* */
+#else
+#endif /* PC_H_INCLUDED  } */
diff --git a/drivers/isdn/hardware/eicon/pc_init.h b/drivers/isdn/hardware/eicon/pc_init.h
new file mode 100644
index 0000000..a616fc9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pc_init.h
@@ -0,0 +1,267 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 PC_INIT_H_
+#define PC_INIT_H_
+/*------------------------------------------------------------------*/
+/*
+  Initialisation parameters for the card
+  0x0008 <byte> TEI
+  0x0009 <byte> NT2 flag
+  0x000a <byte> Default DID length
+  0x000b <byte> Disable watchdog flag
+  0x000c <byte> Permanent connection flag
+  0x000d <byte> Bit 3-8: L1 Hunt Group/Tristate
+  0x000d <byte> Bit 1: QSig small CR length if set to 1
+  0x000d <byte> Bit 2: QSig small CHI length if set to 1
+  0x000e <byte> Bit 1-3: Stable L2, 0=OnDemand,1=NoDisc,2=permanent
+  0x000e <byte> Bit 4: NT mode
+  0x000e <byte> Bit 5: QSig Channel ID format
+  0x000e <byte> Bit 6: QSig Call Forwarding Allowed Flag
+  0x000e <byte> Bit 7: Disable AutoSPID Flag
+  0x000f <byte> No order check flag
+  0x0010 <byte> Force companding type:0=default,1=a-law,2=u-law
+  0x0012 <byte> Low channel flag
+  0x0013 <byte> Protocol version
+  0x0014 <byte> CRC4 option:0=default,1=double_frm,2=multi_frm,3=auto
+  0x0015 <byte> Bit 0: NoHscx30, Bit 1: Loopback flag, Bit 2: ForceHscx30
+  0x0016 <byte> DSP info
+  0x0017-0x0019 Serial number
+  0x001a <byte> Card type
+  0x0020 <string> OAD 0
+  0x0040 <string> OSA 0
+  0x0060 <string> SPID 0 (if not T.1)
+  0x0060 <struct> if T.1: Robbed Bit Configuration
+  0x0060          length (8)
+  0x0061          RBS Answer Delay
+  0x0062          RBS Config Bit 3, 4:
+                             0  0 -> Wink Start
+                             1  0 -> Loop Start
+                             0  1 -> Ground Start
+                             1  1 -> reserved
+                             Bit 5, 6:
+                             0  0 -> Pulse Dial -> Rotary
+                             1  0 -> DTMF
+                             0  1 -> MF
+                             1  1 -> reserved
+  0x0063          RBS RX Digit Timeout
+  0x0064          RBS Bearer Capability
+  0x0065-0x0069   RBS Debug Mask
+  0x0080 <string> OAD 1
+  0x00a0 <string> OSA 1
+  0x00c0 <string> SPID 1
+  0x00e0 <w-element list> Additional configuration
+*/
+#define PCINIT_END_OF_LIST                0x00
+#define PCINIT_MODEM_GUARD_TONE           0x01
+#define PCINIT_MODEM_MIN_SPEED            0x02
+#define PCINIT_MODEM_MAX_SPEED            0x03
+#define PCINIT_MODEM_PROTOCOL_OPTIONS     0x04
+#define PCINIT_FAX_OPTIONS                0x05
+#define PCINIT_FAX_MAX_SPEED              0x06
+#define PCINIT_MODEM_OPTIONS              0x07
+#define PCINIT_MODEM_NEGOTIATION_MODE     0x08
+#define PCINIT_MODEM_MODULATIONS_MASK     0x09
+#define PCINIT_MODEM_TRANSMIT_LEVEL       0x0a
+#define PCINIT_FAX_DISABLED_RESOLUTIONS   0x0b
+#define PCINIT_FAX_MAX_RECORDING_WIDTH    0x0c
+#define PCINIT_FAX_MAX_RECORDING_LENGTH   0x0d
+#define PCINIT_FAX_MIN_SCANLINE_TIME      0x0e
+#define PCINIT_US_EKTS_CACH_HANDLES       0x0f
+#define PCINIT_US_EKTS_BEGIN_CONF         0x10
+#define PCINIT_US_EKTS_DROP_CONF          0x11
+#define PCINIT_US_EKTS_CALL_TRANSFER      0x12
+#define PCINIT_RINGERTONE_OPTION          0x13
+#define PCINIT_CARD_ADDRESS               0x14
+#define PCINIT_FPGA_FEATURES              0x15
+#define PCINIT_US_EKTS_MWI                0x16
+#define PCINIT_MODEM_SPEAKER_CONTROL      0x17
+#define PCINIT_MODEM_SPEAKER_VOLUME       0x18
+#define PCINIT_MODEM_CARRIER_WAIT_TIME    0x19
+#define PCINIT_MODEM_CARRIER_LOSS_TIME    0x1a
+#define PCINIT_UNCHAN_B_MASK              0x1b
+#define PCINIT_PART68_LIMITER             0x1c
+#define PCINIT_XDI_FEATURES               0x1d
+#define PCINIT_QSIG_DIALECT               0x1e
+#define PCINIT_DISABLE_AUTOSPID_FLAG      0x1f
+#define PCINIT_FORCE_VOICE_MAIL_ALERT     0x20
+#define PCINIT_PIAFS_TURNAROUND_FRAMES    0x21
+#define PCINIT_L2_COUNT                   0x22
+#define PCINIT_QSIG_FEATURES              0x23
+#define PCINIT_NO_SIGNALLING              0x24
+#define PCINIT_CARD_SN                    0x25
+#define PCINIT_CARD_PORT                  0x26
+#define PCINIT_ALERTTO                    0x27
+#define PCINIT_MODEM_EYE_SETUP            0x28
+#define PCINIT_FAX_V34_OPTIONS            0x29
+/*------------------------------------------------------------------*/
+#define PCINIT_MODEM_GUARD_TONE_NONE            0x00
+#define PCINIT_MODEM_GUARD_TONE_550HZ           0x01
+#define PCINIT_MODEM_GUARD_TONE_1800HZ          0x02
+#define PCINIT_MODEM_GUARD_TONE_CHOICES         0x03
+#define PCINIT_MODEMPROT_DISABLE_V42_V42BIS     0x0001
+#define PCINIT_MODEMPROT_DISABLE_MNP_MNP5       0x0002
+#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL       0x0004
+#define PCINIT_MODEMPROT_DISABLE_V42_DETECT     0x0008
+#define PCINIT_MODEMPROT_DISABLE_COMPRESSION    0x0010
+#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x0020
+#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_1200    0x0100
+#define PCINIT_MODEMPROT_BUFFER_IN_V42_DETECT   0x0200
+#define PCINIT_MODEMPROT_DISABLE_V42_SREJ       0x0400
+#define PCINIT_MODEMPROT_DISABLE_MNP3           0x0800
+#define PCINIT_MODEMPROT_DISABLE_MNP4           0x1000
+#define PCINIT_MODEMPROT_DISABLE_MNP10          0x2000
+#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V22BIS  0x4000
+#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V32BIS  0x8000
+#define PCINIT_MODEMCONFIG_LEASED_LINE_MODE     0x00000001L
+#define PCINIT_MODEMCONFIG_4_WIRE_OPERATION     0x00000002L
+#define PCINIT_MODEMCONFIG_DISABLE_BUSY_DETECT  0x00000004L
+#define PCINIT_MODEMCONFIG_DISABLE_CALLING_TONE 0x00000008L
+#define PCINIT_MODEMCONFIG_DISABLE_ANSWER_TONE  0x00000010L
+#define PCINIT_MODEMCONFIG_ENABLE_DIAL_TONE_DET 0x00000020L
+#define PCINIT_MODEMCONFIG_USE_POTS_INTERFACE   0x00000040L
+#define PCINIT_MODEMCONFIG_FORCE_RAY_TAYLOR_FAX 0x00000080L
+#define PCINIT_MODEMCONFIG_DISABLE_RETRAIN      0x00000100L
+#define PCINIT_MODEMCONFIG_DISABLE_STEPDOWN     0x00000200L
+#define PCINIT_MODEMCONFIG_DISABLE_SPLIT_SPEED  0x00000400L
+#define PCINIT_MODEMCONFIG_DISABLE_TRELLIS      0x00000800L
+#define PCINIT_MODEMCONFIG_ALLOW_RDL_TEST_LOOP  0x00001000L
+#define PCINIT_MODEMCONFIG_DISABLE_STEPUP       0x00002000L
+#define PCINIT_MODEMCONFIG_DISABLE_FLUSH_TIMER  0x00004000L
+#define PCINIT_MODEMCONFIG_REVERSE_DIRECTION    0x00008000L
+#define PCINIT_MODEMCONFIG_DISABLE_TX_REDUCTION 0x00010000L
+#define PCINIT_MODEMCONFIG_DISABLE_PRECODING    0x00020000L
+#define PCINIT_MODEMCONFIG_DISABLE_PREEMPHASIS  0x00040000L
+#define PCINIT_MODEMCONFIG_DISABLE_SHAPING      0x00080000L
+#define PCINIT_MODEMCONFIG_DISABLE_NONLINEAR_EN 0x00100000L
+#define PCINIT_MODEMCONFIG_DISABLE_MANUALREDUCT 0x00200000L
+#define PCINIT_MODEMCONFIG_DISABLE_16_POINT_TRN 0x00400000L
+#define PCINIT_MODEMCONFIG_DISABLE_2400_SYMBOLS 0x01000000L
+#define PCINIT_MODEMCONFIG_DISABLE_2743_SYMBOLS 0x02000000L
+#define PCINIT_MODEMCONFIG_DISABLE_2800_SYMBOLS 0x04000000L
+#define PCINIT_MODEMCONFIG_DISABLE_3000_SYMBOLS 0x08000000L
+#define PCINIT_MODEMCONFIG_DISABLE_3200_SYMBOLS 0x10000000L
+#define PCINIT_MODEMCONFIG_DISABLE_3429_SYMBOLS 0x20000000L
+#define PCINIT_MODEM_NEGOTIATE_HIGHEST          0x00
+#define PCINIT_MODEM_NEGOTIATE_DISABLED         0x01
+#define PCINIT_MODEM_NEGOTIATE_IN_CLASS         0x02
+#define PCINIT_MODEM_NEGOTIATE_V100             0x03
+#define PCINIT_MODEM_NEGOTIATE_V8               0x04
+#define PCINIT_MODEM_NEGOTIATE_V8BIS            0x05
+#define PCINIT_MODEM_NEGOTIATE_CHOICES          0x06
+#define PCINIT_MODEMMODULATION_DISABLE_V21      0x00000001L
+#define PCINIT_MODEMMODULATION_DISABLE_V23      0x00000002L
+#define PCINIT_MODEMMODULATION_DISABLE_V22      0x00000004L
+#define PCINIT_MODEMMODULATION_DISABLE_V22BIS   0x00000008L
+#define PCINIT_MODEMMODULATION_DISABLE_V32      0x00000010L
+#define PCINIT_MODEMMODULATION_DISABLE_V32BIS   0x00000020L
+#define PCINIT_MODEMMODULATION_DISABLE_V34      0x00000040L
+#define PCINIT_MODEMMODULATION_DISABLE_V90      0x00000080L
+#define PCINIT_MODEMMODULATION_DISABLE_BELL103  0x00000100L
+#define PCINIT_MODEMMODULATION_DISABLE_BELL212A 0x00000200L
+#define PCINIT_MODEMMODULATION_DISABLE_VFC      0x00000400L
+#define PCINIT_MODEMMODULATION_DISABLE_K56FLEX  0x00000800L
+#define PCINIT_MODEMMODULATION_DISABLE_X2       0x00001000L
+#define PCINIT_MODEMMODULATION_ENABLE_V29FDX    0x00010000L
+#define PCINIT_MODEMMODULATION_ENABLE_V33       0x00020000L
+#define PCINIT_MODEMMODULATION_ENABLE_V90A      0x00040000L
+#define PCINIT_MODEM_TRANSMIT_LEVEL_CHOICES     0x10
+#define PCINIT_MODEM_SPEAKER_OFF                0x00
+#define PCINIT_MODEM_SPEAKER_DURING_TRAIN       0x01
+#define PCINIT_MODEM_SPEAKER_TIL_CONNECT        0x02
+#define PCINIT_MODEM_SPEAKER_ALWAYS_ON          0x03
+#define PCINIT_MODEM_SPEAKER_CHOICES            0x04
+#define PCINIT_MODEM_SPEAKER_VOLUME_MIN         0x00
+#define PCINIT_MODEM_SPEAKER_VOLUME_LOW         0x01
+#define PCINIT_MODEM_SPEAKER_VOLUME_HIGH        0x02
+#define PCINIT_MODEM_SPEAKER_VOLUME_MAX         0x03
+#define PCINIT_MODEM_SPEAKER_VOLUME_CHOICES     0x04
+/*------------------------------------------------------------------*/
+#define PCINIT_FAXCONFIG_DISABLE_FINE           0x0001
+#define PCINIT_FAXCONFIG_DISABLE_ECM            0x0002
+#define PCINIT_FAXCONFIG_ECM_64_BYTES           0x0004
+#define PCINIT_FAXCONFIG_DISABLE_2D_CODING      0x0008
+#define PCINIT_FAXCONFIG_DISABLE_T6_CODING      0x0010
+#define PCINIT_FAXCONFIG_DISABLE_UNCOMPR        0x0020
+#define PCINIT_FAXCONFIG_REFUSE_POLLING         0x0040
+#define PCINIT_FAXCONFIG_HIDE_TOTAL_PAGES       0x0080
+#define PCINIT_FAXCONFIG_HIDE_ALL_HEADLINE      0x0100
+#define PCINIT_FAXCONFIG_HIDE_PAGE_INFO         0x0180
+#define PCINIT_FAXCONFIG_HEADLINE_OPTIONS_MASK  0x0180
+#define PCINIT_FAXCONFIG_DISABLE_FEATURE_FALLBACK 0x0200
+#define PCINIT_FAXCONFIG_V34FAX_CONTROL_RATE_1200 0x0800
+#define PCINIT_FAXCONFIG_DISABLE_V34FAX         0x1000
+#define PCINIT_FAXCONFIG_DISABLE_R8_0770_OR_200 0x01
+#define PCINIT_FAXCONFIG_DISABLE_R8_1540        0x02
+#define PCINIT_FAXCONFIG_DISABLE_R16_1540_OR_400 0x04
+#define PCINIT_FAXCONFIG_DISABLE_R4_0385_OR_100 0x08
+#define PCINIT_FAXCONFIG_DISABLE_300_300        0x10
+#define PCINIT_FAXCONFIG_DISABLE_INCH_BASED     0x40
+#define PCINIT_FAXCONFIG_DISABLE_METRIC_BASED   0x80
+#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A3       0
+#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_B4       1
+#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A4       2
+#define PCINIT_FAXCONFIG_REC_WIDTH_COUNT        3
+#define PCINIT_FAXCONFIG_REC_LENGTH_UNLIMITED   0
+#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_B4      1
+#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_A4      2
+#define PCINIT_FAXCONFIG_REC_LENGTH_COUNT       3
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_00_00_00 0
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_05_05_05 1
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_05_05 2
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_10 3
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_10 4
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_20 5
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_20 6
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_40 7
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_8    8
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_9    9
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_10   10
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_05 11
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_05 12
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_10 13
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_10 14
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_20 15
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_COUNT    16
+#define PCINIT_FAXCONFIG_DISABLE_TX_REDUCTION   0x00010000L
+#define PCINIT_FAXCONFIG_DISABLE_PRECODING      0x00020000L
+#define PCINIT_FAXCONFIG_DISABLE_PREEMPHASIS    0x00040000L
+#define PCINIT_FAXCONFIG_DISABLE_SHAPING        0x00080000L
+#define PCINIT_FAXCONFIG_DISABLE_NONLINEAR_EN   0x00100000L
+#define PCINIT_FAXCONFIG_DISABLE_MANUALREDUCT   0x00200000L
+#define PCINIT_FAXCONFIG_DISABLE_16_POINT_TRN   0x00400000L
+#define PCINIT_FAXCONFIG_DISABLE_2400_SYMBOLS   0x01000000L
+#define PCINIT_FAXCONFIG_DISABLE_2743_SYMBOLS   0x02000000L
+#define PCINIT_FAXCONFIG_DISABLE_2800_SYMBOLS   0x04000000L
+#define PCINIT_FAXCONFIG_DISABLE_3000_SYMBOLS   0x08000000L
+#define PCINIT_FAXCONFIG_DISABLE_3200_SYMBOLS   0x10000000L
+#define PCINIT_FAXCONFIG_DISABLE_3429_SYMBOLS   0x20000000L
+/*--------------------------------------------------------------------------*/
+#define PCINIT_XDI_CMA_FOR_ALL_NL_PRIMITIVES    0x01
+/*--------------------------------------------------------------------------*/
+#define PCINIT_FPGA_PLX_ACCESS_SUPPORTED        0x01
+/*--------------------------------------------------------------------------*/
+#endif
+/*--------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/pc_maint.h b/drivers/isdn/hardware/eicon/pc_maint.h
new file mode 100644
index 0000000..352ab8d
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pc_maint.h
@@ -0,0 +1,160 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 PLATFORM_GT_32BIT
+/* #define POINTER_32BIT byte * __ptr32 */
+#define POINTER_32BIT dword 
+#else
+#define POINTER_32BIT byte *
+#endif
+#if !defined(MIPS_SCOM)
+#define BUFFER_SZ  48
+#define MAINT_OFFS 0x380
+#else
+#define BUFFER_SZ  128
+#if defined(PRI)
+#define MAINT_OFFS 0xef00
+#else
+#define MAINT_OFFS 0xff00
+#endif
+#endif
+#define MIPS_BUFFER_SZ  128
+#if defined(PRI)
+#define MIPS_MAINT_OFFS 0xef00
+#else
+#define MIPS_MAINT_OFFS 0xff00
+#endif
+#define LOG                     1
+#define MEMR                    2
+#define MEMW                    3
+#define IOR                     4
+#define IOW                     5
+#define B1TEST                  6
+#define B2TEST                  7
+#define BTESTOFF                8
+#define DSIG_STATS              9
+#define B_CH_STATS              10
+#define D_CH_STATS              11
+#define BL1_STATS               12
+#define BL1_STATS_C             13
+#define GET_VERSION             14
+#define OS_STATS                15
+#define XLOG_SET_MASK           16
+#define XLOG_GET_MASK           17
+#define DSP_READ                20
+#define DSP_WRITE               21
+#define OK 0xff
+#define MORE_EVENTS 0xfe
+#define NO_EVENT 1
+struct DSigStruc
+{
+  byte Id;
+  byte u;
+  byte listen;
+  byte active;
+  byte sin[3];
+  byte bc[6];
+  byte llc[6];
+  byte hlc[6];
+  byte oad[20];
+};
+struct BL1Struc {
+  dword cx_b1;
+  dword cx_b2;
+  dword cr_b1;
+  dword cr_b2;
+  dword px_b1;
+  dword px_b2;
+  dword pr_b1;
+  dword pr_b2;
+  word er_b1;
+  word er_b2;
+};
+struct L2Struc {
+  dword XTotal;
+  dword RTotal;
+  word XError;
+  word RError;
+};
+struct OSStruc {
+  dword free_n;
+};
+typedef union
+{
+  struct DSigStruc DSigStats;
+  struct BL1Struc BL1Stats;
+  struct L2Struc L2Stats;
+  struct OSStruc OSStats;
+  byte   b[BUFFER_SZ];
+  word   w[BUFFER_SZ>>1];
+  word   l[BUFFER_SZ>>2]; /* word is wrong, do not use! Use 'd' instead. */
+  dword  d[BUFFER_SZ>>2];
+} BUFFER;
+typedef union
+{
+  struct DSigStruc DSigStats;
+  struct BL1Struc BL1Stats;
+  struct L2Struc L2Stats;
+  struct OSStruc OSStats;
+  byte   b[MIPS_BUFFER_SZ];
+  word   w[MIPS_BUFFER_SZ>>1];
+  word   l[BUFFER_SZ>>2]; /* word is wrong, do not use! Use 'd' instead. */
+  dword  d[MIPS_BUFFER_SZ>>2];
+} MIPS_BUFFER;
+#if !defined(MIPS_SCOM)
+struct pc_maint
+{
+  byte req;
+  byte rc;
+  POINTER_32BIT mem;
+  short length;
+  word port;
+  byte fill[6];
+  BUFFER data;
+};
+#else
+struct pc_maint
+{
+  byte req;
+  byte rc;
+  byte reserved[2];     /* R3000 alignment ... */
+  POINTER_32BIT mem;
+  short length;
+  word port;
+  byte fill[4];         /* data at offset 16   */
+  BUFFER data;
+};
+#endif
+struct mi_pc_maint
+{
+  byte req;
+  byte rc;
+  byte reserved[2];     /* R3000 alignment ... */
+  POINTER_32BIT mem;
+  short length;
+  word port;
+  byte fill[4];         /* data at offset 16   */
+  MIPS_BUFFER data;
+};
diff --git a/drivers/isdn/hardware/eicon/pkmaint.h b/drivers/isdn/hardware/eicon/pkmaint.h
new file mode 100644
index 0000000..722f85f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pkmaint.h
@@ -0,0 +1,44 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__
+#define __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__
+
+
+/*
+	Only one purpose of this compiler dependent file to pack
+	structures, described in pc_maint.h so that no padding
+	will be included.
+
+	With microsoft compile it is done by "pshpack1.h" and
+	after is restored by "poppack.h"
+	*/
+
+
+#include "pc_maint.h"
+
+
+#endif
+
diff --git a/drivers/isdn/hardware/eicon/platform.h b/drivers/isdn/hardware/eicon/platform.h
new file mode 100644
index 0000000..12b8ff2
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/platform.h
@@ -0,0 +1,394 @@
+/* $Id: platform.h,v 1.37.4.6 2005/01/31 12:22:20 armin Exp $
+ *
+ * platform.h
+ * 
+ *
+ * Copyright 2000-2003  by Armin Schindler (mac@melware.de)
+ * Copyright 2000  Eicon Networks 
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+
+#ifndef	__PLATFORM_H__
+#define	__PLATFORM_H__
+
+#if !defined(DIVA_BUILD)
+#define DIVA_BUILD "local"
+#endif
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <asm/types.h>
+#include <asm/io.h>
+
+#include "cardtype.h"
+
+/* activate debuglib for modules only */
+#ifndef MODULE
+#define DIVA_NO_DEBUGLIB
+#endif
+
+#define DIVA_INIT_FUNCTION  __init
+#define DIVA_EXIT_FUNCTION  __exit
+
+#define DIVA_USER_MODE_CARD_CONFIG 1
+#define	USE_EXTENDED_DEBUGS 1
+
+#define MAX_ADAPTER     32
+
+#define DIVA_ISTREAM 1
+
+#define MEMORY_SPACE_TYPE  0
+#define PORT_SPACE_TYPE    1
+
+
+#include <linux/string.h>
+
+#ifndef	byte
+#define	byte   u8
+#endif
+
+#ifndef	word
+#define	word   u16
+#endif
+
+#ifndef	dword
+#define	dword  u32
+#endif
+
+#ifndef	qword
+#define	qword  u64
+#endif
+
+#ifndef	TRUE
+#define	TRUE	1
+#endif
+
+#ifndef	FALSE
+#define	FALSE	0
+#endif
+
+#ifndef	NULL
+#define	NULL	((void *) 0)
+#endif
+
+#ifndef	MIN
+#define MIN(a,b)	((a)>(b) ? (b) : (a))
+#endif
+
+#ifndef	MAX
+#define MAX(a,b)	((a)>(b) ? (a) : (b))
+#endif
+
+#ifndef	far
+#define far
+#endif
+
+#ifndef	_pascal
+#define _pascal
+#endif
+
+#ifndef	_loadds
+#define _loadds
+#endif
+
+#ifndef	_cdecl
+#define _cdecl
+#endif
+
+#define MEM_TYPE_RAM		0
+#define MEM_TYPE_PORT		1
+#define MEM_TYPE_PROM		2
+#define MEM_TYPE_CTLREG		3
+#define MEM_TYPE_RESET		4
+#define MEM_TYPE_CFG		5
+#define MEM_TYPE_ADDRESS	6
+#define MEM_TYPE_CONFIG		7
+#define MEM_TYPE_CONTROL	8
+
+#define MAX_MEM_TYPE		10
+
+#define DIVA_OS_MEM_ATTACH_RAM(a)	((a)->ram)
+#define DIVA_OS_MEM_ATTACH_PORT(a)	((a)->port)
+#define DIVA_OS_MEM_ATTACH_PROM(a)	((a)->prom)
+#define DIVA_OS_MEM_ATTACH_CTLREG(a)	((a)->ctlReg)
+#define DIVA_OS_MEM_ATTACH_RESET(a)	((a)->reset)
+#define DIVA_OS_MEM_ATTACH_CFG(a)	((a)->cfg)
+#define DIVA_OS_MEM_ATTACH_ADDRESS(a)	((a)->Address)
+#define DIVA_OS_MEM_ATTACH_CONFIG(a)	((a)->Config)
+#define DIVA_OS_MEM_ATTACH_CONTROL(a)	((a)->Control)
+
+#define DIVA_OS_MEM_DETACH_RAM(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_PORT(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_PROM(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_CTLREG(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_RESET(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_CFG(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_ADDRESS(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_CONFIG(a, x)	do { } while(0)
+#define DIVA_OS_MEM_DETACH_CONTROL(a, x)	do { } while(0)
+
+#if !defined(DIM)
+#define DIM(array)  (sizeof (array)/sizeof ((array)[0]))
+#endif
+
+#define DIVA_INVALID_FILE_HANDLE  ((dword)(-1))
+
+#define DIVAS_CONTAINING_RECORD(address, type, field) \
+        ((type *)((char*)(address) - (char*)(&((type *)0)->field)))
+
+extern int sprintf(char *, const char*, ...);
+
+typedef void* LIST_ENTRY;
+
+typedef char    DEVICE_NAME[64];
+typedef struct _ISDN_ADAPTER   ISDN_ADAPTER;
+typedef struct _ISDN_ADAPTER* PISDN_ADAPTER;
+
+typedef void (* DIVA_DI_PRINTF) (unsigned char *, ...);
+#include "debuglib.h"
+
+#define dtrc(p) DBG_PRV0(p)
+#define dbug(a,p) DBG_PRV1(p)
+
+
+typedef struct e_info_s E_INFO ;
+
+typedef char diva_os_dependent_devica_name_t[64];
+typedef void* PDEVICE_OBJECT;
+
+struct _diva_os_soft_isr;
+struct _diva_os_timer;
+struct _ISDN_ADAPTER;
+
+void diva_log_info(unsigned char *, ...);
+
+/*
+**  XDI DIDD Interface
+*/
+void diva_xdi_didd_register_adapter (int card);
+void diva_xdi_didd_remove_adapter (int card);
+
+/*
+** memory allocation
+*/
+static __inline__ void* diva_os_malloc (unsigned long flags, unsigned long size)
+{
+	void *ret = NULL;
+
+	if (size) {
+		ret = (void *) vmalloc((unsigned int) size);
+	}
+	return (ret);
+}
+static __inline__ void  diva_os_free   (unsigned long flags, void* ptr)
+{
+	vfree(ptr);
+}
+
+/*
+** use skbuffs for message buffer
+*/
+typedef struct sk_buff diva_os_message_buffer_s;
+diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size, void **data_buf);
+void diva_os_free_message_buffer(diva_os_message_buffer_s *dmb);
+#define DIVA_MESSAGE_BUFFER_LEN(x) x->len
+#define DIVA_MESSAGE_BUFFER_DATA(x) x->data
+
+/*
+** mSeconds waiting
+*/
+static __inline__ void diva_os_sleep(dword mSec)
+{
+	msleep(mSec);
+}
+static __inline__ void diva_os_wait(dword mSec)
+{
+	mdelay(mSec);
+}
+
+/*
+**  PCI Configuration space access
+*/
+void PCIwrite (byte bus, byte func, int offset, void* data, int length, void* pci_dev_handle);
+void PCIread (byte bus, byte func, int offset, void* data, int length, void* pci_dev_handle);
+
+/*
+**  I/O Port utilities
+*/
+int diva_os_register_io_port (void *adapter, int register, unsigned long port,
+				unsigned long length, const char* name, int id);
+/*
+**  I/O port access abstraction
+*/
+byte inpp (void __iomem *);
+word inppw (void __iomem *);
+void inppw_buffer (void __iomem *, void*, int);
+void outppw (void __iomem *, word);
+void outppw_buffer (void __iomem * , void*, int);
+void outpp (void __iomem *, word);
+
+/*
+**  IRQ 
+*/
+typedef struct _diva_os_adapter_irq_info {
+        byte irq_nr;
+        int  registered;
+        char irq_name[24];
+} diva_os_adapter_irq_info_t;
+int diva_os_register_irq (void* context, byte irq, const char* name);
+void diva_os_remove_irq (void* context, byte irq);
+
+#define diva_os_in_irq() in_irq()
+
+/*
+**  Spin Lock framework
+*/
+typedef long diva_os_spin_lock_magic_t;
+typedef spinlock_t diva_os_spin_lock_t;
+static __inline__ int diva_os_initialize_spin_lock (spinlock_t *lock, void * unused) { \
+  spin_lock_init (lock); return(0); }
+static __inline__ void diva_os_enter_spin_lock (diva_os_spin_lock_t* a, \
+                              diva_os_spin_lock_magic_t* old_irql, \
+                              void* dbg) { spin_lock_bh(a); }
+static __inline__ void diva_os_leave_spin_lock (diva_os_spin_lock_t* a, \
+                              diva_os_spin_lock_magic_t* old_irql, \
+                              void* dbg) { spin_unlock_bh(a); }
+
+#define diva_os_destroy_spin_lock(a,b) do { } while(0)
+
+/*
+**  Deffered processing framework
+*/
+typedef int (*diva_os_isr_callback_t)(struct _ISDN_ADAPTER*);
+typedef void (*diva_os_soft_isr_callback_t)(struct _diva_os_soft_isr* psoft_isr, void* context);
+
+typedef struct _diva_os_soft_isr {
+  void* object;
+  diva_os_soft_isr_callback_t callback;
+  void* callback_context;
+  char dpc_thread_name[24];
+} diva_os_soft_isr_t;
+
+int diva_os_initialize_soft_isr (diva_os_soft_isr_t* psoft_isr, diva_os_soft_isr_callback_t callback, void*   callback_context);
+int diva_os_schedule_soft_isr (diva_os_soft_isr_t* psoft_isr);
+int diva_os_cancel_soft_isr (diva_os_soft_isr_t* psoft_isr);
+void diva_os_remove_soft_isr (diva_os_soft_isr_t* psoft_isr);
+
+/*
+  Get time service
+  */
+void diva_os_get_time (dword* sec, dword* usec);
+
+/*
+**  atomic operation, fake because we use threads
+*/
+typedef int diva_os_atomic_t;
+static diva_os_atomic_t __inline__
+diva_os_atomic_increment(diva_os_atomic_t* pv)
+{
+  *pv += 1;
+  return (*pv);
+}
+static diva_os_atomic_t __inline__
+diva_os_atomic_decrement(diva_os_atomic_t* pv)
+{
+  *pv -= 1;
+  return (*pv);
+}
+
+/* 
+**  CAPI SECTION
+*/
+#define NO_CORNETN
+#define IMPLEMENT_DTMF 1
+#define IMPLEMENT_ECHO_CANCELLER 1
+#define IMPLEMENT_RTP 1
+#define IMPLEMENT_T38 1
+#define IMPLEMENT_FAX_SUB_SEP_PWD 1
+#define IMPLEMENT_V18 1
+#define IMPLEMENT_DTMF_TONE 1
+#define IMPLEMENT_PIAFS 1
+#define IMPLEMENT_FAX_PAPER_FORMATS 1
+#define IMPLEMENT_VOWN 1
+#define IMPLEMENT_CAPIDTMF 1
+#define IMPLEMENT_FAX_NONSTANDARD 1
+#define VSWITCH_SUPPORT 1
+
+#define IMPLEMENT_MARKED_OK_AFTER_FC 1
+
+#define DIVA_IDI_RX_DMA 1
+
+/*
+** endian macros
+**
+** If only...  In some cases we did use them for endianness conversion;
+** unfortunately, other uses were real iomem accesses.
+*/
+#define READ_BYTE(addr)   readb(addr)
+#define READ_WORD(addr)   readw(addr)
+#define READ_DWORD(addr)  readl(addr)
+
+#define WRITE_BYTE(addr,v)  writeb(v,addr)
+#define WRITE_WORD(addr,v)  writew(v,addr)
+#define WRITE_DWORD(addr,v) writel(v,addr)
+
+static inline __u16 GET_WORD(void *addr)
+{
+	return le16_to_cpu(*(__le16 *)addr);
+}
+static inline __u32 GET_DWORD(void *addr)
+{
+	return le32_to_cpu(*(__le32 *)addr);
+}
+static inline void PUT_WORD(void *addr, __u16 v)
+{
+	*(__le16 *)addr = cpu_to_le16(v);
+}
+static inline void PUT_DWORD(void *addr, __u32 v)
+{
+	*(__le32 *)addr = cpu_to_le32(v);
+}
+
+/*
+** 32/64 bit macors
+*/
+#ifdef BITS_PER_LONG
+ #if BITS_PER_LONG > 32 
+  #define PLATFORM_GT_32BIT
+  #define ULongToPtr(x) (void *)(unsigned long)(x)
+ #endif
+#endif
+
+/*
+** undef os definitions of macros we use
+*/
+#undef ID_MASK
+#undef N_DATA
+#undef ADDR
+
+/*
+** dump file
+*/
+#define diva_os_dump_file_t char
+#define diva_os_board_trace_t char
+#define diva_os_dump_file(__x__) do { } while(0)
+
+/*
+** size of internal arrays
+*/
+#define MAX_DESCRIPTORS 64
+
+#endif	/* __PLATFORM_H__ */
diff --git a/drivers/isdn/hardware/eicon/pr_pc.h b/drivers/isdn/hardware/eicon/pr_pc.h
new file mode 100644
index 0000000..bf49a5a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pr_pc.h
@@ -0,0 +1,76 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+struct pr_ram {
+  word NextReq;         /* pointer to next Req Buffer               */
+  word NextRc;          /* pointer to next Rc Buffer                */
+  word NextInd;         /* pointer to next Ind Buffer               */
+  byte ReqInput;        /* number of Req Buffers sent               */
+  byte ReqOutput;       /* number of Req Buffers returned           */
+  byte ReqReserved;     /* number of Req Buffers reserved           */
+  byte Int;             /* ISDN-P interrupt                         */
+  byte XLock;           /* Lock field for arbitration               */
+  byte RcOutput;        /* number of Rc buffers received            */
+  byte IndOutput;       /* number of Ind buffers received           */
+  byte IMask;           /* Interrupt Mask Flag                      */
+  byte Reserved1[2];    /* reserved field, do not use               */
+  byte ReadyInt;        /* request field for ready interrupt        */
+  byte Reserved2[12];   /* reserved field, do not use               */
+  byte InterfaceType;   /* interface type 1=16K interface           */
+  word Signature;       /* ISDN-P initialized indication            */
+  byte B[1];            /* buffer space for Req,Ind and Rc          */
+};
+typedef struct {
+  word next;
+  byte Req;
+  byte ReqId;
+  byte ReqCh;
+  byte Reserved1;
+  word Reference;
+  byte Reserved[8];
+  PBUFFER XBuffer;
+} REQ;
+typedef struct {
+  word next;
+  byte Rc;
+  byte RcId;
+  byte RcCh;
+  byte Reserved1;
+  word Reference;
+  byte Reserved2[8];
+} RC;
+typedef struct {
+  word next;
+  byte Ind;
+  byte IndId;
+  byte IndCh;
+  byte MInd;
+  word MLength;
+  word Reference;
+  byte RNR;
+  byte Reserved;
+  dword Ack;
+  PBUFFER RBuffer;
+} IND;
diff --git a/drivers/isdn/hardware/eicon/s_4bri.c b/drivers/isdn/hardware/eicon/s_4bri.c
new file mode 100644
index 0000000..25c5d7f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/s_4bri.c
@@ -0,0 +1,510 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "divasync.h"
+#include "pc_init.h"
+#include "io.h"
+#include "helpers.h"
+#include "dsrv4bri.h"
+#include "dsp_defs.h"
+#include "sdp_hdr.h"
+
+/*****************************************************************************/
+#define	MAX_XLOG_SIZE	(64 * 1024)
+
+/* --------------------------------------------------------------------------
+		Recovery XLOG from QBRI Card
+	 -------------------------------------------------------------------------- */
+static void qBri_cpu_trapped (PISDN_ADAPTER IoAdapter) {
+	byte  __iomem *base ;
+	word *Xlog ;
+	dword   regs[4], TrapID, offset, size ;
+	Xdesc   xlogDesc ;
+	int factor = (IoAdapter->tasks == 1) ? 1 : 2;
+
+/*
+ *	check for trapped MIPS 46xx CPU, dump exception frame
+ */
+
+	base = DIVA_OS_MEM_ATTACH_CONTROL(IoAdapter);
+	offset = IoAdapter->ControllerNumber * (IoAdapter->MemorySize >> factor) ;
+
+	TrapID = READ_DWORD(&base[0x80]) ;
+
+	if ( (TrapID == 0x99999999) || (TrapID == 0x99999901) )
+	{
+		dump_trap_frame (IoAdapter, &base[0x90]) ;
+		IoAdapter->trapped = 1 ;
+	}
+
+	regs[0] = READ_DWORD((base + offset) + 0x70);
+	regs[1] = READ_DWORD((base + offset) + 0x74);
+	regs[2] = READ_DWORD((base + offset) + 0x78);
+	regs[3] = READ_DWORD((base + offset) + 0x7c);
+	regs[0] &= IoAdapter->MemorySize - 1 ;
+
+	if ( (regs[0] >= offset)
+	  && (regs[0] < offset + (IoAdapter->MemorySize >> factor) - 1) )
+	{
+		if ( !(Xlog = (word *)diva_os_malloc (0, MAX_XLOG_SIZE)) ) {
+			DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base);
+			return ;
+		}
+
+		size = offset + (IoAdapter->MemorySize >> factor) - regs[0] ;
+		if ( size > MAX_XLOG_SIZE )
+			size = MAX_XLOG_SIZE ;
+		memcpy_fromio (Xlog, &base[regs[0]], size) ;
+		xlogDesc.buf = Xlog ;
+		xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]) ;
+		xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]) ;
+		dump_xlog_buffer (IoAdapter, &xlogDesc) ;
+		diva_os_free (0, Xlog) ;
+		IoAdapter->trapped = 2 ;
+	}
+	DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base);
+}
+
+/* --------------------------------------------------------------------------
+		Reset QBRI Hardware
+	 -------------------------------------------------------------------------- */
+static void reset_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+	word volatile __iomem *qBriReset ;
+	byte  volatile __iomem *qBriCntrl ;
+	byte  volatile __iomem *p ;
+
+	qBriReset = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter);
+	WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_SOFT_RESET) ;
+	diva_os_wait (1) ;
+	WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_SOFT_RESET) ;
+	diva_os_wait (1);
+	WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_RELOAD_EEPROM) ;
+	diva_os_wait (1) ;
+	WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_RELOAD_EEPROM) ;
+	diva_os_wait (1);
+	DIVA_OS_MEM_DETACH_PROM(IoAdapter, qBriReset);
+
+	qBriCntrl = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	p = &qBriCntrl[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)];
+	WRITE_DWORD(p, 0) ;
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, qBriCntrl);
+
+	DBG_TRC(("resetted board @ reset addr 0x%08lx", qBriReset))
+	DBG_TRC(("resetted board @ cntrl addr 0x%08lx", p))
+}
+
+/* --------------------------------------------------------------------------
+		Start Card CPU
+	 -------------------------------------------------------------------------- */
+void start_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+	byte volatile __iomem *qBriReset ;
+	byte volatile __iomem *p ;
+
+	p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	qBriReset = &p[(DIVA_4BRI_REVISION(IoAdapter)) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)];
+	WRITE_DWORD(qBriReset, MQ_RISC_COLD_RESET_MASK) ;
+	diva_os_wait (2) ;
+	WRITE_DWORD(qBriReset, MQ_RISC_WARM_RESET_MASK | MQ_RISC_COLD_RESET_MASK) ;
+	diva_os_wait (10) ;
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+	DBG_TRC(("started processor @ addr 0x%08lx", qBriReset))
+}
+
+/* --------------------------------------------------------------------------
+		Stop Card CPU
+	 -------------------------------------------------------------------------- */
+static void stop_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+	byte volatile __iomem *p ;
+	dword volatile __iomem *qBriReset ;
+	dword volatile __iomem *qBriIrq ;
+	dword volatile __iomem *qBriIsacDspReset ;
+	int rev2 = DIVA_4BRI_REVISION(IoAdapter);
+	int reset_offset = rev2 ? (MQ2_BREG_RISC)      : (MQ_BREG_RISC);
+	int irq_offset   = rev2 ? (MQ2_BREG_IRQ_TEST)  : (MQ_BREG_IRQ_TEST);
+	int hw_offset    = rev2 ? (MQ2_ISAC_DSP_RESET) : (MQ_ISAC_DSP_RESET);
+
+	if ( IoAdapter->ControllerNumber > 0 )
+		return ;
+	p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	qBriReset = (dword volatile __iomem *)&p[reset_offset];
+	qBriIsacDspReset = (dword volatile __iomem *)&p[hw_offset];
+/*
+ *	clear interrupt line (reset Local Interrupt Test Register)
+ */
+	WRITE_DWORD(qBriReset, 0) ;
+ 	WRITE_DWORD(qBriIsacDspReset, 0) ;
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+	
+	p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+	WRITE_BYTE(&p[PLX9054_INTCSR], 0x00);	/* disable PCI interrupts */
+	DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+	
+	p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	qBriIrq   = (dword volatile __iomem *)&p[irq_offset];
+	WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF) ;
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+	DBG_TRC(("stopped processor @ addr 0x%08lx", qBriReset))
+
+}
+
+/* --------------------------------------------------------------------------
+		FPGA download
+	 -------------------------------------------------------------------------- */
+#define FPGA_NAME_OFFSET         0x10
+
+static byte * qBri_check_FPGAsrc (PISDN_ADAPTER IoAdapter, char *FileName,
+                                  dword *Length, dword *code) {
+	byte *File ;
+	char  *fpgaFile, *fpgaType, *fpgaDate, *fpgaTime ;
+	dword  fpgaFlen,  fpgaTlen,  fpgaDlen, cnt, year, i ;
+
+	if (!(File = (byte *)xdiLoadFile (FileName, Length, 0))) {
+		return (NULL) ;
+	}
+/*
+ *	 scan file until FF and put id string into buffer
+ */
+	for ( i = 0 ; File[i] != 0xff ; )
+	{
+		if ( ++i >= *Length )
+		{
+			DBG_FTL(("FPGA download: start of data header not found"))
+			xdiFreeFile (File) ;
+			return (NULL) ;
+		}
+	}
+	*code = i++ ;
+
+	if ( (File[i] & 0xF0) != 0x20 )
+	{
+		DBG_FTL(("FPGA download: data header corrupted"))
+		xdiFreeFile (File) ;
+		return (NULL) ;
+	}
+	fpgaFlen = (dword)  File[FPGA_NAME_OFFSET - 1] ;
+	if ( fpgaFlen == 0 )
+		fpgaFlen = 12 ;
+	fpgaFile = (char *)&File[FPGA_NAME_OFFSET] ;
+	fpgaTlen = (dword)  fpgaFile[fpgaFlen + 2] ;
+	if ( fpgaTlen == 0 )
+		fpgaTlen = 10 ;
+	fpgaType = (char *)&fpgaFile[fpgaFlen + 3] ;
+	fpgaDlen = (dword)  fpgaType[fpgaTlen + 2] ;
+	if ( fpgaDlen == 0 )
+		fpgaDlen = 11 ;
+	fpgaDate = (char *)&fpgaType[fpgaTlen + 3] ;
+	fpgaTime = (char *)&fpgaDate[fpgaDlen + 3] ;
+	cnt = (dword)(((File[  i  ] & 0x0F) << 20) + (File[i + 1] << 12)
+	             + (File[i + 2]         <<  4) + (File[i + 3] >>  4)) ;
+
+	if ( (dword)(i + (cnt / 8)) > *Length )
+	{
+		DBG_FTL(("FPGA download: '%s' file too small (%ld < %ld)",
+		         FileName, *Length, code + ((cnt + 7) / 8) ))
+		xdiFreeFile (File) ;
+		return (NULL) ;
+	}
+	i = 0 ;
+	do
+	{
+		while ( (fpgaDate[i] != '\0')
+		     && ((fpgaDate[i] < '0') || (fpgaDate[i] > '9')) )
+		{
+			i++;
+		}
+		year = 0 ;
+		while ( (fpgaDate[i] >= '0') && (fpgaDate[i] <= '9') )
+			year = year * 10 + (fpgaDate[i++] - '0') ;
+	} while ( (year < 2000) && (fpgaDate[i] != '\0') );
+
+	switch (IoAdapter->cardType) {
+		case CARDTYPE_DIVASRV_B_2F_PCI:
+			break;
+
+		default:
+	    if ( year >= 2001 ) {
+				IoAdapter->fpga_features |= PCINIT_FPGA_PLX_ACCESS_SUPPORTED ;
+			}
+	}
+
+	DBG_LOG(("FPGA[%s] file %s (%s %s) len %d",
+	         fpgaType, fpgaFile, fpgaDate, fpgaTime, cnt))
+	return (File) ;
+}
+
+/******************************************************************************/
+
+#define FPGA_PROG   0x0001		/* PROG enable low */
+#define FPGA_BUSY   0x0002		/* BUSY high, DONE low */
+#define	FPGA_CS     0x000C		/* Enable I/O pins */
+#define FPGA_CCLK   0x0100
+#define FPGA_DOUT   0x0400
+#define FPGA_DIN    FPGA_DOUT   /* bidirectional I/O */
+
+int qBri_FPGA_download (PISDN_ADAPTER IoAdapter) {
+	int            bit ;
+	byte           *File ;
+	dword          code, FileLength ;
+	word volatile __iomem *addr = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter);
+	word           val, baseval = FPGA_CS | FPGA_PROG ;
+
+
+
+	if (DIVA_4BRI_REVISION(IoAdapter))
+	{
+		char* name;
+
+		switch (IoAdapter->cardType) {
+			case CARDTYPE_DIVASRV_B_2F_PCI:
+				name = "dsbri2f.bit";
+				break;
+
+			case CARDTYPE_DIVASRV_B_2M_V2_PCI:
+			case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
+				name = "dsbri2m.bit";
+				break;
+
+			default:
+				name = "ds4bri2.bit";
+		}
+
+		File = qBri_check_FPGAsrc (IoAdapter, name,
+	                           		&FileLength, &code);
+	}
+	else
+	{
+		File = qBri_check_FPGAsrc (IoAdapter, "ds4bri.bit",
+		                           &FileLength, &code) ;
+	}
+	if ( !File ) {
+		DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
+		return (0) ;
+	}
+/*
+ *	prepare download, pulse PROGRAM pin down.
+ */
+	WRITE_WORD(addr, baseval & ~FPGA_PROG) ; /* PROGRAM low pulse */
+	WRITE_WORD(addr, baseval) ;              /* release */
+	diva_os_wait (50) ;  /* wait until FPGA finished internal memory clear */
+/*
+ *	check done pin, must be low
+ */
+	if ( READ_WORD(addr) & FPGA_BUSY )
+	{
+		DBG_FTL(("FPGA download: acknowledge for FPGA memory clear missing"))
+		xdiFreeFile (File) ;
+		DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
+		return (0) ;
+	}
+/*
+ *	put data onto the FPGA
+ */
+	while ( code < FileLength )
+	{
+		val = ((word)File[code++]) << 3 ;
+
+		for ( bit = 8 ; bit-- > 0 ; val <<= 1 ) /* put byte onto FPGA */
+		{
+			baseval &= ~FPGA_DOUT ;             /* clr  data bit */
+			baseval |= (val & FPGA_DOUT) ;      /* copy data bit */
+			WRITE_WORD(addr, baseval) ;
+			WRITE_WORD(addr, baseval | FPGA_CCLK) ;     /* set CCLK hi */
+			WRITE_WORD(addr, baseval | FPGA_CCLK) ;     /* set CCLK hi */
+			WRITE_WORD(addr, baseval) ;                 /* set CCLK lo */
+		}
+	}
+	xdiFreeFile (File) ;
+	diva_os_wait (100) ;
+	val = READ_WORD(addr) ;
+
+	DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
+
+	if ( !(val & FPGA_BUSY) )
+	{
+		DBG_FTL(("FPGA download: chip remains in busy state (0x%04x)", val))
+		return (0) ;
+	}
+
+	return (1) ;
+}
+
+static int load_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+	return (0);
+}
+
+/* --------------------------------------------------------------------------
+		Card ISR
+	 -------------------------------------------------------------------------- */
+static int qBri_ISR (struct _ISDN_ADAPTER* IoAdapter) {
+	dword volatile     __iomem *qBriIrq ;
+
+	PADAPTER_LIST_ENTRY QuadroList = IoAdapter->QuadroList ;
+
+	word              	i ;
+	int             	serviced = 0 ;
+	byte __iomem *p;
+
+	p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+
+	if ( !(READ_BYTE(&p[PLX9054_INTCSR]) & 0x80) ) {
+		DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+		return (0) ;
+	}
+	DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+
+/*
+ *	clear interrupt line (reset Local Interrupt Test Register)
+ */
+	p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST)  : (MQ_BREG_IRQ_TEST)]);
+	WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF) ;
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+	for ( i = 0 ; i < IoAdapter->tasks; ++i )
+	{
+		IoAdapter = QuadroList->QuadroAdapter[i] ;
+
+		if ( IoAdapter && IoAdapter->Initialized
+		  && IoAdapter->tst_irq (&IoAdapter->a) )
+		{
+			IoAdapter->IrqCount++ ;
+			serviced = 1 ;
+			diva_os_schedule_soft_isr (&IoAdapter->isr_soft_isr);
+		}
+	}
+
+	return (serviced) ;
+}
+
+/* --------------------------------------------------------------------------
+		Does disable the interrupt on the card
+	 -------------------------------------------------------------------------- */
+static void disable_qBri_interrupt (PISDN_ADAPTER IoAdapter) {
+	dword volatile __iomem *qBriIrq ;
+	byte __iomem *p;
+
+	if ( IoAdapter->ControllerNumber > 0 )
+		return ;
+/*
+ *	clear interrupt line (reset Local Interrupt Test Register)
+ */
+	p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+	WRITE_BYTE(&p[PLX9054_INTCSR], 0x00);	/* disable PCI interrupts */
+	DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+
+	p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+	qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST)  : (MQ_BREG_IRQ_TEST)]);
+	WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF) ;
+	DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+
+/* --------------------------------------------------------------------------
+		Install Adapter Entry Points
+	 -------------------------------------------------------------------------- */
+static void set_common_qBri_functions (PISDN_ADAPTER IoAdapter) {
+	ADAPTER *a;
+
+	a = &IoAdapter->a ;
+
+	a->ram_in           = mem_in ;
+	a->ram_inw          = mem_inw ;
+	a->ram_in_buffer    = mem_in_buffer ;
+	a->ram_look_ahead   = mem_look_ahead ;
+	a->ram_out          = mem_out ;
+	a->ram_outw         = mem_outw ;
+	a->ram_out_buffer   = mem_out_buffer ;
+	a->ram_inc          = mem_inc ;
+
+	IoAdapter->out      = pr_out ;
+	IoAdapter->dpc      = pr_dpc ;
+	IoAdapter->tst_irq  = scom_test_int ;
+	IoAdapter->clr_irq  = scom_clear_int ;
+	IoAdapter->pcm      = (struct pc_maint *)MIPS_MAINT_OFFS ;
+
+	IoAdapter->load     = load_qBri_hardware ;
+
+	IoAdapter->disIrq   = disable_qBri_interrupt ;
+	IoAdapter->rstFnc   = reset_qBri_hardware ;
+	IoAdapter->stop     = stop_qBri_hardware ;
+	IoAdapter->trapFnc  = qBri_cpu_trapped ;
+
+	IoAdapter->diva_isr_handler = qBri_ISR;
+
+	IoAdapter->a.io       = (void*)IoAdapter ;
+}
+
+static void set_qBri_functions (PISDN_ADAPTER IoAdapter) {
+	if (!IoAdapter->tasks) {
+		IoAdapter->tasks = MQ_INSTANCE_COUNT;
+	}
+	IoAdapter->MemorySize = MQ_MEMORY_SIZE ;
+	set_common_qBri_functions (IoAdapter) ;
+	diva_os_set_qBri_functions (IoAdapter) ;
+}
+
+static void set_qBri2_functions (PISDN_ADAPTER IoAdapter) {
+	if (!IoAdapter->tasks) {
+		IoAdapter->tasks = MQ_INSTANCE_COUNT;
+	}
+	IoAdapter->MemorySize = (IoAdapter->tasks == 1) ? BRI2_MEMORY_SIZE : MQ2_MEMORY_SIZE;
+	set_common_qBri_functions (IoAdapter) ;
+	diva_os_set_qBri2_functions (IoAdapter) ;
+}
+
+/******************************************************************************/
+
+void prepare_qBri_functions (PISDN_ADAPTER IoAdapter) {
+
+	set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[0]) ;
+	set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[1]) ;
+	set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[2]) ;
+	set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[3]) ;
+
+}
+
+void prepare_qBri2_functions (PISDN_ADAPTER IoAdapter) {
+	if (!IoAdapter->tasks) {
+		IoAdapter->tasks = MQ_INSTANCE_COUNT;
+	}
+
+	set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[0]) ;
+	if (IoAdapter->tasks > 1) {
+		set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[1]) ;
+		set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[2]) ;
+		set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[3]) ;
+	}
+
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/s_bri.c b/drivers/isdn/hardware/eicon/s_bri.c
new file mode 100644
index 0000000..5c87552
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/s_bri.c
@@ -0,0 +1,191 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "divasync.h"
+#include "io.h"
+#include "helpers.h"
+#include "dsrv_bri.h"
+#include "dsp_defs.h"
+/*****************************************************************************/
+#define MAX_XLOG_SIZE (64 * 1024)
+/* --------------------------------------------------------------------------
+  Investigate card state, recovery trace buffer
+  -------------------------------------------------------------------------- */
+static void bri_cpu_trapped (PISDN_ADAPTER IoAdapter) {
+ byte  __iomem *addrHi, *addrLo, *ioaddr ;
+ word *Xlog ;
+ dword   regs[4], i, size ;
+ Xdesc   xlogDesc ;
+ byte __iomem *Port;
+/*
+ * first read pointers and trap frame
+ */
+ if ( !(Xlog = (word *)diva_os_malloc (0, MAX_XLOG_SIZE)) )
+  return ;
+ Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+ addrHi =   Port + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH) ;
+ addrLo = Port + ADDR ;
+ ioaddr = Port + DATA ;
+ outpp (addrHi,  0) ;
+ outppw (addrLo, 0) ;
+ for ( i = 0 ; i < 0x100 ; Xlog[i++] = inppw(ioaddr) ) ;
+/*
+ * check for trapped MIPS 3xxx CPU, dump only exception frame
+ */
+ if ( GET_DWORD(&Xlog[0x80 / sizeof(Xlog[0])]) == 0x99999999 )
+ {
+  dump_trap_frame (IoAdapter, &((byte *)Xlog)[0x90]) ;
+  IoAdapter->trapped = 1 ;
+ }
+ regs[0] = GET_DWORD(&((byte *)Xlog)[0x70]);
+ regs[1] = GET_DWORD(&((byte *)Xlog)[0x74]);
+ regs[2] = GET_DWORD(&((byte *)Xlog)[0x78]);
+ regs[3] = GET_DWORD(&((byte *)Xlog)[0x7c]);
+ outpp (addrHi, (regs[1] >> 16) & 0x7F) ;
+ outppw (addrLo, regs[1] & 0xFFFF) ;
+ xlogDesc.cnt = inppw(ioaddr) ;
+ outpp (addrHi, (regs[2] >> 16) & 0x7F) ;
+ outppw (addrLo, regs[2] & 0xFFFF) ;
+ xlogDesc.out = inppw(ioaddr) ;
+ xlogDesc.buf = Xlog ;
+ regs[0] &= IoAdapter->MemorySize - 1 ;
+ if ( (regs[0] < IoAdapter->MemorySize - 1) )
+ {
+  size = IoAdapter->MemorySize - regs[0] ;
+  if ( size > MAX_XLOG_SIZE )
+   size = MAX_XLOG_SIZE ;
+  for ( i = 0 ; i < (size / sizeof(*Xlog)) ; regs[0] += 2 )
+  {
+   outpp (addrHi, (regs[0] >> 16) & 0x7F) ;
+   outppw (addrLo, regs[0] & 0xFFFF) ;
+   Xlog[i++] = inppw(ioaddr) ;
+  }
+  dump_xlog_buffer (IoAdapter, &xlogDesc) ;
+  diva_os_free (0, Xlog) ;
+  IoAdapter->trapped = 2 ;
+ }
+ outpp  (addrHi, (byte)((BRI_UNCACHED_ADDR (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+                                            BRI_SHARED_RAM_SIZE)) >> 16)) ;
+ outppw (addrLo, 0x00) ;
+ DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+}
+/* ---------------------------------------------------------------------
+   Reset hardware
+  --------------------------------------------------------------------- */
+static void reset_bri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ outpp (p, 0x00) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+/* ---------------------------------------------------------------------
+   Halt system
+  --------------------------------------------------------------------- */
+static void stop_bri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ if (p) {
+  outpp (p, 0x00) ; /* disable interrupts ! */
+ }
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ outpp (p, 0x00) ;    /* clear int, halt cpu */
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+static int load_bri_hardware (PISDN_ADAPTER IoAdapter) {
+ return (0);
+}
+/******************************************************************************/
+static int bri_ISR (struct _ISDN_ADAPTER* IoAdapter) {
+ byte __iomem *p;
+
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ if ( !(inpp (p) & 0x01) ) {
+  DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+  return (0) ;
+ }
+ /*
+  clear interrupt line
+  */
+ outpp (p, 0x08) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+ IoAdapter->IrqCount++ ;
+ if ( IoAdapter->Initialized ) {
+  diva_os_schedule_soft_isr (&IoAdapter->isr_soft_isr);
+ }
+ return (1) ;
+}
+/* --------------------------------------------------------------------------
+  Disable IRQ in the card hardware
+  -------------------------------------------------------------------------- */
+static void disable_bri_interrupt (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p;
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ if ( p )
+ {
+  outpp (p, 0x00) ; /* disable interrupts ! */
+ }
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ outpp (p, 0x00) ; /* clear int, halt cpu */
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+/* -------------------------------------------------------------------------
+  Fill card entry points
+  ------------------------------------------------------------------------- */
+void prepare_maestra_functions (PISDN_ADAPTER IoAdapter) {
+ ADAPTER *a = &IoAdapter->a ;
+ a->ram_in             = io_in ;
+ a->ram_inw            = io_inw ;
+ a->ram_in_buffer      = io_in_buffer ;
+ a->ram_look_ahead     = io_look_ahead ;
+ a->ram_out            = io_out ;
+ a->ram_outw           = io_outw ;
+ a->ram_out_buffer     = io_out_buffer ;
+ a->ram_inc            = io_inc ;
+ IoAdapter->MemoryBase = BRI_MEMORY_BASE ;
+ IoAdapter->MemorySize = BRI_MEMORY_SIZE ;
+ IoAdapter->out        = pr_out ;
+ IoAdapter->dpc        = pr_dpc ;
+ IoAdapter->tst_irq    = scom_test_int ;
+ IoAdapter->clr_irq    = scom_clear_int ;
+ IoAdapter->pcm        = (struct pc_maint *)MIPS_MAINT_OFFS ;
+ IoAdapter->load       = load_bri_hardware ;
+ IoAdapter->disIrq     = disable_bri_interrupt ;
+ IoAdapter->rstFnc     = reset_bri_hardware ;
+ IoAdapter->stop       = stop_bri_hardware ;
+ IoAdapter->trapFnc    = bri_cpu_trapped ;
+ IoAdapter->diva_isr_handler = bri_ISR;
+ /*
+  Prepare OS dependent functions
+  */
+ diva_os_prepare_maestra_functions (IoAdapter);
+}
+/* -------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/s_pri.c b/drivers/isdn/hardware/eicon/s_pri.c
new file mode 100644
index 0000000..18f2878
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/s_pri.c
@@ -0,0 +1,205 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "divasync.h"
+#include "io.h"
+#include "helpers.h"
+#include "dsrv_pri.h"
+#include "dsp_defs.h"
+/*****************************************************************************/
+#define MAX_XLOG_SIZE  (64 * 1024)
+/* -------------------------------------------------------------------------
+  Does return offset between ADAPTER->ram and real begin of memory
+  ------------------------------------------------------------------------- */
+static dword pri_ram_offset (ADAPTER* a) {
+ return ((dword)MP_SHARED_RAM_OFFSET);
+}
+/* -------------------------------------------------------------------------
+  Recovery XLOG buffer from the card
+  ------------------------------------------------------------------------- */
+static void pri_cpu_trapped (PISDN_ADAPTER IoAdapter) {
+ byte  __iomem *base ;
+ word *Xlog ;
+ dword   regs[4], TrapID, size ;
+ Xdesc   xlogDesc ;
+/*
+ * check for trapped MIPS 46xx CPU, dump exception frame
+ */
+ base   = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+ TrapID = READ_DWORD(&base[0x80]) ;
+ if ( (TrapID == 0x99999999) || (TrapID == 0x99999901) )
+ {
+  dump_trap_frame (IoAdapter, &base[0x90]) ;
+  IoAdapter->trapped = 1 ;
+ }
+ regs[0] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x70]);
+ regs[1] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x74]);
+ regs[2] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x78]);
+ regs[3] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x7c]);
+ regs[0] &= IoAdapter->MemorySize - 1 ;
+ if ( (regs[0] < IoAdapter->MemorySize - 1) )
+ {
+  if ( !(Xlog = (word *)diva_os_malloc (0, MAX_XLOG_SIZE)) ) {
+   DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base);
+   return ;
+  }
+  size = IoAdapter->MemorySize - regs[0] ;
+  if ( size > MAX_XLOG_SIZE )
+   size = MAX_XLOG_SIZE ;
+  memcpy_fromio(Xlog, &base[regs[0]], size) ;
+  xlogDesc.buf = Xlog ;
+  xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]) ;
+  xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]) ;
+  dump_xlog_buffer (IoAdapter, &xlogDesc) ;
+  diva_os_free (0, Xlog) ;
+  IoAdapter->trapped = 2 ;
+ }
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base);
+}
+/* -------------------------------------------------------------------------
+  Hardware reset of PRI card
+  ------------------------------------------------------------------------- */
+static void reset_pri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
+ diva_os_wait (50) ;
+ WRITE_BYTE(p, 0x00);
+ diva_os_wait (50) ;
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+}
+/* -------------------------------------------------------------------------
+  Stop Card Hardware
+  ------------------------------------------------------------------------- */
+static void stop_pri_hardware (PISDN_ADAPTER IoAdapter) {
+ dword i;
+ byte __iomem *p;
+ dword volatile __iomem *cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+ WRITE_DWORD(&cfgReg[3], 0);
+ WRITE_DWORD(&cfgReg[1], 0);
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
+ IoAdapter->a.ram_out (&IoAdapter->a, &RAM->SWReg, SWREG_HALT_CPU) ;
+ i = 0 ;
+ while ( (i < 100) && (IoAdapter->a.ram_in (&IoAdapter->a, &RAM->SWReg) != 0) )
+ {
+  diva_os_wait (1) ;
+  i++ ;
+ }
+ DBG_TRC(("%s: PRI stopped (%d)", IoAdapter->Name, i))
+ cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+ WRITE_DWORD(&cfgReg[0],((dword)(~0x03E00000)));
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
+ diva_os_wait (1) ;
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+}
+static int load_pri_hardware (PISDN_ADAPTER IoAdapter) {
+ return (0);
+}
+/* --------------------------------------------------------------------------
+  PRI Adapter interrupt Service Routine
+   -------------------------------------------------------------------------- */
+static int pri_ISR (struct _ISDN_ADAPTER* IoAdapter) {
+ byte __iomem *cfg = DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+ if ( !(READ_DWORD(cfg) & 0x80000000) ) {
+  DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg);
+  return (0) ;
+ }
+ /*
+  clear interrupt line
+  */
+ WRITE_DWORD(cfg, (dword)~0x03E00000) ;
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg);
+ IoAdapter->IrqCount++ ;
+ if ( IoAdapter->Initialized )
+ {
+  diva_os_schedule_soft_isr (&IoAdapter->isr_soft_isr);
+ }
+ return (1) ;
+}
+/* -------------------------------------------------------------------------
+  Disable interrupt in the card hardware
+  ------------------------------------------------------------------------- */
+static void disable_pri_interrupt (PISDN_ADAPTER IoAdapter) {
+ dword volatile __iomem *cfgReg = (dword volatile __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter) ;
+ WRITE_DWORD(&cfgReg[3], 0);
+ WRITE_DWORD(&cfgReg[1], 0);
+ WRITE_DWORD(&cfgReg[0], (dword)(~0x03E00000)) ;
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
+}
+/* -------------------------------------------------------------------------
+  Install entry points for PRI Adapter
+  ------------------------------------------------------------------------- */
+static void prepare_common_pri_functions (PISDN_ADAPTER IoAdapter) {
+ ADAPTER *a = &IoAdapter->a ;
+ a->ram_in           = mem_in ;
+ a->ram_inw          = mem_inw ;
+ a->ram_in_buffer    = mem_in_buffer ;
+ a->ram_look_ahead   = mem_look_ahead ;
+ a->ram_out          = mem_out ;
+ a->ram_outw         = mem_outw ;
+ a->ram_out_buffer   = mem_out_buffer ;
+ a->ram_inc          = mem_inc ;
+ a->ram_offset       = pri_ram_offset ;
+ a->ram_out_dw    = mem_out_dw;
+ a->ram_in_dw    = mem_in_dw;
+  a->istream_wakeup   = pr_stream;
+ IoAdapter->out      = pr_out ;
+ IoAdapter->dpc      = pr_dpc ;
+ IoAdapter->tst_irq  = scom_test_int ;
+ IoAdapter->clr_irq  = scom_clear_int ;
+ IoAdapter->pcm      = (struct pc_maint *)(MIPS_MAINT_OFFS
+                                        - MP_SHARED_RAM_OFFSET) ;
+ IoAdapter->load     = load_pri_hardware ;
+ IoAdapter->disIrq   = disable_pri_interrupt ;
+ IoAdapter->rstFnc   = reset_pri_hardware ;
+ IoAdapter->stop     = stop_pri_hardware ;
+ IoAdapter->trapFnc  = pri_cpu_trapped ;
+ IoAdapter->diva_isr_handler = pri_ISR;
+}
+/* -------------------------------------------------------------------------
+  Install entry points for PRI Adapter
+  ------------------------------------------------------------------------- */
+void prepare_pri_functions (PISDN_ADAPTER IoAdapter) {
+ IoAdapter->MemorySize = MP_MEMORY_SIZE ;
+ prepare_common_pri_functions (IoAdapter) ;
+ diva_os_prepare_pri_functions (IoAdapter);
+}
+/* -------------------------------------------------------------------------
+  Install entry points for PRI Rev.2 Adapter
+  ------------------------------------------------------------------------- */
+void prepare_pri2_functions (PISDN_ADAPTER IoAdapter) {
+ IoAdapter->MemorySize = MP2_MEMORY_SIZE ;
+ prepare_common_pri_functions (IoAdapter) ;
+ diva_os_prepare_pri2_functions (IoAdapter);
+}
+/* ------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/sdp_hdr.h b/drivers/isdn/hardware/eicon/sdp_hdr.h
new file mode 100644
index 0000000..8f61c69
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/sdp_hdr.h
@@ -0,0 +1,117 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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 __DIVA_SOFT_DSP_TASK_ENTRY_H__
+#define __DIVA_SOFT_DSP_TASK_ENTRY_H__
+/*
+ The soft DSP image is described by binary header contained on begin of this
+ image:
+OFFSET FROM IMAGE START |  VARIABLE
+------------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_LINK_OFFS   |  link to the next image
+  ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_GP_OFFS    |  image gp register value, void*
+  ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS   |  diva_mips_sdp_task_entry_t*
+  ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS |  image image start address (void*)
+  ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS |  image image end address   (void*)
+  ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS |  image id string char[...];
+  ----------------------------------------------------------------------
+ */
+#define DIVA_MIPS_TASK_IMAGE_LINK_OFFS   0x6C
+#define DIVA_MIPS_TASK_IMAGE_GP_OFFS    0x70
+#define DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS   0x74
+#define DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS 0x78
+#define DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS 0x7c
+#define DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS 0x80
+/*
+ This function is called in order to set GP register of this task
+ This function should be always called before any function of the
+ task is called
+ */
+typedef void (*diva_task_set_prog_gp_proc_t)(void* new_gp);
+/*
+ This function is called to clear .bss at task initialization step
+ */
+typedef void  (*diva_task_sys_reset_proc_t)(void);
+/*
+ This function is called in order to provide GP of master call to
+ task, that will be used by calls from the task to the master
+ */
+typedef void (*diva_task_set_main_gp_proc_t)(void* main_gp);
+/*
+ This function is called to provide address of 'dprintf' function
+ to the task
+ */
+typedef word (*diva_prt_proc_t)(char *, ...);
+typedef void (*diva_task_set_prt_proc_t)(diva_prt_proc_t fn);
+/*
+ This function is called to set task PID
+ */
+typedef void (*diva_task_set_pid_proc_t)(dword id);
+/*
+ This function is called for run-time task init
+ */
+typedef int (*diva_task_run_time_init_proc_t)(void*, dword);
+/*
+ This function is called from system scheduler or from timer
+ */
+typedef void (*diva_task_callback_proc_t)(void);
+/*
+ This callback is used by task to get current time im mS
+  */
+typedef dword (*diva_task_get_tick_count_proc_t)(void);
+typedef void (*diva_task_set_get_time_proc_t)(\
+                diva_task_get_tick_count_proc_t fn);
+typedef struct _diva_mips_sdp_task_entry {
+ diva_task_set_prog_gp_proc_t  set_gp_proc;
+ diva_task_sys_reset_proc_t   sys_reset_proc;
+ diva_task_set_main_gp_proc_t  set_main_gp_proc;
+ diva_task_set_prt_proc_t    set_dprintf_proc;
+ diva_task_set_pid_proc_t    set_pid_proc;
+ diva_task_run_time_init_proc_t run_time_init_proc;
+ diva_task_callback_proc_t    task_callback_proc;
+ diva_task_callback_proc_t    timer_callback_proc;
+ diva_task_set_get_time_proc_t  set_get_time_proc;
+ void*              last_entry_proc;
+} diva_mips_sdp_task_entry_t;
+/*
+ 'last_entry_proc' should be set to zero and is used for future extensuios
+ */
+typedef struct _diva_mips_sw_task {
+  diva_mips_sdp_task_entry_t  sdp_entry;
+  void*                       sdp_gp_reg;
+  void*                       own_gp_reg;
+} diva_mips_sw_task_t;
+#if !defined(DIVA_BRI2F_SDP_1_NAME)
+#define DIVA_BRI2F_SDP_1_NAME "sdp0.2q0"
+#endif
+#if !defined(DIVA_BRI2F_SDP_2_NAME)
+#define DIVA_BRI2F_SDP_2_NAME "sdp1.2q0"
+#endif
+#endif
diff --git a/drivers/isdn/hardware/eicon/um_idi.c b/drivers/isdn/hardware/eicon/um_idi.c
new file mode 100644
index 0000000..6563db9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/um_idi.c
@@ -0,0 +1,885 @@
+/* $Id: um_idi.c,v 1.14 2004/03/21 17:54:37 armin Exp $ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "dqueue.h"
+#include "adapter.h"
+#include "entity.h"
+#include "um_xdi.h"
+#include "um_idi.h"
+#include "debuglib.h"
+#include "divasync.h"
+
+#define DIVAS_MAX_XDI_ADAPTERS	64
+
+/* --------------------------------------------------------------------------
+		IMPORTS
+   -------------------------------------------------------------------------- */
+extern void diva_os_wakeup_read(void *os_context);
+extern void diva_os_wakeup_close(void *os_context);
+/* --------------------------------------------------------------------------
+		LOCALS
+   -------------------------------------------------------------------------- */
+static LIST_HEAD(adapter_q);
+static diva_os_spin_lock_t adapter_lock;
+
+static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr);
+static void cleanup_adapter(diva_um_idi_adapter_t * a);
+static void cleanup_entity(divas_um_idi_entity_t * e);
+static int diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t * a,
+					       diva_um_idi_adapter_features_t
+					       * features);
+static int process_idi_request(divas_um_idi_entity_t * e,
+			       const diva_um_idi_req_hdr_t * req);
+static int process_idi_rc(divas_um_idi_entity_t * e, byte rc);
+static int process_idi_ind(divas_um_idi_entity_t * e, byte ind);
+static int write_return_code(divas_um_idi_entity_t * e, byte rc);
+
+/* --------------------------------------------------------------------------
+		MAIN
+   -------------------------------------------------------------------------- */
+int diva_user_mode_idi_init(void)
+{
+	diva_os_initialize_spin_lock(&adapter_lock, "adapter");
+	return (0);
+}
+
+/* --------------------------------------------------------------------------
+		Copy adapter features to user supplied buffer
+   -------------------------------------------------------------------------- */
+static int
+diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t * a,
+				    diva_um_idi_adapter_features_t *
+				    features)
+{
+	IDI_SYNC_REQ sync_req;
+
+	if ((a) && (a->d.request)) {
+		features->type = a->d.type;
+		features->features = a->d.features;
+		features->channels = a->d.channels;
+		memset(features->name, 0, sizeof(features->name));
+
+		sync_req.GetName.Req = 0;
+		sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME;
+		(*(a->d.request)) ((ENTITY *) & sync_req);
+		strlcpy(features->name, sync_req.GetName.name,
+			sizeof(features->name));
+
+		sync_req.GetSerial.Req = 0;
+		sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
+		sync_req.GetSerial.serial = 0;
+		(*(a->d.request)) ((ENTITY *) & sync_req);
+		features->serial_number = sync_req.GetSerial.serial;
+	}
+
+	return ((a) ? 0 : -1);
+}
+
+/* --------------------------------------------------------------------------
+		REMOVE ADAPTER
+   -------------------------------------------------------------------------- */
+void diva_user_mode_idi_remove_adapter(int adapter_nr)
+{
+	struct list_head *tmp;
+	diva_um_idi_adapter_t *a;
+
+	list_for_each(tmp, &adapter_q) {
+		a = list_entry(tmp, diva_um_idi_adapter_t, link);
+		if (a->adapter_nr == adapter_nr) {
+			list_del(tmp);
+			cleanup_adapter(a);
+			DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr));
+			diva_os_free(0, a);
+			break;
+		}
+	}
+}
+
+/* --------------------------------------------------------------------------
+		CALLED ON DRIVER EXIT (UNLOAD)
+   -------------------------------------------------------------------------- */
+void diva_user_mode_idi_finit(void)
+{
+	struct list_head *tmp, *safe;
+	diva_um_idi_adapter_t *a;
+
+	list_for_each_safe(tmp, safe, &adapter_q) {
+		a = list_entry(tmp, diva_um_idi_adapter_t, link);
+		list_del(tmp);
+		cleanup_adapter(a);
+		DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr));
+		diva_os_free(0, a);
+	}
+	diva_os_destroy_spin_lock(&adapter_lock, "adapter");
+}
+
+/* -------------------------------------------------------------------------
+		CREATE AND INIT IDI ADAPTER
+	 ------------------------------------------------------------------------- */
+int diva_user_mode_idi_create_adapter(const DESCRIPTOR * d, int adapter_nr)
+{
+	diva_os_spin_lock_magic_t old_irql;
+	diva_um_idi_adapter_t *a =
+	    (diva_um_idi_adapter_t *) diva_os_malloc(0,
+						     sizeof
+						     (diva_um_idi_adapter_t));
+
+	if (!a) {
+		return (-1);
+	}
+	memset(a, 0x00, sizeof(*a));
+	INIT_LIST_HEAD(&a->entity_q);
+
+	a->d = *d;
+	a->adapter_nr = adapter_nr;
+
+	DBG_LOG(("DIDD_ADD A(%d), type:%02x, features:%04x, channels:%d",
+		 adapter_nr, a->d.type, a->d.features, a->d.channels));
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_adapter");
+	list_add_tail(&a->link, &adapter_q);
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_adapter");
+	return (0);
+}
+
+/* ------------------------------------------------------------------------
+			Find adapter by Adapter number
+   ------------------------------------------------------------------------ */
+static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr)
+{
+	diva_um_idi_adapter_t *a = NULL;
+	struct list_head *tmp;
+
+	list_for_each(tmp, &adapter_q) {
+		a = list_entry(tmp, diva_um_idi_adapter_t, link);
+		DBG_TRC(("find_adapter: (%d)-(%d)", nr, a->adapter_nr));
+		if (a->adapter_nr == (int)nr)
+			break;
+		a = NULL;
+	}
+	return(a);
+}
+
+/* ------------------------------------------------------------------------
+		Cleanup this adapter and cleanup/delete all entities assigned
+		to this adapter
+   ------------------------------------------------------------------------ */
+static void cleanup_adapter(diva_um_idi_adapter_t * a)
+{
+	struct list_head *tmp, *safe;
+	divas_um_idi_entity_t *e;
+
+	list_for_each_safe(tmp, safe, &a->entity_q) {
+		e = list_entry(tmp, divas_um_idi_entity_t, link);
+		list_del(tmp);
+		cleanup_entity(e);
+		if (e->os_context) {
+			diva_os_wakeup_read(e->os_context);
+			diva_os_wakeup_close(e->os_context);
+		}
+	}
+	memset(&a->d, 0x00, sizeof(DESCRIPTOR));
+}
+
+/* ------------------------------------------------------------------------
+		Cleanup, but NOT delete this entity
+   ------------------------------------------------------------------------ */
+static void cleanup_entity(divas_um_idi_entity_t * e)
+{
+	e->os_ref = NULL;
+	e->status = 0;
+	e->adapter = NULL;
+	e->e.Id = 0;
+	e->rc_count = 0;
+
+	e->status |= DIVA_UM_IDI_REMOVED;
+	e->status |= DIVA_UM_IDI_REMOVE_PENDING;
+
+	diva_data_q_finit(&e->data);
+	diva_data_q_finit(&e->rc);
+}
+
+
+/* ------------------------------------------------------------------------
+		Create ENTITY, link it to the adapter and remove pointer to entity
+   ------------------------------------------------------------------------ */
+void *divas_um_idi_create_entity(dword adapter_nr, void *file)
+{
+	divas_um_idi_entity_t *e;
+	diva_um_idi_adapter_t *a;
+	diva_os_spin_lock_magic_t old_irql;
+
+	if ((e = (divas_um_idi_entity_t *) diva_os_malloc(0, sizeof(*e)))) {
+		memset(e, 0x00, sizeof(*e));
+		if (!
+		    (e->os_context =
+		     diva_os_malloc(0, diva_os_get_context_size()))) {
+			DBG_LOG(("E(%08x) no memory for os context", e));
+			diva_os_free(0, e);
+			return NULL;
+		}
+		memset(e->os_context, 0x00, diva_os_get_context_size());
+
+		if ((diva_data_q_init(&e->data, 2048 + 512, 16))) {
+			diva_os_free(0, e->os_context);
+			diva_os_free(0, e);
+			return NULL;
+		}
+		if ((diva_data_q_init(&e->rc, sizeof(diva_um_idi_ind_hdr_t), 2))) {
+			diva_data_q_finit(&e->data);
+			diva_os_free(0, e->os_context);
+			diva_os_free(0, e);
+			return NULL;
+		}
+
+		diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_entity");
+		/*
+		   Look for Adapter requested
+		 */
+		if (!(a = diva_um_idi_find_adapter(adapter_nr))) {
+			/*
+			   No adapter was found, or this adapter was removed
+			 */
+			diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity");
+
+			DBG_LOG(("A: no adapter(%ld)", adapter_nr));
+
+			cleanup_entity(e);
+			diva_os_free(0, e->os_context);
+			diva_os_free(0, e);
+
+			return NULL;
+		}
+
+		e->os_ref = file;	/* link to os handle */
+		e->adapter = a;	/* link to adapter   */
+
+		list_add_tail(&e->link, &a->entity_q);	/* link from adapter */
+
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity");
+
+		DBG_LOG(("A(%ld), create E(%08x)", adapter_nr, e));
+	}
+
+	return (e);
+}
+
+/* ------------------------------------------------------------------------
+		Unlink entity and free memory 
+   ------------------------------------------------------------------------ */
+int divas_um_idi_delete_entity(int adapter_nr, void *entity)
+{
+	divas_um_idi_entity_t *e;
+	diva_um_idi_adapter_t *a;
+	diva_os_spin_lock_magic_t old_irql;
+
+	if (!(e = (divas_um_idi_entity_t *) entity))
+		return (-1);
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "delete_entity");
+	if ((a = e->adapter)) {
+		list_del(&e->link);
+	}
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "delete_entity");
+
+	diva_um_idi_stop_wdog(entity);
+	cleanup_entity(e);
+	diva_os_free(0, e->os_context);
+	memset(e, 0x00, sizeof(*e));
+	diva_os_free(0, e);
+
+	DBG_LOG(("A(%d) remove E:%08x", adapter_nr, e));
+
+	return (0);
+}
+
+/* --------------------------------------------------------------------------
+		Called by application to read data from IDI
+	 -------------------------------------------------------------------------- */
+int diva_um_idi_read(void *entity,
+		     void *os_handle,
+		     void *dst,
+		     int max_length, divas_um_idi_copy_to_user_fn_t cp_fn)
+{
+	divas_um_idi_entity_t *e;
+	diva_um_idi_adapter_t *a;
+	const void *data;
+	int length, ret = 0;
+	diva_um_idi_data_queue_t *q;
+	diva_os_spin_lock_magic_t old_irql;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "read");
+
+	e = (divas_um_idi_entity_t *) entity;
+	if (!e || (!(a = e->adapter)) ||
+	    (e->status & DIVA_UM_IDI_REMOVE_PENDING) ||
+	    (e->status & DIVA_UM_IDI_REMOVED) ||
+	    (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read");
+		DBG_ERR(("E(%08x) read failed - adapter removed", e))
+		return (-1);
+	}
+
+	DBG_TRC(("A(%d) E(%08x) read(%d)", a->adapter_nr, e, max_length));
+
+	/*
+	   Try to read return code first
+	 */
+	data = diva_data_q_get_segment4read(&e->rc);
+	q = &e->rc;
+
+	/*
+	   No return codes available, read indications now
+	 */
+	if (!data) {
+		if (!(e->status & DIVA_UM_IDI_RC_PENDING)) {
+			DBG_TRC(("A(%d) E(%08x) read data", a->adapter_nr, e));
+			data = diva_data_q_get_segment4read(&e->data);
+			q = &e->data;
+		}
+	} else {
+		e->status &= ~DIVA_UM_IDI_RC_PENDING;
+		DBG_TRC(("A(%d) E(%08x) read rc", a->adapter_nr, e));
+	}
+
+	if (data) {
+		if ((length = diva_data_q_get_segment_length(q)) >
+		    max_length) {
+			/*
+			   Not enough space to read message
+			 */
+			DBG_ERR(("A: A(%d) E(%08x) read small buffer",
+				 a->adapter_nr, e, ret));
+			diva_os_leave_spin_lock(&adapter_lock, &old_irql,
+						"read");
+			return (-2);
+		}
+		/*
+		   Copy it to user, this function does access ONLY locked an verified
+		   memory, also we can access it witch spin lock held
+		 */
+
+		if ((ret = (*cp_fn) (os_handle, dst, data, length)) >= 0) {
+			/*
+			   Acknowledge only if read was successfull
+			 */
+			diva_data_q_ack_segment4read(q);
+		}
+	}
+
+
+	DBG_TRC(("A(%d) E(%08x) read=%d", a->adapter_nr, e, ret));
+
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read");
+
+	return (ret);
+}
+
+
+int diva_um_idi_write(void *entity,
+		      void *os_handle,
+		      const void *src,
+		      int length, divas_um_idi_copy_from_user_fn_t cp_fn)
+{
+	divas_um_idi_entity_t *e;
+	diva_um_idi_adapter_t *a;
+	diva_um_idi_req_hdr_t *req;
+	void *data;
+	int ret = 0;
+	diva_os_spin_lock_magic_t old_irql;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "write");
+
+	e = (divas_um_idi_entity_t *) entity;
+	if (!e || (!(a = e->adapter)) ||
+	    (e->status & DIVA_UM_IDI_REMOVE_PENDING) ||
+	    (e->status & DIVA_UM_IDI_REMOVED) ||
+	    (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+		DBG_ERR(("E(%08x) write failed - adapter removed", e))
+		return (-1);
+	}
+
+	DBG_TRC(("A(%d) E(%08x) write(%d)", a->adapter_nr, e, length));
+
+	if ((length < sizeof(*req)) || (length > sizeof(e->buffer))) {
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+		return (-2);
+	}
+
+	if (e->status & DIVA_UM_IDI_RC_PENDING) {
+		DBG_ERR(("A: A(%d) E(%08x) rc pending", a->adapter_nr, e));
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+		return (-1);	/* should wait for RC code first */
+	}
+
+	/*
+	   Copy function does access only locked verified memory,
+	   also it can be called with spin lock held
+	 */
+	if ((ret = (*cp_fn) (os_handle, e->buffer, src, length)) < 0) {
+		DBG_TRC(("A: A(%d) E(%08x) write error=%d", a->adapter_nr,
+			 e, ret));
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+		return (ret);
+	}
+
+	req = (diva_um_idi_req_hdr_t *) & e->buffer[0];
+
+	switch (req->type) {
+	case DIVA_UM_IDI_GET_FEATURES:{
+			DBG_LOG(("A(%d) get_features", a->adapter_nr));
+			if (!(data =
+			     diva_data_q_get_segment4write(&e->data))) {
+				DBG_ERR(("A(%d) get_features, no free buffer",
+					 a->adapter_nr));
+				diva_os_leave_spin_lock(&adapter_lock,
+							&old_irql,
+							"write");
+				return (0);
+			}
+			diva_user_mode_idi_adapter_features(a, &(((diva_um_idi_ind_hdr_t
+								*) data)->hdr.features));
+			((diva_um_idi_ind_hdr_t *) data)->type =
+			    DIVA_UM_IDI_IND_FEATURES;
+			((diva_um_idi_ind_hdr_t *) data)->data_length = 0;
+			diva_data_q_ack_segment4write(&e->data,
+						      sizeof(diva_um_idi_ind_hdr_t));
+
+			diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+
+			diva_os_wakeup_read(e->os_context);
+		}
+		break;
+
+	case DIVA_UM_IDI_REQ:
+	case DIVA_UM_IDI_REQ_MAN:
+	case DIVA_UM_IDI_REQ_SIG:
+	case DIVA_UM_IDI_REQ_NET:
+		DBG_TRC(("A(%d) REQ(%02d)-(%02d)-(%08x)", a->adapter_nr,
+			 req->Req, req->ReqCh,
+			 req->type & DIVA_UM_IDI_REQ_TYPE_MASK));
+		switch (process_idi_request(e, req)) {
+		case -1:
+			diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+			return (-1);
+		case -2:
+			diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+			diva_os_wakeup_read(e->os_context);
+			break;
+		default:
+			diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+			break;
+		}
+		break;
+
+	default:
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+		return (-1);
+	}
+
+	DBG_TRC(("A(%d) E(%08x) write=%d", a->adapter_nr, e, ret));
+
+	return (ret);
+}
+
+/* --------------------------------------------------------------------------
+			CALLBACK FROM XDI
+	 -------------------------------------------------------------------------- */
+static void diva_um_idi_xdi_callback(ENTITY * entity)
+{
+	divas_um_idi_entity_t *e = DIVAS_CONTAINING_RECORD(entity,
+							   divas_um_idi_entity_t,
+							   e);
+	diva_os_spin_lock_magic_t old_irql;
+	int call_wakeup = 0;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
+
+	if (e->e.complete == 255) {
+		if (!(e->status & DIVA_UM_IDI_REMOVE_PENDING)) {
+			diva_um_idi_stop_wdog(e);
+		}
+		if ((call_wakeup = process_idi_rc(e, e->e.Rc))) {
+			if (e->rc_count) {
+				e->rc_count--;
+			}
+		}
+		e->e.Rc = 0;
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
+
+		if (call_wakeup) {
+			diva_os_wakeup_read(e->os_context);
+			diva_os_wakeup_close(e->os_context);
+		}
+	} else {
+		if (e->status & DIVA_UM_IDI_REMOVE_PENDING) {
+			e->e.RNum = 0;
+			e->e.RNR = 2;
+		} else {
+			call_wakeup = process_idi_ind(e, e->e.Ind);
+		}
+		e->e.Ind = 0;
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
+		if (call_wakeup) {
+			diva_os_wakeup_read(e->os_context);
+		}
+	}
+}
+
+static int process_idi_request(divas_um_idi_entity_t * e,
+			       const diva_um_idi_req_hdr_t * req)
+{
+	int assign = 0;
+	byte Req = (byte) req->Req;
+	dword type = req->type & DIVA_UM_IDI_REQ_TYPE_MASK;
+
+	if (!e->e.Id || !e->e.callback) {	/* not assigned */
+		if (Req != ASSIGN) {
+			DBG_ERR(("A: A(%d) E(%08x) not assigned",
+				 e->adapter->adapter_nr, e));
+			return (-1);	/* NOT ASSIGNED */
+		} else {
+			switch (type) {
+			case DIVA_UM_IDI_REQ_TYPE_MAN:
+				e->e.Id = MAN_ID;
+				DBG_TRC(("A(%d) E(%08x) assign MAN",
+					 e->adapter->adapter_nr, e));
+				break;
+
+			case DIVA_UM_IDI_REQ_TYPE_SIG:
+				e->e.Id = DSIG_ID;
+				DBG_TRC(("A(%d) E(%08x) assign SIG",
+					 e->adapter->adapter_nr, e));
+				break;
+
+			case DIVA_UM_IDI_REQ_TYPE_NET:
+				e->e.Id = NL_ID;
+				DBG_TRC(("A(%d) E(%08x) assign NET",
+					 e->adapter->adapter_nr, e));
+				break;
+
+			default:
+				DBG_ERR(("A: A(%d) E(%08x) unknown type=%08x",
+					 e->adapter->adapter_nr, e,
+					 type));
+				return (-1);
+			}
+		}
+		e->e.XNum = 1;
+		e->e.RNum = 1;
+		e->e.callback = diva_um_idi_xdi_callback;
+		e->e.X = &e->XData;
+		e->e.R = &e->RData;
+		assign = 1;
+	}
+	e->status |= DIVA_UM_IDI_RC_PENDING;
+	e->e.Req = Req;
+	e->e.ReqCh = (byte) req->ReqCh;
+	e->e.X->PLength = (word) req->data_length;
+	e->e.X->P = (byte *) & req[1];	/* Our buffer is safe */
+
+	DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))",
+		 e->adapter->adapter_nr, e, e->e.Id, e->e.Req,
+		 e->e.ReqCh, e->e.X->PLength));
+
+	e->rc_count++;
+
+	if (e->adapter && e->adapter->d.request) {
+		diva_um_idi_start_wdog(e);
+		(*(e->adapter->d.request)) (&e->e);
+	}
+
+	if (assign) {
+		if (e->e.Rc == OUT_OF_RESOURCES) {
+			/*
+			   XDI has no entities more, call was not forwarded to the card,
+			   no callback will be scheduled
+			 */
+			DBG_ERR(("A: A(%d) E(%08x) XDI out of entities",
+				 e->adapter->adapter_nr, e));
+
+			e->e.Id = 0;
+			e->e.ReqCh = 0;
+			e->e.RcCh = 0;
+			e->e.Ind = 0;
+			e->e.IndCh = 0;
+			e->e.XNum = 0;
+			e->e.RNum = 0;
+			e->e.callback = NULL;
+			e->e.X = NULL;
+			e->e.R = NULL;
+			write_return_code(e, ASSIGN_RC | OUT_OF_RESOURCES);
+			return (-2);
+		} else {
+			e->status |= DIVA_UM_IDI_ASSIGN_PENDING;
+		}
+	}
+
+	return (0);
+}
+
+static int process_idi_rc(divas_um_idi_entity_t * e, byte rc)
+{
+	DBG_TRC(("A(%d) E(%08x) rc(%02x-%02x-%02x)",
+		 e->adapter->adapter_nr, e, e->e.Id, rc, e->e.RcCh));
+
+	if (e->status & DIVA_UM_IDI_ASSIGN_PENDING) {
+		e->status &= ~DIVA_UM_IDI_ASSIGN_PENDING;
+		if (rc != ASSIGN_OK) {
+			DBG_ERR(("A: A(%d) E(%08x) ASSIGN failed",
+				 e->adapter->adapter_nr, e));
+			e->e.callback = NULL;
+			e->e.Id = 0;
+			e->e.Req = 0;
+			e->e.ReqCh = 0;
+			e->e.Rc = 0;
+			e->e.RcCh = 0;
+			e->e.Ind = 0;
+			e->e.IndCh = 0;
+			e->e.X = NULL;
+			e->e.R = NULL;
+			e->e.XNum = 0;
+			e->e.RNum = 0;
+		}
+	}
+	if ((e->e.Req == REMOVE) && e->e.Id && (rc == 0xff)) {
+		DBG_ERR(("A: A(%d) E(%08x)  discard OK in REMOVE",
+			 e->adapter->adapter_nr, e));
+		return (0);	/* let us do it in the driver */
+	}
+	if ((e->e.Req == REMOVE) && (!e->e.Id)) {	/* REMOVE COMPLETE */
+		e->e.callback = NULL;
+		e->e.Id = 0;
+		e->e.Req = 0;
+		e->e.ReqCh = 0;
+		e->e.Rc = 0;
+		e->e.RcCh = 0;
+		e->e.Ind = 0;
+		e->e.IndCh = 0;
+		e->e.X = NULL;
+		e->e.R = NULL;
+		e->e.XNum = 0;
+		e->e.RNum = 0;
+		e->rc_count = 0;
+	}
+	if ((e->e.Req == REMOVE) && (rc != 0xff)) {	/* REMOVE FAILED */
+		DBG_ERR(("A: A(%d) E(%08x)  REMOVE FAILED",
+			 e->adapter->adapter_nr, e));
+	}
+	write_return_code(e, rc);
+
+	return (1);
+}
+
+static int process_idi_ind(divas_um_idi_entity_t * e, byte ind)
+{
+	int do_wakeup = 0;
+
+	if (e->e.complete != 0x02) {
+		diva_um_idi_ind_hdr_t *pind =
+		    (diva_um_idi_ind_hdr_t *)
+		    diva_data_q_get_segment4write(&e->data);
+		if (pind) {
+			e->e.RNum = 1;
+			e->e.R->P = (byte *) & pind[1];
+			e->e.R->PLength =
+			    (word) (diva_data_q_get_max_length(&e->data) -
+				    sizeof(*pind));
+			DBG_TRC(("A(%d) E(%08x) ind_1(%02x-%02x-%02x)-[%d-%d]",
+				 e->adapter->adapter_nr, e, e->e.Id, ind,
+				 e->e.IndCh, e->e.RLength,
+				 e->e.R->PLength));
+
+		} else {
+			DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-RNR",
+				 e->adapter->adapter_nr, e, e->e.Id, ind,
+				 e->e.IndCh));
+			e->e.RNum = 0;
+			e->e.RNR = 1;
+			do_wakeup = 1;
+		}
+	} else {
+		diva_um_idi_ind_hdr_t *pind =
+		    (diva_um_idi_ind_hdr_t *) (e->e.R->P);
+
+		DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-[%d]",
+			 e->adapter->adapter_nr, e, e->e.Id, ind,
+			 e->e.IndCh, e->e.R->PLength));
+
+		pind--;
+		pind->type = DIVA_UM_IDI_IND;
+		pind->hdr.ind.Ind = ind;
+		pind->hdr.ind.IndCh = e->e.IndCh;
+		pind->data_length = e->e.R->PLength;
+		diva_data_q_ack_segment4write(&e->data,
+					      (int) (sizeof(*pind) +
+						     e->e.R->PLength));
+		do_wakeup = 1;
+	}
+
+	if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) {
+		do_wakeup = 0;
+	}
+
+	return (do_wakeup);
+}
+
+/* --------------------------------------------------------------------------
+		Write return code to the return code queue of entity
+	 -------------------------------------------------------------------------- */
+static int write_return_code(divas_um_idi_entity_t * e, byte rc)
+{
+	diva_um_idi_ind_hdr_t *prc;
+
+	if (!(prc =
+	     (diva_um_idi_ind_hdr_t *) diva_data_q_get_segment4write(&e->rc)))
+	{
+		DBG_ERR(("A: A(%d) E(%08x) rc(%02x) lost",
+			 e->adapter->adapter_nr, e, rc));
+		e->status &= ~DIVA_UM_IDI_RC_PENDING;
+		return (-1);
+	}
+
+	prc->type = DIVA_UM_IDI_IND_RC;
+	prc->hdr.rc.Rc = rc;
+	prc->hdr.rc.RcCh = e->e.RcCh;
+	prc->data_length = 0;
+	diva_data_q_ack_segment4write(&e->rc, sizeof(*prc));
+
+	return (0);
+}
+
+/* --------------------------------------------------------------------------
+		Return amount of entries that can be bead from this entity or
+		-1 if adapter was removed
+	 -------------------------------------------------------------------------- */
+int diva_user_mode_idi_ind_ready(void *entity, void *os_handle)
+{
+	divas_um_idi_entity_t *e;
+	diva_um_idi_adapter_t *a;
+	diva_os_spin_lock_magic_t old_irql;
+	int ret;
+
+	if (!entity)
+		return (-1);
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+	e = (divas_um_idi_entity_t *) entity;
+	a = e->adapter;
+
+	if ((!a) || (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+		/*
+		   Adapter was unloaded
+		 */
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+		return (-1);	/* adapter was removed */
+	}
+	if (e->status & DIVA_UM_IDI_REMOVED) {
+		/*
+		   entity was removed as result of adapter removal
+		   user should assign this entity again
+		 */
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+		return (-1);
+	}
+
+	ret = e->rc.count + e->data.count;
+
+	if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) {
+		ret = 0;
+	}
+
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+
+	return (ret);
+}
+
+void *diva_um_id_get_os_context(void *entity)
+{
+	return (((divas_um_idi_entity_t *) entity)->os_context);
+}
+
+int divas_um_idi_entity_assigned(void *entity)
+{
+	divas_um_idi_entity_t *e;
+	diva_um_idi_adapter_t *a;
+	int ret;
+	diva_os_spin_lock_magic_t old_irql;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "assigned?");
+
+
+	e = (divas_um_idi_entity_t *) entity;
+	if (!e || (!(a = e->adapter)) ||
+	    (e->status & DIVA_UM_IDI_REMOVED) ||
+	    (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?");
+		return (0);
+	}
+
+	e->status |= DIVA_UM_IDI_REMOVE_PENDING;
+
+	ret = (e->e.Id || e->rc_count
+	       || (e->status & DIVA_UM_IDI_ASSIGN_PENDING));
+
+	DBG_TRC(("Id:%02x, rc_count:%d, status:%08x", e->e.Id, e->rc_count,
+		 e->status))
+
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?");
+
+	return (ret);
+}
+
+int divas_um_idi_entity_start_remove(void *entity)
+{
+	divas_um_idi_entity_t *e;
+	diva_um_idi_adapter_t *a;
+	diva_os_spin_lock_magic_t old_irql;
+
+	diva_os_enter_spin_lock(&adapter_lock, &old_irql, "start_remove");
+
+	e = (divas_um_idi_entity_t *) entity;
+	if (!e || (!(a = e->adapter)) ||
+	    (e->status & DIVA_UM_IDI_REMOVED) ||
+	    (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+		return (0);
+	}
+
+	if (e->rc_count) {
+		/*
+		   Entity BUSY
+		 */
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+		return (1);
+	}
+
+	if (!e->e.Id) {
+		/*
+		   Remove request was already pending, and arrived now
+		 */
+		diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+		return (0);	/* REMOVE was pending */
+	}
+
+	/*
+	   Now send remove request
+	 */
+	e->e.Req = REMOVE;
+	e->e.ReqCh = 0;
+
+	e->rc_count++;
+
+	DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))",
+		 e->adapter->adapter_nr, e, e->e.Id, e->e.Req,
+		 e->e.ReqCh, e->e.X->PLength));
+
+	if (a->d.request)
+		(*(a->d.request)) (&e->e);
+
+	diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+
+	return (0);
+}
diff --git a/drivers/isdn/hardware/eicon/um_idi.h b/drivers/isdn/hardware/eicon/um_idi.h
new file mode 100644
index 0000000..141072f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/um_idi.h
@@ -0,0 +1,43 @@
+/* $Id: um_idi.h,v 1.6 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVA_USER_MODE_IDI_CORE_H__
+#define __DIVA_USER_MODE_IDI_CORE_H__
+
+
+/*
+  interface between UM IDI core and OS dependent part
+  */
+int diva_user_mode_idi_init(void);
+void diva_user_mode_idi_finit(void);
+void *divas_um_idi_create_entity(dword adapter_nr, void *file);
+int divas_um_idi_delete_entity(int adapter_nr, void *entity);
+
+typedef int (*divas_um_idi_copy_to_user_fn_t) (void *os_handle,
+					       void *dst,
+					       const void *src,
+					       int length);
+typedef int (*divas_um_idi_copy_from_user_fn_t) (void *os_handle,
+						 void *dst,
+						 const void *src,
+						 int length);
+
+int diva_um_idi_read(void *entity,
+		     void *os_handle,
+		     void *dst,
+		     int max_length, divas_um_idi_copy_to_user_fn_t cp_fn);
+
+int diva_um_idi_write(void *entity,
+		      void *os_handle,
+		      const void *src,
+		      int length, divas_um_idi_copy_from_user_fn_t cp_fn);
+
+int diva_user_mode_idi_ind_ready(void *entity, void *os_handle);
+void *diva_um_id_get_os_context(void *entity);
+int diva_os_get_context_size(void);
+int divas_um_idi_entity_assigned(void *entity);
+int divas_um_idi_entity_start_remove(void *entity);
+
+void diva_um_idi_start_wdog(void *entity);
+void diva_um_idi_stop_wdog(void *entity);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/um_xdi.h b/drivers/isdn/hardware/eicon/um_xdi.h
new file mode 100644
index 0000000..b48fc04
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/um_xdi.h
@@ -0,0 +1,68 @@
+/* $Id: um_xdi.h,v 1.1.2.2 2002/10/02 14:38:38 armin Exp $ */
+
+#ifndef __DIVA_USER_MODE_XDI_H__
+#define __DIVA_USER_MODE_XDI_H__
+
+/*
+  Contains declaratiom of structures shared between application
+  and user mode idi driver
+*/
+
+typedef struct _diva_um_idi_adapter_features {
+	dword type;
+	dword features;
+	dword channels;
+	dword serial_number;
+	char name[128];
+} diva_um_idi_adapter_features_t;
+
+#define DIVA_UM_IDI_REQ_MASK			0x0000FFFF
+#define DIVA_UM_IDI_REQ_TYPE_MASK		(~(DIVA_UM_IDI_REQ_MASK))
+#define DIVA_UM_IDI_GET_FEATURES		1	/* trigger features indication */
+#define DIVA_UM_IDI_REQ				2
+#define DIVA_UM_IDI_REQ_TYPE_MAN		0x10000000
+#define DIVA_UM_IDI_REQ_TYPE_SIG		0x20000000
+#define DIVA_UM_IDI_REQ_TYPE_NET		0x30000000
+#define DIVA_UM_IDI_REQ_MAN			(DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_MAN)
+#define DIVA_UM_IDI_REQ_SIG			(DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_SIG)
+#define DIVA_UM_IDI_REQ_NET			(DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_NET)
+/*
+  data_length  bytes will follow this structure
+*/
+typedef struct _diva_um_idi_req_hdr {
+	dword type;
+	dword Req;
+	dword ReqCh;
+	dword data_length;
+} diva_um_idi_req_hdr_t;
+
+typedef struct _diva_um_idi_ind_parameters {
+	dword Ind;
+	dword IndCh;
+} diva_um_idi_ind_parameters_t;
+
+typedef struct _diva_um_idi_rc_parameters {
+	dword Rc;
+	dword RcCh;
+} diva_um_idi_rc_parameters_t;
+
+typedef union _diva_um_idi_ind {
+	diva_um_idi_adapter_features_t features;
+	diva_um_idi_ind_parameters_t ind;
+	diva_um_idi_rc_parameters_t rc;
+} diva_um_idi_ind_t;
+
+#define DIVA_UM_IDI_IND_FEATURES  1	/* features indication */
+#define DIVA_UM_IDI_IND           2
+#define DIVA_UM_IDI_IND_RC        3
+/*
+  data_length bytes of data follow
+  this structure
+*/
+typedef struct _diva_um_idi_ind_hdr {
+	dword type;
+	diva_um_idi_ind_t hdr;
+	dword data_length;
+} diva_um_idi_ind_hdr_t;
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_adapter.h b/drivers/isdn/hardware/eicon/xdi_adapter.h
new file mode 100644
index 0000000..a3bd163
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/xdi_adapter.h
@@ -0,0 +1,70 @@
+/* $Id: xdi_adapter.h,v 1.7 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVA_OS_XDI_ADAPTER_H__
+#define __DIVA_OS_XDI_ADAPTER_H__
+
+#define DIVAS_XDI_ADAPTER_BUS_PCI  0
+#define DIVAS_XDI_ADAPTER_BUS_ISA  1
+
+typedef struct _divas_pci_card_resources {
+	byte bus;
+	byte func;
+	void *hdev;
+
+	dword bar[8];		/* contains context of appropriate BAR Register */
+	void __iomem *addr[8];		/* same bar, but mapped into memory */
+	dword length[8];	/* bar length */
+	int mem_type_id[MAX_MEM_TYPE];
+	unsigned int qoffset;
+	byte irq;
+} divas_pci_card_resources_t;
+
+typedef union _divas_card_resources {
+	divas_pci_card_resources_t pci;
+} divas_card_resources_t;
+
+struct _diva_os_xdi_adapter;
+typedef int (*diva_init_card_proc_t) (struct _diva_os_xdi_adapter * a);
+typedef int (*diva_cmd_card_proc_t) (struct _diva_os_xdi_adapter * a,
+				     diva_xdi_um_cfg_cmd_t * data,
+				     int length);
+typedef void (*diva_xdi_clear_interrupts_proc_t) (struct
+						  _diva_os_xdi_adapter *);
+
+#define DIVA_XDI_MBOX_BUSY			1
+#define DIVA_XDI_MBOX_WAIT_XLOG	2
+
+typedef struct _xdi_mbox_t {
+	dword status;
+	diva_xdi_um_cfg_cmd_data_t cmd_data;
+	dword data_length;
+	void *data;
+} xdi_mbox_t;
+
+typedef struct _diva_os_idi_adapter_interface {
+	diva_init_card_proc_t cleanup_adapter_proc;
+	diva_cmd_card_proc_t cmd_proc;
+} diva_os_idi_adapter_interface_t;
+
+typedef struct _diva_os_xdi_adapter {
+	struct list_head link;
+	int CardIndex;
+	int CardOrdinal;
+	int controller;		/* number of this controller */
+	int Bus;		/* PCI, ISA, ... */
+	divas_card_resources_t resources;
+	char port_name[24];
+	ISDN_ADAPTER xdi_adapter;
+	xdi_mbox_t xdi_mbox;
+	diva_os_idi_adapter_interface_t interface;
+	struct _diva_os_xdi_adapter *slave_adapters[3];
+	void *slave_list;
+	void *proc_adapter_dir;	/* adapterX proc entry */
+	void *proc_info;	/* info proc entry     */
+	void *proc_grp_opt;	/* group_optimization  */
+	void *proc_d_l1_down;	/* dynamic_l1_down     */
+	volatile diva_xdi_clear_interrupts_proc_t clear_interrupts_proc;
+	dword dsp_mask;
+} diva_os_xdi_adapter_t;
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_msg.h b/drivers/isdn/hardware/eicon/xdi_msg.h
new file mode 100644
index 0000000..3ade28f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/xdi_msg.h
@@ -0,0 +1,127 @@
+/* $Id: xdi_msg.h,v 1.1.2.2 2001/02/16 08:40:36 armin Exp $ */
+
+#ifndef __DIVA_XDI_UM_CFG_MESSSGE_H__
+#define __DIVA_XDI_UM_CFG_MESSAGE_H__
+
+/*
+  Definition of messages used to communicate between
+  XDI device driver and user mode configuration utility
+*/
+
+/*
+  As acknowledge one DWORD - card ordinal will be read from the card
+*/
+#define DIVA_XDI_UM_CMD_GET_CARD_ORDINAL	0
+
+/*
+  no acknowledge will be generated, memory block will be written in the
+  memory at given offset
+*/
+#define DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK	1
+
+/*
+  no acknowledge will be genatated, FPGA will be programmed
+*/
+#define DIVA_XDI_UM_CMD_WRITE_FPGA				2
+
+/*
+  As acknowledge block of SDRAM will be read in the user buffer
+*/
+#define DIVA_XDI_UM_CMD_READ_SDRAM				3
+
+/*
+  As acknowledge dword with serial number will be read in the user buffer
+*/
+#define DIVA_XDI_UM_CMD_GET_SERIAL_NR			4
+
+/*
+  As acknowledge struct consisting from 9 dwords with PCI info.
+  dword[0...7] = 8 PCI BARS
+  dword[9]		 = IRQ
+*/
+#define DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG	5
+
+/*
+  Reset of the board + activation of primary
+  boot loader
+*/
+#define DIVA_XDI_UM_CMD_RESET_ADAPTER			6
+
+/*
+  Called after code download to start adapter
+  at specified address
+  Start does set new set of features due to fact that we not know
+  if protocol features have changed
+*/
+#define DIVA_XDI_UM_CMD_START_ADAPTER			7
+
+/*
+  Stop adapter, called if user
+  wishes to stop adapter without unload
+  of the driver, to reload adapter with
+  different protocol
+*/
+#define DIVA_XDI_UM_CMD_STOP_ADAPTER			8
+
+/*
+  Get state of current adapter
+  Acknowledge is one dword with following values:
+  0 - adapter ready for download
+  1 - adapter running
+  2 - adapter dead
+  3 - out of service, driver should be restarted or hardware problem
+*/
+#define DIVA_XDI_UM_CMD_GET_CARD_STATE		9
+
+/*
+  Reads XLOG entry from the card
+*/
+#define DIVA_XDI_UM_CMD_READ_XLOG_ENTRY		10
+
+/*
+  Set untranslated protocol code features
+  */
+#define DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES	11
+
+typedef struct _diva_xdi_um_cfg_cmd_data_set_features {
+	dword features;
+} diva_xdi_um_cfg_cmd_data_set_features_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_start {
+	dword offset;
+	dword features;
+} diva_xdi_um_cfg_cmd_data_start_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_write_sdram {
+	dword ram_number;
+	dword offset;
+	dword length;
+} diva_xdi_um_cfg_cmd_data_write_sdram_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_write_fpga {
+	dword fpga_number;
+	dword image_length;
+} diva_xdi_um_cfg_cmd_data_write_fpga_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_read_sdram {
+	dword ram_number;
+	dword offset;
+	dword length;
+} diva_xdi_um_cfg_cmd_data_read_sdram_t;
+
+typedef union _diva_xdi_um_cfg_cmd_data {
+	diva_xdi_um_cfg_cmd_data_write_sdram_t write_sdram;
+	diva_xdi_um_cfg_cmd_data_write_fpga_t write_fpga;
+	diva_xdi_um_cfg_cmd_data_read_sdram_t read_sdram;
+	diva_xdi_um_cfg_cmd_data_start_t start;
+	diva_xdi_um_cfg_cmd_data_set_features_t features;
+} diva_xdi_um_cfg_cmd_data_t;
+
+typedef struct _diva_xdi_um_cfg_cmd {
+	dword adapter;		/* Adapter number 1...N */
+	dword command;
+	diva_xdi_um_cfg_cmd_data_t command_data;
+	dword data_length;	/* Plain binary data will follow */
+} diva_xdi_um_cfg_cmd_t;
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_vers.h b/drivers/isdn/hardware/eicon/xdi_vers.h
new file mode 100644
index 0000000..cf34941
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/xdi_vers.h
@@ -0,0 +1,26 @@
+
+/*
+ *
+  Copyright (c) Eicon Networks, 2002.
+ *
+  This source file is supplied for the use with
+  Eicon Networks range of DIVA Server Adapters.
+ *
+  Eicon File Revision :    2.1
+ *
+  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, or (at your option)
+  any later version.
+ *
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
+  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.
+ *
+ */
+static char diva_xdi_common_code_build[] = "102-52"; 
diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig
new file mode 100644
index 0000000..6c7b8bf
--- /dev/null
+++ b/drivers/isdn/hisax/Kconfig
@@ -0,0 +1,442 @@
+
+menu "Passive cards"
+	depends on ISDN_I4L
+
+config ISDN_DRV_HISAX
+	tristate "HiSax SiemensChipSet driver support"
+	select CRC_CCITT
+	---help---
+	  This is a driver supporting the Siemens chipset on various
+	  ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles
+	  S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many
+	  compatibles).
+
+	  HiSax is just the name of this driver, not the name of any hardware.
+
+	  If you have a card with such a chipset, you should say Y here and
+	  also to the configuration option of the driver for your particular
+	  card, below.
+
+if ISDN_DRV_HISAX!=n
+
+comment "D-channel protocol features"
+
+config HISAX_EURO
+	bool "HiSax Support for EURO/DSS1"
+	help
+	  Say Y or N according to the D-channel protocol which your local
+	  telephone service company provides.
+
+	  The call control protocol E-DSS1 is used in most European countries.
+	  If unsure, say Y.
+
+config DE_AOC
+	bool "Support for german chargeinfo"
+	depends on HISAX_EURO
+	help
+	  If you want that the HiSax hardware driver sends messages to the
+	  upper level of the isdn code on each AOCD (Advice Of Charge, During
+	  the call -- transmission of the fee information during a call) and
+	  on each AOCE (Advice Of Charge, at the End of the call --
+	  transmission of fee information at the end of the call), say Y here.
+	  This works only in Germany.
+
+config HISAX_NO_SENDCOMPLETE
+	bool "Disable sending complete"
+	depends on HISAX_EURO
+	help
+	  If you have trouble with some ugly exchanges or you live in
+	  Australia select this option.
+
+config HISAX_NO_LLC
+	bool "Disable sending low layer compatibility"
+	depends on HISAX_EURO
+	help
+	  If you have trouble with some ugly exchanges try to select this
+	  option.
+
+config HISAX_NO_KEYPAD
+	bool "Disable keypad protocol option"
+	depends on HISAX_EURO
+	help
+	  If you like to send special dial strings including * or # without
+	  using the keypad protocol, select this option.
+
+config HISAX_1TR6
+	bool "HiSax Support for german 1TR6"
+	help
+	  Say Y or N according to the D-channel protocol which your local
+	  telephone service company provides.
+
+	  1TR6 is an old call control protocol which was used in Germany
+	  before E-DSS1 was established. Nowadays, all new lines in Germany
+	  use E-DSS1.
+
+config HISAX_NI1
+	bool "HiSax Support for US NI1"
+	help
+	  Enable this if you like to use ISDN in US on a NI1 basic rate
+	  interface.
+
+config HISAX_MAX_CARDS
+	int "Maximum number of cards supported by HiSax"
+	default "8"
+	help
+	  This option allows you to specify the maximum number of cards which
+	  the HiSax driver will be able to handle.  
+
+comment "HiSax supported cards"
+
+config HISAX_16_0
+	bool "Teles 16.0/8.0"
+	depends on ISA
+	help
+	  This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
+	  and many compatibles.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port/shmem settings.
+
+config HISAX_16_3
+	bool "Teles 16.3 or PNP or PCMCIA"
+	help
+	  This enables HiSax support for the Teles ISDN-cards S0-16.3 the
+	  Teles/Creatix PnP and the Teles PCMCIA.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_TELESPCI
+	bool "Teles PCI"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the Teles PCI.
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_S0BOX
+	bool "Teles S0Box"
+	help
+	  This enables HiSax support for the Teles/Creatix parallel port
+	  S0BOX.  See <file:Documentation/isdn/README.HiSax> on how to
+	  configure it.
+
+config HISAX_AVM_A1
+	bool "AVM A1 (Fritz)"
+	depends on ISA
+	help
+	  This enables HiSax support for the AVM A1 (aka "Fritz").
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_FRITZPCI
+	bool "AVM PnP/PCI (Fritz!PnP/PCI)"
+	help
+	  This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_AVM_A1_PCMCIA
+	bool "AVM A1 PCMCIA (Fritz)"
+	help
+	  This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_ELSA
+	bool "Elsa cards"
+	help
+	  This enables HiSax support for the Elsa Mircolink ISA cards, for the
+	  Elsa Quickstep series cards and Elsa PCMCIA.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_IX1MICROR2
+	bool "ITK ix1-micro Revision 2"
+	depends on ISA
+	help
+	  This enables HiSax support for the ITK ix1-micro Revision 2 card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_DIEHLDIVA
+	bool "Eicon.Diehl Diva cards"
+	help
+	  This enables HiSax support for the Eicon.Diehl Diva none PRO
+	  versions passive ISDN cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_ASUSCOM
+	bool "ASUSCOM ISA cards"
+	depends on ISA
+	help
+	  This enables HiSax support for the AsusCom and their OEM versions
+	  passive ISDN ISA cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_TELEINT
+	bool "TELEINT cards"
+	depends on ISA
+	help
+	  This enables HiSax support for the TELEINT SA1 semiactiv ISDN card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_HFCS
+	bool "HFC-S based cards"
+	depends on ISA
+	help
+	  This enables HiSax support for the HFC-S 2BDS0 based cards, like
+	  teles 16.3c.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_SEDLBAUER
+	bool "Sedlbauer cards"
+	help
+	  This enables HiSax support for the Sedlbauer passive ISDN cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using the different cards, a different D-channel protocol, or
+	  non-standard IRQ/port settings.
+
+config HISAX_SPORTSTER
+	bool "USR Sportster internal TA"
+	depends on ISA
+	help
+	  This enables HiSax support for the USR Sportster internal TA card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_MIC
+	bool "MIC card"
+	depends on ISA
+	help
+	  This enables HiSax support for the ITH MIC card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_NETJET
+	bool "NETjet card"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the NetJet from Traverse
+	  Technologies.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_NETJET_U
+	bool "NETspider U card"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the Netspider U interface ISDN card
+	  from Traverse Technologies.
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_NICCY
+	bool "Niccy PnP/PCI card"
+	help
+	  This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_ISURF
+	bool "Siemens I-Surf card"
+	depends on ISA
+	help
+	  This enables HiSax support for the Siemens I-Talk/I-Surf card with
+	  ISAR chip.
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_HSTSAPHIR
+	bool "HST Saphir card"
+	depends on ISA
+	help
+	  This enables HiSax support for the HST Saphir card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_BKM_A4T
+	bool "Telekom A4T card"
+	depends on PCI
+	help
+	  This enables HiSax support for the Telekom A4T card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_SCT_QUADRO
+	bool "Scitel Quadro card"
+	depends on PCI
+	help
+	  This enables HiSax support for the Scitel Quadro card.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_GAZEL
+	bool "Gazel cards"
+	help
+	  This enables HiSax support for the Gazel cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_HFC_PCI
+	bool "HFC PCI-Bus cards"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
+
+	  For more informations see under
+	  <file:Documentation/isdn/README.hfc-pci>.
+
+config HISAX_W6692
+	bool "Winbond W6692 based cards"
+	depends on PCI
+	help
+	  This enables HiSax support for Winbond W6692 based PCI ISDN cards.
+
+	  See <file:Documentation/isdn/README.HiSax> on how to configure it
+	  using a different D-channel protocol, or non-standard IRQ/port
+	  settings.
+
+config HISAX_HFC_SX
+	bool "HFC-S+, HFC-SP, HFC-PCMCIA cards"
+	help
+	  This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA
+	  cards. This code is not finished yet.
+
+#      bool '  TESTEMULATOR (EXPERIMENTAL)' CONFIG_HISAX_TESTEMU
+
+config HISAX_ENTERNOW_PCI
+	bool "Formula-n enter:now PCI card"
+	depends on PCI && (BROKEN || !(SPARC64 || PPC))
+	help
+	  This enables HiSax support for the Formula-n enter:now PCI
+	  ISDN card.
+
+config HISAX_AMD7930
+	bool "Am7930 (EXPERIMENTAL)"
+	depends on EXPERIMENTAL && (SPARC32 || SPARC64)
+	help
+	  This enables HiSax support for the AMD7930 chips on some SPARCs.
+	  This code is not finished yet.
+
+endif
+
+if ISDN_DRV_HISAX
+
+config HISAX_DEBUG
+	bool "HiSax debugging"
+	help
+	  This enables debugging code in the new-style HiSax drivers, i.e.
+	  the ST5481 USB driver currently. 
+	  If in doubt, say yes.
+
+comment "HiSax PCMCIA card service modules"
+
+config HISAX_SEDLBAUER_CS
+	tristate "Sedlbauer PCMCIA cards"
+	depends on PCMCIA && HISAX_SEDLBAUER
+	help
+	  This enables the PCMCIA client driver for the Sedlbauer Speed Star
+	  and Speed Star II cards.
+
+config HISAX_ELSA_CS
+	tristate "ELSA PCMCIA MicroLink cards"
+	depends on PCMCIA && HISAX_ELSA
+	help
+	  This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink
+	  card.
+
+config HISAX_AVM_A1_CS
+	tristate "AVM A1 PCMCIA cards"
+	depends on PCMCIA && ISDN_DRV_HISAX
+	help
+	  This enables the PCMCIA client driver for the AVM A1 / Fritz!Card
+	  PCMCIA cards.
+
+config HISAX_TELES_CS
+	tristate "TELES PCMCIA cards"
+	depends on PCMCIA && HISAX_16_3
+	help
+	  This enables the PCMCIA client driver for the Teles PCMCIA cards.
+
+comment "HiSax sub driver modules"
+
+config HISAX_ST5481
+	tristate "ST5481 USB ISDN modem (EXPERIMENTAL)"
+	depends on USB && EXPERIMENTAL
+	select CRC_CCITT
+	help
+	  This enables the driver for ST5481 based USB ISDN adapters,
+	  e.g. the BeWan Gazel 128 USB
+
+config HISAX_HFCUSB
+	tristate "HFC USB based ISDN modems (EXPERIMENTAL)"
+	depends on USB && EXPERIMENTAL
+	help
+	  This enables the driver for HFC USB based ISDN modems.
+
+config HISAX_HFC4S8S
+	tristate "HFC-4S/8S based ISDN cards (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  This enables the driver for HFC-4S/8S based ISDN cards.
+
+config HISAX_FRITZ_PCIPNP
+	tristate "AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)"
+	depends on PCI && EXPERIMENTAL
+	help
+	  This enables the driver for the AVM Fritz!Card PCI,
+	  Fritz!Card PCI v2 and Fritz!Card PnP.
+	  (the latter also needs you to select "ISA Plug and Play support"
+	  from the menu "Plug and Play configuration")
+
+config HISAX_HDLC
+	bool
+	depends on HISAX_ST5481
+	default y
+
+config HISAX_AVM_A1_PCMCIA
+	bool
+	depends on HISAX_AVM_A1_CS
+	default y
+
+endif
+
+endmenu
+
diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile
new file mode 100644
index 0000000..8d6bb56
--- /dev/null
+++ b/drivers/isdn/hisax/Makefile
@@ -0,0 +1,64 @@
+# Makefile for the hisax ISDN device driver
+
+# The target object and module list name.
+
+# Define maximum number of cards
+
+EXTRA_CFLAGS      += -DHISAX_MAX_CARDS=$(CONFIG_HISAX_MAX_CARDS)
+
+obj-$(CONFIG_ISDN_DRV_HISAX)		+= hisax.o
+obj-$(CONFIG_HISAX_SEDLBAUER_CS)	+= sedlbauer_cs.o
+obj-$(CONFIG_HISAX_ELSA_CS)		+= elsa_cs.o
+obj-$(CONFIG_HISAX_AVM_A1_CS)		+= avma1_cs.o
+obj-$(CONFIG_HISAX_TELES_CS)		+= teles_cs.o
+obj-$(CONFIG_HISAX_ST5481)		+= hisax_st5481.o
+obj-$(CONFIG_HISAX_HFCUSB)		+= hfc_usb.o
+obj-$(CONFIG_HISAX_HFC4S8S)		+= hfc4s8s_l1.o
+obj-$(CONFIG_HISAX_FRITZ_PCIPNP)        += hisax_isac.o hisax_fcpcipnp.o
+
+ifdef CONFIG_HISAX_HDLC
+obj-$(CONFIG_ISDN_DRV_HISAX)		+= isdnhdlc.o
+endif
+
+# Multipart objects.
+
+hisax_st5481-y 				:= st5481_init.o st5481_usb.o st5481_d.o \
+					   st5481_b.o st5481_hdlc.o
+
+hisax-y	  				:= config.o isdnl1.o tei.o isdnl2.o isdnl3.o \
+		     			   lmgr.o q931.o callc.o fsm.o
+hisax-$(CONFIG_HISAX_EURO)		+= l3dss1.o
+hisax-$(CONFIG_HISAX_NI1)		+= l3ni1.o
+hisax-$(CONFIG_HISAX_1TR6)		+= l3_1tr6.o
+
+hisax-$(CONFIG_HISAX_16_0)		+= teles0.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_16_3)		+= teles3.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_TELESPCI)		+= telespci.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_S0BOX)		+= s0box.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_AVM_A1)		+= avm_a1.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_AVM_A1_PCMCIA)	+= avm_a1p.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_FRITZPCI)		+= avm_pci.o isac.o arcofi.o
+hisax-$(CONFIG_HISAX_ELSA)		+= elsa.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_IX1MICROR2)	+= ix1_micro.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_DIEHLDIVA)		+= diva.o isac.o arcofi.o hscx.o ipacx.o 
+hisax-$(CONFIG_HISAX_ASUSCOM)		+= asuscom.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_TELEINT)		+= teleint.o isac.o arcofi.o hfc_2bs0.o
+hisax-$(CONFIG_HISAX_SEDLBAUER)		+= sedlbauer.o isac.o arcofi.o hscx.o \
+					   isar.o
+hisax-$(CONFIG_HISAX_SPORTSTER)		+= sportster.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_MIC)		+= mic.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_NETJET)		+= nj_s.o netjet.o isac.o arcofi.o
+hisax-$(CONFIG_HISAX_NETJET_U)		+= nj_u.o netjet.o icc.o
+hisax-$(CONFIG_HISAX_HFCS)		+= hfcscard.o hfc_2bds0.o
+hisax-$(CONFIG_HISAX_HFC_PCI)		+= hfc_pci.o
+hisax-$(CONFIG_HISAX_HFC_SX)		+= hfc_sx.o
+hisax-$(CONFIG_HISAX_NICCY)		+= niccy.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_ISURF)		+= isurf.o isac.o arcofi.o isar.o
+hisax-$(CONFIG_HISAX_HSTSAPHIR)		+= saphir.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_BKM_A4T)		+= bkm_a4t.o isac.o arcofi.o jade.o
+hisax-$(CONFIG_HISAX_SCT_QUADRO)	+= bkm_a8.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_GAZEL)		+= gazel.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_W6692)		+= w6692.o
+hisax-$(CONFIG_HISAX_ENTERNOW_PCI)	+= enternow_pci.o amd7930_fn.o
+#hisax-$(CONFIG_HISAX_TESTEMU)		+= testemu.o
+
diff --git a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c
new file mode 100644
index 0000000..c4f861a
--- /dev/null
+++ b/drivers/isdn/hisax/amd7930_fn.c
@@ -0,0 +1,796 @@
+/* gerdes_amd7930.c,v 0.99 2001/10/02
+ *
+ * gerdes_amd7930.c     Amd 79C30A and 79C32A specific routines
+ *                      (based on HiSax driver by Karsten Keil)
+ *
+ * Author               Christoph Ersfeld <info@formula-n.de>
+ *                      Formula-n Europe AG (www.formula-n.com)
+ *                      previously Gerdes AG
+ *
+ *
+ *                      This file is (c) under GNU PUBLIC LICENSE
+ *
+ *
+ * Notes:
+ * Version 0.99 is the first release of this driver and there are
+ * certainly a few bugs.
+ *
+ * Please don't report any malfunction to me without sending
+ * (compressed) debug-logs.
+ * It would be nearly impossible to retrace it.
+ *
+ * Log D-channel-processing as follows:
+ *
+ * 1. Load hisax with card-specific parameters, this example ist for
+ *    Formula-n enter:now ISDN PCI and compatible
+ *    (f.e. Gerdes Power ISDN PCI)
+ *
+ *    modprobe hisax type=41 protocol=2 id=gerdes
+ *
+ *    if you chose an other value for id, you need to modify the
+ *    code below, too.
+ *
+ * 2. set debug-level
+ *
+ *    hisaxctrl gerdes 1 0x3ff
+ *    hisaxctrl gerdes 11 0x4f
+ *    cat /dev/isdnctrl >> ~/log &
+ *
+ * Please take also a look into /var/log/messages if there is
+ * anything importand concerning HISAX.
+ *
+ *
+ * Credits:
+ * Programming the driver for Formula-n enter:now ISDN PCI and
+ * necessary this driver for the used Amd 7930 D-channel-controller
+ * was spnsored by Formula-n Europe AG.
+ * Thanks to Karsten Keil and Petr Novak, who gave me support in
+ * Hisax-specific questions.
+ * I want so say special thanks to Carl-Friedrich Braun, who had to
+ * answer a lot of questions about generally ISDN and about handling
+ * of the Amd-Chip.
+ *
+ */
+
+
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "amd7930_fn.h"
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+static void Amd7930_new_ph(struct IsdnCardState *cs);
+
+static WORD initAMD[] = {
+	0x0100,
+
+	0x00A5, 3, 0x01, 0x40, 0x58,				// LPR, LMR1, LMR2
+	0x0086, 1, 0x0B,					// DMR1 (D-Buffer TH-Interrupts on)
+	0x0087, 1, 0xFF,					// DMR2
+	0x0092, 1, 0x03,					// EFCR (extended mode d-channel-fifo on)
+	0x0090, 4, 0xFE, 0xFF, 0x02, 0x0F,			// FRAR4, SRAR4, DMR3, DMR4 (address recognition )
+	0x0084, 2, 0x80, 0x00,					// DRLR
+	0x00C0, 1, 0x47,					// PPCR1
+	0x00C8, 1, 0x01,					// PPCR2
+
+	0x0102,
+	0x0107,
+	0x01A1, 1,
+	0x0121, 1,
+	0x0189, 2,
+
+	0x0045, 4, 0x61, 0x72, 0x00, 0x00,			// MCR1, MCR2, MCR3, MCR4
+	0x0063, 2, 0x08, 0x08,					// GX
+	0x0064, 2, 0x08, 0x08,					// GR
+	0x0065, 2, 0x99, 0x00,					// GER
+	0x0066, 2, 0x7C, 0x8B,					// STG
+	0x0067, 2, 0x00, 0x00,					// FTGR1, FTGR2
+	0x0068, 2, 0x20, 0x20,					// ATGR1, ATGR2
+	0x0069, 1, 0x4F,					// MMR1
+	0x006A, 1, 0x00,					// MMR2
+	0x006C, 1, 0x40,					// MMR3
+	0x0021, 1, 0x02,					// INIT
+	0x00A3, 1, 0x40,					// LMR1
+
+	0xFFFF
+};
+
+
+void /* macro wWordAMD */
+WriteWordAmd7930(struct IsdnCardState *cs, BYTE reg, WORD val)
+{
+        wByteAMD(cs, 0x00, reg);
+        wByteAMD(cs, 0x01, LOBYTE(val));
+        wByteAMD(cs, 0x01, HIBYTE(val));
+}
+
+WORD /* macro rWordAMD */
+ReadWordAmd7930(struct IsdnCardState *cs, BYTE reg)
+{
+        WORD res;
+        /* direct access register */
+        if(reg < 8) {
+        	res = rByteAMD(cs, reg);
+                res += 256*rByteAMD(cs, reg);
+        }
+        /* indirect access register */
+        else {
+                wByteAMD(cs, 0x00, reg);
+	        res = rByteAMD(cs, 0x01);
+                res += 256*rByteAMD(cs, 0x01);
+        }
+	return (res);
+}
+
+
+static void
+Amd7930_ph_command(struct IsdnCardState *cs, u_char command, char *s)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "AMD7930: %s: ph_command 0x%02X", s, command);
+
+        cs->dc.amd7930.lmr1 = command;
+        wByteAMD(cs, 0xA3, command);
+}
+
+
+
+static BYTE i430States[] = {
+// to   reset  F3    F4    F5    F6    F7    F8    AR     from
+        0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00,   // init
+        0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00,   // reset
+        0x01, 0x02, 0x00, 0x00, 0x00, 0x09, 0x05, 0x04,   // F3
+        0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,   // F4
+        0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00,   // F5
+        0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00,   // F6
+        0x11, 0x13, 0x00, 0x00, 0x1B, 0x00, 0x15, 0x00,   // F7
+        0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,   // F8
+        0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0A};  // AR
+
+
+/*                    Row     init    -   reset  F3    F4    F5    F6    F7    F8    AR */
+static BYTE stateHelper[] = { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+
+
+
+static void
+Amd7930_get_state(struct IsdnCardState *cs) {
+        BYTE lsr = rByteAMD(cs, 0xA1);
+        cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
+        Amd7930_new_ph(cs);
+}
+
+
+
+static void
+Amd7930_new_ph(struct IsdnCardState *cs)
+{
+        u_char index = stateHelper[cs->dc.amd7930.old_state]*8 + stateHelper[cs->dc.amd7930.ph_state]-1;
+        u_char message = i430States[index];
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "AMD7930: new_ph %d, old_ph %d, message %d, index %d",
+                        cs->dc.amd7930.ph_state, cs->dc.amd7930.old_state, message & 0x0f, index);
+
+        cs->dc.amd7930.old_state = cs->dc.amd7930.ph_state;
+
+        /* abort transmit if nessesary */
+        if ((message & 0xf0) && (cs->tx_skb)) {
+                wByteAMD(cs, 0x21, 0xC2);
+                wByteAMD(cs, 0x21, 0x02);
+        }
+
+	switch (message & 0x0f) {
+
+                case (1):
+                        l1_msg(cs, HW_RESET | INDICATION, NULL);
+                        Amd7930_get_state(cs);
+                        break;
+                case (2): /* init, Card starts in F3 */
+                        l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+                        break;
+                case (3):
+                        l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+                        break;
+                case (4):
+                        l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+                        Amd7930_ph_command(cs, 0x50, "HW_ENABLE REQUEST");
+                        break;
+                case (5):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+                        break;
+                case (6):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+                        break;
+                case (7): /* init, Card starts in F7 */
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+                        break;
+                case (8):
+                        l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+                        /* fall through */
+                case (9):
+                        Amd7930_ph_command(cs, 0x40, "HW_ENABLE REQ cleared if set");
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+                        break;
+                case (10):
+                        Amd7930_ph_command(cs, 0x40, "T3 expired, HW_ENABLE REQ cleared");
+                        cs->dc.amd7930.old_state = 3;
+                        break;
+                case (11):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+                        break;
+		default:
+			break;
+	}
+}
+
+
+
+static void
+Amd7930_bh(struct IsdnCardState *cs)
+{
+
+        struct PStack *stptr;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+                if (cs->debug)
+			debugl1(cs, "Amd7930: bh, D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+	        if (cs->debug & L1_DEB_ISAC)
+		        debugl1(cs, "AMD7930: bh, D_L1STATECHANGE");
+                Amd7930_new_ph(cs);
+        }
+
+        if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
+	        if (cs->debug & L1_DEB_ISAC)
+		        debugl1(cs, "AMD7930: bh, D_RCVBUFREADY");
+                DChannel_proc_rcv(cs);
+        }
+
+        if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
+	        if (cs->debug & L1_DEB_ISAC)
+		        debugl1(cs, "AMD7930: bh, D_XMTBUFREADY");
+                DChannel_proc_xmt(cs);
+        }
+}
+
+static void
+Amd7930_empty_Dfifo(struct IsdnCardState *cs, int flag)
+{
+
+        BYTE stat, der;
+	BYTE *ptr;
+	struct sk_buff *skb;
+
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "Amd7930: empty_Dfifo");
+
+
+	ptr = cs->rcvbuf + cs->rcvidx;
+
+	/* AMD interrupts off */
+	AmdIrqOff(cs);
+
+	/* read D-Channel-Fifo*/
+	stat = rByteAMD(cs, 0x07); // DSR2
+
+		/* while Data in Fifo ... */
+		while ( (stat & 2) && ((ptr-cs->rcvbuf) < MAX_DFRAME_LEN_L1) ) {
+			*ptr = rByteAMD(cs, 0x04); // DCRB
+			ptr++;
+	                stat = rByteAMD(cs, 0x07); // DSR2
+			cs->rcvidx = ptr - cs->rcvbuf;
+
+                        /* Paket ready? */
+			if (stat & 1) {
+
+                                der = rWordAMD(cs, 0x03);
+
+                                /* no errors, packet ok */
+                                if(!der && !flag) {
+					rWordAMD(cs, 0x89); // clear DRCR
+
+                                        if ((cs->rcvidx) > 0) {
+                                                if (!(skb = alloc_skb(cs->rcvidx, GFP_ATOMIC)))
+							printk(KERN_WARNING "HiSax: Amd7930: empty_Dfifo, D receive out of memory!\n");
+						else {
+                                                        /* Debugging */
+                                                        if (cs->debug & L1_DEB_ISAC_FIFO) {
+								char *t = cs->dlog;
+
+								t += sprintf(t, "Amd7930: empty_Dfifo cnt: %d |", cs->rcvidx);
+								QuickHex(t, cs->rcvbuf, cs->rcvidx);
+								debugl1(cs, cs->dlog);
+							}
+                                                        /* moves received data in sk-buffer */
+							memcpy(skb_put(skb, cs->rcvidx), cs->rcvbuf, cs->rcvidx);
+							skb_queue_tail(&cs->rq, skb);
+						}
+					}
+
+				}
+                                /* throw damaged packets away, reset receive-buffer, indicate RX */
+				ptr = cs->rcvbuf;
+				cs->rcvidx = 0;
+				schedule_event(cs, D_RCVBUFREADY);
+			}
+                }
+		/* Packet to long, overflow */
+		if(cs->rcvidx >= MAX_DFRAME_LEN_L1) {
+			if (cs->debug & L1_DEB_WARN)
+			        debugl1(cs, "AMD7930: empty_Dfifo L2-Framelength overrun");
+			cs->rcvidx = 0;
+			return;
+		}
+	/* AMD interrupts on */
+	AmdIrqOn(cs);
+}
+
+
+static void
+Amd7930_fill_Dfifo(struct IsdnCardState *cs)
+{
+
+        WORD dtcrr, dtcrw, len, count;
+        BYTE txstat, dmr3;
+        BYTE *ptr, *deb_ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "Amd7930: fill_Dfifo");
+
+	if ((!cs->tx_skb) || (cs->tx_skb->len <= 0))
+		return;
+
+        dtcrw = 0;
+        if(!cs->dc.amd7930.tx_xmtlen)
+                /* new Frame */
+                len = dtcrw = cs->tx_skb->len;
+        /* continue frame */
+        else len = cs->dc.amd7930.tx_xmtlen;
+
+
+	/* AMD interrupts off */
+	AmdIrqOff(cs);
+
+        deb_ptr = ptr = cs->tx_skb->data;
+
+        /* while free place in tx-fifo available and data in sk-buffer */
+        txstat = 0x10;
+        while((txstat & 0x10) && (cs->tx_cnt < len)) {
+                wByteAMD(cs, 0x04, *ptr);
+                ptr++;
+                cs->tx_cnt++;
+                txstat= rByteAMD(cs, 0x07);
+        }
+        count = ptr - cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+
+
+        dtcrr = rWordAMD(cs, 0x85); // DTCR
+        dmr3  = rByteAMD(cs, 0x8E);
+
+	if (cs->debug & L1_DEB_ISAC) {
+		debugl1(cs, "Amd7930: fill_Dfifo, DMR3: 0x%02X, DTCR read: 0x%04X write: 0x%02X 0x%02X", dmr3, dtcrr, LOBYTE(dtcrw), HIBYTE(dtcrw));
+        }
+
+        /* writeing of dtcrw starts transmit */
+        if(!cs->dc.amd7930.tx_xmtlen) {
+                wWordAMD(cs, 0x85, dtcrw);
+                cs->dc.amd7930.tx_xmtlen = dtcrw;
+        }
+
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "Amd7930: fill_Dfifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
+	add_timer(&cs->dbusytimer);
+
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "Amd7930: fill_Dfifo cnt: %d |", count);
+		QuickHex(t, deb_ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+	/* AMD interrupts on */
+        AmdIrqOn(cs);
+}
+
+
+void Amd7930_interrupt(struct IsdnCardState *cs, BYTE irflags)
+{
+	BYTE dsr1, dsr2, lsr;
+        WORD der;
+
+ while (irflags)
+ {
+
+        dsr1 = rByteAMD(cs, 0x02);
+        der  = rWordAMD(cs, 0x03);
+        dsr2 = rByteAMD(cs, 0x07);
+        lsr  = rByteAMD(cs, 0xA1);
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: interrupt: flags: 0x%02X, DSR1: 0x%02X, DSR2: 0x%02X, LSR: 0x%02X, DER=0x%04X", irflags, dsr1, dsr2, lsr, der);
+
+        /* D error -> read DER and DSR2 bit 2 */
+	if (der || (dsr2 & 4)) {
+
+                if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "Amd7930: interrupt: D error DER=0x%04X", der);
+
+                /* RX, TX abort if collision detected */
+                if (der & 2) {
+                        wByteAMD(cs, 0x21, 0xC2);
+                        wByteAMD(cs, 0x21, 0x02);
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+                        /* restart frame */
+                        if (cs->tx_skb) {
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+                                cs->dc.amd7930.tx_xmtlen = 0;
+				Amd7930_fill_Dfifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: Amd7930 D-Collision, no skb\n");
+				debugl1(cs, "Amd7930: interrupt: D-Collision, no skb");
+			}
+                }
+                /* remove damaged data from fifo */
+		Amd7930_empty_Dfifo(cs, 1);
+
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+                /* restart TX-Frame */
+                if (cs->tx_skb) {
+			skb_push(cs->tx_skb, cs->tx_cnt);
+			cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen = 0;
+			Amd7930_fill_Dfifo(cs);
+		}
+	}
+
+        /* D TX FIFO empty -> fill */
+	if (irflags & 1) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd7930: interrupt: clear Timer and fill D-TX-FIFO if data");
+
+		/* AMD interrupts off */
+                AmdIrqOff(cs);
+
+                if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len)
+				Amd7930_fill_Dfifo(cs);
+		}
+		/* AMD interrupts on */
+                AmdIrqOn(cs);
+	}
+
+
+        /* D RX FIFO full or tiny packet in Fifo -> empty */
+	if ((irflags & 2) || (dsr1 & 2)) {
+                if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd7930: interrupt: empty D-FIFO");
+                Amd7930_empty_Dfifo(cs, 0);
+	}
+
+
+        /* D-Frame transmit complete */
+	if (dsr1 & 64) {
+		if (cs->debug & L1_DEB_ISAC) {
+			debugl1(cs, "Amd7930: interrupt: transmit packet ready");
+        	}
+		/* AMD interrupts off */
+                AmdIrqOff(cs);
+
+                if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+
+                if (cs->tx_skb) {
+        		if (cs->debug & L1_DEB_ISAC)
+	        		debugl1(cs, "Amd7930: interrupt: TX-Packet ready, freeing skb");
+                        dev_kfree_skb_irq(cs->tx_skb);
+			cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen=0;
+			cs->tx_skb = NULL;
+                }
+                if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+        		if (cs->debug & L1_DEB_ISAC)
+	        		debugl1(cs, "Amd7930: interrupt: TX-Packet ready, next packet dequeued");
+	        	cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen=0;
+			Amd7930_fill_Dfifo(cs);
+		}
+                else
+			schedule_event(cs, D_XMTBUFREADY);
+		/* AMD interrupts on */
+                AmdIrqOn(cs);
+        }
+
+	/* LIU status interrupt -> read LSR, check statechanges */
+	if (lsr & 0x38) {
+                /* AMD interrupts off */
+                AmdIrqOff(cs);
+
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd: interrupt: LSR=0x%02X, LIU is in state %d", lsr, ((lsr & 0x7) +2));
+
+		cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
+
+		schedule_event(cs, D_L1STATECHANGE);
+		/* AMD interrupts on */
+                AmdIrqOn(cs);
+	}
+
+        /* reads Interrupt-Register again. If there is a new interrupt-flag: restart handler */
+        irflags = rByteAMD(cs, 0x00);
+ }
+
+}
+
+static void
+Amd7930_l1hw(struct PStack *st, int pr, void *arg)
+{
+        struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: l1hw called, pr: 0x%04X", pr);
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+                        if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+                                cs->dc.amd7930.tx_xmtlen=0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA", 0);
+#endif
+				Amd7930_fill_Dfifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "Amd7930: l1hw: l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+                        cs->dc.amd7930.tx_xmtlen=0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA_PULLED", 0);
+#endif
+			Amd7930_fill_Dfifo(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "Amd7930: l1hw: -> PH_REQUEST_PULL, skb: %s", (cs->tx_skb)? "yes":"no");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.amd7930.ph_state == 8)) {
+				/* b-channels off, PH-AR cleared
+				 * change to F3 */
+				Amd7930_ph_command(cs, 0x20, "HW_RESET REQEST"); //LMR1 bit 5
+				spin_unlock_irqrestore(&cs->lock, flags);
+			} else {
+				Amd7930_ph_command(cs, 0x40, "HW_RESET REQUEST");
+				cs->dc.amd7930.ph_state = 2;
+				spin_unlock_irqrestore(&cs->lock, flags);
+				Amd7930_new_ph(cs);
+			}
+			break;
+		case (HW_ENABLE | REQUEST):
+                        cs->dc.amd7930.ph_state = 9;
+                        Amd7930_new_ph(cs);
+			break;
+		case (HW_INFO3 | REQUEST):
+			// automatic
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			/* not implemented yet */
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+                        skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "Amd7930: l1hw: unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_Amd7930(struct PStack *st, struct IsdnCardState *cs)
+{
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: setstack called");
+
+        st->l1.l1hw = Amd7930_l1hw;
+}
+
+
+void
+DC_Close_Amd7930(struct IsdnCardState *cs) {
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: DC_Close called");
+}
+
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	u_long flags;
+	struct PStack *stptr;
+        WORD dtcr, der;
+        BYTE dsr1, dsr2;
+
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: dbusy_timer expired!");
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		spin_lock_irqsave(&cs->lock, flags);
+                /* D Transmit Byte Count Register:
+                 * Counts down packet's number of Bytes, 0 if packet ready */
+                dtcr = rWordAMD(cs, 0x85);
+                dsr1 = rByteAMD(cs, 0x02);
+                dsr2 = rByteAMD(cs, 0x07);
+                der  = rWordAMD(cs, 0x03);
+
+	        if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Amd7930: dbusy_timer_handler: DSR1=0x%02X, DSR2=0x%02X, DER=0x%04X, cs->tx_skb->len=%u, tx_stat=%u, dtcr=%u, cs->tx_cnt=%u", dsr1, dsr2, der, cs->tx_skb->len, cs->dc.amd7930.tx_xmtlen, dtcr, cs->tx_cnt);
+
+		if ((cs->dc.amd7930.tx_xmtlen - dtcr) < cs->tx_cnt) {	/* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			spin_unlock_irqrestore(&cs->lock, flags);
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+                                cs->dc.amd7930.tx_xmtlen = 0;
+			} else {
+				printk(KERN_WARNING "HiSax: Amd7930: D-Channel Busy no skb\n");
+				debugl1(cs, "Amd7930: D-Channel Busy no skb");
+
+			}
+			/* Transmitter reset, abort transmit */
+			wByteAMD(cs, 0x21, 0x82);
+			wByteAMD(cs, 0x21, 0x02);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			cs->irq_func(cs->irq, cs, NULL);
+
+                        if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "Amd7930: dbusy_timer_handler: Transmitter reset");
+		}
+	}
+}
+
+
+
+void __devinit
+Amd7930_init(struct IsdnCardState *cs)
+{
+    WORD *ptr;
+    BYTE cmd, cnt;
+
+        if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "Amd7930: initamd called");
+
+        cs->dc.amd7930.tx_xmtlen = 0;
+        cs->dc.amd7930.old_state = 0;
+        cs->dc.amd7930.lmr1 = 0x40;
+        cs->dc.amd7930.ph_command = Amd7930_ph_command;
+	cs->setstack_d = setstack_Amd7930;
+	cs->DC_Close = DC_Close_Amd7930;
+
+	/* AMD Initialisation */
+	for (ptr = initAMD; *ptr != 0xFFFF; ) {
+		cmd = LOBYTE(*ptr);
+
+                /* read */
+                if (*ptr++ >= 0x100) {
+			if (cmd < 8)
+                                /* setzt Register zurück */
+                                rByteAMD(cs, cmd);
+			else {
+				wByteAMD(cs, 0x00, cmd);
+				for (cnt = *ptr++; cnt > 0; cnt--)
+					rByteAMD(cs, 0x01);
+			}
+		}
+                /* write */
+                else if (cmd < 8)
+			wByteAMD(cs, cmd, LOBYTE(*ptr++));
+
+		else {
+			wByteAMD(cs, 0x00, cmd);
+			for (cnt = *ptr++; cnt > 0; cnt--)
+				wByteAMD(cs, 0x01, LOBYTE(*ptr++));
+		}
+	}
+}
+
+void __devinit
+setup_Amd7930(struct IsdnCardState *cs)
+{
+        INIT_WORK(&cs->tqueue, (void *)(void *) Amd7930_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/amd7930_fn.h b/drivers/isdn/hisax/amd7930_fn.h
new file mode 100644
index 0000000..e039c3a
--- /dev/null
+++ b/drivers/isdn/hisax/amd7930_fn.h
@@ -0,0 +1,37 @@
+/* 2001/10/02
+ *
+ * gerdes_amd7930.h     Header-file included by
+ *                      gerdes_amd7930.c
+ *
+ * Author               Christoph Ersfeld <info@formula-n.de>
+ *                      Formula-n Europe AG (www.formula-n.com)
+ *                      previously Gerdes AG
+ *
+ *
+ *                      This file is (c) under GNU PUBLIC LICENSE
+ */
+
+
+
+
+#define BYTE							unsigned char
+#define WORD							unsigned int
+#define rByteAMD(cs, reg)					cs->readisac(cs, reg)
+#define wByteAMD(cs, reg, val)					cs->writeisac(cs, reg, val)
+#define rWordAMD(cs, reg)					ReadWordAmd7930(cs, reg)
+#define wWordAMD(cs, reg, val)					WriteWordAmd7930(cs, reg, val)
+#define HIBYTE(w)						((unsigned char)((w & 0xff00) / 256))
+#define LOBYTE(w)						((unsigned char)(w & 0x00ff))
+
+#define AmdIrqOff(cs)						cs->dc.amd7930.setIrqMask(cs, 0)
+#define AmdIrqOn(cs)						cs->dc.amd7930.setIrqMask(cs, 1)
+
+#define AMD_CR		0x00
+#define AMD_DR		0x01
+
+
+#define DBUSY_TIMER_VALUE 80
+
+extern void Amd7930_interrupt(struct IsdnCardState *, unsigned char);
+extern void Amd7930_init(struct IsdnCardState *);
+extern void setup_Amd7930(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c
new file mode 100644
index 0000000..d30ce5b
--- /dev/null
+++ b/drivers/isdn/hisax/arcofi.c
@@ -0,0 +1,134 @@
+/* $Id: arcofi.c,v 1.14.2.3 2004/01/13 14:31:24 keil Exp $
+ *
+ * Ansteuerung ARCOFI 2165
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+ 
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "arcofi.h"
+
+#define ARCOFI_TIMER_VALUE	20
+
+static void
+add_arcofi_timer(struct IsdnCardState *cs) {
+	if (test_and_set_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+		del_timer(&cs->dc.isac.arcofitimer);
+	}	
+	init_timer(&cs->dc.isac.arcofitimer);
+	cs->dc.isac.arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dc.isac.arcofitimer);
+}
+
+static void
+send_arcofi(struct IsdnCardState *cs) {
+	u_char val;
+	
+	add_arcofi_timer(cs);
+	cs->dc.isac.mon_txp = 0;
+	cs->dc.isac.mon_txc = cs->dc.isac.arcofi_list->len;
+	memcpy(cs->dc.isac.mon_tx, cs->dc.isac.arcofi_list->msg, cs->dc.isac.mon_txc);
+	switch(cs->dc.isac.arcofi_bc) {
+		case 0: break;
+		case 1: cs->dc.isac.mon_tx[1] |= 0x40;
+			break;
+		default: break;
+	}
+	cs->dc.isac.mocr &= 0x0f;
+	cs->dc.isac.mocr |= 0xa0;
+	cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+	val = cs->readisac(cs, ISAC_MOSR);
+	cs->writeisac(cs, ISAC_MOX1, cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+	cs->dc.isac.mocr |= 0x10;
+	cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+}
+
+int
+arcofi_fsm(struct IsdnCardState *cs, int event, void *data) {
+	if (cs->debug & L1_DEB_MONITOR) {
+		debugl1(cs, "arcofi state %d event %d", cs->dc.isac.arcofi_state, event);
+	}
+	if (event == ARCOFI_TIMEOUT) {
+		cs->dc.isac.arcofi_state = ARCOFI_NOP;
+		test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags);
+		wake_up(&cs->dc.isac.arcofi_wait);
+ 		return(1);
+	}
+	switch (cs->dc.isac.arcofi_state) {
+		case ARCOFI_NOP:
+			if (event == ARCOFI_START) {
+				cs->dc.isac.arcofi_list = data;
+				cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
+				send_arcofi(cs);
+			}
+			break;
+		case ARCOFI_TRANSMIT:
+			if (event == ARCOFI_TX_END) {
+				if (cs->dc.isac.arcofi_list->receive) {
+					add_arcofi_timer(cs);
+					cs->dc.isac.arcofi_state = ARCOFI_RECEIVE;
+				} else {
+					if (cs->dc.isac.arcofi_list->next) {
+						cs->dc.isac.arcofi_list =
+							cs->dc.isac.arcofi_list->next;
+						send_arcofi(cs);
+					} else {
+						if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+							del_timer(&cs->dc.isac.arcofitimer);
+						}
+						cs->dc.isac.arcofi_state = ARCOFI_NOP;
+						wake_up(&cs->dc.isac.arcofi_wait);
+					}
+				}
+			}
+			break;
+		case ARCOFI_RECEIVE:
+			if (event == ARCOFI_RX_END) {
+				if (cs->dc.isac.arcofi_list->next) {
+					cs->dc.isac.arcofi_list =
+						cs->dc.isac.arcofi_list->next;
+					cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
+					send_arcofi(cs);
+				} else {
+					if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+						del_timer(&cs->dc.isac.arcofitimer);
+					}
+					cs->dc.isac.arcofi_state = ARCOFI_NOP;
+					wake_up(&cs->dc.isac.arcofi_wait);
+				}
+			}
+			break;
+		default:
+			debugl1(cs, "Arcofi unknown state %x", cs->dc.isac.arcofi_state);
+			return(2);
+	}
+	return(0);
+}
+
+static void
+arcofi_timer(struct IsdnCardState *cs) {
+	arcofi_fsm(cs, ARCOFI_TIMEOUT, NULL);
+}
+
+void
+clear_arcofi(struct IsdnCardState *cs) {
+	if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+		del_timer(&cs->dc.isac.arcofitimer);
+	}
+}
+
+void
+init_arcofi(struct IsdnCardState *cs) {
+	cs->dc.isac.arcofitimer.function = (void *) arcofi_timer;
+	cs->dc.isac.arcofitimer.data = (long) cs;
+	init_timer(&cs->dc.isac.arcofitimer);
+	init_waitqueue_head(&cs->dc.isac.arcofi_wait);
+	test_and_set_bit(HW_ARCOFI, &cs->HW_Flags);
+}
diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h
new file mode 100644
index 0000000..00c44d3
--- /dev/null
+++ b/drivers/isdn/hisax/arcofi.h
@@ -0,0 +1,27 @@
+/* $Id: arcofi.h,v 1.6.6.2 2001/09/23 22:24:46 kai Exp $
+ *
+ * Ansteuerung ARCOFI 2165
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+ 
+#define ARCOFI_USE	1
+
+/* states */
+#define ARCOFI_NOP	0
+#define ARCOFI_TRANSMIT	1
+#define ARCOFI_RECEIVE	2
+/* events */
+#define ARCOFI_START	1
+#define ARCOFI_TX_END	2
+#define ARCOFI_RX_END	3
+#define ARCOFI_TIMEOUT	4
+
+extern int arcofi_fsm(struct IsdnCardState *cs, int event, void *data);
+extern void init_arcofi(struct IsdnCardState *cs);
+extern void clear_arcofi(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c
new file mode 100644
index 0000000..7546e2e
--- /dev/null
+++ b/drivers/isdn/hisax/asuscom.c
@@ -0,0 +1,427 @@
+/* $Id: asuscom.c,v 1.14.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to  ASUSCOM NETWORK INC. Taiwan and  Dynalink NL for information
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *Asuscom_revision = "$Revision: 1.14.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ASUS_ISAC	0
+#define ASUS_HSCX	1
+#define ASUS_ADR	2
+#define ASUS_CTRL_U7	3
+#define ASUS_CTRL_POTS	5
+
+#define ASUS_IPAC_ALE	0
+#define ASUS_IPAC_DATA	1
+
+#define ASUS_ISACHSCX	1
+#define ASUS_IPAC	2
+
+/* CARD_ADR (Write) */
+#define ASUS_RESET      0x80	/* Bit 7 Reset-Leitung */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.asus.adr,
+			cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.asus.adr,
+		 cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \
+		cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+asuscom_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ASUS IRQ LOOP\n");
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_asuscom(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.asus.cfg_reg)
+		release_region(cs->hw.asus.cfg_reg, bytecnt);
+}
+
+static void
+reset_asuscom(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20);
+	else
+		byteout(cs->hw.asus.adr, ASUS_RESET);	/* Reset On */
+	mdelay(10);
+	if (cs->subtyp == ASUS_IPAC)
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
+	else
+		byteout(cs->hw.asus.adr, 0);	/* Reset Off */
+	mdelay(10);
+	if (cs->subtyp == ASUS_IPAC) {
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0);
+		writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12);
+	}
+}
+
+static int
+Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_asuscom(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_asuscom(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->debug |= L1_DEB_IPAC;
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id asus_ids[] __initdata = {
+	{ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688),
+	  ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688), 
+	  (unsigned long) "Asus1688 PnP" },
+	{ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690),
+	  ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690), 
+	  (unsigned long) "Asus1690 PnP" },
+	{ ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020),
+	  ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020), 
+	  (unsigned long) "Isurf2 PnP" },
+	{ ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000),
+	  ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000), 
+	  (unsigned long) "Iscas TE320" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &asus_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __init
+setup_asuscom(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	u_char val;
+	char tmp[64];
+
+	strcpy(tmp, Asuscom_revision);
+	printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_ASUSCOM)
+		return (0);
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "AsusPnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "AsusPnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "AsusPnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	bytecnt = 8;
+	cs->hw.asus.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.asus.cfg_reg,
+		       cs->hw.asus.cfg_reg + bytecnt);
+		return (0);
+	}
+	printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
+		cs->hw.asus.cfg_reg, cs->irq);
+	setup_isac(cs);
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Asus_card_msg;
+	val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE, 
+		cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
+	if ((val == 1) || (val == 2)) {
+		cs->subtyp = ASUS_IPAC;
+		cs->hw.asus.adr  = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &asuscom_interrupt_ipac;
+		printk(KERN_INFO "Asus: IPAC version %x\n", val);
+	} else {
+		cs->subtyp = ASUS_ISACHSCX;
+		cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
+		cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
+		cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
+		cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
+		cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		cs->irq_func = &asuscom_interrupt;
+		ISACVersion(cs, "ISDNLink:");
+		if (HscxVersion(cs, "ISDNLink:")) {
+			printk(KERN_WARNING
+		     	"ISDNLink: wrong HSCX versions check IO address\n");
+			release_io_asuscom(cs);
+			return (0);
+		}
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c
new file mode 100644
index 0000000..8f028d4
--- /dev/null
+++ b/drivers/isdn/hisax/avm_a1.c
@@ -0,0 +1,317 @@
+/* $Id: avm_a1.c,v 2.15.2.4 2004/01/13 21:46:03 keil Exp $
+ *
+ * low level stuff for AVM A1 (Fritz) isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+static const char *avm_revision = "$Revision: 2.15.2.4 $";
+
+#define	 AVM_A1_STAT_ISAC	0x01
+#define	 AVM_A1_STAT_HSCX	0x02
+#define	 AVM_A1_STAT_TIMER	0x04
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.avm.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.avm.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.avm.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.avm.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.avm.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.avm.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+avm_a1_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) {
+		if (!(sval & AVM_A1_STAT_TIMER)) {
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			sval = bytein(cs->hw.avm.cfg_reg);
+		} else if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
+		if (!(sval & AVM_A1_STAT_HSCX)) {
+			val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
+			if (val)
+				hscx_int_main(cs, val);
+		}
+		if (!(sval & AVM_A1_STAT_ISAC)) {
+			val = readreg(cs->hw.avm.isac, ISAC_ISTA);
+			if (val)
+				isac_interrupt(cs, val);
+		}
+	}
+	writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF);
+	writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF);
+	writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.avm.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0);
+	writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+inline static void
+release_ioregs(struct IsdnCardState *cs, int mask)
+{
+	release_region(cs->hw.avm.cfg_reg, 8);
+	if (mask & 1)
+		release_region(cs->hw.avm.isac + 32, 32);
+	if (mask & 2)
+		release_region(cs->hw.avm.isacfifo, 1);
+	if (mask & 4)
+		release_region(cs->hw.avm.hscx[0] + 32, 32);
+	if (mask & 8)
+		release_region(cs->hw.avm.hscxfifo[0], 1);
+	if (mask & 0x10)
+		release_region(cs->hw.avm.hscx[1] + 32, 32);
+	if (mask & 0x20)
+		release_region(cs->hw.avm.hscxfifo[1], 1);
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_ioregs(cs, 0x3f);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 1);
+			byteout(cs->hw.avm.cfg_reg, 0x16);
+			byteout(cs->hw.avm.cfg_reg, 0x1E);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_avm_a1(struct IsdnCard *card)
+{
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_A1)
+		return (0);
+
+	cs->hw.avm.cfg_reg = card->para[1] + 0x1800;
+	cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20;
+	cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20;
+	cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20;
+	cs->hw.avm.isacfifo = card->para[1] + 0x1000;
+	cs->hw.avm.hscxfifo[0] = card->para[1];
+	cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800;
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.avm.cfg_reg, 8, "avm cfg")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 8);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.isac + 32, 32, "HiSax isac")) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac ports %x-%x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.isac + 32,
+		       cs->hw.avm.isac + 64);
+		release_ioregs(cs, 0);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo")) {
+		printk(KERN_WARNING
+		       "HiSax: %s isac fifo port %x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.isacfifo);
+		release_ioregs(cs, 1);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A ports %x-%x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[0] + 32,
+		       cs->hw.avm.hscx[0] + 64);
+		release_ioregs(cs, 3);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx A fifo port %x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[0]);
+		release_ioregs(cs, 7);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B ports %x-%x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscx[1] + 32,
+		       cs->hw.avm.hscx[1] + 64);
+		release_ioregs(cs, 0xf);
+		return (0);
+	}
+	if (!request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo")) {
+		printk(KERN_WARNING
+		       "HiSax: %s hscx B fifo port %x already in use\n",
+		       CardType[cs->typ],
+		       cs->hw.avm.hscxfifo[1]);
+		release_ioregs(cs, 0x1f);
+		return (0);
+	}
+	byteout(cs->hw.avm.cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg, 0x1);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+	val = cs->irq;
+	if (val == 9)
+		val = 2;
+	byteout(cs->hw.avm.cfg_reg + 1, val);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg, 0x0);
+	HZDELAY(HZ / 5 + 1);
+
+	val = bytein(cs->hw.avm.cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg, val);
+	val = bytein(cs->hw.avm.cfg_reg + 3);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg + 3, val);
+	val = bytein(cs->hw.avm.cfg_reg + 2);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg + 2, val);
+	val = bytein(cs->hw.avm.cfg_reg);
+	printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+	       cs->hw.avm.cfg_reg, val);
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.avm.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: isac:0x%X/0x%X\n",
+	       cs->hw.avm.isac + 32, cs->hw.avm.isacfifo);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X/0x%X  hscx B:0x%X/0x%X\n",
+	       cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0],
+	       cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	setup_isac(cs);
+	cs->cardmsg = &AVM_card_msg;
+	cs->irq_func = &avm_a1_interrupt;
+	ISACVersion(cs, "AVM A1:");
+	if (HscxVersion(cs, "AVM A1:")) {
+		printk(KERN_WARNING
+		       "AVM A1: wrong HSCX versions check IO address\n");
+		release_ioregs(cs, 0x3f);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c
new file mode 100644
index 0000000..d643bb3
--- /dev/null
+++ b/drivers/isdn/hisax/avm_a1p.c
@@ -0,0 +1,268 @@
+/* $Id: avm_a1p.c,v 2.9.2.5 2004/01/24 20:47:19 keil Exp $
+ *
+ * low level stuff for the following AVM cards:
+ * A1 PCMCIA
+ * FRITZ!Card PCMCIA
+ * FRITZ!Card PCMCIA 2.0
+ *
+ * Author       Carsten Paeth
+ * Copyright    by Carsten Paeth     <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+/* register offsets */
+#define ADDRREG_OFFSET		0x02
+#define DATAREG_OFFSET		0x03
+#define ASL0_OFFSET		0x04
+#define ASL1_OFFSET		0x05
+#define MODREG_OFFSET		0x06
+#define VERREG_OFFSET		0x07
+
+/* address offsets */
+#define ISAC_FIFO_OFFSET	0x00
+#define ISAC_REG_OFFSET		0x20
+#define HSCX_CH_DIFF		0x40
+#define HSCX_FIFO_OFFSET	0x80
+#define HSCX_REG_OFFSET		0xa0
+
+/* read bits ASL0 */
+#define	 ASL0_R_TIMER		0x10 /* active low */
+#define	 ASL0_R_ISAC		0x20 /* active low */
+#define	 ASL0_R_HSCX		0x40 /* active low */
+#define	 ASL0_R_TESTBIT		0x80
+#define  ASL0_R_IRQPENDING	(ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER)
+
+/* write bits ASL0 */
+#define	 ASL0_W_RESET		0x01
+#define	 ASL0_W_TDISABLE	0x02
+#define	 ASL0_W_TRESET		0x04
+#define	 ASL0_W_IRQENABLE	0x08
+#define	 ASL0_W_TESTBIT		0x80
+
+/* write bits ASL1 */
+#define	 ASL1_W_LED0		0x10
+#define	 ASL1_W_LED1		0x20
+#define	 ASL1_W_ENABLE_S0	0xC0
+ 
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static const char *avm_revision = "$Revision: 2.9.2.5 $";
+
+static inline u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+        u_char ret;
+
+        offset -= 0x20;
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	return ret;
+}
+
+static inline void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+        offset -= 0x20;
+        byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+}
+
+static inline void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	u_char ret;
+
+        offset -= 0x20;
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+	return ret;
+}
+
+static inline void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+        offset -= 0x20;
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+	byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+}
+
+static inline void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+			HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+	outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt) 
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
+		if (cs->debug & L1_DEB_INTSTAT)
+			debugl1(cs, "avm IntStatus %x", sval);
+		if (sval & ASL0_R_HSCX) {
+                        val = ReadHSCX(cs, 1, HSCX_ISTA);
+			if (val)
+				hscx_int_main(cs, val);
+		}
+		if (sval & ASL0_R_ISAC) {
+			val = ReadISAC(cs, ISAC_ISTA);
+			if (val)
+				isac_interrupt(cs, val);
+		}
+	}
+	WriteHSCX(cs, 0, HSCX_MASK, 0xff);
+	WriteHSCX(cs, 1, HSCX_MASK, 0xff);
+	WriteISAC(cs, ISAC_MASK, 0xff);
+	WriteISAC(cs, ISAC_MASK, 0x00);
+	WriteHSCX(cs, 0, HSCX_MASK, 0x00);
+	WriteHSCX(cs, 1, HSCX_MASK, 0x00);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+			HZDELAY(HZ / 5 + 1);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return 0;
+
+		case CARD_RELEASE:
+			/* free_irq is done in HiSax_closecard(). */
+		        /* free_irq(cs->irq, cs); */
+			return 0;
+
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE);
+			clear_pending_isac_ints(cs);
+			clear_pending_hscx_ints(cs);
+			inithscxisac(cs, 1);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return 0;
+
+		case CARD_TEST:
+			/* we really don't need it for the PCMCIA Version */
+			return 0;
+
+		default:
+			/* all card drivers ignore others, so we do the same */
+			return 0;
+	}
+	return 0;
+}
+
+int
+setup_avm_a1_pcmcia(struct IsdnCard *card)
+{
+	u_char model, vers;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+
+	strcpy(tmp, avm_revision);
+	printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
+						 HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
+		return (0);
+
+	cs->hw.avm.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+
+
+	byteout(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+	HZDELAY(HZ / 5 + 1);
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+
+	byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET);
+
+	model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET);
+	vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET);
+
+	printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
+				cs->hw.avm.cfg_reg, cs->irq, model, vers);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+	cs->irq_flags = SA_SHIRQ;
+	cs->irq_func = &avm_a1p_interrupt;
+
+	ISACVersion(cs, "AVM A1 PCMCIA:");
+	if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
+		printk(KERN_WARNING
+		       "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c
new file mode 100644
index 0000000..6fcb2cf
--- /dev/null
+++ b/drivers/isdn/hisax/avm_pci.c
@@ -0,0 +1,865 @@
+/* $Id: avm_pci.c,v 1.29.2.4 2004/02/11 13:21:32 keil Exp $
+ *
+ * low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to AVM, Berlin for information
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+static const char *avm_pci_rev = "$Revision: 1.29.2.4 $";
+
+#define  AVM_FRITZ_PCI		1
+#define  AVM_FRITZ_PNP		2
+
+#define  HDLC_FIFO		0x0
+#define  HDLC_STATUS		0x4
+
+#define	 AVM_HDLC_1		0x00
+#define	 AVM_HDLC_2		0x01
+#define	 AVM_ISAC_FIFO		0x02
+#define	 AVM_ISAC_REG_LOW	0x04
+#define	 AVM_ISAC_REG_HIGH	0x06
+
+#define  AVM_STATUS0_IRQ_ISAC	0x01
+#define  AVM_STATUS0_IRQ_HDLC	0x02
+#define  AVM_STATUS0_IRQ_TIMER	0x04
+#define  AVM_STATUS0_IRQ_MASK	0x07
+
+#define  AVM_STATUS0_RESET	0x01
+#define  AVM_STATUS0_DIS_TIMER	0x02
+#define  AVM_STATUS0_RES_TIMER	0x04
+#define  AVM_STATUS0_ENA_IRQ	0x08
+#define  AVM_STATUS0_TESTBIT	0x10
+
+#define  AVM_STATUS1_INT_SEL	0x0f
+#define  AVM_STATUS1_ENA_IOM	0x80
+
+#define  HDLC_MODE_ITF_FLG	0x01
+#define  HDLC_MODE_TRANS	0x02
+#define  HDLC_MODE_CCR_7	0x04
+#define  HDLC_MODE_CCR_16	0x08
+#define  HDLC_MODE_TESTLOOP	0x80
+
+#define  HDLC_INT_XPR		0x80
+#define  HDLC_INT_XDU		0x40
+#define  HDLC_INT_RPR		0x20
+#define  HDLC_INT_MASK		0xE0
+
+#define  HDLC_STAT_RME		0x01
+#define  HDLC_STAT_RDO		0x10
+#define  HDLC_STAT_CRCVFRRAB	0x0E
+#define  HDLC_STAT_CRCVFR	0x06
+#define  HDLC_STAT_RML_MASK	0x3f00
+
+#define  HDLC_CMD_XRS		0x80
+#define  HDLC_CMD_XME		0x01
+#define  HDLC_CMD_RRS		0x20
+#define  HDLC_CMD_XML_MASK	0x3f00
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+	register u_char val;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + (offset & 0xf));
+	return (val);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + (offset & 0xf));
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	insb(cs->hw.avm.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+	outsb(cs->hw.avm.isac, data, size);
+}
+
+static inline u_int
+ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_int val;
+
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	val = inl(cs->hw.avm.isac + offset);
+	return (val);
+}
+
+static inline void
+WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value)
+{
+	register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+
+	outl(idx, cs->hw.avm.cfg_reg + 4);
+	outl(value, cs->hw.avm.isac + offset);
+}
+
+static inline u_char
+ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+	register u_char val;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	val = inb(cs->hw.avm.isac + offset);
+	return (val);
+}
+
+static inline void
+WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+
+	outb(idx, cs->hw.avm.cfg_reg + 4);
+	outb(value, cs->hw.avm.isac + offset);
+}
+
+static u_char
+ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset)
+{
+	return(0xff & ReadHDLCPCI(cs, chan, offset));
+}
+
+static void
+WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+	WriteHDLCPCI(cs, chan, offset, value);
+}
+
+static inline
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void
+write_ctrl(struct BCState *bcs, int which) {
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "hdlc %c wr%x ctrl %x",
+			'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl);
+	if (bcs->cs->subtyp == AVM_FRITZ_PCI) {
+		WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl);
+	} else {
+		if (which & 4)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2,
+				bcs->hw.hdlc.ctrl.sr.mode);
+		if (which & 2)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1,
+				bcs->hw.hdlc.ctrl.sr.xml);
+		if (which & 1)
+			WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS,
+				bcs->hw.hdlc.ctrl.sr.cmd);
+	}
+}
+
+void
+modehdlc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hdlc = bcs->channel;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d",
+			'A' + hdlc, bcs->mode, mode, hdlc, bc);
+	bcs->hw.hdlc.ctrl.ctrl = 0;
+	switch (mode) {
+		case (-1): /* used for init */
+			bcs->mode = 1;
+			bcs->channel = bc;
+			bc = 0;
+		case (L1_MODE_NULL):
+			if (bcs->mode == L1_MODE_NULL)
+				return;
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			bcs->mode = L1_MODE_NULL;
+			bcs->channel = bc;
+			break;
+		case (L1_MODE_TRANS):
+			bcs->mode = mode;
+			bcs->channel = bc;
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			schedule_event(bcs, B_XMTBUFREADY);
+			break;
+		case (L1_MODE_HDLC):
+			bcs->mode = mode;
+			bcs->channel = bc;
+			bcs->hw.hdlc.ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+			bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+			write_ctrl(bcs, 5);
+			bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd = 0;
+			schedule_event(bcs, B_XMTBUFREADY);
+			break;
+	}
+}
+
+static inline void
+hdlc_empty_fifo(struct BCState *bcs, int count)
+{
+	register u_int *ptr;
+	u_char *p;
+	u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1;
+	int cnt=0;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_empty_fifo %d", count);
+	if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hdlc_empty_fifo: incoming packet too large");
+		return;
+	}
+	p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
+	ptr = (u_int *)p;
+	bcs->hw.hdlc.rcvidx += count;
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		outl(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+#ifdef __powerpc__
+#ifdef CONFIG_APUS
+			*ptr++ = in_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE));
+#else
+			*ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE));
+#endif /* CONFIG_APUS */
+#else
+			*ptr++ = inl(cs->hw.avm.isac);
+#endif /* __powerpc__ */
+			cnt += 4;
+		}
+	} else {
+		outb(idx, cs->hw.avm.cfg_reg + 4);
+		while (cnt < count) {
+			*p++ = inb(cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+hdlc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count, cnt =0;
+	int fifo_size = 32;
+	u_char *p;
+	u_int *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (bcs->tx_skb->len > fifo_size) {
+		count = fifo_size;
+	} else {
+		count = bcs->tx_skb->len;
+		if (bcs->mode != L1_MODE_TRANS)
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len);
+	p = bcs->tx_skb->data;
+	ptr = (u_int *)p;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hdlc.count += count;
+	bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+	write_ctrl(bcs, 3);  /* sets the correct index too */
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		while (cnt<count) {
+#ifdef __powerpc__
+#ifdef CONFIG_APUS
+			out_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);
+#else
+			out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);
+#endif /* CONFIG_APUS */
+#else
+			outl(*ptr++, cs->hw.avm.isac);
+#endif /* __powerpc__ */
+			cnt += 4;
+		}
+	} else {
+		while (cnt<count) {
+			outb(*p++, cs->hw.avm.isac);
+			cnt++;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		if (cs->subtyp == AVM_FRITZ_PNP)
+			p = (u_char *) ptr;
+		t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
+			     bcs->channel ? 'B' : 'A', count);
+		QuickHex(t, p, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+HDLC_irq(struct BCState *bcs, u_int stat) {
+	int len;
+	struct sk_buff *skb;
+
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+	if (stat & HDLC_INT_RPR) {
+		if (stat & HDLC_STAT_RDO) {
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "RDO");
+			else
+				debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+			bcs->hw.hdlc.ctrl.sr.xml = 0;
+			bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+			write_ctrl(bcs, 1);
+			bcs->hw.hdlc.rcvidx = 0;
+		} else {
+			if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
+				len = 32;
+			hdlc_empty_fifo(bcs, len);
+			if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+				if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
+					(bcs->mode == L1_MODE_TRANS)) {
+					if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx)))
+						printk(KERN_WARNING "HDLC: receive out of memory\n");
+					else {
+						memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx),
+							bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx);
+						skb_queue_tail(&bcs->rqueue, skb);
+					}
+					bcs->hw.hdlc.rcvidx = 0;
+					schedule_event(bcs, B_RCVBUFREADY);
+				} else {
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "invalid frame");
+					else
+						debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat);
+					bcs->hw.hdlc.rcvidx = 0;
+				}
+			}
+		}
+	}
+	if (stat & HDLC_INT_XDU) {
+		/* Here we lost an TX interrupt, so
+		 * restart transmitting the whole frame.
+		 */
+		if (bcs->tx_skb) {
+			skb_push(bcs->tx_skb, bcs->hw.hdlc.count);
+			bcs->tx_cnt += bcs->hw.hdlc.count;
+			bcs->hw.hdlc.count = 0;
+			if (bcs->cs->debug & L1_DEB_WARN)
+				debugl1(bcs->cs, "ch%d XDU", bcs->channel);
+		} else if (bcs->cs->debug & L1_DEB_WARN)
+			debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel);
+		bcs->hw.hdlc.ctrl.sr.xml = 0;
+		bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+		write_ctrl(bcs, 1);
+		hdlc_fill_fifo(bcs);
+	} else if (stat & HDLC_INT_XPR) {
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				hdlc_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hdlc.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hdlc.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hdlc.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hdlc_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+inline void
+HDLC_irq_main(struct IsdnCardState *cs)
+{
+	u_int stat;
+	struct BCState *bcs;
+
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 0))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 0 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		stat = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+	} else {
+		stat = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		if (stat & HDLC_INT_RPR)
+			stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8;
+	}
+	if (stat & HDLC_INT_MASK) {
+		if (!(bcs = Sel_BCS(cs, 1))) {
+			if (cs->debug)
+				debugl1(cs, "hdlc spurious channel 1 IRQ");
+		} else
+			HDLC_irq(bcs, stat);
+	}
+}
+
+void
+hdlc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.hdlc.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->hw.hdlc.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			modehdlc(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			modehdlc(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_hdlcstate(struct BCState *bcs)
+{
+	modehdlc(bcs, 0, 0);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hdlc.rcvbuf) {
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+int
+open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hdlc.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hdlc.rcvbuf);
+			bcs->hw.hdlc.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hdlc.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hdlc(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hdlcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hdlc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void __init
+clear_pending_hdlc_ints(struct IsdnCardState *cs)
+{
+	u_int val;
+
+	if (cs->subtyp == AVM_FRITZ_PCI) {
+		val = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+	} else {
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+		debugl1(cs, "HDLC 1 STA %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 1 RML %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 1 MODE %x", val);
+		val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 1 VIN %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+		debugl1(cs, "HDLC 2 STA %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1);
+		debugl1(cs, "HDLC 2 RML %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2);
+		debugl1(cs, "HDLC 2 MODE %x", val);
+		val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3);
+		debugl1(cs, "HDLC 2 VIN %x", val);
+	}
+}
+
+void __init
+inithdlc(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_hdlc;
+	cs->bcs[1].BC_SetStack = setstack_hdlc;
+	cs->bcs[0].BC_Close = close_hdlcstate;
+	cs->bcs[1].BC_Close = close_hdlcstate;
+	modehdlc(cs->bcs, -1, 0);
+	modehdlc(cs->bcs + 1, -1, 1);
+}
+
+static irqreturn_t
+avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_long flags;
+	u_char val;
+	u_char sval;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	sval = inb(cs->hw.avm.cfg_reg + 2);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
+		/* possible a shared  IRQ reqest */
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+		val = ReadISAC(cs, ISAC_ISTA);
+		isac_interrupt(cs, val);
+	}
+	if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
+		HDLC_irq_main(cs);
+	}
+	WriteISAC(cs, ISAC_MASK, 0xFF);
+	WriteISAC(cs, ISAC_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+reset_avmpcipnp(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "AVM PCI/PnP: reset\n");
+	outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
+	mdelay(10);
+	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+	outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
+	mdelay(10);
+	printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_avmpcipnp(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			outb(0, cs->hw.avm.cfg_reg + 2);
+			release_region(cs->hw.avm.cfg_reg, 32);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_avmpcipnp(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			inithdlc(cs);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER,
+				cs->hw.avm.cfg_reg + 2);
+			WriteISAC(cs, ISAC_MASK, 0);
+			outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+				AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+			/* RESET Receiver and Transmitter */
+			WriteISAC(cs, ISAC_CMDR, 0x41);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef CONFIG_PCI
+static struct pci_dev *dev_avm __initdata = NULL;
+#endif
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_avm_c __initdata = NULL;
+#endif
+
+int __init
+setup_avm_pcipnp(struct IsdnCard *card)
+{
+	u_int val, ver;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, avm_pci_rev);
+	printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_FRITZPCI)
+		return (0);
+	if (card->para[1]) {
+		/* old manual method */
+		cs->hw.avm.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		cs->subtyp = AVM_FRITZ_PNP;
+		goto ready;
+	}
+#ifdef __ISAPNP__
+	if (isapnp_present()) {
+		struct pnp_dev *pnp_avm_d = NULL;
+		if ((pnp_avm_c = pnp_find_card(
+			ISAPNP_VENDOR('A', 'V', 'M'),
+			ISAPNP_FUNCTION(0x0900), pnp_avm_c))) {
+			if ((pnp_avm_d = pnp_find_dev(pnp_avm_c,
+				ISAPNP_VENDOR('A', 'V', 'M'),
+				ISAPNP_FUNCTION(0x0900), pnp_avm_d))) {
+				int err;
+
+				pnp_disable_dev(pnp_avm_d);
+				err = pnp_activate_dev(pnp_avm_d);
+				if (err<0) {
+					printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+						__FUNCTION__, err);
+					return(0);
+				}
+				cs->hw.avm.cfg_reg =
+					pnp_port_start(pnp_avm_d, 0);
+				cs->irq = pnp_irq(pnp_avm_d, 0);
+				if (!cs->irq) {
+					printk(KERN_ERR "FritzPnP:No IRQ\n");
+					return(0);
+				}
+				if (!cs->hw.avm.cfg_reg) {
+					printk(KERN_ERR "FritzPnP:No IO address\n");
+					return(0);
+				}
+				cs->subtyp = AVM_FRITZ_PNP;
+				goto ready;
+			}
+		}
+	} else {
+		printk(KERN_INFO "FritzPnP: no ISA PnP present\n");
+	}
+#endif
+#ifdef CONFIG_PCI
+	if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM,
+		PCI_DEVICE_ID_AVM_A1,  dev_avm))) {
+		if (pci_enable_device(dev_avm))
+			return(0);
+		cs->irq = dev_avm->irq;
+		if (!cs->irq) {
+			printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n");
+			return(0);
+		}
+		cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1);
+		if (!cs->hw.avm.cfg_reg) {
+			printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		cs->subtyp = AVM_FRITZ_PCI;
+	} else {
+		printk(KERN_WARNING "FritzPCI: No PCI card found\n");
+		return(0);
+	}
+	cs->irq_flags |= SA_SHIRQ;
+#else
+	printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n");
+	return (0);
+#endif /* CONFIG_PCI */
+ready:
+	cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
+	if (!request_region(cs->hw.avm.cfg_reg, 32,
+		(cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.avm.cfg_reg,
+		       cs->hw.avm.cfg_reg + 31);
+		return (0);
+	}
+	switch (cs->subtyp) {
+	  case AVM_FRITZ_PCI:
+		val = inl(cs->hw.avm.cfg_reg);
+		printk(KERN_INFO "AVM PCI: stat %#x\n", val);
+		printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
+			val & 0xff, (val>>8) & 0xff);
+		cs->BC_Read_Reg = &ReadHDLC_s;
+		cs->BC_Write_Reg = &WriteHDLC_s;
+		break;
+	  case AVM_FRITZ_PNP:
+		val = inb(cs->hw.avm.cfg_reg);
+		ver = inb(cs->hw.avm.cfg_reg + 1);
+		printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
+		cs->BC_Read_Reg = &ReadHDLCPnP;
+		cs->BC_Write_Reg = &WriteHDLCPnP;
+		break;
+	  default:
+	  	printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
+	  	return(0);
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n",
+		(cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP",
+		cs->irq, cs->hw.avm.cfg_reg);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Send_Data = &hdlc_fill_fifo;
+	cs->cardmsg = &AVM_card_msg;
+	cs->irq_func = &avm_pcipnp_interrupt;
+	cs->writeisac(cs, ISAC_MASK, 0xFF);
+	ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
new file mode 100644
index 0000000..663a0bf
--- /dev/null
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -0,0 +1,527 @@
+/*
+ * PCMCIA client driver for AVM A1 / Fritz!PCMCIA
+ *
+ * Author       Carsten Paeth
+ * Copyright    1998-2001 by Carsten Paeth <calle@calle.in-berlin.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"avma1_cs.c 1.00 1998/01/23 10:00:00 (Carsten Paeth)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int isdnprot = 2;
+
+module_param(isdnprot, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the skeleton event
+   handler.
+*/
+
+static void avma1cs_config(dev_link_t *link);
+static void avma1cs_release(dev_link_t *link);
+static int avma1cs_event(event_t event, int priority,
+			  event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *avma1cs_attach(void);
+static void avma1cs_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "avma1_cs";
+
+/*
+   A linked list of "instances" of the skeleton device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally can't be allocated dynamically.
+*/
+   
+typedef struct local_info_t {
+    dev_node_t	node;
+} local_info_t;
+
+/*======================================================================
+
+    avma1cs_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+    
+======================================================================*/
+
+static dev_link_t *avma1cs_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret;
+    
+    DEBUG(0, "avma1cs_attach()\n");
+
+    /* Initialize the dev_link_t structure */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    if (!link)
+	return NULL;
+    memset(link, 0, sizeof(struct dev_link_t));
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) {
+	kfree(link);
+	return NULL;
+    }
+    memset(local, 0, sizeof(local_info_t));
+    link->priv = local;
+
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 16;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+    link->io.IOAddrLines = 5;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &avma1cs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	avma1cs_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* avma1cs_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void avma1cs_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "avma1cs_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    /*
+       If the device is currently configured and active, we won't
+       actually delete it yet.  Instead, it is marked so that when
+       the release() function is called, that will trigger a proper
+       detach().
+    */
+    if (link->state & DEV_CONFIG) {
+#ifdef PCMCIA_DEBUG
+	printk(KERN_DEBUG "avma1_cs: detach postponed, '%s' "
+	       "still locked\n", link->dev->dev_name);
+#endif
+	link->state |= DEV_STALE_LINK;
+	return;
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+    	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    if (link->priv) {
+	kfree(link->priv);
+    }
+    kfree(link);
+    
+} /* avma1cs_detach */
+
+/*======================================================================
+
+    avma1cs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+    
+======================================================================*/
+
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_tuple_data(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_first_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse)
+{
+    int i = pcmcia_get_next_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static void avma1cs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    local_info_t *dev;
+    int i;
+    u_char buf[64];
+    char devname[128];
+    IsdnCard_t	icard;
+    int busy = 0;
+    
+    handle = link->handle;
+    dev = link->priv;
+
+    DEBUG(0, "avma1cs_config(0x%p)\n", link);
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    do {
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	i = pcmcia_get_first_tuple(handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 64;
+	tuple.TupleOffset = 0;
+	i = pcmcia_get_tuple_data(handle, &tuple);
+	if (i != CS_SUCCESS) break;
+	i = pcmcia_parse_tuple(handle, &tuple, &parse);
+	if (i != CS_SUCCESS) break;
+	link->conf.ConfigBase = parse.config.base;
+    } while (0);
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, ParseTuple, i);
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+    }
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    do {
+
+	tuple.Attributes = 0;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = 254;
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_VERS_1;
+
+	devname[0] = 0;
+	if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
+	    strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], 
+			sizeof(devname));
+	}
+	/*
+         * find IO port
+         */
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	i = first_tuple(handle, &tuple, &parse);
+	while (i == CS_SUCCESS) {
+	    if (cf->io.nwin > 0) {
+		link->conf.ConfigIndex = cf->index;
+		link->io.BasePort1 = cf->io.win[0].base;
+		link->io.NumPorts1 = cf->io.win[0].len;
+		link->io.NumPorts2 = 0;
+		printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n",
+			link->io.BasePort1,
+			link->io.BasePort1+link->io.NumPorts1 - 1);
+		i = pcmcia_request_io(link->handle, &link->io);
+		if (i == CS_SUCCESS) goto found_port;
+	    }
+	    i = next_tuple(handle, &tuple, &parse);
+	}
+
+found_port:
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIO, i);
+	    break;
+	}
+	
+	/*
+	 * allocate an interrupt line
+	 */
+	i = pcmcia_request_irq(link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestIRQ, i);
+	    pcmcia_release_io(link->handle, &link->io);
+	    break;
+	}
+	
+	/*
+	 * configure the PCMCIA socket
+	 */
+	i = pcmcia_request_configuration(link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+	    cs_error(link->handle, RequestConfiguration, i);
+	    pcmcia_release_io(link->handle, &link->io);
+	    pcmcia_release_irq(link->handle, &link->irq);
+	    break;
+	}
+
+    } while (0);
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. */
+
+    strcpy(dev->node.dev_name, "A1");
+    dev->node.major = 45;
+    dev->node.minor = 0;
+    link->dev = &dev->node;
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    /* If any step failed, release any partially configured state */
+    if (i != 0) {
+	avma1cs_release(link);
+	return;
+    }
+
+    printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n",
+				link->io.BasePort1, link->irq.AssignedIRQ);
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = isdnprot;
+    icard.typ = ISDN_CTYPE_A1_PCMCIA;
+    
+    i = hisax_init_pcmcia(link, &busy, &icard);
+    if (i < 0) {
+    	printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 PCMCIA %d at i/o %#x\n", i, link->io.BasePort1);
+	avma1cs_release(link);
+	return;
+    }
+    dev->node.minor = i;
+
+} /* avma1cs_config */
+
+/*======================================================================
+
+    After a card is removed, avma1cs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+static void avma1cs_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+
+    DEBUG(0, "avma1cs_release(0x%p)\n", link);
+
+    /* no unregister function with hisax */
+    HiSax_closecard(local->node.minor);
+
+    /* Unlink the device chain */
+    link->dev = NULL;
+    
+    /* Don't bother checking to see if these succeed or not */
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+    
+    if (link->state & DEV_STALE_LINK)
+	avma1cs_detach(link);
+} /* avma1cs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+    
+======================================================================*/
+
+static int avma1cs_event(event_t event, int priority,
+			  event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    DEBUG(1, "avma1cs_event(0x%06x)\n", event);
+    
+    switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+	    if (link->state & DEV_CONFIG)
+		avma1cs_release(link);
+	    break;
+	case CS_EVENT_CARD_INSERTION:
+	    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	    avma1cs_config(link);
+	    break;
+	case CS_EVENT_PM_SUSPEND:
+	    link->state |= DEV_SUSPEND;
+	    /* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+	    if (link->state & DEV_CONFIG)
+		pcmcia_release_configuration(link->handle);
+	    break;
+	case CS_EVENT_PM_RESUME:
+	    link->state &= ~DEV_SUSPEND;
+	    /* Fall through... */
+	case CS_EVENT_CARD_RESET:
+ 	    if (link->state & DEV_CONFIG)
+		pcmcia_request_configuration(link->handle, &link->conf);
+	    break;
+    }
+    return 0;
+} /* avma1cs_event */
+
+static struct pcmcia_driver avma1cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "avma1_cs",
+	},
+	.attach		= avma1cs_attach,
+	.detach		= avma1cs_detach,
+};
+ 
+/*====================================================================*/
+
+static int __init init_avma1_cs(void)
+{
+	return(pcmcia_register_driver(&avma1cs_driver));
+}
+
+static void __exit exit_avma1_cs(void)
+{
+	pcmcia_unregister_driver(&avma1cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_avma1_cs);
+module_exit(exit_avma1_cs);
diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c
new file mode 100644
index 0000000..f410f62
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_a4t.c
@@ -0,0 +1,344 @@
+/* $Id: bkm_a4t.c,v 1.22.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for T-Berkom A4T
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "jade.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include "bkm_ax.h"
+
+extern const char *CardType[];
+
+const char *bkm_a4t_revision = "$Revision: 1.22.2.4 $";
+
+
+static inline u_char
+readreg(unsigned int ale, unsigned long adr, u_char off)
+{
+	register u_int ret;
+	unsigned int *po = (unsigned int *) adr;	/* Postoffice */
+
+	*po = (GCS_2 | PO_WRITE | off);
+	__WAITI20__(po);
+	*po = (ale | PO_READ);
+	__WAITI20__(po);
+	ret = *po;
+	return ((unsigned char) ret);
+}
+
+
+static inline void
+readfifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size)
+{
+	int i;
+	for (i = 0; i < size; i++)
+		*data++ = readreg(ale, adr, off);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned long adr, u_char off, u_char data)
+{
+	unsigned int *po = (unsigned int *) adr;	/* Postoffice */
+	*po = (GCS_2 | PO_WRITE | off);
+	__WAITI20__(po);
+	*po = (ale | PO_WRITE | data);
+	__WAITI20__(po);
+}
+
+
+static inline void
+writefifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		writereg(ale, adr, off, *data++);
+}
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
+}
+
+static u_char
+ReadJADE(struct IsdnCardState *cs, int jade, u_char offset)
+{
+	return (readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80))));
+}
+
+static void
+WriteJADE(struct IsdnCardState *cs, int jade, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)), value);
+}
+
+/*
+ * fast interrupt JADE stuff goes here
+ */
+
+#define READJADE(cs, nr, reg) readreg(cs->hw.ax.jade_ale,\
+ 		cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)))
+#define WRITEJADE(cs, nr, reg, data) writereg(cs->hw.ax.jade_ale,\
+ 		cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), data)
+
+#define READJADEFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.jade_ale,\
+		cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
+#define WRITEJADEFIFO(cs, nr, ptr, cnt) writefifo( cs->hw.ax.jade_ale,\
+		cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
+
+#include "jade_irq.c"
+
+static irqreturn_t
+bkm_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val = 0;
+	u_long flags;
+	I20_REGISTER_FILE *pI20_Regs;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+
+	/* ISDN interrupt pending? */
+	if (pI20_Regs->i20IntStatus & intISDN) {
+		/* Reset the ISDN interrupt     */
+		pI20_Regs->i20IntStatus = intISDN;
+		/* Disable ISDN interrupt */
+		pI20_Regs->i20IntCtrl &= ~intISDN;
+		/* Channel A first */
+		val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0x80);
+		if (val) {
+			jade_int_main(cs, val, 0);
+		}
+		/* Channel B  */
+		val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0xC0);
+		if (val) {
+			jade_int_main(cs, val, 1);
+		}
+		/* D-Channel */
+		val = readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, ISAC_ISTA);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+		/* Reenable ISDN interrupt */
+		pI20_Regs->i20IntCtrl |= intISDN;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_HANDLED;
+	} else {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+}
+
+void
+release_io_bkm(struct IsdnCardState *cs)
+{
+	if (cs->hw.ax.base) {
+		iounmap((void *) cs->hw.ax.base);
+		cs->hw.ax.base = 0;
+	}
+}
+
+static void
+enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
+{
+	if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+		I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+		if (bEnable)
+			pI20_Regs->i20IntCtrl |= (intISDN | intPCI);
+		else
+			/* CAUTION: This disables the video capture driver too */
+			pI20_Regs->i20IntCtrl &= ~(intISDN | intPCI);
+	}
+}
+
+static void
+reset_bkm(struct IsdnCardState *cs)
+{
+	if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+		I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+		/* Issue the I20 soft reset     */
+		pI20_Regs->i20SysControl = 0xFF;	/* all in */
+		mdelay(10);
+		/* Remove the soft reset */
+		pI20_Regs->i20SysControl = sysRESET | 0xFF;
+		mdelay(10);
+		/* Set our configuration */
+		pI20_Regs->i20SysControl = sysRESET | sysCFG;
+		/* Issue ISDN reset     */
+		pI20_Regs->i20GuestControl = guestWAIT_CFG |
+		    g_A4T_JADE_RES |
+		    g_A4T_ISAR_RES |
+		    g_A4T_ISAC_RES |
+		    g_A4T_JADE_BOOTR |
+		    g_A4T_ISAR_BOOTR;
+		mdelay(10);
+
+		/* Remove RESET state from ISDN */
+		pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES |
+						g_A4T_JADE_RES |
+						g_A4T_ISAR_RES);
+		mdelay(10);
+	}
+}
+
+static int
+BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			/* Disable ints */
+			spin_lock_irqsave(&cs->lock, flags);
+			enable_bkm_int(cs, 0);
+			reset_bkm(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			/* Sanity */
+			spin_lock_irqsave(&cs->lock, flags);
+			enable_bkm_int(cs, 0);
+			reset_bkm(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			release_io_bkm(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			clear_pending_isac_ints(cs);
+			clear_pending_jade_ints(cs);
+			initisac(cs);
+			initjade(cs);
+			/* Enable ints */
+			enable_bkm_int(cs, 1);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+static struct pci_dev *dev_a4t __initdata = NULL;
+
+int __init
+setup_bkm_a4t(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_int pci_memaddr = 0, found = 0;
+	I20_REGISTER_FILE *pI20_Regs;
+#ifdef CONFIG_PCI
+#endif
+
+	strcpy(tmp, bkm_a4t_revision);
+	printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+		cs->subtyp = BKM_A4T;
+	} else
+		return (0);
+
+#ifdef CONFIG_PCI
+	while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN,
+		PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) {
+		u16 sub_sys;
+		u16 sub_vendor;
+
+		sub_vendor = dev_a4t->subsystem_vendor;
+		sub_sys = dev_a4t->subsystem_device;
+		if ((sub_sys == PCI_DEVICE_ID_BERKOM_A4T) && (sub_vendor == PCI_VENDOR_ID_BERKOM)) {
+			if (pci_enable_device(dev_a4t))
+				return(0);
+			found = 1;
+			pci_memaddr = pci_resource_start(dev_a4t, 0);
+			cs->irq = dev_a4t->irq;
+			break;
+		}
+	}
+	if (!found) {
+		printk(KERN_WARNING "HiSax: %s: Card not found\n", CardType[card->typ]);
+		return (0);
+	}
+	if (!cs->irq) {		/* IRQ range check ?? */
+		printk(KERN_WARNING "HiSax: %s: No IRQ\n", CardType[card->typ]);
+		return (0);
+	}
+	if (!pci_memaddr) {
+		printk(KERN_WARNING "HiSax: %s: No Memory base address\n", CardType[card->typ]);
+		return (0);
+	}
+	cs->hw.ax.base = (long) ioremap(pci_memaddr, 4096);
+	/* Check suspecious address */
+	pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+	if ((pI20_Regs->i20IntStatus & 0x8EFFFFFF) != 0) {
+		printk(KERN_WARNING "HiSax: %s address %lx-%lx suspecious\n",
+		       CardType[card->typ], cs->hw.ax.base, cs->hw.ax.base + 4096);
+		iounmap((void *) cs->hw.ax.base);
+		cs->hw.ax.base = 0;
+		return (0);
+	}
+	cs->hw.ax.isac_adr = cs->hw.ax.base + PO_OFFSET;
+	cs->hw.ax.jade_adr = cs->hw.ax.base + PO_OFFSET;
+	cs->hw.ax.isac_ale = GCS_1;
+	cs->hw.ax.jade_ale = GCS_3;
+#else
+	printk(KERN_WARNING "HiSax: %s: NO_PCI_BIOS\n", CardType[card->typ]);
+	printk(KERN_WARNING "HiSax: %s: unable to configure\n", CardType[card->typ]);
+	return (0);
+#endif				/* CONFIG_PCI */
+	printk(KERN_INFO "HiSax: %s: Card configured at 0x%lX IRQ %d\n",
+	       CardType[card->typ], cs->hw.ax.base, cs->irq);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadJADE;
+	cs->BC_Write_Reg = &WriteJADE;
+	cs->BC_Send_Data = &jade_fill_fifo;
+	cs->cardmsg = &BKM_card_msg;
+	cs->irq_func = &bkm_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ISACVersion(cs, "Telekom A4T:");
+	/* Jade version */
+	JadeVersion(cs, "Telekom A4T:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c
new file mode 100644
index 0000000..94bb83c
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_a8.c
@@ -0,0 +1,451 @@
+/* $Id: bkm_a8.c,v 1.22.2.4 2004/01/15 14:02:34 keil Exp $
+ *
+ * low level stuff for Scitel Quadro (4*S0, passive)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include "bkm_ax.h"
+
+#ifdef CONFIG_PCI
+
+#define	ATTEMPT_PCI_REMAPPING	/* Required for PLX rev 1 */
+
+extern const char *CardType[];
+
+const char sct_quadro_revision[] = "$Revision: 1.22.2.4 $";
+
+static const char *sct_quadro_subtypes[] =
+{
+	"",
+	"#1",
+	"#2",
+	"#3",
+	"#4"
+};
+
+
+#define wordout(addr,val) outw(val,addr)
+#define wordin(addr) inw(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	wordout(ale, off);
+	ret = wordin(adr) & 0xFF;
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	int i;
+	wordout(ale, off);
+	for (i = 0; i < size; i++)
+		data[i] = wordin(adr) & 0xFF;
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	wordout(ale, off);
+	wordout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	int i;
+	wordout(ale, off);
+	for (i = 0; i < size; i++)
+		wordout(adr, data[i]);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
+}
+
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* Set the specific ipac to active */
+static void
+set_ipac_active(struct IsdnCardState *cs, u_int active)
+{
+	/* set irq mask */
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK,
+		active ? 0xc0 : 0xff);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \
+	cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+bkm_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
+	if (!(ista & 0x3f)) { /* not this IPAC */
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+      Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val) {
+			hscx_int_main(cs, val);
+		}
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "HiSax: %s (%s) IRQ LOOP\n",
+		       CardType[cs->typ],
+		       sct_quadro_subtypes[cs->subtyp]);
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF);
+	writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_sct_quadro(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.ax.base & 0xffffffc0, 128);
+	if (cs->subtyp == SCT_1)
+		release_region(cs->hw.ax.plx_adr, 64);
+}
+
+static void
+enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
+{
+	if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
+		if (bEnable)
+			wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41));
+		else
+			wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41));
+	}
+}
+
+static void
+reset_bkm(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == SCT_1) {
+		wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4));
+		mdelay(10);
+		/* Remove the soft reset */
+		wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
+		mdelay(10);
+	}
+}
+
+static int
+BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			/* Disable ints */
+			set_ipac_active(cs, 0);
+			enable_bkm_int(cs, 0);
+			reset_bkm(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			/* Sanity */
+			spin_lock_irqsave(&cs->lock, flags);
+			set_ipac_active(cs, 0);
+			enable_bkm_int(cs, 0);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			release_io_sct_quadro(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->debug |= L1_DEB_IPAC;
+			set_ipac_active(cs, 1);
+			inithscxisac(cs, 3);
+			/* Enable ints */
+			enable_bkm_int(cs, 1);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+int __init
+sct_alloc_io(u_int adr, u_int len)
+{
+	if (!request_region(adr, len, "scitel")) {
+		printk(KERN_WARNING
+			"HiSax: Scitel port %#x-%#x already in use\n",
+			adr, adr + len);
+		return (1);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_a8 __initdata = NULL;
+static u16  sub_vendor_id __initdata = 0;
+static u16  sub_sys_id __initdata = 0;
+static u_char pci_bus __initdata = 0;
+static u_char pci_device_fn __initdata = 0;
+static u_char pci_irq __initdata = 0;
+
+#endif /* CONFIG_PCI */
+
+int __init
+setup_sct_quadro(struct IsdnCard *card)
+{
+#ifdef CONFIG_PCI
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_char pci_rev_id;
+	u_int found = 0;
+	u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5;
+
+	strcpy(tmp, sct_quadro_revision);
+	printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
+		cs->subtyp = SCT_1;	/* Preset */
+	} else
+		return (0);
+
+	/* Identify subtype by para[0] */
+	if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4)
+		cs->subtyp = card->para[0];
+	else {
+		printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n",
+			CardType[card->typ]);
+		return (0);
+	}
+	if ((cs->subtyp != SCT_1) && ((sub_sys_id != PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) ||
+		(sub_vendor_id != PCI_VENDOR_ID_BERKOM)))
+		return (0);
+	if (cs->subtyp == SCT_1) {
+		while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX,
+			PCI_DEVICE_ID_PLX_9050, dev_a8))) {
+			
+			sub_vendor_id = dev_a8->subsystem_vendor;
+			sub_sys_id = dev_a8->subsystem_device;
+			if ((sub_sys_id == PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) &&
+				(sub_vendor_id == PCI_VENDOR_ID_BERKOM)) {
+				if (pci_enable_device(dev_a8))
+					return(0);
+				pci_ioaddr1 = pci_resource_start(dev_a8, 1);
+				pci_irq = dev_a8->irq;
+				pci_bus = dev_a8->bus->number;
+				pci_device_fn = dev_a8->devfn;
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			printk(KERN_WARNING "HiSax: %s (%s): Card not found\n",
+				CardType[card->typ],
+				sct_quadro_subtypes[cs->subtyp]);
+			return (0);
+		}
+#ifdef ATTEMPT_PCI_REMAPPING
+/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */
+		pci_read_config_byte(dev_a8, PCI_REVISION_ID, &pci_rev_id);
+		if ((pci_ioaddr1 & 0x80) && (pci_rev_id == 1)) {
+			printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n",
+				CardType[card->typ],
+				sct_quadro_subtypes[cs->subtyp]);
+			/* Restart PCI negotiation */
+			pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, (u_int) - 1);
+			/* Move up by 0x80 byte */
+			pci_ioaddr1 += 0x80;
+			pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
+			pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, pci_ioaddr1);
+			dev_a8->resource[ 1].start = pci_ioaddr1;
+		}
+#endif /* End HACK */
+	}
+	if (!pci_irq) {		/* IRQ range check ?? */
+		printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n",
+		       CardType[card->typ],
+		       sct_quadro_subtypes[cs->subtyp]);
+		return (0);
+	}
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_1, &pci_ioaddr1);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_2, &pci_ioaddr2);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_3, &pci_ioaddr3);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_4, &pci_ioaddr4);
+	pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_5, &pci_ioaddr5);
+	if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) {
+		printk(KERN_WARNING "HiSax: %s (%s): No IO base address(es)\n",
+		       CardType[card->typ],
+		       sct_quadro_subtypes[cs->subtyp]);
+		return (0);
+	}
+	pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK;
+	pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK;
+	/* Take over */
+	cs->irq = pci_irq;
+	cs->irq_flags |= SA_SHIRQ;
+	/* pci_ioaddr1 is unique to all subdevices */
+	/* pci_ioaddr2 is for the fourth subdevice only */
+	/* pci_ioaddr3 is for the third subdevice only */
+	/* pci_ioaddr4 is for the second subdevice only */
+	/* pci_ioaddr5 is for the first subdevice only */
+	cs->hw.ax.plx_adr = pci_ioaddr1;
+	/* Enter all ipac_base addresses */
+	switch(cs->subtyp) {
+		case 1:
+			cs->hw.ax.base = pci_ioaddr5 + 0x00;
+			if (sct_alloc_io(pci_ioaddr1, 128))
+				return(0);
+			if (sct_alloc_io(pci_ioaddr5, 64))
+				return(0);
+			/* disable all IPAC */
+			writereg(pci_ioaddr5, pci_ioaddr5 + 4,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr4 + 0x08, pci_ioaddr4 + 0x0c,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr3 + 0x10, pci_ioaddr3 + 0x14,
+				IPAC_MASK, 0xFF);
+			writereg(pci_ioaddr2 + 0x20, pci_ioaddr2 + 0x24,
+				IPAC_MASK, 0xFF);
+			break;
+		case 2:
+			cs->hw.ax.base = pci_ioaddr4 + 0x08;
+			if (sct_alloc_io(pci_ioaddr4, 64))
+				return(0);
+			break;
+		case 3:
+			cs->hw.ax.base = pci_ioaddr3 + 0x10;
+			if (sct_alloc_io(pci_ioaddr3, 64))
+				return(0);
+			break;
+		case 4:
+			cs->hw.ax.base = pci_ioaddr2 + 0x20;
+			if (sct_alloc_io(pci_ioaddr2, 64))
+				return(0);
+			break;
+	}	
+	/* For isac and hscx data path */
+	cs->hw.ax.data_adr = cs->hw.ax.base + 4;
+
+	printk(KERN_INFO "HiSax: %s (%s) configured at 0x%.4lX, 0x%.4lX, 0x%.4lX and IRQ %d\n",
+	       CardType[card->typ],
+	       sct_quadro_subtypes[cs->subtyp],
+	       cs->hw.ax.plx_adr,
+	       cs->hw.ax.base,
+	       cs->hw.ax.data_adr,
+	       cs->irq);
+
+	test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &BKM_card_msg;
+	cs->irq_func = &bkm_interrupt_ipac;
+
+	printk(KERN_INFO "HiSax: %s (%s): IPAC Version %d\n",
+		CardType[card->typ],
+		sct_quadro_subtypes[cs->subtyp],
+		readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID));
+	return (1);
+#else
+	printk(KERN_ERR "HiSax: bkm_a8 only supported on PCI Systems\n");
+#endif /* CONFIG_PCI */
+}
diff --git a/drivers/isdn/hisax/bkm_ax.h b/drivers/isdn/hisax/bkm_ax.h
new file mode 100644
index 0000000..029e0a2
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_ax.h
@@ -0,0 +1,119 @@
+/* $Id: bkm_ax.h,v 1.5.6.3 2001/09/23 22:24:46 kai Exp $
+ *
+ * low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef	__BKM_AX_H__
+#define	__BKM_AX_H__
+
+/* Supported boards	(subtypes) */
+#define SCT_1		1
+#define	SCT_2		2
+#define	SCT_3		3
+#define	SCT_4		4
+#define BKM_A4T		5
+
+#define	PLX_ADDR_PLX		0x14	/* Addr PLX configuration */
+#define	PLX_ADDR_ISAC		0x18	/* Addr ISAC */
+#define	PLX_ADDR_HSCX		0x1C	/* Addr HSCX */
+#define	PLX_ADDR_ALE		0x20	/* Addr ALE */
+#define	PLX_ADDR_ALEPLUS	0x24	/* Next Addr behind ALE */
+
+#define	PLX_SUBVEN		0x2C	/* Offset SubVendor */
+#define	PLX_SUBSYS		0x2E	/* Offset SubSystem */
+
+
+/* Application specific registers I20 (Siemens SZB6120H) */
+typedef	struct {
+		/* Video front end horizontal configuration register */
+	volatile u_int	i20VFEHorzCfg;	/* Offset 00 */
+		/* Video front end vertical configuration register */
+	volatile u_int	i20VFEVertCfg;	/* Offset 04 */	
+		/* Video front end scaler and pixel format register */
+	volatile u_int	i20VFEScaler;	/* Offset 08 */	
+		/* Video display top register */
+	volatile u_int	i20VDispTop;   	/* Offset 0C */	
+		/* Video display bottom register */
+	volatile u_int	i20VDispBottom;	/* Offset 10 */	
+		/* Video stride, status and frame grab register */
+	volatile u_int	i20VidFrameGrab;/* Offset 14 */	
+		/* Video display configuration register */
+	volatile u_int	i20VDispCfg;	/* Offset 18 */	
+		/* Video masking map top */
+	volatile u_int	i20VMaskTop;	/* Offset 1C */	
+		/* Video masking map bottom */
+	volatile u_int	i20VMaskBottom;	/* Offset 20 */	
+		/* Overlay control register */
+	volatile u_int 	i20OvlyControl;	/* Offset 24 */	
+		/* System, PCI and general purpose pins control register */
+ 	volatile u_int	i20SysControl; 	/* Offset 28 */	
+#define	sysRESET		0x01000000	/* bit 24:Softreset (Low)		*/
+			/* GPIO 4...0: Output fixed for our cfg! */
+#define	sysCFG			0x000000E0	/* GPIO 7,6,5: Input */
+	/* General purpose pins and guest bus control register */
+ 	volatile u_int	i20GuestControl;/* Offset 2C */	
+#define	guestWAIT_CFG	0x00005555	/* 4 PCI waits for all */
+#define	guestISDN_INT_E	0x01000000	/* ISDN Int en (low) */
+#define	guestVID_INT_E 	0x02000000	/* Video interrupt en (low) */
+#define	guestADI1_INT_R	0x04000000	/* ADI #1 int req (low) */
+#define	guestADI2_INT_R	0x08000000	/* ADI #2 int req (low) */
+#define	guestISDN_RES	0x10000000	/* ISDN reset bit (high) */
+#define	guestADI1_INT_S	0x20000000	/* ADI #1 int pending (low) */
+#define	guestADI2_INT_S	0x40000000	/* ADI #2 int pending (low) */
+#define	guestISDN_INT_S	0x80000000	/* ISAC int pending (low) */
+
+#define	g_A4T_JADE_RES	0x01000000	/* JADE Reset (High) */
+#define	g_A4T_ISAR_RES	0x02000000	/* ISAR Reset (High) */
+#define	g_A4T_ISAC_RES	0x04000000	/* ISAC Reset (High) */
+#define	g_A4T_JADE_BOOTR 0x08000000	/* JADE enable boot SRAM (Low) NOT USED */
+#define	g_A4T_ISAR_BOOTR 0x10000000	/* ISAR enable boot SRAM (Low) NOT USED */
+#define	g_A4T_JADE_INT_S 0x20000000	/* JADE interrupt pnd (Low) */
+#define	g_A4T_ISAR_INT_S 0x40000000	/* ISAR interrupt pnd (Low) */
+#define	g_A4T_ISAC_INT_S 0x80000000	/* ISAC interrupt pnd (Low) */
+
+ 	volatile u_int	i20CodeSource;	/* Offset 30 */	
+ 	volatile u_int	i20CodeXferCtrl;/* Offset 34 */	
+ 	volatile u_int	i20CodeMemPtr;	/* Offset 38 */	
+
+  	volatile u_int	i20IntStatus;	/* Offset 3C */	
+ 	volatile u_int	i20IntCtrl;	/* Offset 40 */	
+#define	intISDN		0x40000000	/* GIRQ1En (ISAC/ADI) (High) */
+#define	intVID		0x20000000	/* GIRQ0En (VSYNC)    (High) */
+#define	intCOD		0x10000000	/* CodRepIrqEn        (High) */
+#define	intPCI 		0x01000000	/* PCI IntA enable    (High) */
+
+ 	volatile u_int	i20I2CCtrl;	/* Offset 44					*/	
+} I20_REGISTER_FILE, *PI20_REGISTER_FILE;
+
+/*
+ * Postoffice structure for A4T
+ *
+ */
+#define	PO_OFFSET	0x00000200	/* Postoffice offset from base */
+
+#define	GCS_0		0x00000000 	/* Guest bus chip selects */
+#define	GCS_1		0x00100000
+#define	GCS_2		0x00200000
+#define	GCS_3		0x00300000
+
+#define	PO_READ		0x00000000	/* R/W from/to guest bus */
+#define	PO_WRITE	0x00800000
+
+#define	PO_PEND		0x02000000
+
+#define POSTOFFICE(postoffice) *(volatile unsigned int*)(postoffice)
+
+/* Wait unlimited (don't worry)										*/ 
+#define	__WAITI20__(postoffice)										\
+do {		   	 		   											\
+  	while ((POSTOFFICE(postoffice) & PO_PEND)) ;					\
+} while (0)
+
+#endif	/* __BKM_AX_H__ */
diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c
new file mode 100644
index 0000000..04065ab
--- /dev/null
+++ b/drivers/isdn/hisax/callc.c
@@ -0,0 +1,1793 @@
+/* $Id: callc.c,v 2.59.2.4 2004/02/11 13:21:32 keil Exp $
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * based on the teles driver from Jan den Ouden
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include <linux/isdn/capicmd.h>
+
+const char *lli_revision = "$Revision: 2.59.2.4 $";
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+
+static int init_b_st(struct Channel *chanp, int incoming);
+static void release_b_st(struct Channel *chanp);
+
+static struct Fsm callcfsm;
+static int chancount;
+
+/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
+#define ALERT_REJECT 0
+
+/* Value to delay the sending of the first B-channel paket after CONNECT
+ * here is no value given by ITU, but experience shows that 300 ms will
+ * work on many networks, if you or your other side is behind local exchanges
+ * a greater value may be recommented. If the delay is to short the first paket
+ * will be lost and autodetect on many comercial routers goes wrong !
+ * You can adjust this value on runtime with
+ * hisaxctrl <id> 2 <value>
+ * value is in milliseconds
+ */
+#define DEFAULT_B_DELAY	300
+
+/* Flags for remembering action done in lli */
+
+#define  FLG_START_B	0
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState *
+hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return (cards[i].cs);
+	return (struct IsdnCardState *) 0;
+}
+
+static void
+link_debug(struct Channel *chanp, int direction, char *fmt, ...)
+{
+	va_list args;
+	char tmp[16];
+
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d %s ", chanp->chan,
+		direction ? "LL->HL" : "HL->LL");
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
+}
+
+enum {
+	ST_NULL,		/*  0 inactive */
+	ST_OUT_DIAL,		/*  1 outgoing, SETUP send; awaiting confirm */
+	ST_IN_WAIT_LL,		/*  2 incoming call received; wait for LL confirm */
+	ST_IN_ALERT_SENT,	/*  3 incoming call received; ALERT send */
+	ST_IN_WAIT_CONN_ACK,	/*  4 incoming CONNECT send; awaiting CONN_ACK */
+	ST_WAIT_BCONN,		/*  5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
+	ST_ACTIVE,		/*  6 active, b channel prot. established */
+	ST_WAIT_BRELEASE,	/*  7 call clear. (initiator), awaiting b channel prot. rel. */
+	ST_WAIT_BREL_DISC,	/*  8 call clear. (receiver), DISCONNECT req. received */
+	ST_WAIT_DCOMMAND,	/*  9 call clear. (receiver), awaiting DCHANNEL message */
+	ST_WAIT_DRELEASE,	/* 10 DISCONNECT sent, awaiting RELEASE */
+	ST_WAIT_D_REL_CNF,	/* 11 RELEASE sent, awaiting RELEASE confirm */
+	ST_IN_PROCEED_SEND,	/* 12 incoming call, proceeding send */ 
+};
+  
+
+#define STATE_COUNT (ST_IN_PROCEED_SEND + 1)
+
+static char *strState[] =
+{
+	"ST_NULL",
+	"ST_OUT_DIAL",
+	"ST_IN_WAIT_LL",
+	"ST_IN_ALERT_SENT",
+	"ST_IN_WAIT_CONN_ACK",
+	"ST_WAIT_BCONN",
+	"ST_ACTIVE",
+	"ST_WAIT_BRELEASE",
+	"ST_WAIT_BREL_DISC",
+	"ST_WAIT_DCOMMAND",
+	"ST_WAIT_DRELEASE",
+	"ST_WAIT_D_REL_CNF",
+	"ST_IN_PROCEED_SEND",
+};
+
+enum {
+	EV_DIAL,		/*  0 */
+	EV_SETUP_CNF,		/*  1 */
+	EV_ACCEPTB,		/*  2 */
+	EV_DISCONNECT_IND,	/*  3 */
+	EV_RELEASE, 		/*  4 */
+	EV_LEASED,		/*  5 */
+	EV_LEASED_REL,		/*  6 */
+	EV_SETUP_IND,		/*  7 */
+	EV_ACCEPTD,		/*  8 */
+	EV_SETUP_CMPL_IND,	/*  9 */
+	EV_BC_EST,		/* 10 */
+	EV_WRITEBUF,		/* 11 */
+	EV_HANGUP,		/* 12 */
+	EV_BC_REL,		/* 13 */
+	EV_CINF,		/* 14 */
+	EV_SUSPEND,		/* 15 */
+	EV_RESUME,		/* 16 */
+	EV_NOSETUP_RSP,		/* 17 */
+	EV_SETUP_ERR,		/* 18 */
+	EV_CONNECT_ERR,		/* 19 */
+	EV_PROCEED,		/* 20 */
+	EV_ALERT,		/* 21 */ 
+	EV_REDIR,		/* 22 */ 
+};
+
+#define EVENT_COUNT (EV_REDIR + 1)
+
+static char *strEvent[] =
+{
+	"EV_DIAL",
+	"EV_SETUP_CNF",
+	"EV_ACCEPTB",
+	"EV_DISCONNECT_IND",
+	"EV_RELEASE",
+	"EV_LEASED",
+	"EV_LEASED_REL",
+	"EV_SETUP_IND",
+	"EV_ACCEPTD",
+	"EV_SETUP_CMPL_IND",
+	"EV_BC_EST",
+	"EV_WRITEBUF",
+	"EV_HANGUP",
+	"EV_BC_REL",
+	"EV_CINF",
+	"EV_SUSPEND",
+	"EV_RESUME",
+	"EV_NOSETUP_RSP",
+	"EV_SETUP_ERR",
+	"EV_CONNECT_ERR",
+	"EV_PROCEED",
+	"EV_ALERT",
+	"EV_REDIR",
+};
+
+
+static inline void
+HL_LL(struct Channel *chanp, int command)
+{
+	isdn_ctrl ic;
+
+	ic.driver = chanp->cs->myid;
+	ic.command = command;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+}
+
+static inline void
+lli_deliver_cause(struct Channel *chanp)
+{
+	isdn_ctrl ic;
+
+	if (!chanp->proc)
+		return;
+	if (chanp->proc->para.cause == NO_CAUSE)
+		return;
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	if (chanp->cs->protocol == ISDN_PTYPE_EURO)
+		sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
+	else
+		sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
+			chanp->proc->para.cause & 0x7f);
+	chanp->cs->iif.statcallb(&ic);
+}
+
+static inline void
+lli_close(struct FsmInst *fi)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_NULL);
+	chanp->Flags = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+}
+
+static void
+lli_leased_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
+
+	if (!chanp->leased)
+		return;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	FsmChangeState(fi, ST_IN_WAIT_LL);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_ICALL_LEASED");
+	ic.driver = chanp->cs->myid;
+	ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW);
+	ic.arg = chanp->chan;
+	ic.parm.setup.si1 = 7;
+	ic.parm.setup.si2 = 0;
+	ic.parm.setup.plan = 0;
+	ic.parm.setup.screen = 0;
+	sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
+	sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
+	ret = chanp->cs->iif.statcallb(&ic);
+	if (chanp->debug & 1)
+		link_debug(chanp, 1, "statcallb ret=%d", ret);
+	if (!ret) {
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+		FsmChangeState(fi, ST_NULL);
+	}
+}
+
+
+/*
+ * Dial out
+ */
+static void
+lli_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DCONN");
+	HL_LL(chanp, ISDN_STAT_DCONN);
+	init_b_st(chanp, 0);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lli_prep_dialout(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (chanp->leased) {
+		lli_init_bchan_out(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_OUT_DIAL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp);
+	}
+}
+
+static void
+lli_resume(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmDelTimer(&chanp->drel_timer, 60);
+	FsmDelTimer(&chanp->dial_timer, 73);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	if (chanp->leased) {
+		lli_init_bchan_out(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_OUT_DIAL);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp);
+	}
+}
+
+static void
+lli_go_active(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+
+	FsmChangeState(fi, ST_ACTIVE);
+	chanp->data_open = !0;
+	if (chanp->bcs->conmsg)
+		strcpy(ic.parm.num, chanp->bcs->conmsg);
+	else
+		ic.parm.num[0] = 0;
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BCONN %s", ic.parm.num);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_BCONN;
+	ic.arg = chanp->chan;
+	chanp->cs->iif.statcallb(&ic);
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan);
+}
+
+
+/*
+ * RESUME
+ */
+
+/* incoming call */
+
+static void
+lli_deliver_call(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+	int ret;
+
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+	/*
+	 * Report incoming calls only once to linklevel, use CallFlags
+	 * which is set to 3 with each broadcast message in isdnl1.c
+	 * and resetted if a interface  answered the STAT_ICALL.
+	 */
+	if (1) { /* for only one TEI */
+		FsmChangeState(fi, ST_IN_WAIT_LL);
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, (chanp->chan < 2) ? "STAT_ICALL" : "STAT_ICALLW");
+		ic.driver = chanp->cs->myid;
+		ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW);
+
+		ic.arg = chanp->chan;
+		/*
+		 * No need to return "unknown" for calls without OAD,
+		 * cause that's handled in linklevel now (replaced by '0')
+		 */
+		memcpy(&ic.parm.setup, &chanp->proc->para.setup, sizeof(setup_parm));
+		ret = chanp->cs->iif.statcallb(&ic);
+		if (chanp->debug & 1)
+			link_debug(chanp, 1, "statcallb ret=%d", ret);
+
+		switch (ret) {
+			case 1:	/* OK, someone likes this call */
+				FsmDelTimer(&chanp->drel_timer, 61);
+				FsmChangeState(fi, ST_IN_ALERT_SENT);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+				break;
+			case 5: /* direct redirect */
+			case 4: /* Proceeding desired */
+				FsmDelTimer(&chanp->drel_timer, 61);
+				FsmChangeState(fi, ST_IN_PROCEED_SEND);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
+				if (ret == 5) {
+					memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm));
+					chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+				}
+				break;
+			case 2:	/* Rejecting Call */
+				break;
+			case 3:	/* incomplete number */
+				FsmDelTimer(&chanp->drel_timer, 61);
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
+				break;
+			case 0:	/* OK, nobody likes this call */
+			default:	/* statcallb problems */
+				chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+				chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+				FsmChangeState(fi, ST_NULL);
+				break;
+		}
+	} else {
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+		chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+	}
+}
+
+static void
+lli_send_dconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+}
+
+static void
+lli_send_alert(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_IN_ALERT_SENT);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+}
+
+static void
+lli_send_redir(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+}
+
+static void
+lli_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_BCONN);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DCONN");
+	HL_LL(chanp, ISDN_STAT_DCONN);
+	chanp->l2_active_protocol = chanp->l2_protocol;
+	chanp->incoming = !0;
+	init_b_st(chanp, !0);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lli_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_init_bchan_in(fi, event, arg);
+	} else {
+		FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+#ifdef WANT_ALERT
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+#endif
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+	}
+}
+
+/* Call suspend */
+
+static void
+lli_suspend(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
+}
+
+/* Call clearing */
+
+static void
+lli_leased_hup(struct FsmInst *fi, struct Channel *chanp)
+{
+	isdn_ctrl ic;
+
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "L0010");
+	chanp->cs->iif.statcallb(&ic);
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	lli_close(fi);
+}
+
+static void
+lli_disconnect_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		if (chanp->proc)
+			chanp->proc->para.cause = 0x10;	/* Normal Call Clearing */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+			chanp->proc);
+	}
+}
+
+static void
+lli_disconnect_reject(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_DRELEASE);
+		if (chanp->proc)
+			chanp->proc->para.cause = 0x15;	/* Call Rejected */
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+			chanp->proc);
+	}
+}
+
+static void
+lli_dhup_close(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		if (chanp->debug & 1)
+			link_debug(chanp, 0, "STAT_DHUP");
+		lli_deliver_cause(chanp);
+		HL_LL(chanp, ISDN_STAT_DHUP);
+		lli_close(fi);
+	}
+}
+
+static void
+lli_reject_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+		return;
+	}
+#ifndef ALERT_REJECT
+	if (chanp->proc)
+		chanp->proc->para.cause = 0x15;	/* Call Rejected */
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
+	lli_dhup_close(fi, event, arg);
+#else
+	FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
+	FsmChangeState(fi, ST_IN_ALERT_SENT);
+	chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+#endif
+}
+
+static void
+lli_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_BRELEASE);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+}
+
+static void
+lli_start_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		lli_disconnect_req(fi, event, arg);
+	}
+}
+
+static void
+lli_rel_b_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_start_disc(fi, event, arg);
+}
+
+static void
+lli_bhup_disc(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+ 
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_disc(fi, event, arg);
+}
+
+static void
+lli_bhup_rel_b(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	FsmChangeState(fi, ST_WAIT_DCOMMAND);
+	chanp->data_open = 0;
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	release_b_st(chanp);
+}
+
+static void
+lli_release_bchan(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	FsmChangeState(fi, ST_WAIT_BREL_DISC);
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+}
+
+
+static void
+lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_dhup_close(fi, event, arg);
+}
+
+static void
+lli_bhup_dhup(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_dhup(fi, event, arg);
+}
+
+static void
+lli_abort(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	lli_bhup_dhup(fi, event, arg);
+}
+ 
+static void
+lli_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->leased) {
+		lli_leased_hup(fi, chanp);
+	} else {
+		FsmChangeState(fi, ST_WAIT_D_REL_CNF);
+		chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST,
+			chanp->proc);
+	}
+}
+
+static void
+lli_rel_b_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_release_req(fi, event, arg);
+}
+
+static void
+lli_bhup_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+ 
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_release_req(fi, event, arg);
+}
+
+
+/* processing charge info */
+static void
+lli_charge_info(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CINF;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo);
+	chanp->cs->iif.statcallb(&ic);
+}
+
+/* error procedures */
+
+static void
+lli_dchan_not_ready(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	HL_LL(chanp, ISDN_STAT_DHUP); 
+}
+
+static void
+lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_DHUP");
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	lli_close(fi); 
+}
+
+static void
+lli_error(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_WAIT_DRELEASE);
+}
+
+static void
+lli_failure_l(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+	isdn_ctrl ic;
+
+	FsmChangeState(fi, ST_NULL);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_CAUSE;
+	ic.arg = chanp->chan;
+	sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f);
+	chanp->cs->iif.statcallb(&ic);
+	HL_LL(chanp, ISDN_STAT_DHUP);
+	chanp->Flags = 0;
+	chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+}
+
+static void
+lli_rel_b_fail(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	release_b_st(chanp);
+	lli_failure_l(fi, event, arg);
+}
+
+static void
+lli_bhup_fail(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	if (chanp->debug & 1)
+		link_debug(chanp, 0, "STAT_BHUP");
+	HL_LL(chanp, ISDN_STAT_BHUP);
+	lli_rel_b_fail(fi, event, arg);
+}
+
+static void
+lli_failure_a(struct FsmInst *fi, int event, void *arg)
+{
+	struct Channel *chanp = fi->userdata;
+
+	chanp->data_open = 0;
+	chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+	lli_bhup_fail(fi, event, arg);
+}
+
+/* *INDENT-OFF* */
+static struct FsmNode fnlist[] __initdata =
+{
+        {ST_NULL,               EV_DIAL,                lli_prep_dialout},
+        {ST_NULL,               EV_RESUME,              lli_resume},
+        {ST_NULL,               EV_SETUP_IND,           lli_deliver_call},
+        {ST_NULL,               EV_LEASED,              lli_leased_in},
+        {ST_OUT_DIAL,           EV_SETUP_CNF,           lli_init_bchan_out},
+        {ST_OUT_DIAL,           EV_HANGUP,              lli_disconnect_req},
+        {ST_OUT_DIAL,           EV_DISCONNECT_IND,      lli_release_req},
+        {ST_OUT_DIAL,           EV_RELEASE,             lli_dhup_close},
+        {ST_OUT_DIAL,           EV_NOSETUP_RSP,         lli_no_setup_rsp},
+        {ST_OUT_DIAL,           EV_SETUP_ERR,           lli_error},
+        {ST_IN_WAIT_LL,         EV_LEASED_REL,          lli_failure_l},
+        {ST_IN_WAIT_LL,         EV_ACCEPTD,             lli_setup_rsp},
+        {ST_IN_WAIT_LL,         EV_HANGUP,              lli_reject_req},
+        {ST_IN_WAIT_LL,         EV_DISCONNECT_IND,      lli_release_req},
+        {ST_IN_WAIT_LL,         EV_RELEASE,             lli_dhup_close},
+        {ST_IN_WAIT_LL,         EV_SETUP_IND,           lli_deliver_call},
+        {ST_IN_WAIT_LL,         EV_SETUP_ERR,           lli_error},
+        {ST_IN_ALERT_SENT,      EV_SETUP_CMPL_IND,      lli_init_bchan_in},
+        {ST_IN_ALERT_SENT,      EV_ACCEPTD,             lli_send_dconnect},
+        {ST_IN_ALERT_SENT,      EV_HANGUP,              lli_disconnect_reject},
+        {ST_IN_ALERT_SENT,      EV_DISCONNECT_IND,      lli_release_req},
+        {ST_IN_ALERT_SENT,      EV_RELEASE,             lli_dhup_close},
+	{ST_IN_ALERT_SENT,	EV_REDIR,		lli_send_redir},
+	{ST_IN_PROCEED_SEND,	EV_REDIR,		lli_send_redir},
+	{ST_IN_PROCEED_SEND,	EV_ALERT,		lli_send_alert},
+	{ST_IN_PROCEED_SEND,	EV_ACCEPTD,		lli_send_dconnect},
+	{ST_IN_PROCEED_SEND,	EV_HANGUP,		lli_disconnect_reject},
+	{ST_IN_PROCEED_SEND,	EV_DISCONNECT_IND,	lli_dhup_close},
+        {ST_IN_ALERT_SENT,      EV_RELEASE,             lli_dhup_close},
+        {ST_IN_WAIT_CONN_ACK,   EV_SETUP_CMPL_IND,      lli_init_bchan_in},
+        {ST_IN_WAIT_CONN_ACK,   EV_HANGUP,              lli_disconnect_req},
+        {ST_IN_WAIT_CONN_ACK,   EV_DISCONNECT_IND,      lli_release_req},
+        {ST_IN_WAIT_CONN_ACK,   EV_RELEASE,             lli_dhup_close},
+        {ST_IN_WAIT_CONN_ACK,   EV_CONNECT_ERR,         lli_error},
+        {ST_WAIT_BCONN,         EV_BC_EST,              lli_go_active},
+        {ST_WAIT_BCONN,         EV_BC_REL,              lli_rel_b_disc},
+        {ST_WAIT_BCONN,         EV_HANGUP,              lli_rel_b_disc},
+        {ST_WAIT_BCONN,         EV_DISCONNECT_IND,      lli_rel_b_release_req},
+        {ST_WAIT_BCONN,         EV_RELEASE,             lli_rel_b_dhup},
+        {ST_WAIT_BCONN,         EV_LEASED_REL,          lli_rel_b_fail},
+        {ST_WAIT_BCONN,         EV_CINF,                lli_charge_info},
+        {ST_ACTIVE,             EV_CINF,                lli_charge_info},
+        {ST_ACTIVE,             EV_BC_REL,              lli_bhup_rel_b},
+        {ST_ACTIVE,             EV_SUSPEND,             lli_suspend},
+        {ST_ACTIVE,             EV_HANGUP,              lli_disconn_bchan},
+        {ST_ACTIVE,             EV_DISCONNECT_IND,      lli_release_bchan},
+        {ST_ACTIVE,             EV_RELEASE,             lli_abort},
+        {ST_ACTIVE,             EV_LEASED_REL,          lli_failure_a},
+        {ST_WAIT_BRELEASE,      EV_BC_REL,              lli_bhup_disc},
+        {ST_WAIT_BRELEASE,      EV_DISCONNECT_IND,      lli_bhup_release_req},
+        {ST_WAIT_BRELEASE,      EV_RELEASE,             lli_bhup_dhup},
+        {ST_WAIT_BRELEASE,      EV_LEASED_REL,          lli_bhup_fail},
+        {ST_WAIT_BREL_DISC,     EV_BC_REL,              lli_bhup_release_req},
+        {ST_WAIT_BREL_DISC,     EV_RELEASE,             lli_bhup_dhup},
+        {ST_WAIT_DCOMMAND,      EV_HANGUP,              lli_start_disc},
+        {ST_WAIT_DCOMMAND,      EV_DISCONNECT_IND,      lli_release_req},
+        {ST_WAIT_DCOMMAND,      EV_RELEASE,             lli_dhup_close},
+        {ST_WAIT_DCOMMAND,      EV_LEASED_REL,          lli_failure_l},
+        {ST_WAIT_DRELEASE,      EV_RELEASE,             lli_dhup_close},
+        {ST_WAIT_DRELEASE,      EV_DIAL,                lli_dchan_not_ready},
+  /* ETS 300-104 16.1 */
+        {ST_WAIT_D_REL_CNF,     EV_RELEASE,             lli_dhup_close},
+        {ST_WAIT_D_REL_CNF,     EV_DIAL,                lli_dchan_not_ready},
+};
+/* *INDENT-ON* */
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+int __init
+CallcNew(void)
+{
+	callcfsm.state_count = STATE_COUNT;
+	callcfsm.event_count = EVENT_COUNT;
+	callcfsm.strEvent = strEvent;
+	callcfsm.strState = strState;
+	return FsmNew(&callcfsm, fnlist, FNCOUNT);
+}
+
+void
+CallcFree(void)
+{
+	FsmFree(&callcfsm);
+}
+
+static void
+release_b_st(struct Channel *chanp)
+{
+	struct PStack *st = chanp->b_st;
+
+	if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) {
+		chanp->bcs->BC_Close(chanp->bcs);
+		switch (chanp->l2_active_protocol) {
+			case (ISDN_PROTO_L2_X75I):
+				releasestack_isdnl2(st);
+				break;
+			case (ISDN_PROTO_L2_HDLC):
+			case (ISDN_PROTO_L2_HDLC_56K):
+			case (ISDN_PROTO_L2_TRANS):
+			case (ISDN_PROTO_L2_MODEM):
+			case (ISDN_PROTO_L2_FAX):
+				releasestack_transl2(st);
+				break;
+		}
+	} 
+}
+
+struct Channel
+*selectfreechannel(struct PStack *st, int bch)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp = st->lli.userdata;
+	int i;
+
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		i=1;
+	else
+		i=0;
+
+	if (!bch) {
+		i = 2; /* virtual channel */
+		chanp += 2;
+	}
+
+	while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) {
+		if (chanp->fi.state == ST_NULL)
+			return (chanp);
+		chanp++;
+		i++;
+	}
+
+	if (bch) /* number of channels is limited */ {
+		i = 2; /* virtual channel */
+		chanp = st->lli.userdata;
+		chanp += i;
+		while (i < (2 + MAX_WAITING_CALLS)) {
+			if (chanp->fi.state == ST_NULL)
+				return (chanp);
+			chanp++;
+			i++;
+		}
+	}
+	return (NULL);
+}
+
+static void stat_redir_result(struct IsdnCardState *cs, int chan, ulong result)
+{	isdn_ctrl ic;
+  
+	ic.driver = cs->myid;
+	ic.command = ISDN_STAT_REDIR;
+	ic.arg = chan; 
+	ic.parm.num[0] = result;
+	cs->iif.statcallb(&ic);
+} /* stat_redir_result */
+
+static void
+dchan_l3l4(struct PStack *st, int pr, void *arg)
+{
+	struct l3_process *pc = arg;
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct Channel *chanp;
+
+	if(!pc)
+		return;
+
+	if (pr == (CC_SETUP | INDICATION)) {
+		if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) {
+			pc->para.cause = 0x11;	/* User busy */
+			pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc);
+		} else {
+			chanp->proc = pc;
+			pc->chan = chanp;
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+		}
+		return;
+	}
+	if (!(chanp = pc->chan))
+		return;
+
+	switch (pr) {
+		case (CC_MORE_INFO | INDICATION):
+			FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+			break;
+		case (CC_DISCONNECT | INDICATION):
+			FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+			break;
+		case (CC_RELEASE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_SUSPEND | CONFIRM):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_RESUME | CONFIRM):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_RESUME_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_RELEASE | INDICATION):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_SETUP_COMPL | INDICATION):
+			FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+			break;
+		case (CC_SETUP | CONFIRM):
+			FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+			break;
+		case (CC_CHARGE | INDICATION):
+			FsmEvent(&chanp->fi, EV_CINF, NULL);
+			break;
+		case (CC_NOSETUP_RSP):
+			FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
+			break;
+		case (CC_SETUP_ERR):
+			FsmEvent(&chanp->fi, EV_SETUP_ERR, NULL);
+			break;
+		case (CC_CONNECT_ERR):
+			FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL);
+			break;
+		case (CC_RELEASE_ERR):
+			FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+			break;
+		case (CC_PROCEED_SEND | INDICATION):
+		case (CC_PROCEEDING | INDICATION):
+		case (CC_ALERTING | INDICATION):
+		case (CC_PROGRESS | INDICATION):
+		case (CC_NOTIFY | INDICATION):
+			break;
+		case (CC_REDIR | INDICATION):
+			stat_redir_result(cs, chanp->chan, pc->redir_result); 
+			break;
+			default:
+			if (chanp->debug & 0x800) {
+				HiSax_putstatus(chanp->cs, "Ch",
+					"%d L3->L4 unknown primitiv %#x",
+					chanp->chan, pr);
+			}
+	}
+}
+
+static void
+dummy_pstack(struct PStack *st, int pr, void *arg) {
+	printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg);
+}
+
+static int
+init_PStack(struct PStack **stp) {
+	*stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+	if (!*stp)
+		return -ENOMEM;
+	(*stp)->next = NULL;
+	(*stp)->l1.l1l2 = dummy_pstack;
+	(*stp)->l1.l1hw = dummy_pstack;
+	(*stp)->l1.l1tei = dummy_pstack;
+	(*stp)->l2.l2tei = dummy_pstack;
+	(*stp)->l2.l2l1 = dummy_pstack;
+	(*stp)->l2.l2l3 = dummy_pstack;
+	(*stp)->l3.l3l2 = dummy_pstack;
+	(*stp)->l3.l3ml3 = dummy_pstack;
+	(*stp)->l3.l3l4 = dummy_pstack;
+	(*stp)->lli.l4l3 = dummy_pstack;
+	(*stp)->ma.layer = dummy_pstack;
+	return 0;
+}
+
+static int
+init_d_st(struct Channel *chanp)
+{
+	struct PStack *st;
+	struct IsdnCardState *cs = chanp->cs;
+	char tmp[16];
+	int err;
+
+	err = init_PStack(&chanp->d_st);
+	if (err)
+		return err;
+	st = chanp->d_st;
+	st->next = NULL;
+	HiSax_addlist(cs, st);
+	setstack_HiSax(st, cs);
+	st->l2.sap = 0;
+	st->l2.tei = -1;
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_MOD128, &st->l2.flag);
+	test_and_set_bit(FLG_LAPD, &st->l2.flag);
+	test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.maxlen = MAX_DFRAME_LEN;
+	st->l2.window = 1;
+	st->l2.T200 = 1000;	/* 1000 milliseconds  */
+	st->l2.N200 = 3;	/* try 3 times        */
+	st->l2.T203 = 10000;	/* 10000 milliseconds */
+	if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+		sprintf(tmp, "DCh%d Q.921 ", chanp->chan);
+	else
+		sprintf(tmp, "DCh Q.921 ");
+	setstack_isdnl2(st, tmp);
+	setstack_l3dc(st, chanp);
+	st->lli.userdata = chanp;
+	st->l3.l3l4 = dchan_l3l4;
+
+	return 0;
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct Channel *chanp = fi->userdata;
+	char tmp[16];
+
+	va_start(args, fmt);
+	sprintf(tmp, "Ch%d callc ", chanp->chan);
+	VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+	va_end(args);
+}
+
+static int
+init_chan(int chan, struct IsdnCardState *csta)
+{
+	struct Channel *chanp = csta->channel + chan;
+	int err;
+
+	chanp->cs = csta;
+	chanp->bcs = csta->bcs + chan;
+	chanp->chan = chan;
+	chanp->incoming = 0;
+	chanp->debug = 0;
+	chanp->Flags = 0;
+	chanp->leased = 0;
+	err = init_PStack(&chanp->b_st);
+	if (err)
+		return err;
+	chanp->b_st->l1.delay = DEFAULT_B_DELAY;
+	chanp->fi.fsm = &callcfsm;
+	chanp->fi.state = ST_NULL;
+	chanp->fi.debug = 0;
+	chanp->fi.userdata = chanp;
+	chanp->fi.printdebug = callc_debug;
+	FsmInitTimer(&chanp->fi, &chanp->dial_timer);
+	FsmInitTimer(&chanp->fi, &chanp->drel_timer);
+	if (!chan || (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags) && chan < 2)) {
+		err = init_d_st(chanp);
+		if (err)
+			return err;
+	} else {
+		chanp->d_st = csta->channel->d_st;
+	}
+	chanp->data_open = 0;
+	return 0;
+}
+
+int
+CallcNewChan(struct IsdnCardState *csta) {
+	int i, err;
+
+	chancount += 2;
+	err = init_chan(0, csta);
+	if (err)
+		return err;
+	err = init_chan(1, csta);
+	if (err)
+		return err;
+	printk(KERN_INFO "HiSax: 2 channels added\n");
+
+	for (i = 0; i < MAX_WAITING_CALLS; i++) { 
+		err = init_chan(i+2,csta);
+		if (err)
+			return err;
+	}
+	printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n");
+	if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
+		printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+		csta->channel->d_st->lli.l4l3(csta->channel->d_st,
+			DL_ESTABLISH | REQUEST, NULL);
+	}
+	return (0);
+}
+
+static void
+release_d_st(struct Channel *chanp)
+{
+	struct PStack *st = chanp->d_st;
+
+	if (!st)
+		return;
+	releasestack_isdnl2(st);
+	releasestack_isdnl3(st);
+	HiSax_rmlist(st->l1.hardware, st);
+	kfree(st);
+	chanp->d_st = NULL;
+}
+
+void
+CallcFreeChan(struct IsdnCardState *csta)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		FsmDelTimer(&csta->channel[i].drel_timer, 74);
+		FsmDelTimer(&csta->channel[i].dial_timer, 75);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
+			release_d_st(csta->channel + i);
+		if (csta->channel[i].b_st) {
+			release_b_st(csta->channel + i);
+			kfree(csta->channel[i].b_st);
+			csta->channel[i].b_st = NULL;
+		} else
+			printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
+		if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+			release_d_st(csta->channel + i);
+		} else
+			csta->channel[i].d_st = NULL;
+	}
+}
+
+static void
+lldata_handler(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (DL_DATA  | INDICATION):
+			if (chanp->data_open) {
+				if (chanp->debug & 0x800)
+					link_debug(chanp, 0, "lldata: %d", skb->len);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
+			} else {
+				link_debug(chanp, 0, "lldata: channel not open");
+				dev_kfree_skb(skb);
+			}
+			break;
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
+		default:
+			printk(KERN_WARNING "lldata_handler unknown primitive %#x\n",
+				pr);
+			break;
+	}
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			if (chanp->data_open) {
+				if (chanp->debug & 0x800)
+					link_debug(chanp, 0, "lltrans: %d", skb->len);
+				chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
+			} else {
+				link_debug(chanp, 0, "lltrans: channel not open");
+				dev_kfree_skb(skb);
+			}
+			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+			break;
+		default:
+			printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n",
+				pr);
+			break;
+	}
+}
+
+void
+lli_writewakeup(struct PStack *st, int len)
+{
+	struct Channel *chanp = st->lli.userdata;
+	isdn_ctrl ic;
+
+	if (chanp->debug & 0x800)
+		link_debug(chanp, 0, "llwakeup: %d", len);
+	ic.driver = chanp->cs->myid;
+	ic.command = ISDN_STAT_BSENT;
+	ic.arg = chanp->chan;
+	ic.parm.length = len;
+	chanp->cs->iif.statcallb(&ic);
+}
+
+static int
+init_b_st(struct Channel *chanp, int incoming)
+{
+	struct PStack *st = chanp->b_st;
+	struct IsdnCardState *cs = chanp->cs;
+	char tmp[16];
+
+	st->l1.hardware = cs;
+	if (chanp->leased)
+		st->l1.bc = chanp->chan & 1;
+	else
+		st->l1.bc = chanp->proc->para.bchannel - 1;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+		case (ISDN_PROTO_L2_HDLC):
+			st->l1.mode = L1_MODE_HDLC;
+			break;
+		case (ISDN_PROTO_L2_HDLC_56K):
+			st->l1.mode = L1_MODE_HDLC_56K;
+			break;
+		case (ISDN_PROTO_L2_TRANS):
+			st->l1.mode = L1_MODE_TRANS;
+			break;
+		case (ISDN_PROTO_L2_MODEM):
+			st->l1.mode = L1_MODE_V32;
+			break;
+		case (ISDN_PROTO_L2_FAX):
+			st->l1.mode = L1_MODE_FAX;
+			break;
+	}
+	chanp->bcs->conmsg = NULL;
+	if (chanp->bcs->BC_SetStack(st, chanp->bcs))
+		return (-1);
+	st->l2.flag = 0;
+	test_and_set_bit(FLG_LAPB, &st->l2.flag);
+	st->l2.maxlen = MAX_DATA_SIZE;
+	if (!incoming)
+		test_and_set_bit(FLG_ORIG, &st->l2.flag);
+	st->l2.T200 = 1000;	/* 1000 milliseconds */
+	st->l2.window = 7;
+	st->l2.N200 = 4;	/* try 4 times       */
+	st->l2.T203 = 5000;	/* 5000 milliseconds */
+	st->l3.debug = 0;
+	switch (chanp->l2_active_protocol) {
+		case (ISDN_PROTO_L2_X75I):
+			sprintf(tmp, "Ch%d X.75", chanp->chan);
+			setstack_isdnl2(st, tmp);
+			setstack_l3bc(st, chanp);
+			st->l2.l2l3 = lldata_handler;
+			st->lli.userdata = chanp;
+			test_and_clear_bit(FLG_LLI_L1WAKEUP, &st->lli.flag);
+			test_and_set_bit(FLG_LLI_L2WAKEUP, &st->lli.flag);
+			st->l2.l2m.debug = chanp->debug & 16;
+			st->l2.debug = chanp->debug & 64;
+			break;
+		case (ISDN_PROTO_L2_HDLC):
+		case (ISDN_PROTO_L2_HDLC_56K):
+		case (ISDN_PROTO_L2_TRANS):
+		case (ISDN_PROTO_L2_MODEM):
+		case (ISDN_PROTO_L2_FAX):
+			st->l1.l1l2 = lltrans_handler;
+			st->lli.userdata = chanp;
+			test_and_set_bit(FLG_LLI_L1WAKEUP, &st->lli.flag);
+			test_and_clear_bit(FLG_LLI_L2WAKEUP, &st->lli.flag);
+			setstack_transl2(st);
+			setstack_l3bc(st, chanp);
+			break;
+	}
+	test_and_set_bit(FLG_START_B, &chanp->Flags);
+	return (0);
+}
+
+static void
+leased_l4l3(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			break;
+		default:
+			printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n",
+				pr);
+			break;
+	}
+}
+
+static void
+leased_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct Channel *chanp = (struct Channel *) st->lli.userdata;
+	struct sk_buff *skb = arg;
+	int i,event = EV_LEASED_REL;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			link_debug(chanp, 0, "leased line d-channel DATA");
+			dev_kfree_skb(skb);
+			break;
+		case (PH_ACTIVATE | INDICATION):
+		case (PH_ACTIVATE | CONFIRM):
+			event = EV_LEASED;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags))
+				i = 1;
+			else
+				i = 0;
+			while (i < 2) {
+				FsmEvent(&chanp->fi, event, NULL);
+				chanp++;
+				i++;
+			}
+			break;
+		default:
+			printk(KERN_WARNING
+				"transd_l1l2 unknown primitive %#x\n", pr);
+			break;
+	}
+}
+
+static void
+distr_debug(struct IsdnCardState *csta, int debugflags)
+{
+	int i;
+	struct Channel *chanp = csta->channel;
+
+	for (i = 0; i < (2 + MAX_WAITING_CALLS) ; i++) {
+		chanp[i].debug = debugflags;
+		chanp[i].fi.debug = debugflags & 2;
+		chanp[i].d_st->l2.l2m.debug = debugflags & 8;
+		chanp[i].b_st->l2.l2m.debug = debugflags & 0x10;
+		chanp[i].d_st->l2.debug = debugflags & 0x20;
+		chanp[i].b_st->l2.debug = debugflags & 0x40;
+		chanp[i].d_st->l3.l3m.debug = debugflags & 0x80;
+		chanp[i].b_st->l3.l3m.debug = debugflags & 0x100;
+		chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200;
+		chanp[i].b_st->ma.debug = debugflags & 0x200;
+		chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000;
+		chanp[i].b_st->l1.l1m.debug = debugflags & 0x2000;
+	}
+	if (debugflags & 4)
+		csta->debug |= DEB_DLOG_HEX;
+	else
+		csta->debug &= ~DEB_DLOG_HEX;
+}
+
+static char tmpbuf[256];
+
+static void
+capi_debug(struct Channel *chanp, capi_msg *cm)
+{
+	char *t = tmpbuf;
+
+	t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length);
+	t--;
+	*t= 0;
+	HiSax_putstatus(chanp->cs, "Ch", "%d CAPIMSG %s", chanp->chan, tmpbuf);
+}
+
+void
+lli_got_fac_req(struct Channel *chanp, capi_msg *cm) {
+	if ((cm->para[0] != 3) || (cm->para[1] != 0))
+		return;
+	if (cm->para[2]<3)
+		return;
+	if (cm->para[4] != 0)
+		return;
+	switch(cm->para[3]) {
+		case 4: /* Suspend */
+			strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+			FsmEvent(&chanp->fi, EV_SUSPEND, cm);
+			break;
+		case 5: /* Resume */
+			strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+			if (chanp->fi.state == ST_NULL) {
+				FsmEvent(&chanp->fi, EV_RESUME, cm);
+			} else {
+				FsmDelTimer(&chanp->dial_timer, 72);
+				FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73);
+			}
+			break;
+	}
+}
+
+void
+lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) {
+	if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) ||
+		(cs->typ == ISDN_CTYPE_ELSA_PCI)) {
+		if (cs->hw.elsa.MFlag) {
+			cs->cardmsg(cs, CARD_AUX_IND, cm->para);
+		}
+	}
+}
+
+
+/***************************************************************/
+/* Limit the available number of channels for the current card */
+/***************************************************************/
+static int 
+set_channel_limit(struct IsdnCardState *cs, int chanmax)
+{
+	isdn_ctrl ic;
+	int i, ii;
+
+	if ((chanmax < 0) || (chanmax > 2))
+		return(-EINVAL);
+	cs->chanlimit = 0;
+	for (ii = 0; ii < 2; ii++) {
+		ic.driver = cs->myid;
+		ic.command = ISDN_STAT_DISCH;
+		ic.arg = ii;
+		if (ii >= chanmax)
+			ic.parm.num[0] = 0; /* disabled */
+		else
+			ic.parm.num[0] = 1; /* enabled */
+		i = cs->iif.statcallb(&ic); 
+		if (i) return(-EINVAL);
+		if (ii < chanmax) 
+			cs->chanlimit++;
+	}
+	return(0);
+} /* set_channel_limit */
+
+int
+HiSax_command(isdn_ctrl * ic)
+{
+	struct IsdnCardState *csta = hisax_findcard(ic->driver);
+	struct PStack *st;
+	struct Channel *chanp;
+	int i;
+	u_int num;
+
+	if (!csta) {
+		printk(KERN_ERR
+		"HiSax: if_command %d called with invalid driverId %d!\n",
+			ic->command, ic->driver);
+		return -ENODEV;
+	}
+	switch (ic->command) {
+		case (ISDN_CMD_SETEAZ):
+			chanp = csta->channel + ic->arg;
+			break;
+		case (ISDN_CMD_SETL2):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "SETL2 card %d %ld",
+					csta->cardnr + 1, ic->arg >> 8);
+			chanp->l2_protocol = ic->arg >> 8;
+			break;
+		case (ISDN_CMD_SETL3):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "SETL3 card %d %ld",
+					csta->cardnr + 1, ic->arg >> 8);
+			chanp->l3_protocol = ic->arg >> 8;
+			break;
+		case (ISDN_CMD_DIAL):
+			chanp = csta->channel + (ic->arg & 0xff);
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)",
+					ic->parm.setup.eazmsn, ic->parm.setup.phone,
+					ic->parm.setup.si1, ic->parm.setup.si2);
+			memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
+			if (!strcmp(chanp->setup.eazmsn, "0"))
+				chanp->setup.eazmsn[0] = '\0';
+			/* this solution is dirty and may be change, if
+			 * we make a callreference based callmanager */
+			if (chanp->fi.state == ST_NULL) {
+				FsmEvent(&chanp->fi, EV_DIAL, NULL);
+			} else {
+				FsmDelTimer(&chanp->dial_timer, 70);
+				FsmAddTimer(&chanp->dial_timer, 50, EV_DIAL, NULL, 71);
+			}
+			break;
+		case (ISDN_CMD_ACCEPTB):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "ACCEPTB");
+			FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+			break;
+		case (ISDN_CMD_ACCEPTD):
+			chanp = csta->channel + ic->arg;
+			memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "ACCEPTD");
+			FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+			break;
+		case (ISDN_CMD_HANGUP):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "HANGUP");
+			FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+			break;
+		case (CAPI_PUT_MESSAGE):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				capi_debug(chanp, &ic->parm.cmsg);
+			if (ic->parm.cmsg.Length < 8)
+				break;
+			switch(ic->parm.cmsg.Command) {
+				case CAPI_FACILITY:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_fac_req(chanp, &ic->parm.cmsg);
+					break;
+				case CAPI_MANUFACTURER:
+					if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+						lli_got_manufacturer(chanp, csta, &ic->parm.cmsg);
+					break;
+				default:
+					break;
+			}
+			break;
+		case (ISDN_CMD_IOCTL):
+			switch (ic->arg) {
+				case (0):
+					num = *(unsigned int *) ic->parm.num;
+					HiSax_reportcard(csta->cardnr, num);
+					break;
+				case (1):
+					num = *(unsigned int *) ic->parm.num;
+					distr_debug(csta, num);
+					printk(KERN_DEBUG "HiSax: debugging flags card %d set to %x\n",
+						csta->cardnr + 1, num);
+					HiSax_putstatus(csta, "debugging flags ",
+						"card %d set to %x", csta->cardnr + 1, num);
+					break;
+				case (2):
+					num = *(unsigned int *) ic->parm.num;
+					csta->channel[0].b_st->l1.delay = num;
+					csta->channel[1].b_st->l1.delay = num;
+					HiSax_putstatus(csta, "delay ", "card %d set to %d ms",
+						csta->cardnr + 1, num);
+					printk(KERN_DEBUG "HiSax: delay card %d set to %d ms\n",
+						csta->cardnr + 1, num);
+					break;
+				case (5):	/* set card in leased mode */
+					num = *(unsigned int *) ic->parm.num;
+					if ((num <1) || (num > 2)) {
+						HiSax_putstatus(csta, "Set LEASED ",
+							"wrong channel %d", num);
+						printk(KERN_WARNING "HiSax: Set LEASED wrong channel %d\n",
+							num);
+					} else {
+						num--;
+						chanp = csta->channel +num;
+						chanp->leased = 1;
+						HiSax_putstatus(csta, "Card",
+							"%d channel %d set leased mode\n",
+							csta->cardnr + 1, num + 1);
+						chanp->d_st->l1.l1l2 = leased_l1l2;
+						chanp->d_st->lli.l4l3 = leased_l4l3;
+						chanp->d_st->lli.l4l3(chanp->d_st,
+							DL_ESTABLISH | REQUEST, NULL);
+					}
+					break;
+				case (6):	/* set B-channel test loop */
+					num = *(unsigned int *) ic->parm.num;
+					if (csta->stlist)
+						csta->stlist->l2.l2l1(csta->stlist,
+							PH_TESTLOOP | REQUEST, (void *) (long)num);
+					break;
+				case (7):	/* set card in PTP mode */
+					num = *(unsigned int *) ic->parm.num;
+					if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+						printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n");
+					} else if (num) {
+						test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						csta->channel[0].d_st->l2.tei = 0;
+						HiSax_putstatus(csta, "set card ", "in PTP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
+						printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+						csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
+							DL_ESTABLISH | REQUEST, NULL);
+					} else {
+						test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+						test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+						HiSax_putstatus(csta, "set card ", "in PTMP mode");
+						printk(KERN_DEBUG "HiSax: set card in PTMP mode\n");
+					}
+					break;
+				case (8):	/* set card in FIXED TEI mode */
+					num = *(unsigned int *) ic->parm.num;
+					chanp = csta->channel + (num & 1);
+					num = num >>1;
+					if (num == 127) {
+						test_and_clear_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+						chanp->d_st->l2.tei = -1;
+						HiSax_putstatus(csta, "set card ", "in VAR TEI mode");
+						printk(KERN_DEBUG "HiSax: set card in VAR TEI mode\n");
+					} else {
+						test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+						chanp->d_st->l2.tei = num;
+						HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
+						printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
+							num);
+					}
+					chanp->d_st->lli.l4l3(chanp->d_st,
+						DL_ESTABLISH | REQUEST, NULL);
+					break;
+				case (11):
+					num = csta->debug & DEB_DLOG_HEX;
+					csta->debug = *(unsigned int *) ic->parm.num;
+					csta->debug |= num;
+					HiSax_putstatus(cards[0].cs, "l1 debugging ",
+						"flags card %d set to %x",
+						csta->cardnr + 1, csta->debug);
+					printk(KERN_DEBUG "HiSax: l1 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, csta->debug);
+					break;
+				case (13):
+					csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+					csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+					HiSax_putstatus(cards[0].cs, "l3 debugging ",
+						"flags card %d set to %x\n", csta->cardnr + 1,
+						*(unsigned int *) ic->parm.num);
+					printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n",
+						csta->cardnr + 1, *(unsigned int *) ic->parm.num);
+					break;
+				case (10):
+					i = *(unsigned int *) ic->parm.num;
+					return(set_channel_limit(csta, i));
+				default:
+					if (csta->auxcmd)
+						return(csta->auxcmd(csta, ic));
+					printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
+						(int) ic->arg);
+					return (-EINVAL);
+			}
+			break;
+		
+		case (ISDN_CMD_PROCEED):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "PROCEED");
+			FsmEvent(&chanp->fi, EV_PROCEED, NULL);
+			break;
+
+		case (ISDN_CMD_ALERT):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "ALERT");
+			FsmEvent(&chanp->fi, EV_ALERT, NULL);
+			break;
+
+		case (ISDN_CMD_REDIR):
+			chanp = csta->channel + ic->arg;
+			if (chanp->debug & 1)
+				link_debug(chanp, 1, "REDIR");
+			memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
+			FsmEvent(&chanp->fi, EV_REDIR, NULL);
+			break;
+
+		/* protocol specific io commands */
+		case (ISDN_CMD_PROT_IO):
+			for (st = csta->stlist; st; st = st->next)
+				if (st->protocol == (ic->arg & 0xFF))
+					return(st->lli.l4l3_proto(st, ic));
+			return(-EINVAL);
+			break;
+		default:
+			if (csta->auxcmd)
+				return(csta->auxcmd(csta, ic));
+			return(-EINVAL);
+	}
+	return (0);
+}
+
+int
+HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb)
+{
+	struct IsdnCardState *csta = hisax_findcard(id);
+	struct Channel *chanp;
+	struct PStack *st;
+	int len = skb->len;
+	struct sk_buff *nskb;
+
+	if (!csta) {
+		printk(KERN_ERR
+			"HiSax: if_sendbuf called with invalid driverId!\n");
+		return -ENODEV;
+	}
+	chanp = csta->channel + chan;
+	st = chanp->b_st;
+	if (!chanp->data_open) {
+		link_debug(chanp, 1, "writebuf: channel not open");
+		return -EIO;
+	}
+	if (len > MAX_DATA_SIZE) {
+		link_debug(chanp, 1, "writebuf: packet too large (%d bytes)", len);
+		printk(KERN_WARNING "HiSax_writebuf: packet too large (%d bytes) !\n",
+			len);
+		return -EINVAL;
+	}
+	if (len) {
+		if ((len + chanp->bcs->tx_cnt) > MAX_DATA_MEM) {
+			/* Must return 0 here, since this is not an error
+			 * but a temporary lack of resources.
+			 */
+			if (chanp->debug & 0x800)
+				link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len);
+			return 0;
+		} else if (chanp->debug & 0x800)
+			link_debug(chanp, 1, "writebuf %d/%d/%d", len, chanp->bcs->tx_cnt,MAX_DATA_MEM);
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			nskb->truesize = nskb->len;
+			if (!ack)
+				nskb->pkt_type = PACKET_NOACK;
+			if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
+				st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
+			else {
+				chanp->bcs->tx_cnt += len;
+				st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
+			}
+			dev_kfree_skb(skb);
+		} else
+			len = 0;
+	}
+	return (len);
+}
diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
new file mode 100644
index 0000000..1663ee6
--- /dev/null
+++ b/drivers/isdn/hisax/config.c
@@ -0,0 +1,1958 @@
+/* $Id: config.c,v 2.84.2.5 2004/02/11 13:21:33 keil Exp $
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * based on the teles driver from Jan den Ouden
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#define HISAX_STATUS_BUFSIZE 4096
+#define INCLUDE_INLINE_FUNCS
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ *
+ * { type, protocol, p0, p1, p2, NULL }
+ *
+ * type
+ *    1 Teles 16.0       p0=irq p1=membase p2=iobase
+ *    2 Teles  8.0       p0=irq p1=membase
+ *    3 Teles 16.3       p0=irq p1=iobase
+ *    4 Creatix PNP      p0=irq p1=IO0 (ISAC)  p2=IO1 (HSCX)
+ *    5 AVM A1 (Fritz)   p0=irq p1=iobase
+ *    6 ELSA PC          [p0=iobase] or nothing (autodetect)
+ *    7 ELSA Quickstep   p0=irq p1=iobase
+ *    8 Teles PCMCIA     p0=irq p1=iobase
+ *    9 ITK ix1-micro    p0=irq p1=iobase
+ *   10 ELSA PCMCIA      p0=irq p1=iobase
+ *   11 Eicon.Diehl Diva p0=irq p1=iobase
+ *   12 Asuscom ISDNLink p0=irq p1=iobase
+ *   13 Teleint          p0=irq p1=iobase
+ *   14 Teles 16.3c      p0=irq p1=iobase
+ *   15 Sedlbauer speed  p0=irq p1=iobase
+ *   15 Sedlbauer PC/104	p0=irq p1=iobase
+ *   15 Sedlbauer speed pci	no parameter
+ *   16 USR Sportster internal  p0=irq  p1=iobase
+ *   17 MIC card                p0=irq  p1=iobase
+ *   18 ELSA Quickstep 1000PCI  no parameter
+ *   19 Compaq ISDN S0 ISA card p0=irq  p1=IO0 (HSCX)  p2=IO1 (ISAC) p3=IO2
+ *   20 Travers Technologies NETjet-S PCI card
+ *   21 TELES PCI               no parameter
+ *   22 Sedlbauer Speed Star    p0=irq p1=iobase
+ *   23 reserved
+ *   24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only)
+ *   25 Teles S0Box             p0=irq p1=iobase (from isapnp setup)
+ *   26 AVM A1 PCMCIA (Fritz)   p0=irq p1=iobase
+ *   27 AVM PnP/PCI 		p0=irq p1=iobase (PCI no parameter)
+ *   28 Sedlbauer Speed Fax+ 	p0=irq p1=iobase (from isapnp setup)
+ *   29 Siemens I-Surf          p0=irq p1=iobase p2=memory (from isapnp setup)
+ *   30 ACER P10                p0=irq p1=iobase (from isapnp setup)
+ *   31 HST Saphir              p0=irq  p1=iobase
+ *   32 Telekom A4T             none
+ *   33 Scitel Quadro		p0=subcontroller (4*S0, subctrl 1...4)
+ *   34	Gazel ISDN cards
+ *   35 HFC 2BDS0 PCI           none
+ *   36 Winbond 6692 PCI        none
+ *   37 HFC 2BDS0 S+/SP         p0=irq p1=iobase
+ *   38 Travers Technologies NETspider-U PCI card
+ *   39 HFC 2BDS0-SP PCMCIA     p0=irq p1=iobase
+ *   40 hotplug interface
+ *   41 Formula-n enter:now ISDN PCI a/b   none
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
+ *
+ *
+ */
+
+const char *CardType[] = {
+	"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3",
+	"Creatix/Teles PnP", "AVM A1", "Elsa ML", "Elsa Quickstep",
+	"Teles PCMCIA",	"ITK ix1-micro Rev.2", "Elsa PCMCIA",
+	"Eicon.Diehl Diva", "ISDNLink",	"TeleInt", "Teles 16.3c",
+	"Sedlbauer Speed Card", "USR Sportster", "ith mic Linux",
+	"Elsa PCI", "Compaq ISA", "NETjet-S", "Teles PCI", 
+	"Sedlbauer Speed Star (PCMCIA)", "AMD 7930", "NICCY", "S0Box",
+	"AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", "Sedlbauer Speed Fax +",
+	"Siemens I-Surf", "Acer P10", "HST Saphir", "Telekom A4T",
+	"Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", "Winbond 6692",
+	"HFC 2BDS0 SX", "NETspider-U", "HFC-2BDS0-SP PCMCIA",
+	"Hotplug", "Formula-n enter:now PCI a/b", 
+};
+
+#ifdef CONFIG_HISAX_ELSA
+#define DEFAULT_CARD ISDN_CTYPE_ELSA
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1
+#define DEFAULT_CFG {10,0x340,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA
+#define DEFAULT_CFG {11,0x170,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_FRITZPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_FRITZPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_16_3
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_3
+#define DEFAULT_CFG {15,0x180,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_S0BOX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_S0BOX
+#define DEFAULT_CFG {7,0x378,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_16_0
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_0
+#define DEFAULT_CFG {15,0xd0000,0xd80,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELESPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELESPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_IX1MICROR2
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2
+#define DEFAULT_CFG {5,0x390,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_DIEHLDIVA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ASUSCOM
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM
+#define DEFAULT_CFG {5,0x200,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELEINT
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELEINT
+#define DEFAULT_CFG {5,0x300,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SEDLBAUER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER
+#define DEFAULT_CFG {11,0x270,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SPORTSTER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER
+#define DEFAULT_CFG {7,0x268,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_MIC
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_MIC
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_S
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFCS
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELES3C
+#define DEFAULT_CFG {5,0x500,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFC_PCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HFC_PCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFC_SX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HFC_SX
+#define DEFAULT_CFG {5,0x2E0,0,0}
+#endif
+
+
+#ifdef CONFIG_HISAX_AMD7930
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_AMD7930
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NICCY
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NICCY
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ISURF
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ISURF
+#define DEFAULT_CFG {5,0x100,0xc8000,0}
+#endif
+
+#ifdef CONFIG_HISAX_HSTSAPHIR
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HSTSAPHIR
+#define DEFAULT_CFG {5,0x250,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_BKM_A4T
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_BKM_A4T
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SCT_QUADRO
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SCT_QUADRO
+#define DEFAULT_CFG {1,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_GAZEL
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_GAZEL
+#define DEFAULT_CFG {15,0x180,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_W6692
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_W6692
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET_U
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_U
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_1TR6
+#define DEFAULT_PROTO ISDN_PTYPE_1TR6
+#define DEFAULT_PROTO_NAME "1TR6"
+#endif
+#ifdef CONFIG_HISAX_NI1
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_NI1
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "NI1"
+#endif
+#ifdef CONFIG_HISAX_EURO
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_EURO
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "EURO"
+#endif
+#ifndef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN
+#define DEFAULT_PROTO_NAME "UNKNOWN"
+#endif
+#ifndef DEFAULT_CARD
+#define DEFAULT_CARD 0
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#define FIRST_CARD { \
+	DEFAULT_CARD, \
+	DEFAULT_PROTO, \
+	DEFAULT_CFG, \
+	NULL, \
+}
+
+struct IsdnCard cards[HISAX_MAX_CARDS] = {
+	FIRST_CARD,
+};
+
+#define HISAX_IDSIZE (HISAX_MAX_CARDS*8)
+static char HiSaxID[HISAX_IDSIZE] = { 0, };
+
+char *HiSax_id = HiSaxID;
+#ifdef MODULE
+/* Variables for insmod */
+static int type[HISAX_MAX_CARDS] = { 0, };
+static int protocol[HISAX_MAX_CARDS] = { 0, };
+static int io[HISAX_MAX_CARDS] = { 0, };
+#undef IO0_IO1
+#ifdef CONFIG_HISAX_16_3
+#define IO0_IO1
+#endif
+#ifdef CONFIG_HISAX_NICCY
+#undef IO0_IO1
+#define IO0_IO1
+#endif
+#ifdef IO0_IO1
+static int io0[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static int io1[HISAX_MAX_CARDS] __devinitdata = { 0, };
+#endif
+static int irq[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static int mem[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static char *id = HiSaxID;
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for passive ISDN cards");
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param_array(type, int, NULL, 0);
+module_param_array(protocol, int, NULL, 0);
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param(id, charp, 0);
+#ifdef IO0_IO1
+module_param_array(io0, int, NULL, 0);
+module_param_array(io1, int, NULL, 0);
+#endif
+#endif /* MODULE */
+
+int nrcards;
+
+extern char *l1_revision;
+extern char *l2_revision;
+extern char *l3_revision;
+extern char *lli_revision;
+extern char *tei_revision;
+
+char *HiSax_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+void __init HiSaxVersion(void)
+{
+	char tmp[64];
+
+	printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
+#ifdef MODULE
+	printk(KERN_INFO "HiSax: Version 3.5 (module)\n");
+#else
+	printk(KERN_INFO "HiSax: Version 3.5 (kernel)\n");
+#endif
+	strcpy(tmp, l1_revision);
+	printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, l2_revision);
+	printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, tei_revision);
+	printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, l3_revision);
+	printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp));
+	strcpy(tmp, lli_revision);
+	printk(KERN_INFO "HiSax: LinkLayer Revision %s\n",
+	       HiSax_getrev(tmp));
+}
+
+#ifndef MODULE
+#define MAX_ARG	(HISAX_MAX_CARDS*5)
+static int __init HiSax_setup(char *line)
+{
+	int i, j, argc;
+	int ints[MAX_ARG + 1];
+	char *str;
+
+	str = get_options(line, MAX_ARG, ints);
+	argc = ints[0];
+	printk(KERN_DEBUG "HiSax_setup: argc(%d) str(%s)\n", argc, str);
+	i = 0;
+	j = 1;
+	while (argc && (i < HISAX_MAX_CARDS)) {
+		cards[i].protocol = DEFAULT_PROTO;
+		if (argc) {
+			cards[i].typ = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].protocol = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[0] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[1] = ints[j];
+			j++;
+			argc--;
+		}
+		if (argc) {
+			cards[i].para[2] = ints[j];
+			j++;
+			argc--;
+		}
+		i++;
+	}
+  	if (str && *str) {
+		if (strlen(str) < HISAX_IDSIZE)
+			strcpy(HiSaxID, str);
+		else
+			printk(KERN_WARNING "HiSax: ID too long!");
+	} else
+		strcpy(HiSaxID, "HiSax");
+
+	HiSax_id = HiSaxID;
+	return 1;
+}
+
+__setup("hisax=", HiSax_setup);
+#endif /* MODULES */
+
+#if CARD_TELES0
+extern int setup_teles0(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3
+extern int setup_teles3(struct IsdnCard *card);
+#endif
+
+#if CARD_S0BOX
+extern int setup_s0box(struct IsdnCard *card);
+#endif
+
+#if CARD_TELESPCI
+extern int setup_telespci(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1
+extern int setup_avm_a1(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1_PCMCIA
+extern int setup_avm_a1_pcmcia(struct IsdnCard *card);
+#endif
+
+#if CARD_FRITZPCI
+extern int setup_avm_pcipnp(struct IsdnCard *card);
+#endif
+
+#if CARD_ELSA
+extern int setup_elsa(struct IsdnCard *card);
+#endif
+
+#if CARD_IX1MICROR2
+extern int setup_ix1micro(struct IsdnCard *card);
+#endif
+
+#if CARD_DIEHLDIVA
+extern int setup_diva(struct IsdnCard *card);
+#endif
+
+#if CARD_ASUSCOM
+extern int setup_asuscom(struct IsdnCard *card);
+#endif
+
+#if CARD_TELEINT
+extern int setup_TeleInt(struct IsdnCard *card);
+#endif
+
+#if CARD_SEDLBAUER
+extern int setup_sedlbauer(struct IsdnCard *card);
+#endif
+
+#if CARD_SPORTSTER
+extern int setup_sportster(struct IsdnCard *card);
+#endif
+
+#if CARD_MIC
+extern int setup_mic(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET_S
+extern int setup_netjet_s(struct IsdnCard *card);
+#endif
+
+#if CARD_HFCS
+extern int setup_hfcs(struct IsdnCard *card);
+#endif
+
+#if CARD_HFC_PCI
+extern int setup_hfcpci(struct IsdnCard *card);
+#endif
+
+#if CARD_HFC_SX
+extern int setup_hfcsx(struct IsdnCard *card);
+#endif
+
+#if CARD_AMD7930
+extern int setup_amd7930(struct IsdnCard *card);
+#endif
+
+#if CARD_NICCY
+extern int setup_niccy(struct IsdnCard *card);
+#endif
+
+#if CARD_ISURF
+extern int setup_isurf(struct IsdnCard *card);
+#endif
+
+#if CARD_HSTSAPHIR
+extern int setup_saphir(struct IsdnCard *card);
+#endif
+
+#if CARD_TESTEMU
+extern int setup_testemu(struct IsdnCard *card);
+#endif
+
+#if CARD_BKM_A4T
+extern int setup_bkm_a4t(struct IsdnCard *card);
+#endif
+
+#if CARD_SCT_QUADRO
+extern int setup_sct_quadro(struct IsdnCard *card);
+#endif
+
+#if CARD_GAZEL
+extern int setup_gazel(struct IsdnCard *card);
+#endif
+
+#if CARD_W6692
+extern int setup_w6692(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET_U
+extern int setup_netjet_u(struct IsdnCard *card);
+#endif
+
+#if CARD_FN_ENTERNOW_PCI
+extern int setup_enternow_pci(struct IsdnCard *card);
+#endif
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState *hisax_findcard(int driverid)
+{
+	int i;
+
+	for (i = 0; i < nrcards; i++)
+		if (cards[i].cs)
+			if (cards[i].cs->myid == driverid)
+				return cards[i].cs;
+	return NULL;
+}
+
+/*
+ * Find card with given card number
+ */
+struct IsdnCardState *hisax_get_card(int cardnr)
+{
+	if ((cardnr <= nrcards) && (cardnr > 0))
+		if (cards[cardnr - 1].cs)
+			return cards[cardnr - 1].cs;
+	return NULL;
+}
+
+int HiSax_readstatus(u_char __user *buf, int len, int id, int channel)
+{
+	int count, cnt;
+	u_char __user *p = buf;
+	struct IsdnCardState *cs = hisax_findcard(id);
+
+	if (cs) {
+		if (len > HISAX_STATUS_BUFSIZE) {
+			printk(KERN_WARNING
+			       "HiSax: status overflow readstat %d/%d\n",
+			       len, HISAX_STATUS_BUFSIZE);
+		}
+		count = cs->status_end - cs->status_read + 1;
+		if (count >= len)
+			count = len;
+		copy_to_user(p, cs->status_read, count);
+		cs->status_read += count;
+		if (cs->status_read > cs->status_end)
+			cs->status_read = cs->status_buf;
+		p += count;
+		count = len - count;
+		while (count) {
+			if (count > HISAX_STATUS_BUFSIZE)
+				cnt = HISAX_STATUS_BUFSIZE;
+			else
+				cnt = count;
+			copy_to_user(p, cs->status_read, cnt);
+			p += cnt;
+			cs->status_read += cnt % HISAX_STATUS_BUFSIZE;
+			count -= cnt;
+		}
+		return len;
+	} else {
+		printk(KERN_ERR
+		       "HiSax: if_readstatus called with invalid driverId!\n");
+		return -ENODEV;
+	}
+}
+
+int jiftime(char *s, long mark)
+{
+	s += 8;
+
+	*s-- = '\0';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = '.';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 6 + '0';
+	mark /= 6;
+	*s-- = ':';
+	*s-- = mark % 10 + '0';
+	mark /= 10;
+	*s-- = mark % 10 + '0';
+	return 8;
+}
+
+static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
+
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
+		      va_list args)
+{
+	/* if head == NULL the fmt contains the full info */
+
+	u_long		flags;
+	int		count, i;
+	u_char		*p;
+	isdn_ctrl	ic;
+	int		len;
+
+	if (!cs) {
+		printk(KERN_WARNING "HiSax: No CardStatus for message");
+		return;
+	}
+	spin_lock_irqsave(&cs->statlock, flags);
+	p = tmpbuf;
+	if (head) {
+		p += jiftime(p, jiffies);
+		p += sprintf(p, " %s", head);
+		p += vsprintf(p, fmt, args);
+		*p++ = '\n';
+		*p = 0;
+		len = p - tmpbuf;
+		p = tmpbuf;
+	} else {
+		p = fmt;
+		len = strlen(fmt);
+	}
+	if (len > HISAX_STATUS_BUFSIZE) {
+		spin_unlock_irqrestore(&cs->statlock, flags);
+		printk(KERN_WARNING "HiSax: status overflow %d/%d\n",
+		       len, HISAX_STATUS_BUFSIZE);
+		return;
+	}
+	count = len;
+	i = cs->status_end - cs->status_write + 1;
+	if (i >= len)
+		i = len;
+	len -= i;
+	memcpy(cs->status_write, p, i);
+	cs->status_write += i;
+	if (cs->status_write > cs->status_end)
+		cs->status_write = cs->status_buf;
+	p += i;
+	if (len) {
+		memcpy(cs->status_write, p, len);
+		cs->status_write += len;
+	}
+#ifdef KERNELSTACK_DEBUG
+	i = (ulong) & len - current->kernel_stack_page;
+	sprintf(tmpbuf, "kstack %s %lx use %ld\n", current->comm,
+		current->kernel_stack_page, i);
+	len = strlen(tmpbuf);
+	for (p = tmpbuf, i = len; i > 0; i--, p++) {
+		*cs->status_write++ = *p;
+		if (cs->status_write > cs->status_end)
+			cs->status_write = cs->status_buf;
+		count++;
+	}
+#endif
+	spin_unlock_irqrestore(&cs->statlock, flags);
+	if (count) {
+		ic.command = ISDN_STAT_STAVAIL;
+		ic.driver = cs->myid;
+		ic.arg = count;
+		cs->iif.statcallb(&ic);
+	}
+}
+
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(cs, head, fmt, args);
+	va_end(args);
+}
+
+int ll_run(struct IsdnCardState *cs, int addfeatures)
+{
+	isdn_ctrl ic;
+
+	ic.driver = cs->myid;
+	ic.command = ISDN_STAT_RUN;
+	cs->iif.features |= addfeatures;
+	cs->iif.statcallb(&ic);
+	return 0;
+}
+
+void ll_stop(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_STOP;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	//      CallcFreeChan(cs);
+}
+
+static void ll_unload(struct IsdnCardState *cs)
+{
+	isdn_ctrl ic;
+
+	ic.command = ISDN_STAT_UNLOAD;
+	ic.driver = cs->myid;
+	cs->iif.statcallb(&ic);
+	if (cs->status_buf)
+		kfree(cs->status_buf);
+	cs->status_read = NULL;
+	cs->status_write = NULL;
+	cs->status_end = NULL;
+	kfree(cs->dlog);
+	cs->dlog = NULL;
+}
+
+static void closecard(int cardnr)
+{
+	struct IsdnCardState *csta = cards[cardnr].cs;
+
+	if (csta->bcs->BC_Close != NULL) {
+		csta->bcs->BC_Close(csta->bcs + 1);
+		csta->bcs->BC_Close(csta->bcs);
+	}
+
+	skb_queue_purge(&csta->rq);
+	skb_queue_purge(&csta->sq);
+	if (csta->rcvbuf) {
+		kfree(csta->rcvbuf);
+		csta->rcvbuf = NULL;
+	}
+	if (csta->tx_skb) {
+		dev_kfree_skb(csta->tx_skb);
+		csta->tx_skb = NULL;
+	}
+	if (csta->DC_Close != NULL) {
+		csta->DC_Close(csta);
+	}
+	if (csta->cardmsg)
+		csta->cardmsg(csta, CARD_RELEASE, NULL);
+	if (csta->dbusytimer.function != NULL) // FIXME?
+		del_timer(&csta->dbusytimer);
+	ll_unload(csta);
+}
+
+static int init_card(struct IsdnCardState *cs)
+{
+	int 	irq_cnt, cnt = 3, ret;
+
+	if (!cs->irq) {
+		ret = cs->cardmsg(cs, CARD_INIT, NULL);
+		return(ret);
+	}
+	irq_cnt = kstat_irqs(cs->irq);
+	printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
+	       cs->irq, irq_cnt);
+	if (request_irq(cs->irq, cs->irq_func, cs->irq_flags, "HiSax", cs)) {
+		printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+		       cs->irq);
+		return 1;
+	}
+	while (cnt) {
+		cs->cardmsg(cs, CARD_INIT, NULL);
+		/* Timeout 10ms */
+		msleep(10);
+		printk(KERN_INFO "%s: IRQ %d count %d\n",
+		       CardType[cs->typ], cs->irq, kstat_irqs(cs->irq));
+		if (kstat_irqs(cs->irq) == irq_cnt) {
+			printk(KERN_WARNING
+			       "%s: IRQ(%d) getting no interrupts during init %d\n",
+			       CardType[cs->typ], cs->irq, 4 - cnt);
+			if (cnt == 1) {
+				free_irq(cs->irq, cs);
+				return 2;
+			} else {
+				cs->cardmsg(cs, CARD_RESET, NULL);
+				cnt--;
+			}
+		} else {
+			cs->cardmsg(cs, CARD_TEST, NULL);
+			return 0;
+		}
+	}
+	return 3;
+}
+
+static int checkcard(int cardnr, char *id, int *busy_flag, struct module *lockowner)
+{
+	int ret = 0;
+	struct IsdnCard *card = cards + cardnr;
+	struct IsdnCardState *cs;
+
+	cs = kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC);
+	if (!cs) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for IsdnCardState(card %d)\n",
+		       cardnr + 1);
+		goto out;
+	}
+	memset(cs, 0, sizeof(struct IsdnCardState));
+	card->cs = cs;
+	spin_lock_init(&cs->statlock);
+	spin_lock_init(&cs->lock);
+	cs->chanlimit = 2;	/* maximum B-channel number */
+	cs->logecho = 0;	/* No echo logging */
+	cs->cardnr = cardnr;
+	cs->debug = L1_DEB_WARN;
+	cs->HW_Flags = 0;
+	cs->busy_flag = busy_flag;
+	cs->irq_flags = I4L_IRQ_FLAG;
+#if TEI_PER_CARD
+	if (card->protocol == ISDN_PTYPE_NI1)
+		test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#else
+	test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#endif
+	cs->protocol = card->protocol;
+
+	if (card->typ <= 0 || card->typ > ISDN_CTYPE_COUNT) {
+		printk(KERN_WARNING
+		       "HiSax: Card Type %d out of range\n", card->typ);
+		goto outf_cs;
+	}
+	if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for dlog(card %d)\n", cardnr + 1);
+		goto outf_cs;
+	}
+	if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for status_buf(card %d)\n",
+		       cardnr + 1);
+		goto outf_dlog;
+	}
+	cs->stlist = NULL;
+	cs->status_read = cs->status_buf;
+	cs->status_write = cs->status_buf;
+	cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
+	cs->typ = card->typ;
+#ifdef MODULE
+	cs->iif.owner = lockowner;
+#endif
+	strcpy(cs->iif.id, id);
+	cs->iif.channels = 2;
+	cs->iif.maxbufsize = MAX_DATA_SIZE;
+	cs->iif.hl_hdrlen = MAX_HEADER_LEN;
+	cs->iif.features =
+		ISDN_FEATURE_L2_X75I |
+		ISDN_FEATURE_L2_HDLC |
+		ISDN_FEATURE_L2_HDLC_56K |
+		ISDN_FEATURE_L2_TRANS |
+		ISDN_FEATURE_L3_TRANS |
+#ifdef	CONFIG_HISAX_1TR6
+		ISDN_FEATURE_P_1TR6 |
+#endif
+#ifdef	CONFIG_HISAX_EURO
+		ISDN_FEATURE_P_EURO |
+#endif
+#ifdef	CONFIG_HISAX_NI1
+		ISDN_FEATURE_P_NI1 |
+#endif
+		0;
+
+	cs->iif.command = HiSax_command;
+	cs->iif.writecmd = NULL;
+	cs->iif.writebuf_skb = HiSax_writebuf_skb;
+	cs->iif.readstat = HiSax_readstatus;
+	register_isdn(&cs->iif);
+	cs->myid = cs->iif.channels;
+	printk(KERN_INFO
+	       "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
+	       (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
+	       (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
+	       (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
+	       (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
+	       "NONE", cs->iif.id, cs->myid);
+	switch (card->typ) {
+#if CARD_TELES0
+	case ISDN_CTYPE_16_0:
+	case ISDN_CTYPE_8_0:
+		ret = setup_teles0(card);
+		break;
+#endif
+#if CARD_TELES3
+	case ISDN_CTYPE_16_3:
+	case ISDN_CTYPE_PNP:
+	case ISDN_CTYPE_TELESPCMCIA:
+	case ISDN_CTYPE_COMPAQ_ISA:
+		ret = setup_teles3(card);
+		break;
+#endif
+#if CARD_S0BOX
+	case ISDN_CTYPE_S0BOX:
+		ret = setup_s0box(card);
+		break;
+#endif
+#if CARD_TELESPCI
+	case ISDN_CTYPE_TELESPCI:
+		ret = setup_telespci(card);
+		break;
+#endif
+#if CARD_AVM_A1
+	case ISDN_CTYPE_A1:
+		ret = setup_avm_a1(card);
+		break;
+#endif
+#if CARD_AVM_A1_PCMCIA
+	case ISDN_CTYPE_A1_PCMCIA:
+		ret = setup_avm_a1_pcmcia(card);
+		break;
+#endif
+#if CARD_FRITZPCI
+	case ISDN_CTYPE_FRITZPCI:
+		ret = setup_avm_pcipnp(card);
+		break;
+#endif
+#if CARD_ELSA
+	case ISDN_CTYPE_ELSA:
+	case ISDN_CTYPE_ELSA_PNP:
+	case ISDN_CTYPE_ELSA_PCMCIA:
+	case ISDN_CTYPE_ELSA_PCI:
+		ret = setup_elsa(card);
+		break;
+#endif
+#if CARD_IX1MICROR2
+	case ISDN_CTYPE_IX1MICROR2:
+		ret = setup_ix1micro(card);
+		break;
+#endif
+#if CARD_DIEHLDIVA
+	case ISDN_CTYPE_DIEHLDIVA:
+		ret = setup_diva(card);
+		break;
+#endif
+#if CARD_ASUSCOM
+	case ISDN_CTYPE_ASUSCOM:
+		ret = setup_asuscom(card);
+		break;
+#endif
+#if CARD_TELEINT
+	case ISDN_CTYPE_TELEINT:
+		ret = setup_TeleInt(card);
+		break;
+#endif
+#if CARD_SEDLBAUER
+	case ISDN_CTYPE_SEDLBAUER:
+	case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+	case ISDN_CTYPE_SEDLBAUER_FAX:
+		ret = setup_sedlbauer(card);
+		break;
+#endif
+#if CARD_SPORTSTER
+	case ISDN_CTYPE_SPORTSTER:
+		ret = setup_sportster(card);
+		break;
+#endif
+#if CARD_MIC
+	case ISDN_CTYPE_MIC:
+		ret = setup_mic(card);
+		break;
+#endif
+#if CARD_NETJET_S
+	case ISDN_CTYPE_NETJET_S:
+		ret = setup_netjet_s(card);
+		break;
+#endif
+#if CARD_HFCS
+	case ISDN_CTYPE_TELES3C:
+	case ISDN_CTYPE_ACERP10:
+		ret = setup_hfcs(card);
+		break;
+#endif
+#if CARD_HFC_PCI
+	case ISDN_CTYPE_HFC_PCI:
+		ret = setup_hfcpci(card);
+		break;
+#endif
+#if CARD_HFC_SX
+	case ISDN_CTYPE_HFC_SX:
+		ret = setup_hfcsx(card);
+		break;
+#endif
+#if CARD_NICCY
+	case ISDN_CTYPE_NICCY:
+		ret = setup_niccy(card);
+		break;
+#endif
+#if CARD_AMD7930
+	case ISDN_CTYPE_AMD7930:
+		ret = setup_amd7930(card);
+		break;
+#endif
+#if CARD_ISURF
+	case ISDN_CTYPE_ISURF:
+		ret = setup_isurf(card);
+		break;
+#endif
+#if CARD_HSTSAPHIR
+	case ISDN_CTYPE_HSTSAPHIR:
+		ret = setup_saphir(card);
+		break;
+#endif
+#if CARD_TESTEMU
+	case ISDN_CTYPE_TESTEMU:
+		ret = setup_testemu(card);
+		break;
+#endif
+#if	CARD_BKM_A4T
+	case ISDN_CTYPE_BKM_A4T:
+		ret = setup_bkm_a4t(card);
+		break;
+#endif
+#if	CARD_SCT_QUADRO
+	case ISDN_CTYPE_SCT_QUADRO:
+		ret = setup_sct_quadro(card);
+		break;
+#endif
+#if CARD_GAZEL
+	case ISDN_CTYPE_GAZEL:
+		ret = setup_gazel(card);
+		break;
+#endif
+#if CARD_W6692
+	case ISDN_CTYPE_W6692:
+		ret = setup_w6692(card);
+		break;
+#endif
+#if CARD_NETJET_U
+	case ISDN_CTYPE_NETJET_U:
+		ret = setup_netjet_u(card);
+		break;
+#endif
+#if CARD_FN_ENTERNOW_PCI
+	case ISDN_CTYPE_ENTERNOW:
+		ret = setup_enternow_pci(card);
+		break;
+#endif
+	case ISDN_CTYPE_DYNAMIC:
+		ret = 2;
+		break;
+	default:
+		printk(KERN_WARNING
+		       "HiSax: Support for %s Card not selected\n",
+		       CardType[card->typ]);
+		ll_unload(cs);
+		goto outf_cs;
+	}
+	if (!ret) {
+		ll_unload(cs);
+		goto outf_cs;
+	}
+	if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No memory for isac rcvbuf\n");
+		ll_unload(cs);
+		goto outf_cs;
+	}
+	cs->rcvidx = 0;
+	cs->tx_skb = NULL;
+	cs->tx_cnt = 0;
+	cs->event = 0;
+	cs->tqueue.data = cs;
+
+	skb_queue_head_init(&cs->rq);
+	skb_queue_head_init(&cs->sq);
+
+	init_bcstate(cs, 0);
+	init_bcstate(cs, 1);
+
+	/* init_card only handles interrupts which are not */
+	/* used here for the loadable driver */
+	switch (card->typ) {
+		case ISDN_CTYPE_DYNAMIC:
+			ret = 0;
+			break;
+		default:
+			ret = init_card(cs);
+			break;
+	}
+	if (ret) {
+		closecard(cardnr);
+		ret = 0;
+		goto outf_cs;
+	}
+	init_tei(cs, cs->protocol);
+	ret = CallcNewChan(cs);
+	if (ret) {
+		closecard(cardnr);
+		ret = 0;
+		goto outf_cs;
+	}
+	/* ISAR needs firmware download first */
+	if (!test_bit(HW_ISAR, &cs->HW_Flags))
+		ll_run(cs, 0);
+
+	ret = 1;
+	goto out;
+
+ outf_dlog:
+	kfree(cs->dlog);
+ outf_cs:
+	kfree(cs);
+	card->cs = NULL;
+ out:
+	return ret;
+}
+
+void HiSax_shiftcards(int idx)
+{
+	int i;
+
+	for (i = idx; i < (HISAX_MAX_CARDS - 1); i++)
+		memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+int HiSax_inithardware(int *busy_flag)
+{
+	int foundcards = 0;
+	int i = 0;
+	int t = ',';
+	int flg = 0;
+	char *id;
+	char *next_id = HiSax_id;
+	char ids[20];
+
+	if (strchr(HiSax_id, ','))
+		t = ',';
+	else if (strchr(HiSax_id, '%'))
+		t = '%';
+
+	while (i < nrcards) {
+		if (cards[i].typ < 1)
+			break;
+		id = next_id;
+		if ((next_id = strchr(id, t))) {
+			*next_id++ = 0;
+			strcpy(ids, id);
+			flg = i + 1;
+		} else {
+			next_id = id;
+			if (flg >= i)
+				strcpy(ids, id);
+			else
+				sprintf(ids, "%s%d", id, i);
+		}
+		if (checkcard(i, ids, busy_flag, THIS_MODULE)) {
+			foundcards++;
+			i++;
+		} else {
+			/* make sure we don't oops the module */
+			if (cards[i].typ > 0 && cards[i].typ <= ISDN_CTYPE_COUNT) {
+				printk(KERN_WARNING
+			       		"HiSax: Card %s not installed !\n",
+			       		CardType[cards[i].typ]);
+			}
+			HiSax_shiftcards(i);
+			nrcards--;
+		}
+	}
+	return foundcards;
+}
+
+void HiSax_closecard(int cardnr)
+{
+	int i, last = nrcards - 1;
+
+	if (cardnr > last || cardnr < 0)
+		return;
+	if (cards[cardnr].cs) {
+		ll_stop(cards[cardnr].cs);
+		release_tei(cards[cardnr].cs);
+		CallcFreeChan(cards[cardnr].cs);
+
+		closecard(cardnr);
+		if (cards[cardnr].cs->irq)
+			free_irq(cards[cardnr].cs->irq, cards[cardnr].cs);
+		kfree((void *) cards[cardnr].cs);
+		cards[cardnr].cs = NULL;
+	}
+	i = cardnr;
+	while (i <= last) {
+		cards[i] = cards[i + 1];
+		i++;
+	}
+	nrcards--;
+}
+
+void HiSax_reportcard(int cardnr, int sel)
+{
+	struct IsdnCardState *cs = cards[cardnr].cs;
+
+	printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+	printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
+	printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
+	printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+	       (ulong) & HiSax_reportcard);
+	printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
+	printk(KERN_DEBUG "HiSax: HW_Flags %lx bc0 flg %lx bc1 flg %lx\n",
+	       cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag);
+	printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n",
+	       cs->bcs[0].mode, cs->bcs[0].channel);
+	printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n",
+	       cs->bcs[1].mode, cs->bcs[1].channel);
+#ifdef ERROR_STATISTIC
+	printk(KERN_DEBUG "HiSax: dc errors(rx,crc,tx) %d,%d,%d\n",
+	       cs->err_rx, cs->err_crc, cs->err_tx);
+	printk(KERN_DEBUG
+	       "HiSax: bc0 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+	       cs->bcs[0].err_inv, cs->bcs[0].err_rdo, cs->bcs[0].err_crc,
+	       cs->bcs[0].err_tx);
+	printk(KERN_DEBUG
+	       "HiSax: bc1 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+	       cs->bcs[1].err_inv, cs->bcs[1].err_rdo, cs->bcs[1].err_crc,
+	       cs->bcs[1].err_tx);
+	if (sel == 99) {
+		cs->err_rx  = 0;
+		cs->err_crc = 0;
+		cs->err_tx  = 0;
+		cs->bcs[0].err_inv = 0;
+		cs->bcs[0].err_rdo = 0;
+		cs->bcs[0].err_crc = 0;
+		cs->bcs[0].err_tx  = 0;
+		cs->bcs[1].err_inv = 0;
+		cs->bcs[1].err_rdo = 0;
+		cs->bcs[1].err_crc = 0;
+		cs->bcs[1].err_tx  = 0;
+	}
+#endif
+}
+
+static int __init HiSax_init(void)
+{
+	int i, retval;
+#ifdef MODULE
+	int j;
+	int nzproto = 0;
+#endif
+
+	HiSaxVersion();
+	retval = CallcNew();
+	if (retval)
+		goto out;
+	retval = Isdnl3New();
+	if (retval)
+		goto out_callc;
+	retval = Isdnl2New();
+	if (retval)
+		goto out_isdnl3;
+	retval = TeiNew();
+	if (retval)
+		goto out_isdnl2;
+	retval = Isdnl1New();
+	if (retval)
+		goto out_tei;
+
+#ifdef MODULE
+	if (!type[0]) {
+		/* We 'll register drivers later, but init basic functions */
+		for (i = 0; i < HISAX_MAX_CARDS; i++)
+			cards[i].typ = 0;
+		return 0;
+	}
+#ifdef CONFIG_HISAX_ELSA
+	if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) {
+		/* we have exported  and return in this case */
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+	if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+	if (type[0] == ISDN_CTYPE_A1_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
+#ifdef CONFIG_HISAX_HFC_SX
+	if (type[0] == ISDN_CTYPE_HFC_SP_PCMCIA) {
+		/* we have to export  and return in this case */
+		return 0;
+	}
+#endif
+#endif
+	nrcards = 0;
+#ifdef MODULE
+	if (id)			/* If id= string used */
+		HiSax_id = id;
+	for (i = j = 0; j < HISAX_MAX_CARDS; i++) {
+		cards[j].typ = type[i];
+		if (protocol[i]) {
+			cards[j].protocol = protocol[i];
+			nzproto++;
+		} else {
+			cards[j].protocol = DEFAULT_PROTO;
+		}
+		switch (type[i]) {
+		case ISDN_CTYPE_16_0:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = mem[i];
+			cards[j].para[2] = io[i];
+			break;
+
+		case ISDN_CTYPE_8_0:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = mem[i];
+			break;
+
+#ifdef IO0_IO1
+		case ISDN_CTYPE_PNP:
+		case ISDN_CTYPE_NICCY:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io0[i];
+			cards[j].para[2] = io1[i];
+			break;
+		case ISDN_CTYPE_COMPAQ_ISA:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io0[i];
+			cards[j].para[2] = io1[i];
+			cards[j].para[3] = io[i];
+			break;
+#endif
+		case ISDN_CTYPE_ELSA:
+		case ISDN_CTYPE_HFC_PCI:
+			cards[j].para[0] = io[i];
+			break;
+		case ISDN_CTYPE_16_3:
+		case ISDN_CTYPE_TELESPCMCIA:
+		case ISDN_CTYPE_A1:
+		case ISDN_CTYPE_A1_PCMCIA:
+		case ISDN_CTYPE_ELSA_PNP:
+		case ISDN_CTYPE_ELSA_PCMCIA:
+		case ISDN_CTYPE_IX1MICROR2:
+		case ISDN_CTYPE_DIEHLDIVA:
+		case ISDN_CTYPE_ASUSCOM:
+		case ISDN_CTYPE_TELEINT:
+		case ISDN_CTYPE_SEDLBAUER:
+		case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+		case ISDN_CTYPE_SEDLBAUER_FAX:
+		case ISDN_CTYPE_SPORTSTER:
+		case ISDN_CTYPE_MIC:
+		case ISDN_CTYPE_TELES3C:
+		case ISDN_CTYPE_ACERP10:
+		case ISDN_CTYPE_S0BOX:
+		case ISDN_CTYPE_FRITZPCI:
+		case ISDN_CTYPE_HSTSAPHIR:
+		case ISDN_CTYPE_GAZEL:
+		case ISDN_CTYPE_HFC_SX:
+		case ISDN_CTYPE_HFC_SP_PCMCIA:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io[i];
+			break;
+		case ISDN_CTYPE_ISURF:
+			cards[j].para[0] = irq[i];
+			cards[j].para[1] = io[i];
+			cards[j].para[2] = mem[i];
+			break;
+		case ISDN_CTYPE_ELSA_PCI:
+		case ISDN_CTYPE_NETJET_S:
+		case ISDN_CTYPE_AMD7930:
+		case ISDN_CTYPE_TELESPCI:
+		case ISDN_CTYPE_W6692:
+		case ISDN_CTYPE_NETJET_U:
+			break;
+		case ISDN_CTYPE_BKM_A4T:
+			break;
+		case ISDN_CTYPE_SCT_QUADRO:
+			if (irq[i]) {
+				cards[j].para[0] = irq[i];
+			} else {
+				/* QUADRO is a 4 BRI card */
+				cards[j++].para[0] = 1;
+				/* we need to check if further cards can be added */
+				if (j < HISAX_MAX_CARDS) {
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+					cards[j].protocol = protocol[i];
+					cards[j++].para[0] = 2;
+				}
+				if (j < HISAX_MAX_CARDS) {
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+					cards[j].protocol = protocol[i];
+					cards[j++].para[0] = 3;
+				}
+				if (j < HISAX_MAX_CARDS) {
+					cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+					cards[j].protocol = protocol[i];
+					cards[j].para[0] = 4;
+				}
+			}
+			break;
+		}
+		j++;
+	}
+	if (!nzproto) {
+		printk(KERN_WARNING
+		       "HiSax: Warning - no protocol specified\n");
+		printk(KERN_WARNING "HiSax: using protocol %s\n",
+		       DEFAULT_PROTO_NAME);
+	}
+#endif
+	if (!HiSax_id)
+		HiSax_id = HiSaxID;
+	if (!HiSaxID[0])
+		strcpy(HiSaxID, "HiSax");
+	for (i = 0; i < HISAX_MAX_CARDS; i++)
+		if (cards[i].typ > 0)
+			nrcards++;
+	printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+	       nrcards, (nrcards > 1) ? "s" : "");
+
+	/* Install only, if at least one card found */
+	if (!HiSax_inithardware(NULL))
+		return -ENODEV;
+	return 0;
+
+ out_tei:
+	TeiFree();
+ out_isdnl2:
+	Isdnl2Free();
+ out_isdnl3:
+	Isdnl3Free();
+ out_callc:
+	CallcFree();
+ out:
+	return retval;
+}
+
+static void __exit HiSax_exit(void)
+{
+	int cardnr = nrcards - 1;
+
+	while (cardnr >= 0)
+		HiSax_closecard(cardnr--);
+	Isdnl1Free();
+	TeiFree();
+	Isdnl2Free();
+	Isdnl3Free();
+	CallcFree();
+	printk(KERN_INFO "HiSax module removed\n");
+}
+
+int hisax_init_pcmcia(void *pcm_iob, int *busy_flag, struct IsdnCard *card)
+{
+	u_char ids[16];
+	int ret = -1;
+
+	cards[nrcards] = *card;
+	if (nrcards)
+		sprintf(ids, "HiSax%d", nrcards);
+	else
+		sprintf(ids, "HiSax");
+	if (!checkcard(nrcards, ids, busy_flag, THIS_MODULE))
+		goto error;
+
+	ret = nrcards;
+	nrcards++;
+error:
+	return ret;
+}
+
+EXPORT_SYMBOL(hisax_init_pcmcia);
+EXPORT_SYMBOL(HiSax_closecard);
+
+#include "hisax_if.h"
+
+EXPORT_SYMBOL(hisax_register);
+EXPORT_SYMBOL(hisax_unregister);
+
+static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg);
+static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg);
+static void hisax_d_l2l1(struct PStack *st, int pr, void *arg);
+static void hisax_b_l2l1(struct PStack *st, int pr, void *arg);
+static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg);
+static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs);
+static void hisax_bc_close(struct BCState *bcs);
+static void hisax_bh(struct IsdnCardState *cs);
+static void EChannel_proc_rcv(struct hisax_d_if *d_if);
+
+int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[],
+		   char *name, int protocol)
+{
+	int i, retval;
+	char id[20];
+	struct IsdnCardState *cs;
+
+	for (i = 0; i < HISAX_MAX_CARDS; i++) {
+		if (!cards[i].typ)
+			break;
+	}
+
+	if (i >= HISAX_MAX_CARDS)
+		return -EBUSY;
+
+	cards[i].typ = ISDN_CTYPE_DYNAMIC;
+	cards[i].protocol = protocol;
+	sprintf(id, "%s%d", name, i);
+	nrcards++;
+	retval = checkcard(i, id, NULL, hisax_d_if->owner);
+	if (retval == 0) { // yuck
+		cards[i].typ = 0;
+		nrcards--;
+		return retval;
+	}
+	cs = cards[i].cs;
+	hisax_d_if->cs = cs;
+	cs->hw.hisax_d_if = hisax_d_if;
+	cs->cardmsg = hisax_cardmsg;
+	INIT_WORK(&cs->tqueue, (void *)(void *)hisax_bh, cs);
+	cs->channel[0].d_st->l2.l2l1 = hisax_d_l2l1;
+	for (i = 0; i < 2; i++) {
+		cs->bcs[i].BC_SetStack = hisax_bc_setstack;
+		cs->bcs[i].BC_Close = hisax_bc_close;
+
+		b_if[i]->ifc.l1l2 = hisax_b_l1l2;
+
+		hisax_d_if->b_if[i] = b_if[i];
+	}
+	hisax_d_if->ifc.l1l2 = hisax_d_l1l2;
+	skb_queue_head_init(&hisax_d_if->erq);
+	clear_bit(0, &hisax_d_if->ph_state);
+	
+	return 0;
+}
+
+void hisax_unregister(struct hisax_d_if *hisax_d_if)
+{
+	cards[hisax_d_if->cs->cardnr].typ = 0;
+	HiSax_closecard(hisax_d_if->cs->cardnr);
+	skb_queue_purge(&hisax_d_if->erq);
+}
+
+#include "isdnl1.h"
+
+static void hisax_sched_event(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	schedule_work(&cs->tqueue);
+}
+
+static void hisax_bh(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+	int pr;
+
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(E_RCVBUFREADY, &cs->event))
+		EChannel_proc_rcv(cs->hw.hisax_d_if);
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		if (test_bit(0, &cs->hw.hisax_d_if->ph_state))
+			pr = PH_ACTIVATE | INDICATION;
+		else
+			pr = PH_DEACTIVATE | INDICATION;
+		for (st = cs->stlist; st; st = st->next)
+			st->l1.l1l2(st, pr, NULL);
+		
+	}
+}
+
+static void hisax_b_sched_event(struct BCState *bcs, int event)
+{
+	test_and_set_bit(event, &bcs->event);
+	schedule_work(&bcs->tqueue);
+}
+
+static inline void D_L2L1(struct hisax_d_if *d_if, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) d_if;
+	ifc->l2l1(ifc, pr, arg);
+}
+
+static inline void B_L2L1(struct hisax_b_if *b_if, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) b_if;
+	ifc->l2l1(ifc, pr, arg);
+}
+
+static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct hisax_d_if *d_if = (struct hisax_d_if *) ifc;
+	struct IsdnCardState *cs = d_if->cs;
+	struct PStack *st;
+	struct sk_buff *skb;
+
+	switch (pr) {
+	case PH_ACTIVATE | INDICATION:
+		set_bit(0, &d_if->ph_state);
+		hisax_sched_event(cs, D_L1STATECHANGE);
+		break;
+	case PH_DEACTIVATE | INDICATION:
+		clear_bit(0, &d_if->ph_state);
+		hisax_sched_event(cs, D_L1STATECHANGE);
+		break;
+	case PH_DATA | INDICATION:
+		skb_queue_tail(&cs->rq, arg);
+		hisax_sched_event(cs, D_RCVBUFREADY);
+		break;
+	case PH_DATA | CONFIRM:
+		skb = skb_dequeue(&cs->sq);
+		if (skb) {
+			D_L2L1(d_if, PH_DATA | REQUEST, skb);
+			break;
+		}
+		clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+		for (st = cs->stlist; st; st = st->next) {
+			if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+				break;
+			}
+		}
+		break;
+	case PH_DATA_E | INDICATION:
+		skb_queue_tail(&d_if->erq, arg);
+		hisax_sched_event(cs, E_RCVBUFREADY);
+		break;
+	default:
+		printk("pr %#x\n", pr);
+		break;
+	}
+}
+
+static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct hisax_b_if *b_if = (struct hisax_b_if *) ifc;
+	struct BCState *bcs = b_if->bcs;
+	struct PStack *st = bcs->st;
+	struct sk_buff *skb;
+
+	// FIXME use isdnl1?
+	switch (pr) {
+	case PH_ACTIVATE | INDICATION:
+		st->l1.l1l2(st, pr, NULL);
+		break;
+	case PH_DEACTIVATE | INDICATION:
+		st->l1.l1l2(st, pr, NULL);
+		clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		skb_queue_purge(&bcs->squeue);
+		bcs->hw.b_if = NULL;
+		break;
+	case PH_DATA | INDICATION:
+		skb_queue_tail(&bcs->rqueue, arg);
+		hisax_b_sched_event(bcs, B_RCVBUFREADY);
+		break;
+	case PH_DATA | CONFIRM:
+		bcs->tx_cnt -= (int) arg;
+		if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag)) {
+			u_long	flags;
+			spin_lock_irqsave(&bcs->aclock, flags);
+			bcs->ackcnt += (int) arg;
+			spin_unlock_irqrestore(&bcs->aclock, flags);
+			schedule_event(bcs, B_ACKPENDING);
+		}
+		skb = skb_dequeue(&bcs->squeue);
+		if (skb) {
+			B_L2L1(b_if, PH_DATA | REQUEST, skb);
+			break;
+		}
+		clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
+			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		}
+		break;
+	default:
+		printk("hisax_b_l1l2 pr %#x\n", pr);
+		break;
+	}
+}
+
+static void hisax_d_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+	case PH_PULL | INDICATION:
+		if (cs->debug & DEB_DLOG_HEX)
+			LogFrame(cs, skb->data, skb->len);
+		if (cs->debug & DEB_DLOG_VERBOSE)
+			dlogframe(cs, skb, 0);
+		Logl2Frame(cs, skb, "PH_DATA_REQ", 0);
+		// FIXME lock?
+		if (!test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			D_L2L1(hisax_d_if, PH_DATA | REQUEST, skb);
+		else
+			skb_queue_tail(&cs->sq, skb);
+		break;
+	case PH_PULL | REQUEST:
+		if (!test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		else
+			set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		break;
+	default:
+		D_L2L1(hisax_d_if, pr, arg);
+		break;
+	}
+}
+
+static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	return 0;
+}
+
+static void hisax_b_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct hisax_b_if *b_if = bcs->hw.b_if;
+
+	switch (pr) {
+	case PH_ACTIVATE | REQUEST:
+		B_L2L1(b_if, pr, (void *) st->l1.mode);
+		break;
+	case PH_DATA | REQUEST:
+	case PH_PULL | INDICATION:
+		// FIXME lock?
+		if (!test_and_set_bit(BC_FLG_BUSY, &bcs->Flag)) {
+			B_L2L1(b_if, PH_DATA | REQUEST, arg);
+		} else {
+			skb_queue_tail(&bcs->squeue, arg);
+		}
+		break;
+	case PH_PULL | REQUEST:
+		if (!test_bit(BC_FLG_BUSY, &bcs->Flag))
+			st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		else
+			set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		skb_queue_purge(&bcs->squeue);
+	default:
+		B_L2L1(b_if, pr, arg);
+		break;
+	}
+}
+
+static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+	struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if;
+
+	bcs->channel = st->l1.bc;
+
+	bcs->hw.b_if = hisax_d_if->b_if[st->l1.bc];
+	hisax_d_if->b_if[st->l1.bc]->bcs = bcs;
+
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hisax_b_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	skb_queue_head_init(&bcs->rqueue);
+	skb_queue_head_init(&bcs->squeue);
+	return 0;
+}
+
+static void hisax_bc_close(struct BCState *bcs)
+{
+	struct hisax_b_if *b_if = bcs->hw.b_if;
+
+	if (b_if)
+		B_L2L1(b_if, PH_DEACTIVATE | REQUEST, NULL);
+}
+
+static void EChannel_proc_rcv(struct hisax_d_if *d_if)
+{
+	struct IsdnCardState *cs = d_if->cs;
+	u_char *ptr;
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&d_if->erq)) != NULL) {
+		if (cs->debug & DEB_DLOG_HEX) {
+			ptr = cs->dlog;
+			if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) {
+				*ptr++ = 'E';
+				*ptr++ = 'C';
+				*ptr++ = 'H';
+				*ptr++ = 'O';
+				*ptr++ = ':';
+				ptr += QuickHex(ptr, skb->data, skb->len);
+				ptr--;
+				*ptr++ = '\n';
+				*ptr = 0;
+				HiSax_putstatus(cs, NULL, cs->dlog);
+			} else
+				HiSax_putstatus(cs, "LogEcho: ",
+						"warning Frame too big (%d)",
+						skb->len);
+		}
+		dev_kfree_skb_any(skb);
+	}
+}
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+
+static struct pci_device_id hisax_pci_tbl[] __initdata = {
+#ifdef CONFIG_HISAX_FRITZPCI
+	{PCI_VENDOR_ID_AVM,      PCI_DEVICE_ID_AVM_A1,           PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_DIEHLDIVA
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA20,     PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA20_U,   PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA201,    PCI_ANY_ID, PCI_ANY_ID},
+//#########################################################################################	
+	{PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA202,    PCI_ANY_ID, PCI_ANY_ID},
+//#########################################################################################	
+#endif
+#ifdef CONFIG_HISAX_ELSA
+	{PCI_VENDOR_ID_ELSA,     PCI_DEVICE_ID_ELSA_MICROLINK,   PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ELSA,     PCI_DEVICE_ID_ELSA_QS3000,      PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_GAZEL
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_R685,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_R753,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_DJINN_ITOO,   PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_QUADRO
+	{PCI_VENDOR_ID_PLX,      PCI_DEVICE_ID_PLX_9050,         PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_NICCY
+	{PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY,   PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+	{PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,     PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#if defined(CONFIG_HISAX_NETJET) || defined(CONFIG_HISAX_NETJET_U)
+	{PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300,     PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#if defined(CONFIG_HISAX_TELESPCI) || defined(CONFIG_HISAX_SCT_QUADRO)
+	{PCI_VENDOR_ID_ZORAN,    PCI_DEVICE_ID_ZORAN_36120,      PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_W6692
+	{PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,  PCI_ANY_ID,PCI_ANY_ID},
+	{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,    PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_HFC_PCI
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_2BD0,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B000,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B006,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B007,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B008,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B009,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B00A,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B00B,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B00C,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_CCD,      PCI_DEVICE_ID_CCD_B100,         PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ABOCOM,   PCI_DEVICE_ID_ABOCOM_2BD1,      PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ASUSTEK,  PCI_DEVICE_ID_ASUSTEK_0675,     PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_BERKOM,   PCI_DEVICE_ID_BERKOM_T_CONCEPT, PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_BERKOM,   PCI_DEVICE_ID_BERKOM_A1T,       PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ANIGMA,   PCI_DEVICE_ID_ANIGMA_MC145575,  PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_ZOLTRIX,  PCI_DEVICE_ID_ZOLTRIX_2BD0,     PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_E,      PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, PCI_ANY_ID, PCI_ANY_ID},
+	{PCI_VENDOR_ID_DIGI,     PCI_DEVICE_ID_DIGI_DF_M_A,      PCI_ANY_ID, PCI_ANY_ID},
+#endif
+	{ }				/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, hisax_pci_tbl);
+#endif /* CONFIG_PCI */
+
+module_init(HiSax_init);
+module_exit(HiSax_exit);
+
+EXPORT_SYMBOL(FsmNew);
+EXPORT_SYMBOL(FsmFree);
+EXPORT_SYMBOL(FsmEvent);
+EXPORT_SYMBOL(FsmChangeState);
+EXPORT_SYMBOL(FsmInitTimer);
+EXPORT_SYMBOL(FsmDelTimer);
+EXPORT_SYMBOL(FsmRestartTimer);
diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c
new file mode 100644
index 0000000..394d481
--- /dev/null
+++ b/drivers/isdn/hisax/diva.c
@@ -0,0 +1,1183 @@
+/* $Id: diva.c,v 1.33.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * low level stuff for Eicon.Diehl Diva Family ISDN cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Eicon Technology for documents and information
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "ipac.h"
+#include "ipacx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+const char *Diva_revision = "$Revision: 1.33.2.6 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define DIVA_HSCX_DATA		0
+#define DIVA_HSCX_ADR		4
+#define DIVA_ISA_ISAC_DATA	2
+#define DIVA_ISA_ISAC_ADR	6
+#define DIVA_ISA_CTRL		7
+#define DIVA_IPAC_ADR		0
+#define DIVA_IPAC_DATA		1
+
+#define DIVA_PCI_ISAC_DATA	8
+#define DIVA_PCI_ISAC_ADR	0xc
+#define DIVA_PCI_CTRL		0x10
+
+/* SUB Types */
+#define DIVA_ISA	1
+#define DIVA_PCI	2
+#define DIVA_IPAC_ISA	3
+#define DIVA_IPAC_PCI	4
+#define DIVA_IPACX_PCI	5
+
+/* CTRL (Read) */
+#define DIVA_IRQ_STAT	0x01
+#define DIVA_EEPROM_SDA	0x02
+
+/* CTRL (Write) */
+#define DIVA_IRQ_REQ	0x01
+#define DIVA_RESET	0x08
+#define DIVA_EEPROM_CLK	0x40
+#define DIVA_PCI_LED_A	0x10
+#define DIVA_PCI_LED_B	0x20
+#define DIVA_ISA_LED_A	0x20
+#define DIVA_ISA_LED_B	0x40
+#define DIVA_IRQ_CLR	0x80
+
+/* Siemens PITA */
+#define PITA_MISC_REG		0x1c
+#ifdef __BIG_ENDIAN
+#define PITA_PARA_SOFTRESET	0x00000001
+#define PITA_SER_SOFTRESET	0x00000002
+#define PITA_PARA_MPX_MODE	0x00000004
+#define PITA_INT0_ENABLE	0x00000200
+#else
+#define PITA_PARA_SOFTRESET	0x01000000
+#define PITA_SER_SOFTRESET	0x02000000
+#define PITA_PARA_MPX_MODE	0x04000000
+#define PITA_INT0_ENABLE	0x00020000
+#endif
+#define PITA_INT0_STATUS	0x02
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+static inline u_char
+memreadreg(unsigned long adr, u_char off)
+{
+	return(*((unsigned char *)
+		(((unsigned int *)adr) + off)));
+}
+
+static inline void
+memwritereg(unsigned long adr, u_char off, u_char data)
+{
+	register u_char *p;
+	
+	p = (unsigned char *)(((unsigned int *)adr) + off);
+	*p = data;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(readreg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.diva.hscx_adr,
+		cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+static u_char
+MemReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (memreadreg(cs->hw.diva.cfg_reg, offset+0x80));
+}
+
+static void
+MemWriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset|0x80, value);
+}
+
+static void
+MemReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		*data++ = memreadreg(cs->hw.diva.cfg_reg, 0x80);
+}
+
+static void
+MemWriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		memwritereg(cs->hw.diva.cfg_reg, 0x80, *data++);
+}
+
+static u_char
+MemReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(memreadreg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* IO-Functions for IPACX type cards */
+static u_char
+MemReadISAC_IPACX(struct IsdnCardState *cs, u_char offset)
+{
+	return (memreadreg(cs->hw.diva.cfg_reg, offset));
+}
+
+static void
+MemWriteISAC_IPACX(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset, value);
+}
+
+static void
+MemReadISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		*data++ = memreadreg(cs->hw.diva.cfg_reg, 0);
+}
+
+static void
+MemWriteISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+	while(size--)
+		memwritereg(cs->hw.diva.cfg_reg, 0, *data++);
+}
+
+static u_char
+MemReadHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return(memreadreg(cs->hw.diva.cfg_reg, offset + 
+                    (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1)));
+}
+
+static void
+MemWriteHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	memwritereg(cs->hw.diva.cfg_reg, offset + 
+              (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \
+                cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \
+		cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+diva_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+	int cnt=5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) {
+		val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40);
+		if (val)
+			hscx_int_main(cs, val);
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA);
+		if (val)
+			isac_interrupt(cs, val);
+		cnt--;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "Diva: IRQ LOOP\n");
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva_irq_ipac_isa(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	u_long flags;
+	int icnt=5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+Start_IPACISA:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPACISA;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n");
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static inline void
+MemwaitforCEC(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+MemwaitforXFW(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((!(MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+MemWriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+	MemwaitforCEC(cs, hscx);
+	MemWriteHSCX(cs, hscx, HSCX_CMDR, data);
+}
+
+static void
+Memhscx_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+	int cnt;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+		MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	cnt = count;
+	while (cnt--)
+		*ptr++ = memreadreg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0);
+	MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+Memhscx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count, cnt;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	u_char *ptr,*p;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_fill_fifo");
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->tx_skb->len;
+	cnt = count;
+	MemwaitforXFW(cs, bcs->hw.hscx.hscx);
+	p = ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	while(cnt--)
+		memwritereg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0,
+			*p++);
+	MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+Memhscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + hscx;
+	struct sk_buff *skb;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	int count;
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = MemReadHSCX(cs, hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX invalid frame");
+			if ((r & 0x40) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX RDO mode=%d",
+						bcs->mode);
+			if (!(r & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX CRC error");
+			MemWriteHSCXCMDR(cs, hscx, 0x80);
+		} else {
+			count = MemReadHSCX(cs, hscx, HSCX_RBCL) & (
+				test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+			if (count == 0)
+				count = fifo_size;
+			Memhscx_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HSCX: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		Memhscx_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				Memhscx_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hscx.count = 0; 
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			Memhscx_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+Memhscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+	u_char exval;
+	struct BCState *bcs;
+
+	if (val & 0x01) { // EXB
+		bcs = cs->bcs + 1;
+		exval = MemReadHSCX(cs, 1, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == 1)
+				Memhscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B EXIR %x", exval);
+	}
+	if (val & 0xf8) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B interrupt %x", val);
+		Memhscx_interrupt(cs, val, 1);
+	}
+	if (val & 0x02) {	// EXA
+		bcs = cs->bcs;
+		exval = MemReadHSCX(cs, 0, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == L1_MODE_TRANS)
+				Memhscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A EXIR %x", exval);
+	}
+	if (val & 0x04) {	// ICA
+		exval = MemReadHSCX(cs, 0, HSCX_ISTA);
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A interrupt %x", exval);
+		Memhscx_interrupt(cs, exval, 0);
+	}
+}
+
+static irqreturn_t
+diva_irq_ipac_pci(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista,val;
+	int icnt=5;
+	u_char *cfg;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	cfg = (u_char *) cs->hw.diva.pci_cfg;
+	val = *cfg;
+	if (!(val & PITA_INT0_STATUS)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE; /* other shared IRQ */
+	}
+	*cfg = PITA_INT0_STATUS; /* Reset pending INT0 */
+	ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA);
+Start_IPACPCI:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = memreadreg(cs->hw.diva.cfg_reg, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			Memhscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & memreadreg(cs->hw.diva.cfg_reg, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPACPCI;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "DIVA IPAC PCI IRQ LOOP\n");
+	memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xFF);
+	memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva_irq_ipacx_pci(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_char *cfg;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	cfg = (u_char *) cs->hw.diva.pci_cfg;
+	val = *cfg;
+	if (!(val &PITA_INT0_STATUS)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE; // other shared IRQ
+	}
+ 	interrupt_ipacx(cs);      // handler for chip
+	*cfg = PITA_INT0_STATUS;  // Reset PLX interrupt
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_diva(struct IsdnCardState *cs)
+{
+	int bytecnt;
+
+	if ((cs->subtyp == DIVA_IPAC_PCI) || 
+	    (cs->subtyp == DIVA_IPACX_PCI)   ) {
+		u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg;
+
+		*cfg = 0; /* disable INT0/1 */ 
+		*cfg = 2; /* reset pending INT0 */
+		iounmap((void *)cs->hw.diva.cfg_reg);
+		iounmap((void *)cs->hw.diva.pci_cfg);
+		return;
+	} else if (cs->subtyp != DIVA_IPAC_ISA) {
+		del_timer(&cs->hw.diva.tl);
+		if (cs->hw.diva.cfg_reg)
+			byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
+	}
+	if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA))
+		bytecnt = 8;
+	else
+		bytecnt = 32;
+	if (cs->hw.diva.cfg_reg) {
+		release_region(cs->hw.diva.cfg_reg, bytecnt);
+	}
+}
+
+static void
+reset_diva(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
+		mdelay(10);
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
+		mdelay(10);
+		writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
+	} else if (cs->subtyp == DIVA_IPAC_PCI) {
+		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
+					PITA_MISC_REG);
+		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
+		mdelay(10);
+		*ireg = PITA_PARA_MPX_MODE;
+		mdelay(10);
+		memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0);
+	} else if (cs->subtyp == DIVA_IPACX_PCI) {
+		unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
+					PITA_MISC_REG);
+		*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
+		mdelay(10);
+		*ireg = PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET;
+		mdelay(10);
+		MemWriteISAC_IPACX(cs, IPACX_MASK, 0xff); // Interrupts off
+	} else { /* DIVA 2.0 */
+		cs->hw.diva.ctrl_reg = 0;        /* Reset On */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		mdelay(10);
+		cs->hw.diva.ctrl_reg |= DIVA_RESET;  /* Reset Off */
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+		mdelay(10);
+		if (cs->subtyp == DIVA_ISA)
+			cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
+		else {
+			/* Workaround PCI9060 */
+			byteout(cs->hw.diva.pci_cfg + 0x69, 9);
+			cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
+		}
+		byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	}
+}
+
+#define DIVA_ASSIGN 1
+
+static void
+diva_led_handler(struct IsdnCardState *cs)
+{
+	int blink = 0;
+
+	if ((cs->subtyp == DIVA_IPAC_ISA) ||
+	    (cs->subtyp == DIVA_IPAC_PCI) ||
+	    (cs->subtyp == DIVA_IPACX_PCI)   )
+		return;
+	del_timer(&cs->hw.diva.tl);
+	if (cs->hw.diva.status & DIVA_ASSIGN)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+	else {
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+		blink = 250;
+	}
+	if (cs->hw.diva.status & 0xf000)
+		cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+	else if (cs->hw.diva.status & 0x0f00) {
+		cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+		blink = 500;
+	} else
+		cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ?
+			DIVA_ISA_LED_B : DIVA_PCI_LED_B);
+
+	byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.diva.tl);
+		cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.diva.tl);
+	}
+}
+
+static int
+Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_int *ireg;
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_diva(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_diva(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_diva(cs);
+			if (cs->subtyp == DIVA_IPACX_PCI) {
+				ireg = (unsigned int *)cs->hw.diva.pci_cfg;
+				*ireg = PITA_INT0_ENABLE;
+				init_ipacx(cs, 3); // init chip and enable interrupts
+				spin_unlock_irqrestore(&cs->lock, flags);
+				return (0);
+			}
+			if (cs->subtyp == DIVA_IPAC_PCI) {
+				ireg = (unsigned int *)cs->hw.diva.pci_cfg;
+				*ireg = PITA_INT0_ENABLE;
+			}
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+		case (MDL_REMOVE | REQUEST):
+			cs->hw.diva.status = 0;
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			cs->hw.diva.status |= DIVA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((long)arg)
+				cs->hw.diva.status |=  0x0200;
+			else
+				cs->hw.diva.status |=  0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((long)arg)
+				cs->hw.diva.status |=  0x2000;
+			else
+				cs->hw.diva.status |=  0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((long)arg) {
+				cs->hw.diva.status &=  ~0x2000;
+				cs->hw.diva.status &=  ~0x0200;
+			} else {
+				cs->hw.diva.status &=  ~0x1000;
+				cs->hw.diva.status &=  ~0x0100;
+			}
+			break;
+	}
+	if ((cs->subtyp != DIVA_IPAC_ISA) && 
+	    (cs->subtyp != DIVA_IPAC_PCI) &&
+	    (cs->subtyp != DIVA_IPACX_PCI)) {
+	    	spin_lock_irqsave(&cs->lock, flags);
+		diva_led_handler(cs);
+		spin_unlock_irqrestore(&cs->lock, flags);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_diva __initdata = NULL;
+static struct pci_dev *dev_diva_u __initdata = NULL;
+static struct pci_dev *dev_diva201 __initdata = NULL;
+static struct pci_dev *dev_diva202 __initdata = NULL;
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id diva_ids[] __initdata = {
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51),
+	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), 
+	  (unsigned long) "Diva picola" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51),
+	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x51), 
+	  (unsigned long) "Diva picola" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71),
+	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71), 
+	  (unsigned long) "Diva 2.0" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71),
+	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x71), 
+	  (unsigned long) "Diva 2.0" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1),
+	  ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1), 
+	  (unsigned long) "Diva 2.01" },
+	{ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1),
+	  ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0xA1), 
+	  (unsigned long) "Diva 2.01" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &diva_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+
+int __init
+setup_diva(struct IsdnCard *card)
+{
+	int bytecnt = 8;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Diva_revision);
+	printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_DIEHLDIVA)
+		return(0);
+	cs->hw.diva.status = 0;
+	if (card->para[1]) {
+		cs->hw.diva.ctrl_reg = 0;
+		cs->hw.diva.cfg_reg = card->para[1];
+		val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR,
+			cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+		if ((val == 1) || (val==2)) {
+			cs->subtyp = DIVA_IPAC_ISA;
+			cs->hw.diva.ctrl = 0;
+			cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		} else {
+			cs->subtyp = DIVA_ISA;
+			cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
+			cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
+			cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
+			cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
+			cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+		}
+		cs->irq = card->para[0];
+	} else {
+#ifdef __ISAPNP__
+		if (isapnp_present()) {
+			struct pnp_dev *pnp_d;
+			while(ipid->card_vendor) {
+				if ((pnp_c = pnp_find_card(ipid->card_vendor,
+					ipid->card_device, pnp_c))) {
+					pnp_d = NULL;
+					if ((pnp_d = pnp_find_dev(pnp_c,
+						ipid->vendor, ipid->function, pnp_d))) {
+						int err;
+
+						printk(KERN_INFO "HiSax: %s detected\n",
+							(char *)ipid->driver_data);
+						pnp_disable_dev(pnp_d);
+						err = pnp_activate_dev(pnp_d);
+						if (err<0) {
+							printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+								__FUNCTION__, err);
+							return(0);
+						}
+						card->para[1] = pnp_port_start(pnp_d, 0);
+						card->para[0] = pnp_irq(pnp_d, 0);
+						if (!card->para[0] || !card->para[1]) {
+							printk(KERN_ERR "Diva PnP:some resources are missing %ld/%lx\n",
+								card->para[0], card->para[1]);
+							pnp_disable_dev(pnp_d); 
+							return(0);
+						}
+						cs->hw.diva.cfg_reg  = card->para[1];
+						cs->irq = card->para[0];
+						if (ipid->function == ISAPNP_FUNCTION(0xA1)) {
+							cs->subtyp = DIVA_IPAC_ISA;
+							cs->hw.diva.ctrl = 0;
+							cs->hw.diva.isac =
+								card->para[1] + DIVA_IPAC_DATA;
+							cs->hw.diva.hscx =
+								card->para[1] + DIVA_IPAC_DATA;
+							cs->hw.diva.isac_adr =
+								card->para[1] + DIVA_IPAC_ADR;
+							cs->hw.diva.hscx_adr =
+								card->para[1] + DIVA_IPAC_ADR;
+							test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+						} else {
+							cs->subtyp = DIVA_ISA;
+							cs->hw.diva.ctrl =
+								card->para[1] + DIVA_ISA_CTRL;
+							cs->hw.diva.isac =
+								card->para[1] + DIVA_ISA_ISAC_DATA;
+							cs->hw.diva.hscx =
+								card->para[1] + DIVA_HSCX_DATA;
+							cs->hw.diva.isac_adr =
+								card->para[1] + DIVA_ISA_ISAC_ADR;
+							cs->hw.diva.hscx_adr =
+								card->para[1] + DIVA_HSCX_ADR;
+						}
+						goto ready;
+					} else {
+						printk(KERN_ERR "Diva PnP: PnP error card found, no device\n");
+						return(0);
+					}
+				}
+				ipid++;
+				pnp_c=NULL;
+			} 
+			if (!ipid->card_vendor) {
+				printk(KERN_INFO "Diva PnP: no ISAPnP card found\n");
+			}
+		}
+#endif
+#ifdef CONFIG_PCI
+		cs->subtyp = 0;
+		if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) {
+			if (pci_enable_device(dev_diva))
+				return(0);
+			cs->subtyp = DIVA_PCI;
+			cs->irq = dev_diva->irq;
+			cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2);
+		} else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) {
+			if (pci_enable_device(dev_diva_u))
+				return(0);
+			cs->subtyp = DIVA_PCI;
+			cs->irq = dev_diva_u->irq;
+			cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2);
+		} else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) {
+			if (pci_enable_device(dev_diva201))
+				return(0);
+			cs->subtyp = DIVA_IPAC_PCI;
+			cs->irq = dev_diva201->irq;
+			cs->hw.diva.pci_cfg =
+				(ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096);
+			cs->hw.diva.cfg_reg =
+				(ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096);
+		} else if ((dev_diva202 = pci_find_device(PCI_VENDOR_ID_EICON,
+			PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) {
+			if (pci_enable_device(dev_diva202))
+				return(0);
+			cs->subtyp = DIVA_IPACX_PCI;
+			cs->irq = dev_diva202->irq;
+			cs->hw.diva.pci_cfg =
+				(ulong) ioremap(pci_resource_start(dev_diva202, 0), 4096);
+			cs->hw.diva.cfg_reg =
+				(ulong) ioremap(pci_resource_start(dev_diva202, 1), 4096);
+		} else {
+			printk(KERN_WARNING "Diva: No PCI card found\n");
+			return(0);
+		}
+
+		if (!cs->irq) {
+			printk(KERN_WARNING "Diva: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!cs->hw.diva.cfg_reg) {
+			printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		cs->irq_flags |= SA_SHIRQ;
+#else
+		printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Diva: unable to config DIVA PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+		if ((cs->subtyp == DIVA_IPAC_PCI) ||
+		    (cs->subtyp == DIVA_IPACX_PCI)   ) {
+			cs->hw.diva.ctrl = 0;
+			cs->hw.diva.isac = 0;
+			cs->hw.diva.hscx = 0;
+			cs->hw.diva.isac_adr = 0;
+			cs->hw.diva.hscx_adr = 0;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+			bytecnt = 0;
+		} else {
+			cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL;
+			cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA;
+			cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA;
+			cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR;
+			cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR;
+			bytecnt = 32;
+		}
+	}
+ready:
+	printk(KERN_INFO
+		"Diva: %s card configured at %#lx IRQ %d\n",
+		(cs->subtyp == DIVA_PCI) ? "PCI" :
+		(cs->subtyp == DIVA_ISA) ? "ISA" : 
+		(cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" :
+		(cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
+		cs->hw.diva.cfg_reg, cs->irq);
+	if ((cs->subtyp == DIVA_IPAC_PCI)  || 
+	    (cs->subtyp == DIVA_IPACX_PCI) || 
+	    (cs->subtyp == DIVA_PCI)         )
+		printk(KERN_INFO "Diva: %s space at %#lx\n",
+			(cs->subtyp == DIVA_PCI) ? "PCI" :
+			(cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
+			cs->hw.diva.pci_cfg);
+	if ((cs->subtyp != DIVA_IPAC_PCI) &&
+	    (cs->subtyp != DIVA_IPACX_PCI)   ) {
+		if (!request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn")) {
+			printk(KERN_WARNING
+			       "HiSax: %s config port %lx-%lx already in use\n",
+			       CardType[card->typ],
+			       cs->hw.diva.cfg_reg,
+			       cs->hw.diva.cfg_reg + bytecnt);
+			return (0);
+		}
+	}
+	cs->BC_Read_Reg  = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Diva_card_msg;
+	setup_isac(cs);
+	if (cs->subtyp == DIVA_IPAC_ISA) {
+		cs->readisac  = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo  = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &diva_irq_ipac_isa;
+		val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+	} else if (cs->subtyp == DIVA_IPAC_PCI) {
+		cs->readisac  = &MemReadISAC_IPAC;
+		cs->writeisac = &MemWriteISAC_IPAC;
+		cs->readisacfifo  = &MemReadISACfifo_IPAC;
+		cs->writeisacfifo = &MemWriteISACfifo_IPAC;
+		cs->BC_Read_Reg  = &MemReadHSCX;
+		cs->BC_Write_Reg = &MemWriteHSCX;
+		cs->BC_Send_Data = &Memhscx_fill_fifo;
+		cs->irq_func = &diva_irq_ipac_pci;
+		val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID);
+		printk(KERN_INFO "Diva: IPAC version %x\n", val);
+	} else if (cs->subtyp == DIVA_IPACX_PCI) {
+		cs->readisac  = &MemReadISAC_IPACX;
+		cs->writeisac = &MemWriteISAC_IPACX;
+		cs->readisacfifo  = &MemReadISACfifo_IPACX;
+		cs->writeisacfifo = &MemWriteISACfifo_IPACX;
+		cs->BC_Read_Reg  = &MemReadHSCX_IPACX;
+		cs->BC_Write_Reg = &MemWriteHSCX_IPACX;
+		cs->BC_Send_Data = NULL; // function located in ipacx module
+		cs->irq_func = &diva_irq_ipacx_pci;
+		printk(KERN_INFO "Diva: IPACX Design Id: %x\n", 
+			MemReadISAC_IPACX(cs, IPACX_ID) &0x3F);
+	} else { /* DIVA 2.0 */
+		cs->hw.diva.tl.function = (void *) diva_led_handler;
+		cs->hw.diva.tl.data = (long) cs;
+		init_timer(&cs->hw.diva.tl);
+		cs->readisac  = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo  = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		cs->irq_func = &diva_interrupt;
+		ISACVersion(cs, "Diva:");
+		if (HscxVersion(cs, "Diva:")) {
+			printk(KERN_WARNING
+		       "Diva: wrong HSCX versions check IO address\n");
+			release_io_diva(cs);
+			return (0);
+		}
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c
new file mode 100644
index 0000000..4d7a025
--- /dev/null
+++ b/drivers/isdn/hisax/elsa.c
@@ -0,0 +1,1190 @@
+/* $Id: elsa.c,v 2.32.2.4 2004/01/24 20:47:21 keil Exp $
+ *
+ * low level stuff for Elsa isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Elsa GmbH for documents and information
+ *
+ *              Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
+ *              for ELSA PCMCIA support
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "arcofi.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+extern const char *CardType[];
+
+const char *Elsa_revision = "$Revision: 2.32.2.4 $";
+const char *Elsa_Types[] =
+{"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
+ "PCMCIA", "QS 1000", "QS 3000", "Microlink PCI", "QS 3000 PCI", 
+ "PCMCIA-IPAC" };
+
+const char *ITACVer[] =
+{"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
+ "B1", "A1"};
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ELSA_ISAC	0
+#define ELSA_ISAC_PCM	1
+#define ELSA_ITAC	1
+#define ELSA_HSCX	2
+#define ELSA_ALE	3
+#define ELSA_ALE_PCM	4
+#define ELSA_CONTROL	4
+#define ELSA_CONFIG	5
+#define ELSA_START_TIMER 6
+#define ELSA_TRIG_IRQ	7
+
+#define ELSA_PC      1
+#define ELSA_PCC8    2
+#define ELSA_PCC16   3
+#define ELSA_PCF     4
+#define ELSA_PCFPRO  5
+#define ELSA_PCMCIA  6
+#define ELSA_QS1000  7
+#define ELSA_QS3000  8
+#define ELSA_QS1000PCI 9
+#define ELSA_QS3000PCI 10
+#define ELSA_PCMCIA_IPAC 11
+
+/* PCI stuff */
+#define ELSA_PCI_IRQ_MASK	0x04
+
+/* ITAC Registeradressen (only Microlink PC) */
+#define ITAC_SYS	0x34
+#define ITAC_ISEN	0x48
+#define ITAC_RFIE	0x4A
+#define ITAC_XFIE	0x4C
+#define ITAC_SCIE	0x4E
+#define ITAC_STIE	0x46
+
+/***                                                                    ***
+ ***   Makros als Befehle fuer die Kartenregister                       ***
+ ***   (mehrere Befehle werden durch Bit-Oderung kombiniert)            ***
+ ***                                                                    ***/
+
+/* Config-Register (Read) */
+#define ELSA_TIMER_RUN       0x02	/* Bit 1 des Config-Reg     */
+#define ELSA_TIMER_RUN_PCC8  0x01	/* Bit 0 des Config-Reg  bei PCC */
+#define ELSA_IRQ_IDX       0x38	/* Bit 3,4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PCC8  0x30	/* Bit 4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PC    0x0c	/* Bit 2,3 des Config-Reg */
+
+/* Control-Register (Write) */
+#define ELSA_LINE_LED        0x02	/* Bit 1 Gelbe LED */
+#define ELSA_STAT_LED        0x08	/* Bit 3 Gruene LED */
+#define ELSA_ISDN_RESET      0x20	/* Bit 5 Reset-Leitung */
+#define ELSA_ENA_TIMER_INT   0x80	/* Bit 7 Freigabe Timer Interrupt */
+
+/* ALE-Register (Read) */
+#define ELSA_HW_RELEASE      0x07	/* Bit 0-2 Hardwarerkennung */
+#define ELSA_S0_POWER_BAD    0x08	/* Bit 3 S0-Bus Spannung fehlt */
+
+/* Status Flags */
+#define ELSA_TIMER_AKTIV 1
+#define ELSA_BAD_PWR     2
+#define ELSA_ASSIGN      4
+
+#define RS_ISR_PASS_LIMIT 256
+#define _INLINE_ inline
+#define FLG_MODEM_ACTIVE 1
+/* IPAC AUX */
+#define ELSA_IPAC_LINE_LED	0x40	/* Bit 6 Gelbe LED */
+#define ELSA_IPAC_STAT_LED	0x80	/* Bit 7 Gruene LED */
+
+#if ARCOFI_USE
+static struct arcofi_msg ARCOFI_XOP_F =
+	{NULL,0,2,{0xa1,0x3f,0,0,0,0,0,0,0,0}}; /* Normal OP */
+static struct arcofi_msg ARCOFI_XOP_1 =
+	{&ARCOFI_XOP_F,0,2,{0xa1,0x31,0,0,0,0,0,0,0,0}}; /* PWR UP */
+static struct arcofi_msg ARCOFI_SOP_F = 
+	{&ARCOFI_XOP_1,0,10,{0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}};
+static struct arcofi_msg ARCOFI_COP_9 =
+	{&ARCOFI_SOP_F,0,10,{0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}}; /* RX */
+static struct arcofi_msg ARCOFI_COP_8 =
+	{&ARCOFI_COP_9,0,10,{0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}}; /* TX */
+static struct arcofi_msg ARCOFI_COP_7 =
+	{&ARCOFI_COP_8,0,4,{0xa1,0x27,0x80,0x80,0,0,0,0,0,0}}; /* GZ */
+static struct arcofi_msg ARCOFI_COP_6 =
+	{&ARCOFI_COP_7,0,6,{0xa1,0x26,0,0,0x82,0x7c,0,0,0,0}}; /* GRL GRH */
+static struct arcofi_msg ARCOFI_COP_5 =
+	{&ARCOFI_COP_6,0,4,{0xa1,0x25,0xbb,0x4a,0,0,0,0,0,0}}; /* GTX */
+static struct arcofi_msg ARCOFI_VERSION =
+	{NULL,1,2,{0xa0,0,0,0,0,0,0,0,0,0}};
+static struct arcofi_msg ARCOFI_XOP_0 =
+	{NULL,0,2,{0xa1,0x30,0,0,0,0,0,0,0,0}}; /* PWR Down */
+
+static void set_arcofi(struct IsdnCardState *cs, int bc);
+
+#include "elsa_ser.c"
+#endif /* ARCOFI_USE */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.elsa.ale,
+			cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.elsa.ale,
+		 cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+static inline u_char
+readitac(struct IsdnCardState *cs, u_char off)
+{
+	register u_char ret;
+
+	byteout(cs->hw.elsa.ale, off);
+	ret = bytein(cs->hw.elsa.itac);
+	return (ret);
+}
+
+static inline void
+writeitac(struct IsdnCardState *cs, u_char off, u_char data)
+{
+	byteout(cs->hw.elsa.ale, off);
+	byteout(cs->hw.elsa.itac, data);
+}
+
+static inline int
+TimerRun(struct IsdnCardState *cs)
+{
+	register u_char v;
+
+	v = bytein(cs->hw.elsa.cfg);
+	if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000))
+		return (0 == (v & ELSA_TIMER_RUN));
+	else if (cs->subtyp == ELSA_PCC8)
+		return (v & ELSA_TIMER_RUN_PCC8);
+	return (v & ELSA_TIMER_RUN);
+}
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \
+		cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+elsa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_long flags;
+	u_char val;
+	int icnt=5;
+
+	if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
+	/* The card tends to generate interrupts while being removed
+	   causing us to just crash the kernel. bad. */
+		printk(KERN_WARNING "Elsa: card not available!\n");
+		return IRQ_NONE;
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
+		}
+	}
+#endif
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val) {
+		hscx_int_main(cs, val);
+	}
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val) {
+		isac_interrupt(cs, val);
+	}
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		icnt--;
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+	if (val && icnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		icnt--;
+		goto Start_ISAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING"ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF);
+	if (cs->hw.elsa.status & ELSA_TIMER_AKTIV) {
+		if (!TimerRun(cs)) {
+			/* Timer Restart */
+			byteout(cs->hw.elsa.timer, 0);
+			cs->hw.elsa.counter++;
+		}
+	}
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+		val = serial_inp(cs, UART_MCR);
+		val ^= 0x8;
+		serial_outp(cs, UART_MCR, val);
+	}
+#endif
+	if (cs->hw.elsa.trig)
+		byteout(cs->hw.elsa.trig, 0x00);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+elsa_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_long flags;
+	u_char ista,val;
+	int icnt=5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (cs->subtyp == ELSA_QS1000PCI || cs->subtyp == ELSA_QS3000PCI) {
+		val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
+		if (!(val & ELSA_PCI_IRQ_MASK)) {
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_NONE;
+		}
+	}
+#if ARCOFI_USE
+	if (cs->hw.elsa.MFlag) {
+		val = serial_inp(cs, UART_IIR);
+		if (!(val & UART_IIR_NO_INT)) {
+			debugl1(cs,"IIR %02x", val);
+			rs_interrupt_elsa(intno, cs);
+		}
+	}
+#endif
+	ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		printk(KERN_WARNING "ELSA IRQ LOOP\n");
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_elsa(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	del_timer(&cs->hw.elsa.tl);
+#if ARCOFI_USE
+	clear_arcofi(cs);
+#endif
+	if (cs->hw.elsa.ctrl)
+		byteout(cs->hw.elsa.ctrl, 0);	/* LEDs Out */
+	if (cs->subtyp == ELSA_QS1000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x01);  /* disable IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		bytecnt = 2;
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+	if (cs->subtyp == ELSA_QS3000PCI) {
+		byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* disable ELSA PCI IRQ */
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		release_region(cs->hw.elsa.cfg, 0x80);
+	}
+ 	if (cs->subtyp == ELSA_PCMCIA_IPAC) {
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ 	}
+	if ((cs->subtyp == ELSA_PCFPRO) ||
+		(cs->subtyp == ELSA_QS3000) ||
+		(cs->subtyp == ELSA_PCF) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
+		bytecnt = 16;
+#if ARCOFI_USE
+		release_modem(cs);
+#endif
+	}
+	if (cs->hw.elsa.base)
+		release_region(cs->hw.elsa.base, bytecnt);
+}
+
+static void
+reset_elsa(struct IsdnCardState *cs)
+{
+	if (cs->hw.elsa.timer) {
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= 0x50;
+		cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET;	/* Reset On */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET;	/* Reset Off */
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+		/* Wait 1 Timer */
+		byteout(cs->hw.elsa.timer, 0);
+		while (TimerRun(cs));
+		if (cs->hw.elsa.trig)
+			byteout(cs->hw.elsa.trig, 0xff);
+	}
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) {
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
+		mdelay(10);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00);
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
+		mdelay(10);
+		if (cs->subtyp != ELSA_PCMCIA_IPAC) {
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0);
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c);
+		} else {
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_PCFG, 0x10);
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x4);
+			writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0xf8);
+		}
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+		if (cs->subtyp == ELSA_QS1000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+		else if (cs->subtyp == ELSA_QS3000PCI)
+			byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */
+	}
+}
+
+#if ARCOFI_USE
+
+static void
+set_arcofi(struct IsdnCardState *cs, int bc) {
+	cs->dc.isac.arcofi_bc = bc;
+	arcofi_fsm(cs, ARCOFI_START, &ARCOFI_COP_5);
+	interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+}
+
+static int
+check_arcofi(struct IsdnCardState *cs)
+{
+	int arcofi_present = 0;
+	char tmp[40];
+	char *t;
+	u_char *p;
+
+	if (!cs->dc.isac.mon_tx)
+		if (!(cs->dc.isac.mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "ISAC MON TX out of buffers!");
+			return(0);
+		}
+	cs->dc.isac.arcofi_bc = 0;
+	arcofi_fsm(cs, ARCOFI_START, &ARCOFI_VERSION);
+	interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+	if (!test_and_clear_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags)) {
+			debugl1(cs, "Arcofi response received %d bytes", cs->dc.isac.mon_rxp);
+			p = cs->dc.isac.mon_rx;
+			t = tmp;
+			t += sprintf(tmp, "Arcofi data");
+			QuickHex(t, p, cs->dc.isac.mon_rxp);
+			debugl1(cs, tmp);
+			if ((cs->dc.isac.mon_rxp == 2) && (cs->dc.isac.mon_rx[0] == 0xa0)) {
+				switch(cs->dc.isac.mon_rx[1]) {
+					case 0x80:
+						debugl1(cs, "Arcofi 2160 detected");
+						arcofi_present = 1;
+						break;
+					case 0x82:
+						debugl1(cs, "Arcofi 2165 detected");
+						arcofi_present = 2;
+						break;
+					case 0x84:
+						debugl1(cs, "Arcofi 2163 detected");
+						arcofi_present = 3;
+						break;
+					default:
+						debugl1(cs, "unknown Arcofi response");
+						break;
+				}
+			} else
+				debugl1(cs, "undefined Monitor response");
+			cs->dc.isac.mon_rxp = 0;
+	} else if (cs->dc.isac.mon_tx) {
+		debugl1(cs, "Arcofi not detected");
+	}
+	if (arcofi_present) {
+		if (cs->subtyp==ELSA_QS1000) {
+			cs->subtyp = ELSA_QS3000;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%lx\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (!request_region(cs->hw.elsa.base, 16, "elsa isdn modem")) {
+				printk(KERN_WARNING
+					"HiSax: %s config port %lx-%lx already in use\n",
+					Elsa_Types[cs->subtyp],
+					cs->hw.elsa.base + 8,
+					cs->hw.elsa.base + 16);
+			}
+		} else if (cs->subtyp==ELSA_PCC16) {
+			cs->subtyp = ELSA_PCF;
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%lx\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+			release_region(cs->hw.elsa.base, 8);
+			if (!request_region(cs->hw.elsa.base, 16, "elsa isdn modem")) {
+				printk(KERN_WARNING
+					"HiSax: %s config port %lx-%lx already in use\n",
+					Elsa_Types[cs->subtyp],
+					cs->hw.elsa.base + 8,
+					cs->hw.elsa.base + 16);
+			}
+		} else
+			printk(KERN_INFO
+				"Elsa: %s detected modem at 0x%lx\n",
+				Elsa_Types[cs->subtyp],
+				cs->hw.elsa.base+8);
+		arcofi_fsm(cs, ARCOFI_START, &ARCOFI_XOP_0);
+		interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+		return(1);
+	}
+	return(0);
+}
+#endif /* ARCOFI_USE */
+
+static void
+elsa_led_handler(struct IsdnCardState *cs)
+{
+	int blink = 0;
+
+	if (cs->subtyp == ELSA_PCMCIA || cs->subtyp == ELSA_PCMCIA_IPAC)
+		return;
+	del_timer(&cs->hw.elsa.tl);
+	if (cs->hw.elsa.status & ELSA_ASSIGN)
+		cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED;
+	else if (cs->hw.elsa.status & ELSA_BAD_PWR)
+		cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED;
+	else {
+		cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED;
+		blink = 250;
+	}
+	if (cs->hw.elsa.status & 0xf000)
+		cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED;
+	else if (cs->hw.elsa.status & 0x0f00) {
+		cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED;
+		blink = 500;
+	} else
+		cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED;
+
+	if ((cs->subtyp == ELSA_QS1000PCI) ||
+		(cs->subtyp == ELSA_QS3000PCI)) {
+		u_char led = 0xff;
+		if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED)
+			led ^= ELSA_IPAC_LINE_LED;
+		if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED)
+			led ^= ELSA_IPAC_STAT_LED;
+		writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led);
+	} else
+		byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+	if (blink) {
+		init_timer(&cs->hw.elsa.tl);
+		cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000);
+		add_timer(&cs->hw.elsa.tl);
+	}
+}
+
+static int
+Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	int ret = 0;
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_elsa(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_elsa(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->debug |= L1_DEB_IPAC;
+			reset_elsa(cs);
+			inithscxisac(cs, 1);
+			if ((cs->subtyp == ELSA_QS1000) ||
+			    (cs->subtyp == ELSA_QS3000))
+			{
+				byteout(cs->hw.elsa.timer, 0);
+			}
+			if (cs->hw.elsa.trig)
+				byteout(cs->hw.elsa.trig, 0xff);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			if ((cs->subtyp == ELSA_PCMCIA) ||
+				(cs->subtyp == ELSA_PCMCIA_IPAC) ||
+				(cs->subtyp == ELSA_QS1000PCI)) {
+				return(0);
+			} else if (cs->subtyp == ELSA_QS3000PCI) {
+				ret = 0;
+			} else {
+				spin_lock_irqsave(&cs->lock, flags);
+				cs->hw.elsa.counter = 0;
+				cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT;
+				cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				byteout(cs->hw.elsa.timer, 0);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				msleep(110);
+				spin_lock_irqsave(&cs->lock, flags);
+				cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
+				byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+				cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
+				spin_unlock_irqrestore(&cs->lock, flags);
+				printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+				       cs->hw.elsa.counter);
+				if ((cs->hw.elsa.counter > 10) &&
+					(cs->hw.elsa.counter < 16)) {
+					printk(KERN_INFO "Elsa: timer and irq OK\n");
+					ret = 0;
+				} else {
+					printk(KERN_WARNING
+					       "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+					       cs->hw.elsa.counter, cs->irq);
+					ret = 1;
+				}
+			}
+#if ARCOFI_USE
+			if (check_arcofi(cs)) {
+				init_modem(cs);
+			}
+#endif
+			elsa_led_handler(cs);
+			return(ret);
+		case (MDL_REMOVE | REQUEST):
+			cs->hw.elsa.status &= 0;
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			cs->hw.elsa.status |= ELSA_ASSIGN;
+			break;
+		case MDL_INFO_SETUP:
+			if ((long) arg)
+				cs->hw.elsa.status |= 0x0200;
+			else
+				cs->hw.elsa.status |= 0x0100;
+			break;
+		case MDL_INFO_CONN:
+			if ((long) arg)
+				cs->hw.elsa.status |= 0x2000;
+			else
+				cs->hw.elsa.status |= 0x1000;
+			break;
+		case MDL_INFO_REL:
+			if ((long) arg) {
+				cs->hw.elsa.status &= ~0x2000;
+				cs->hw.elsa.status &= ~0x0200;
+			} else {
+				cs->hw.elsa.status &= ~0x1000;
+				cs->hw.elsa.status &= ~0x0100;
+			}
+			break;
+#if ARCOFI_USE
+		case CARD_AUX_IND:
+			if (cs->hw.elsa.MFlag) {
+				int len;
+				u_char *msg;
+
+				if (!arg)
+					return(0);
+				msg = arg;
+				len = *msg;
+				msg++;
+				modem_write_cmd(cs, msg, len);
+			}
+			break;
+#endif
+	}
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		int pwr = bytein(cs->hw.elsa.ale);
+		if (pwr & 0x08)
+			cs->hw.elsa.status |= ELSA_BAD_PWR;
+		else
+			cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+	}
+	elsa_led_handler(cs);
+	return(ret);
+}
+
+static unsigned char
+probe_elsa_adr(unsigned int adr, int typ)
+{
+	int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0,
+	 pc_2 = 0, pfp_1 = 0, pfp_2 = 0;
+
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (typ != ISDN_CTYPE_ELSA_PCMCIA) {
+		if (request_region(adr, 8, "elsa card")) {
+			release_region(adr, 8);
+		} else {
+			printk(KERN_WARNING
+			       "Elsa: Probing Port 0x%x: already in use\n", adr);
+			return (0);
+		}
+	}
+	for (i = 0; i < 16; i++) {
+		in1 = inb(adr + ELSA_CONFIG);	/* 'toggelt' bei */
+		in2 = inb(adr + ELSA_CONFIG);	/* jedem Zugriff */
+		p16_1 += 0x04 & in1;
+		p16_2 += 0x04 & in2;
+		p8_1 += 0x02 & in1;
+		p8_2 += 0x02 & in2;
+		pc_1 += 0x01 & in1;
+		pc_2 += 0x01 & in2;
+		pfp_1 += 0x40 & in1;
+		pfp_2 += 0x40 & in2;
+	}
+	printk(KERN_INFO "Elsa: Probing IO 0x%x", adr);
+	if (65 == ++p16_1 * ++p16_2) {
+		printk(" PCC-16/PCF found\n");
+		return (ELSA_PCC16);
+	} else if (1025 == ++pfp_1 * ++pfp_2) {
+		printk(" PCF-Pro found\n");
+		return (ELSA_PCFPRO);
+	} else if (33 == ++p8_1 * ++p8_2) {
+		printk(" PCC8 found\n");
+		return (ELSA_PCC8);
+	} else if (17 == ++pc_1 * ++pc_2) {
+		printk(" PC found\n");
+		return (ELSA_PC);
+	} else {
+		printk(" failed\n");
+		return (0);
+	}
+}
+
+static unsigned int
+probe_elsa(struct IsdnCardState *cs)
+{
+	int i;
+	unsigned int CARD_portlist[] =
+	{0x160, 0x170, 0x260, 0x360, 0};
+
+	for (i = 0; CARD_portlist[i]; i++) {
+		if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ)))
+			break;
+	}
+	return (CARD_portlist[i]);
+}
+
+static 	struct pci_dev *dev_qs1000 __devinitdata = NULL;
+static 	struct pci_dev *dev_qs3000 __devinitdata = NULL;
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id elsa_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0133),
+	  ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0133), 
+	  (unsigned long) "Elsa QS1000" },
+	{ ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0134),
+	  ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0134), 
+	  (unsigned long) "Elsa QS3000" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &elsa_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_elsa(struct IsdnCard *card)
+{
+	int bytecnt;
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, Elsa_revision);
+	printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
+	cs->hw.elsa.ctrl_reg = 0;
+	cs->hw.elsa.status = 0;
+	cs->hw.elsa.MFlag = 0;
+	cs->subtyp = 0;
+	if (cs->typ == ISDN_CTYPE_ELSA) {
+		cs->hw.elsa.base = card->para[0];
+		printk(KERN_INFO "Elsa: Microlink IO probing\n");
+		if (cs->hw.elsa.base) {
+			if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base,
+							  cs->typ))) {
+				printk(KERN_WARNING
+				       "Elsa: no Elsa Microlink at %#lx\n",
+				       cs->hw.elsa.base);
+				return (0);
+			}
+		} else
+			cs->hw.elsa.base = probe_elsa(cs);
+		if (cs->hw.elsa.base) {
+			cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+			cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+			cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+			cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+			cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+			cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+			cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+			val = bytein(cs->hw.elsa.cfg);
+			if (cs->subtyp == ELSA_PC) {
+				const u_char CARD_IrqTab[8] =
+				{7, 3, 5, 9, 0, 0, 0, 0};
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2];
+			} else if (cs->subtyp == ELSA_PCC8) {
+				const u_char CARD_IrqTab[8] =
+				{7, 3, 5, 9, 0, 0, 0, 0};
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4];
+			} else {
+				const u_char CARD_IrqTab[8] =
+				{15, 10, 15, 3, 11, 5, 11, 9};
+				cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3];
+			}
+			val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE;
+			if (val < 3)
+				val |= 8;
+			val += 'A' - 3;
+			if (val == 'B' || val == 'C')
+				val ^= 1;
+			if ((cs->subtyp == ELSA_PCFPRO) && (val = 'G'))
+				val = 'C';
+			printk(KERN_INFO
+			       "Elsa: %s found at %#lx Rev.:%c IRQ %d\n",
+			       Elsa_Types[cs->subtyp],
+			       cs->hw.elsa.base,
+			       val, cs->irq);
+			val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD;
+			if (val) {
+				printk(KERN_WARNING
+				   "Elsa: Microlink S0 bus power bad\n");
+				cs->hw.elsa.status |= ELSA_BAD_PWR;
+			}
+		} else {
+			printk(KERN_WARNING
+			       "No Elsa Microlink found\n");
+			return (0);
+		}
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PNP) {
+#ifdef __ISAPNP__
+		if (!card->para[1] && isapnp_present()) {
+			struct pnp_dev *pnp_d;
+			while(ipid->card_vendor) {
+				if ((pnp_c = pnp_find_card(ipid->card_vendor,
+					ipid->card_device, pnp_c))) {
+					pnp_d = NULL;
+					if ((pnp_d = pnp_find_dev(pnp_c,
+						ipid->vendor, ipid->function, pnp_d))) {
+						int err;
+
+						printk(KERN_INFO "HiSax: %s detected\n",
+							(char *)ipid->driver_data);
+						pnp_disable_dev(pnp_d);
+						err = pnp_activate_dev(pnp_d);
+						if (err<0) {
+							printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+								__FUNCTION__, err);
+							return(0);
+						}
+						card->para[1] = pnp_port_start(pnp_d, 0);
+						card->para[0] = pnp_irq(pnp_d, 0);
+
+						if (!card->para[0] || !card->para[1]) {
+							printk(KERN_ERR "Elsa PnP:some resources are missing %ld/%lx\n",
+								card->para[0], card->para[1]);
+							pnp_disable_dev(pnp_d);
+							return(0);
+						}
+						if (ipid->function == ISAPNP_FUNCTION(0x133))
+							cs->subtyp = ELSA_QS1000;
+						else
+							cs->subtyp = ELSA_QS3000;
+						break;
+					} else {
+						printk(KERN_ERR "Elsa PnP: PnP error card found, no device\n");
+						return(0);
+					}
+				}
+				ipid++;
+				pnp_c=NULL;
+			} 
+			if (!ipid->card_vendor) {
+				printk(KERN_INFO "Elsa PnP: no ISAPnP card found\n");
+				return(0);
+			}
+		}
+#endif
+		if (card->para[1] && card->para[0]) { 
+			cs->hw.elsa.base = card->para[1];
+			cs->irq = card->para[0];
+			if (!cs->subtyp)
+				cs->subtyp = ELSA_QS1000;
+		} else {
+			printk(KERN_ERR "Elsa PnP: no parameter\n");
+		}
+		cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+		cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+		cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+		cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+		cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+		cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+		printk(KERN_INFO
+		       "Elsa: %s defined at %#lx IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA) {
+		cs->hw.elsa.base = card->para[1];
+		cs->irq = card->para[0];
+		val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID);
+		if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */
+			cs->subtyp = ELSA_PCMCIA_IPAC;
+			cs->hw.elsa.ale = cs->hw.elsa.base + 0;
+			cs->hw.elsa.isac = cs->hw.elsa.base + 2;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + 2;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		} else {
+			cs->subtyp = ELSA_PCMCIA;
+			cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM;
+			cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM;
+			cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+		}
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig = 0;
+		cs->hw.elsa.ctrl = 0;
+		cs->irq_flags |= SA_SHIRQ;
+		printk(KERN_INFO
+		       "Elsa: %s defined at %#lx IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->irq);
+	} else if (cs->typ == ISDN_CTYPE_ELSA_PCI) {
+#ifdef CONFIG_PCI
+		cs->subtyp = 0;
+		if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+			PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) {
+			if (pci_enable_device(dev_qs1000))
+				return(0);
+			cs->subtyp = ELSA_QS1000PCI;
+			cs->irq = dev_qs1000->irq;
+			cs->hw.elsa.cfg = pci_resource_start(dev_qs1000, 1);
+			cs->hw.elsa.base = pci_resource_start(dev_qs1000, 3);
+		} else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+			PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) {
+			if (pci_enable_device(dev_qs3000))
+				return(0);
+			cs->subtyp = ELSA_QS3000PCI;
+			cs->irq = dev_qs3000->irq;
+			cs->hw.elsa.cfg = pci_resource_start(dev_qs3000, 1);
+			cs->hw.elsa.base = pci_resource_start(dev_qs3000, 3);
+		} else {
+			printk(KERN_WARNING "Elsa: No PCI card found\n");
+			return(0);
+		}
+		if (!cs->irq) {
+			printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n");
+			return(0);
+		}
+
+		if (!(cs->hw.elsa.base && cs->hw.elsa.cfg)) {
+			printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n");
+			return(0);
+		}
+		if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) {
+			printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n");
+			printk(KERN_WARNING "Elsa: If your system hangs now, read\n");
+			printk(KERN_WARNING "Elsa: Documentation/isdn/README.HiSax\n");
+		}
+		cs->hw.elsa.ale  = cs->hw.elsa.base;
+		cs->hw.elsa.isac = cs->hw.elsa.base +1;
+		cs->hw.elsa.hscx = cs->hw.elsa.base +1; 
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->hw.elsa.timer = 0;
+		cs->hw.elsa.trig  = 0;
+		cs->irq_flags |= SA_SHIRQ;
+		printk(KERN_INFO
+		       "Elsa: %s defined at %#lx/0x%x IRQ %d\n",
+		       Elsa_Types[cs->subtyp],
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.cfg,
+		       cs->irq);
+#else
+		printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	} else 
+		return (0);
+
+	switch (cs->subtyp) {
+		case ELSA_PC:
+		case ELSA_PCC8:
+		case ELSA_PCC16:
+		case ELSA_QS1000:
+		case ELSA_PCMCIA:
+		case ELSA_PCMCIA_IPAC:
+			bytecnt = 8;
+			break;
+		case ELSA_PCFPRO:
+		case ELSA_PCF:
+		case ELSA_QS3000:
+		case ELSA_QS3000PCI:
+			bytecnt = 16;
+			break;
+		case ELSA_QS1000PCI:
+			bytecnt = 2;
+			break;
+		default:
+			printk(KERN_WARNING
+			       "Unknown ELSA subtype %d\n", cs->subtyp);
+			return (0);
+	}
+	/* In case of the elsa pcmcia card, this region is in use,
+	   reserved for us by the card manager. So we do not check it
+	   here, it would fail. */
+	if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && !request_region(cs->hw.elsa.base, bytecnt, "elsa isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %#lx-%#lx already in use\n",
+		       CardType[card->typ],
+		       cs->hw.elsa.base,
+		       cs->hw.elsa.base + bytecnt);
+		return (0);
+	}
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+		if (!request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci")) {
+			printk(KERN_WARNING
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.elsa.cfg,
+				cs->hw.elsa.cfg + 0x80);
+			release_region(cs->hw.elsa.base, bytecnt);
+			return (0);
+		}
+	}
+#if ARCOFI_USE
+	init_arcofi(cs);
+#endif
+	setup_isac(cs);
+	cs->hw.elsa.tl.function = (void *) elsa_led_handler;
+	cs->hw.elsa.tl.data = (long) cs;
+	init_timer(&cs->hw.elsa.tl);
+	/* Teste Timer */
+	if (cs->hw.elsa.timer) {
+		byteout(cs->hw.elsa.trig, 0xff);
+		byteout(cs->hw.elsa.timer, 0);
+		if (!TimerRun(cs)) {
+			byteout(cs->hw.elsa.timer, 0);	/* 2. Versuch */
+			if (!TimerRun(cs)) {
+				printk(KERN_WARNING
+				       "Elsa: timer do not start\n");
+				release_io_elsa(cs);
+				return (0);
+			}
+		}
+		HZDELAY((HZ/100) + 1);	/* wait >=10 ms */
+		if (TimerRun(cs)) {
+			printk(KERN_WARNING "Elsa: timer do not run down\n");
+			release_io_elsa(cs);
+			return (0);
+		}
+		printk(KERN_INFO "Elsa: timer OK; resetting card\n");
+	}
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Elsa_card_msg;
+	if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) {
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &elsa_interrupt_ipac;
+		val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID);
+		printk(KERN_INFO "Elsa: IPAC version %x\n", val);
+	} else {
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		cs->irq_func = &elsa_interrupt;
+		ISACVersion(cs, "Elsa:");
+		if (HscxVersion(cs, "Elsa:")) {
+			printk(KERN_WARNING
+				"Elsa: wrong HSCX versions check IO address\n");
+			release_io_elsa(cs);
+			return (0);
+		}
+	}
+	if (cs->subtyp == ELSA_PC) {
+		val = readitac(cs, ITAC_SYS);
+		printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]);
+		writeitac(cs, ITAC_ISEN, 0);
+		writeitac(cs, ITAC_RFIE, 0);
+		writeitac(cs, ITAC_XFIE, 0);
+		writeitac(cs, ITAC_SCIE, 0);
+		writeitac(cs, ITAC_STIE, 0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
new file mode 100644
index 0000000..bfc0132
--- /dev/null
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -0,0 +1,532 @@
+/*======================================================================
+
+    An elsa_cs PCMCIA client driver
+
+    This driver is for the Elsa PCM ISDN Cards, i.e. the MicroLink
+
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Modifications from dummy_cs.c are Copyright (C) 1999-2001 Klaus
+    Lichtenwalder <Lichtenwalder@ACM.org>. All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in
+    which case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Elsa PCM cards");
+MODULE_AUTHOR("Klaus Lichtenwalder");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"elsa_cs.c $Revision: 1.2.2.4 $ $Date: 2004/01/25 15:07:06 $ (K.Lichtenwalder)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2;        /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the elsa_cs event
+   handler.
+*/
+
+static void elsa_cs_config(dev_link_t *link);
+static void elsa_cs_release(dev_link_t *link);
+static int elsa_cs_event(event_t event, int priority,
+                          event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *elsa_cs_attach(void);
+static void elsa_cs_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "elsa_cs";
+
+/*
+   A linked list of "instances" of the elsa_cs device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   To simplify the data structure handling, we actually include the
+   dev_link_t structure in the device's private data structure.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally shouldn't be allocated dynamically.
+   In this case, we also provide a flag to indicate if a device is
+   "stopped" due to a power management event, or card ejection.  The
+   device IO routines can use a flag like this to throttle IO to a
+   card that is not ready to accept it.
+*/
+
+typedef struct local_info_t {
+    dev_link_t          link;
+    dev_node_t          node;
+    int                 busy;
+    int			cardnr;
+} local_info_t;
+
+/*======================================================================
+
+    elsa_cs_attach() creates an "instance" of the driver, allocatingx
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+
+======================================================================*/
+
+static dev_link_t *elsa_cs_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret;
+
+    DEBUG(0, "elsa_cs_attach()\n");
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) return NULL;
+    memset(local, 0, sizeof(local_info_t));
+    local->cardnr = -1;
+    link = &local->link; link->priv = local;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
+    link->irq.Handler = NULL;
+
+    /*
+      General socket configuration defaults can go here.  In this
+      client, we assume very little, and rely on the CIS for almost
+      everything.  In most clients, many details (i.e., number, sizes,
+      and attributes of IO windows) are fixed by the nature of the
+      device, and can be hard-wired here.
+    */
+    link->io.NumPorts1 = 8;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 3;
+
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+        CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+        CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+        CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &elsa_cs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+        cs_error(link->handle, RegisterClient, ret);
+        elsa_cs_detach(link);
+        return NULL;
+    }
+
+    return link;
+} /* elsa_cs_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void elsa_cs_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    local_info_t *info = link->priv;
+    int ret;
+
+    DEBUG(0, "elsa_cs_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+        if (*linkp == link) break;
+    if (*linkp == NULL)
+        return;
+
+    if (link->state & DEV_CONFIG)
+        elsa_cs_release(link);
+
+    /* Break the link with Card Services */
+    if (link->handle) {
+        ret = pcmcia_deregister_client(link->handle);
+	if (ret != CS_SUCCESS)
+	    cs_error(link->handle, DeregisterClient, ret);
+    }
+
+    /* Unlink device structure and free it */
+    *linkp = link->next;
+    kfree(info);
+
+} /* elsa_cs_detach */
+
+/*======================================================================
+
+    elsa_cs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    device available to the system.
+
+======================================================================*/
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_tuple_data(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_first_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_next_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static void elsa_cs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    local_info_t *dev;
+    int i, j, last_fn;
+    u_short buf[128];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    IsdnCard_t icard;
+
+    DEBUG(0, "elsa_config(0x%p)\n", link);
+    handle = link->handle;
+    dev = link->priv;
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = 255;
+    tuple.TupleOffset = 0;
+    tuple.Attributes = 0;
+    i = first_tuple(handle, &tuple, &parse);
+    if (i != CS_SUCCESS) {
+        last_fn = ParseTuple;
+	goto cs_failed;
+    }
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+    tuple.Attributes = 0;
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    i = first_tuple(handle, &tuple, &parse);
+    while (i == CS_SUCCESS) {
+        if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
+            printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
+            link->conf.ConfigIndex = cf->index;
+            link->io.BasePort1 = cf->io.win[0].base;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+        } else {
+          printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
+          link->conf.ConfigIndex = cf->index;
+          for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
+            link->io.BasePort1 = j;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+          }
+          break;
+        }
+        i = next_tuple(handle, &tuple, &parse);
+    }
+
+    if (i != CS_SUCCESS) {
+	last_fn = RequestIO;
+	goto cs_failed;
+    }
+
+    i = pcmcia_request_irq(link->handle, &link->irq);
+    if (i != CS_SUCCESS) {
+        link->irq.AssignedIRQ = 0;
+	last_fn = RequestIRQ;
+        goto cs_failed;
+    }
+
+    i = pcmcia_request_configuration(link->handle, &link->conf);
+    if (i != CS_SUCCESS) {
+      last_fn = RequestConfiguration;
+      goto cs_failed;
+    }
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. *//*  */
+    sprintf(dev->node.dev_name, "elsa");
+    dev->node.major = dev->node.minor = 0x0;
+
+    link->dev = &dev->node;
+
+    /* Finally, report what we've done */
+    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+           dev->node.dev_name, link->conf.ConfigIndex,
+           link->conf.Vcc/10, link->conf.Vcc%10);
+    if (link->conf.Vpp1)
+        printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+        printk(", irq %d", link->irq.AssignedIRQ);
+    if (link->io.NumPorts1)
+        printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+               link->io.BasePort1+link->io.NumPorts1-1);
+    if (link->io.NumPorts2)
+        printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+               link->io.BasePort2+link->io.NumPorts2-1);
+    printk("\n");
+
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = protocol;
+    icard.typ = ISDN_CTYPE_ELSA_PCMCIA;
+    
+    i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
+    if (i < 0) {
+    	printk(KERN_ERR "elsa_cs: failed to initialize Elsa PCMCIA %d at i/o %#x\n",
+    		i, link->io.BasePort1);
+    	elsa_cs_release(link);
+    } else
+    	((local_info_t*)link->priv)->cardnr = i;
+
+    return;
+cs_failed:
+    cs_error(link->handle, last_fn, i);
+    elsa_cs_release(link);
+} /* elsa_cs_config */
+
+/*======================================================================
+
+    After a card is removed, elsa_cs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void elsa_cs_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+
+    DEBUG(0, "elsa_cs_release(0x%p)\n", link);
+
+    if (local) {
+    	if (local->cardnr >= 0) {
+    	    /* no unregister function with hisax */
+	    HiSax_closecard(local->cardnr);
+	}
+    }
+    /* Unlink the device chain */
+    link->dev = NULL;
+
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win)
+        pcmcia_release_window(link->win);
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+} /* elsa_cs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+
+======================================================================*/
+
+static int elsa_cs_event(event_t event, int priority,
+                          event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    local_info_t *dev = link->priv;
+
+    DEBUG(1, "elsa_cs_event(%d)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+        link->state &= ~DEV_PRESENT;
+        if (link->state & DEV_CONFIG) {
+            ((local_info_t*)link->priv)->busy = 1;
+	    elsa_cs_release(link);
+        }
+        break;
+    case CS_EVENT_CARD_INSERTION:
+        link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+        elsa_cs_config(link);
+        break;
+    case CS_EVENT_PM_SUSPEND:
+        link->state |= DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+        /* Mark the device as stopped, to block IO until later */
+        dev->busy = 1;
+        if (link->state & DEV_CONFIG)
+            pcmcia_release_configuration(link->handle);
+        break;
+    case CS_EVENT_PM_RESUME:
+        link->state &= ~DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+        if (link->state & DEV_CONFIG)
+            pcmcia_request_configuration(link->handle, &link->conf);
+        dev->busy = 0;
+        break;
+    }
+    return 0;
+} /* elsa_cs_event */
+
+static struct pcmcia_driver elsa_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "elsa_cs",
+	},
+	.attach		= elsa_cs_attach,
+	.detach		= elsa_cs_detach,
+};
+
+static int __init init_elsa_cs(void)
+{
+	return pcmcia_register_driver(&elsa_cs_driver);
+}
+
+static void __exit exit_elsa_cs(void)
+{
+	pcmcia_unregister_driver(&elsa_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_elsa_cs);
+module_exit(exit_elsa_cs);
diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c
new file mode 100644
index 0000000..689c833
--- /dev/null
+++ b/drivers/isdn/hisax/elsa_ser.c
@@ -0,0 +1,657 @@
+/* $Id: elsa_ser.c,v 2.14.2.3 2004/02/11 13:21:33 keil Exp $
+ *
+ * stuff for the serial modem on ELSA cards
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+#define MAX_MODEM_BUF	256
+#define WAKEUP_CHARS	(MAX_MODEM_BUF/2)
+#define RS_ISR_PASS_LIMIT 256
+#define BASE_BAUD ( 1843200 / 16 )
+
+//#define SERIAL_DEBUG_OPEN 1
+//#define SERIAL_DEBUG_INTR 1
+//#define SERIAL_DEBUG_FLOW 1
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_REG
+//#define SERIAL_DEBUG_REG 1
+
+#ifdef SERIAL_DEBUG_REG
+static u_char deb[32];
+const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"};
+const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"};
+#endif
+
+static char *MInit_1 = "AT&F&C1E0&D2\r\0";
+static char *MInit_2 = "ATL2M1S64=13\r\0";
+static char *MInit_3 = "AT+FCLASS=0\r\0";
+static char *MInit_4 = "ATV1S2=128X1\r\0";
+static char *MInit_5 = "AT\\V8\\N3\r\0";
+static char *MInit_6 = "ATL0M0&G0%E1\r\0";
+static char *MInit_7 = "AT%L1%M0%C3\r\0";
+
+static char *MInit_speed28800 = "AT%G0%B28800\r\0";
+
+static char *MInit_dialout = "ATs7=60 x1 d\r\0";
+static char *MInit_dialin = "ATs7=60 x1 a\r\0";
+
+
+static inline unsigned int serial_in(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"in   %s %02x",ModemIn[offset], val);
+	return(val);
+#else
+	return inb(cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	u_int val = inb(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inp  %s %02x",ModemIn[offset], val);
+#else
+	u_int val = inb_p(cs->hw.elsa.base + 8 + offset);
+	debugl1(cs,"inP  %s %02x",ModemIn[offset], val);
+#endif
+	return(val);
+#else
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	return inb(cs->hw.elsa.base + 8 + offset);
+#else
+	return inb_p(cs->hw.elsa.base + 8 + offset);
+#endif
+#endif
+}
+
+static inline void serial_out(struct IsdnCardState *cs, int offset, int value)
+{
+#ifdef SERIAL_DEBUG_REG
+	debugl1(cs,"out  %s %02x",ModemOut[offset], value);
+#endif
+	outb(value, cs->hw.elsa.base + 8 + offset);
+}
+
+static inline void serial_outp(struct IsdnCardState *cs, int offset,
+			       int value)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	debugl1(cs,"outp %s %02x",ModemOut[offset], value);
+#else
+	debugl1(cs,"outP %s %02x",ModemOut[offset], value);
+#endif
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+	outb(value, cs->hw.elsa.base + 8 + offset);
+#else
+    	outb_p(value, cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct IsdnCardState *cs, int baud)
+{
+	int	quot = 0, baud_base;
+	unsigned cval, fcr = 0;
+	int	bits;
+
+
+	/* byte size and parity */
+	cval = 0x03; bits = 10;
+	/* Determine divisor based on baud rate */
+	baud_base = BASE_BAUD;
+	quot = baud_base / baud;
+	/* If the quotient is ever zero, default to 9600 bps */
+	if (!quot)
+		quot = baud_base / 9600;
+
+	/* Set up FIFO's */
+	if ((baud_base / quot) < 2400)
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+	else
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+	serial_outp(cs, UART_FCR, fcr);
+	/* CTS flow control flag and modem status interrupts */
+	cs->hw.elsa.IER &= ~UART_IER_MSI;
+	cs->hw.elsa.IER |= UART_IER_MSI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+
+	debugl1(cs,"modem quot=0x%x", quot);
+	serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+	serial_outp(cs, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_outp(cs, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_outp(cs, UART_LCR, cval);		/* reset DLAB */
+	serial_inp(cs, UART_RX);
+}
+
+static int mstartup(struct IsdnCardState *cs)
+{
+	int	retval=0;
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+
+	/*
+	 * At this point there's no way the LSR could still be 0xFF;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (serial_inp(cs, UART_LSR) == 0xff) {
+		retval = -ENODEV;
+		goto errout;
+	}
+	
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_inp(cs, UART_RX);
+	(void) serial_inp(cs, UART_IIR);
+	(void) serial_inp(cs, UART_MSR);
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	serial_outp(cs, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
+
+	cs->hw.elsa.MCR = 0;
+	cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+	serial_outp(cs, UART_IER, cs->hw.elsa.IER);	/* enable interrupts */
+	
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void)serial_inp(cs, UART_LSR);
+	(void)serial_inp(cs, UART_RX);
+	(void)serial_inp(cs, UART_IIR);
+	(void)serial_inp(cs, UART_MSR);
+
+	cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0;
+	cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0;
+
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(cs, BASE_BAUD);
+	cs->hw.elsa.MFlag = 1;
+errout:
+	return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void mshutdown(struct IsdnCardState *cs)
+{
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk(KERN_DEBUG"Shutting down serial ....");
+#endif
+	
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+
+	cs->hw.elsa.IER = 0;
+	serial_outp(cs, UART_IER, 0x00);	/* disable all intrs */
+	cs->hw.elsa.MCR &= ~UART_MCR_OUT2;
+	
+	/* disable break condition */
+	serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC);
+	
+	cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+	serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+	/* disable FIFO's */	
+	serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+	serial_inp(cs, UART_RX);    /* read data port to reset things */
+	
+#ifdef SERIAL_DEBUG_OPEN
+	printk(" done\n");
+#endif
+}
+
+inline int
+write_modem(struct BCState *bcs) {
+	int ret=0;
+	struct IsdnCardState *cs = bcs->cs;
+	int count, len, fp;
+	
+	if (!bcs->tx_skb)
+		return 0;
+	if (bcs->tx_skb->len <= 0)
+		return 0;
+	len = bcs->tx_skb->len;
+	if (len > MAX_MODEM_BUF - cs->hw.elsa.transcnt)
+		len = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = len;
+	if (count > MAX_MODEM_BUF - fp) {
+		count = MAX_MODEM_BUF - fp;
+		memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+		skb_pull(bcs->tx_skb, count);
+		cs->hw.elsa.transcnt += count;
+		ret = count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+	skb_pull(bcs->tx_skb, count);
+	cs->hw.elsa.transcnt += count;
+	ret += count;
+	
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+			cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+	return(ret);
+}
+
+inline void
+modem_fill(struct BCState *bcs) {
+		
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			write_modem(bcs);
+			return;
+		} else {
+			if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+				u_long	flags;
+				spin_lock_irqsave(&bcs->aclock, flags);
+				bcs->ackcnt += bcs->hw.hscx.count;
+				spin_unlock_irqrestore(&bcs->aclock, flags);
+				schedule_event(bcs, B_ACKPENDING);
+			}
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.hscx.count = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		write_modem(bcs);
+	} else {
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		schedule_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+static inline void receive_chars(struct IsdnCardState *cs,
+				 int *status)
+{
+	unsigned char ch;
+	struct sk_buff *skb;
+
+	do {
+		ch = serial_in(cs, UART_RX);
+		if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF)
+			break;
+		cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch;
+#ifdef SERIAL_DEBUG_INTR
+		printk("DR%02x:%02x...", ch, *status);
+#endif
+		if (*status & (UART_LSR_BI | UART_LSR_PE |
+			       UART_LSR_FE | UART_LSR_OE)) {
+					
+#ifdef SERIAL_DEBUG_INTR
+			printk("handling exept....");
+#endif
+		}
+		*status = serial_inp(cs, UART_LSR);
+	} while (*status & UART_LSR_DR);
+	if (cs->hw.elsa.MFlag == 2) {
+		if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt)))
+			printk(KERN_WARNING "ElsaSER: receive out of memory\n");
+		else {
+			memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf, 
+				cs->hw.elsa.rcvcnt);
+			skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb);
+		}
+		schedule_event(cs->hw.elsa.bcs, B_RCVBUFREADY);
+	} else {
+		char tmp[128];
+		char *t = tmp;
+
+		t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt);
+		QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt);
+		debugl1(cs, tmp);
+	}
+	cs->hw.elsa.rcvcnt = 0;
+}
+
+static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done)
+{
+	int count;
+	
+	debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp, 
+		cs->hw.elsa.transcnt);
+	
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_out(cs, UART_IER, cs->hw.elsa.IER);
+		return;
+	}
+	count = 16;
+	do {
+		serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]);
+		if (cs->hw.elsa.transp >= MAX_MODEM_BUF)
+			cs->hw.elsa.transp=0;
+		if (--cs->hw.elsa.transcnt <= 0)
+			break;
+	} while (--count > 0);
+	if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2))
+		modem_fill(cs->hw.elsa.bcs);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+	if (intr_done)
+		*intr_done = 0;
+	if (cs->hw.elsa.transcnt <= 0) {
+		cs->hw.elsa.IER &= ~UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+}
+
+
+static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs)
+{
+	int status, iir, msr;
+	int pass_counter = 0;
+	
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+	do {
+		status = serial_inp(cs, UART_LSR);
+		debugl1(cs,"rs LSR %02x", status);
+#ifdef SERIAL_DEBUG_INTR
+		printk("status = %x...", status);
+#endif
+		if (status & UART_LSR_DR)
+			receive_chars(cs, &status);
+		if (status & UART_LSR_THRE)
+			transmit_chars(cs, NULL);
+		if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+			printk("rs_single loop break.\n");
+			break;
+		}
+		iir = serial_inp(cs, UART_IIR);
+		debugl1(cs,"rs IIR %02x", iir);
+		if ((iir & 0xf) == 0) {
+			msr = serial_inp(cs, UART_MSR);
+			debugl1(cs,"rs MSR %02x", msr);
+		}
+	} while (!(iir & UART_IIR_NO_INT));
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void hscx_l2l1(struct PStack *st, int pr, void *arg);
+
+void
+close_elsastate(struct BCState *bcs)
+{
+	modehscx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			if (bcs->mode != L1_MODE_MODEM)
+				kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+void
+modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
+	int count, fp;
+	u_char *msg = buf;
+	
+	if (!len)
+		return;
+	if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
+		return;
+	}
+	fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+	fp &= (MAX_MODEM_BUF -1);
+	count = len;
+	if (count > MAX_MODEM_BUF - fp) {
+		count = MAX_MODEM_BUF - fp;
+		memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+		cs->hw.elsa.transcnt += count;
+		msg += count;
+		count = len - count;
+		fp = 0;
+	}
+	memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+	cs->hw.elsa.transcnt += count;
+	if (cs->hw.elsa.transcnt && 
+	    !(cs->hw.elsa.IER & UART_IER_THRI)) {
+		cs->hw.elsa.IER |= UART_IER_THRI;
+		serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+	}
+}
+
+void
+modem_set_init(struct IsdnCardState *cs) {
+	int timeout;
+
+#define RCV_DELAY 20000	
+	modem_write_cmd(cs, MInit_1, strlen(MInit_1));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_2, strlen(MInit_2));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_3, strlen(MInit_3));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_4, strlen(MInit_4));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY );
+	modem_write_cmd(cs, MInit_5, strlen(MInit_5));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_6, strlen(MInit_6));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	modem_write_cmd(cs, MInit_7, strlen(MInit_7));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+}
+
+void
+modem_set_dial(struct IsdnCardState *cs, int outgoing) {
+	int timeout;
+#define RCV_DELAY 20000	
+
+	modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+	if (outgoing)
+		modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout));
+	else
+		modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin));
+	timeout = 1000;
+	while(timeout-- && cs->hw.elsa.transcnt)
+		udelay(1000);
+	debugl1(cs, "msi tout=%d", timeout);
+	udelay(RCV_DELAY);
+}
+
+void
+modem_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	if (pr == (PH_DATA | REQUEST)) {
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		if (bcs->tx_skb) {
+			skb_queue_tail(&bcs->squeue, skb);
+		} else {
+			bcs->tx_skb = skb;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.hscx.count = 0;
+			write_modem(bcs);
+		}
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+	} else if (pr == (PH_ACTIVATE | REQUEST)) {
+		test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+		st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+		set_arcofi(bcs->cs, st->l1.bc);
+		mstartup(bcs->cs);
+		modem_set_dial(bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
+		bcs->cs->hw.elsa.MFlag=2;
+	} else if (pr == (PH_DEACTIVATE | REQUEST)) {
+		test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+		bcs->cs->dc.isac.arcofi_bc = st->l1.bc;
+		arcofi_fsm(bcs->cs, ARCOFI_START, &ARCOFI_XOP_0);
+		interruptible_sleep_on(&bcs->cs->dc.isac.arcofi_wait);
+		bcs->cs->hw.elsa.MFlag=1;
+	} else {
+		printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr);
+	}
+}
+
+int
+setstack_elsa(struct PStack *st, struct BCState *bcs)
+{
+
+	bcs->channel = st->l1.bc;
+	switch (st->l1.mode) {
+		case L1_MODE_HDLC:
+		case L1_MODE_TRANS:
+			if (open_hscxstate(st->l1.hardware, bcs))
+				return (-1);
+			st->l2.l2l1 = hscx_l2l1;
+			break;
+		case L1_MODE_MODEM:
+			bcs->mode = L1_MODE_MODEM;
+			if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+				bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf;
+				skb_queue_head_init(&bcs->rqueue);
+				skb_queue_head_init(&bcs->squeue);
+			}
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->event = 0;
+			bcs->hw.hscx.rcvidx = 0;
+			bcs->tx_cnt = 0;
+			bcs->cs->hw.elsa.bcs = bcs;
+			st->l2.l2l1 = modem_l2l1;
+			break;
+	}
+	st->l1.bcs = bcs;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void
+init_modem(struct IsdnCardState *cs) {
+
+	cs->bcs[0].BC_SetStack = setstack_elsa;
+	cs->bcs[1].BC_SetStack = setstack_elsa;
+	cs->bcs[0].BC_Close = close_elsastate;
+	cs->bcs[1].BC_Close = close_elsastate;
+	if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.rcvbuf\n");
+		return;
+	}
+	if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF,
+		GFP_ATOMIC))) {
+		printk(KERN_WARNING
+			"Elsa: No modem mem hw.elsa.transbuf\n");
+		kfree(cs->hw.elsa.rcvbuf);
+		cs->hw.elsa.rcvbuf = NULL;
+		return;
+	}
+	if (mstartup(cs)) {
+		printk(KERN_WARNING "Elsa: problem startup modem\n");
+	}
+	modem_set_init(cs);
+}
+
+void
+release_modem(struct IsdnCardState *cs) {
+
+	cs->hw.elsa.MFlag = 0;
+	if (cs->hw.elsa.transbuf) {
+		if (cs->hw.elsa.rcvbuf) {
+			mshutdown(cs);
+			kfree(cs->hw.elsa.rcvbuf);
+			cs->hw.elsa.rcvbuf = NULL;
+		}
+		kfree(cs->hw.elsa.transbuf);
+		cs->hw.elsa.transbuf = NULL;
+	}
+}
diff --git a/drivers/isdn/hisax/enternow.h b/drivers/isdn/hisax/enternow.h
new file mode 100644
index 0000000..ed2eec5
--- /dev/null
+++ b/drivers/isdn/hisax/enternow.h
@@ -0,0 +1,51 @@
+/* 2001/10/02
+ *
+ * enternow.h   Header-file included by
+ *              enternow_pci.c
+ *
+ * Author       Christoph Ersfeld <info@formula-n.de>
+ *              Formula-n Europe AG (www.formula-n.com)
+ *              previously Gerdes AG
+ *
+ *
+ *              This file is (c) under GNU PUBLIC LICENSE
+ */
+
+
+/* ***************************************************************************************** *
+ * ****************************** datatypes and macros ************************************* *
+ * ***************************************************************************************** */
+
+#define BYTE							unsigned char
+#define WORD							unsigned int
+#define HIBYTE(w)						((unsigned char)((w & 0xff00) / 256))
+#define LOBYTE(w)						((unsigned char)(w & 0x00ff))
+#define InByte(addr)						inb(addr)
+#define OutByte(addr,val)					outb(val,addr)
+
+
+
+/* ***************************************************************************************** *
+ * *********************************** card-specific *************************************** *
+ * ***************************************************************************************** */
+
+/* für PowerISDN PCI */
+#define TJ_AMD_IRQ 						0x20
+#define TJ_LED1 						0x40
+#define TJ_LED2 						0x80
+
+
+/* Das Fenster zum AMD...
+ * Ab Adresse hw.njet.base + TJ_AMD_PORT werden vom AMD jeweils 8 Bit in
+ * den TigerJet i/o-Raum gemappt
+ * -> 0x01 des AMD bei hw.njet.base + 0C4 */
+#define TJ_AMD_PORT						0xC0
+
+
+
+/* ***************************************************************************************** *
+ * *************************************** Prototypen ************************************** *
+ * ***************************************************************************************** */
+
+BYTE ReadByteAmd7930(struct IsdnCardState *cs, BYTE offset);
+void WriteByteAmd7930(struct IsdnCardState *cs, BYTE offset, BYTE value);
diff --git a/drivers/isdn/hisax/enternow_pci.c b/drivers/isdn/hisax/enternow_pci.c
new file mode 100644
index 0000000..1cc4d11
--- /dev/null
+++ b/drivers/isdn/hisax/enternow_pci.c
@@ -0,0 +1,399 @@
+/* enternow_pci.c,v 0.99 2001/10/02
+ *
+ * enternow_pci.c       Card-specific routines for
+ *                      Formula-n enter:now ISDN PCI ab
+ *                      Gerdes AG Power ISDN PCI
+ *                      Woerltronic SA 16 PCI
+ *                      (based on HiSax driver by Karsten Keil)
+ *
+ * Author               Christoph Ersfeld <info@formula-n.de>
+ *                      Formula-n Europe AG (www.formula-n.com)
+ *                      previously Gerdes AG
+ *
+ *
+ *                      This file is (c) under GNU PUBLIC LICENSE
+ *
+ * Notes:
+ * This driver interfaces to netjet.c which performs B-channel
+ * processing.
+ *
+ * Version 0.99 is the first release of this driver and there are
+ * certainly a few bugs.
+ * It isn't testet on linux 2.4 yet, so consider this code to be
+ * beta.
+ *
+ * Please don't report me any malfunction without sending
+ * (compressed) debug-logs.
+ * It would be nearly impossible to retrace it.
+ *
+ * Log D-channel-processing as follows:
+ *
+ * 1. Load hisax with card-specific parameters, this example ist for
+ *    Formula-n enter:now ISDN PCI and compatible
+ *    (f.e. Gerdes Power ISDN PCI)
+ *
+ *    modprobe hisax type=41 protocol=2 id=gerdes
+ *
+ *    if you chose an other value for id, you need to modify the
+ *    code below, too.
+ *
+ * 2. set debug-level
+ *
+ *    hisaxctrl gerdes 1 0x3ff
+ *    hisaxctrl gerdes 11 0x4f
+ *    cat /dev/isdnctrl >> ~/log &
+ *
+ * Please take also a look into /var/log/messages if there is
+ * anything importand concerning HISAX.
+ *
+ *
+ * Credits:
+ * Programming the driver for Formula-n enter:now ISDN PCI and
+ * necessary the driver for the used Amd 7930 D-channel-controller
+ * was spnsored by Formula-n Europe AG.
+ * Thanks to Karsten Keil and Petr Novak, who gave me support in
+ * Hisax-specific questions.
+ * I want so say special thanks to Carl-Friedrich Braun, who had to
+ * answer a lot of questions about generally ISDN and about handling
+ * of the Amd-Chip.
+ *
+ */
+
+
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include "amd7930_fn.h"
+#include "enternow.h"
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "netjet.h"
+
+
+
+const char *enternow_pci_rev = "$Revision: 1.1.4.5 $";
+
+
+/* *************************** I/O-Interface functions ************************************* */
+
+
+/* cs->readisac, macro rByteAMD */
+BYTE
+ReadByteAmd7930(struct IsdnCardState *cs, BYTE offset)
+{
+	/* direktes Register */
+	if(offset < 8)
+		return (InByte(cs->hw.njet.isac + 4*offset));
+
+	/* indirektes Register */
+	else {
+		OutByte(cs->hw.njet.isac + 4*AMD_CR, offset);
+		return(InByte(cs->hw.njet.isac + 4*AMD_DR));
+	}
+}
+
+/* cs->writeisac, macro wByteAMD */
+void
+WriteByteAmd7930(struct IsdnCardState *cs, BYTE offset, BYTE value)
+{
+	/* direktes Register */
+	if(offset < 8)
+		OutByte(cs->hw.njet.isac + 4*offset, value);
+
+	/* indirektes Register */
+	else {
+		OutByte(cs->hw.njet.isac + 4*AMD_CR, offset);
+		OutByte(cs->hw.njet.isac + 4*AMD_DR, value);
+	}
+}
+
+
+void
+enpci_setIrqMask(struct IsdnCardState *cs, BYTE val) {
+        if (!val)
+	        OutByte(cs->hw.njet.base+NETJET_IRQMASK1, 0x00);
+        else
+	        OutByte(cs->hw.njet.base+NETJET_IRQMASK1, TJ_AMD_IRQ);
+}
+
+
+static BYTE dummyrr(struct IsdnCardState *cs, int chan, BYTE off)
+{
+        return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, BYTE off, BYTE value)
+{
+
+}
+
+
+/* ******************************************************************************** */
+
+
+static void
+reset_enpci(struct IsdnCardState *cs)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "enter:now PCI: reset");
+
+	/* Reset on, (also for AMD) */
+	cs->hw.njet.ctrl_reg = 0x07;
+	OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(20);
+	/* Reset off */
+	cs->hw.njet.ctrl_reg = 0x30;
+	OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	/* 20ms delay */
+	mdelay(20);
+	cs->hw.njet.auxd = 0;  // LED-status
+	cs->hw.njet.dmactrl = 0;
+	OutByte(cs->hw.njet.base + NETJET_AUXCTRL, ~TJ_AMD_IRQ);
+	OutByte(cs->hw.njet.base + NETJET_IRQMASK1, TJ_AMD_IRQ);
+	OutByte(cs->hw.njet.auxa, cs->hw.njet.auxd); // LED off
+}
+
+
+static int
+enpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+        BYTE *chan;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "enter:now PCI: card_msg: 0x%04X", mt);
+
+        switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_enpci(cs);
+                        Amd7930_init(cs);
+                        spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			break;
+		case CARD_INIT:
+			reset_enpci(cs);
+			inittiger(cs);
+			/* irq must be on here */
+			Amd7930_init(cs);
+			break;
+		case CARD_TEST:
+			break;
+                case MDL_ASSIGN:
+                        /* TEI assigned, LED1 on */
+                        cs->hw.njet.auxd = TJ_AMD_IRQ << 1;
+                        OutByte(cs->hw.njet.base + NETJET_AUXDATA, cs->hw.njet.auxd);
+                        break;
+                case MDL_REMOVE:
+                        /* TEI removed, LEDs off */
+	                cs->hw.njet.auxd = 0;
+                        OutByte(cs->hw.njet.base + NETJET_AUXDATA, 0x00);
+                        break;
+                case MDL_BC_ASSIGN:
+                        /* activate B-channel */
+                        chan = (BYTE *)arg;
+
+                        if (cs->debug & L1_DEB_ISAC)
+		                debugl1(cs, "enter:now PCI: assign phys. BC %d in AMD LMR1", *chan);
+
+                        cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 | (*chan + 1)), "MDL_BC_ASSIGN");
+                        /* at least one b-channel in use, LED 2 on */
+                        cs->hw.njet.auxd |= TJ_AMD_IRQ << 2;
+                        OutByte(cs->hw.njet.base + NETJET_AUXDATA, cs->hw.njet.auxd);
+                        break;
+                case MDL_BC_RELEASE:
+                        /* deactivate B-channel */
+                        chan = (BYTE *)arg;
+
+                        if (cs->debug & L1_DEB_ISAC)
+		                debugl1(cs, "enter:now PCI: release phys. BC %d in Amd LMR1", *chan);
+
+                        cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 & ~(*chan + 1)), "MDL_BC_RELEASE");
+                        /* no b-channel active -> LED2 off */
+                        if (!(cs->dc.amd7930.lmr1 & 3)) {
+                                cs->hw.njet.auxd &= ~(TJ_AMD_IRQ << 2);
+                                OutByte(cs->hw.njet.base + NETJET_AUXDATA, cs->hw.njet.auxd);
+                        }
+                        break;
+                default:
+                        break;
+
+	}
+	return(0);
+}
+
+static irqreturn_t
+enpci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	BYTE s0val, s1val, ir;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	s1val = InByte(cs->hw.njet.base + NETJET_IRQSTAT1);
+
+        /* AMD threw an interrupt */
+	if (!(s1val & TJ_AMD_IRQ)) {
+                /* read and clear interrupt-register */
+		ir = ReadByteAmd7930(cs, 0x00);
+		Amd7930_interrupt(cs, ir);
+		s1val = 1;
+	} else
+		s1val = 0;
+	s0val = InByte(cs->hw.njet.base + NETJET_IRQSTAT0);
+	if ((s0val | s1val)==0) { // shared IRQ
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	} 
+	if (s0val)
+		OutByte(cs->hw.njet.base + NETJET_IRQSTAT0, s0val);
+
+	/* DMA-Interrupt: B-channel-stuff */
+	/* set bits in sval to indicate which page is free */
+	if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+		/* the 2nd write page is free */
+		s0val = 0x08;
+	else	/* the 1st write page is free */
+		s0val = 0x04;
+	if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+		/* the 2nd read page is free */
+		s0val = s0val | 0x02;
+	else	/* the 1st read page is free */
+		s0val = s0val | 0x01;
+	if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+	{
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		cs->hw.njet.irqstat0 = s0val;
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+			/* we have a read dma int */
+			read_tiger(cs);
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+			/* we have a write dma int */
+			write_tiger(cs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+
+static struct pci_dev *dev_netjet __initdata = NULL;
+
+/* called by config.c */
+int __init
+setup_enternow_pci(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+#ifdef CONFIG_PCI
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+        strcpy(tmp, enternow_pci_rev);
+	printk(KERN_INFO "HiSax: Formula-n Europe AG enter:now ISDN PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_ENTERNOW)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+	for ( ;; )
+	{
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "enter:now PCI: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+                        /* checks Sub-Vendor ID because system crashes with Traverse-Card */
+			if ((dev_netjet->subsystem_vendor != 0x55) ||
+				(dev_netjet->subsystem_device != 0x02)) {
+				printk(KERN_WARNING "enter:now: You tried to load this driver with an incompatible TigerJet-card\n");
+                                printk(KERN_WARNING "Use type=20 for Traverse NetJet PCI Card.\n");
+                                return(0);
+                        }
+		} else {
+                        printk(KERN_WARNING "enter:now PCI: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base + 0xC0; // Fenster zum AMD
+
+		/* Reset an */
+		cs->hw.njet.ctrl_reg = 0x07;  // geändert von 0xff
+		OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		/* 20 ms Pause */
+		mdelay(20);
+
+		cs->hw.njet.ctrl_reg = 0x30;  /* Reset Off and status read clear */
+		OutByte(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.auxd = 0x00; // war 0xc0
+		cs->hw.njet.dmactrl = 0;
+
+		OutByte(cs->hw.njet.base + NETJET_AUXCTRL, ~TJ_AMD_IRQ);
+		OutByte(cs->hw.njet.base + NETJET_IRQMASK1, TJ_AMD_IRQ);
+		OutByte(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		break;
+	}
+#else
+
+	printk(KERN_WARNING "enter:now PCI: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "enter:now PCI: unable to config Formula-n enter:now ISDN PCI ab\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"enter:now PCI: PCI card configured at 0x%lx IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) {
+		printk(KERN_WARNING
+			   "HiSax: %s config port %lx-%lx already in use\n",
+			   CardType[card->typ],
+			   cs->hw.njet.base,
+			   cs->hw.njet.base + bytecnt);
+		return (0);
+	}
+	setup_Amd7930(cs);
+	cs->hw.njet.last_is0 = 0;
+        /* macro rByteAMD */
+        cs->readisac = &ReadByteAmd7930;
+        /* macro wByteAMD */
+        cs->writeisac = &WriteByteAmd7930;
+        cs->dc.amd7930.setIrqMask = &enpci_setIrqMask;
+
+        cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	cs->cardmsg = &enpci_card_msg;
+	cs->irq_func = &enpci_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+
+        return (1);
+}
diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c
new file mode 100644
index 0000000..0d44a3f
--- /dev/null
+++ b/drivers/isdn/hisax/fsm.c
@@ -0,0 +1,163 @@
+/* $Id: fsm.c,v 1.14.6.4 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "hisax.h"
+
+#define FSM_TIMER_DEBUG 0
+
+int
+FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount)
+{
+	int i;
+
+	fsm->jumpmatrix = (FSMFNPTR *)
+		kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
+	if (!fsm->jumpmatrix)
+		return -ENOMEM;
+
+	memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count);
+
+	for (i = 0; i < fncount; i++) 
+		if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) {
+			printk(KERN_ERR "FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n",
+				i,(long)fnlist[i].state,(long)fsm->state_count,
+				(long)fnlist[i].event,(long)fsm->event_count);
+		} else		
+			fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+				fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
+	return 0;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+	kfree((void *) fsm->jumpmatrix);
+}
+
+int
+FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+	FSMFNPTR r;
+
+	if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) {
+		printk(KERN_ERR "FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
+			(long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count);
+		return(1);
+	}
+	r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+	if (r) {
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+		r(fi, event, arg);
+		return (0);
+	} else {
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s no routine",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+		return (!0);
+	}
+}
+
+void
+FsmChangeState(struct FsmInst *fi, int newstate)
+{
+	fi->state = newstate;
+	if (fi->debug)
+		fi->printdebug(fi, "ChangeState %s",
+			fi->fsm->strState[newstate]);
+}
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
+#endif
+	FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+	ft->fi = fi;
+	ft->tl.function = (void *) FsmExpireTimer;
+	ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
+#endif
+	init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
+#endif
+	del_timer(&ft->tl);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (timer_pending(&ft->tl)) {
+		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+		ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
+		return -1;
+	}
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+	return 0;
+}
+
+void
+FsmRestartTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (timer_pending(&ft->tl))
+		del_timer(&ft->tl);
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+}
diff --git a/drivers/isdn/hisax/fsm.h b/drivers/isdn/hisax/fsm.h
new file mode 100644
index 0000000..f02f7da
--- /dev/null
+++ b/drivers/isdn/hisax/fsm.h
@@ -0,0 +1,61 @@
+/* $Id: fsm.h,v 1.3.2.2 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __FSM_H__
+#define __FSM_H__
+
+#include <linux/timer.h>
+
+struct FsmInst;
+
+typedef void (* FSMFNPTR)(struct FsmInst *, int, void *);
+
+struct Fsm {
+	FSMFNPTR *jumpmatrix;
+	int state_count, event_count;
+	char **strEvent, **strState;
+};
+
+struct FsmInst {
+	struct Fsm *fsm;
+	int state;
+	int debug;
+	void *userdata;
+	int userint;
+	void (*printdebug) (struct FsmInst *, char *, ...);
+};
+
+struct FsmNode {
+	int state, event;
+	void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+	struct FsmInst *fi;
+	struct timer_list tl;
+	int event;
+	void *arg;
+};
+
+int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+		void *arg, int where);
+void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
+		     void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+
+#endif
diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c
new file mode 100644
index 0000000..24a05a4
--- /dev/null
+++ b/drivers/isdn/hisax/gazel.c
@@ -0,0 +1,684 @@
+/* $Id: gazel.c,v 2.19.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for Gazel isdn cards
+ *
+ * Author       BeWan Systems
+ *              based on source code from Karsten Keil
+ * Copyright    by BeWan Systems
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include "ipac.h"
+#include <linux/pci.h>
+
+extern const char *CardType[];
+const char *gazel_revision = "$Revision: 2.19.2.4 $";
+
+#define R647      1
+#define R685      2
+#define R753      3
+#define R742      4
+
+#define PLX_CNTRL    0x50	/* registre de controle PLX */
+#define RESET_GAZEL  0x4
+#define RESET_9050   0x40000000
+#define PLX_INCSR    0x4C	/* registre d'IT du 9050 */
+#define INT_ISAC_EN  0x8	/* 1 = enable IT isac */
+#define INT_ISAC     0x20	/* 1 = IT isac en cours */
+#define INT_HSCX_EN  0x1	/* 1 = enable IT hscx */
+#define INT_HSCX     0x4	/* 1 = IT hscx en cours */
+#define INT_PCI_EN   0x40	/* 1 = enable IT PCI */
+#define INT_IPAC_EN  0x3	/* enable IT ipac */
+
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_short off)
+{
+	return bytein(adr + off);
+}
+
+static inline void
+writereg(unsigned int adr, u_short off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+static inline u_char
+readreg_ipac(unsigned int adr, u_short off)
+{
+	register u_char ret;
+
+	byteout(adr, off);
+	ret = bytein(adr + 4);
+	return ret;
+}
+
+static inline void
+writereg_ipac(unsigned int adr, u_short off, u_char data)
+{
+	byteout(adr, off);
+	byteout(adr + 4, data);
+}
+
+
+static inline void
+read_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size)
+{
+	byteout(adr, off);
+	insb(adr + 4, data, size);
+}
+
+static void
+write_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size)
+{
+	byteout(adr, off);
+	outsb(adr + 4, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			return (readreg(cs->hw.gazel.isac, off2));
+		case R753:
+		case R742:
+			return (readreg_ipac(cs->hw.gazel.ipac, 0x80 + off2));
+	}
+	return 0;
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			writereg(cs->hw.gazel.isac, off2, value);
+			break;
+		case R753:
+		case R742:
+			writereg_ipac(cs->hw.gazel.ipac, 0x80 + off2, value);
+			break;
+	}
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			read_fifo(cs->hw.gazel.isacfifo, data, size);
+			break;
+		case R753:
+		case R742:
+			read_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
+			break;
+	}
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			write_fifo(cs->hw.gazel.isacfifo, data, size);
+			break;
+		case R753:
+		case R742:
+			write_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
+			break;
+	}
+}
+
+static void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			read_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
+			break;
+		case R753:
+		case R742:
+			read_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
+			break;
+	}
+}
+
+static void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			write_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
+			break;
+		case R753:
+		case R742:
+			write_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
+			break;
+	}
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			return (readreg(cs->hw.gazel.hscx[hscx], off2));
+		case R753:
+		case R742:
+			return (readreg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2));
+	}
+	return 0;
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	u_short off2 = offset;
+
+	switch (cs->subtyp) {
+		case R647:
+			off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+		case R685:
+			writereg(cs->hw.gazel.hscx[hscx], off2, value);
+			break;
+		case R753:
+		case R742:
+			writereg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2, value);
+			break;
+	}
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+gazel_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 5
+	struct IsdnCardState *cs = dev_id;
+	u_char valisac, valhscx;
+	int count = 0;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	do {
+		valhscx = ReadHSCX(cs, 1, HSCX_ISTA);
+		if (valhscx)
+			hscx_int_main(cs, valhscx);
+		valisac = ReadISAC(cs, ISAC_ISTA);
+		if (valisac)
+			isac_interrupt(cs, valisac);
+		count++;
+	} while ((valhscx || valisac) && (count < MAXCOUNT));
+
+	WriteHSCX(cs, 0, HSCX_MASK, 0xFF);
+	WriteHSCX(cs, 1, HSCX_MASK, 0xFF);
+	WriteISAC(cs, ISAC_MASK, 0xFF);
+	WriteISAC(cs, ISAC_MASK, 0x0);
+	WriteHSCX(cs, 0, HSCX_MASK, 0x0);
+	WriteHSCX(cs, 1, HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t
+gazel_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val;
+	int count = 0;
+	u_long flags;
+	
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = ReadISAC(cs, IPAC_ISTA - 0x80);
+	do {
+		if (ista & 0x0f) {
+			val = ReadHSCX(cs, 1, HSCX_ISTA);
+			if (ista & 0x01)
+				val |= 0x01;
+			if (ista & 0x04)
+				val |= 0x02;
+			if (ista & 0x08)
+				val |= 0x04;
+			if (val) {
+				hscx_int_main(cs, val);
+			}
+		}
+		if (ista & 0x20) {
+			val = 0xfe & ReadISAC(cs, ISAC_ISTA);
+			if (val) {
+				isac_interrupt(cs, val);
+			}
+		}
+		if (ista & 0x10) {
+			val = 0x01;
+			isac_interrupt(cs, val);
+		}
+		ista = ReadISAC(cs, IPAC_ISTA - 0x80);
+		count++;
+	}
+	while ((ista & 0x3f) && (count < MAXCOUNT));
+
+	WriteISAC(cs, IPAC_MASK - 0x80, 0xFF);
+	WriteISAC(cs, IPAC_MASK - 0x80, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+void
+release_io_gazel(struct IsdnCardState *cs)
+{
+	unsigned int i;
+
+	switch (cs->subtyp) {
+		case R647:
+			for (i = 0x0000; i < 0xC000; i += 0x1000)
+				release_region(i + cs->hw.gazel.hscx[0], 16);
+			release_region(0xC000 + cs->hw.gazel.hscx[0], 1);
+			break;
+
+		case R685:
+			release_region(cs->hw.gazel.hscx[0], 0x100);
+			release_region(cs->hw.gazel.cfg_reg, 0x80);
+			break;
+
+		case R753:
+			release_region(cs->hw.gazel.ipac, 0x8);
+			release_region(cs->hw.gazel.cfg_reg, 0x80);
+			break;
+
+		case R742:
+			release_region(cs->hw.gazel.ipac, 8);
+			break;
+	}
+}
+
+static int
+reset_gazel(struct IsdnCardState *cs)
+{
+	unsigned long plxcntrl, addr = cs->hw.gazel.cfg_reg;
+
+	switch (cs->subtyp) {
+		case R647:
+			writereg(addr, 0, 0);
+			HZDELAY(10);
+			writereg(addr, 0, 1);
+			HZDELAY(2);
+			break;
+		case R685:
+			plxcntrl = inl(addr + PLX_CNTRL);
+			plxcntrl |= (RESET_9050 + RESET_GAZEL);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
+			HZDELAY(4);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			HZDELAY(10);
+			outb(INT_ISAC_EN + INT_HSCX_EN + INT_PCI_EN, addr + PLX_INCSR);
+			break;
+		case R753:
+			plxcntrl = inl(addr + PLX_CNTRL);
+			plxcntrl |= (RESET_9050 + RESET_GAZEL);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
+			HZDELAY(4);
+			outl(plxcntrl, addr + PLX_CNTRL);
+			HZDELAY(10);
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
+			WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
+			WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
+			WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
+			outb(INT_IPAC_EN + INT_PCI_EN, addr + PLX_INCSR);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
+			break;
+		case R742:
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
+			HZDELAY(4);
+			WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
+			WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
+			WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
+			WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
+			WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
+			break;
+	}
+	return (0);
+}
+
+static int
+Gazel_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_gazel(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			release_io_gazel(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 1);
+			if ((cs->subtyp==R647)||(cs->subtyp==R685)) {
+				int i;
+				for (i=0;i<(2+MAX_WAITING_CALLS);i++) {
+					cs->bcs[i].hw.hscx.tsaxr0 = 0x1f;
+					cs->bcs[i].hw.hscx.tsaxr1 = 0x23;
+				}
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+static int
+reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs)
+{
+	unsigned int i, j, base = 0, adr = 0, len = 0;
+
+	switch (cs->subtyp) {
+		case R647:
+			base = cs->hw.gazel.hscx[0];
+			if (!request_region(adr = (0xC000 + base), len = 1, "gazel"))
+				goto error;
+			for (i = 0x0000; i < 0xC000; i += 0x1000) {
+				if (!request_region(adr = (i + base), len = 16, "gazel"))
+					goto error;
+			}
+			if (i != 0xC000) {
+				for (j = 0; j < i; j+= 0x1000)
+					release_region(j + base, 16);
+				release_region(0xC000 + base, 1);
+				goto error;
+			}
+			break;
+
+		case R685:
+			if (!request_region(adr = cs->hw.gazel.hscx[0], len = 0x100, "gazel"))
+				goto error;
+			if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
+				release_region(cs->hw.gazel.hscx[0],0x100);
+				goto error;
+			}
+			break;
+
+		case R753:
+			if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
+				goto error;
+			if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
+				release_region(cs->hw.gazel.ipac, 8);
+				goto error;
+			}
+			break;
+
+		case R742:
+			if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
+				goto error;
+			break;
+	}
+
+	return 0;
+
+      error:
+	printk(KERN_WARNING "Gazel: %s io ports 0x%x-0x%x already in use\n",
+	       CardType[cs->typ], adr, adr + len);
+	return 1;
+}
+
+static int __init
+setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n");
+	// we got an irq parameter, assume it is an ISA card
+	// R742 decodes address even in not started...
+	// R647 returns FF if not present or not started
+	// eventually needs improvment
+	if (readreg_ipac(card->para[1], IPAC_ID) == 1)
+		cs->subtyp = R742;
+	else
+		cs->subtyp = R647;
+
+	setup_isac(cs);
+	cs->hw.gazel.cfg_reg = card->para[1] + 0xC000;
+	cs->hw.gazel.ipac = card->para[1];
+	cs->hw.gazel.isac = card->para[1] + 0x8000;
+	cs->hw.gazel.hscx[0] = card->para[1];
+	cs->hw.gazel.hscx[1] = card->para[1] + 0x4000;
+	cs->irq = card->para[0];
+	cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
+	cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
+	cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
+
+	switch (cs->subtyp) {
+		case R647:
+			printk(KERN_INFO "Gazel: Card ISA R647/R648 found\n");
+			cs->dc.isac.adf2 = 0x87;
+			printk(KERN_INFO
+				"Gazel: config irq:%d isac:0x%X  cfg:0x%X\n",
+				cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
+			printk(KERN_INFO
+				"Gazel: hscx A:0x%X  hscx B:0x%X\n",
+				cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
+
+			break;
+		case R742:
+			printk(KERN_INFO "Gazel: Card ISA R742 found\n");
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+			printk(KERN_INFO
+			       "Gazel: config irq:%d ipac:0x%X\n",
+			       cs->irq, cs->hw.gazel.ipac);
+			break;
+	}
+
+	return (0);
+}
+
+static struct pci_dev *dev_tel __initdata = NULL;
+
+static int __init
+setup_gazelpci(struct IsdnCardState *cs)
+{
+	u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0;
+	u_char pci_irq = 0, found;
+	u_int nbseek, seekcard;
+
+	printk(KERN_WARNING "Gazel: PCI card automatic recognition\n");
+
+	found = 0;
+	seekcard = PCI_DEVICE_ID_PLX_R685;
+	for (nbseek = 0; nbseek < 3; nbseek++) {
+		if ((dev_tel = pci_find_device(PCI_VENDOR_ID_PLX, seekcard, dev_tel))) {
+			if (pci_enable_device(dev_tel))
+				return 1;
+			pci_irq = dev_tel->irq;
+			pci_ioaddr0 = pci_resource_start(dev_tel, 1);
+			pci_ioaddr1 = pci_resource_start(dev_tel, 2);
+			found = 1;
+		}
+		if (found)
+			break;
+		else {
+			switch (seekcard) {
+				case PCI_DEVICE_ID_PLX_R685:
+					seekcard = PCI_DEVICE_ID_PLX_R753;
+					break;
+				case PCI_DEVICE_ID_PLX_R753:
+					seekcard = PCI_DEVICE_ID_PLX_DJINN_ITOO;
+					break;
+			}
+		}
+	}
+	if (!found) {
+		printk(KERN_WARNING "Gazel: No PCI card found\n");
+		return (1);
+	}
+	if (!pci_irq) {
+		printk(KERN_WARNING "Gazel: No IRQ for PCI card found\n");
+		return 1;
+	}
+	cs->hw.gazel.pciaddr[0] = pci_ioaddr0;
+	cs->hw.gazel.pciaddr[1] = pci_ioaddr1;
+	setup_isac(cs);
+	pci_ioaddr1 &= 0xfffe;
+	cs->hw.gazel.cfg_reg = pci_ioaddr0 & 0xfffe;
+	cs->hw.gazel.ipac = pci_ioaddr1;
+	cs->hw.gazel.isac = pci_ioaddr1 + 0x80;
+	cs->hw.gazel.hscx[0] = pci_ioaddr1;
+	cs->hw.gazel.hscx[1] = pci_ioaddr1 + 0x40;
+	cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
+	cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
+	cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
+	cs->irq = pci_irq;
+	cs->irq_flags |= SA_SHIRQ;
+
+	switch (seekcard) {
+		case PCI_DEVICE_ID_PLX_R685:
+			printk(KERN_INFO "Gazel: Card PCI R685 found\n");
+			cs->subtyp = R685;
+			cs->dc.isac.adf2 = 0x87;
+			printk(KERN_INFO
+			    "Gazel: config irq:%d isac:0x%X  cfg:0x%X\n",
+			cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
+			printk(KERN_INFO
+			       "Gazel: hscx A:0x%X  hscx B:0x%X\n",
+			     cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
+			break;
+		case PCI_DEVICE_ID_PLX_R753:
+		case PCI_DEVICE_ID_PLX_DJINN_ITOO:
+			printk(KERN_INFO "Gazel: Card PCI R753 found\n");
+			cs->subtyp = R753;
+			test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+			printk(KERN_INFO
+			    "Gazel: config irq:%d ipac:0x%X  cfg:0x%X\n",
+			cs->irq, cs->hw.gazel.ipac, cs->hw.gazel.cfg_reg);
+			break;
+	}
+
+	return (0);
+}
+
+int __init
+setup_gazel(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_char val;
+
+	strcpy(tmp, gazel_revision);
+	printk(KERN_INFO "Gazel: Driver Revision %s\n", HiSax_getrev(tmp));
+
+	if (cs->typ != ISDN_CTYPE_GAZEL)
+		return (0);
+
+	if (card->para[0]) {
+		if (setup_gazelisa(card, cs))
+			return (0);
+	} else {
+
+#ifdef CONFIG_PCI
+		if (setup_gazelpci(cs))
+			return (0);
+#else
+		printk(KERN_WARNING "Gazel: Card PCI requested and NO_PCI_BIOS, unable to config\n");
+		return (0);
+#endif				/* CONFIG_PCI */
+	}
+
+	if (reserve_regions(card, cs)) {
+		return (0);
+	}
+	if (reset_gazel(cs)) {
+		printk(KERN_WARNING "Gazel: wrong IRQ\n");
+		release_io_gazel(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Gazel_card_msg;
+
+	switch (cs->subtyp) {
+		case R647:
+		case R685:
+			cs->irq_func = &gazel_interrupt;
+			ISACVersion(cs, "Gazel:");
+			if (HscxVersion(cs, "Gazel:")) {
+				printk(KERN_WARNING
+				       "Gazel: wrong HSCX versions check IO address\n");
+				release_io_gazel(cs);
+				return (0);
+			}
+			break;
+		case R742:
+		case R753:
+			cs->irq_func = &gazel_interrupt_ipac;
+			val = ReadISAC(cs, IPAC_ID - 0x80);
+			printk(KERN_INFO "Gazel: IPAC version %x\n", val);
+			break;
+	}
+
+	return (1);
+}
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c
new file mode 100644
index 0000000..1ac46c26
--- /dev/null
+++ b/drivers/isdn/hisax/hfc4s8s_l1.c
@@ -0,0 +1,1714 @@
+/*************************************************************************/
+/* $Id: hfc4s8s_l1.c,v 1.10 2005/02/09 16:31:09 martinb1 Exp $           */
+/* HFC-4S/8S low layer interface for Cologne Chip HFC-4S/8S isdn chips   */
+/* The low layer (L1) is implemented as a loadable module for usage with */
+/* the HiSax isdn driver for passive cards.                              */
+/*                                                                       */
+/* Author: Werner Cornelius                                              */
+/* (C) 2003 Cornelius Consult (werner@cornelius-consult.de)              */
+/*                                                                       */
+/* Driver maintained by Cologne Chip                                     */
+/*   - Martin Bachem, support@colognechip.com                            */
+/*                                                                       */
+/* This driver only works with chip revisions >= 1, older revision 0     */
+/* engineering samples (only first manufacturer sample cards) will not   */
+/* work and are rejected by the driver.                                  */
+/*                                                                       */
+/* This file distributed under the GNU GPL.                              */
+/*                                                                       */
+/* See Version History at the end of this file                           */
+/*                                                                       */
+/*************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include "hisax_if.h"
+#include "hfc4s8s_l1.h"
+
+static const char hfc4s8s_rev[] = "Revision: 1.10";
+
+/***************************************************************/
+/* adjustable transparent mode fifo threshold                  */
+/* The value defines the used fifo threshold with the equation */
+/*                                                             */
+/* notify number of bytes = 2 * 2 ^ TRANS_FIFO_THRES           */
+/*                                                             */
+/* The default value is 5 which results in a buffer size of 64 */
+/* and an interrupt rate of 8ms.                               */
+/* The maximum value is 7 due to fifo size restrictions.       */
+/* Values below 3-4 are not recommended due to high interrupt  */
+/* load of the processor. For non critical applications the    */
+/* value should be raised to 7 to reduce any interrupt overhead*/
+/***************************************************************/
+#define TRANS_FIFO_THRES 5
+
+/*************/
+/* constants */
+/*************/
+#define CLOCKMODE_0     0	/* ext. 24.576 MhZ clk freq, int. single clock mode */
+#define CLOCKMODE_1     1	/* ext. 49.576 MhZ clk freq, int. single clock mode */
+#define CHIP_ID_SHIFT   4
+#define HFC_MAX_ST 8
+#define MAX_D_FRAME_SIZE 270
+#define MAX_B_FRAME_SIZE 1536
+#define TRANS_TIMER_MODE (TRANS_FIFO_THRES & 0xf)
+#define TRANS_FIFO_BYTES (2 << TRANS_FIFO_THRES)
+#define MAX_F_CNT 0x0f
+
+#define CLKDEL_NT 0x6c
+#define CLKDEL_TE 0xf
+#define CTRL0_NT  4
+#define CTRL0_TE  0
+
+#define L1_TIMER_T4 2		/* minimum in jiffies */
+#define L1_TIMER_T3 (7 * HZ)	/* activation timeout */
+#define L1_TIMER_T1 ((120 * HZ) / 1000)	/* NT mode deactivation timeout */
+
+
+/******************/
+/* types and vars */
+/******************/
+static int card_cnt;
+
+/* private driver_data */
+typedef struct {
+	int chip_id;
+	int clock_mode;
+	int max_st_ports;
+	char *device_name;
+} hfc4s8s_param;
+
+static struct pci_device_id hfc4s8s_ids[] = {
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_4S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0x08b4,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_0, 4,
+					    "HFC-4S Evaluation Board"}),
+	 },
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_8S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0x16b8,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_0, 8,
+					    "HFC-8S Evaluation Board"}),
+	 },
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_4S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0xb520,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_1, 4,
+					    "IOB4ST"}),
+	 },
+	{.vendor = PCI_VENDOR_ID_CCD,
+	 .device = PCI_DEVICE_ID_8S,
+	 .subvendor = 0x1397,
+	 .subdevice = 0xb522,
+	 .driver_data =
+	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_1, 8,
+					    "IOB8ST"}),
+	 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, hfc4s8s_ids);
+
+MODULE_AUTHOR("Werner Cornelius, werner@cornelius-consult.de");
+MODULE_DESCRIPTION("ISDN layer 1 for Cologne Chip HFC-4S/8S chips");
+MODULE_LICENSE("GPL");
+
+/***********/
+/* layer 1 */
+/***********/
+struct hfc4s8s_btype {
+	spinlock_t lock;
+	struct hisax_b_if b_if;
+	struct hfc4s8s_l1 *l1p;
+	struct sk_buff_head tx_queue;
+	struct sk_buff *tx_skb;
+	struct sk_buff *rx_skb;
+	__u8 *rx_ptr;
+	int tx_cnt;
+	int bchan;
+	int mode;
+};
+
+struct _hfc4s8s_hw;
+
+struct hfc4s8s_l1 {
+	spinlock_t lock;
+	struct _hfc4s8s_hw *hw;	/* pointer to hardware area */
+	int l1_state;		/* actual l1 state */
+	struct timer_list l1_timer;	/* layer 1 timer structure */
+	int nt_mode;		/* set to nt mode */
+	int st_num;		/* own index */
+	int enabled;		/* interface is enabled */
+	struct sk_buff_head d_tx_queue;	/* send queue */
+	int tx_cnt;		/* bytes to send */
+	struct hisax_d_if d_if;	/* D-channel interface */
+	struct hfc4s8s_btype b_ch[2];	/* B-channel data */
+	struct hisax_b_if *b_table[2];
+};
+
+/**********************/
+/* hardware structure */
+/**********************/
+typedef struct _hfc4s8s_hw {
+	spinlock_t lock;
+
+	int cardnum;
+	int ifnum;
+	int iobase;
+	int nt_mode;
+	u_char *membase;
+	u_char *hw_membase;
+	void *pdev;
+	int max_fifo;
+	hfc4s8s_param driver_data;
+	int irq;
+	int fifo_sched_cnt;
+	struct work_struct tqueue;
+	struct hfc4s8s_l1 l1[HFC_MAX_ST];
+	char card_name[60];
+	struct {
+		u_char r_irq_ctrl;
+		u_char r_ctrl0;
+		volatile u_char r_irq_statech;	/* active isdn l1 status */
+		u_char r_irqmsk_statchg;	/* enabled isdn status ints */
+		u_char r_irq_fifo_blx[8];	/* fifo status registers */
+		u_char fifo_rx_trans_enables[8];	/* mask for enabled transparent rx fifos */
+		u_char fifo_slow_timer_service[8];	/* mask for fifos needing slower timer service */
+		volatile u_char r_irq_oview;	/* contents of overview register */
+		volatile u_char timer_irq;
+		int timer_usg_cnt;	/* number of channels using timer */
+	} mr;
+} hfc4s8s_hw;
+
+
+
+/***************************/
+/* inline function defines */
+/***************************/
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM	/* inline functions mempry mapped */
+
+/* memory write and dummy IO read to avoid PCI byte merge problems */
+#define Write_hfc8(a,b,c) {(*((volatile u_char *)(a->membase+b)) = c); inb(a->iobase+4);}
+/* memory write without dummy IO access for fifo data access */
+#define fWrite_hfc8(a,b,c) (*((volatile u_char *)(a->membase+b)) = c)
+#define Read_hfc8(a,b) (*((volatile u_char *)(a->membase+b)))
+#define Write_hfc16(a,b,c) (*((volatile unsigned short *)(a->membase+b)) = c)
+#define Read_hfc16(a,b) (*((volatile unsigned short *)(a->membase+b)))
+#define Write_hfc32(a,b,c) (*((volatile unsigned long *)(a->membase+b)) = c)
+#define Read_hfc32(a,b) (*((volatile unsigned long *)(a->membase+b)))
+#define wait_busy(a) {while ((Read_hfc8(a, R_STATUS) & M_BUSY));}
+#define PCI_ENA_MEMIO	0x03
+
+#else
+
+/* inline functions io mapped */
+static inline void
+SetRegAddr(hfc4s8s_hw * a, u_char b)
+{
+	outb(b, (a->iobase) + 4);
+}
+
+static inline u_char
+GetRegAddr(hfc4s8s_hw * a)
+{
+	return (inb((volatile u_int) (a->iobase + 4)));
+}
+
+
+static inline void
+Write_hfc8(hfc4s8s_hw * a, u_char b, u_char c)
+{
+	SetRegAddr(a, b);
+	outb(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc8(hfc4s8s_hw * a, u_char c)
+{
+	outb(c, a->iobase);
+}
+
+static inline void
+Write_hfc16(hfc4s8s_hw * a, u_char b, u_short c)
+{
+	SetRegAddr(a, b);
+	outw(c, a->iobase);
+}
+
+static inline void
+Write_hfc32(hfc4s8s_hw * a, u_char b, u_long c)
+{
+	SetRegAddr(a, b);
+	outl(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc32(hfc4s8s_hw * a, u_long c)
+{
+	outl(c, a->iobase);
+}
+
+static inline u_char
+Read_hfc8(hfc4s8s_hw * a, u_char b)
+{
+	SetRegAddr(a, b);
+	return (inb((volatile u_int) a->iobase));
+}
+
+static inline u_char
+fRead_hfc8(hfc4s8s_hw * a)
+{
+	return (inb((volatile u_int) a->iobase));
+}
+
+
+static inline u_short
+Read_hfc16(hfc4s8s_hw * a, u_char b)
+{
+	SetRegAddr(a, b);
+	return (inw((volatile u_int) a->iobase));
+}
+
+static inline u_long
+Read_hfc32(hfc4s8s_hw * a, u_char b)
+{
+	SetRegAddr(a, b);
+	return (inl((volatile u_int) a->iobase));
+}
+
+static inline u_long
+fRead_hfc32(hfc4s8s_hw * a)
+{
+	return (inl((volatile u_int) a->iobase));
+}
+
+static inline void
+wait_busy(hfc4s8s_hw * a)
+{
+	SetRegAddr(a, R_STATUS);
+	while (inb((volatile u_int) a->iobase) & M_BUSY);
+}
+
+#define PCI_ENA_REGIO	0x01
+
+#endif				/* CONFIG_HISAX_HFC4S8S_PCIMEM */
+
+/******************************************************/
+/* function to read critical counter registers that   */
+/* may be udpated by the chip during read             */
+/******************************************************/
+static volatile u_char
+Read_hfc8_stable(hfc4s8s_hw * hw, int reg)
+{
+	u_char ref8;
+	u_char in8;
+	ref8 = Read_hfc8(hw, reg);
+	while (((in8 = Read_hfc8(hw, reg)) != ref8)) {
+		ref8 = in8;
+	}
+	return in8;
+}
+
+static volatile int
+Read_hfc16_stable(hfc4s8s_hw * hw, int reg)
+{
+	int ref16;
+	int in16;
+
+	ref16 = Read_hfc16(hw, reg);
+	while (((in16 = Read_hfc16(hw, reg)) != ref16)) {
+		ref16 = in16;
+	}
+	return in16;
+}
+
+/*****************************/
+/* D-channel call from HiSax */
+/*****************************/
+static void
+dch_l2l1(struct hisax_d_if *iface, int pr, void *arg)
+{
+	struct hfc4s8s_l1 *l1 = iface->ifc.priv;
+	struct sk_buff *skb = (struct sk_buff *) arg;
+	u_long flags;
+
+	switch (pr) {
+
+		case (PH_DATA | REQUEST):
+			if (!l1->enabled) {
+				dev_kfree_skb(skb);
+				break;
+			}
+			spin_lock_irqsave(&l1->lock, flags);
+			skb_queue_tail(&l1->d_tx_queue, skb);
+			if ((skb_queue_len(&l1->d_tx_queue) == 1) &&
+			    (l1->tx_cnt <= 0)) {
+				l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+				    0x10;
+				spin_unlock_irqrestore(&l1->lock, flags);
+				schedule_work(&l1->hw->tqueue);
+			} else
+				spin_unlock_irqrestore(&l1->lock, flags);
+			break;
+
+		case (PH_ACTIVATE | REQUEST):
+			if (!l1->enabled)
+				break;
+			if (!l1->nt_mode) {
+				if (l1->l1_state < 6) {
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					Write_hfc8(l1->hw, A_ST_WR_STA,
+						   0x60);
+					mod_timer(&l1->l1_timer,
+						  jiffies + L1_TIMER_T3);
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+				} else if (l1->l1_state == 7)
+					l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+							  PH_ACTIVATE |
+							  INDICATION,
+							  NULL);
+			} else {
+				if (l1->l1_state != 3) {
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					Write_hfc8(l1->hw, A_ST_WR_STA,
+						   0x60);
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+				} else if (l1->l1_state == 3)
+					l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+							  PH_ACTIVATE |
+							  INDICATION,
+							  NULL);
+			}
+			break;
+
+		default:
+			printk(KERN_INFO
+			       "HFC-4S/8S: Unknown D-chan cmd 0x%x received, ignored\n",
+			       pr);
+			break;
+	}
+	if (!l1->enabled)
+		l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+				  PH_DEACTIVATE | INDICATION, NULL);
+}				/* dch_l2l1 */
+
+/*****************************/
+/* B-channel call from HiSax */
+/*****************************/
+static void
+bch_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct hfc4s8s_btype *bch = ifc->priv;
+	struct hfc4s8s_l1 *l1 = bch->l1p;
+	struct sk_buff *skb = (struct sk_buff *) arg;
+	int mode = (int) arg;
+	u_long flags;
+
+	switch (pr) {
+
+		case (PH_DATA | REQUEST):
+			if (!l1->enabled || (bch->mode == L1_MODE_NULL)) {
+				dev_kfree_skb(skb);
+				break;
+			}
+			spin_lock_irqsave(&l1->lock, flags);
+			skb_queue_tail(&bch->tx_queue, skb);
+			if (!bch->tx_skb && (bch->tx_cnt <= 0)) {
+				l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+				    ((bch->bchan == 1) ? 1 : 4);
+				spin_unlock_irqrestore(&l1->lock, flags);
+				schedule_work(&l1->hw->tqueue);
+			} else
+				spin_unlock_irqrestore(&l1->lock, flags);
+			break;
+
+		case (PH_ACTIVATE | REQUEST):
+		case (PH_DEACTIVATE | REQUEST):
+			if (!l1->enabled)
+				break;
+			if (pr == (PH_DEACTIVATE | REQUEST))
+				mode = L1_MODE_NULL;
+
+			switch (mode) {
+				case L1_MODE_HDLC:
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					l1->hw->mr.timer_usg_cnt++;
+					l1->hw->mr.
+					    fifo_slow_timer_service[l1->
+								    st_num]
+					    |=
+					    ((bch->bchan ==
+					      1) ? 0x2 : 0x8);
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 0 : 2)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xc);	/* HDLC mode, flag fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 1);	/* enable TX interrupts for hdlc */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+					wait_busy(l1->hw);
+
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 1 : 3)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xc);	/* HDLC mode, flag fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 1);	/* enable RX interrupts for hdlc */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					l1->hw->mr.r_ctrl0 |=
+					    (bch->bchan & 3);
+					Write_hfc8(l1->hw, A_ST_CTRL0,
+						   l1->hw->mr.r_ctrl0);
+					bch->mode = L1_MODE_HDLC;
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+
+					bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+							   PH_ACTIVATE |
+							   INDICATION,
+							   NULL);
+					break;
+
+				case L1_MODE_TRANS:
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					l1->hw->mr.
+					    fifo_rx_trans_enables[l1->
+								  st_num]
+					    |=
+					    ((bch->bchan ==
+					      1) ? 0x2 : 0x8);
+					l1->hw->mr.timer_usg_cnt++;
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 0 : 2)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xf);	/* Transparent mode, 1 fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable TX interrupts */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+					wait_busy(l1->hw);
+
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 1 : 3)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_CON_HDLC, 0xf);	/* Transparent mode, 1 fill, connect ST */
+					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable RX interrupts */
+					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					l1->hw->mr.r_ctrl0 |=
+					    (bch->bchan & 3);
+					Write_hfc8(l1->hw, A_ST_CTRL0,
+						   l1->hw->mr.r_ctrl0);
+					bch->mode = L1_MODE_TRANS;
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+
+					bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+							   PH_ACTIVATE |
+							   INDICATION,
+							   NULL);
+					break;
+
+				default:
+					if (bch->mode == L1_MODE_NULL)
+						break;
+					spin_lock_irqsave(&l1->lock,
+							  flags);
+					l1->hw->mr.
+					    fifo_slow_timer_service[l1->
+								    st_num]
+					    &=
+					    ~((bch->bchan ==
+					       1) ? 0x3 : 0xc);
+					l1->hw->mr.
+					    fifo_rx_trans_enables[l1->
+								  st_num]
+					    &=
+					    ~((bch->bchan ==
+					       1) ? 0x3 : 0xc);
+					l1->hw->mr.timer_usg_cnt--;
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 0 : 2)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable TX interrupts */
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, R_FIFO,
+						   (l1->st_num * 8 +
+						    ((bch->bchan ==
+						      1) ? 1 : 3)));
+					wait_busy(l1->hw);
+					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable RX interrupts */
+					Write_hfc8(l1->hw, R_ST_SEL,
+						   l1->st_num);
+					l1->hw->mr.r_ctrl0 &=
+					    ~(bch->bchan & 3);
+					Write_hfc8(l1->hw, A_ST_CTRL0,
+						   l1->hw->mr.r_ctrl0);
+					spin_unlock_irqrestore(&l1->lock,
+							       flags);
+
+					bch->mode = L1_MODE_NULL;
+					bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+							   PH_DEACTIVATE |
+							   INDICATION,
+							   NULL);
+					if (bch->tx_skb) {
+						dev_kfree_skb(bch->tx_skb);
+						bch->tx_skb = NULL;
+					}
+					if (bch->rx_skb) {
+						dev_kfree_skb(bch->rx_skb);
+						bch->rx_skb = NULL;
+					}
+					skb_queue_purge(&bch->tx_queue);
+					bch->tx_cnt = 0;
+					bch->rx_ptr = NULL;
+					break;
+			}
+
+			/* timer is only used when at least one b channel */
+			/* is set up to transparent mode */
+			if (l1->hw->mr.timer_usg_cnt) {
+				Write_hfc8(l1->hw, R_IRQMSK_MISC,
+					   M_TI_IRQMSK);
+			} else {
+				Write_hfc8(l1->hw, R_IRQMSK_MISC, 0);
+			}
+
+			break;
+
+		default:
+			printk(KERN_INFO
+			       "HFC-4S/8S: Unknown B-chan cmd 0x%x received, ignored\n",
+			       pr);
+			break;
+	}
+	if (!l1->enabled)
+		bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+				   PH_DEACTIVATE | INDICATION, NULL);
+}				/* bch_l2l1 */
+
+/**************************/
+/* layer 1 timer function */
+/**************************/
+static void
+hfc_l1_timer(struct hfc4s8s_l1 *l1)
+{
+	u_long flags;
+
+	if (!l1->enabled)
+		return;
+
+	spin_lock_irqsave(&l1->lock, flags);
+	if (l1->nt_mode) {
+		l1->l1_state = 1;
+		Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x11);
+		spin_unlock_irqrestore(&l1->lock, flags);
+		l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+				  PH_DEACTIVATE | INDICATION, NULL);
+		spin_lock_irqsave(&l1->lock, flags);
+		l1->l1_state = 1;
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x1);
+		spin_unlock_irqrestore(&l1->lock, flags);
+	} else {
+		/* activation timed out */
+		Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x13);
+		spin_unlock_irqrestore(&l1->lock, flags);
+		l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+				  PH_DEACTIVATE | INDICATION, NULL);
+		spin_lock_irqsave(&l1->lock, flags);
+		Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+		Write_hfc8(l1->hw, A_ST_WR_STA, 0x3);
+		spin_unlock_irqrestore(&l1->lock, flags);
+	}
+}				/* hfc_l1_timer */
+
+/****************************************/
+/* a complete D-frame has been received */
+/****************************************/
+static void
+rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
+{
+	int z1, z2;
+	u_char f1, f2, df;
+	struct sk_buff *skb;
+	u_char *cp;
+
+
+	if (!l1p->enabled)
+		return;
+	do {
+		/* E/D RX fifo */
+		Write_hfc8(l1p->hw, R_FIFO,
+			   (l1p->st_num * 8 + ((ech) ? 7 : 5)));
+		wait_busy(l1p->hw);
+
+		f1 = Read_hfc8_stable(l1p->hw, A_F1);
+		f2 = Read_hfc8(l1p->hw, A_F2);
+		df = f1 - f2;
+		if ((f1 - f2) < 0)
+			df = f1 - f2 + MAX_F_CNT + 1;
+
+
+		if (!df) {
+			return;	/* no complete frame in fifo */
+		}
+
+		z1 = Read_hfc16_stable(l1p->hw, A_Z1);
+		z2 = Read_hfc16(l1p->hw, A_Z2);
+
+		z1 = z1 - z2 + 1;
+		if (z1 < 0)
+			z1 += 384;
+
+		if (!(skb = dev_alloc_skb(MAX_D_FRAME_SIZE))) {
+			printk(KERN_INFO
+			       "HFC-4S/8S: Could not allocate D/E "
+			       "channel receive buffer");
+			Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+			wait_busy(l1p->hw);
+			return;
+		}
+
+		if (((z1 < 4) || (z1 > MAX_D_FRAME_SIZE))) {
+			if (skb)
+				dev_kfree_skb(skb);
+			/* remove errornous D frame */
+			if (df == 1) {
+				/* reset fifo */
+				Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+				wait_busy(l1p->hw);
+				return;
+			} else {
+				/* read errornous D frame */
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+				SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+				while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+					Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+					fRead_hfc32(l1p->hw);
+#endif
+					z1 -= 4;
+				}
+
+				while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+					Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+					fRead_hfc8(l1p->hw);
+#endif
+
+				Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);
+				wait_busy(l1p->hw);
+				return;
+			}
+		}
+
+		cp = skb->data;
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+		while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*((unsigned long *) cp) =
+			    Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+			*((unsigned long *) cp) = fRead_hfc32(l1p->hw);
+#endif
+			cp += 4;
+			z1 -= 4;
+		}
+
+		while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*cp++ = Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+			*cp++ = fRead_hfc8(l1p->hw);
+#endif
+
+		Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);	/* increment f counter */
+		wait_busy(l1p->hw);
+
+		if (*(--cp)) {
+			dev_kfree_skb(skb);
+		} else {
+			skb->len = (cp - skb->data) - 2;
+			if (ech)
+				l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+						   PH_DATA_E | INDICATION,
+						   skb);
+			else
+				l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+						   PH_DATA | INDICATION,
+						   skb);
+		}
+	} while (1);
+}				/* rx_d_frame */
+
+/*************************************************************/
+/* a B-frame has been received (perhaps not fully completed) */
+/*************************************************************/
+static void
+rx_b_frame(struct hfc4s8s_btype *bch)
+{
+	int z1, z2, hdlc_complete;
+	u_char f1, f2;
+	struct hfc4s8s_l1 *l1 = bch->l1p;
+	struct sk_buff *skb;
+
+	if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+		return;
+
+	do {
+		/* RX Fifo */
+		Write_hfc8(l1->hw, R_FIFO,
+			   (l1->st_num * 8 + ((bch->bchan == 1) ? 1 : 3)));
+		wait_busy(l1->hw);
+
+		if (bch->mode == L1_MODE_HDLC) {
+			f1 = Read_hfc8_stable(l1->hw, A_F1);
+			f2 = Read_hfc8(l1->hw, A_F2);
+			hdlc_complete = ((f1 ^ f2) & MAX_F_CNT);
+		} else
+			hdlc_complete = 0;
+		z1 = Read_hfc16_stable(l1->hw, A_Z1);
+		z2 = Read_hfc16(l1->hw, A_Z2);
+		z1 = (z1 - z2);
+		if (hdlc_complete)
+			z1++;
+		if (z1 < 0)
+			z1 += 384;
+
+		if (!z1)
+			break;
+
+		if (!(skb = bch->rx_skb)) {
+			if (!
+			    (skb =
+			     dev_alloc_skb((bch->mode ==
+					    L1_MODE_TRANS) ? z1
+					   : (MAX_B_FRAME_SIZE + 3)))) {
+				printk(KERN_ERR
+				       "HFC-4S/8S: Could not allocate B "
+				       "channel receive buffer");
+				return;
+			}
+			bch->rx_ptr = skb->data;
+			bch->rx_skb = skb;
+		}
+
+		skb->len = (bch->rx_ptr - skb->data) + z1;
+
+		/* HDLC length check */
+		if ((bch->mode == L1_MODE_HDLC) &&
+		    ((hdlc_complete && (skb->len < 4)) ||
+		     (skb->len > (MAX_B_FRAME_SIZE + 3)))) {
+
+			skb->len = 0;
+			bch->rx_ptr = skb->data;
+			Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+			wait_busy(l1->hw);
+			return;
+		}
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+
+		while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*((unsigned long *) bch->rx_ptr) =
+			    Read_hfc32(l1->hw, A_FIFO_DATA0);
+#else
+			*((unsigned long *) bch->rx_ptr) =
+			    fRead_hfc32(l1->hw);
+#endif
+			bch->rx_ptr += 4;
+			z1 -= 4;
+		}
+
+		while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			*(bch->rx_ptr++) = Read_hfc8(l1->hw, A_FIFO_DATA0);
+#else
+			*(bch->rx_ptr++) = fRead_hfc8(l1->hw);
+#endif
+
+		if (hdlc_complete) {
+			/* increment f counter */
+			Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+			wait_busy(l1->hw);
+
+			/* hdlc crc check */
+			bch->rx_ptr--;
+			if (*bch->rx_ptr) {
+				skb->len = 0;
+				bch->rx_ptr = skb->data;
+				continue;
+			}
+			skb->len -= 3;
+		}
+		if (hdlc_complete || (bch->mode == L1_MODE_TRANS)) {
+			bch->rx_skb = NULL;
+			bch->rx_ptr = NULL;
+			bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+					   PH_DATA | INDICATION, skb);
+		}
+
+	} while (1);
+}				/* rx_b_frame */
+
+/********************************************/
+/* a D-frame has been/should be transmitted */
+/********************************************/
+static void
+tx_d_frame(struct hfc4s8s_l1 *l1p)
+{
+	struct sk_buff *skb;
+	u_char f1, f2;
+	u_char *cp;
+	int cnt;
+
+	if (l1p->l1_state != 7)
+		return;
+
+	/* TX fifo */
+	Write_hfc8(l1p->hw, R_FIFO, (l1p->st_num * 8 + 4));
+	wait_busy(l1p->hw);
+
+	f1 = Read_hfc8(l1p->hw, A_F1);
+	f2 = Read_hfc8_stable(l1p->hw, A_F2);
+
+	if ((f1 ^ f2) & MAX_F_CNT)
+		return;		/* fifo is still filled */
+
+	if (l1p->tx_cnt > 0) {
+		cnt = l1p->tx_cnt;
+		l1p->tx_cnt = 0;
+		l1p->d_if.ifc.l1l2(&l1p->d_if.ifc, PH_DATA | CONFIRM,
+				   (void *) cnt);
+	}
+
+	if ((skb = skb_dequeue(&l1p->d_tx_queue))) {
+		cp = skb->data;
+		cnt = skb->len;
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+		while (cnt >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			fWrite_hfc32(l1p->hw, A_FIFO_DATA0,
+				     *(unsigned long *) cp);
+#else
+			SetRegAddr(l1p->hw, A_FIFO_DATA0);
+			fWrite_hfc32(l1p->hw, *(unsigned long *) cp);
+#endif
+			cp += 4;
+			cnt -= 4;
+		}
+
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+		while (cnt--)
+			fWrite_hfc8(l1p->hw, A_FIFO_DATA0, *cp++);
+#else
+		while (cnt--)
+			fWrite_hfc8(l1p->hw, *cp++);
+#endif
+
+		l1p->tx_cnt = skb->truesize;
+		Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);	/* increment f counter */
+		wait_busy(l1p->hw);
+
+		dev_kfree_skb(skb);
+	}
+}				/* tx_d_frame */
+
+/******************************************************/
+/* a B-frame may be transmitted (or is not completed) */
+/******************************************************/
+static void
+tx_b_frame(struct hfc4s8s_btype *bch)
+{
+	struct sk_buff *skb;
+	struct hfc4s8s_l1 *l1 = bch->l1p;
+	u_char *cp;
+	int cnt, max, hdlc_num, ack_len = 0;
+
+	if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+		return;
+
+	/* TX fifo */
+	Write_hfc8(l1->hw, R_FIFO,
+		   (l1->st_num * 8 + ((bch->bchan == 1) ? 0 : 2)));
+	wait_busy(l1->hw);
+	do {
+
+		if (bch->mode == L1_MODE_HDLC) {
+			hdlc_num = Read_hfc8(l1->hw, A_F1) & MAX_F_CNT;
+			hdlc_num -=
+			    (Read_hfc8_stable(l1->hw, A_F2) & MAX_F_CNT);
+			if (hdlc_num < 0)
+				hdlc_num += 16;
+			if (hdlc_num >= 15)
+				break;	/* fifo still filled up with hdlc frames */
+		} else
+			hdlc_num = 0;
+
+		if (!(skb = bch->tx_skb)) {
+			if (!(skb = skb_dequeue(&bch->tx_queue))) {
+				l1->hw->mr.fifo_slow_timer_service[l1->
+								   st_num]
+				    &= ~((bch->bchan == 1) ? 1 : 4);
+				break;	/* list empty */
+			}
+			bch->tx_skb = skb;
+			bch->tx_cnt = 0;
+		}
+
+		if (!hdlc_num)
+			l1->hw->mr.fifo_slow_timer_service[l1->st_num] |=
+			    ((bch->bchan == 1) ? 1 : 4);
+		else
+			l1->hw->mr.fifo_slow_timer_service[l1->st_num] &=
+			    ~((bch->bchan == 1) ? 1 : 4);
+
+		max = Read_hfc16_stable(l1->hw, A_Z2);
+		max -= Read_hfc16(l1->hw, A_Z1);
+		if (max <= 0)
+			max += 384;
+		max--;
+
+		if (max < 16)
+			break;	/* don't write to small amounts of bytes */
+
+		cnt = skb->len - bch->tx_cnt;
+		if (cnt > max)
+			cnt = max;
+		cp = skb->data + bch->tx_cnt;
+		bch->tx_cnt += cnt;
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+		while (cnt >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			fWrite_hfc32(l1->hw, A_FIFO_DATA0,
+				     *(unsigned long *) cp);
+#else
+			fWrite_hfc32(l1->hw, *(unsigned long *) cp);
+#endif
+			cp += 4;
+			cnt -= 4;
+		}
+
+		while (cnt--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+			fWrite_hfc8(l1->hw, A_FIFO_DATA0, *cp++);
+#else
+			fWrite_hfc8(l1->hw, *cp++);
+#endif
+
+		if (bch->tx_cnt >= skb->len) {
+			if (bch->mode == L1_MODE_HDLC) {
+				/* increment f counter */
+				Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+			}
+			ack_len += skb->truesize;
+			bch->tx_skb = 0;
+			bch->tx_cnt = 0;
+			dev_kfree_skb(skb);
+		} else
+			/* Re-Select */
+			Write_hfc8(l1->hw, R_FIFO,
+				   (l1->st_num * 8 +
+				    ((bch->bchan == 1) ? 0 : 2)));
+		wait_busy(l1->hw);
+	} while (1);
+
+	if (ack_len)
+		bch->b_if.ifc.l1l2((struct hisax_if *) &bch->b_if,
+				   PH_DATA | CONFIRM, (void *) ack_len);
+}				/* tx_b_frame */
+
+/*************************************/
+/* bottom half handler for interrupt */
+/*************************************/
+static void
+hfc4s8s_bh(hfc4s8s_hw * hw)
+{
+	u_char b;
+	struct hfc4s8s_l1 *l1p;
+	volatile u_char *fifo_stat;
+	int idx;
+
+	/* handle layer 1 state changes */
+	b = 1;
+	l1p = hw->l1;
+	while (b) {
+		if ((b & hw->mr.r_irq_statech)) {
+			/* reset l1 event */
+			hw->mr.r_irq_statech &= ~b;
+			if (l1p->enabled) {
+				if (l1p->nt_mode) {
+					u_char oldstate = l1p->l1_state;
+
+					Write_hfc8(l1p->hw, R_ST_SEL,
+						   l1p->st_num);
+					l1p->l1_state =
+					    Read_hfc8(l1p->hw,
+						      A_ST_RD_STA) & 0xf;
+
+					if ((oldstate == 3)
+					    && (l1p->l1_state != 3))
+						l1p->d_if.ifc.l1l2(&l1p->
+								   d_if.
+								   ifc,
+								   PH_DEACTIVATE
+								   |
+								   INDICATION,
+								   NULL);
+
+					if (l1p->l1_state != 2) {
+						del_timer(&l1p->l1_timer);
+						if (l1p->l1_state == 3) {
+							l1p->d_if.ifc.
+							    l1l2(&l1p->
+								 d_if.ifc,
+								 PH_ACTIVATE
+								 |
+								 INDICATION,
+								 NULL);
+						}
+					} else {
+						/* allow transition */
+						Write_hfc8(hw, A_ST_WR_STA,
+							   M_SET_G2_G3);
+						mod_timer(&l1p->l1_timer,
+							  jiffies +
+							  L1_TIMER_T1);
+					}
+					printk(KERN_INFO
+					       "HFC-4S/8S: NT ch %d l1 state %d -> %d\n",
+					       l1p->st_num, oldstate,
+					       l1p->l1_state);
+				} else {
+					u_char oldstate = l1p->l1_state;
+
+					Write_hfc8(l1p->hw, R_ST_SEL,
+						   l1p->st_num);
+					l1p->l1_state =
+					    Read_hfc8(l1p->hw,
+						      A_ST_RD_STA) & 0xf;
+
+					if (((l1p->l1_state == 3) &&
+					     ((oldstate == 7) ||
+					      (oldstate == 8))) ||
+					    ((timer_pending
+					      (&l1p->l1_timer))
+					     && (l1p->l1_state == 8))) {
+						mod_timer(&l1p->l1_timer,
+							  L1_TIMER_T4 +
+							  jiffies);
+					} else {
+						if (l1p->l1_state == 7) {
+							del_timer(&l1p->
+								  l1_timer);
+							l1p->d_if.ifc.
+							    l1l2(&l1p->
+								 d_if.ifc,
+								 PH_ACTIVATE
+								 |
+								 INDICATION,
+								 NULL);
+							tx_d_frame(l1p);
+						}
+						if (l1p->l1_state == 3) {
+							if (oldstate != 3)
+								l1p->d_if.
+								    ifc.
+								    l1l2
+								    (&l1p->
+								     d_if.
+								     ifc,
+								     PH_DEACTIVATE
+								     |
+								     INDICATION,
+								     NULL);
+						}
+					}
+					printk(KERN_INFO
+					       "HFC-4S/8S: TE %d ch %d l1 state %d -> %d\n",
+					       l1p->hw->cardnum,
+					       l1p->st_num, oldstate,
+					       l1p->l1_state);
+				}
+			}
+		}
+		b <<= 1;
+		l1p++;
+	}
+
+	/* now handle the fifos */
+	idx = 0;
+	fifo_stat = hw->mr.r_irq_fifo_blx;
+	l1p = hw->l1;
+	while (idx < hw->driver_data.max_st_ports) {
+
+		if (hw->mr.timer_irq) {
+			*fifo_stat |= hw->mr.fifo_rx_trans_enables[idx];
+			if (hw->fifo_sched_cnt <= 0) {
+				*fifo_stat |=
+				    hw->mr.fifo_slow_timer_service[l1p->
+								   st_num];
+			}
+		}
+		/* ignore fifo 6 (TX E fifo) */
+		*fifo_stat &= 0xff - 0x40;
+
+		while (*fifo_stat) {
+
+			if (!l1p->nt_mode) {
+				/* RX Fifo has data to read */
+				if ((*fifo_stat & 0x20)) {
+					*fifo_stat &= ~0x20;
+					rx_d_frame(l1p, 0);
+				}
+				/* E Fifo has data to read */
+				if ((*fifo_stat & 0x80)) {
+					*fifo_stat &= ~0x80;
+					rx_d_frame(l1p, 1);
+				}
+				/* TX Fifo completed send */
+				if ((*fifo_stat & 0x10)) {
+					*fifo_stat &= ~0x10;
+					tx_d_frame(l1p);
+				}
+			}
+			/* B1 RX Fifo has data to read */
+			if ((*fifo_stat & 0x2)) {
+				*fifo_stat &= ~0x2;
+				rx_b_frame(l1p->b_ch);
+			}
+			/* B1 TX Fifo has send completed */
+			if ((*fifo_stat & 0x1)) {
+				*fifo_stat &= ~0x1;
+				tx_b_frame(l1p->b_ch);
+			}
+			/* B2 RX Fifo has data to read */
+			if ((*fifo_stat & 0x8)) {
+				*fifo_stat &= ~0x8;
+				rx_b_frame(l1p->b_ch + 1);
+			}
+			/* B2 TX Fifo has send completed */
+			if ((*fifo_stat & 0x4)) {
+				*fifo_stat &= ~0x4;
+				tx_b_frame(l1p->b_ch + 1);
+			}
+		}
+		fifo_stat++;
+		l1p++;
+		idx++;
+	}
+
+	if (hw->fifo_sched_cnt <= 0)
+		hw->fifo_sched_cnt += (1 << (7 - TRANS_TIMER_MODE));
+	hw->mr.timer_irq = 0;	/* clear requested timer irq */
+}				/* hfc4s8s_bh */
+
+/*********************/
+/* interrupt handler */
+/*********************/
+static irqreturn_t
+hfc4s8s_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	hfc4s8s_hw *hw = dev_id;
+	u_char b, ovr;
+	volatile u_char *ovp;
+	int idx;
+	u_char old_ioreg;
+
+	if (!hw || !(hw->mr.r_irq_ctrl & M_GLOB_IRQ_EN))
+		return IRQ_NONE;
+
+#ifndef	CONFIG_HISAX_HFC4S8S_PCIMEM
+	/* read current selected regsister */
+	old_ioreg = GetRegAddr(hw);
+#endif
+
+	/* Layer 1 State change */
+	hw->mr.r_irq_statech |=
+	    (Read_hfc8(hw, R_SCI) & hw->mr.r_irqmsk_statchg);
+	if (!
+	    (b = (Read_hfc8(hw, R_STATUS) & (M_MISC_IRQSTA | M_FR_IRQSTA)))
+&& !hw->mr.r_irq_statech) {
+#ifndef	CONFIG_HISAX_HFC4S8S_PCIMEM
+		SetRegAddr(hw, old_ioreg);
+#endif
+		return IRQ_NONE;
+	}
+
+	/* timer event */
+	if (Read_hfc8(hw, R_IRQ_MISC) & M_TI_IRQ) {
+		hw->mr.timer_irq = 1;
+		hw->fifo_sched_cnt--;
+	}
+
+	/* FIFO event */
+	if ((ovr = Read_hfc8(hw, R_IRQ_OVIEW))) {
+		hw->mr.r_irq_oview |= ovr;
+		idx = R_IRQ_FIFO_BL0;
+		ovp = hw->mr.r_irq_fifo_blx;
+		while (ovr) {
+			if ((ovr & 1)) {
+				*ovp |= Read_hfc8(hw, idx);
+			}
+			ovp++;
+			idx++;
+			ovr >>= 1;
+		}
+	}
+
+	/* queue the request to allow other cards to interrupt */
+	schedule_work(&hw->tqueue);
+
+#ifndef	CONFIG_HISAX_HFC4S8S_PCIMEM
+	SetRegAddr(hw, old_ioreg);
+#endif
+	return IRQ_HANDLED;
+}				/* hfc4s8s_interrupt */
+
+/***********************************************************************/
+/* reset the complete chip, don't release the chips irq but disable it */
+/***********************************************************************/
+static void
+chipreset(hfc4s8s_hw * hw)
+{
+	u_long flags;
+
+	spin_lock_irqsave(&hw->lock, flags);
+	Write_hfc8(hw, R_CTRL, 0);	/* use internal RAM */
+	Write_hfc8(hw, R_RAM_MISC, 0);	/* 32k*8 RAM */
+	Write_hfc8(hw, R_FIFO_MD, 0);	/* fifo mode 386 byte/fifo simple mode */
+	Write_hfc8(hw, R_CIRM, M_SRES);	/* reset chip */
+	hw->mr.r_irq_ctrl = 0;	/* interrupt is inactive */
+	spin_unlock_irqrestore(&hw->lock, flags);
+
+	udelay(3);
+	Write_hfc8(hw, R_CIRM, 0);	/* disable reset */
+	wait_busy(hw);
+
+	Write_hfc8(hw, R_PCM_MD0, M_PCM_MD);	/* master mode */
+	Write_hfc8(hw, R_RAM_MISC, M_FZ_MD);	/* transmit fifo option */
+	if (hw->driver_data.clock_mode == 1)
+		Write_hfc8(hw, R_BRG_PCM_CFG, M_PCM_CLK);	/* PCM clk / 2 */
+	Write_hfc8(hw, R_TI_WD, TRANS_TIMER_MODE);	/* timer interval */
+
+	memset(&hw->mr, 0, sizeof(hw->mr));
+}				/* chipreset */
+
+/********************************************/
+/* disable/enable hardware in nt or te mode */
+/********************************************/
+void
+hfc_hardware_enable(hfc4s8s_hw * hw, int enable, int nt_mode)
+{
+	u_long flags;
+	char if_name[40];
+	int i;
+
+	if (enable) {
+		/* save system vars */
+		hw->nt_mode = nt_mode;
+
+		/* enable fifo and state irqs, but not global irq enable */
+		hw->mr.r_irq_ctrl = M_FIFO_IRQ;
+		Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+		hw->mr.r_irqmsk_statchg = 0;
+		Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+		Write_hfc8(hw, R_PWM_MD, 0x80);
+		Write_hfc8(hw, R_PWM1, 26);
+		if (!nt_mode)
+			Write_hfc8(hw, R_ST_SYNC, M_AUTO_SYNC);
+
+		/* enable the line interfaces and fifos */
+		for (i = 0; i < hw->driver_data.max_st_ports; i++) {
+			hw->mr.r_irqmsk_statchg |= (1 << i);
+			Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+			Write_hfc8(hw, R_ST_SEL, i);
+			Write_hfc8(hw, A_ST_CLK_DLY,
+				   ((nt_mode) ? CLKDEL_NT : CLKDEL_TE));
+			hw->mr.r_ctrl0 = ((nt_mode) ? CTRL0_NT : CTRL0_TE);
+			Write_hfc8(hw, A_ST_CTRL0, hw->mr.r_ctrl0);
+			Write_hfc8(hw, A_ST_CTRL2, 3);
+			Write_hfc8(hw, A_ST_WR_STA, 0);	/* enable state machine */
+
+			hw->l1[i].enabled = 1;
+			hw->l1[i].nt_mode = nt_mode;
+
+			if (!nt_mode) {
+				/* setup E-fifo */
+				Write_hfc8(hw, R_FIFO, i * 8 + 7);	/* E fifo */
+				wait_busy(hw);
+				Write_hfc8(hw, A_CON_HDLC, 0x11);	/* HDLC mode, 1 fill, connect ST */
+				Write_hfc8(hw, A_SUBCH_CFG, 2);	/* only 2 bits */
+				Write_hfc8(hw, A_IRQ_MSK, 1);	/* enable interrupt */
+				Write_hfc8(hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+				wait_busy(hw);
+
+				/* setup D RX-fifo */
+				Write_hfc8(hw, R_FIFO, i * 8 + 5);	/* RX fifo */
+				wait_busy(hw);
+				Write_hfc8(hw, A_CON_HDLC, 0x11);	/* HDLC mode, 1 fill, connect ST */
+				Write_hfc8(hw, A_SUBCH_CFG, 2);	/* only 2 bits */
+				Write_hfc8(hw, A_IRQ_MSK, 1);	/* enable interrupt */
+				Write_hfc8(hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+				wait_busy(hw);
+
+				/* setup D TX-fifo */
+				Write_hfc8(hw, R_FIFO, i * 8 + 4);	/* TX fifo */
+				wait_busy(hw);
+				Write_hfc8(hw, A_CON_HDLC, 0x11);	/* HDLC mode, 1 fill, connect ST */
+				Write_hfc8(hw, A_SUBCH_CFG, 2);	/* only 2 bits */
+				Write_hfc8(hw, A_IRQ_MSK, 1);	/* enable interrupt */
+				Write_hfc8(hw, A_INC_RES_FIFO, 2);	/* reset fifo */
+				wait_busy(hw);
+			}
+
+			sprintf(if_name, "hfc4s8s_%d%d_", hw->cardnum, i);
+
+			if (hisax_register
+			    (&hw->l1[i].d_if, hw->l1[i].b_table, if_name,
+			     ((nt_mode) ? 3 : 2))) {
+
+				hw->l1[i].enabled = 0;
+				hw->mr.r_irqmsk_statchg &= ~(1 << i);
+				Write_hfc8(hw, R_SCI_MSK,
+					   hw->mr.r_irqmsk_statchg);
+				printk(KERN_INFO
+				       "HFC-4S/8S: Unable to register S/T device %s, break\n",
+				       if_name);
+				break;
+			}
+		}
+		spin_lock_irqsave(&hw->lock, flags);
+		hw->mr.r_irq_ctrl |= M_GLOB_IRQ_EN;
+		Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+		spin_unlock_irqrestore(&hw->lock, flags);
+	} else {
+		/* disable hardware */
+		spin_lock_irqsave(&hw->lock, flags);
+		hw->mr.r_irq_ctrl &= ~M_GLOB_IRQ_EN;
+		Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+		spin_unlock_irqrestore(&hw->lock, flags);
+
+		for (i = hw->driver_data.max_st_ports - 1; i >= 0; i--) {
+			hw->l1[i].enabled = 0;
+			hisax_unregister(&hw->l1[i].d_if);
+			del_timer(&hw->l1[i].l1_timer);
+			skb_queue_purge(&hw->l1[i].d_tx_queue);
+			skb_queue_purge(&hw->l1[i].b_ch[0].tx_queue);
+			skb_queue_purge(&hw->l1[i].b_ch[1].tx_queue);
+		}
+		chipreset(hw);
+	}
+}				/* hfc_hardware_enable */
+
+/******************************************/
+/* disable memory mapped ports / io ports */
+/******************************************/
+void
+release_pci_ports(hfc4s8s_hw * hw)
+{
+	pci_write_config_word(hw->pdev, PCI_COMMAND, 0);
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	if (hw->membase)
+		iounmap((void *) hw->membase);
+#else
+	if (hw->iobase)
+		release_region(hw->iobase, 8);
+#endif
+}
+
+/*****************************************/
+/* enable memory mapped ports / io ports */
+/*****************************************/
+void
+enable_pci_ports(hfc4s8s_hw * hw)
+{
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+#else
+	pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_REGIO);
+#endif
+}
+
+/*************************************/
+/* initialise the HFC-4s/8s hardware */
+/* return 0 on success.              */
+/*************************************/
+static int __devinit
+setup_instance(hfc4s8s_hw * hw)
+{
+	int err = -EIO;
+	int i;
+
+	for (i = 0; i < HFC_MAX_ST; i++) {
+		struct hfc4s8s_l1 *l1p;
+
+		l1p = hw->l1 + i;
+		spin_lock_init(&l1p->lock);
+		l1p->hw = hw;
+		l1p->l1_timer.function = (void *) hfc_l1_timer;
+		l1p->l1_timer.data = (long) (l1p);
+		init_timer(&l1p->l1_timer);
+		l1p->st_num = i;
+		skb_queue_head_init(&l1p->d_tx_queue);
+		l1p->d_if.ifc.priv = hw->l1 + i;
+		l1p->d_if.ifc.l2l1 = (void *) dch_l2l1;
+
+		spin_lock_init(&l1p->b_ch[0].lock);
+		l1p->b_ch[0].b_if.ifc.l2l1 = (void *) bch_l2l1;
+		l1p->b_ch[0].b_if.ifc.priv = (void *) &l1p->b_ch[0];
+		l1p->b_ch[0].l1p = hw->l1 + i;
+		l1p->b_ch[0].bchan = 1;
+		l1p->b_table[0] = &l1p->b_ch[0].b_if;
+		skb_queue_head_init(&l1p->b_ch[0].tx_queue);
+
+		spin_lock_init(&l1p->b_ch[1].lock);
+		l1p->b_ch[1].b_if.ifc.l2l1 = (void *) bch_l2l1;
+		l1p->b_ch[1].b_if.ifc.priv = (void *) &l1p->b_ch[1];
+		l1p->b_ch[1].l1p = hw->l1 + i;
+		l1p->b_ch[1].bchan = 2;
+		l1p->b_table[1] = &l1p->b_ch[1].b_if;
+		skb_queue_head_init(&l1p->b_ch[1].tx_queue);
+	}
+
+	enable_pci_ports(hw);
+	chipreset(hw);
+
+	i = Read_hfc8(hw, R_CHIP_ID) >> CHIP_ID_SHIFT;
+	if (i != hw->driver_data.chip_id) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: invalid chip id 0x%x instead of 0x%x, card ignored\n",
+		       i, hw->driver_data.chip_id);
+		goto out;
+	}
+
+	i = Read_hfc8(hw, R_CHIP_RV) & 0xf;
+	if (!i) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: chip revision 0 not supported, card ignored\n");
+		goto out;
+	}
+
+	INIT_WORK(&hw->tqueue, (void *) (void *) hfc4s8s_bh, hw);
+
+	if (request_irq
+	    (hw->irq, hfc4s8s_interrupt, SA_SHIRQ, hw->card_name, hw)) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: unable to alloc irq %d, card ignored\n",
+		       hw->irq);
+		goto out;
+	}
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	printk(KERN_INFO
+	       "HFC-4S/8S: found PCI card at membase 0x%p, irq %d\n",
+	       hw->hw_membase, hw->irq);
+#else
+	printk(KERN_INFO
+	       "HFC-4S/8S: found PCI card at iobase 0x%x, irq %d\n",
+	       hw->iobase, hw->irq);
+#endif
+
+	hfc_hardware_enable(hw, 1, 0);
+
+	return (0);
+
+      out:
+	hw->irq = 0;
+	release_pci_ports(hw);
+	kfree(hw);
+	return (err);
+}
+
+/*****************************************/
+/* PCI hotplug interface: probe new card */
+/*****************************************/
+static int __devinit
+hfc4s8s_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int err = -ENOMEM;
+	hfc4s8s_param *driver_data = (hfc4s8s_param *) ent->driver_data;
+	hfc4s8s_hw *hw;
+
+	if (!(hw = kmalloc(sizeof(hfc4s8s_hw), GFP_ATOMIC))) {
+		printk(KERN_ERR "No kmem for HFC-4S/8S card\n");
+		return (err);
+	}
+	memset(hw, 0, sizeof(hfc4s8s_hw));
+
+	hw->pdev = pdev;
+	err = pci_enable_device(pdev);
+
+	if (err)
+		goto out;
+
+	hw->cardnum = card_cnt;
+	sprintf(hw->card_name, "hfc4s8s_%d", hw->cardnum);
+	printk(KERN_INFO "HFC-4S/8S: found adapter %s (%s) at %s\n",
+	       driver_data->device_name, hw->card_name, pci_name(pdev));
+
+	spin_lock_init(&hw->lock);
+
+	hw->driver_data = *driver_data;
+	hw->irq = pdev->irq;
+	hw->iobase = pci_resource_start(pdev, 0);
+
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+	hw->hw_membase = (u_char *) pci_resource_start(pdev, 1);
+	hw->membase = ioremap((ulong) hw->hw_membase, 256);
+#else
+	if (!request_region(hw->iobase, 8, hw->card_name)) {
+		printk(KERN_INFO
+		       "HFC-4S/8S: failed to rquest address space at 0x%04x\n",
+		       hw->iobase);
+		goto out;
+	}
+#endif
+
+	pci_set_drvdata(pdev, hw);
+	err = setup_instance(hw);
+	if (!err)
+		card_cnt++;
+	return (err);
+
+      out:
+	kfree(hw);
+	return (err);
+}
+
+/**************************************/
+/* PCI hotplug interface: remove card */
+/**************************************/
+static void __devexit
+hfc4s8s_remove(struct pci_dev *pdev)
+{
+	hfc4s8s_hw *hw = pci_get_drvdata(pdev);
+
+	printk(KERN_INFO "HFC-4S/8S: removing card %d\n", hw->cardnum);
+	hfc_hardware_enable(hw, 0, 0);
+
+	if (hw->irq)
+		free_irq(hw->irq, hw);
+	hw->irq = 0;
+	release_pci_ports(hw);
+
+	card_cnt--;
+	pci_disable_device(pdev);
+	kfree(hw);
+	return;
+}
+
+static struct pci_driver hfc4s8s_driver = {
+      name:"hfc4s8s_l1",
+      probe:hfc4s8s_probe,
+      remove:__devexit_p(hfc4s8s_remove),
+      id_table:hfc4s8s_ids,
+};
+
+/**********************/
+/* driver Module init */
+/**********************/
+static int __init
+hfc4s8s_module_init(void)
+{
+	int err;
+
+	printk(KERN_INFO
+	       "HFC-4S/8S: Layer 1 driver module for HFC-4S/8S isdn chips, %s\n",
+	       hfc4s8s_rev);
+	printk(KERN_INFO
+	       "HFC-4S/8S: (C) 2003 Cornelius Consult, www.cornelius-consult.de\n");
+
+	card_cnt = 0;
+
+	err = pci_register_driver(&hfc4s8s_driver);
+	if (err < 0) {
+		goto out;
+	}
+	printk(KERN_INFO "HFC-4S/8S: found %d cards\n", card_cnt);
+
+#if !defined(CONFIG_HOTPLUG)
+	if (err == 0) {
+		err = -ENODEV;
+		pci_unregister_driver(&hfc4s8s_driver);
+		goto out;
+	}
+#endif
+
+	return 0;
+      out:
+	return (err);
+}				/* hfc4s8s_init_hw */
+
+/*************************************/
+/* driver module exit :              */
+/* release the HFC-4s/8s hardware    */
+/*************************************/
+static void
+hfc4s8s_module_exit(void)
+{
+	pci_unregister_driver(&hfc4s8s_driver);
+	printk(KERN_INFO "HFC-4S/8S: module removed\n");
+}				/* hfc4s8s_release_hw */
+
+module_init(hfc4s8s_module_init);
+module_exit(hfc4s8s_module_exit);
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.h b/drivers/isdn/hisax/hfc4s8s_l1.h
new file mode 100644
index 0000000..e8f9c07
--- /dev/null
+++ b/drivers/isdn/hisax/hfc4s8s_l1.h
@@ -0,0 +1,88 @@
+/***************************************************************/
+/*  $Id: hfc4s8s_l1.h,v 1.1 2005/02/02 17:28:55 martinb1 Exp $ */
+/*                                                             */
+/*  This file is a minimal required extraction of hfc48scu.h   */
+/*  (Genero 3.2, HFC XML 1.7a for HFC-E1, HFC-4S and HFC-8S)   */
+/*                                                             */
+/*  To get this complete register description contact          */
+/*  Cologne Chip AG :                                          */
+/*  Internet:  http://www.colognechip.com/                     */
+/*  E-Mail:    info@colognechip.com                            */
+/***************************************************************/
+
+#ifndef _HFC4S8S_L1_H_
+#define _HFC4S8S_L1_H_
+
+
+/*
+*  include Genero generated HFC-4S/8S header file hfc48scu.h
+*  for comlete register description. This will define _HFC48SCU_H_
+*  to prevent redefinitions
+*/
+
+// #include "hfc48scu.h"
+
+#ifndef _HFC48SCU_H_
+#define _HFC48SCU_H_
+
+#ifndef PCI_VENDOR_ID_CCD
+#define PCI_VENDOR_ID_CCD	0x1397
+#endif
+
+#define CHIP_ID_4S		0x0C
+#define CHIP_ID_8S		0x08
+#define PCI_DEVICE_ID_4S	0x08B4
+#define PCI_DEVICE_ID_8S	0x16B8
+
+#define R_IRQ_MISC	0x11
+#define M_TI_IRQ	0x02
+#define A_ST_RD_STA	0x30
+#define A_ST_WR_STA	0x30
+#define M_SET_G2_G3	0x80
+#define A_ST_CTRL0	0x31
+#define A_ST_CTRL2	0x33
+#define A_ST_CLK_DLY	0x37
+#define A_Z1		0x04
+#define A_Z2		0x06
+#define R_CIRM		0x00
+#define M_SRES		0x08
+#define R_CTRL		0x01
+#define R_BRG_PCM_CFG	0x02
+#define M_PCM_CLK	0x20
+#define R_RAM_MISC	0x0C
+#define M_FZ_MD		0x80
+#define R_FIFO_MD	0x0D
+#define A_INC_RES_FIFO	0x0E
+#define R_FIFO		0x0F
+#define A_F1		0x0C
+#define A_F2		0x0D
+#define R_IRQ_OVIEW	0x10
+#define R_CHIP_ID	0x16
+#define R_STATUS	0x1C
+#define M_BUSY		0x01
+#define M_MISC_IRQSTA	0x40
+#define M_FR_IRQSTA	0x80
+#define R_CHIP_RV	0x1F
+#define R_IRQ_CTRL	0x13
+#define M_FIFO_IRQ	0x01
+#define M_GLOB_IRQ_EN	0x08
+#define R_PCM_MD0	0x14
+#define M_PCM_MD	0x01
+#define A_FIFO_DATA0	0x80
+#define R_TI_WD		0x1A
+#define R_PWM1		0x39
+#define R_PWM_MD	0x46
+#define R_IRQ_FIFO_BL0	0xC8
+#define A_CON_HDLC	0xFA
+#define A_SUBCH_CFG	0xFB
+#define A_IRQ_MSK	0xFF
+#define R_SCI_MSK	0x12
+#define R_ST_SEL	0x16
+#define R_ST_SYNC	0x17
+#define M_AUTO_SYNC	0x08
+#define R_SCI		0x12
+#define R_IRQMSK_MISC	0x11
+#define M_TI_IRQMSK	0x02
+
+#endif	/* _HFC4S8S_L1_H_ */
+#endif	/* _HFC48SCU_H_ */
diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c
new file mode 100644
index 0000000..ebea3fe
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bds0.c
@@ -0,0 +1,1082 @@
+/* $Id: hfc_2bds0.c,v 1.18.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * specific routines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+/*
+#define KDEBUG_DEF
+#include "kdebug.h"
+*/
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static void
+dummyf(struct IsdnCardState *cs, u_char * data, int size)
+{
+	printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n");
+}
+
+static inline u_char
+ReadReg(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		if (cs->hw.hfcD.cip != reg) { 
+			cs->hw.hfcD.cip = reg;
+			byteout(cs->hw.hfcD.addr | 1, reg);
+		}
+		ret = bytein(cs->hw.hfcD.addr);
+#ifdef HFC_REG_DEBUG
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "t3c RD %02x %02x", reg, ret);
+#endif
+	} else
+		ret = bytein(cs->hw.hfcD.addr | 1);
+	return (ret);
+}
+
+static inline void
+WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	if (cs->hw.hfcD.cip != reg) { 
+		cs->hw.hfcD.cip = reg;
+		byteout(cs->hw.hfcD.addr | 1, reg);
+	}
+	if (data)
+		byteout(cs->hw.hfcD.addr, value);
+#ifdef HFC_REG_DEBUG
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB))
+		debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
+#endif
+}
+
+/* Interface functions */
+
+static u_char
+readreghfcd(struct IsdnCardState *cs, u_char offset)
+{
+	return(ReadReg(cs, HFCD_DATA, offset));
+}
+
+static void
+writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	WriteReg(cs, HFCD_DATA, offset, value);
+}
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+
+	while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: WaitForBusy timeout\n");
+	return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+
+	while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to) 
+		printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n");
+	return (to);
+}
+
+static int
+SelFiFo(struct IsdnCardState *cs, u_char FiFo)
+{
+	u_char cip;
+
+	if (cs->hw.hfcD.fifo == FiFo)
+		return(1);
+	switch(FiFo) {
+		case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1;
+			break;
+		case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1;
+			break;
+		case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2;
+			break;
+		case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2;
+			break;
+		case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND;
+			break;
+		case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC;
+			break;
+		default:
+			debugl1(cs, "SelFiFo Error");
+			return(0);
+	}
+	cs->hw.hfcD.fifo = FiFo;
+	WaitNoBusy(cs);
+	cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0);
+	WaitForBusy(cs);
+	return(2);
+}
+
+static int
+GetFreeFifoBytes_B(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfcD.bfifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfcD.bfifosize;
+	s = bcs->cs->hw.hfcD.bfifosize - s;
+	return (s);
+}
+
+static int
+GetFreeFifoBytes_D(struct IsdnCardState *cs)
+{
+	int s;
+
+	if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2)
+		return (cs->hw.hfcD.dfifosize);
+	s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2];
+	if (s <= 0)
+		s += cs->hw.hfcD.dfifosize;
+	s = cs->hw.hfcD.dfifosize - s;
+	return (s);
+}
+
+static int
+ReadZReg(struct IsdnCardState *cs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(cs);
+	val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH);
+	WaitNoBusy(cs);
+	val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW);
+	return (val);
+}
+
+static struct sk_buff
+*hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	u_char stat, cip;
+	
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		while (idx++ < count) {
+			WaitNoBusy(cs);
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+		}
+		skb = NULL;
+	} else if (count < 4) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
+		while ((idx++ < count) && WaitNoBusy(cs))
+			ReadReg(cs, HFCD_DATA_NODEB, cip);
+		skb = NULL;
+	} else if (!(skb = dev_alloc_skb(count - 3)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		ptr = skb_put(skb, count - 3);
+		idx = 0;
+		cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+		while (idx < (count - 3)) {
+			if (!WaitNoBusy(cs))
+				break;
+			*ptr = ReadReg(cs,  HFCD_DATA_NODEB, cip);
+			ptr++;
+			idx++;
+		}
+		if (idx != count - 3) {
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb_irq(skb);
+			skb = NULL;
+		} else {
+			WaitNoBusy(cs);
+			chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+			WaitNoBusy(cs);
+			chksum += ReadReg(cs, HFCD_DATA, cip);
+			WaitNoBusy(cs);
+			stat = ReadReg(cs, HFCD_DATA, cip);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+					bcs->channel, chksum, stat);
+			if (stat) {
+				debugl1(cs, "FIFO CRC error");
+				dev_kfree_skb_irq(skb);
+				skb = NULL;
+#ifdef ERROR_STATISTIC
+				bcs->err_crc++;
+#endif
+			}
+		}
+	}
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC |
+		HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	WaitForBusy(cs);
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int idx, fcnt;
+	int count;
+	u_char cip;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+	SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel)); 
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip);
+	WaitNoBusy(cs);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip);
+	bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+ 	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+			bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+			bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+	fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	if (fcnt < 0)
+		fcnt += 32;
+	if (fcnt > 30) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo more as 30 frames");
+		return;
+	}
+	count = GetFreeFifoBytes_B(bcs);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d),%lx",
+			bcs->channel, bcs->tx_skb->len,
+			count, current->state);
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		return;
+	}
+	cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+	idx = 0;
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	while (idx < bcs->tx_skb->len) {
+		if (!WaitNoBusy(cs))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx]);
+		idx++;
+	}
+	if (idx != bcs->tx_skb->len) {
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		bcs->tx_cnt -= bcs->tx_skb->len;
+		if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+			(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+			u_long	flags;
+			spin_lock_irqsave(&bcs->aclock, flags);
+			bcs->ackcnt += bcs->tx_skb->len;
+			spin_unlock_irqrestore(&bcs->aclock, flags);
+			schedule_event(bcs, B_ACKPENDING);
+		}
+		dev_kfree_skb_any(bcs->tx_skb);
+		bcs->tx_skb = NULL;
+	}
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+	WaitForBusy(cs);
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	return;
+}
+
+static void
+hfc_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		hfc_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs,"send_data %d blocked", bcs->channel);
+}
+
+void
+main_rec_2bds0(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, count = 5;
+	struct sk_buff *skb;
+
+    Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs,"rec_data %d blocked", bcs->channel);
+		return;
+	}
+	SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel));
+	cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f1 = ReadReg(cs, HFCD_DATA, cip);
+	cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = ReadReg(cs, HFCD_DATA, cip);
+	if (f1 != f2) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+		z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.bfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+		if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+			skb_queue_tail(&bcs->rqueue, skb);
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+		rcnt = f1 -f2;
+		if (rcnt<0)
+			rcnt += 32;
+		if (rcnt>1)
+			receive = 1;
+		else
+			receive = 0;
+	} else
+		receive = 0;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;	
+	return;
+}
+
+void
+mode_2bs0(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCD bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcD.conn |= 0x18;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.conn |= 0x3;
+				cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfcD.ctmt |= 2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt |= 1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfcD.ctmt &= ~2;
+				cs->hw.hfcD.conn &= ~0x18;
+				cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcD.ctmt &= ~1;
+				cs->hw.hfcD.conn &= ~0x3;
+				cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+			}
+			break;
+	}
+	WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+			} else {
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_2bs0(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_2bs0(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_2bs0(struct BCState *bcs)
+{
+	mode_2bs0(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+static void
+hfcd_bh(struct IsdnCardState *cs)
+{
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		switch (cs->dc.hfcd.ph_state) {
+			case (0):
+				l1_msg(cs, HW_RESET | INDICATION, NULL);
+				break;
+			case (3):
+				l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+				break;
+			case (8):
+				l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+				break;
+			case (6):
+				l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+				break;
+			case (7):
+				l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+				break;
+			default:
+				break;
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+static
+int receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	int idx;
+	int rcnt, z1, z2;
+	u_char stat, cip, f1, f2;
+	int chksum;
+	int count=5;
+	u_char *ptr;
+
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		return(1);
+	}
+	SelFiFo(cs, 4 | HFCD_REC);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_REC;
+	WaitNoBusy(cs);
+	f1 = cs->readisac(cs, cip) & 0xf;
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+	WaitNoBusy(cs);
+	f2 = cs->readisac(cs, cip) & 0xf;
+	while ((f1 != f2) && count--) {
+		z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC);
+		z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC);
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfcD.dfifosize;
+		rcnt++;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+				f1, f2, z1, z2, rcnt);
+		idx = 0;
+		cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC;
+		if (rcnt > MAX_DFRAME_LEN + 3) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too large");
+			while (idx < rcnt) {
+				if (!(WaitNoBusy(cs)))
+					break;
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+				idx++;
+			}
+		} else if (rcnt < 4) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo d: incoming packet too small");
+			while ((idx++ < rcnt) && WaitNoBusy(cs))
+				ReadReg(cs, HFCD_DATA_NODEB, cip);
+		} else if ((skb = dev_alloc_skb(rcnt - 3))) {
+			ptr = skb_put(skb, rcnt - 3);
+			while (idx < (rcnt - 3)) {
+				if (!(WaitNoBusy(cs)))
+					break;
+				*ptr = ReadReg(cs, HFCD_DATA_NODEB, cip);
+				idx++;
+				ptr++;
+			}
+			if (idx != (rcnt - 3)) {
+				debugl1(cs, "RFIFO D BUSY error");
+				printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n");
+				dev_kfree_skb_irq(skb);
+				skb = NULL;
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			} else {
+				WaitNoBusy(cs);
+				chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+				WaitNoBusy(cs);
+				chksum += ReadReg(cs, HFCD_DATA, cip);
+				WaitNoBusy(cs);
+				stat = ReadReg(cs, HFCD_DATA, cip);
+				if (cs->debug & L1_DEB_ISAC)
+					debugl1(cs, "empty_dfifo chksum %x stat %x",
+						chksum, stat);
+				if (stat) {
+					debugl1(cs, "FIFO CRC error");
+					dev_kfree_skb_irq(skb);
+					skb = NULL;
+#ifdef ERROR_STATISTIC
+					cs->err_crc++;
+#endif
+				} else {
+					skb_queue_tail(&cs->rq, skb);
+					schedule_event(cs, D_RCVBUFREADY);
+				}
+			}
+		} else
+			printk(KERN_WARNING "HFC: D receive out of memory\n");
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC;
+		WaitNoBusy(cs);
+		stat = ReadReg(cs, HFCD_DATA, cip);
+		WaitForBusy(cs);
+		cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+		WaitNoBusy(cs);
+		f2 = cs->readisac(cs, cip) & 0xf;
+	}
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return(1);
+} 
+
+static void
+hfc_fill_dfifo(struct IsdnCardState *cs)
+{
+	int idx, fcnt;
+	int count;
+	u_char cip;
+
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	SelFiFo(cs, 4 | HFCD_SEND);
+	cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND;
+	WaitNoBusy(cs);
+	cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	WaitNoBusy(cs);
+	cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND;
+	cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+	cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND);
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
+			cs->hw.hfcD.f1, cs->hw.hfcD.f2,
+			cs->hw.hfcD.send[cs->hw.hfcD.f1]);
+	fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2;
+	if (fcnt < 0)
+		fcnt += 16;
+	if (fcnt > 14) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_Dfifo more as 14 frames");
+		return;
+	}
+	count = GetFreeFifoBytes_D(cs);
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfc_fill_Dfifo count(%ld/%d)",
+			cs->tx_skb->len, count);
+	if (count < cs->tx_skb->len) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfc_fill_Dfifo no fifo mem");
+		return;
+	}
+	cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND;
+	idx = 0;
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]);
+	while (idx < cs->tx_skb->len) {
+		if (!(WaitNoBusy(cs)))
+			break;
+		WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]);
+		idx++;
+	}
+	if (idx != cs->tx_skb->len) {
+		debugl1(cs, "DFIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n");
+	}
+	WaitForBusy(cs);
+	WaitNoBusy(cs);
+	ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND);
+	dev_kfree_skb_any(cs->tx_skb);
+	cs->tx_skb = NULL;
+	WaitForBusy(cs);
+	return;
+}
+
+static 
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return(&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return(&cs->bcs[1]);
+	else
+		return(NULL);
+}
+
+void
+hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val)
+{
+       	u_char exval;
+       	struct BCState *bcs;
+	int count=15;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCD irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcD.int_m1;
+	if (val & 0x40) { /* TE state machine irq */
+		exval = cs->readisac(cs, HFCD_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcd.ph_state,
+				exval);
+		cs->dc.hfcd.ph_state = exval;
+		schedule_event(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	while (val) {
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcD.int_s1 |= val;
+			return;
+		}
+		if (cs->hw.hfcD.int_s1 & 0x18) {
+			exval = val;
+			val =  cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = exval;
+		}	
+		if (val & 0x08) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x08 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x10) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x10 IRQ");
+			} else 
+				main_rec_2bds0(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs=Sel_BCS(cs, 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs=Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcd spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs,"fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfc_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs,"fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfc_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfc_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb_irq(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfc_fill_dfifo irq blocked");
+				}
+			} else
+				schedule_event(cs, D_XMTBUFREADY);
+		}
+      afterXPR:
+		if (cs->hw.hfcD.int_s1 && count--) {
+			val = cs->hw.hfcD.int_s1;
+			cs->hw.hfcD.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFCD irq %x loop %d", val, 15-count);
+		} else
+			val = 0;
+	}
+}
+
+static void
+HFCD_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfc_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfc_fill_dfifo blocked");
+
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfc_fill_dfifo(cs);
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfc_fill_dfifo blocked");
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */
+			udelay(6);
+			cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcD.mst_m &= ~HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcD.mst_m |= HFCD_MASTER;
+			cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcd_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+void
+setstack_hfcd(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCD_l1hw;
+}
+
+static void
+hfc_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+unsigned int __init
+*init_send_hfcd(int cnt)
+{
+	int i, *send;
+
+	if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfcd.send\n");
+		return(NULL);
+	}
+	for (i = 0; i < cnt; i++)
+		send[i] = 0x1fff;
+	return(send);
+}
+
+void __init
+init2bds0(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_hfcd;
+	if (!cs->hw.hfcD.send)
+		cs->hw.hfcD.send = init_send_hfcd(16);
+	if (!cs->bcs[0].hw.hfc.send)
+		cs->bcs[0].hw.hfc.send = init_send_hfcd(32);
+	if (!cs->bcs[1].hw.hfc.send)
+		cs->bcs[1].hw.hfc.send = init_send_hfcd(32);
+	cs->BC_Send_Data = &hfc_send_data;
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_2bs0;
+	cs->bcs[1].BC_Close = close_2bs0;
+	mode_2bs0(cs->bcs, 0, 0);
+	mode_2bs0(cs->bcs + 1, 0, 1);
+}
+
+void
+release2bds0(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+	if (cs->hw.hfcD.send) {
+		kfree(cs->hw.hfcD.send);
+		cs->hw.hfcD.send = NULL;
+	}
+}
+
+void
+set_cs_func(struct IsdnCardState *cs)
+{
+	cs->readisac = &readreghfcd;
+	cs->writeisac = &writereghfcd;
+	cs->readisacfifo = &dummyf;
+	cs->writeisacfifo = &dummyf;
+	cs->BC_Read_Reg = &ReadReg;
+	cs->BC_Write_Reg = &WriteReg;
+	cs->dbusytimer.function = (void *) hfc_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	INIT_WORK(&cs->tqueue, (void *)(void *) hfcd_bh, cs);
+}
diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h
new file mode 100644
index 0000000..30f1924
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bds0.h
@@ -0,0 +1,128 @@
+/* $Id: hfc_2bds0.h,v 1.6.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define HFCD_CIRM  	0x18
+#define HFCD_CTMT	0x19
+#define HFCD_INT_M1  	0x1A
+#define HFCD_INT_M2  	0x1B
+#define HFCD_INT_S1  	0x1E
+#define HFCD_STAT  	0x1C
+#define HFCD_STAT_DISB  0x1D
+#define HFCD_STATES  	0x30
+#define HFCD_SCTRL  	0x31
+#define HFCD_TEST  	0x32
+#define HFCD_SQ  	0x34
+#define HFCD_CLKDEL  	0x37
+#define HFCD_MST_MODE	0x2E
+#define HFCD_CONN  	0x2F
+
+#define HFCD_FIFO	0x80
+#define HFCD_Z1		0x10
+#define HFCD_Z2		0x18
+#define HFCD_Z_LOW	0x00
+#define HFCD_Z_HIGH	0x04
+#define HFCD_F1_INC	0x12
+#define HFCD_FIFO_IN	0x16
+#define HFCD_F1		0x1a
+#define HFCD_F2		0x1e
+#define HFCD_F2_INC	0x22
+#define HFCD_FIFO_OUT	0x26
+#define HFCD_REC	0x01
+#define HFCD_SEND	0x00
+
+#define HFCB_FIFO	0x80
+#define HFCB_Z1		0x00
+#define HFCB_Z2		0x08
+#define HFCB_Z_LOW	0x00
+#define HFCB_Z_HIGH	0x04
+#define HFCB_F1_INC	0x28
+#define HFCB_FIFO_IN	0x2c
+#define HFCB_F1		0x30
+#define HFCB_F2		0x34
+#define HFCB_F2_INC	0x38
+#define HFCB_FIFO_OUT	0x3c
+#define HFCB_REC	0x01
+#define HFCB_SEND	0x00
+#define HFCB_B1		0x00
+#define HFCB_B2		0x02
+#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1)
+
+#define HFCD_STATUS	0
+#define HFCD_DATA	1
+#define HFCD_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFCD_BUSY	0x01
+#define HFCD_BUSY_NBUSY	0x04
+#define HFCD_TIMER_ELAP	0x10
+#define HFCD_STATINT	0x20
+#define HFCD_FRAMEINT	0x40
+#define HFCD_ANYINT	0x80
+
+/* CTMT (Write) */
+#define HFCD_CLTIMER 0x80
+#define HFCD_TIM25  0x00
+#define HFCD_TIM50  0x08
+#define HFCD_TIM400 0x10
+#define HFCD_TIM800 0x18
+#define HFCD_AUTO_TIMER 0x20
+#define HFCD_TRANSB2 0x02
+#define HFCD_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFCD_RESET  	0x08
+#define HFCD_MEM8K	0x10
+#define HFCD_INTA	0x01
+#define HFCD_INTB	0x02
+#define HFCD_INTC	0x03
+#define HFCD_INTD	0x04
+#define HFCD_INTE	0x05
+#define HFCD_INTF	0x06
+
+/* INT_M1;INT_S1 */
+#define HFCD_INTS_B1TRANS	0x01
+#define HFCD_INTS_B2TRANS	0x02
+#define HFCD_INTS_DTRANS	0x04
+#define HFCD_INTS_B1REC		0x08
+#define HFCD_INTS_B2REC		0x10
+#define HFCD_INTS_DREC		0x20
+#define HFCD_INTS_L1STATE	0x40
+#define HFCD_INTS_TIMER		0x80
+
+/* INT_M2 */
+#define HFCD_IRQ_ENABLE		0x08
+
+/* STATES */
+#define HFCD_LOAD_STATE		0x10
+#define HFCD_ACTIVATE		0x20
+#define HFCD_DO_ACTION		0x40
+
+/* HFCD_MST_MODE */
+#define HFCD_MASTER		0x01
+
+/* HFCD_SCTRL */
+#define SCTRL_B1_ENA		0x01
+#define SCTRL_B2_ENA		0x02
+#define SCTRL_LOW_PRIO		0x08
+#define SCTRL_SQ_ENA		0x10
+#define SCTRL_TEST		0x20
+#define SCTRL_NONE_CAP		0x40
+#define SCTRL_PWR_DOWN		0x80
+
+/* HFCD_TEST */
+#define HFCD_AUTO_AWAKE		0x01
+
+extern void main_irq_2bds0(struct BCState *bcs);
+extern void init2bds0(struct IsdnCardState *cs);
+extern void release2bds0(struct IsdnCardState *cs);
+extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val);
+extern void set_cs_func(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c
new file mode 100644
index 0000000..bb376f3
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bs0.c
@@ -0,0 +1,593 @@
+/* $Id: hfc_2bs0.c,v 1.20.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * specific routines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_2bs0.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+	int to = 130;
+	u_char val;
+
+	while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 |
+				      (cs->hw.hfc.cip & 3));
+		udelay(1);
+		to--;
+	}
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+	int to = 125;
+
+	while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to) {
+		printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+		return (0);
+	} else
+		return (to);
+}
+
+int
+GetFreeFifoBytes(struct BCState *bcs)
+{
+	int s;
+
+	if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+		return (bcs->cs->hw.hfc.fifosize);
+	s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+	if (s <= 0)
+		s += bcs->cs->hw.hfc.fifosize;
+	s = bcs->cs->hw.hfc.fifosize - s;
+	return (s);
+}
+
+int
+ReadZReg(struct BCState *bcs, u_char reg)
+{
+	int val;
+
+	WaitNoBusy(bcs->cs);
+	val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH);
+	WaitNoBusy(bcs->cs);
+	val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW);
+	return (val);
+}
+
+static void
+hfc_clear_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int idx, cnt;
+	int rcnt, z1, z2;
+	u_char cip, f1, f2;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_clear_fifo");
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	WaitNoBusy(cs);
+	f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	cnt = 32;
+	while (((f1 != f2) || (z1 != z2)) && cnt--) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d f1(%d) f2(%d)",
+				bcs->channel, f1, f2);
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		if (rcnt)
+			rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, z1, z2, rcnt);
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		idx = 0;
+		while ((idx < rcnt) && WaitNoBusy(cs)) {
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (f1 != f2) {
+			WaitNoBusy(cs);
+			cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					HFC_CHANNEL(bcs->channel));
+			WaitForBusy(cs);
+		}
+		cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	}
+	return;
+}
+
+
+static struct sk_buff
+*
+hfc_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int idx;
+	int chksum;
+	u_char stat, cip;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfc_empty_fifo");
+	idx = 0;
+	if (count > HSCX_BUFMAX + 3) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+		return (NULL);
+	}
+	if ((count < 4) && (bcs->mode != L1_MODE_TRANS)) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx++ < count) && WaitNoBusy(cs))
+			cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+		WaitNoBusy(cs);
+		stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+				       HFC_CHANNEL(bcs->channel));
+		WaitForBusy(cs);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
+		return (NULL);
+	}
+	if (bcs->mode == L1_MODE_TRANS)
+	  count -= 1;
+	else
+	  count -= 3;
+	if (!(skb = dev_alloc_skb(count)))
+		printk(KERN_WARNING "HFC: receive out of memory\n");
+	else {
+		ptr = skb_put(skb, count);
+		idx = 0;
+		cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+		while ((idx < count) && WaitNoBusy(cs)) {
+			*ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+			idx++;
+		}
+		if (idx != count) {
+			debugl1(cs, "RFIFO BUSY error");
+			printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+			dev_kfree_skb_any(skb);
+			if (bcs->mode != L1_MODE_TRANS) {
+			  WaitNoBusy(cs);
+			  stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+						 HFC_CHANNEL(bcs->channel));
+			  WaitForBusy(cs);
+			}
+			return (NULL);
+		}
+		if (bcs->mode != L1_MODE_TRANS) {
+		  WaitNoBusy(cs);
+		  chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
+		  WaitNoBusy(cs);
+		  chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		  WaitNoBusy(cs);
+		  stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		  if (cs->debug & L1_DEB_HSCX)
+		    debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+			    bcs->channel, chksum, stat);
+		  if (stat) {
+		    debugl1(cs, "FIFO CRC error");
+		    dev_kfree_skb_any(skb);
+		    skb = NULL;
+#ifdef ERROR_STATISTIC
+		    bcs->err_crc++;
+#endif
+		  }
+		  WaitNoBusy(cs);
+		  stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+					 HFC_CHANNEL(bcs->channel));
+		  WaitForBusy(cs);
+		}
+	}
+	return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int idx, fcnt;
+	int count;
+	int z1, z2;
+	u_char cip;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+	  cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+	  WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	if (bcs->mode != L1_MODE_TRANS) {
+	  bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	  cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	  WaitNoBusy(cs);
+	  bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+	  bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
+	  if (cs->debug & L1_DEB_HSCX)
+	    debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+		    bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+		    bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+	  fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+	  if (fcnt < 0)
+	    fcnt += 32;
+	  if (fcnt > 30) {
+	    if (cs->debug & L1_DEB_HSCX)
+	      debugl1(cs, "hfc_fill_fifo more as 30 frames");
+	    return;
+	  }
+	  count = GetFreeFifoBytes(bcs);
+	} 
+	else {
+	  WaitForBusy(cs);
+	  z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	  z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+	  count = z1 - z2;
+	  if (count < 0)
+	    count += cs->hw.hfc.fifosize; 
+	} /* L1_MODE_TRANS */
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)",
+			bcs->channel, bcs->tx_skb->len,
+			count);
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfc_fill_fifo no fifo mem");
+		return;
+	}
+	cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
+	idx = 0;
+	while ((idx < bcs->tx_skb->len) && WaitNoBusy(cs))
+		cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+	if (idx != bcs->tx_skb->len) {
+		debugl1(cs, "FIFO Send BUSY error");
+		printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+	} else {
+		count =  bcs->tx_skb->len;
+		bcs->tx_cnt -= count;
+		if (PACKET_NOACK == bcs->tx_skb->pkt_type)
+			count = -1;
+		dev_kfree_skb_any(bcs->tx_skb);
+		bcs->tx_skb = NULL;
+		if (bcs->mode != L1_MODE_TRANS) {
+		  WaitForBusy(cs);
+		  WaitNoBusy(cs);
+		  cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
+		}
+		if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+			(count >= 0)) {
+			u_long	flags;
+			spin_lock_irqsave(&bcs->aclock, flags);
+			bcs->ackcnt += count;
+			spin_unlock_irqrestore(&bcs->aclock, flags);
+			schedule_event(bcs, B_ACKPENDING);
+		}
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+	return;
+}
+
+void
+main_irq_hfc(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int z1, z2, rcnt;
+	u_char f1, f2, cip;
+	int receive, transmit, count = 5;
+	struct sk_buff *skb;
+
+      Begin:
+	count--;
+	cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+	if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+		cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+		WaitForBusy(cs);
+	}
+	WaitNoBusy(cs);
+	receive = 0;
+	if (bcs->mode == L1_MODE_HDLC) {
+		f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+		WaitNoBusy(cs);
+		f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+		if (f1 != f2) {
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+					bcs->channel, f1, f2);
+			receive = 1; 
+		}
+	}
+	if (receive || (bcs->mode == L1_MODE_TRANS)) {
+		WaitForBusy(cs);
+		z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+		rcnt = z1 - z2;
+		if (rcnt < 0)
+			rcnt += cs->hw.hfc.fifosize;
+		if ((bcs->mode == L1_MODE_HDLC) || (rcnt)) {
+			rcnt++;
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+					bcs->channel, z1, z2, rcnt);
+			/*              sti(); */
+			if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+				skb_queue_tail(&bcs->rqueue, skb);
+				schedule_event(bcs, B_RCVBUFREADY);
+			}
+		}
+		receive = 1;
+	}
+	if (bcs->tx_skb) {
+		transmit = 1;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		hfc_fill_fifo(bcs);
+		if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+			transmit = 0;
+	} else {
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			transmit = 1;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hfc_fill_fifo(bcs);
+			if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+				transmit = 0;
+		} else {
+			transmit = 0;
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+	if ((receive || transmit) && count)
+		goto Begin;
+	return;
+}
+
+void
+mode_hfc(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+		        if (bc) {
+				cs->hw.hfc.ctmt &= ~1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+			}
+			else {
+				cs->hw.hfc.ctmt &= ~2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+			}
+			break;
+		case (L1_MODE_TRANS):
+		        cs->hw.hfc.ctmt &= ~(1 << bc); /* set HDLC mode */ 
+			cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+			hfc_clear_fifo(bcs); /* complete fifo clear */ 
+			if (bc) {
+				cs->hw.hfc.ctmt |= 1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt |= 2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfc.ctmt &= ~1;
+				cs->hw.hfc.isac_spcr &= ~0x03;
+				cs->hw.hfc.isac_spcr |= 0x02;
+			} else {
+				cs->hw.hfc.ctmt &= ~2;
+				cs->hw.hfc.isac_spcr &= ~0x0c;
+				cs->hw.hfc.isac_spcr |= 0x08;
+			}
+			break;
+	}
+	cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+	cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr);
+	if (mode == L1_MODE_HDLC)
+		hfc_clear_fifo(bcs);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState	*bcs = st->l1.bcs;
+	struct sk_buff	*skb = arg;
+	u_long		flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_hfc(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_hfc(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+
+void
+close_hfcstate(struct BCState *bcs)
+{
+	mode_hfc(bcs, 0, bcs->channel);
+	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+	test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hfc(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfc_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void __init
+init_send(struct BCState *bcs)
+{
+	int i;
+
+	if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for hfc.send\n");
+		return;
+	}
+	for (i = 0; i < 32; i++)
+		bcs->hw.hfc.send[i] = 0x1fff;
+}
+
+void __init
+inithfc(struct IsdnCardState *cs)
+{
+	init_send(&cs->bcs[0]);
+	init_send(&cs->bcs[1]);
+	cs->BC_Send_Data = &hfc_fill_fifo;
+	cs->bcs[0].BC_SetStack = setstack_hfc;
+	cs->bcs[1].BC_SetStack = setstack_hfc;
+	cs->bcs[0].BC_Close = close_hfcstate;
+	cs->bcs[1].BC_Close = close_hfcstate;
+	mode_hfc(cs->bcs, 0, 0);
+	mode_hfc(cs->bcs + 1, 0, 0);
+}
+
+void
+releasehfc(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.hfc.send) {
+		kfree(cs->bcs[0].hw.hfc.send);
+		cs->bcs[0].hw.hfc.send = NULL;
+	}
+	if (cs->bcs[1].hw.hfc.send) {
+		kfree(cs->bcs[1].hw.hfc.send);
+		cs->bcs[1].hw.hfc.send = NULL;
+	}
+}
diff --git a/drivers/isdn/hisax/hfc_2bs0.h b/drivers/isdn/hisax/hfc_2bs0.h
new file mode 100644
index 0000000..1a50d4a
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bs0.h
@@ -0,0 +1,60 @@
+/* $Id: hfc_2bs0.h,v 1.5.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BS0
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define HFC_CTMT	0xe0
+#define HFC_CIRM  	0xc0
+#define HFC_CIP		0x80
+#define HFC_Z1		0x00
+#define HFC_Z2		0x08
+#define HFC_Z_LOW	0x00
+#define HFC_Z_HIGH	0x04
+#define HFC_F1_INC	0x28
+#define HFC_FIFO_IN	0x2c
+#define HFC_F1		0x30
+#define HFC_F2		0x34
+#define HFC_F2_INC	0x38
+#define HFC_FIFO_OUT	0x3c
+#define HFC_B1          0x00
+#define HFC_B2		0x02
+#define HFC_REC		0x01
+#define HFC_SEND	0x00
+#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1)
+
+#define HFC_STATUS	0
+#define HFC_DATA	1
+#define HFC_DATA_NODEB	2
+
+/* Status (READ) */
+#define HFC_BUSY	0x01
+#define HFC_TIMINT	0x02
+#define HFC_EXTINT	0x04
+
+/* CTMT (Write) */
+#define HFC_CLTIMER 0x10
+#define HFC_TIM50MS 0x08
+#define HFC_TIMIRQE 0x04
+#define HFC_TRANSB2 0x02
+#define HFC_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFC_RESET  	0x08
+#define HFC_MEM8K	0x10
+#define HFC_INTA	0x01
+#define HFC_INTB	0x02
+#define HFC_INTC	0x03
+#define HFC_INTD	0x04
+#define HFC_INTE	0x05
+#define HFC_INTF	0x06
+
+extern void main_irq_hfc(struct BCState *bcs);
+extern void inithfc(struct IsdnCardState *cs);
+extern void releasehfc(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
new file mode 100644
index 0000000..c2db526
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -0,0 +1,1747 @@
+/* $Id: hfc_pci.c,v 1.48.2.4 2004/02/11 13:21:33 keil Exp $
+ *
+ * low level driver for CCD´s hfc-pci based cards
+ *
+ * Author       Werner Cornelius
+ *              based on existing driver for CCD hfc ISA cards
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ *              by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "hfc_pci.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+
+static const char *hfcpci_revision = "$Revision: 1.48.2.4 $";
+
+/* table entry in the PCI devices list */
+typedef struct {
+	int vendor_id;
+	int device_id;
+	char *vendor_name;
+	char *card_name;
+} PCI_ENTRY;
+
+#define NT_T1_COUNT	20	/* number of 3.125ms interrupts for G2 timeout */
+#define CLKDEL_TE	0x0e	/* CLKDEL in TE mode */
+#define CLKDEL_NT	0x6c	/* CLKDEL in NT mode */
+
+static const PCI_ENTRY id_list[] =
+{
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"},
+	{PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"},
+	{PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"},
+	{PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"},
+	{PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"},
+	{0, 0, NULL, NULL},
+};
+
+
+#ifdef CONFIG_PCI
+
+/******************************************/
+/* free hardware resources used by driver */
+/******************************************/
+void
+release_io_hfcpci(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "HiSax: release hfcpci at %p\n",
+		cs->hw.hfcpci.pci_io);
+	cs->hw.hfcpci.int_m2 = 0;					/* interrupt output off ! */
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);			/* Reset On */
+	mdelay(10);
+	Write_hfc(cs, HFCPCI_CIRM, 0);					/* Reset Off */
+	mdelay(10);
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, 0);	/* disable memory mapped ports + busmaster */
+	del_timer(&cs->hw.hfcpci.timer);
+	kfree(cs->hw.hfcpci.share_start);
+	cs->hw.hfcpci.share_start = NULL;
+	iounmap((void *)cs->hw.hfcpci.pci_io);
+}
+
+/********************************************************************************/
+/* function called to reset the HFC PCI chip. A complete software reset of chip */
+/* and fifos is done.                                                           */
+/********************************************************************************/
+static void
+reset_hfcpci(struct IsdnCardState *cs)
+{
+	pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO);	/* enable memory mapped ports, disable busmaster */
+	cs->hw.hfcpci.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+
+	printk(KERN_INFO "HFC_PCI: resetting card\n");
+	pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER);	/* enable memory ports + busmaster */
+	Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET);	/* Reset On */
+	mdelay(10);
+	Write_hfc(cs, HFCPCI_CIRM, 0);	/* Reset Off */
+	mdelay(10);
+	if (Read_hfc(cs, HFCPCI_STATUS) & 2)
+		printk(KERN_WARNING "HFC-PCI init bit busy\n");
+
+	cs->hw.hfcpci.fifo_en = 0x30;	/* only D fifos enabled */
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+
+	cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK;	/* no echo connect , threshold */
+	Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+
+	Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_TE); /* ST-Bit delay for TE-Mode */
+	cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE;
+	Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);	/* S/T Auto awake */
+	cs->hw.hfcpci.bswapped = 0;	/* no exchange */
+	cs->hw.hfcpci.nt_mode = 0;	/* we are in TE mode */
+	cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
+	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+
+	cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+	    HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
+	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+
+	/* Clear already pending ints */
+	if (Read_hfc(cs, HFCPCI_INT_S1));
+
+	Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2);	/* HFC ST 2 */
+	udelay(10);
+	Write_hfc(cs, HFCPCI_STATES, 2);	/* HFC ST 2 */
+	cs->hw.hfcpci.mst_m = HFCPCI_MASTER;	/* HFC Master Mode */
+
+	Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+	cs->hw.hfcpci.sctrl = 0x40;	/* set tx_lo mode, error in datasheet ! */
+	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+	cs->hw.hfcpci.sctrl_r = 0;
+	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+
+	/* Init GCI/IOM2 in master mode */
+	/* Slots 0 and 1 are set for B-chan 1 and 2 */
+	/* D- and monitor/CI channel are not enabled */
+	/* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */
+	/* STIO2 is used as data input, B1+B2 from IOM->ST */
+	/* ST B-channel send disabled -> continous 1s */
+	/* The IOM slots are always enabled */
+	cs->hw.hfcpci.conn = 0x36;	/* set data flow directions */
+	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+	Write_hfc(cs, HFCPCI_B1_SSL, 0x80);	/* B1-Slot 0 STIO1 out enabled */
+	Write_hfc(cs, HFCPCI_B2_SSL, 0x81);	/* B2-Slot 1 STIO1 out enabled */
+	Write_hfc(cs, HFCPCI_B1_RSL, 0x80);	/* B1-Slot 0 STIO2 in enabled */
+	Write_hfc(cs, HFCPCI_B2_RSL, 0x81);	/* B2-Slot 1 STIO2 in enabled */
+
+	/* Finally enable IRQ output */
+	cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE;
+	Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+	if (Read_hfc(cs, HFCPCI_INT_S1));
+}
+
+/***************************************************/
+/* Timer function called when kernel timer expires */
+/***************************************************/
+static void
+hfcpci_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcpci.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*      WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcpci.ctmt | 0x80);
+   add_timer(&cs->hw.hfcpci.timer);
+ */
+}
+
+
+/*********************************/
+/* schedule a new D-channel task */
+/*********************************/
+static void
+sched_event_D_pci(struct IsdnCardState *cs, int event)
+{
+	test_and_set_bit(event, &cs->event);
+	schedule_work(&cs->tqueue);
+}
+
+/*********************************/
+/* schedule a new b_channel task */
+/*********************************/
+static void
+hfcpci_sched_event(struct BCState *bcs, int event)
+{
+	test_and_set_bit(event, &bcs->event);
+	schedule_work(&bcs->tqueue);
+}
+
+/************************************************/
+/* select a b-channel entry matching and active */
+/************************************************/
+static
+struct BCState *
+Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return (&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return (&cs->bcs[1]);
+	else
+		return (NULL);
+}
+
+/***************************************/
+/* clear the desired B-channel rx fifo */
+/***************************************/
+static void hfcpci_clear_fifo_rx(struct IsdnCardState *cs, int fifo)
+{       u_char fifo_state;
+        bzfifo_type *bzr;
+
+	if (fifo) {
+	        bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B2RX;
+	} else {
+	        bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B1RX;
+	}
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en ^= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	cs->hw.hfcpci.last_bfifo_cnt[fifo] = 0;
+	bzr->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1;
+	bzr->za[MAX_B_FRAMES].z2 = bzr->za[MAX_B_FRAMES].z1;
+	bzr->f1 = MAX_B_FRAMES;
+	bzr->f2 = bzr->f1;	/* init F pointers to remain constant */
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en |= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+}   
+
+/***************************************/
+/* clear the desired B-channel tx fifo */
+/***************************************/
+static void hfcpci_clear_fifo_tx(struct IsdnCardState *cs, int fifo)
+{       u_char fifo_state;
+        bzfifo_type *bzt;
+
+	if (fifo) {
+	        bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B2TX;
+	} else {
+	        bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
+		fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B1TX;
+	}
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en ^= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	bzt->za[MAX_B_FRAMES].z1 = B_FIFO_SIZE + B_SUB_VAL - 1;
+	bzt->za[MAX_B_FRAMES].z2 = bzt->za[MAX_B_FRAMES].z1;
+	bzt->f1 = MAX_B_FRAMES;
+	bzt->f2 = bzt->f1;	/* init F pointers to remain constant */
+	if (fifo_state)
+	        cs->hw.hfcpci.fifo_en |= fifo_state;
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+}   
+
+/*********************************************/
+/* read a complete B-frame out of the buffer */
+/*********************************************/
+static struct sk_buff
+*
+hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int count)
+{
+	u_char *ptr, *ptr1, new_f2;
+	struct sk_buff *skb;
+	struct IsdnCardState *cs = bcs->cs;
+	int total, maxlen, new_z2;
+	z_type *zp;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hfcpci_empty_fifo");
+	zp = &bz->za[bz->f2];	/* point to Z-Regs */
+	new_z2 = zp->z2 + count;	/* new position in fifo */
+	if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+		new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
+	new_f2 = (bz->f2 + 1) & MAX_B_FRAMES;
+	if ((count > HSCX_BUFMAX + 3) || (count < 4) ||
+	    (*(bdata + (zp->z1 - B_SUB_VAL)))) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count);
+#ifdef ERROR_STATISTIC
+		bcs->err_inv++;
+#endif
+		bz->za[new_f2].z2 = new_z2;
+		bz->f2 = new_f2;	/* next buffer */
+		skb = NULL;
+	} else if (!(skb = dev_alloc_skb(count - 3)))
+		printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+	else {
+		total = count;
+		count -= 3;
+		ptr = skb_put(skb, count);
+
+		if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL)
+			maxlen = count;		/* complete transfer */
+		else
+			maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;	/* maximum */
+
+		ptr1 = bdata + (zp->z2 - B_SUB_VAL);	/* start of data */
+		memcpy(ptr, ptr1, maxlen);	/* copy data */
+		count -= maxlen;
+
+		if (count) {	/* rest remaining */
+			ptr += maxlen;
+			ptr1 = bdata;	/* start of buffer */
+			memcpy(ptr, ptr1, count);	/* rest */
+		}
+		bz->za[new_f2].z2 = new_z2;
+		bz->f2 = new_f2;	/* next buffer */
+
+	}
+	return (skb);
+}
+
+/*******************************/
+/* D-channel receive procedure */
+/*******************************/
+static
+int
+receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	int maxlen;
+	int rcnt, total;
+	int count = 5;
+	u_char *ptr, *ptr1;
+	dfifo_type *df;
+	z_type *zp;
+
+	df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_rx;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		return (1);
+	}
+	while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) {
+		zp = &df->za[df->f2 & D_FREG_MASK];
+		rcnt = zp->z1 - zp->z2;
+		if (rcnt < 0)
+			rcnt += D_FIFO_SIZE;
+		rcnt++;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+				df->f1, df->f2, zp->z1, zp->z2, rcnt);
+
+		if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) ||
+		    (df->data[zp->z1])) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[zp->z1]);
+#ifdef ERROR_STATISTIC
+			cs->err_rx++;
+#endif
+			df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1);	/* next buffer */
+			df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1);
+		} else if ((skb = dev_alloc_skb(rcnt - 3))) {
+			total = rcnt;
+			rcnt -= 3;
+			ptr = skb_put(skb, rcnt);
+
+			if (zp->z2 + rcnt <= D_FIFO_SIZE)
+				maxlen = rcnt;	/* complete transfer */
+			else
+				maxlen = D_FIFO_SIZE - zp->z2;	/* maximum */
+
+			ptr1 = df->data + zp->z2;	/* start of data */
+			memcpy(ptr, ptr1, maxlen);	/* copy data */
+			rcnt -= maxlen;
+
+			if (rcnt) {	/* rest remaining */
+				ptr += maxlen;
+				ptr1 = df->data;	/* start of buffer */
+				memcpy(ptr, ptr1, rcnt);	/* rest */
+			}
+			df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1);	/* next buffer */
+			df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + total) & (D_FIFO_SIZE - 1);
+
+			skb_queue_tail(&cs->rq, skb);
+			sched_event_D_pci(cs, D_RCVBUFREADY);
+		} else
+			printk(KERN_WARNING "HFC-PCI: D receive out of memory\n");
+	}
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return (1);
+}
+
+/*******************************************************************************/
+/* check for transparent receive data and read max one threshold size if avail */
+/*******************************************************************************/
+int
+hfcpci_empty_fifo_trans(struct BCState *bcs, bzfifo_type * bz, u_char * bdata)
+{
+	unsigned short *z1r, *z2r;
+	int new_z2, fcnt, maxlen;
+	struct sk_buff *skb;
+	u_char *ptr, *ptr1;
+
+	z1r = &bz->za[MAX_B_FRAMES].z1;		/* pointer to z reg */
+	z2r = z1r + 1;
+
+	if (!(fcnt = *z1r - *z2r))
+		return (0);	/* no data avail */
+
+	if (fcnt <= 0)
+		fcnt += B_FIFO_SIZE;	/* bytes actually buffered */
+	if (fcnt > HFCPCI_BTRANS_THRESHOLD)
+		fcnt = HFCPCI_BTRANS_THRESHOLD;		/* limit size */
+
+	new_z2 = *z2r + fcnt;	/* new position in fifo */
+	if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+		new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
+
+	if (!(skb = dev_alloc_skb(fcnt)))
+		printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+	else {
+		ptr = skb_put(skb, fcnt);
+		if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
+			maxlen = fcnt;	/* complete transfer */
+		else
+			maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r;	/* maximum */
+
+		ptr1 = bdata + (*z2r - B_SUB_VAL);	/* start of data */
+		memcpy(ptr, ptr1, maxlen);	/* copy data */
+		fcnt -= maxlen;
+
+		if (fcnt) {	/* rest remaining */
+			ptr += maxlen;
+			ptr1 = bdata;	/* start of buffer */
+			memcpy(ptr, ptr1, fcnt);	/* rest */
+		}
+		skb_queue_tail(&bcs->rqueue, skb);
+		hfcpci_sched_event(bcs, B_RCVBUFREADY);
+	}
+
+	*z2r = new_z2;		/* new position */
+	return (1);
+}				/* hfcpci_empty_fifo_trans */
+
+/**********************************/
+/* B-channel main receive routine */
+/**********************************/
+void
+main_rec_hfcpci(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int rcnt, real_fifo;
+	int receive, count = 5;
+	struct sk_buff *skb;
+	bzfifo_type *bz;
+	u_char *bdata;
+	z_type *zp;
+
+
+	if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2;
+		real_fifo = 1;
+	} else {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b1;
+		real_fifo = 0;
+	}
+      Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_data %d blocked", bcs->channel);
+		return;
+	}
+	if (bz->f1 != bz->f2) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci rec %d f1(%d) f2(%d)",
+				bcs->channel, bz->f1, bz->f2);
+		zp = &bz->za[bz->f2];
+
+		rcnt = zp->z1 - zp->z2;
+		if (rcnt < 0)
+			rcnt += B_FIFO_SIZE;
+		rcnt++;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci rec %d z1(%x) z2(%x) cnt(%d)",
+				bcs->channel, zp->z1, zp->z2, rcnt);
+		if ((skb = hfcpci_empty_fifo(bcs, bz, bdata, rcnt))) {
+			skb_queue_tail(&bcs->rqueue, skb);
+			hfcpci_sched_event(bcs, B_RCVBUFREADY);
+		}
+		rcnt = bz->f1 - bz->f2;
+		if (rcnt < 0)
+			rcnt += MAX_B_FRAMES + 1;
+		if (cs->hw.hfcpci.last_bfifo_cnt[real_fifo] > rcnt + 1) {
+		        rcnt = 0;
+			hfcpci_clear_fifo_rx(cs, real_fifo);
+		}
+		cs->hw.hfcpci.last_bfifo_cnt[real_fifo] = rcnt;
+		if (rcnt > 1)
+			receive = 1;
+		else
+			receive = 0;
+	} else if (bcs->mode == L1_MODE_TRANS)
+		receive = hfcpci_empty_fifo_trans(bcs, bz, bdata);
+	else
+		receive = 0;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;
+	return;
+}
+
+/**************************/
+/* D-channel send routine */
+/**************************/
+static void
+hfcpci_fill_dfifo(struct IsdnCardState *cs)
+{
+	int fcnt;
+	int count, new_z1, maxlen;
+	dfifo_type *df;
+	u_char *src, *dst, new_f1;
+
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_tx;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)",
+			df->f1, df->f2,
+			df->za[df->f1 & D_FREG_MASK].z1);
+	fcnt = df->f1 - df->f2;	/* frame count actually buffered */
+	if (fcnt < 0)
+		fcnt += (MAX_D_FRAMES + 1);	/* if wrap around */
+	if (fcnt > (MAX_D_FRAMES - 1)) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames");
+#ifdef ERROR_STATISTIC
+		cs->err_tx++;
+#endif
+		return;
+	}
+	/* now determine free bytes in FIFO buffer */
+	count = df->za[df->f2 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1 - 1;
+	if (count <= 0)
+		count += D_FIFO_SIZE;	/* count now contains available bytes */
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "hfcpci_fill_Dfifo count(%ld/%d)",
+			cs->tx_skb->len, count);
+	if (count < cs->tx_skb->len) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcpci_fill_Dfifo no fifo mem");
+		return;
+	}
+	count = cs->tx_skb->len;	/* get frame len */
+	new_z1 = (df->za[df->f1 & D_FREG_MASK].z1 + count) & (D_FIFO_SIZE - 1);
+	new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1);
+	src = cs->tx_skb->data;	/* source pointer */
+	dst = df->data + df->za[df->f1 & D_FREG_MASK].z1;
+	maxlen = D_FIFO_SIZE - df->za[df->f1 & D_FREG_MASK].z1;		/* end fifo */
+	if (maxlen > count)
+		maxlen = count;	/* limit size */
+	memcpy(dst, src, maxlen);	/* first copy */
+
+	count -= maxlen;	/* remaining bytes */
+	if (count) {
+		dst = df->data;	/* start of buffer */
+		src += maxlen;	/* new position */
+		memcpy(dst, src, count);
+	}
+	df->za[new_f1 & D_FREG_MASK].z1 = new_z1;	/* for next buffer */
+	df->za[df->f1 & D_FREG_MASK].z1 = new_z1;	/* new pos actual buffer */
+	df->f1 = new_f1;	/* next frame */
+
+	dev_kfree_skb_any(cs->tx_skb);
+	cs->tx_skb = NULL;
+	return;
+}
+
+/**************************/
+/* B-channel send routine */
+/**************************/
+static void
+hfcpci_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int maxlen, fcnt;
+	int count, new_z1;
+	bzfifo_type *bz;
+	u_char *bdata;
+	u_char new_f1, *src, *dst;
+	unsigned short *z1t, *z2t;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b2;
+	} else {
+		bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
+		bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b1;
+	}
+
+	if (bcs->mode == L1_MODE_TRANS) {
+		z1t = &bz->za[MAX_B_FRAMES].z1;
+		z2t = z1t + 1;
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)",
+				bcs->channel, *z1t, *z2t);
+		fcnt = *z2t - *z1t;
+		if (fcnt <= 0)
+			fcnt += B_FIFO_SIZE;	/* fcnt contains available bytes in fifo */
+		fcnt = B_FIFO_SIZE - fcnt;	/* remaining bytes to send */
+
+		while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) {
+			if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) {
+				/* data is suitable for fifo */
+				count = bcs->tx_skb->len;
+
+				new_z1 = *z1t + count;	/* new buffer Position */
+				if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+					new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
+				src = bcs->tx_skb->data;	/* source pointer */
+				dst = bdata + (*z1t - B_SUB_VAL);
+				maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t;	/* end of fifo */
+				if (maxlen > count)
+					maxlen = count;		/* limit size */
+				memcpy(dst, src, maxlen);	/* first copy */
+
+				count -= maxlen;	/* remaining bytes */
+				if (count) {
+					dst = bdata;	/* start of buffer */
+					src += maxlen;	/* new position */
+					memcpy(dst, src, count);
+				}
+				bcs->tx_cnt -= bcs->tx_skb->len;
+				fcnt += bcs->tx_skb->len;
+				*z1t = new_z1;	/* now send data */
+			} else if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded",
+					bcs->channel, bcs->tx_skb->len);
+
+			if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+				u_long	flags;
+				spin_lock_irqsave(&bcs->aclock, flags);
+				bcs->ackcnt += bcs->tx_skb->len;
+				spin_unlock_irqrestore(&bcs->aclock, flags);
+				schedule_event(bcs, B_ACKPENDING);
+			}
+
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = skb_dequeue(&bcs->squeue);	/* fetch next data */
+		}
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		return;
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)",
+			bcs->channel, bz->f1, bz->f2,
+			bz->za[bz->f1].z1);
+
+	fcnt = bz->f1 - bz->f2;	/* frame count actually buffered */
+	if (fcnt < 0)
+		fcnt += (MAX_B_FRAMES + 1);	/* if wrap around */
+	if (fcnt > (MAX_B_FRAMES - 1)) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci_fill_Bfifo more as 14 frames");
+		return;
+	}
+	/* now determine free bytes in FIFO buffer */
+	count = bz->za[bz->f2].z2 - bz->za[bz->f1].z1 - 1;
+	if (count <= 0)
+		count += B_FIFO_SIZE;	/* count now contains available bytes */
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hfcpci_fill_fifo %d count(%ld/%d),%lx",
+			bcs->channel, bcs->tx_skb->len,
+			count, current->state);
+
+	if (count < bcs->tx_skb->len) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "hfcpci_fill_fifo no fifo mem");
+		return;
+	}
+	count = bcs->tx_skb->len;	/* get frame len */
+	new_z1 = bz->za[bz->f1].z1 + count;	/* new buffer Position */
+	if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+		new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
+
+	new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES);
+	src = bcs->tx_skb->data;	/* source pointer */
+	dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL);
+	maxlen = (B_FIFO_SIZE + B_SUB_VAL) - bz->za[bz->f1].z1;		/* end fifo */
+	if (maxlen > count)
+		maxlen = count;	/* limit size */
+	memcpy(dst, src, maxlen);	/* first copy */
+
+	count -= maxlen;	/* remaining bytes */
+	if (count) {
+		dst = bdata;	/* start of buffer */
+		src += maxlen;	/* new position */
+		memcpy(dst, src, count);
+	}
+	bcs->tx_cnt -= bcs->tx_skb->len;
+	if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+		(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+		u_long	flags;
+		spin_lock_irqsave(&bcs->aclock, flags);
+		bcs->ackcnt += bcs->tx_skb->len;
+		spin_unlock_irqrestore(&bcs->aclock, flags);
+		schedule_event(bcs, B_ACKPENDING);
+	}
+
+	bz->za[new_f1].z1 = new_z1;	/* for next buffer */
+	bz->f1 = new_f1;	/* next frame */
+
+	dev_kfree_skb_any(bcs->tx_skb);
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	return;
+}
+
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+		case (PH_PULL | REQUEST):
+		case (PH_PULL | INDICATION):
+			st->l1.l1hw(st, pr, arg);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			break;
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+
+
+/***********************/
+/* set/reset echo mode */
+/***********************/
+static int
+hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
+{
+	u_long	flags;
+	int	i = *(unsigned int *) ic->parm.num;
+
+	if ((ic->arg == 98) &&
+	    (!(cs->hw.hfcpci.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) {
+	    	spin_lock_irqsave(&cs->lock, flags);
+		Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_NT); /* ST-Bit delay for NT-Mode */
+		Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0);	/* HFC ST G0 */
+		udelay(10);
+		cs->hw.hfcpci.sctrl |= SCTRL_MODE_NT;
+		Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);	/* set NT-mode */
+		udelay(10);
+		Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1);	/* HFC ST G1 */
+		udelay(10);
+		Write_hfc(cs, HFCPCI_STATES, 1 | HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
+		cs->dc.hfcpci.ph_state = 1;
+		cs->hw.hfcpci.nt_mode = 1;
+		cs->hw.hfcpci.nt_timer = 0;
+		cs->stlist->l2.l2l1 = dch_nt_l2l1;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		debugl1(cs, "NT mode activated");
+		return (0);
+	}
+	if ((cs->chanlimit > 1) || (cs->hw.hfcpci.bswapped) ||
+	    (cs->hw.hfcpci.nt_mode) || (ic->arg != 12))
+		return (-EINVAL);
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (i) {
+		cs->logecho = 1;
+		cs->hw.hfcpci.trm |= 0x20;	/* enable echo chan */
+		cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC;
+		cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX;
+	} else {
+		cs->logecho = 0;
+		cs->hw.hfcpci.trm &= ~0x20;	/* disable echo chan */
+		cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC;
+		cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX;
+	}
+	cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
+	cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
+	cs->hw.hfcpci.conn |= 0x10;	/* B2-IOM -> B2-ST */
+	cs->hw.hfcpci.ctmt &= ~2;
+	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+	Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return (0);
+}				/* hfcpci_auxcmd */
+
+/*****************************/
+/* E-channel receive routine */
+/*****************************/
+static void
+receive_emsg(struct IsdnCardState *cs)
+{
+	int rcnt;
+	int receive, count = 5;
+	bzfifo_type *bz;
+	u_char *bdata;
+	z_type *zp;
+	u_char *ptr, *ptr1, new_f2;
+	int total, maxlen, new_z2;
+	u_char e_buffer[256];
+
+	bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+	bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2;
+      Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "echo_rec_data blocked");
+		return;
+	}
+	if (bz->f1 != bz->f2) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcpci e_rec f1(%d) f2(%d)",
+				bz->f1, bz->f2);
+		zp = &bz->za[bz->f2];
+
+		rcnt = zp->z1 - zp->z2;
+		if (rcnt < 0)
+			rcnt += B_FIFO_SIZE;
+		rcnt++;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)",
+				zp->z1, zp->z2, rcnt);
+		new_z2 = zp->z2 + rcnt;		/* new position in fifo */
+		if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+			new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
+		new_f2 = (bz->f2 + 1) & MAX_B_FRAMES;
+		if ((rcnt > 256 + 3) || (count < 4) ||
+		    (*(bdata + (zp->z1 - B_SUB_VAL)))) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt);
+			bz->za[new_f2].z2 = new_z2;
+			bz->f2 = new_f2;	/* next buffer */
+		} else {
+			total = rcnt;
+			rcnt -= 3;
+			ptr = e_buffer;
+
+			if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL)
+				maxlen = rcnt;	/* complete transfer */
+			else
+				maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;	/* maximum */
+
+			ptr1 = bdata + (zp->z2 - B_SUB_VAL);	/* start of data */
+			memcpy(ptr, ptr1, maxlen);	/* copy data */
+			rcnt -= maxlen;
+
+			if (rcnt) {	/* rest remaining */
+				ptr += maxlen;
+				ptr1 = bdata;	/* start of buffer */
+				memcpy(ptr, ptr1, rcnt);	/* rest */
+			}
+			bz->za[new_f2].z2 = new_z2;
+			bz->f2 = new_f2;	/* next buffer */
+			if (cs->debug & DEB_DLOG_HEX) {
+				ptr = cs->dlog;
+				if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) {
+					*ptr++ = 'E';
+					*ptr++ = 'C';
+					*ptr++ = 'H';
+					*ptr++ = 'O';
+					*ptr++ = ':';
+					ptr += QuickHex(ptr, e_buffer, total - 3);
+					ptr--;
+					*ptr++ = '\n';
+					*ptr = 0;
+					HiSax_putstatus(cs, NULL, cs->dlog);
+				} else
+					HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3);
+			}
+		}
+
+		rcnt = bz->f1 - bz->f2;
+		if (rcnt < 0)
+			rcnt += MAX_B_FRAMES + 1;
+		if (rcnt > 1)
+			receive = 1;
+		else
+			receive = 0;
+	} else
+		receive = 0;
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && receive)
+		goto Begin;
+	return;
+}				/* receive_emsg */
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t
+hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	u_long flags;
+	struct IsdnCardState *cs = dev_id;
+	u_char exval;
+	struct BCState *bcs;
+	int count = 15;
+	u_char val, stat;
+
+	if (!(cs->hw.hfcpci.int_m2 & 0x08)) {
+		debugl1(cs, "HFC-PCI: int_m2 %x not initialised", cs->hw.hfcpci.int_m2);
+		return IRQ_NONE;	/* not initialised */
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	if (HFCPCI_ANYINT & (stat = Read_hfc(cs, HFCPCI_STATUS))) {
+		val = Read_hfc(cs, HFCPCI_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFC-PCI: stat(%02x) s1(%02x)", stat, val);
+	} else {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFC-PCI irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcpci.int_m1;
+	if (val & 0x40) {	/* state machine irq */
+		exval = Read_hfc(cs, HFCPCI_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state,
+				exval);
+		cs->dc.hfcpci.ph_state = exval;
+		sched_event_D_pci(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	if (val & 0x80) {	/* timer irq */
+		if (cs->hw.hfcpci.nt_mode) {
+			if ((--cs->hw.hfcpci.nt_timer) < 0)
+				sched_event_D_pci(cs, D_L1STATECHANGE);
+		}
+		val &= ~0x80;
+		Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+	}
+	while (val) {
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcpci.int_s1 |= val;
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		if (cs->hw.hfcpci.int_s1 & 0x18) {
+			exval = val;
+			val = cs->hw.hfcpci.int_s1;
+			cs->hw.hfcpci.int_s1 = exval;
+		}
+		if (val & 0x08) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x08 IRQ");
+			} else
+				main_rec_hfcpci(bcs);
+		}
+		if (val & 0x10) {
+			if (cs->logecho)
+				receive_emsg(cs);
+			else if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x10 IRQ");
+			} else
+				main_rec_hfcpci(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcpci_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcpci_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						hfcpci_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcpci spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcpci_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcpci_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						hfcpci_sched_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				sched_event_D_pci(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcpci_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfcpci_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb_irq(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfcpci_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfcpci_fill_dfifo irq blocked");
+				}
+			} else
+				sched_event_D_pci(cs, D_XMTBUFREADY);
+		}
+	      afterXPR:
+		if (cs->hw.hfcpci.int_s1 && count--) {
+			val = cs->hw.hfcpci.int_s1;
+			cs->hw.hfcpci.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFC-PCI irq %x loop %d", val, 15 - count);
+		} else
+			val = 0;
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+/********************************************************************/
+/* timer callback for D-chan busy resolution. Currently no function */
+/********************************************************************/
+static void
+hfcpci_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+/*************************************/
+/* Layer 1 D-channel hardware access */
+/*************************************/
+static void
+HFCPCI_l1hw(struct PStack *st, int pr, void *arg)
+{
+	u_long flags;
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfcpci_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfcpci_fill_dfifo blocked");
+
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfcpci_fill_dfifo(cs);
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfcpci_fill_dfifo blocked");
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3);	/* HFC ST 3 */
+			udelay(6);
+			Write_hfc(cs, HFCPCI_STATES, 3);	/* HFC ST 2 */
+			cs->hw.hfcpci.mst_m |= HFCPCI_MASTER;
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCPCI_STATES, HFCPCI_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER;
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcpci.mst_m |= HFCPCI_MASTER;
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			switch ((int) arg) {
+				case (1):
+					Write_hfc(cs, HFCPCI_B1_SSL, 0x80);	/* tx slot */
+					Write_hfc(cs, HFCPCI_B1_RSL, 0x80);	/* rx slot */
+					cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~7) | 1;
+					Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+					break;
+
+				case (2):
+					Write_hfc(cs, HFCPCI_B2_SSL, 0x81);	/* tx slot */
+					Write_hfc(cs, HFCPCI_B2_RSL, 0x81);	/* rx slot */
+					cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~0x38) | 0x08;
+					Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+					break;
+
+				default:
+					spin_unlock_irqrestore(&cs->lock, flags);
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "hfcpci_l1hw loop invalid %4x", (int) arg);
+					return;
+			}
+			cs->hw.hfcpci.trm |= 0x80;	/* enable IOM-loop */
+			Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+/***********************************************/
+/* called during init setting l1 stack pointer */
+/***********************************************/
+void
+setstack_hfcpci(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCPCI_l1hw;
+}
+
+/**************************************/
+/* send B-channel data if not blocked */
+/**************************************/
+static void
+hfcpci_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		hfcpci_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs, "send_data %d blocked", bcs->channel);
+}
+
+/***************************************************************/
+/* activate/deactivate hardware for selected channels and mode */
+/***************************************************************/
+void
+mode_hfcpci(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int fifo2;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	fifo2 = bc;
+	if (cs->chanlimit > 1) {
+		cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+		cs->hw.hfcpci.sctrl_e &= ~0x80;
+	} else {
+		if (bc) {
+			if (mode != L1_MODE_NULL) {
+				cs->hw.hfcpci.bswapped = 1;	/* B1 and B2 exchanged */
+				cs->hw.hfcpci.sctrl_e |= 0x80;
+			} else {
+				cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+				cs->hw.hfcpci.sctrl_e &= ~0x80;
+			}
+			fifo2 = 0;
+		} else {
+			cs->hw.hfcpci.bswapped = 0;	/* B1 and B2 normal mode */
+			cs->hw.hfcpci.sctrl_e &= ~0x80;
+		}
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r &= ~SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+			} else {
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+			}
+			break;
+		case (L1_MODE_TRANS):
+		        hfcpci_clear_fifo_rx(cs, fifo2);
+		        hfcpci_clear_fifo_tx(cs, fifo2);
+			if (bc) {
+				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+				cs->hw.hfcpci.ctmt |= 2;
+				cs->hw.hfcpci.conn &= ~0x18;
+			} else {
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.ctmt |= 1;
+				cs->hw.hfcpci.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_HDLC):
+		        hfcpci_clear_fifo_rx(cs, fifo2);
+		        hfcpci_clear_fifo_tx(cs, fifo2);
+			if (bc) {
+				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+			        cs->hw.hfcpci.last_bfifo_cnt[1] = 0;  
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+				cs->hw.hfcpci.ctmt &= ~2;
+				cs->hw.hfcpci.conn &= ~0x18;
+			} else {
+			        cs->hw.hfcpci.last_bfifo_cnt[0] = 0;  
+				cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+				cs->hw.hfcpci.ctmt &= ~1;
+				cs->hw.hfcpci.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_EXTRN):
+			if (bc) {
+				cs->hw.hfcpci.conn |= 0x10;
+				cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+			} else {
+				cs->hw.hfcpci.conn |= 0x02;
+				cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+				cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
+				cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+			}
+			break;
+	}
+	Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);
+	Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+	Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+	Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+	Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+	Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+	Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+}
+
+/******************************/
+/* Layer2 -> Layer 1 Transfer */
+/******************************/
+static void
+hfcpci_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState	*bcs = st->l1.bcs;
+	u_long		flags;
+	struct sk_buff	*skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ 				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				spin_unlock_irqrestore(&bcs->cs->lock, flags);
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+				break;
+			}
+//			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->tx_skb = skb;
+			bcs->cs->BC_Send_Data(bcs);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_hfcpci(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_hfcpci(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+/******************************************/
+/* deactivate B-channel access and queues */
+/******************************************/
+static void
+close_hfcpci(struct BCState *bcs)
+{
+	mode_hfcpci(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+/*************************************/
+/* init B-channel queues and control */
+/*************************************/
+static int
+open_hfcpcistate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+/*********************************/
+/* inits the stack for B-channel */
+/*********************************/
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcpcistate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfcpci_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+/***************************/
+/* handle L1 state changes */
+/***************************/
+static void
+hfcpci_bh(struct IsdnCardState *cs)
+{
+	u_long	flags;
+//      struct PStack *stptr;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		if (!cs->hw.hfcpci.nt_mode)
+			switch (cs->dc.hfcpci.ph_state) {
+				case (0):
+					l1_msg(cs, HW_RESET | INDICATION, NULL);
+					break;
+				case (3):
+					l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+					break;
+				case (8):
+					l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+					break;
+				case (6):
+					l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+					break;
+				case (7):
+					l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+					break;
+				default:
+					break;
+		} else {
+			spin_lock_irqsave(&cs->lock, flags);
+			switch (cs->dc.hfcpci.ph_state) {
+				case (2):
+					if (cs->hw.hfcpci.nt_timer < 0) {
+						cs->hw.hfcpci.nt_timer = 0;
+						cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+						Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+						/* Clear already pending ints */
+						if (Read_hfc(cs, HFCPCI_INT_S1));
+						Write_hfc(cs, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
+						udelay(10);
+						Write_hfc(cs, HFCPCI_STATES, 4);
+						cs->dc.hfcpci.ph_state = 4;
+					} else {
+						cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_TIMER;
+						Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+						cs->hw.hfcpci.ctmt &= ~HFCPCI_AUTO_TIMER;
+						cs->hw.hfcpci.ctmt |= HFCPCI_TIM3_125;
+						Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+						Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+						cs->hw.hfcpci.nt_timer = NT_T1_COUNT;
+						Write_hfc(cs, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);	/* allow G2 -> G3 transition */
+					}
+					break;
+				case (1):
+				case (3):
+				case (4):
+					cs->hw.hfcpci.nt_timer = 0;
+					cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+					Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+					break;
+				default:
+					break;
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+
+/********************************/
+/* called for card init message */
+/********************************/
+void __init
+inithfcpci(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_hfcpci;
+	cs->bcs[1].BC_Close = close_hfcpci;
+	cs->dbusytimer.function = (void *) hfcpci_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	mode_hfcpci(cs->bcs, 0, 0);
+	mode_hfcpci(cs->bcs + 1, 0, 1);
+}
+
+
+
+/*******************************************/
+/* handle card messages from control layer */
+/*******************************************/
+static int
+hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCPCI: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcpci(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			release_io_hfcpci(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithfcpci(cs);
+			reset_hfcpci(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			msleep(80);				/* Timeout 80ms */
+			/* now switch timer interrupt off */
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+			Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+			/* reinit mode reg */
+			Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+
+/* this variable is used as card index when more than one cards are present */
+static struct pci_dev *dev_hfcpci __initdata = NULL;
+
+#endif				/* CONFIG_PCI */
+
+int __init
+setup_hfcpci(struct IsdnCard *card)
+{
+	u_long flags;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	int i;
+	struct pci_dev *tmp_hfcpci = NULL;
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, hfcpci_revision);
+	printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp));
+#ifdef CONFIG_PCI
+	cs->hw.hfcpci.int_s1 = 0;
+	cs->dc.hfcpci.ph_state = 0;
+	cs->hw.hfcpci.fifo = 255;
+	if (cs->typ == ISDN_CTYPE_HFC_PCI) {
+		i = 0;
+		while (id_list[i].vendor_id) {
+			tmp_hfcpci = pci_find_device(id_list[i].vendor_id,
+						     id_list[i].device_id,
+						     dev_hfcpci);
+			i++;
+			if (tmp_hfcpci) {
+				if (pci_enable_device(tmp_hfcpci))
+					continue;
+				pci_set_master(tmp_hfcpci);
+				if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->resource[ 0].start & PCI_BASE_ADDRESS_IO_MASK)))
+					continue;
+				else
+					break;
+			}
+		}
+
+		if (tmp_hfcpci) {
+			i--;
+			dev_hfcpci = tmp_hfcpci;	/* old device */
+			cs->hw.hfcpci.dev = dev_hfcpci;
+			cs->irq = dev_hfcpci->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
+				return (0);
+			}
+			cs->hw.hfcpci.pci_io = (char *) dev_hfcpci->resource[ 1].start;
+			printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name);
+		} else {
+			printk(KERN_WARNING "HFC-PCI: No PCI card found\n");
+			return (0);
+		}
+		if (!cs->hw.hfcpci.pci_io) {
+			printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
+			return (0);
+		}
+		/* Allocate memory for FIFOS */
+		/* Because the HFC-PCI needs a 32K physical alignment, we */
+		/* need to allocate the double mem and align the address */
+		if (!(cs->hw.hfcpci.share_start = kmalloc(65536, GFP_KERNEL))) {
+			printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n");
+			return 0;
+		}
+		cs->hw.hfcpci.fifos = (void *)
+		    (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000;
+		pci_write_config_dword(cs->hw.hfcpci.dev, 0x80, (u_int) virt_to_bus(cs->hw.hfcpci.fifos));
+		cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256);
+		printk(KERN_INFO
+		       "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n",
+		       (u_int) cs->hw.hfcpci.pci_io,
+		       (u_int) cs->hw.hfcpci.fifos,
+		       (u_int) virt_to_bus(cs->hw.hfcpci.fifos),
+		       cs->irq, HZ);
+		spin_lock_irqsave(&cs->lock, flags);
+		pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO);	/* enable memory mapped ports, disable busmaster */
+		cs->hw.hfcpci.int_m2 = 0;	/* disable alle interrupts */
+		cs->hw.hfcpci.int_m1 = 0;
+		Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+		Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+		/* At this point the needed PCI config is done */
+		/* fifos are still not enabled */
+		INIT_WORK(&cs->tqueue, (void *)(void *) hfcpci_bh, cs);
+		cs->setstack_d = setstack_hfcpci;
+		cs->BC_Send_Data = &hfcpci_send_data;
+		cs->readisac = NULL;
+		cs->writeisac = NULL;
+		cs->readisacfifo = NULL;
+		cs->writeisacfifo = NULL;
+		cs->BC_Read_Reg = NULL;
+		cs->BC_Write_Reg = NULL;
+		cs->irq_func = &hfcpci_interrupt;
+		cs->irq_flags |= SA_SHIRQ;
+		cs->hw.hfcpci.timer.function = (void *) hfcpci_Timer;
+		cs->hw.hfcpci.timer.data = (long) cs;
+		init_timer(&cs->hw.hfcpci.timer);
+		cs->cardmsg = &hfcpci_card_msg;
+		cs->auxcmd = &hfcpci_auxcmd;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return (1);
+	} else
+		return (0);	/* no valid card type */
+#else
+	printk(KERN_WARNING "HFC-PCI: NO_PCI_BIOS\n");
+	return (0);
+#endif				/* CONFIG_PCI */
+}
diff --git a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h
new file mode 100644
index 0000000..4df036ed
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_pci.h
@@ -0,0 +1,236 @@
+/* $Id: hfc_pci.h,v 1.10.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0 PCI chips
+ *
+ * Author       Werner Cornelius
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*********************************************/
+/* thresholds for transparent B-channel mode */
+/* change mask and threshold simultaneously  */
+/*********************************************/
+#define HFCPCI_BTRANS_THRESHOLD 128
+#define HFCPCI_BTRANS_THRESMASK 0x00
+
+
+
+/* defines for PCI config */
+
+#define PCI_ENA_MEMIO    0x02
+#define PCI_ENA_MASTER   0x04
+
+
+/* GCI/IOM bus monitor registers */
+
+#define HCFPCI_C_I       0x08
+#define HFCPCI_TRxR      0x0C
+#define HFCPCI_MON1_D    0x28
+#define HFCPCI_MON2_D    0x2C
+
+
+/* GCI/IOM bus timeslot registers */
+
+#define HFCPCI_B1_SSL    0x80
+#define HFCPCI_B2_SSL    0x84
+#define HFCPCI_AUX1_SSL  0x88
+#define HFCPCI_AUX2_SSL  0x8C
+#define HFCPCI_B1_RSL    0x90
+#define HFCPCI_B2_RSL    0x94
+#define HFCPCI_AUX1_RSL  0x98
+#define HFCPCI_AUX2_RSL  0x9C
+
+/* GCI/IOM bus data registers */
+
+#define HFCPCI_B1_D      0xA0
+#define HFCPCI_B2_D      0xA4
+#define HFCPCI_AUX1_D    0xA8
+#define HFCPCI_AUX2_D    0xAC
+
+/* GCI/IOM bus configuration registers */
+
+#define HFCPCI_MST_EMOD  0xB4
+#define HFCPCI_MST_MODE	 0xB8
+#define HFCPCI_CONNECT 	 0xBC
+
+
+/* Interrupt and status registers */
+
+#define HFCPCI_FIFO_EN   0x44
+#define HFCPCI_TRM       0x48
+#define HFCPCI_B_MODE    0x4C
+#define HFCPCI_CHIP_ID   0x58
+#define HFCPCI_CIRM  	 0x60
+#define HFCPCI_CTMT	 0x64
+#define HFCPCI_INT_M1  	 0x68
+#define HFCPCI_INT_M2  	 0x6C
+#define HFCPCI_INT_S1  	 0x78
+#define HFCPCI_INT_S2  	 0x7C
+#define HFCPCI_STATUS  	 0x70
+
+/* S/T section registers */
+
+#define HFCPCI_STATES  	 0xC0
+#define HFCPCI_SCTRL  	 0xC4
+#define HFCPCI_SCTRL_E   0xC8
+#define HFCPCI_SCTRL_R   0xCC
+#define HFCPCI_SQ  	 0xD0
+#define HFCPCI_CLKDEL  	 0xDC
+#define HFCPCI_B1_REC    0xF0
+#define HFCPCI_B1_SEND   0xF0
+#define HFCPCI_B2_REC    0xF4
+#define HFCPCI_B2_SEND   0xF4
+#define HFCPCI_D_REC     0xF8
+#define HFCPCI_D_SEND    0xF8
+#define HFCPCI_E_REC     0xFC
+
+
+/* bits in status register (READ) */
+#define HFCPCI_PCI_PROC   0x02
+#define HFCPCI_NBUSY	  0x04 
+#define HFCPCI_TIMER_ELAP 0x10
+#define HFCPCI_STATINT	  0x20
+#define HFCPCI_FRAMEINT	  0x40
+#define HFCPCI_ANYINT	  0x80
+
+/* bits in CTMT (Write) */
+#define HFCPCI_CLTIMER    0x80
+#define HFCPCI_TIM3_125   0x04
+#define HFCPCI_TIM25      0x10
+#define HFCPCI_TIM50      0x14
+#define HFCPCI_TIM400     0x18
+#define HFCPCI_TIM800     0x1C
+#define HFCPCI_AUTO_TIMER 0x20
+#define HFCPCI_TRANSB2    0x02
+#define HFCPCI_TRANSB1    0x01
+
+/* bits in CIRM (Write) */
+#define HFCPCI_AUX_MSK    0x07
+#define HFCPCI_RESET  	  0x08
+#define HFCPCI_B1_REV     0x40
+#define HFCPCI_B2_REV     0x80
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCPCI_INTS_B1TRANS  0x01
+#define HFCPCI_INTS_B2TRANS  0x02
+#define HFCPCI_INTS_DTRANS   0x04
+#define HFCPCI_INTS_B1REC    0x08
+#define HFCPCI_INTS_B2REC    0x10
+#define HFCPCI_INTS_DREC     0x20
+#define HFCPCI_INTS_L1STATE  0x40
+#define HFCPCI_INTS_TIMER    0x80
+
+/* bits in INT_M2 */
+#define HFCPCI_PROC_TRANS    0x01
+#define HFCPCI_GCI_I_CHG     0x02
+#define HFCPCI_GCI_MON_REC   0x04
+#define HFCPCI_IRQ_ENABLE    0x08
+#define HFCPCI_PMESEL        0x80
+
+/* bits in STATES */
+#define HFCPCI_STATE_MSK     0x0F
+#define HFCPCI_LOAD_STATE    0x10
+#define HFCPCI_ACTIVATE	     0x20
+#define HFCPCI_DO_ACTION     0x40
+#define HFCPCI_NT_G2_G3      0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCPCI_MASTER	     0x01
+#define HFCPCI_SLAVE         0x00
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define SCTRL_B1_ENA	     0x01
+#define SCTRL_B2_ENA	     0x02
+#define SCTRL_MODE_TE        0x00
+#define SCTRL_MODE_NT        0x04
+#define SCTRL_LOW_PRIO	     0x08
+#define SCTRL_SQ_ENA	     0x10
+#define SCTRL_TEST	     0x20
+#define SCTRL_NONE_CAP	     0x40
+#define SCTRL_PWR_DOWN	     0x80
+
+/* bits in SCTRL_E  */
+#define HFCPCI_AUTO_AWAKE    0x01
+#define HFCPCI_DBIT_1        0x04
+#define HFCPCI_IGNORE_COL    0x08
+#define HFCPCI_CHG_B1_B2     0x80
+
+/****************************/
+/* bits in FIFO_EN register */
+/****************************/
+#define HFCPCI_FIFOEN_B1     0x03
+#define HFCPCI_FIFOEN_B2     0x0C
+#define HFCPCI_FIFOEN_DTX    0x10
+#define HFCPCI_FIFOEN_B1TX   0x01
+#define HFCPCI_FIFOEN_B1RX   0x02
+#define HFCPCI_FIFOEN_B2TX   0x04
+#define HFCPCI_FIFOEN_B2RX   0x08
+
+
+/***********************************/
+/* definitions of fifo memory area */
+/***********************************/
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL    0x200
+#define B_FIFO_SIZE  (0x2000 - B_SUB_VAL)
+#define D_FIFO_SIZE  512
+#define D_FREG_MASK  0xF
+
+typedef struct {
+    unsigned short z1;  /* Z1 pointer 16 Bit */
+    unsigned short z2;  /* Z2 pointer 16 Bit */
+  } z_type;
+
+typedef struct {
+    u_char data[D_FIFO_SIZE]; /* FIFO data space */
+    u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */
+    u_char f1,f2; /* f pointers */
+    u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */
+    z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */
+    u_char fill3[0x4000-0x2100]; /* align 16K */  
+  } dfifo_type;
+
+typedef struct {
+    z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ 
+    u_char f1,f2; /* f pointers */
+    u_char fill[0x2100-0x2082]; /* alignment */
+  } bzfifo_type;
+
+
+typedef union {
+    struct { 
+      dfifo_type d_tx; /* D-send channel */
+      dfifo_type d_rx; /* D-receive channel */
+    } d_chan; 
+    struct {
+      u_char fill1[0x200];
+      u_char txdat_b1[B_FIFO_SIZE];
+      bzfifo_type txbz_b1;
+
+      bzfifo_type txbz_b2;
+      u_char txdat_b2[B_FIFO_SIZE];
+
+      u_char fill2[D_FIFO_SIZE];
+
+      u_char rxdat_b1[B_FIFO_SIZE];
+      bzfifo_type rxbz_b1;
+
+      bzfifo_type rxbz_b2;
+      u_char rxdat_b2[B_FIFO_SIZE];
+    } b_chans;  
+    u_char fill[32768]; 
+  } fifo_area;
+
+
+#define Write_hfc(a,b,c) (*(((u_char *)a->hw.hfcpci.pci_io)+b) = c) 
+#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b))
+
+extern void main_irq_hcpci(struct BCState *bcs);
+extern void inithfcpci(struct IsdnCardState *cs);
+extern void releasehfcpci(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c
new file mode 100644
index 0000000..a307fcb
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_sx.c
@@ -0,0 +1,1521 @@
+/* $Id: hfc_sx.c,v 1.12.2.5 2004/02/11 13:21:33 keil Exp $
+ *
+ * level driver for Cologne Chip Designs hfc-s+/sp based cards
+ *
+ * Author       Werner Cornelius
+ *              based on existing driver for CCD HFC PCI cards
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_sx.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+static const char *hfcsx_revision = "$Revision: 1.12.2.5 $";
+
+/***************************************/
+/* IRQ-table for CCDs demo board       */
+/* IRQs 6,5,10,11,12,15 are supported  */
+/***************************************/
+
+/* Teles 16.3c Vendor Id TAG2620, Version 1.0, Vendor version 2.1
+ *
+ * Thanks to Uwe Wisniewski
+ *
+ * ISA-SLOT  Signal      PIN
+ * B25        IRQ3     92 IRQ_G
+ * B23        IRQ5     94 IRQ_A
+ * B4         IRQ2/9   95 IRQ_B
+ * D3         IRQ10    96 IRQ_C
+ * D4         IRQ11    97 IRQ_D
+ * D5         IRQ12    98 IRQ_E
+ * D6         IRQ15    99 IRQ_F
+ */
+
+#undef CCD_DEMO_BOARD
+#ifdef CCD_DEMO_BOARD
+static u_char ccd_sp_irqtab[16] = {
+  0,0,0,0,0,2,1,0,0,0,3,4,5,0,0,6
+};
+#else /* Teles 16.3c */
+static u_char ccd_sp_irqtab[16] = {
+  0,0,0,7,0,1,0,0,0,2,3,4,5,0,0,6
+};
+#endif
+#define NT_T1_COUNT 20		/* number of 3.125ms interrupts for G2 timeout */
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/******************************/
+/* In/Out access to registers */
+/******************************/
+static inline void
+Write_hfc(struct IsdnCardState *cs, u_char regnum, u_char val)
+{
+        byteout(cs->hw.hfcsx.base+1, regnum);
+	byteout(cs->hw.hfcsx.base, val);
+} 
+
+static inline u_char
+Read_hfc(struct IsdnCardState *cs, u_char regnum)
+{
+        u_char ret; 
+
+        byteout(cs->hw.hfcsx.base+1, regnum);
+	ret = bytein(cs->hw.hfcsx.base);
+	return(ret);
+} 
+
+
+/**************************************************/
+/* select a fifo and remember which one for reuse */
+/**************************************************/
+static void
+fifo_select(struct IsdnCardState *cs, u_char fifo)
+{
+        if (fifo == cs->hw.hfcsx.last_fifo) 
+	  return; /* still valid */
+
+        byteout(cs->hw.hfcsx.base+1, HFCSX_FIF_SEL);
+	byteout(cs->hw.hfcsx.base, fifo);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	udelay(4);
+	byteout(cs->hw.hfcsx.base, fifo);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+}
+
+/******************************************/
+/* reset the specified fifo to defaults.  */
+/* If its a send fifo init needed markers */
+/******************************************/
+static void
+reset_fifo(struct IsdnCardState *cs, u_char fifo)
+{
+	fifo_select(cs, fifo); /* first select the fifo */
+	byteout(cs->hw.hfcsx.base+1, HFCSX_CIRM);
+	byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.cirm | 0x80); /* reset cmd */
+	udelay(1);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+} 
+
+
+/*************************************************************/
+/* write_fifo writes the skb contents to the desired fifo    */
+/* if no space is available or an error occurs 0 is returned */
+/* the skb is not released in any way.                       */
+/*************************************************************/
+static int
+write_fifo(struct IsdnCardState *cs, struct sk_buff *skb, u_char fifo, int trans_max)
+{
+       unsigned short *msp;
+        int fifo_size, count, z1, z2;
+	u_char f_msk, f1, f2, *src;
+
+	if (skb->len <= 0) return(0);
+        if (fifo & 1) return(0); /* no write fifo */
+
+	fifo_select(cs, fifo);
+	if (fifo & 4) {
+	  fifo_size = D_FIFO_SIZE; /* D-channel */
+	  f_msk = MAX_D_FRAMES;
+	  if (trans_max) return(0); /* only HDLC */
+	}
+	else {
+	  fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+	  f_msk = MAX_B_FRAMES;
+	}
+
+        z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+
+	/* Check for transparent mode */
+	if (trans_max) {
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+	  count = z2 - z1;
+	  if (count <= 0)
+	    count += fifo_size; /* free bytes */
+	  if (count < skb->len+1) return(0); /* no room */
+	  count = fifo_size - count; /* bytes still not send */
+	  if (count > 2 * trans_max) return(0); /* delay to long */
+	  count = skb->len;
+	  src = skb->data;
+	  while (count--)
+	    Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+	  return(1); /* success */
+	}
+
+        msp = ((struct hfcsx_extra *)(cs->hw.hfcsx.extra))->marker;
+	msp += (((fifo >> 1) & 3) * (MAX_B_FRAMES+1));
+	f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+	f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+	count = f1 - f2; /* frame count actually buffered */
+	if (count < 0)
+		count += (f_msk + 1);	/* if wrap around */
+	if (count > f_msk-1) {
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_write_fifo %d more as %d frames",fifo,f_msk-1);
+	  return(0);
+	}
+
+	*(msp + f1) = z1; /* remember marker */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO)
+		debugl1(cs, "hfcsx_write_fifo %d f1(%x) f2(%x) z1(f1)(%x)",
+			fifo, f1, f2, z1);
+	/* now determine free bytes in FIFO buffer */
+	count = *(msp + f2) - z1;
+	if (count <= 0)
+	  count += fifo_size;	/* count now contains available bytes */
+
+	if (cs->debug & L1_DEB_ISAC_FIFO)
+	  debugl1(cs, "hfcsx_write_fifo %d count(%ld/%d)",
+		  fifo, skb->len, count);
+	if (count < skb->len) {
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_write_fifo %d no fifo mem", fifo);
+	  return(0);
+	}
+	
+	count = skb->len; /* get frame len */
+	src = skb->data;	/* source pointer */
+	while (count--)
+	  Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+	
+	Read_hfc(cs, HFCSX_FIF_INCF1); /* increment F1 */
+	udelay(1);
+	while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	return(1);
+} 
+
+/***************************************************************/
+/* read_fifo reads data to an skb from the desired fifo        */
+/* if no data is available or an error occurs NULL is returned */
+/* the skb is not released in any way.                         */
+/***************************************************************/
+static struct sk_buff * 
+read_fifo(struct IsdnCardState *cs, u_char fifo, int trans_max)
+{       int fifo_size, count, z1, z2;
+	u_char f_msk, f1, f2, *dst;
+	struct sk_buff *skb;
+
+        if (!(fifo & 1)) return(NULL); /* no read fifo */
+	fifo_select(cs, fifo);
+	if (fifo & 4) {
+	  fifo_size = D_FIFO_SIZE; /* D-channel */
+	  f_msk = MAX_D_FRAMES;
+	  if (trans_max) return(NULL); /* only hdlc */
+	}
+	else {
+	  fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+	  f_msk = MAX_B_FRAMES;
+	}
+
+	/* transparent mode */
+	if (trans_max) {
+	  z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	  z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+	  /* now determine bytes in actual FIFO buffer */
+	  count = z1 - z2;
+	  if (count <= 0)
+	    count += fifo_size;	/* count now contains buffered bytes */
+	  count++;
+	  if (count > trans_max) 
+	    count = trans_max; /* limit length */
+	    if ((skb = dev_alloc_skb(count))) {
+	      dst = skb_put(skb, count);
+	      while (count--) 
+		*dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+	      return(skb);
+	    }
+	    else return(NULL); /* no memory */
+	}
+
+	do {
+	  f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+	  f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+	  if (f1 == f2) return(NULL); /* no frame available */
+
+	  z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+	  z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+	  z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+	  z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_read_fifo %d f1(%x) f2(%x) z1(f2)(%x) z2(f2)(%x)",
+			fifo, f1, f2, z1, z2);
+	  /* now determine bytes in actual FIFO buffer */
+	  count = z1 - z2;
+	  if (count <= 0)
+	    count += fifo_size;	/* count now contains buffered bytes */
+	  count++;
+
+	  if (cs->debug & L1_DEB_ISAC_FIFO)
+	    debugl1(cs, "hfcsx_read_fifo %d count %ld)",
+		    fifo, count);
+
+	  if ((count > fifo_size) || (count < 4)) {
+	    if (cs->debug & L1_DEB_WARN)
+	      debugl1(cs, "hfcsx_read_fifo %d paket inv. len %d ", fifo , count);
+	    while (count) {
+	      count--; /* empty fifo */
+	      Read_hfc(cs, HFCSX_FIF_DRD);
+	    }
+	    skb = NULL;
+	  } else 
+	    if ((skb = dev_alloc_skb(count - 3))) {
+	      count -= 3;
+	      dst = skb_put(skb, count);
+
+	      while (count--) 
+		*dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+		    
+	      Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 1 */
+	      Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 2 */
+	      if (Read_hfc(cs, HFCSX_FIF_DRD)) {
+		dev_kfree_skb_irq(skb);
+		if (cs->debug & L1_DEB_ISAC_FIFO)
+		  debugl1(cs, "hfcsx_read_fifo %d crc error", fifo);
+		skb = NULL;
+	      }
+	    } else {
+	      printk(KERN_WARNING "HFC-SX: receive out of memory\n");
+	      return(NULL);
+	    }
+
+	  Read_hfc(cs, HFCSX_FIF_INCF2); /* increment F2 */
+	  udelay(1);
+	  while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+	  udelay(1);
+	} while (!skb); /* retry in case of crc error */
+	return(skb);
+} 
+
+/******************************************/
+/* free hardware resources used by driver */
+/******************************************/
+void
+release_io_hfcsx(struct IsdnCardState *cs)
+{
+	cs->hw.hfcsx.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET);	/* Reset On */
+	msleep(30);				/* Timeout 30ms */
+	Write_hfc(cs, HFCSX_CIRM, 0);	/* Reset Off */
+	del_timer(&cs->hw.hfcsx.timer);
+	release_region(cs->hw.hfcsx.base, 2); /* release IO-Block */
+	kfree(cs->hw.hfcsx.extra);
+	cs->hw.hfcsx.extra = NULL;
+}
+
+/**********************************************************/
+/* set_fifo_size determines the size of the RAM and FIFOs */
+/* returning 0 -> need to reset the chip again.           */
+/**********************************************************/
+static int set_fifo_size(struct IsdnCardState *cs)
+{
+        
+        if (cs->hw.hfcsx.b_fifo_size) return(1); /* already determined */
+
+	if ((cs->hw.hfcsx.chip >> 4) == 9) {
+	  cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_32K;
+	  return(1);
+	}
+
+	  cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_8K;
+	  cs->hw.hfcsx.cirm |= 0x10; /* only 8K of ram */
+	  return(0);
+
+}
+
+/********************************************************************************/
+/* function called to reset the HFC SX chip. A complete software reset of chip */
+/* and fifos is done.                                                           */
+/********************************************************************************/
+static void
+reset_hfcsx(struct IsdnCardState *cs)
+{
+	cs->hw.hfcsx.int_m2 = 0;	/* interrupt output off ! */
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+
+	printk(KERN_INFO "HFC_SX: resetting card\n");
+	while (1) {
+	  Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET | cs->hw.hfcsx.cirm ); /* Reset */
+	  mdelay(30);
+	  Write_hfc(cs, HFCSX_CIRM, cs->hw.hfcsx.cirm); /* Reset Off */
+	  mdelay(20);
+	  if (Read_hfc(cs, HFCSX_STATUS) & 2)
+	    printk(KERN_WARNING "HFC-SX init bit busy\n");
+	  cs->hw.hfcsx.last_fifo = 0xff; /* invalidate */
+	  if (!set_fifo_size(cs)) continue;
+	  break;
+	}
+
+	cs->hw.hfcsx.trm = 0 + HFCSX_BTRANS_THRESMASK;	/* no echo connect , threshold */
+	Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+
+	Write_hfc(cs, HFCSX_CLKDEL, 0x0e);	/* ST-Bit delay for TE-Mode */
+	cs->hw.hfcsx.sctrl_e = HFCSX_AUTO_AWAKE;
+	Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e);	/* S/T Auto awake */
+	cs->hw.hfcsx.bswapped = 0;	/* no exchange */
+	cs->hw.hfcsx.nt_mode = 0;	/* we are in TE mode */
+	cs->hw.hfcsx.ctmt = HFCSX_TIM3_125 | HFCSX_AUTO_TIMER;
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+
+	cs->hw.hfcsx.int_m1 = HFCSX_INTS_DTRANS | HFCSX_INTS_DREC | 
+	    HFCSX_INTS_L1STATE | HFCSX_INTS_TIMER;
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+
+	/* Clear already pending ints */
+	if (Read_hfc(cs, HFCSX_INT_S1));
+
+	Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 2);	/* HFC ST 2 */
+	udelay(10);
+	Write_hfc(cs, HFCSX_STATES, 2);	/* HFC ST 2 */
+	cs->hw.hfcsx.mst_m = HFCSX_MASTER;	/* HFC Master Mode */
+
+	Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+	cs->hw.hfcsx.sctrl = 0x40;	/* set tx_lo mode, error in datasheet ! */
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	cs->hw.hfcsx.sctrl_r = 0;
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+
+	/* Init GCI/IOM2 in master mode */
+	/* Slots 0 and 1 are set for B-chan 1 and 2 */
+	/* D- and monitor/CI channel are not enabled */
+	/* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */
+	/* STIO2 is used as data input, B1+B2 from IOM->ST */
+	/* ST B-channel send disabled -> continous 1s */
+	/* The IOM slots are always enabled */
+	cs->hw.hfcsx.conn = 0x36;	/* set data flow directions */
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	Write_hfc(cs, HFCSX_B1_SSL, 0x80);	/* B1-Slot 0 STIO1 out enabled */
+	Write_hfc(cs, HFCSX_B2_SSL, 0x81);	/* B2-Slot 1 STIO1 out enabled */
+	Write_hfc(cs, HFCSX_B1_RSL, 0x80);	/* B1-Slot 0 STIO2 in enabled */
+	Write_hfc(cs, HFCSX_B2_RSL, 0x81);	/* B2-Slot 1 STIO2 in enabled */
+
+	/* Finally enable IRQ output */
+	cs->hw.hfcsx.int_m2 = HFCSX_IRQ_ENABLE;
+	Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	if (Read_hfc(cs, HFCSX_INT_S2));
+}
+
+/***************************************************/
+/* Timer function called when kernel timer expires */
+/***************************************************/
+static void
+hfcsx_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcsx.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*      WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcsx.ctmt | 0x80);
+   add_timer(&cs->hw.hfcsx.timer);
+ */
+}
+
+/************************************************/
+/* select a b-channel entry matching and active */
+/************************************************/
+static
+struct BCState *
+Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+	if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+		return (&cs->bcs[0]);
+	else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+		return (&cs->bcs[1]);
+	else
+		return (NULL);
+}
+
+/*******************************/
+/* D-channel receive procedure */
+/*******************************/
+static
+int
+receive_dmsg(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	int count = 5;
+
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_dmsg blocked");
+		return (1);
+	}
+
+	do {
+	  skb = read_fifo(cs, HFCSX_SEL_D_RX, 0);
+	  if (skb) {
+	    skb_queue_tail(&cs->rq, skb);
+	    schedule_event(cs, D_RCVBUFREADY);
+	  }
+	} while (--count && skb);
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return (1);
+}
+
+/**********************************/
+/* B-channel main receive routine */
+/**********************************/
+void
+main_rec_hfcsx(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count = 5;
+	struct sk_buff *skb;
+
+      Begin:
+	count--;
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "rec_data %d blocked", bcs->channel);
+		return;
+	}
+	skb = read_fifo(cs, ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? 
+			HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX,
+			(bcs->mode == L1_MODE_TRANS) ? 
+			HFCSX_BTRANS_THRESHOLD : 0);
+
+	if (skb) {
+	  skb_queue_tail(&bcs->rqueue, skb);
+	  schedule_event(bcs, B_RCVBUFREADY);
+	}
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	if (count && skb)
+		goto Begin;
+	return;
+}
+
+/**************************/
+/* D-channel send routine */
+/**************************/
+static void
+hfcsx_fill_dfifo(struct IsdnCardState *cs)
+{
+	if (!cs->tx_skb)
+		return;
+	if (cs->tx_skb->len <= 0)
+		return;
+
+	if (write_fifo(cs, cs->tx_skb, HFCSX_SEL_D_TX, 0)) {
+	  dev_kfree_skb_any(cs->tx_skb);
+	  cs->tx_skb = NULL;
+	}
+	return;
+}
+
+/**************************/
+/* B-channel send routine */
+/**************************/
+static void
+hfcsx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	if (write_fifo(cs, bcs->tx_skb, 
+		       ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ? 
+		       HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX,
+		       (bcs->mode == L1_MODE_TRANS) ? 
+		       HFCSX_BTRANS_THRESHOLD : 0)) {
+
+	  bcs->tx_cnt -= bcs->tx_skb->len;
+	  if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+		(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+		u_long	flags;
+		spin_lock_irqsave(&bcs->aclock, flags);
+		bcs->ackcnt += bcs->tx_skb->len;
+		spin_unlock_irqrestore(&bcs->aclock, flags);
+		schedule_event(bcs, B_ACKPENDING);
+	  }
+	  dev_kfree_skb_any(bcs->tx_skb);
+	  bcs->tx_skb = NULL;
+	  test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+}
+
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+		case (PH_PULL | REQUEST):
+		case (PH_PULL | INDICATION):
+			st->l1.l1hw(st, pr, arg);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			break;
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+
+
+/***********************/
+/* set/reset echo mode */
+/***********************/
+static int
+hfcsx_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
+{
+	unsigned long flags;
+	int i = *(unsigned int *) ic->parm.num;
+
+	if ((ic->arg == 98) &&
+	    (!(cs->hw.hfcsx.int_m1 & (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC + HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC)))) {
+	    	spin_lock_irqsave(&cs->lock, flags);
+		Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 0);	/* HFC ST G0 */
+		udelay(10);
+		cs->hw.hfcsx.sctrl |= SCTRL_MODE_NT;
+		Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);	/* set NT-mode */
+		udelay(10);
+		Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 1);	/* HFC ST G1 */
+		udelay(10);
+		Write_hfc(cs, HFCSX_STATES, 1 | HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+		cs->dc.hfcsx.ph_state = 1;
+		cs->hw.hfcsx.nt_mode = 1;
+		cs->hw.hfcsx.nt_timer = 0;
+		spin_unlock_irqrestore(&cs->lock, flags);
+		cs->stlist->l2.l2l1 = dch_nt_l2l1;
+		debugl1(cs, "NT mode activated");
+		return (0);
+	}
+	if ((cs->chanlimit > 1) || (cs->hw.hfcsx.bswapped) ||
+	    (cs->hw.hfcsx.nt_mode) || (ic->arg != 12))
+		return (-EINVAL);
+
+	if (i) {
+		cs->logecho = 1;
+		cs->hw.hfcsx.trm |= 0x20;	/* enable echo chan */
+		cs->hw.hfcsx.int_m1 |= HFCSX_INTS_B2REC;
+		/* reset Channel !!!!! */
+	} else {
+		cs->logecho = 0;
+		cs->hw.hfcsx.trm &= ~0x20;	/* disable echo chan */
+		cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_B2REC;
+	}
+	cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+	cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+	cs->hw.hfcsx.conn |= 0x10;	/* B2-IOM -> B2-ST */
+	cs->hw.hfcsx.ctmt &= ~2;
+	spin_lock_irqsave(&cs->lock, flags);
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return (0);
+}				/* hfcsx_auxcmd */
+
+/*****************************/
+/* E-channel receive routine */
+/*****************************/
+static void
+receive_emsg(struct IsdnCardState *cs)
+{
+	int count = 5;
+	u_char *ptr;
+	struct sk_buff *skb;
+
+	if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+		debugl1(cs, "echo_rec_data blocked");
+		return;
+	}
+	do {
+	  skb = read_fifo(cs, HFCSX_SEL_B2_RX, 0);
+	  if (skb) {
+	    if (cs->debug & DEB_DLOG_HEX) {
+	      ptr = cs->dlog;
+	      if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) {
+		*ptr++ = 'E';
+		*ptr++ = 'C';
+		*ptr++ = 'H';
+		*ptr++ = 'O';
+		*ptr++ = ':';
+		ptr += QuickHex(ptr, skb->data, skb->len);
+		ptr--;
+		*ptr++ = '\n';
+		*ptr = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+	      } else
+		HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", skb->len);
+	    }
+	    dev_kfree_skb_any(skb);
+	  }
+	} while (--count && skb);
+
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	return;
+}				/* receive_emsg */
+
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t
+hfcsx_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char exval;
+	struct BCState *bcs;
+	int count = 15;
+	u_long flags;
+	u_char val, stat;
+
+	if (!(cs->hw.hfcsx.int_m2 & 0x08))
+		return IRQ_NONE;		/* not initialised */
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (HFCSX_ANYINT & (stat = Read_hfc(cs, HFCSX_STATUS))) {
+		val = Read_hfc(cs, HFCSX_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFC-SX: stat(%02x) s1(%02x)", stat, val);
+	} else {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFC-SX irq %x %s", val,
+			test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+			"locked" : "unlocked");
+	val &= cs->hw.hfcsx.int_m1;
+	if (val & 0x40) {	/* state machine irq */
+		exval = Read_hfc(cs, HFCSX_STATES) & 0xf;
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcsx.ph_state,
+				exval);
+		cs->dc.hfcsx.ph_state = exval;
+		schedule_event(cs, D_L1STATECHANGE);
+		val &= ~0x40;
+	}
+	if (val & 0x80) {	/* timer irq */
+		if (cs->hw.hfcsx.nt_mode) {
+			if ((--cs->hw.hfcsx.nt_timer) < 0)
+				schedule_event(cs, D_L1STATECHANGE);
+		}
+		val &= ~0x80;
+		Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+	}
+	while (val) {
+		if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			cs->hw.hfcsx.int_s1 |= val;
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		if (cs->hw.hfcsx.int_s1 & 0x18) {
+			exval = val;
+			val = cs->hw.hfcsx.int_s1;
+			cs->hw.hfcsx.int_s1 = exval;
+		}
+		if (val & 0x08) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x08 IRQ");
+			} else
+				main_rec_hfcsx(bcs);
+		}
+		if (val & 0x10) {
+			if (cs->logecho)
+				receive_emsg(cs);
+			else if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x10 IRQ");
+			} else
+				main_rec_hfcsx(bcs);
+		}
+		if (val & 0x01) {
+			if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x01 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcsx_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x02) {
+			if (!(bcs = Sel_BCS(cs, 1))) {
+				if (cs->debug)
+					debugl1(cs, "hfcsx spurious 0x02 IRQ");
+			} else {
+				if (bcs->tx_skb) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_fifo(bcs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else
+						debugl1(cs, "fill_data %d blocked", bcs->channel);
+				} else {
+					if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+						if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+							hfcsx_fill_fifo(bcs);
+							test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+						} else
+							debugl1(cs, "fill_data %d blocked", bcs->channel);
+					} else {
+						schedule_event(bcs, B_XMTBUFREADY);
+					}
+				}
+			}
+		}
+		if (val & 0x20) {	/* receive dframe */
+			receive_dmsg(cs);
+		}
+		if (val & 0x04) {	/* dframe transmitted */
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {
+				if (cs->tx_skb->len) {
+					if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+						hfcsx_fill_dfifo(cs);
+						test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+					} else {
+						debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+					}
+					goto afterXPR;
+				} else {
+					dev_kfree_skb_irq(cs->tx_skb);
+					cs->tx_cnt = 0;
+					cs->tx_skb = NULL;
+				}
+			}
+			if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+				cs->tx_cnt = 0;
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+					hfcsx_fill_dfifo(cs);
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else {
+					debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+				}
+			} else
+				schedule_event(cs, D_XMTBUFREADY);
+		}
+	      afterXPR:
+		if (cs->hw.hfcsx.int_s1 && count--) {
+			val = cs->hw.hfcsx.int_s1;
+			cs->hw.hfcsx.int_s1 = 0;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "HFC-SX irq %x loop %d", val, 15 - count);
+		} else
+			val = 0;
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+/********************************************************************/
+/* timer callback for D-chan busy resolution. Currently no function */
+/********************************************************************/
+static void
+hfcsx_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+/*************************************/
+/* Layer 1 D-channel hardware access */
+/*************************************/
+static void
+HFCSX_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				        hfcsx_fill_dfifo(cs); 
+					test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+				} else
+					debugl1(cs, "hfcsx_fill_dfifo blocked");
+
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+				hfcsx_fill_dfifo(cs); 
+				test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+			} else
+				debugl1(cs, "hfcsx_fill_dfifo blocked");
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 3);	/* HFC ST 3 */
+			udelay(6);
+			Write_hfc(cs, HFCSX_STATES, 3);	/* HFC ST 2 */
+			cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcsx.mst_m &= ~HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			switch ((int) arg) {
+				case (1):
+					Write_hfc(cs, HFCSX_B1_SSL, 0x80);	/* tx slot */
+					Write_hfc(cs, HFCSX_B1_RSL, 0x80);	/* rx slot */
+					cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~7) | 1;
+					Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+					break;
+				case (2):
+					Write_hfc(cs, HFCSX_B2_SSL, 0x81);	/* tx slot */
+					Write_hfc(cs, HFCSX_B2_RSL, 0x81);	/* rx slot */
+					cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~0x38) | 0x08;
+					Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+					break;
+				default:
+					spin_unlock_irqrestore(&cs->lock, flags);
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "hfcsx_l1hw loop invalid %4x", (int) arg);
+					return;
+			}
+			cs->hw.hfcsx.trm |= 0x80;	/* enable IOM-loop */
+			Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "hfcsx_l1hw unknown pr %4x", pr);
+			break;
+	}
+}
+
+/***********************************************/
+/* called during init setting l1 stack pointer */
+/***********************************************/
+void
+setstack_hfcsx(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = HFCSX_l1hw;
+}
+
+/**************************************/
+/* send B-channel data if not blocked */
+/**************************************/
+static void
+hfcsx_send_data(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+	  hfcsx_fill_fifo(bcs);
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	} else
+		debugl1(cs, "send_data %d blocked", bcs->channel);
+}
+
+/***************************************************************/
+/* activate/deactivate hardware for selected channels and mode */
+/***************************************************************/
+void
+mode_hfcsx(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int fifo2;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "HFCSX bchannel mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	fifo2 = bc;
+	if (cs->chanlimit > 1) {
+		cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+		cs->hw.hfcsx.sctrl_e &= ~0x80;
+	} else {
+		if (bc) {
+			if (mode != L1_MODE_NULL) {
+				cs->hw.hfcsx.bswapped = 1;	/* B1 and B2 exchanged */
+				cs->hw.hfcsx.sctrl_e |= 0x80;
+			} else {
+				cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+				cs->hw.hfcsx.sctrl_e &= ~0x80;
+			}
+			fifo2 = 0;
+		} else {
+			cs->hw.hfcsx.bswapped = 0;	/* B1 and B2 normal mode */
+			cs->hw.hfcsx.sctrl_e &= ~0x80;
+		}
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			if (bc) {
+				cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl &= ~SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r &= ~SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+			} else {
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+			}
+			break;
+		case (L1_MODE_TRANS):
+			if (bc) {
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+				cs->hw.hfcsx.ctmt |= 2;
+				cs->hw.hfcsx.conn &= ~0x18;
+			} else {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+				cs->hw.hfcsx.ctmt |= 1;
+				cs->hw.hfcsx.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_HDLC):
+			if (bc) {
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+			} else {
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+			}
+			if (fifo2) {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+				cs->hw.hfcsx.ctmt &= ~2;
+				cs->hw.hfcsx.conn &= ~0x18;
+			} else {
+				cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+				cs->hw.hfcsx.ctmt &= ~1;
+				cs->hw.hfcsx.conn &= ~0x03;
+			}
+			break;
+		case (L1_MODE_EXTRN):
+			if (bc) {
+				cs->hw.hfcsx.conn |= 0x10;
+				cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+			} else {
+				cs->hw.hfcsx.conn |= 0x02;
+				cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+				cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+			}
+			break;
+	}
+	Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e);
+	Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+	Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+	Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+	Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+	Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+	if (mode != L1_MODE_EXTRN) {
+	  reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX);
+	  reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX);
+	}
+}
+
+/******************************/
+/* Layer2 -> Layer 1 Transfer */
+/******************************/
+static void
+hfcsx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+//                              test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+			} else {
+//				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_hfcsx(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_hfcsx(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+/******************************************/
+/* deactivate B-channel access and queues */
+/******************************************/
+static void
+close_hfcsx(struct BCState *bcs)
+{
+	mode_hfcsx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+/*************************************/
+/* init B-channel queues and control */
+/*************************************/
+static int
+open_hfcsxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+/*********************************/
+/* inits the stack for B-channel */
+/*********************************/
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hfcsxstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hfcsx_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+/***************************/
+/* handle L1 state changes */
+/***************************/
+static void
+hfcsx_bh(struct IsdnCardState *cs)
+{
+	u_long flags;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+		if (!cs->hw.hfcsx.nt_mode)
+			switch (cs->dc.hfcsx.ph_state) {
+				case (0):
+					l1_msg(cs, HW_RESET | INDICATION, NULL);
+					break;
+				case (3):
+					l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+					break;
+				case (8):
+					l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+					break;
+				case (6):
+					l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+					break;
+				case (7):
+					l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+					break;
+				default:
+					break;
+		} else {
+			switch (cs->dc.hfcsx.ph_state) {
+				case (2):
+					spin_lock_irqsave(&cs->lock, flags);
+					if (cs->hw.hfcsx.nt_timer < 0) {
+						cs->hw.hfcsx.nt_timer = 0;
+						cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+						Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+						/* Clear already pending ints */
+						if (Read_hfc(cs, HFCSX_INT_S1));
+
+						Write_hfc(cs, HFCSX_STATES, 4 | HFCSX_LOAD_STATE);
+						udelay(10);
+						Write_hfc(cs, HFCSX_STATES, 4);
+						cs->dc.hfcsx.ph_state = 4;
+					} else {
+						cs->hw.hfcsx.int_m1 |= HFCSX_INTS_TIMER;
+						Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+						cs->hw.hfcsx.ctmt &= ~HFCSX_AUTO_TIMER;
+						cs->hw.hfcsx.ctmt |= HFCSX_TIM3_125;
+						Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+						Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+						cs->hw.hfcsx.nt_timer = NT_T1_COUNT;
+						Write_hfc(cs, HFCSX_STATES, 2 | HFCSX_NT_G2_G3);	/* allow G2 -> G3 transition */
+					}
+					spin_unlock_irqrestore(&cs->lock, flags);
+					break;
+				case (1):
+				case (3):
+				case (4):
+					spin_lock_irqsave(&cs->lock, flags);
+					cs->hw.hfcsx.nt_timer = 0;
+					cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+					Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+					spin_unlock_irqrestore(&cs->lock, flags);
+					break;
+				default:
+					break;
+			}
+		}
+	}
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+}
+
+
+/********************************/
+/* called for card init message */
+/********************************/
+void __devinit
+inithfcsx(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_hfcsx;
+	cs->BC_Send_Data = &hfcsx_send_data;
+	cs->bcs[0].BC_SetStack = setstack_2b;
+	cs->bcs[1].BC_SetStack = setstack_2b;
+	cs->bcs[0].BC_Close = close_hfcsx;
+	cs->bcs[1].BC_Close = close_hfcsx;
+	mode_hfcsx(cs->bcs, 0, 0);
+	mode_hfcsx(cs->bcs + 1, 0, 1);
+}
+
+
+
+/*******************************************/
+/* handle card messages from control layer */
+/*******************************************/
+static int
+hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCSX: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcsx(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_RELEASE:
+			release_io_hfcsx(cs);
+			return (0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithfcsx(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			msleep(80);				/* Timeout 80ms */
+			/* now switch timer interrupt off */
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+			Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+			/* reinit mode reg */
+			Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id hfc_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2620),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2620), 
+	  (unsigned long) "Teles 16.3c2" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &hfc_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_hfcsx(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, hfcsx_revision);
+	printk(KERN_INFO "HiSax: HFC-SX driver Rev. %s\n", HiSax_getrev(tmp));
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "HFC PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "HFC PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	cs->hw.hfcsx.base = card->para[1] & 0xfffe;
+	cs->irq = card->para[0];
+	cs->hw.hfcsx.int_s1 = 0;
+	cs->dc.hfcsx.ph_state = 0;
+	cs->hw.hfcsx.fifo = 255;
+	if ((cs->typ == ISDN_CTYPE_HFC_SX) || 
+	    (cs->typ == ISDN_CTYPE_HFC_SP_PCMCIA)) {
+	        if ((!cs->hw.hfcsx.base) || !request_region(cs->hw.hfcsx.base, 2, "HFCSX isdn")) {
+		  printk(KERN_WARNING
+			 "HiSax: HFC-SX io-base %#lx already in use\n",
+		          cs->hw.hfcsx.base);
+		  return(0);
+		}
+		byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.base & 0xFF);
+		byteout(cs->hw.hfcsx.base + 1,
+			((cs->hw.hfcsx.base >> 8) & 3) | 0x54);
+		udelay(10);
+	        cs->hw.hfcsx.chip = Read_hfc(cs,HFCSX_CHIP_ID);
+                switch (cs->hw.hfcsx.chip >> 4) {
+		  case 1: 
+		    tmp[0] ='+';
+		    break;
+		  case 9: 
+		    tmp[0] ='P';
+		    break;
+		  default:
+		    printk(KERN_WARNING
+			   "HFC-SX: invalid chip id 0x%x\n",
+			   cs->hw.hfcsx.chip >> 4);
+		    release_region(cs->hw.hfcsx.base, 2);
+		    return(0);
+		}  
+		if (!ccd_sp_irqtab[cs->irq & 0xF]) {
+		  printk(KERN_WARNING 
+			 "HFC_SX: invalid irq %d specified\n",cs->irq & 0xF);
+		  release_region(cs->hw.hfcsx.base, 2);
+		  return(0);
+		}  
+		if (!(cs->hw.hfcsx.extra = (void *)
+		      kmalloc(sizeof(struct hfcsx_extra), GFP_ATOMIC))) {
+		  release_region(cs->hw.hfcsx.base, 2);
+		  printk(KERN_WARNING "HFC-SX: unable to allocate memory\n");
+		  return(0);
+		}
+		printk(KERN_INFO "HFC-S%c chip detected at base 0x%x IRQ %d HZ %d\n",
+			tmp[0], (u_int) cs->hw.hfcsx.base, cs->irq, HZ);
+		cs->hw.hfcsx.int_m2 = 0;	/* disable alle interrupts */
+		cs->hw.hfcsx.int_m1 = 0;
+		Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+		Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+	} else
+		return (0);	/* no valid card type */
+
+	cs->dbusytimer.function = (void *) hfcsx_dbusy_timer;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+	INIT_WORK(&cs->tqueue, (void *)(void *) hfcsx_bh, cs);
+	cs->readisac = NULL;
+	cs->writeisac = NULL;
+	cs->readisacfifo = NULL;
+	cs->writeisacfifo = NULL;
+	cs->BC_Read_Reg = NULL;
+	cs->BC_Write_Reg = NULL;
+	cs->irq_func = &hfcsx_interrupt;
+
+	cs->hw.hfcsx.timer.function = (void *) hfcsx_Timer;
+	cs->hw.hfcsx.timer.data = (long) cs;
+	cs->hw.hfcsx.b_fifo_size = 0; /* fifo size still unknown */
+	cs->hw.hfcsx.cirm = ccd_sp_irqtab[cs->irq & 0xF]; /* RAM not evaluated */
+	init_timer(&cs->hw.hfcsx.timer);
+
+	reset_hfcsx(cs);
+	cs->cardmsg = &hfcsx_card_msg;
+	cs->auxcmd = &hfcsx_auxcmd;
+	return (1);
+}
diff --git a/drivers/isdn/hisax/hfc_sx.h b/drivers/isdn/hisax/hfc_sx.h
new file mode 100644
index 0000000..12f5415
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_sx.h
@@ -0,0 +1,197 @@
+/* $Id: hfc_sx.h,v 1.2.6.1 2001/09/23 22:24:48 kai Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0 S+,SP chips
+ *
+ * Author       Werner Cornelius
+ *              based on existing driver for CCD HFC PCI cards
+ * Copyright    by Werner Cornelius  <werner@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*********************************************/
+/* thresholds for transparent B-channel mode */
+/* change mask and threshold simultaneously  */
+/*********************************************/
+#define HFCSX_BTRANS_THRESHOLD 128
+#define HFCSX_BTRANS_THRESMASK 0x00
+
+/* GCI/IOM bus monitor registers */
+
+#define HFCSX_C_I       0x02
+#define HFCSX_TRxR      0x03
+#define HFCSX_MON1_D    0x0A
+#define HFCSX_MON2_D    0x0B
+
+
+/* GCI/IOM bus timeslot registers */
+
+#define HFCSX_B1_SSL    0x20
+#define HFCSX_B2_SSL    0x21
+#define HFCSX_AUX1_SSL  0x22
+#define HFCSX_AUX2_SSL  0x23
+#define HFCSX_B1_RSL    0x24
+#define HFCSX_B2_RSL    0x25
+#define HFCSX_AUX1_RSL  0x26
+#define HFCSX_AUX2_RSL  0x27
+
+/* GCI/IOM bus data registers */
+
+#define HFCSX_B1_D      0x28
+#define HFCSX_B2_D      0x29
+#define HFCSX_AUX1_D    0x2A
+#define HFCSX_AUX2_D    0x2B
+
+/* GCI/IOM bus configuration registers */
+
+#define HFCSX_MST_EMOD  0x2D
+#define HFCSX_MST_MODE	0x2E
+#define HFCSX_CONNECT 	0x2F
+
+
+/* Interrupt and status registers */
+
+#define HFCSX_TRM       0x12
+#define HFCSX_B_MODE    0x13
+#define HFCSX_CHIP_ID   0x16
+#define HFCSX_CIRM  	0x18
+#define HFCSX_CTMT	0x19
+#define HFCSX_INT_M1  	0x1A
+#define HFCSX_INT_M2  	0x1B
+#define HFCSX_INT_S1  	0x1E
+#define HFCSX_INT_S2  	0x1F
+#define HFCSX_STATUS  	0x1C
+
+/* S/T section registers */
+
+#define HFCSX_STATES  	0x30
+#define HFCSX_SCTRL  	0x31
+#define HFCSX_SCTRL_E   0x32
+#define HFCSX_SCTRL_R   0x33
+#define HFCSX_SQ  	0x34
+#define HFCSX_CLKDEL  	0x37
+#define HFCSX_B1_REC    0x3C
+#define HFCSX_B1_SEND   0x3C
+#define HFCSX_B2_REC    0x3D
+#define HFCSX_B2_SEND   0x3D
+#define HFCSX_D_REC     0x3E
+#define HFCSX_D_SEND    0x3E
+#define HFCSX_E_REC     0x3F
+
+/****************/
+/* FIFO section */
+/****************/
+#define HFCSX_FIF_SEL   0x10
+#define HFCSX_FIF_Z1L   0x80
+#define HFCSX_FIF_Z1H   0x84
+#define HFCSX_FIF_Z2L   0x88
+#define HFCSX_FIF_Z2H   0x8C
+#define HFCSX_FIF_INCF1 0xA8
+#define HFCSX_FIF_DWR   0xAC
+#define HFCSX_FIF_F1    0xB0
+#define HFCSX_FIF_F2    0xB4
+#define HFCSX_FIF_INCF2 0xB8
+#define HFCSX_FIF_DRD   0xBC
+
+/* bits in status register (READ) */
+#define HFCSX_SX_PROC    0x02
+#define HFCSX_NBUSY	 0x04 
+#define HFCSX_TIMER_ELAP 0x10
+#define HFCSX_STATINT	 0x20
+#define HFCSX_FRAMEINT	 0x40
+#define HFCSX_ANYINT	 0x80
+
+/* bits in CTMT (Write) */
+#define HFCSX_CLTIMER    0x80
+#define HFCSX_TIM3_125   0x04
+#define HFCSX_TIM25      0x10
+#define HFCSX_TIM50      0x14
+#define HFCSX_TIM400     0x18
+#define HFCSX_TIM800     0x1C
+#define HFCSX_AUTO_TIMER 0x20
+#define HFCSX_TRANSB2    0x02
+#define HFCSX_TRANSB1    0x01
+
+/* bits in CIRM (Write) */
+#define HFCSX_IRQ_SELMSK 0x07
+#define HFCSX_IRQ_SELDIS 0x00
+#define HFCSX_RESET  	 0x08
+#define HFCSX_FIFO_RESET 0x80
+
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCSX_INTS_B1TRANS  0x01
+#define HFCSX_INTS_B2TRANS  0x02
+#define HFCSX_INTS_DTRANS   0x04
+#define HFCSX_INTS_B1REC    0x08
+#define HFCSX_INTS_B2REC    0x10
+#define HFCSX_INTS_DREC     0x20
+#define HFCSX_INTS_L1STATE  0x40
+#define HFCSX_INTS_TIMER    0x80
+
+/* bits in INT_M2 */
+#define HFCSX_PROC_TRANS    0x01
+#define HFCSX_GCI_I_CHG     0x02
+#define HFCSX_GCI_MON_REC   0x04
+#define HFCSX_IRQ_ENABLE    0x08
+
+/* bits in STATES */
+#define HFCSX_STATE_MSK     0x0F
+#define HFCSX_LOAD_STATE    0x10
+#define HFCSX_ACTIVATE	    0x20
+#define HFCSX_DO_ACTION     0x40
+#define HFCSX_NT_G2_G3      0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCSX_MASTER	    0x01
+#define HFCSX_SLAVE         0x00
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define SCTRL_B1_ENA	    0x01
+#define SCTRL_B2_ENA	    0x02
+#define SCTRL_MODE_TE       0x00
+#define SCTRL_MODE_NT       0x04
+#define SCTRL_LOW_PRIO	    0x08
+#define SCTRL_SQ_ENA	    0x10
+#define SCTRL_TEST	    0x20
+#define SCTRL_NONE_CAP	    0x40
+#define SCTRL_PWR_DOWN	    0x80
+
+/* bits in SCTRL_E  */
+#define HFCSX_AUTO_AWAKE    0x01
+#define HFCSX_DBIT_1        0x04
+#define HFCSX_IGNORE_COL    0x08
+#define HFCSX_CHG_B1_B2     0x80
+
+/**********************************/
+/* definitions for FIFO selection */
+/**********************************/
+#define HFCSX_SEL_D_RX      5
+#define HFCSX_SEL_D_TX      4
+#define HFCSX_SEL_B1_RX     1
+#define HFCSX_SEL_B1_TX     0
+#define HFCSX_SEL_B2_RX     3
+#define HFCSX_SEL_B2_TX     2
+
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL_32K       0x0200
+#define B_FIFO_SIZE_32K    (0x2000 - B_SUB_VAL_32K)
+#define B_SUB_VAL_8K        0x1A00
+#define B_FIFO_SIZE_8K     (0x2000 - B_SUB_VAL_8K)
+#define D_FIFO_SIZE  512
+#define D_FREG_MASK  0xF
+
+/************************************************************/
+/* structure holding additional dynamic data -> send marker */
+/************************************************************/
+struct hfcsx_extra {
+  unsigned short marker[2*(MAX_B_FRAMES+1) + (MAX_D_FRAMES+1)];
+};
+
+extern void main_irq_hfcsx(struct BCState *bcs);
+extern void inithfcsx(struct IsdnCardState *cs);
+extern void releasehfcsx(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
new file mode 100644
index 0000000..ffd74b8
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -0,0 +1,1828 @@
+/*
+ * hfc_usb.c
+ *
+ * $Id: hfc_usb.c,v 4.34 2005/01/26 17:25:53 martinb1 Exp $
+ *
+ * modular HiSax ISDN driver for Colognechip HFC-S USB chip
+ *
+ * Authors : Peter Sprenger  (sprenger@moving-bytes.de)
+ *           Martin Bachem   (info@colognechip.com)
+ *
+ *           based on the first hfc_usb driver of
+ *           Werner Cornelius (werner@isdn-development.de)
+ *
+ * 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, 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.
+ *
+ * See Version Histroy at the bottom of this file
+ *
+*/
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/usb.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include "hisax.h"
+#include "hisax_if.h"
+#include "hfc_usb.h"
+
+/*
+* Version Information
+* (do not modify the CVS Makros $Revision: 4.34 $ and $Date: 2005/01/26 17:25:53 $ !)
+*/
+static const char *hfcusb_revision =
+    "Revision: 4.34 $ Date: 2005/01/26 17:25:53 $ ";
+
+/* Hisax debug support
+* use "modprobe debug=x" where x is bitfield of USB_DBG & ISDN_DBG
+*/
+#ifdef CONFIG_HISAX_DEBUG
+#include <linux/moduleparam.h>
+#define __debug_variable hfc_debug
+#include "hisax_debug.h"
+static u_int debug;
+module_param(debug, uint, 0);
+int hfc_debug;
+#endif
+
+
+/****************************************/
+/* data defining the devices to be used */
+/****************************************/
+static struct usb_device_id hfc_usb_idtab[] = {
+	{USB_DEVICE(0x0959, 0x2bd0)},	/* Colognechip USB eval TA */
+	{USB_DEVICE(0x0675, 0x1688)},	/* DrayTek miniVigor 128 USB ISDN TA */
+	{USB_DEVICE(0x07b0, 0x0007)},	/* Billion USB TA 2 */
+	{USB_DEVICE(0x0742, 0x2008)},	/* Stollmann USB TA */
+	{USB_DEVICE(0x0742, 0x2009)},	/* Aceex USB ISDN TA */
+	{USB_DEVICE(0x0742, 0x200A)},	/* OEM USB ISDN TA */
+	{USB_DEVICE(0x08e3, 0x0301)},	/* OliTec ISDN USB */
+	{USB_DEVICE(0x07fa, 0x0846)},	/* Bewan ISDN USB TA */
+	{USB_DEVICE(0x07fa, 0x0847)},	/* Djinn Numeris USB */
+	{USB_DEVICE(0x07b0, 0x0006)},	/* Twister ISDN USB TA */
+	{}			/* end with an all-zeroes entry */
+};
+
+/* driver internal device specific data:
+*   VendorID, ProductID, Devicename, LED_SCHEME,
+*   LED's BitMask in HFCUSB_P_DATA Register : LED_USB, LED_S0, LED_B1, LED_B2
+*/
+vendor_data vdata[] = {
+	/* CologneChip Eval TA */
+	{0x0959, 0x2bd0, "ISDN USB TA (Cologne Chip HFC-S USB based)",
+	 LED_OFF, {4, 0, 2, 1}
+	 }
+	,
+	/* DrayTek miniVigor 128 USB ISDN TA */
+	{0x0675, 0x1688, "DrayTek miniVigor 128 USB ISDN TA",
+	 LED_SCHEME1, {1, 2, 0, 0}
+	 }
+	,
+	/* Billion TA */
+	{0x07b0, 0x0007, "Billion tiny USB ISDN TA 128",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	/* Stollmann TA */
+	{0x0742, 0x2008, "Stollmann USB TA",
+	 LED_SCHEME1, {4, 0, 2, 1}
+	 }
+	,
+	/* Aceex USB ISDN TA */
+	{0x0742, 0x2009, "Aceex USB ISDN TA",
+	 LED_SCHEME1, {4, 0, 2, 1}
+	 }
+	,
+	/* OEM USB ISDN TA */
+	{0x0742, 0x200A, "OEM USB ISDN TA",
+	 LED_SCHEME1, {4, 0, 2, 1}
+	 }
+	,
+	/* Olitec TA  */
+	{0x08e3, 0x0301, "Olitec USB RNIS",
+	 LED_SCHEME1, {2, 0, 1, 4}
+	 }
+	,
+	/* Bewan TA   */
+	{0x07fa, 0x0846, "Bewan Modem RNIS USB",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	/* Bewan TA   */
+	{0x07fa, 0x0847, "Djinn Numeris USB",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	/* Twister ISDN TA   */
+	{0x07b0, 0x0006, "Twister ISDN TA",
+	 LED_SCHEME1, {0x80, -64, -32, -16}
+	 }
+	,
+	{0, 0, 0}		/* EOL element */
+};
+
+/***************************************************************/
+/* structure defining input+output fifos (interrupt/bulk mode) */
+/***************************************************************/
+struct usb_fifo;		/* forward definition */
+typedef struct iso_urb_struct {
+	struct urb *purb;
+	__u8 buffer[ISO_BUFFER_SIZE];	/* buffer incoming/outgoing data */
+	struct usb_fifo *owner_fifo;	/* pointer to owner fifo */
+} iso_urb_struct;
+
+
+struct hfcusb_data;		/* forward definition */
+typedef struct usb_fifo {
+	int fifonum;		/* fifo index attached to this structure */
+	int active;		/* fifo is currently active */
+	struct hfcusb_data *hfc;	/* pointer to main structure */
+	int pipe;		/* address of endpoint */
+	__u8 usb_packet_maxlen;	/* maximum length for usb transfer */
+	unsigned int max_size;	/* maximum size of receive/send packet */
+	__u8 intervall;		/* interrupt interval */
+	struct sk_buff *skbuff;	/* actual used buffer */
+	struct urb *urb;	/* transfer structure for usb routines */
+	__u8 buffer[128];	/* buffer incoming/outgoing data */
+	int bit_line;		/* how much bits are in the fifo? */
+
+	volatile __u8 usb_transfer_mode;	/* switched between ISO and INT */
+	iso_urb_struct iso[2];	/* need two urbs to have one always for pending */
+	struct hisax_if *hif;	/* hisax interface */
+	int delete_flg;		/* only delete skbuff once */
+	int last_urblen;	/* remember length of last packet */
+
+} usb_fifo;
+
+/*********************************************/
+/* structure holding all data for one device */
+/*********************************************/
+typedef struct hfcusb_data {
+	/* HiSax Interface for loadable Layer1 drivers */
+	struct hisax_d_if d_if;	/* see hisax_if.h */
+	struct hisax_b_if b_if[2];	/* see hisax_if.h */
+	int protocol;
+
+	struct usb_device *dev;	/* our device */
+	int if_used;		/* used interface number */
+	int alt_used;		/* used alternate config */
+	int ctrl_paksize;	/* control pipe packet size */
+	int ctrl_in_pipe, ctrl_out_pipe;	/* handles for control pipe */
+	int cfg_used;		/* configuration index used */
+	int vend_idx;		/* vendor found */
+	int b_mode[2];		/* B-channel mode */
+	int l1_activated;	/* layer 1 activated */
+	int disc_flag;		/* TRUE if device was disonnected to avoid some USB actions */
+	int packet_size, iso_packet_size;
+
+	/* control pipe background handling */
+	ctrl_buft ctrl_buff[HFC_CTRL_BUFSIZE];	/* buffer holding queued data */
+	volatile int ctrl_in_idx, ctrl_out_idx, ctrl_cnt;	/* input/output pointer + count */
+	struct urb *ctrl_urb;	/* transfer structure for control channel */
+
+	struct usb_ctrlrequest ctrl_write;	/* buffer for control write request */
+	struct usb_ctrlrequest ctrl_read;	/* same for read request */
+
+	__u8 old_led_state, led_state, led_new_data, led_b_active;
+
+	volatile __u8 threshold_mask;	/* threshold actually reported */
+	volatile __u8 bch_enables;	/* or mask for sctrl_r and sctrl register values */
+
+	usb_fifo fifos[HFCUSB_NUM_FIFOS];	/* structure holding all fifo data */
+
+	volatile __u8 l1_state;	/* actual l1 state */
+	struct timer_list t3_timer;	/* timer 3 for activation/deactivation */
+	struct timer_list t4_timer;	/* timer 4 for activation/deactivation */
+	struct timer_list led_timer;	/* timer flashing leds */
+
+} hfcusb_data;
+
+
+static void collect_rx_frame(usb_fifo * fifo, __u8 * data, int len,
+			     int finish);
+
+
+static inline const char *
+symbolic(struct hfcusb_symbolic_list list[], const int num)
+{
+	int i;
+	for (i = 0; list[i].name != NULL; i++)
+		if (list[i].num == num)
+			return (list[i].name);
+	return "<unkown>";
+}
+
+
+/******************************************************/
+/* start next background transfer for control channel */
+/******************************************************/
+static void
+ctrl_start_transfer(hfcusb_data * hfc)
+{
+	if (hfc->ctrl_cnt) {
+		hfc->ctrl_urb->pipe = hfc->ctrl_out_pipe;
+		hfc->ctrl_urb->setup_packet = (u_char *) & hfc->ctrl_write;
+		hfc->ctrl_urb->transfer_buffer = NULL;
+		hfc->ctrl_urb->transfer_buffer_length = 0;
+		hfc->ctrl_write.wIndex =
+		    hfc->ctrl_buff[hfc->ctrl_out_idx].hfc_reg;
+		hfc->ctrl_write.wValue =
+		    hfc->ctrl_buff[hfc->ctrl_out_idx].reg_val;
+
+		usb_submit_urb(hfc->ctrl_urb, GFP_ATOMIC);	/* start transfer */
+	}
+}				/* ctrl_start_transfer */
+
+/************************************/
+/* queue a control transfer request */
+/* return 0 on success.             */
+/************************************/
+static int
+queue_control_request(hfcusb_data * hfc, __u8 reg, __u8 val, int action)
+{
+	ctrl_buft *buf;
+
+	if (hfc->ctrl_cnt >= HFC_CTRL_BUFSIZE)
+		return (1);	/* no space left */
+	buf = &hfc->ctrl_buff[hfc->ctrl_in_idx];	/* pointer to new index */
+	buf->hfc_reg = reg;
+	buf->reg_val = val;
+	buf->action = action;
+	if (++hfc->ctrl_in_idx >= HFC_CTRL_BUFSIZE)
+		hfc->ctrl_in_idx = 0;	/* pointer wrap */
+	if (++hfc->ctrl_cnt == 1)
+		ctrl_start_transfer(hfc);
+	return (0);
+}				/* queue_control_request */
+
+static int
+control_action_handler(hfcusb_data * hfc, int reg, int val, int action)
+{
+	if (!action)
+		return (1);	/* no action defined */
+	return (0);
+}
+
+/***************************************************************/
+/* control completion routine handling background control cmds */
+/***************************************************************/
+static void
+ctrl_complete(struct urb *urb, struct pt_regs *regs)
+{
+	hfcusb_data *hfc = (hfcusb_data *) urb->context;
+	ctrl_buft *buf;
+
+	urb->dev = hfc->dev;
+	if (hfc->ctrl_cnt) {
+		buf = &hfc->ctrl_buff[hfc->ctrl_out_idx];
+		control_action_handler(hfc, buf->hfc_reg, buf->reg_val,
+				       buf->action);
+
+		hfc->ctrl_cnt--;	/* decrement actual count */
+		if (++hfc->ctrl_out_idx >= HFC_CTRL_BUFSIZE)
+			hfc->ctrl_out_idx = 0;	/* pointer wrap */
+
+		ctrl_start_transfer(hfc);	/* start next transfer */
+	}
+}				/* ctrl_complete */
+
+/***************************************************/
+/* write led data to auxport & invert if necessary */
+/***************************************************/
+static void
+write_led(hfcusb_data * hfc, __u8 led_state)
+{
+	if (led_state != hfc->old_led_state) {
+		hfc->old_led_state = led_state;
+		queue_control_request(hfc, HFCUSB_P_DATA, led_state, 1);
+	}
+}
+
+/**************************/
+/* handle LED bits        */
+/**************************/
+static void
+set_led_bit(hfcusb_data * hfc, signed short led_bits, int unset)
+{
+	if (unset) {
+		if (led_bits < 0)
+			hfc->led_state |= abs(led_bits);
+		else
+			hfc->led_state &= ~led_bits;
+	} else {
+		if (led_bits < 0)
+			hfc->led_state &= ~abs(led_bits);
+		else
+			hfc->led_state |= led_bits;
+	}
+}
+
+/******************************************/
+/* invert B-channel LEDs if data is sent  */
+/******************************************/
+static void
+led_timer(hfcusb_data * hfc)
+{
+	static int cnt = 0;
+
+	if (cnt) {
+		if (hfc->led_b_active & 1)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],
+				    0);
+		if (hfc->led_b_active & 2)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],
+				    0);
+	} else {
+		if (!(hfc->led_b_active & 1) || hfc->led_new_data & 1)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],
+				    1);
+		if (!(hfc->led_b_active & 2) || hfc->led_new_data & 2)
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],
+				    1);
+	}
+
+	write_led(hfc, hfc->led_state);
+	hfc->led_new_data = 0;
+
+	cnt = !cnt;
+
+	/* restart 4 hz timer */
+	if (!timer_pending(&hfc->led_timer)) {
+		add_timer(&hfc->led_timer);
+		hfc->led_timer.expires = jiffies + (LED_TIME * HZ) / 1000;
+	}
+}
+
+/**************************/
+/* handle LED requests    */
+/**************************/
+static void
+handle_led(hfcusb_data * hfc, int event)
+{
+	/* if no scheme -> no LED action */
+	if (vdata[hfc->vend_idx].led_scheme == LED_OFF)
+		return;
+
+	switch (event) {
+		case LED_POWER_ON:
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[0],
+				    0);
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],
+				    1);
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[2],
+				    1);
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[3],
+				    1);
+			break;
+		case LED_POWER_OFF:	/* no Power off handling */
+			break;
+		case LED_S0_ON:
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],
+				    0);
+			break;
+		case LED_S0_OFF:
+			set_led_bit(hfc, vdata[hfc->vend_idx].led_bits[1],
+				    1);
+			break;
+		case LED_B1_ON:
+			hfc->led_b_active |= 1;
+			break;
+		case LED_B1_OFF:
+			hfc->led_b_active &= ~1;
+			break;
+		case LED_B1_DATA:
+			hfc->led_new_data |= 1;
+			break;
+		case LED_B2_ON:
+			hfc->led_b_active |= 2;
+			break;
+		case LED_B2_OFF:
+			hfc->led_b_active &= ~2;
+			break;
+		case LED_B2_DATA:
+			hfc->led_new_data |= 2;
+			break;
+	}
+
+	write_led(hfc, hfc->led_state);
+}
+
+/********************************/
+/* called when timer t3 expires */
+/********************************/
+static void
+l1_timer_expire_t3(hfcusb_data * hfc)
+{
+	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,
+			   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG,
+	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T3 expire)");
+#endif
+	hfc->l1_activated = FALSE;
+	handle_led(hfc, LED_S0_OFF);
+	/* deactivate : */
+	queue_control_request(hfc, HFCUSB_STATES, 0x10, 1);
+	queue_control_request(hfc, HFCUSB_STATES, 3, 1);
+}
+
+/********************************/
+/* called when timer t4 expires */
+/********************************/
+static void
+l1_timer_expire_t4(hfcusb_data * hfc)
+{
+	hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,
+			   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG,
+	    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T4 expire)");
+#endif
+	hfc->l1_activated = FALSE;
+	handle_led(hfc, LED_S0_OFF);
+}
+
+/*****************************/
+/* handle S0 state changes   */
+/*****************************/
+static void
+state_handler(hfcusb_data * hfc, __u8 state)
+{
+	__u8 old_state;
+
+	old_state = hfc->l1_state;
+	if (state == old_state || state < 1 || state > 8)
+		return;
+
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG, "HFC-S USB: new S0 state:%d old_state:%d", state,
+	    old_state);
+#endif
+	if (state < 4 || state == 7 || state == 8) {
+		if (timer_pending(&hfc->t3_timer))
+			del_timer(&hfc->t3_timer);
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(ISDN_DBG, "HFC-S USB: T3 deactivated");
+#endif
+	}
+	if (state >= 7) {
+		if (timer_pending(&hfc->t4_timer))
+			del_timer(&hfc->t4_timer);
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(ISDN_DBG, "HFC-S USB: T4 deactivated");
+#endif
+	}
+
+	if (state == 7 && !hfc->l1_activated) {
+		hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+				   PH_ACTIVATE | INDICATION, NULL);
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(ISDN_DBG, "HFC-S USB: PH_ACTIVATE | INDICATION sent");
+#endif
+		hfc->l1_activated = TRUE;
+		handle_led(hfc, LED_S0_ON);
+	} else if (state <= 3 /* && activated */ ) {
+		if (old_state == 7 || old_state == 8) {
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(ISDN_DBG, "HFC-S USB: T4 activated");
+#endif
+			if (!timer_pending(&hfc->t4_timer)) {
+				hfc->t4_timer.expires =
+				    jiffies + (HFC_TIMER_T4 * HZ) / 1000;
+				add_timer(&hfc->t4_timer);
+			}
+		} else {
+			hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+					   PH_DEACTIVATE | INDICATION,
+					   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(ISDN_DBG,
+			    "HFC-S USB: PH_DEACTIVATE | INDICATION sent");
+#endif
+			hfc->l1_activated = FALSE;
+			handle_led(hfc, LED_S0_OFF);
+		}
+	}
+	hfc->l1_state = state;
+}
+
+/* prepare iso urb */
+static void
+fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,
+	      void *buf, int num_packets, int packet_size, int interval,
+	      usb_complete_t complete, void *context)
+{
+	int k;
+
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->complete = complete;
+	urb->number_of_packets = num_packets;
+	urb->transfer_buffer_length = packet_size * num_packets;
+	urb->context = context;
+	urb->transfer_buffer = buf;
+	urb->transfer_flags = URB_ISO_ASAP;
+	urb->actual_length = 0;
+	urb->interval = interval;
+	for (k = 0; k < num_packets; k++) {
+		urb->iso_frame_desc[k].offset = packet_size * k;
+		urb->iso_frame_desc[k].length = packet_size;
+		urb->iso_frame_desc[k].actual_length = 0;
+	}
+}
+
+/* allocs urbs and start isoc transfer with two pending urbs to avoid
+   gaps in the transfer chain */
+static int
+start_isoc_chain(usb_fifo * fifo, int num_packets_per_urb,
+		 usb_complete_t complete, int packet_size)
+{
+	int i, k, errcode;
+
+	printk(KERN_INFO "HFC-S USB: starting ISO-chain for Fifo %i\n",
+	       fifo->fifonum);
+
+	/* allocate Memory for Iso out Urbs */
+	for (i = 0; i < 2; i++) {
+		if (!(fifo->iso[i].purb)) {
+			fifo->iso[i].purb =
+			    usb_alloc_urb(num_packets_per_urb, GFP_KERNEL);
+			if (!(fifo->iso[i].purb)) {
+				printk(KERN_INFO
+				       "alloc urb for fifo %i failed!!!",
+				       fifo->fifonum);
+			}
+			fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo;
+
+			/* Init the first iso */
+			if (ISO_BUFFER_SIZE >=
+			    (fifo->usb_packet_maxlen *
+			     num_packets_per_urb)) {
+				fill_isoc_urb(fifo->iso[i].purb,
+					      fifo->hfc->dev, fifo->pipe,
+					      fifo->iso[i].buffer,
+					      num_packets_per_urb,
+					      fifo->usb_packet_maxlen,
+					      fifo->intervall, complete,
+					      &fifo->iso[i]);
+				memset(fifo->iso[i].buffer, 0,
+				       sizeof(fifo->iso[i].buffer));
+				/* defining packet delimeters in fifo->buffer */
+				for (k = 0; k < num_packets_per_urb; k++) {
+					fifo->iso[i].purb->
+					    iso_frame_desc[k].offset =
+					    k * packet_size;
+					fifo->iso[i].purb->
+					    iso_frame_desc[k].length =
+					    packet_size;
+				}
+			} else {
+				printk(KERN_INFO
+				       "HFC-S USB: ISO Buffer size to small!\n");
+			}
+		}
+		fifo->bit_line = BITLINE_INF;
+
+		errcode = usb_submit_urb(fifo->iso[i].purb, GFP_KERNEL);
+		fifo->active = (errcode >= 0) ? 1 : 0;
+		if (errcode < 0) {
+			printk(KERN_INFO "HFC-S USB: %s  URB nr:%d\n",
+			       symbolic(urb_errlist, errcode), i);
+		};
+	}
+	return (fifo->active);
+}
+
+/* stops running iso chain and frees their pending urbs */
+static void
+stop_isoc_chain(usb_fifo * fifo)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		if (fifo->iso[i].purb) {
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(USB_DBG,
+			    "HFC-S USB: Stopping iso chain for fifo %i.%i",
+			    fifo->fifonum, i);
+#endif
+			usb_unlink_urb(fifo->iso[i].purb);
+			usb_free_urb(fifo->iso[i].purb);
+			fifo->iso[i].purb = NULL;
+		}
+	}
+	if (fifo->urb) {
+		usb_unlink_urb(fifo->urb);
+		usb_free_urb(fifo->urb);
+		fifo->urb = NULL;
+	}
+	fifo->active = 0;
+}
+
+/* defines how much ISO packets are handled in one URB */
+static int iso_packets[8] =
+    { ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B,
+	ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D
+};
+
+/*****************************************************/
+/* transmit completion routine for all ISO tx fifos */
+/*****************************************************/
+static void
+tx_iso_complete(struct urb *urb, struct pt_regs *regs)
+{
+	iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context;
+	usb_fifo *fifo = context_iso_urb->owner_fifo;
+	hfcusb_data *hfc = fifo->hfc;
+	int k, tx_offset, num_isoc_packets, sink, len, current_len,
+	    errcode;
+	int frame_complete, transp_mode, fifon, status;
+	__u8 threshbit;
+	__u8 threshtable[8] = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80 };
+
+	fifon = fifo->fifonum;
+	status = urb->status;
+
+	tx_offset = 0;
+
+	if (fifo->active && !status) {
+		transp_mode = 0;
+		if (fifon < 4 && hfc->b_mode[fifon / 2] == L1_MODE_TRANS)
+			transp_mode = TRUE;
+
+		/* is FifoFull-threshold set for our channel? */
+		threshbit = threshtable[fifon] & hfc->threshold_mask;
+		num_isoc_packets = iso_packets[fifon];
+
+		/* predict dataflow to avoid fifo overflow */
+		if (fifon >= HFCUSB_D_TX) {
+			sink = (threshbit) ? SINK_DMIN : SINK_DMAX;
+		} else {
+			sink = (threshbit) ? SINK_MIN : SINK_MAX;
+		}
+		fill_isoc_urb(urb, fifo->hfc->dev, fifo->pipe,
+			      context_iso_urb->buffer, num_isoc_packets,
+			      fifo->usb_packet_maxlen, fifo->intervall,
+			      tx_iso_complete, urb->context);
+		memset(context_iso_urb->buffer, 0,
+		       sizeof(context_iso_urb->buffer));
+		frame_complete = FALSE;
+		/* Generate next Iso Packets */
+		for (k = 0; k < num_isoc_packets; ++k) {
+			if (fifo->skbuff) {
+				len = fifo->skbuff->len;
+				/* we lower data margin every msec */
+				fifo->bit_line -= sink;
+				current_len = (0 - fifo->bit_line) / 8;
+				/* maximum 15 byte for every ISO packet makes our life easier */
+				if (current_len > 14)
+					current_len = 14;
+				current_len =
+				    (len <=
+				     current_len) ? len : current_len;
+				/* how much bit do we put on the line? */
+				fifo->bit_line += current_len * 8;
+
+				context_iso_urb->buffer[tx_offset] = 0;
+				if (current_len == len) {
+					if (!transp_mode) {
+						/* here frame completion */
+						context_iso_urb->
+						    buffer[tx_offset] = 1;
+						/* add 2 byte flags and 16bit CRC at end of ISDN frame */
+						fifo->bit_line += 32;
+					}
+					frame_complete = TRUE;
+				}
+
+				memcpy(context_iso_urb->buffer +
+				       tx_offset + 1, fifo->skbuff->data,
+				       current_len);
+				skb_pull(fifo->skbuff, current_len);
+
+				/* define packet delimeters within the URB buffer */
+				urb->iso_frame_desc[k].offset = tx_offset;
+				urb->iso_frame_desc[k].length =
+				    current_len + 1;
+
+				tx_offset += (current_len + 1);
+				if (!transp_mode) {
+					if (fifon == HFCUSB_B1_TX)
+						handle_led(hfc,
+							   LED_B1_DATA);
+					if (fifon == HFCUSB_B2_TX)
+						handle_led(hfc,
+							   LED_B2_DATA);
+				}
+			} else {
+				urb->iso_frame_desc[k].offset =
+				    tx_offset++;
+
+				urb->iso_frame_desc[k].length = 1;
+				fifo->bit_line -= sink;	/* we lower data margin every msec */
+
+				if (fifo->bit_line < BITLINE_INF) {
+					fifo->bit_line = BITLINE_INF;
+				}
+			}
+
+			if (frame_complete) {
+				fifo->delete_flg = TRUE;
+				fifo->hif->l1l2(fifo->hif,
+						PH_DATA | CONFIRM,
+						(void *) fifo->skbuff->
+						truesize);
+				if (fifo->skbuff && fifo->delete_flg) {
+					dev_kfree_skb_any(fifo->skbuff);
+					fifo->skbuff = NULL;
+					fifo->delete_flg = FALSE;
+				}
+				frame_complete = FALSE;
+			}
+		}
+		errcode = usb_submit_urb(urb, GFP_ATOMIC);
+		if (errcode < 0) {
+			printk(KERN_INFO
+			       "HFC-S USB: error submitting ISO URB: %d \n",
+			       errcode);
+		}
+	} else {
+		if (status && !hfc->disc_flag) {
+			printk(KERN_INFO
+			       "HFC-S USB: tx_iso_complete : urb->status %s (%i), fifonum=%d\n",
+			       symbolic(urb_errlist, status), status,
+			       fifon);
+		}
+	}
+}				/* tx_iso_complete */
+
+/*****************************************************/
+/* receive completion routine for all ISO tx fifos   */
+/*****************************************************/
+static void
+rx_iso_complete(struct urb *urb, struct pt_regs *regs)
+{
+	iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context;
+	usb_fifo *fifo = context_iso_urb->owner_fifo;
+	hfcusb_data *hfc = fifo->hfc;
+	int k, len, errcode, offset, num_isoc_packets, fifon, maxlen,
+	    status;
+	unsigned int iso_status;
+	__u8 *buf;
+	static __u8 eof[8];
+#ifdef CONFIG_HISAX_DEBUG
+	__u8 i;
+#endif
+
+	fifon = fifo->fifonum;
+	status = urb->status;
+
+	if (urb->status == -EOVERFLOW) {
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(USB_DBG,
+		    "HFC-USB: ignoring USB DATAOVERRUN  for fifo  %i \n",
+		    fifon);
+#endif
+		status = 0;
+	}
+	if (fifo->active && !status) {
+		num_isoc_packets = iso_packets[fifon];
+		maxlen = fifo->usb_packet_maxlen;
+		for (k = 0; k < num_isoc_packets; ++k) {
+			len = urb->iso_frame_desc[k].actual_length;
+			offset = urb->iso_frame_desc[k].offset;
+			buf = context_iso_urb->buffer + offset;
+			iso_status = urb->iso_frame_desc[k].status;
+#ifdef CONFIG_HISAX_DEBUG
+			if (iso_status && !hfc->disc_flag)
+				DBG(USB_DBG,
+				    "HFC-S USB: ISO packet failure - status:%x",
+				    iso_status);
+
+			if ((fifon == 5) && (debug > 1)) {
+				printk(KERN_INFO
+				       "HFC-S USB: ISO-D-RX lst_urblen:%2d "
+				       "act_urblen:%2d max-urblen:%2d "
+				       "EOF:0x%0x DATA: ",
+				       fifo->last_urblen, len, maxlen,
+				       eof[5]);
+				for (i = 0; i < len; i++)
+					printk("%.2x ", buf[i]);
+				printk("\n");
+			}
+#endif
+			if (fifo->last_urblen != maxlen) {
+				/* the threshold mask is in the 2nd status byte */
+				hfc->threshold_mask = buf[1];
+				/* care for L1 state only for D-Channel
+				   to avoid overlapped iso completions */
+				if (fifon == 5) {
+					/* the S0 state is in the upper half
+					   of the 1st status byte */
+					state_handler(hfc, buf[0] >> 4);
+				}
+				eof[fifon] = buf[0] & 1;
+				if (len > 2)
+					collect_rx_frame(fifo, buf + 2,
+							 len - 2,
+							 (len <
+							  maxlen) ?
+							 eof[fifon] : 0);
+			} else {
+				collect_rx_frame(fifo, buf, len,
+						 (len <
+						  maxlen) ? eof[fifon] :
+						 0);
+			}
+			fifo->last_urblen = len;
+		}
+
+		fill_isoc_urb(urb, fifo->hfc->dev, fifo->pipe,
+			      context_iso_urb->buffer, num_isoc_packets,
+			      fifo->usb_packet_maxlen, fifo->intervall,
+			      rx_iso_complete, urb->context);
+		errcode = usb_submit_urb(urb, GFP_ATOMIC);
+		if (errcode < 0) {
+			printk(KERN_INFO
+			       "HFC-S USB: error submitting ISO URB: %d \n",
+			       errcode);
+		}
+	} else {
+		if (status && !hfc->disc_flag) {
+			printk(KERN_INFO
+			       "HFC-S USB: rx_iso_complete : "
+			       "urb->status %d, fifonum %d\n",
+			       status, fifon);
+		}
+	}
+}				/* rx_iso_complete */
+
+/*****************************************************/
+/* collect data from interrupt or isochron in        */
+/*****************************************************/
+static void
+collect_rx_frame(usb_fifo * fifo, __u8 * data, int len, int finish)
+{
+	hfcusb_data *hfc = fifo->hfc;
+	int transp_mode, fifon;
+#ifdef CONFIG_HISAX_DEBUG
+	int i;
+#endif
+	fifon = fifo->fifonum;
+	transp_mode = 0;
+	if (fifon < 4 && hfc->b_mode[fifon / 2] == L1_MODE_TRANS)
+		transp_mode = TRUE;
+
+	if (!fifo->skbuff) {
+		fifo->skbuff = dev_alloc_skb(fifo->max_size + 3);
+		if (!fifo->skbuff) {
+			printk(KERN_INFO
+			       "HFC-S USB: cannot allocate buffer (dev_alloc_skb) fifo:%d\n",
+			       fifon);
+			return;
+		}
+	}
+	if (len) {
+		if (fifo->skbuff->len + len < fifo->max_size) {
+			memcpy(skb_put(fifo->skbuff, len), data, len);
+		} else {
+#ifdef CONFIG_HISAX_DEBUG
+			printk(KERN_INFO "HFC-S USB: ");
+			for (i = 0; i < 15; i++)
+				printk("%.2x ",
+				       fifo->skbuff->data[fifo->skbuff->
+							  len - 15 + i]);
+			printk("\n");
+#endif
+			printk(KERN_INFO
+			       "HCF-USB: got frame exceeded fifo->max_size:%d on fifo:%d\n",
+			       fifo->max_size, fifon);
+		}
+	}
+	if (transp_mode && fifo->skbuff->len >= 128) {
+		fifo->hif->l1l2(fifo->hif, PH_DATA | INDICATION,
+				fifo->skbuff);
+		fifo->skbuff = NULL;
+		return;
+	}
+	/* we have a complete hdlc packet */
+	if (finish) {
+		if ((!fifo->skbuff->data[fifo->skbuff->len - 1])
+		    && (fifo->skbuff->len > 3)) {
+			/* remove CRC & status */
+			skb_trim(fifo->skbuff, fifo->skbuff->len - 3);
+			if (fifon == HFCUSB_PCM_RX) {
+				fifo->hif->l1l2(fifo->hif,
+						PH_DATA_E | INDICATION,
+						fifo->skbuff);
+			} else
+				fifo->hif->l1l2(fifo->hif,
+						PH_DATA | INDICATION,
+						fifo->skbuff);
+			fifo->skbuff = NULL;	/* buffer was freed from upper layer */
+		} else {
+			if (fifo->skbuff->len > 3) {
+				printk(KERN_INFO
+				       "HFC-S USB: got frame %d bytes but CRC ERROR on fifo:%d!!!\n",
+				       fifo->skbuff->len, fifon);
+#ifdef CONFIG_HISAX_DEBUG
+				if (debug > 1) {
+					printk(KERN_INFO "HFC-S USB: ");
+					for (i = 0; i < 15; i++)
+						printk("%.2x ",
+						       fifo->skbuff->
+						       data[fifo->skbuff->
+							    len - 15 + i]);
+					printk("\n");
+				}
+#endif
+			}
+#ifdef CONFIG_HISAX_DEBUG
+			else {
+				printk(KERN_INFO
+				       "HFC-S USB: frame to small (%d bytes)!!!\n",
+				       fifo->skbuff->len);
+			}
+#endif
+			skb_trim(fifo->skbuff, 0);
+		}
+	}
+
+	/* LED flashing only in HDLC mode */
+	if (!transp_mode) {
+		if (fifon == HFCUSB_B1_RX)
+			handle_led(hfc, LED_B1_DATA);
+		if (fifon == HFCUSB_B2_RX)
+			handle_led(hfc, LED_B2_DATA);
+	}
+}
+
+/***********************************************/
+/* receive completion routine for all rx fifos */
+/***********************************************/
+static void
+rx_complete(struct urb *urb, struct pt_regs *regs)
+{
+	int len;
+	int status;
+	__u8 *buf, maxlen, fifon;
+	usb_fifo *fifo = (usb_fifo *) urb->context;
+	hfcusb_data *hfc = fifo->hfc;
+	static __u8 eof[8];
+#ifdef CONFIG_HISAX_DEBUG
+	__u8 i;
+#endif
+
+	urb->dev = hfc->dev;	/* security init */
+
+	fifon = fifo->fifonum;
+	if ((!fifo->active) || (urb->status)) {
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(USB_DBG, "HFC-S USB: RX-Fifo %i is going down (%i)",
+		    fifon, urb->status);
+#endif
+		fifo->urb->interval = 0;	/* cancel automatic rescheduling */
+		if (fifo->skbuff) {
+			dev_kfree_skb_any(fifo->skbuff);
+			fifo->skbuff = NULL;
+		}
+		return;
+	}
+	len = urb->actual_length;
+	buf = fifo->buffer;
+	maxlen = fifo->usb_packet_maxlen;
+
+#ifdef CONFIG_HISAX_DEBUG
+	if ((fifon == 5) && (debug > 1)) {
+		printk(KERN_INFO
+		       "HFC-S USB: INT-D-RX lst_urblen:%2d act_urblen:%2d max-urblen:%2d EOF:0x%0x DATA: ",
+		       fifo->last_urblen, len, maxlen, eof[5]);
+		for (i = 0; i < len; i++)
+			printk("%.2x ", buf[i]);
+		printk("\n");
+	}
+#endif
+
+	if (fifo->last_urblen != fifo->usb_packet_maxlen) {
+		/* the threshold mask is in the 2nd status byte */
+		hfc->threshold_mask = buf[1];
+		/* the S0 state is in the upper half of the 1st status byte */
+		state_handler(hfc, buf[0] >> 4);
+		eof[fifon] = buf[0] & 1;
+		/* if we have more than the 2 status bytes -> collect data */
+		if (len > 2)
+			collect_rx_frame(fifo, buf + 2,
+					 urb->actual_length - 2,
+					 (len < maxlen) ? eof[fifon] : 0);
+	} else {
+		collect_rx_frame(fifo, buf, urb->actual_length,
+				 (len < maxlen) ? eof[fifon] : 0);
+	}
+	fifo->last_urblen = urb->actual_length;
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status) {
+		printk(KERN_INFO
+		       "HFC-S USB: error resubmitting URN at rx_complete...\n");
+	}
+}				/* rx_complete */
+
+/***************************************************/
+/* start the interrupt transfer for the given fifo */
+/***************************************************/
+static void
+start_int_fifo(usb_fifo * fifo)
+{
+	int errcode;
+
+	printk(KERN_INFO "HFC-S USB: starting intr IN fifo:%d\n",
+	       fifo->fifonum);
+
+	if (!fifo->urb) {
+		fifo->urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!fifo->urb)
+			return;
+	}
+	usb_fill_int_urb(fifo->urb, fifo->hfc->dev, fifo->pipe,
+			 fifo->buffer, fifo->usb_packet_maxlen,
+			 rx_complete, fifo, fifo->intervall);
+	fifo->active = 1;	/* must be marked active */
+	errcode = usb_submit_urb(fifo->urb, GFP_KERNEL);
+	if (errcode) {
+		printk(KERN_INFO
+		       "HFC-S USB: submit URB error(start_int_info): status:%i\n",
+		       errcode);
+		fifo->active = 0;
+		fifo->skbuff = NULL;
+	}
+}				/* start_int_fifo */
+
+/*****************************/
+/* set the B-channel mode    */
+/*****************************/
+static void
+set_hfcmode(hfcusb_data * hfc, int channel, int mode)
+{
+	__u8 val, idx_table[2] = { 0, 2 };
+
+	if (hfc->disc_flag) {
+		return;
+	}
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(ISDN_DBG, "HFC-S USB: setting channel %d to mode %d", channel,
+	    mode);
+#endif
+	hfc->b_mode[channel] = mode;
+
+	/* setup CON_HDLC */
+	val = 0;
+	if (mode != L1_MODE_NULL)
+		val = 8;	/* enable fifo? */
+	if (mode == L1_MODE_TRANS)
+		val |= 2;	/* set transparent bit */
+
+	/* set FIFO to transmit register */
+	queue_control_request(hfc, HFCUSB_FIFO, idx_table[channel], 1);
+	queue_control_request(hfc, HFCUSB_CON_HDLC, val, 1);
+	/* reset fifo */
+	queue_control_request(hfc, HFCUSB_INC_RES_F, 2, 1);
+	/* set FIFO to receive register */
+	queue_control_request(hfc, HFCUSB_FIFO, idx_table[channel] + 1, 1);
+	queue_control_request(hfc, HFCUSB_CON_HDLC, val, 1);
+	/* reset fifo */
+	queue_control_request(hfc, HFCUSB_INC_RES_F, 2, 1);
+
+	val = 0x40;
+	if (hfc->b_mode[0])
+		val |= 1;
+	if (hfc->b_mode[1])
+		val |= 2;
+	queue_control_request(hfc, HFCUSB_SCTRL, val, 1);
+
+	val = 0;
+	if (hfc->b_mode[0])
+		val |= 1;
+	if (hfc->b_mode[1])
+		val |= 2;
+	queue_control_request(hfc, HFCUSB_SCTRL_R, val, 1);
+
+	if (mode == L1_MODE_NULL) {
+		if (channel)
+			handle_led(hfc, LED_B2_OFF);
+		else
+			handle_led(hfc, LED_B1_OFF);
+	} else {
+		if (channel)
+			handle_led(hfc, LED_B2_ON);
+		else
+			handle_led(hfc, LED_B1_ON);
+	}
+}
+
+void
+hfc_usb_l2l1(struct hisax_if *my_hisax_if, int pr, void *arg)
+{
+	usb_fifo *fifo = my_hisax_if->priv;
+	hfcusb_data *hfc = fifo->hfc;
+
+	switch (pr) {
+		case PH_ACTIVATE | REQUEST:
+			if (fifo->fifonum == HFCUSB_D_TX) {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 D-chan: PH_ACTIVATE | REQUEST");
+#endif
+				if (hfc->l1_state != 3
+				    && hfc->l1_state != 7) {
+					hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+							   PH_DEACTIVATE |
+							   INDICATION,
+							   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+					DBG(ISDN_DBG,
+					    "HFC-S USB: PH_DEACTIVATE | INDICATION sent (not state 3 or 7)");
+#endif
+				} else {
+					if (hfc->l1_state == 7) {	/* l1 already active */
+						hfc->d_if.ifc.l1l2(&hfc->
+								   d_if.
+								   ifc,
+								   PH_ACTIVATE
+								   |
+								   INDICATION,
+								   NULL);
+#ifdef CONFIG_HISAX_DEBUG
+						DBG(ISDN_DBG,
+						    "HFC-S USB: PH_ACTIVATE | INDICATION sent again ;)");
+#endif
+					} else {
+						/* force sending sending INFO1 */
+						queue_control_request(hfc,
+								      HFCUSB_STATES,
+								      0x14,
+								      1);
+						mdelay(1);
+						/* start l1 activation */
+						queue_control_request(hfc,
+								      HFCUSB_STATES,
+								      0x04,
+								      1);
+						if (!timer_pending
+						    (&hfc->t3_timer)) {
+							hfc->t3_timer.
+							    expires =
+							    jiffies +
+							    (HFC_TIMER_T3 *
+							     HZ) / 1000;
+							add_timer(&hfc->
+								  t3_timer);
+						}
+					}
+				}
+			} else {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 Bx-chan: PH_ACTIVATE | REQUEST");
+#endif
+				set_hfcmode(hfc,
+					    (fifo->fifonum ==
+					     HFCUSB_B1_TX) ? 0 : 1,
+					    (int) arg);
+				fifo->hif->l1l2(fifo->hif,
+						PH_ACTIVATE | INDICATION,
+						NULL);
+			}
+			break;
+		case PH_DEACTIVATE | REQUEST:
+			if (fifo->fifonum == HFCUSB_D_TX) {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 D-chan: PH_DEACTIVATE | REQUEST");
+#endif
+				printk(KERN_INFO
+				       "HFC-S USB: ISDN TE device should not deativate...\n");
+			} else {
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(ISDN_DBG,
+				    "HFC_USB: hfc_usb_d_l2l1 Bx-chan: PH_DEACTIVATE | REQUEST");
+#endif
+				set_hfcmode(hfc,
+					    (fifo->fifonum ==
+					     HFCUSB_B1_TX) ? 0 : 1,
+					    (int) L1_MODE_NULL);
+				fifo->hif->l1l2(fifo->hif,
+						PH_DEACTIVATE | INDICATION,
+						NULL);
+			}
+			break;
+		case PH_DATA | REQUEST:
+			if (fifo->skbuff && fifo->delete_flg) {
+				dev_kfree_skb_any(fifo->skbuff);
+				fifo->skbuff = NULL;
+				fifo->delete_flg = FALSE;
+			}
+			fifo->skbuff = arg;	/* we have a new buffer */
+			break;
+		default:
+			printk(KERN_INFO
+			       "HFC_USB: hfc_usb_d_l2l1: unkown state : %#x\n",
+			       pr);
+			break;
+	}
+}
+
+/***************************************************************************/
+/* usb_init is called once when a new matching device is detected to setup */
+/* main parameters. It registers the driver at the main hisax module.      */
+/* on success 0 is returned.                                               */
+/***************************************************************************/
+static int
+usb_init(hfcusb_data * hfc)
+{
+	usb_fifo *fifo;
+	int i, err;
+	u_char b;
+	struct hisax_b_if *p_b_if[2];
+
+	/* check the chip id */
+	if (read_usb(hfc, HFCUSB_CHIP_ID, &b) != 1) {
+		printk(KERN_INFO "HFC-USB: cannot read chip id\n");
+		return (1);
+	}
+	if (b != HFCUSB_CHIPID) {
+		printk(KERN_INFO "HFC-S USB: Invalid chip id 0x%02x\n", b);
+		return (1);
+	}
+
+	/* first set the needed config, interface and alternate */
+	err = usb_set_interface(hfc->dev, hfc->if_used, hfc->alt_used);
+
+	/* do Chip reset */
+	write_usb(hfc, HFCUSB_CIRM, 8);
+	/* aux = output, reset off */
+	write_usb(hfc, HFCUSB_CIRM, 0x10);
+
+	/* set USB_SIZE to match the the wMaxPacketSize for INT or BULK transfers */
+	write_usb(hfc, HFCUSB_USB_SIZE,
+		  (hfc->packet_size / 8) | ((hfc->packet_size / 8) << 4));
+
+	/* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */
+	write_usb(hfc, HFCUSB_USB_SIZE_I, hfc->iso_packet_size);
+
+	/* enable PCM/GCI master mode */
+	write_usb(hfc, HFCUSB_MST_MODE1, 0);	/* set default values */
+	write_usb(hfc, HFCUSB_MST_MODE0, 1);	/* enable master mode */
+
+	/* init the fifos */
+	write_usb(hfc, HFCUSB_F_THRES,
+		  (HFCUSB_TX_THRESHOLD /
+		   8) | ((HFCUSB_RX_THRESHOLD / 8) << 4));
+
+	fifo = hfc->fifos;
+	for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+		write_usb(hfc, HFCUSB_FIFO, i);	/* select the desired fifo */
+		fifo[i].skbuff = NULL;	/* init buffer pointer */
+		fifo[i].max_size =
+		    (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN;
+		fifo[i].last_urblen = 0;
+		/* set 2 bit for D- & E-channel */
+		write_usb(hfc, HFCUSB_HDLC_PAR,
+			  ((i <= HFCUSB_B2_RX) ? 0 : 2));
+		/* rx hdlc, enable IFF for D-channel */
+		write_usb(hfc, HFCUSB_CON_HDLC,
+			  ((i == HFCUSB_D_TX) ? 0x09 : 0x08));
+		write_usb(hfc, HFCUSB_INC_RES_F, 2);	/* reset the fifo */
+	}
+
+	write_usb(hfc, HFCUSB_CLKDEL, 0x0f);	/* clock delay value */
+	write_usb(hfc, HFCUSB_STATES, 3 | 0x10);	/* set deactivated mode */
+	write_usb(hfc, HFCUSB_STATES, 3);	/* enable state machine */
+
+	write_usb(hfc, HFCUSB_SCTRL_R, 0);	/* disable both B receivers */
+	write_usb(hfc, HFCUSB_SCTRL, 0x40);	/* disable B transmitters + capacitive mode */
+
+	/* set both B-channel to not connected */
+	hfc->b_mode[0] = L1_MODE_NULL;
+	hfc->b_mode[1] = L1_MODE_NULL;
+
+	hfc->l1_activated = FALSE;
+	hfc->disc_flag = FALSE;
+	hfc->led_state = 0;
+	hfc->led_new_data = 0;
+	hfc->old_led_state = 0;
+
+	/* init the t3 timer */
+	init_timer(&hfc->t3_timer);
+	hfc->t3_timer.data = (long) hfc;
+	hfc->t3_timer.function = (void *) l1_timer_expire_t3;
+
+	/* init the t4 timer */
+	init_timer(&hfc->t4_timer);
+	hfc->t4_timer.data = (long) hfc;
+	hfc->t4_timer.function = (void *) l1_timer_expire_t4;
+
+	/* init the led timer */
+	init_timer(&hfc->led_timer);
+	hfc->led_timer.data = (long) hfc;
+	hfc->led_timer.function = (void *) led_timer;
+
+	/* trigger 4 hz led timer */
+	if (!timer_pending(&hfc->led_timer)) {
+		hfc->led_timer.expires = jiffies + (LED_TIME * HZ) / 1000;
+		add_timer(&hfc->led_timer);
+	}
+
+	/* init the background machinery for control requests */
+	hfc->ctrl_read.bRequestType = 0xc0;
+	hfc->ctrl_read.bRequest = 1;
+	hfc->ctrl_read.wLength = 1;
+	hfc->ctrl_write.bRequestType = 0x40;
+	hfc->ctrl_write.bRequest = 0;
+	hfc->ctrl_write.wLength = 0;
+	usb_fill_control_urb(hfc->ctrl_urb,
+			     hfc->dev,
+			     hfc->ctrl_out_pipe,
+			     (u_char *) & hfc->ctrl_write,
+			     NULL, 0, ctrl_complete, hfc);
+	/* Init All Fifos */
+	for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+		hfc->fifos[i].iso[0].purb = NULL;
+		hfc->fifos[i].iso[1].purb = NULL;
+		hfc->fifos[i].active = 0;
+	}
+	/* register Modul to upper Hisax Layers */
+	hfc->d_if.owner = THIS_MODULE;
+	hfc->d_if.ifc.priv = &hfc->fifos[HFCUSB_D_TX];
+	hfc->d_if.ifc.l2l1 = hfc_usb_l2l1;
+	for (i = 0; i < 2; i++) {
+		hfc->b_if[i].ifc.priv = &hfc->fifos[HFCUSB_B1_TX + i * 2];
+		hfc->b_if[i].ifc.l2l1 = hfc_usb_l2l1;
+		p_b_if[i] = &hfc->b_if[i];
+	}
+	/* default Prot: EURO ISDN, should be a module_param */
+	hfc->protocol = 2;
+	hisax_register(&hfc->d_if, p_b_if, "hfc_usb", hfc->protocol);
+
+#ifdef CONFIG_HISAX_DEBUG
+	hfc_debug = debug;
+#endif
+
+	for (i = 0; i < 4; i++)
+		hfc->fifos[i].hif = &p_b_if[i / 2]->ifc;
+	for (i = 4; i < 8; i++)
+		hfc->fifos[i].hif = &hfc->d_if.ifc;
+
+	/* 3 (+1) INT IN + 3 ISO OUT */
+	if (hfc->cfg_used == CNF_3INT3ISO || hfc->cfg_used == CNF_4INT3ISO) {
+		start_int_fifo(hfc->fifos + HFCUSB_D_RX);
+		if (hfc->fifos[HFCUSB_PCM_RX].pipe)
+			start_int_fifo(hfc->fifos + HFCUSB_PCM_RX);
+		start_int_fifo(hfc->fifos + HFCUSB_B1_RX);
+		start_int_fifo(hfc->fifos + HFCUSB_B2_RX);
+	}
+	/* 3 (+1) ISO IN + 3 ISO OUT */
+	if (hfc->cfg_used == CNF_3ISO3ISO || hfc->cfg_used == CNF_4ISO3ISO) {
+		start_isoc_chain(hfc->fifos + HFCUSB_D_RX, ISOC_PACKETS_D,
+				 rx_iso_complete, 16);
+		if (hfc->fifos[HFCUSB_PCM_RX].pipe)
+			start_isoc_chain(hfc->fifos + HFCUSB_PCM_RX,
+					 ISOC_PACKETS_D, rx_iso_complete,
+					 16);
+		start_isoc_chain(hfc->fifos + HFCUSB_B1_RX, ISOC_PACKETS_B,
+				 rx_iso_complete, 16);
+		start_isoc_chain(hfc->fifos + HFCUSB_B2_RX, ISOC_PACKETS_B,
+				 rx_iso_complete, 16);
+	}
+
+	start_isoc_chain(hfc->fifos + HFCUSB_D_TX, ISOC_PACKETS_D,
+			 tx_iso_complete, 1);
+	start_isoc_chain(hfc->fifos + HFCUSB_B1_TX, ISOC_PACKETS_B,
+			 tx_iso_complete, 1);
+	start_isoc_chain(hfc->fifos + HFCUSB_B2_TX, ISOC_PACKETS_B,
+			 tx_iso_complete, 1);
+
+	handle_led(hfc, LED_POWER_ON);
+
+	return (0);
+}				/* usb_init */
+
+/*************************************************/
+/* function called to probe a new plugged device */
+/*************************************************/
+static int
+hfc_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	hfcusb_data *context;
+	struct usb_host_interface *iface = intf->cur_altsetting;
+	struct usb_host_interface *iface_used = NULL;
+	struct usb_host_endpoint *ep;
+	int ifnum = iface->desc.bInterfaceNumber;
+	int i, idx, alt_idx, probe_alt_setting, vend_idx, cfg_used, *vcf,
+	    attr, cfg_found, cidx, ep_addr;
+	int cmptbl[16], small_match, iso_packet_size, packet_size,
+	    alt_used = 0;
+
+	vend_idx = 0xffff;
+	for (i = 0; vdata[i].vendor; i++) {
+		if (dev->descriptor.idVendor == vdata[i].vendor
+		    && dev->descriptor.idProduct == vdata[i].prod_id)
+			vend_idx = i;
+	}
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(USB_DBG,
+	    "HFC-USB: probing interface(%d) actalt(%d) minor(%d)\n", ifnum,
+	    iface->desc.bAlternateSetting, intf->minor);
+#endif
+	printk(KERN_INFO
+	       "HFC-S USB: probing interface(%d) actalt(%d) minor(%d)\n",
+	       ifnum, iface->desc.bAlternateSetting, intf->minor);
+
+	if (vend_idx != 0xffff) {
+#ifdef CONFIG_HISAX_DEBUG
+		DBG(USB_DBG, "HFC-S USB: found vendor idx:%d  name:%s",
+		    vend_idx, vdata[vend_idx].vend_name);
+#endif
+		/* if vendor and product ID is OK, start probing alternate settings */
+		alt_idx = 0;
+		small_match = 0xffff;
+
+		/* default settings */
+		iso_packet_size = 16;
+		packet_size = 64;
+
+		while (alt_idx < intf->num_altsetting) {
+			iface = intf->altsetting + alt_idx;
+			probe_alt_setting = iface->desc.bAlternateSetting;
+			cfg_used = 0;
+
+			/* check for config EOL element */
+			while (validconf[cfg_used][0]) {
+				cfg_found = TRUE;
+				vcf = validconf[cfg_used];
+				/* first endpoint descriptor */
+				ep = iface->endpoint;
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(USB_DBG,
+				    "HFC-S USB: (if=%d alt=%d cfg_used=%d)\n",
+				    ifnum, probe_alt_setting, cfg_used);
+#endif
+				memcpy(cmptbl, vcf, 16 * sizeof(int));
+
+				/* check for all endpoints in this alternate setting */
+				for (i = 0; i < iface->desc.bNumEndpoints;
+				     i++) {
+					ep_addr =
+					    ep->desc.bEndpointAddress;
+					/* get endpoint base */
+					idx = ((ep_addr & 0x7f) - 1) * 2;
+					if (ep_addr & 0x80)
+						idx++;
+					attr = ep->desc.bmAttributes;
+					if (cmptbl[idx] == EP_NUL) {
+						cfg_found = FALSE;
+					}
+					if (attr == USB_ENDPOINT_XFER_INT
+					    && cmptbl[idx] == EP_INT)
+						cmptbl[idx] = EP_NUL;
+					if (attr == USB_ENDPOINT_XFER_BULK
+					    && cmptbl[idx] == EP_BLK)
+						cmptbl[idx] = EP_NUL;
+					if (attr == USB_ENDPOINT_XFER_ISOC
+					    && cmptbl[idx] == EP_ISO)
+						cmptbl[idx] = EP_NUL;
+
+					/* check if all INT endpoints match minimum interval */
+					if (attr == USB_ENDPOINT_XFER_INT
+					    && ep->desc.bInterval <
+					    vcf[17]) {
+#ifdef CONFIG_HISAX_DEBUG
+						if (cfg_found)
+							DBG(USB_DBG,
+							    "HFC-S USB: Interrupt Endpoint interval < %d found - skipping config",
+							    vcf[17]);
+#endif
+						cfg_found = FALSE;
+					}
+					ep++;
+				}
+				for (i = 0; i < 16; i++) {
+					/* all entries must be EP_NOP or EP_NUL for a valid config */
+					if (cmptbl[i] != EP_NOP
+					    && cmptbl[i] != EP_NUL)
+						cfg_found = FALSE;
+				}
+				if (cfg_found) {
+					if (cfg_used < small_match) {
+						small_match = cfg_used;
+						alt_used =
+						    probe_alt_setting;
+						iface_used = iface;
+					}
+#ifdef CONFIG_HISAX_DEBUG
+					DBG(USB_DBG,
+					    "HFC-USB: small_match=%x %x\n",
+					    small_match, alt_used);
+#endif
+				}
+				cfg_used++;
+			}
+			alt_idx++;
+		}		/* (alt_idx < intf->num_altsetting) */
+
+		/* found a valid USB Ta Endpint config */
+		if (small_match != 0xffff) {
+			iface = iface_used;
+			if (!
+			    (context =
+			     kmalloc(sizeof(hfcusb_data), GFP_KERNEL)))
+				return (-ENOMEM);	/* got no mem */
+			memset(context, 0, sizeof(hfcusb_data));
+
+			ep = iface->endpoint;
+			vcf = validconf[small_match];
+
+			for (i = 0; i < iface->desc.bNumEndpoints; i++) {
+				ep_addr = ep->desc.bEndpointAddress;
+				/* get endpoint base */
+				idx = ((ep_addr & 0x7f) - 1) * 2;
+				if (ep_addr & 0x80)
+					idx++;
+				cidx = idx & 7;
+				attr = ep->desc.bmAttributes;
+
+				/* init Endpoints */
+				if (vcf[idx] != EP_NOP
+				    && vcf[idx] != EP_NUL) {
+					switch (attr) {
+						case USB_ENDPOINT_XFER_INT:
+							context->
+							    fifos[cidx].
+							    pipe =
+							    usb_rcvintpipe
+							    (dev,
+							     ep->desc.
+							     bEndpointAddress);
+							context->
+							    fifos[cidx].
+							    usb_transfer_mode
+							    = USB_INT;
+							packet_size =
+							    ep->desc.
+							    wMaxPacketSize;
+							break;
+						case USB_ENDPOINT_XFER_BULK:
+							if (ep_addr & 0x80)
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_rcvbulkpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							else
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_sndbulkpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							context->
+							    fifos[cidx].
+							    usb_transfer_mode
+							    = USB_BULK;
+							packet_size =
+							    ep->desc.
+							    wMaxPacketSize;
+							break;
+						case USB_ENDPOINT_XFER_ISOC:
+							if (ep_addr & 0x80)
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_rcvisocpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							else
+								context->
+								    fifos
+								    [cidx].
+								    pipe =
+								    usb_sndisocpipe
+								    (dev,
+								     ep->
+								     desc.
+								     bEndpointAddress);
+							context->
+							    fifos[cidx].
+							    usb_transfer_mode
+							    = USB_ISOC;
+							iso_packet_size =
+							    ep->desc.
+							    wMaxPacketSize;
+							break;
+						default:
+							context->
+							    fifos[cidx].
+							    pipe = 0;
+					}	/* switch attribute */
+
+					if (context->fifos[cidx].pipe) {
+						context->fifos[cidx].
+						    fifonum = cidx;
+						context->fifos[cidx].hfc =
+						    context;
+						context->fifos[cidx].
+						    usb_packet_maxlen =
+						    ep->desc.
+						    wMaxPacketSize;
+						context->fifos[cidx].
+						    intervall =
+						    ep->desc.bInterval;
+						context->fifos[cidx].
+						    skbuff = NULL;
+					}
+				}
+				ep++;
+			}
+			context->dev = dev;	/* save device */
+			context->if_used = ifnum;	/* save used interface */
+			context->alt_used = alt_used;	/* and alternate config */
+			context->ctrl_paksize = dev->descriptor.bMaxPacketSize0;	/* control size */
+			context->cfg_used = vcf[16];	/* store used config */
+			context->vend_idx = vend_idx;	/* store found vendor */
+			context->packet_size = packet_size;
+			context->iso_packet_size = iso_packet_size;
+
+			/* create the control pipes needed for register access */
+			context->ctrl_in_pipe =
+			    usb_rcvctrlpipe(context->dev, 0);
+			context->ctrl_out_pipe =
+			    usb_sndctrlpipe(context->dev, 0);
+			context->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+			printk(KERN_INFO
+			       "HFC-S USB: detected \"%s\"\n",
+			       vdata[vend_idx].vend_name);
+#ifdef CONFIG_HISAX_DEBUG
+			DBG(USB_DBG,
+			    "HFC-S USB: Endpoint-Config: %s (if=%d alt=%d)\n",
+			    conf_str[small_match], context->if_used,
+			    context->alt_used);
+			printk(KERN_INFO
+			       "HFC-S USB: E-channel (\"ECHO:\") logging ");
+			if (validconf[small_match][18])
+				printk(" possible\n");
+			else
+				printk("NOT possible\n");
+#endif
+			/* init the chip and register the driver */
+			if (usb_init(context)) {
+				if (context->ctrl_urb) {
+					usb_unlink_urb(context->ctrl_urb);
+					usb_free_urb(context->ctrl_urb);
+					context->ctrl_urb = NULL;
+				}
+				kfree(context);
+				return (-EIO);
+			}
+			usb_set_intfdata(intf, context);
+			return (0);
+		}
+	} else {
+		printk(KERN_INFO
+		       "HFC-S USB: no valid vendor found in USB descriptor\n");
+	}
+	return (-EIO);
+}
+
+/****************************************************/
+/* function called when an active device is removed */
+/****************************************************/
+static void
+hfc_usb_disconnect(struct usb_interface
+		   *intf)
+{
+	hfcusb_data *context = usb_get_intfdata(intf);
+	int i;
+	printk(KERN_INFO "HFC-S USB: device disconnect\n");
+	context->disc_flag = TRUE;
+	usb_set_intfdata(intf, NULL);
+	if (!context)
+		return;
+	if (timer_pending(&context->t3_timer))
+		del_timer(&context->t3_timer);
+	if (timer_pending(&context->t4_timer))
+		del_timer(&context->t4_timer);
+	if (timer_pending(&context->led_timer))
+		del_timer(&context->led_timer);
+	/* tell all fifos to terminate */
+	for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+		if (context->fifos[i].usb_transfer_mode == USB_ISOC) {
+			if (context->fifos[i].active > 0) {
+				stop_isoc_chain(&context->fifos[i]);
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(USB_DBG,
+				    "HFC-S USB: hfc_usb_disconnect: stopping ISOC chain Fifo no %i",
+				    i);
+#endif
+			}
+		} else {
+			if (context->fifos[i].active > 0) {
+				context->fifos[i].active = 0;
+#ifdef CONFIG_HISAX_DEBUG
+				DBG(USB_DBG,
+				    "HFC-S USB: hfc_usb_disconnect: unlinking URB for Fifo no %i",
+				    i);
+#endif
+			}
+			if (context->fifos[i].urb) {
+				usb_unlink_urb(context->fifos[i].urb);
+				usb_free_urb(context->fifos[i].urb);
+				context->fifos[i].urb = NULL;
+			}
+		}
+		context->fifos[i].active = 0;
+	}
+	/* wait for all URBS to terminate */
+	mdelay(10);
+	if (context->ctrl_urb) {
+		usb_unlink_urb(context->ctrl_urb);
+		usb_free_urb(context->ctrl_urb);
+		context->ctrl_urb = NULL;
+	}
+	hisax_unregister(&context->d_if);
+	kfree(context);		/* free our structure again */
+}				/* hfc_usb_disconnect */
+
+/************************************/
+/* our driver information structure */
+/************************************/
+static struct usb_driver hfc_drv = {
+	.owner = THIS_MODULE,.name =
+	    "hfc_usb",.id_table = hfc_usb_idtab,.probe =
+	    hfc_usb_probe,.disconnect = hfc_usb_disconnect,
+};
+static void __exit
+hfc_usb_exit(void)
+{
+#ifdef CONFIG_HISAX_DEBUG
+	DBG(USB_DBG, "HFC-S USB: calling \"hfc_usb_exit\" ...");
+#endif
+	usb_deregister(&hfc_drv);	/* release our driver */
+	printk(KERN_INFO "HFC-S USB: module removed\n");
+}
+
+static int __init
+hfc_usb_init(void)
+{
+#ifndef CONFIG_HISAX_DEBUG
+	unsigned int debug = -1;
+#endif
+	char revstr[30], datestr[30], dummy[30];
+	sscanf(hfcusb_revision,
+	       "%s %s $ %s %s %s $ ", dummy, revstr,
+	       dummy, datestr, dummy);
+	printk(KERN_INFO
+	       "HFC-S USB: driver module revision %s date %s loaded, (debug=%i)\n",
+	       revstr, datestr, debug);
+	if (usb_register(&hfc_drv)) {
+		printk(KERN_INFO
+		       "HFC-S USB: Unable to register HFC-S USB module at usb stack\n");
+		return (-1);	/* unable to register */
+	}
+	return (0);
+}
+
+module_init(hfc_usb_init);
+module_exit(hfc_usb_exit);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, hfc_usb_idtab);
diff --git a/drivers/isdn/hisax/hfc_usb.h b/drivers/isdn/hisax/hfc_usb.h
new file mode 100644
index 0000000..b171600
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_usb.h
@@ -0,0 +1,228 @@
+/*
+* hfc_usb.h
+*
+* $Id: hfc_usb.h,v 4.1 2005/01/26 17:25:53 martinb1 Exp $
+*/
+
+#ifndef __HFC_USB_H__
+#define __HFC_USB_H__
+
+#define DRIVER_AUTHOR   "Peter Sprenger (sprenger@moving-byters.de)"
+#define DRIVER_DESC     "HFC-S USB based HiSAX ISDN driver"
+
+#define VERBOSE_USB_DEBUG
+
+#define TRUE  1
+#define FALSE 0
+
+
+/***********/
+/* defines */
+/***********/
+#define HFC_CTRL_TIMEOUT 20	/* 5ms timeout writing/reading regs */
+#define HFC_TIMER_T3 8000	/* timeout for l1 activation timer */
+#define HFC_TIMER_T4 500	/* time for state change interval */
+
+#define HFCUSB_L1_STATECHANGE 0	/* L1 state changed */
+#define HFCUSB_L1_DRX 1		/* D-frame received */
+#define HFCUSB_L1_ERX 2		/* E-frame received */
+#define HFCUSB_L1_DTX 4		/* D-frames completed */
+
+#define MAX_BCH_SIZE 2048	/* allowed B-channel packet size */
+
+#define HFCUSB_RX_THRESHOLD 64	/* threshold for fifo report bit rx */
+#define HFCUSB_TX_THRESHOLD 64	/* threshold for fifo report bit tx */
+
+#define HFCUSB_CHIP_ID		0x16	/* Chip ID register index */
+#define HFCUSB_CIRM		0x00	/* cirm register index */
+#define HFCUSB_USB_SIZE		0x07	/* int length register */
+#define HFCUSB_USB_SIZE_I	0x06	/* iso length register */
+#define HFCUSB_F_CROSS		0x0b	/* bit order register */
+#define HFCUSB_CLKDEL		0x37	/* bit delay register */
+#define HFCUSB_CON_HDLC		0xfa	/* channel connect register */
+#define HFCUSB_HDLC_PAR		0xfb
+#define HFCUSB_SCTRL		0x31	/* S-bus control register (tx) */
+#define HFCUSB_SCTRL_E		0x32	/* same for E and special funcs */
+#define HFCUSB_SCTRL_R		0x33	/* S-bus control register (rx) */
+#define HFCUSB_F_THRES		0x0c	/* threshold register */
+#define HFCUSB_FIFO		0x0f	/* fifo select register */
+#define HFCUSB_F_USAGE		0x1a	/* fifo usage register */
+#define HFCUSB_MST_MODE0	0x14
+#define HFCUSB_MST_MODE1	0x15
+#define HFCUSB_P_DATA		0x1f
+#define HFCUSB_INC_RES_F	0x0e
+#define HFCUSB_STATES		0x30
+
+#define HFCUSB_CHIPID		0x40	/* ID value of HFC-S USB */
+
+/******************/
+/* fifo registers */
+/******************/
+#define HFCUSB_NUM_FIFOS	8	/* maximum number of fifos */
+#define HFCUSB_B1_TX		0	/* index for B1 transmit bulk/int */
+#define HFCUSB_B1_RX		1	/* index for B1 receive bulk/int */
+#define HFCUSB_B2_TX		2
+#define HFCUSB_B2_RX		3
+#define HFCUSB_D_TX		4
+#define HFCUSB_D_RX		5
+#define HFCUSB_PCM_TX		6
+#define HFCUSB_PCM_RX		7
+
+/*
+* used to switch snd_transfer_mode for different TA modes e.g. the Billion USB TA just
+* supports ISO out, while the Cologne Chip EVAL TA just supports BULK out
+*/
+#define USB_INT		0
+#define USB_BULK	1
+#define USB_ISOC	2
+
+#define ISOC_PACKETS_D	8
+#define ISOC_PACKETS_B	8
+#define ISO_BUFFER_SIZE	128
+
+// ISO send definitions
+#define SINK_MAX	68
+#define SINK_MIN	48
+#define SINK_DMIN	12
+#define SINK_DMAX	18
+#define BITLINE_INF	(-64*8)
+
+
+/**********/
+/* macros */
+/**********/
+#define write_usb(a,b,c)usb_control_msg((a)->dev,(a)->ctrl_out_pipe,0,0x40,(c),(b),0,0,HFC_CTRL_TIMEOUT)
+#define read_usb(a,b,c) usb_control_msg((a)->dev,(a)->ctrl_in_pipe,1,0xC0,0,(b),(c),1,HFC_CTRL_TIMEOUT)
+
+
+/*******************/
+/* Debugging Flags */
+/*******************/
+#define USB_DBG   1
+#define ISDN_DBG  2
+
+
+/* *********************/
+/* USB related defines */
+/***********************/
+#define HFC_CTRL_BUFSIZE 32
+
+
+
+/*************************************************/
+/* entry and size of output/input control buffer */
+/*************************************************/
+typedef struct {
+	__u8 hfc_reg;		/* register number */
+	__u8 reg_val;		/* value to be written (or read) */
+	int action;		/* data for action handler */
+} ctrl_buft;
+
+
+/********************/
+/* URB error codes: */
+/********************/
+/* Used to represent a list of values and their respective symbolic names */
+struct hfcusb_symbolic_list {
+	const int num;
+	const char *name;
+};
+
+static struct hfcusb_symbolic_list urb_errlist[] = {
+	{-ENOMEM, "No memory for allocation of internal structures"},
+	{-ENOSPC, "The host controller's bandwidth is already consumed"},
+	{-ENOENT, "URB was canceled by unlink_urb"},
+	{-EXDEV, "ISO transfer only partially completed"},
+	{-EAGAIN, "Too match scheduled for the future"},
+	{-ENXIO, "URB already queued"},
+	{-EFBIG, "Too much ISO frames requested"},
+	{-ENOSR, "Buffer error (overrun)"},
+	{-EPIPE, "Specified endpoint is stalled (device not responding)"},
+	{-EOVERFLOW, "Babble (bad cable?)"},
+	{-EPROTO, "Bit-stuff error (bad cable?)"},
+	{-EILSEQ, "CRC/Timeout"},
+	{-ETIMEDOUT, "NAK (device does not respond)"},
+	{-ESHUTDOWN, "Device unplugged"},
+	{-1, NULL}
+};
+
+
+/*****************************************************/
+/* device dependant information to support different */
+/* ISDN Ta's using the HFC-S USB chip                */
+/*****************************************************/
+
+/* USB descriptor need to contain one of the following EndPoint combination: */
+#define CNF_4INT3ISO	1	// 4 INT IN, 3 ISO OUT
+#define CNF_3INT3ISO	2	// 3 INT IN, 3 ISO OUT
+#define CNF_4ISO3ISO	3	// 4 ISO IN, 3 ISO OUT
+#define CNF_3ISO3ISO	4	// 3 ISO IN, 3 ISO OUT
+
+#define EP_NUL 1		// Endpoint at this position not allowed
+#define EP_NOP 2		// all type of endpoints allowed at this position
+#define EP_ISO 3		// Isochron endpoint mandatory at this position
+#define EP_BLK 4		// Bulk endpoint mandatory at this position
+#define EP_INT 5		// Interrupt endpoint mandatory at this position
+
+/* this array represents all endpoints possible in the HCF-USB the last
+* 3 entries are the configuration number, the minimum interval for
+* Interrupt endpoints & boolean if E-channel logging possible
+*/
+int validconf[][19] = {
+	// INT in, ISO out config
+	{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT,
+	 EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+	 CNF_4INT3ISO, 2, 1},
+	{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL,
+	 EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+	 CNF_3INT3ISO, 2, 0},
+	// ISO in, ISO out config
+	{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+	 EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO,
+	 CNF_4ISO3ISO, 2, 1},
+	{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+	 EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL,
+	 CNF_3ISO3ISO, 2, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}	// EOL element
+};
+
+// string description of chosen config
+char *conf_str[] = {
+	"4 Interrupt IN + 3 Isochron OUT",
+	"3 Interrupt IN + 3 Isochron OUT",
+	"4 Isochron IN + 3 Isochron OUT",
+	"3 Isochron IN + 3 Isochron OUT"
+};
+
+
+typedef struct {
+	int vendor;		// vendor id
+	int prod_id;		// product id
+	char *vend_name;	// vendor string
+	__u8 led_scheme;	// led display scheme
+	signed short led_bits[8];	// array of 8 possible LED bitmask settings
+} vendor_data;
+
+#define LED_OFF      0		// no LED support
+#define LED_SCHEME1  1		// LED standard scheme
+#define LED_SCHEME2  2		// not used yet...
+
+#define LED_POWER_ON	1
+#define LED_POWER_OFF	2
+#define LED_S0_ON	3
+#define LED_S0_OFF	4
+#define LED_B1_ON	5
+#define LED_B1_OFF	6
+#define LED_B1_DATA	7
+#define LED_B2_ON	8
+#define LED_B2_OFF	9
+#define LED_B2_DATA	10
+
+#define LED_NORMAL   0		// LEDs are normal
+#define LED_INVERTED 1		// LEDs are inverted
+
+/* time in ms to perform a Flashing LED when B-Channel has traffic */
+#define LED_TIME      250
+
+
+#endif				// __HFC_USB_H__
diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c
new file mode 100644
index 0000000..6fc55fe
--- /dev/null
+++ b/drivers/isdn/hisax/hfcscard.c
@@ -0,0 +1,266 @@
+/* $Id: hfcscard.c,v 1.10.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for hfcs based cards (Teles3c, ACER P10)
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+static const char *hfcs_revision = "$Revision: 1.10.2.4 $";
+
+static irqreturn_t
+hfcs_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, stat;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) & 
+		(stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) {
+		val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFCS: stat(%02x) s1(%02x)", stat, val);
+		hfc2bds0_interrupt(cs, val);
+	} else {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "HFCS: irq_no_irq stat(%02x)", stat);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+hfcs_Timer(struct IsdnCardState *cs)
+{
+	cs->hw.hfcD.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*	WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80);
+	add_timer(&cs->hw.hfcD.timer);
+*/
+}
+
+void
+release_io_hfcs(struct IsdnCardState *cs)
+{
+	release2bds0(cs);
+	del_timer(&cs->hw.hfcD.timer);
+	if (cs->hw.hfcD.addr)
+		release_region(cs->hw.hfcD.addr, 2);
+}
+
+static void
+reset_hfcs(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "HFCS: resetting card\n");
+	cs->hw.hfcD.cirm = HFCD_RESET;
+	if (cs->typ == ISDN_CTYPE_TELES3C)
+		cs->hw.hfcD.cirm |= HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset On */
+	mdelay(10);
+	cs->hw.hfcD.cirm = 0;
+	if (cs->typ == ISDN_CTYPE_TELES3C)
+		cs->hw.hfcD.cirm |= HFCD_MEM8K;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);	/* Reset Off */
+	mdelay(10);
+	if (cs->typ == ISDN_CTYPE_TELES3C)
+		cs->hw.hfcD.cirm |= HFCD_INTB;
+	else if (cs->typ == ISDN_CTYPE_ACERP10)
+		cs->hw.hfcD.cirm |= HFCD_INTA;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */
+	cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+	cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE;
+	cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS |
+		HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC |
+		HFCD_INTS_DREC | HFCD_INTS_L1STATE;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */
+	udelay(10);
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */
+	cs->hw.hfcD.mst_m = HFCD_MASTER;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); /* HFC Master */
+	cs->hw.hfcD.sctrl = 0;
+	cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+}
+
+static int
+hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+	int delay;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "HFCS: card_msg %x", mt);
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcs(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_hfcs(cs);
+			return(0);
+		case CARD_INIT:
+			delay = (75*HZ)/100 +1;
+			cs->hw.hfcD.timer.expires = jiffies + delay;
+			add_timer(&cs->hw.hfcD.timer);
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_hfcs(cs);
+			init2bds0(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			delay = (80*HZ)/1000 +1;
+			msleep(80);
+			spin_lock_irqsave(&cs->lock, flags);
+			cs->hw.hfcD.ctmt |= HFCD_TIM800;
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt); 
+			cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id hfc_ids[] __initdata = {
+	{ ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114),
+	  ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114), 
+	  (unsigned long) "Acer P10" },
+	{ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002),
+	  ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002), 
+	  (unsigned long) "Billion 2" },
+	{ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001),
+	  ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001), 
+	  (unsigned long) "Billion 1" },
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410), 
+	  (unsigned long) "IStar PnP" },
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610), 
+	  (unsigned long) "Teles 16.3c" },
+	{ ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001),
+	  ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001), 
+	  (unsigned long) "Tornado Tipa C" },
+	{ ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001),
+	  ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001), 
+	  (unsigned long) "Genius Speed Surfer" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &hfc_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __init
+setup_hfcs(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, hfcs_revision);
+	printk(KERN_INFO "HiSax: HFC-S driver Rev. %s\n", HiSax_getrev(tmp));
+
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "HFC PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "HFC PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	cs->hw.hfcD.addr = card->para[1] & 0xfffe;
+	cs->irq = card->para[0];
+	cs->hw.hfcD.cip = 0;
+	cs->hw.hfcD.int_s1 = 0;
+	cs->hw.hfcD.send = NULL;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfcD.dfifosize = 512;
+	cs->dc.hfcd.ph_state = 0;
+	cs->hw.hfcD.fifo = 255;
+	if (cs->typ == ISDN_CTYPE_TELES3C) {
+		cs->hw.hfcD.bfifosize = 1024 + 512;
+	} else if (cs->typ == ISDN_CTYPE_ACERP10) {
+		cs->hw.hfcD.bfifosize = 7*1024 + 512;
+	} else
+		return (0);
+	if (!request_region(cs->hw.hfcD.addr, 2, "HFCS isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfcD.addr,
+		       cs->hw.hfcD.addr + 2);
+		return (0);
+	}
+	printk(KERN_INFO
+	       "HFCS: defined at 0x%x IRQ %d HZ %d\n",
+	       cs->hw.hfcD.addr,
+	       cs->irq, HZ);
+	if (cs->typ == ISDN_CTYPE_TELES3C) {
+		/* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */
+		outb(0x00, cs->hw.hfcD.addr);
+		outb(0x56, cs->hw.hfcD.addr | 1);
+	} else if (cs->typ == ISDN_CTYPE_ACERP10) {
+		/* Acer P10 IO ADR is 0x300 */
+		outb(0x00, cs->hw.hfcD.addr);
+		outb(0x57, cs->hw.hfcD.addr | 1);
+	}
+	set_cs_func(cs);
+	cs->hw.hfcD.timer.function = (void *) hfcs_Timer;
+	cs->hw.hfcD.timer.data = (long) cs;
+	init_timer(&cs->hw.hfcD.timer);
+	cs->cardmsg = &hfcs_card_msg;
+	cs->irq_func = &hfcs_interrupt;
+	return (1);
+}
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
new file mode 100644
index 0000000..dc579172
--- /dev/null
+++ b/drivers/isdn/hisax/hisax.h
@@ -0,0 +1,1341 @@
+/* $Id: hisax.h,v 2.64.2.4 2004/02/11 13:21:33 keil Exp $
+ *
+ * Basic declarations, defines and prototypes
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+#include <linux/serial_reg.h>
+#include <linux/netdevice.h>
+
+#define ERROR_STATISTIC
+
+#define REQUEST		0
+#define CONFIRM		1
+#define INDICATION	2
+#define RESPONSE	3
+
+#define HW_ENABLE	0x0000
+#define HW_RESET	0x0004
+#define HW_POWERUP	0x0008
+#define HW_ACTIVATE	0x0010
+#define HW_DEACTIVATE	0x0018
+
+#define HW_INFO1	0x0010
+#define HW_INFO2	0x0020
+#define HW_INFO3	0x0030
+#define HW_INFO4	0x0040
+#define HW_INFO4_P8	0x0040
+#define HW_INFO4_P10	0x0048
+#define HW_RSYNC	0x0060
+#define HW_TESTLOOP	0x0070
+#define CARD_RESET	0x00F0
+#define CARD_INIT	0x00F2
+#define CARD_RELEASE	0x00F3
+#define CARD_TEST	0x00F4
+#define CARD_AUX_IND	0x00F5
+
+#define PH_ACTIVATE	0x0100
+#define PH_DEACTIVATE	0x0110
+#define PH_DATA		0x0120
+#define PH_PULL		0x0130
+#define PH_TESTLOOP	0x0140
+#define PH_PAUSE	0x0150
+#define MPH_ACTIVATE	0x0180
+#define MPH_DEACTIVATE	0x0190
+#define MPH_INFORMATION	0x01A0
+
+#define DL_ESTABLISH	0x0200
+#define DL_RELEASE	0x0210
+#define DL_DATA		0x0220
+#define DL_FLUSH	0x0224
+#define DL_UNIT_DATA	0x0230
+
+#define MDL_BC_RELEASE  0x0278  // Formula-n enter:now
+#define MDL_BC_ASSIGN   0x027C  // Formula-n enter:now
+#define MDL_ASSIGN	0x0280
+#define MDL_REMOVE	0x0284
+#define MDL_ERROR	0x0288
+#define MDL_INFO_SETUP	0x02E0
+#define MDL_INFO_CONN	0x02E4
+#define MDL_INFO_REL	0x02E8
+
+#define CC_SETUP	0x0300
+#define CC_RESUME	0x0304
+#define CC_MORE_INFO	0x0310
+#define CC_IGNORE	0x0320
+#define CC_REJECT	0x0324
+#define CC_SETUP_COMPL	0x0330
+#define CC_PROCEEDING	0x0340
+#define CC_ALERTING	0x0344
+#define CC_PROGRESS	0x0348
+#define CC_CONNECT	0x0350
+#define CC_CHARGE	0x0354
+#define CC_NOTIFY	0x0358
+#define CC_DISCONNECT	0x0360
+#define CC_RELEASE	0x0368
+#define CC_SUSPEND	0x0370
+#define CC_PROCEED_SEND 0x0374
+#define CC_REDIR        0x0378
+#define CC_T302		0x0382
+#define CC_T303		0x0383
+#define CC_T304		0x0384
+#define CC_T305		0x0385
+#define CC_T308_1	0x0388
+#define CC_T308_2	0x038A
+#define CC_T309         0x0309
+#define CC_T310		0x0390
+#define CC_T313		0x0393
+#define CC_T318		0x0398
+#define CC_T319		0x0399
+#define CC_TSPID	0x03A0
+#define CC_NOSETUP_RSP	0x03E0
+#define CC_SETUP_ERR	0x03E1
+#define CC_SUSPEND_ERR	0x03E2
+#define CC_RESUME_ERR	0x03E3
+#define CC_CONNECT_ERR	0x03E4
+#define CC_RELEASE_ERR	0x03E5
+#define CC_RESTART	0x03F4
+#define CC_TDSS1_IO     0x13F4    /* DSS1 IO user timer */
+#define CC_TNI1_IO      0x13F5    /* NI1 IO user timer */
+
+/* define maximum number of possible waiting incoming calls */
+#define MAX_WAITING_CALLS 2
+
+
+#ifdef __KERNEL__
+
+/* include l3dss1 & ni1 specific process structures, but no other defines */
+#ifdef CONFIG_HISAX_EURO
+  #define l3dss1_process
+  #include "l3dss1.h" 
+  #undef  l3dss1_process
+#endif /* CONFIG_HISAX_EURO */
+
+#ifdef CONFIG_HISAX_NI1
+  #define l3ni1_process
+  #include "l3ni1.h" 
+  #undef  l3ni1_process
+#endif /* CONFIG_HISAX_NI1 */
+
+#define MAX_DFRAME_LEN	260
+#define MAX_DFRAME_LEN_L1	300
+#define HSCX_BUFMAX	4096
+#define MAX_DATA_SIZE	(HSCX_BUFMAX - 4)
+#define MAX_DATA_MEM	(HSCX_BUFMAX + 64)
+#define RAW_BUFMAX	(((HSCX_BUFMAX*6)/5) + 5)
+#define MAX_HEADER_LEN	4
+#define MAX_WINDOW	8
+#define MAX_MON_FRAME	32
+#define MAX_DLOG_SPACE	2048
+#define MAX_BLOG_SPACE	256
+
+/* #define I4L_IRQ_FLAG SA_INTERRUPT */
+#define I4L_IRQ_FLAG    0
+
+/*
+ * Statemachine
+ */
+
+struct FsmInst;
+
+typedef void (* FSMFNPTR)(struct FsmInst *, int, void *);
+
+struct Fsm {
+	FSMFNPTR *jumpmatrix;
+	int state_count, event_count;
+	char **strEvent, **strState;
+};
+
+struct FsmInst {
+	struct Fsm *fsm;
+	int state;
+	int debug;
+	void *userdata;
+	int userint;
+	void (*printdebug) (struct FsmInst *, char *, ...);
+};
+
+struct FsmNode {
+	int state, event;
+	void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+	struct FsmInst *fi;
+	struct timer_list tl;
+	int event;
+	void *arg;
+};
+
+struct L3Timer {
+	struct l3_process *pc;
+	struct timer_list tl;
+	int event;
+};
+
+#define FLG_L1_ACTIVATING	1
+#define FLG_L1_ACTIVATED	2
+#define FLG_L1_DEACTTIMER	3
+#define FLG_L1_ACTTIMER		4
+#define FLG_L1_T3RUN		5
+#define FLG_L1_PULL_REQ		6
+#define FLG_L1_UINT		7
+
+struct Layer1 {
+	void *hardware;
+	struct BCState *bcs;
+	struct PStack **stlistp;
+	long Flags;
+	struct FsmInst l1m;
+	struct FsmTimer	timer;
+	void (*l1l2) (struct PStack *, int, void *);
+	void (*l1hw) (struct PStack *, int, void *);
+	void (*l1tei) (struct PStack *, int, void *);
+	int mode, bc;
+	int delay;
+};
+
+#define GROUP_TEI	127
+#define TEI_SAPI	63
+#define CTRL_SAPI	0
+#define PACKET_NOACK	250
+
+/* Layer2 Flags */
+
+#define FLG_LAPB	0
+#define FLG_LAPD	1
+#define FLG_ORIG	2
+#define FLG_MOD128	3
+#define FLG_PEND_REL	4
+#define FLG_L3_INIT	5
+#define FLG_T200_RUN	6
+#define FLG_ACK_PEND	7
+#define FLG_REJEXC	8
+#define FLG_OWN_BUSY	9
+#define FLG_PEER_BUSY	10
+#define FLG_DCHAN_BUSY	11
+#define FLG_L1_ACTIV	12
+#define FLG_ESTAB_PEND	13
+#define FLG_PTP		14
+#define FLG_FIXED_TEI	15
+#define FLG_L2BLOCK	16
+
+struct Layer2 {
+	int tei;
+	int sap;
+	int maxlen;
+	u_long flag;
+	spinlock_t lock;
+	u_int vs, va, vr;
+	int rc;
+	unsigned int window;
+	unsigned int sow;
+	struct sk_buff *windowar[MAX_WINDOW];
+	struct sk_buff_head i_queue;
+	struct sk_buff_head ui_queue;
+	void (*l2l1) (struct PStack *, int, void *);
+	void (*l2l3) (struct PStack *, int, void *);
+	void (*l2tei) (struct PStack *, int, void *);
+	struct FsmInst l2m;
+	struct FsmTimer t200, t203;
+	int T200, N200, T203;
+	int debug;
+	char debug_id[16];
+};
+
+struct Layer3 {
+	void (*l3l4) (struct PStack *, int, void *);
+        void (*l3ml3) (struct PStack *, int, void *);
+	void (*l3l2) (struct PStack *, int, void *);
+	struct FsmInst l3m;
+        struct FsmTimer l3m_timer;
+	struct sk_buff_head squeue;
+	struct l3_process *proc;
+	struct l3_process *global;
+	int N303;
+	int debug;
+	char debug_id[8];
+};
+
+struct LLInterface {
+	void (*l4l3) (struct PStack *, int, void *);
+        int  (*l4l3_proto) (struct PStack *, isdn_ctrl *);
+	void *userdata;
+	u_long flag;
+};
+
+#define	FLG_LLI_L1WAKEUP	1
+#define	FLG_LLI_L2WAKEUP	2
+
+struct Management {
+	int	ri;
+	struct FsmInst tei_m;
+	struct FsmTimer t202;
+	int T202, N202, debug;
+	void (*layer) (struct PStack *, int, void *);
+};
+
+#define NO_CAUSE 254
+
+struct Param {
+	u_char cause;
+	u_char loc;
+	u_char diag[6];
+	int bchannel;
+	int chargeinfo;
+	int spv;		/* SPV Flag */
+	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
+	u_char moderate;	/* transfer mode and rate (bearer octet 4) */
+};
+
+
+struct PStack {
+	struct PStack *next;
+	struct Layer1 l1;
+	struct Layer2 l2;
+	struct Layer3 l3;
+	struct LLInterface lli;
+	struct Management ma;
+	int protocol;		/* EDSS1, 1TR6 or NI1 */
+
+        /* protocol specific data fields */
+        union
+	 { u_char uuuu; /* only as dummy */
+#ifdef CONFIG_HISAX_EURO
+           dss1_stk_priv dss1; /* private dss1 data */
+#endif /* CONFIG_HISAX_EURO */              
+#ifdef CONFIG_HISAX_NI1
+           ni1_stk_priv ni1; /* private ni1 data */
+#endif /* CONFIG_HISAX_NI1 */             
+	 } prot;
+};
+
+struct l3_process {
+	int callref;
+	int state;
+	struct L3Timer timer;
+	int N303;
+	int debug;
+	struct Param para;
+	struct Channel *chan;
+	struct PStack *st;
+	struct l3_process *next;
+        ulong redir_result;
+
+        /* protocol specific data fields */
+        union 
+	 { u_char uuuu; /* only when euro not defined, avoiding empty union */
+#ifdef CONFIG_HISAX_EURO 
+           dss1_proc_priv dss1; /* private dss1 data */
+#endif /* CONFIG_HISAX_EURO */            
+#ifdef CONFIG_HISAX_NI1
+           ni1_proc_priv ni1; /* private ni1 data */
+#endif /* CONFIG_HISAX_NI1 */             
+	 } prot;
+};
+
+struct hscx_hw {
+	int hscx;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+	u_char tsaxr0;
+	u_char tsaxr1;
+};
+
+struct w6692B_hw {
+	int bchan;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+};
+
+struct isar_reg {
+	unsigned long Flags;
+	volatile u_char bstat;
+	volatile u_char iis;
+	volatile u_char cmsb;
+	volatile u_char clsb;
+	volatile u_char par[8];
+};
+
+struct isar_hw {
+	int dpath;
+	int rcvidx;
+	int txcnt;
+	int mml;
+	u_char state;
+	u_char cmd;
+	u_char mod;
+	u_char newcmd;
+	u_char newmod;
+	char try_mod;
+	struct timer_list ftimer;
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+	u_char conmsg[16];
+	struct isar_reg *reg;
+};
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+	u_char fill __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char cmd  __attribute__((packed));
+#else
+	u_char cmd  __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char fill __attribute__((packed));
+#endif
+};
+
+struct hdlc_hw {
+	union {
+		u_int ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u_int stat;
+	int rcvidx;
+	int count;              /* Current skb sent count */
+	u_char *rcvbuf;         /* B-Channel receive Buffer */
+};
+
+struct hfcB_hw {
+	unsigned int *send;
+	int f1;
+	int f2;
+};
+
+struct tiger_hw {
+	u_int *send;
+	u_int *s_irq;
+	u_int *s_end;
+	u_int *sendp;
+	u_int *rec;
+	int free;
+	u_char *rcvbuf;
+	u_char *sendbuf;
+	u_char *sp;
+	int sendcnt;
+	u_int s_tot;
+	u_int r_bitcnt;
+	u_int r_tot;
+	u_int r_err;
+	u_int r_fcs;
+	u_char r_state;
+	u_char r_one;
+	u_char r_val;
+	u_char s_state;
+};
+
+struct amd7930_hw {
+	u_char *tx_buff;
+	u_char *rv_buff;
+	int rv_buff_in;
+	int rv_buff_out;
+	struct sk_buff *rv_skb;
+	struct hdlc_state *hdlc_state;
+	struct work_struct tq_rcv;
+	struct work_struct tq_xmt;
+};
+
+#define BC_FLG_INIT	1
+#define BC_FLG_ACTIV	2
+#define BC_FLG_BUSY	3
+#define BC_FLG_NOFRAME	4
+#define BC_FLG_HALF	5
+#define BC_FLG_EMPTY	6
+#define BC_FLG_ORIG	7
+#define BC_FLG_DLEETX	8
+#define BC_FLG_LASTDLE	9
+#define BC_FLG_FIRST	10
+#define BC_FLG_LASTDATA	11
+#define BC_FLG_NMD_DATA	12
+#define BC_FLG_FTI_RUN	13
+#define BC_FLG_LL_OK	14
+#define BC_FLG_LL_CONN	15
+#define BC_FLG_FTI_FTS	16
+#define BC_FLG_FRH_WAIT	17
+
+#define L1_MODE_NULL	0
+#define L1_MODE_TRANS	1
+#define L1_MODE_HDLC	2
+#define L1_MODE_EXTRN	3
+#define L1_MODE_HDLC_56K 4
+#define L1_MODE_MODEM	7
+#define L1_MODE_V32	8
+#define L1_MODE_FAX	9
+
+struct BCState {
+	int channel;
+	int mode;
+	u_long Flag;
+	struct IsdnCardState *cs;
+	int tx_cnt;		/* B-Channel transmit counter */
+	struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+	struct sk_buff_head rqueue;	/* B-Channel receive Queue */
+	struct sk_buff_head squeue;	/* B-Channel send Queue */
+	int ackcnt;
+	spinlock_t aclock;
+	struct PStack *st;
+	u_char *blog;
+	u_char *conmsg;
+	struct timer_list transbusy;
+	struct work_struct tqueue;
+	u_long event;
+	int  (*BC_SetStack) (struct PStack *, struct BCState *);
+	void (*BC_Close) (struct BCState *);
+#ifdef ERROR_STATISTIC
+	int err_crc;
+	int err_tx;
+	int err_rdo;
+	int err_inv;
+#endif
+	union {
+		struct hscx_hw hscx;
+		struct hdlc_hw hdlc;
+		struct isar_hw isar;
+		struct hfcB_hw hfc;
+		struct tiger_hw tiger;
+		struct amd7930_hw  amd7930;
+		struct w6692B_hw w6692;
+		struct hisax_b_if *b_if;
+	} hw;
+};
+
+struct Channel {
+	struct PStack *b_st, *d_st;
+	struct IsdnCardState *cs;
+	struct BCState *bcs;
+	int chan;
+	int incoming;
+	struct FsmInst fi;
+	struct FsmTimer drel_timer, dial_timer;
+	int debug;
+	int l2_protocol, l2_active_protocol;
+	int l3_protocol;
+	int data_open;
+	struct l3_process *proc;
+	setup_parm setup;	/* from isdnif.h numbers and Serviceindicator */
+	u_long Flags;		/* for remembering action done in l4 */
+	int leased;
+};
+
+struct elsa_hw {
+	struct pci_dev *dev;
+	unsigned long base;
+	unsigned int cfg;
+	unsigned int ctrl;
+	unsigned int ale;
+	unsigned int isac;
+	unsigned int itac;
+	unsigned int hscx;
+	unsigned int trig;
+	unsigned int timer;
+	unsigned int counter;
+	unsigned int status;
+	struct timer_list tl;
+	unsigned int MFlag;
+	struct BCState *bcs;
+	u_char *transbuf;
+	u_char *rcvbuf;
+	unsigned int transp;
+	unsigned int rcvp;
+	unsigned int transcnt;
+	unsigned int rcvcnt;
+	u_char IER;
+	u_char FCR;
+	u_char LCR;
+	u_char MCR;
+	u_char ctrl_reg;
+};
+
+struct teles3_hw {
+	unsigned int cfg_reg;
+	signed   int isac;
+	signed   int hscx[2];
+	signed   int isacfifo;
+	signed   int hscxfifo[2];
+};
+
+struct teles0_hw {
+	unsigned int cfg_reg;
+	void __iomem *membase;
+	unsigned long phymem;
+};
+
+struct avm_hw {
+	unsigned int cfg_reg;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned int isacfifo;
+	unsigned int hscxfifo[2];
+	unsigned int counter;
+	struct pci_dev *dev;
+};
+
+struct ix1_hw {
+	unsigned int cfg_reg;
+	unsigned int isac_ale;
+	unsigned int isac;
+	unsigned int hscx_ale;
+	unsigned int hscx;
+};
+
+struct diva_hw {
+	unsigned long cfg_reg;
+	unsigned long pci_cfg;
+	unsigned int ctrl;
+	unsigned long isac_adr;
+	unsigned int isac;
+	unsigned long hscx_adr;
+	unsigned int hscx;
+	unsigned int status;
+	struct timer_list tl;
+	u_char ctrl_reg;
+	struct pci_dev *dev;
+};
+
+struct asus_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int u7;
+	unsigned int pots;
+};
+
+
+struct hfc_hw {
+	unsigned int addr;
+	unsigned int fifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	u_char isac_spcr;
+	struct timer_list timer;
+};	
+
+struct sedl_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+	unsigned int reset_on;
+	unsigned int reset_off;
+	struct isar_reg isar;
+	unsigned int chip;
+	unsigned int bus;
+	struct pci_dev *dev;
+};
+
+struct spt_hw {
+	unsigned int cfg_reg;
+	unsigned int isac;
+	unsigned int hscx[2];
+	unsigned char res_irq;
+};
+
+struct mic_hw {
+	unsigned int cfg_reg;
+	unsigned int adr;
+	unsigned int isac;
+	unsigned int hscx;
+};
+
+struct njet_hw {
+	unsigned long base;
+	unsigned int isac;
+	unsigned int auxa;
+	unsigned char auxd;
+	unsigned char dmactrl;
+	unsigned char ctrl_reg;
+	unsigned char irqmask0;
+	unsigned char irqstat0;
+	unsigned char last_is0;
+	struct pci_dev *dev;
+};
+
+struct hfcPCI_hw {
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+        unsigned char sctrl_r;
+        unsigned char sctrl_e;
+        unsigned char trm;
+	unsigned char stat;
+	unsigned char fifo;
+        unsigned char fifo_en;
+        unsigned char bswapped;
+        unsigned char nt_mode;
+        int nt_timer;
+        struct pci_dev *dev;
+        unsigned char *pci_io; /* start of PCI IO memory */
+        void *share_start; /* shared memory for Fifos start */
+        void *fifos; /* FIFO memory */ 
+        int last_bfifo_cnt[2]; /* marker saving last b-fifo frame count */
+	struct timer_list timer;
+};
+
+struct hfcSX_hw {
+        unsigned long base;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+        unsigned char sctrl_r;
+        unsigned char sctrl_e;
+        unsigned char trm;
+	unsigned char stat;
+	unsigned char fifo;
+        unsigned char bswapped;
+        unsigned char nt_mode;
+        unsigned char chip;
+        int b_fifo_size;
+        unsigned char last_fifo;
+        void *extra;
+        int nt_timer;
+	struct timer_list timer;
+};
+
+struct hfcD_hw {
+	unsigned int addr;
+	unsigned int bfifosize;
+	unsigned int dfifosize;
+	unsigned char cirm;
+	unsigned char ctmt;
+	unsigned char cip;
+	unsigned char conn;
+	unsigned char mst_m;
+	unsigned char int_m1;
+	unsigned char int_m2;
+	unsigned char int_s1;
+	unsigned char sctrl;
+	unsigned char stat;
+	unsigned char fifo;
+	unsigned char f1;
+	unsigned char f2;
+	unsigned int *send;
+	struct timer_list timer;
+};
+
+struct isurf_hw {
+	unsigned int reset;
+	unsigned long phymem;
+	void __iomem *isac;
+	void __iomem *isar;
+	struct isar_reg isar_r;
+};
+
+struct saphir_hw {
+	struct pci_dev *dev;
+	unsigned int cfg_reg;
+	unsigned int ale;
+	unsigned int isac;
+	unsigned int hscx;
+	struct timer_list timer;
+};
+
+struct bkm_hw {
+	struct pci_dev *dev;
+	unsigned long base;
+	/* A4T stuff */
+	unsigned long isac_adr;
+	unsigned int isac_ale;
+	unsigned long jade_adr;
+	unsigned int jade_ale;
+	/* Scitel Quadro stuff */
+	unsigned long plx_adr;
+	unsigned long data_adr;
+};	
+
+struct gazel_hw {
+	struct pci_dev *dev;
+	unsigned int cfg_reg;
+	unsigned int pciaddr[2];
+        signed   int ipac;
+	signed   int isac;
+	signed   int hscx[2];
+	signed   int isacfifo;
+	signed   int hscxfifo[2];
+	unsigned char timeslot;
+	unsigned char iom2;
+};
+
+struct w6692_hw {
+	struct pci_dev *dev;
+	unsigned int iobase;
+	struct timer_list timer;
+};
+
+#ifdef  CONFIG_HISAX_TESTEMU
+struct te_hw {
+	unsigned char *sfifo;
+	unsigned char *sfifo_w;
+	unsigned char *sfifo_r;
+	unsigned char *sfifo_e;
+	int sfifo_cnt;
+	unsigned int stat;
+	wait_queue_head_t rwaitq;
+	wait_queue_head_t swaitq;
+};
+#endif
+
+struct arcofi_msg {
+	struct arcofi_msg *next;
+	u_char receive;
+	u_char len;
+	u_char msg[10];
+};
+
+struct isac_chip {
+	int ph_state;
+	u_char *mon_tx;
+	u_char *mon_rx;
+	int mon_txp;
+	int mon_txc;
+	int mon_rxp;
+	struct arcofi_msg *arcofi_list;
+	struct timer_list arcofitimer;
+	wait_queue_head_t arcofi_wait;
+	u_char arcofi_bc;
+	u_char arcofi_state;
+	u_char mocr;
+	u_char adf2;
+};
+
+struct hfcd_chip {
+	int ph_state;
+};
+
+struct hfcpci_chip {
+	int ph_state;
+};
+
+struct hfcsx_chip {
+	int ph_state;
+};
+
+struct w6692_chip {
+	int ph_state;
+};
+
+struct amd7930_chip {
+	u_char lmr1;
+	u_char ph_state;
+	u_char old_state;
+	u_char flg_t3;
+	unsigned int tx_xmtlen;
+	struct timer_list timer3;
+	void (*ph_command) (struct IsdnCardState *, u_char, char *);
+	void (*setIrqMask) (struct IsdnCardState *, u_char);
+};
+
+struct icc_chip {
+	int ph_state;
+	u_char *mon_tx;
+	u_char *mon_rx;
+	int mon_txp;
+	int mon_txc;
+	int mon_rxp;
+	struct arcofi_msg *arcofi_list;
+	struct timer_list arcofitimer;
+	wait_queue_head_t arcofi_wait;
+	u_char arcofi_bc;
+	u_char arcofi_state;
+	u_char mocr;
+	u_char adf2;
+};
+
+#define HW_IOM1			0
+#define HW_IPAC			1
+#define HW_ISAR			2
+#define HW_ARCOFI		3
+#define FLG_TWO_DCHAN		4
+#define FLG_L1_DBUSY		5
+#define FLG_DBUSY_TIMER 	6
+#define FLG_LOCK_ATOMIC 	7
+#define FLG_ARCOFI_TIMER	8
+#define FLG_ARCOFI_ERROR	9
+#define FLG_HW_L1_UINT		10
+
+struct IsdnCardState {
+	spinlock_t	lock;
+	u_char		typ;
+	u_char		subtyp;
+	int		protocol;
+	u_int		irq;
+	u_long		irq_flags;
+	u_long		HW_Flags;
+	int		*busy_flag;
+        int		chanlimit; /* limited number of B-chans to use */
+        int		logecho; /* log echo if supported by card */
+	union {
+		struct elsa_hw elsa;
+		struct teles0_hw teles0;
+		struct teles3_hw teles3;
+		struct avm_hw avm;
+		struct ix1_hw ix1;
+		struct diva_hw diva;
+		struct asus_hw asus;
+		struct hfc_hw hfc;
+		struct sedl_hw sedl;
+		struct spt_hw spt;
+		struct mic_hw mic;
+		struct njet_hw njet;
+		struct hfcD_hw hfcD;
+		struct hfcPCI_hw hfcpci;
+		struct hfcSX_hw hfcsx;
+		struct ix1_hw niccy;
+		struct isurf_hw isurf;
+		struct saphir_hw saphir;
+#ifdef CONFIG_HISAX_TESTEMU
+		struct te_hw te;
+#endif
+		struct bkm_hw ax;
+		struct gazel_hw gazel;
+		struct w6692_hw w6692;
+		struct hisax_d_if *hisax_d_if;
+	} hw;
+	int		myid;
+	isdn_if		iif;
+	spinlock_t	statlock;
+	u_char		*status_buf;
+	u_char		*status_read;
+	u_char		*status_write;
+	u_char		*status_end;
+	u_char		(*readisac) (struct IsdnCardState *, u_char);
+	void		(*writeisac) (struct IsdnCardState *, u_char, u_char);
+	void		(*readisacfifo) (struct IsdnCardState *, u_char *, int);
+	void		(*writeisacfifo) (struct IsdnCardState *, u_char *, int);
+	u_char		(*BC_Read_Reg) (struct IsdnCardState *, int, u_char);
+	void		(*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char);
+	void		(*BC_Send_Data) (struct BCState *);
+	int		(*cardmsg) (struct IsdnCardState *, int, void *);
+	void		(*setstack_d) (struct PStack *, struct IsdnCardState *);
+	void		(*DC_Close) (struct IsdnCardState *);
+	int		(*irq_func) (int, void *, struct pt_regs *);
+	int		(*auxcmd) (struct IsdnCardState *, isdn_ctrl *);
+	struct Channel	channel[2+MAX_WAITING_CALLS];
+	struct BCState	bcs[2+MAX_WAITING_CALLS];
+	struct PStack	*stlist;
+	struct sk_buff_head rq, sq; /* D-channel queues */
+	int		cardnr;
+	char		*dlog;
+	int		debug;
+	union {
+		struct isac_chip isac;
+		struct hfcd_chip hfcd;
+		struct hfcpci_chip hfcpci;
+		struct hfcsx_chip hfcsx;
+		struct w6692_chip w6692;
+		struct amd7930_chip amd7930;
+		struct icc_chip icc;
+	} dc;
+	u_char		*rcvbuf;
+	int		rcvidx;
+	struct sk_buff	*tx_skb;
+	int		tx_cnt;
+	u_long		event;
+	struct work_struct tqueue;
+	struct timer_list dbusytimer;
+#ifdef ERROR_STATISTIC
+	int		err_crc;
+	int		err_tx;
+	int		err_rx;
+#endif
+};
+
+
+#define  schedule_event(s, ev)	do {test_and_set_bit(ev, &s->event);schedule_work(&s->tqueue); } while(0)
+
+#define  MON0_RX	1
+#define  MON1_RX	2
+#define  MON0_TX	4
+#define  MON1_TX	8
+
+
+#ifdef ISDN_CHIP_ISAC
+#undef ISDN_CHIP_ISAC
+#endif
+
+#ifdef	CONFIG_HISAX_16_0
+#define  CARD_TELES0 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELES0  0
+#endif
+
+#ifdef	CONFIG_HISAX_16_3
+#define  CARD_TELES3 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELES3  0
+#endif
+
+#ifdef	CONFIG_HISAX_TELESPCI
+#define  CARD_TELESPCI 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_TELESPCI  0
+#endif
+
+#ifdef	CONFIG_HISAX_AVM_A1
+#define  CARD_AVM_A1 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_AVM_A1  0
+#endif
+
+#ifdef	CONFIG_HISAX_AVM_A1_PCMCIA
+#define  CARD_AVM_A1_PCMCIA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_AVM_A1_PCMCIA  0
+#endif
+
+#ifdef	CONFIG_HISAX_FRITZPCI
+#define  CARD_FRITZPCI 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_FRITZPCI  0
+#endif
+
+#ifdef	CONFIG_HISAX_ELSA
+#define  CARD_ELSA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_ELSA  0
+#endif
+
+#ifdef	CONFIG_HISAX_IX1MICROR2
+#define	CARD_IX1MICROR2 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_IX1MICROR2 0
+#endif
+
+#ifdef  CONFIG_HISAX_DIEHLDIVA
+#define CARD_DIEHLDIVA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_DIEHLDIVA 0
+#endif
+
+#ifdef  CONFIG_HISAX_ASUSCOM
+#define CARD_ASUSCOM 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ASUSCOM 0
+#endif
+
+#ifdef  CONFIG_HISAX_TELEINT
+#define CARD_TELEINT 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELEINT 0
+#endif
+
+#ifdef  CONFIG_HISAX_SEDLBAUER
+#define CARD_SEDLBAUER 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SEDLBAUER 0
+#endif
+
+#ifdef  CONFIG_HISAX_SPORTSTER
+#define CARD_SPORTSTER 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SPORTSTER 0
+#endif
+
+#ifdef  CONFIG_HISAX_MIC
+#define CARD_MIC 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_MIC 0
+#endif
+
+#ifdef  CONFIG_HISAX_NETJET
+#define CARD_NETJET_S 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NETJET_S 0
+#endif
+
+#ifdef	CONFIG_HISAX_HFCS
+#define  CARD_HFCS 1
+#else
+#define  CARD_HFCS 0
+#endif
+
+#ifdef	CONFIG_HISAX_HFC_PCI
+#define  CARD_HFC_PCI 1
+#else
+#define  CARD_HFC_PCI 0
+#endif
+
+#ifdef	CONFIG_HISAX_HFC_SX
+#define  CARD_HFC_SX 1
+#else
+#define  CARD_HFC_SX 0
+#endif
+
+#ifdef  CONFIG_HISAX_AMD7930
+#define CARD_AMD7930 1
+#else
+#define CARD_AMD7930 0
+#endif
+
+#ifdef	CONFIG_HISAX_NICCY
+#define	CARD_NICCY 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NICCY 0
+#endif
+
+#ifdef	CONFIG_HISAX_ISURF
+#define	CARD_ISURF 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ISURF 0
+#endif
+
+#ifdef	CONFIG_HISAX_S0BOX
+#define	CARD_S0BOX 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_S0BOX 0
+#endif
+
+#ifdef	CONFIG_HISAX_HSTSAPHIR
+#define	CARD_HSTSAPHIR 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_HSTSAPHIR 0
+#endif
+
+#ifdef	CONFIG_HISAX_TESTEMU
+#define	CARD_TESTEMU 1
+#define ISDN_CTYPE_TESTEMU 99
+#undef ISDN_CTYPE_COUNT
+#define  ISDN_CTYPE_COUNT ISDN_CTYPE_TESTEMU
+#else
+#define CARD_TESTEMU 0
+#endif
+
+#ifdef	CONFIG_HISAX_BKM_A4T
+#define	CARD_BKM_A4T 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_BKM_A4T 0
+#endif
+
+#ifdef	CONFIG_HISAX_SCT_QUADRO
+#define	CARD_SCT_QUADRO 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SCT_QUADRO 0
+#endif
+
+#ifdef	CONFIG_HISAX_GAZEL
+#define  CARD_GAZEL 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define  CARD_GAZEL  0
+#endif
+
+#ifdef	CONFIG_HISAX_W6692
+#define	CARD_W6692	1
+#ifndef	ISDN_CHIP_W6692
+#define	ISDN_CHIP_W6692	1
+#endif
+#else
+#define	CARD_W6692	0
+#endif
+
+#ifdef  CONFIG_HISAX_NETJET_U
+#define CARD_NETJET_U 1
+#ifndef ISDN_CHIP_ICC
+#define ISDN_CHIP_ICC 1
+#endif
+#ifndef HISAX_UINTERFACE
+#define HISAX_UINTERFACE 1
+#endif
+#else
+#define CARD_NETJET_U 0
+#endif
+
+#ifdef CONFIG_HISAX_ENTERNOW_PCI
+#define CARD_FN_ENTERNOW_PCI 1
+#endif
+
+#define TEI_PER_CARD 1
+
+/* L1 Debug */
+#define	L1_DEB_WARN		0x01
+#define	L1_DEB_INTSTAT		0x02
+#define	L1_DEB_ISAC		0x04
+#define	L1_DEB_ISAC_FIFO	0x08
+#define	L1_DEB_HSCX		0x10
+#define	L1_DEB_HSCX_FIFO	0x20
+#define	L1_DEB_LAPD	        0x40
+#define	L1_DEB_IPAC	        0x80
+#define	L1_DEB_RECEIVE_FRAME    0x100
+#define L1_DEB_MONITOR		0x200
+#define DEB_DLOG_HEX		0x400
+#define DEB_DLOG_VERBOSE	0x800
+
+#define L2FRAME_DEBUG
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
+#endif
+
+#include "hisax_cfg.h"
+
+void init_bcstate(struct IsdnCardState *cs, int bc);
+
+void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs);
+unsigned int random_ri(void);
+void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
+
+void setstack_l1_B(struct PStack *st);
+
+void setstack_tei(struct PStack *st);
+void setstack_manager(struct PStack *st);
+
+void setstack_isdnl2(struct PStack *st, char *debug_id);
+void releasestack_isdnl2(struct PStack *st);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+void lli_writewakeup(struct PStack *st, int len);
+
+void setstack_l3dc(struct PStack *st, struct Channel *chanp);
+void setstack_l3bc(struct PStack *st, struct Channel *chanp);
+void releasestack_isdnl3(struct PStack *st);
+
+u_char *findie(u_char * p, int size, u_char ie, int wanted_set);
+int getcallref(u_char * p);
+int newcallref(void);
+
+int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+	void *arg, int where);
+void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
+	void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+int jiftime(char *s, long mark);
+
+int HiSax_command(isdn_ctrl * ic);
+int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
+void HiSax_reportcard(int cardnr, int sel);
+int QuickHex(char *txt, u_char * p, int cnt);
+void LogFrame(struct IsdnCardState *cs, u_char * p, int size);
+void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
+void iecpy(u_char * dest, u_char * iestart, int ieoffset);
+#ifdef ISDN_CHIP_ISAC
+void setstack_isac(struct PStack *st, struct IsdnCardState *cs);
+#endif	/* ISDN_CHIP_ISAC */
+#endif	/* __KERNEL__ */
+
+#define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
+
+int ll_run(struct IsdnCardState *cs, int addfeatures);
+void ll_stop(struct IsdnCardState *cs);
+int CallcNew(void);
+void CallcFree(void);
+int CallcNewChan(struct IsdnCardState *cs);
+void CallcFreeChan(struct IsdnCardState *cs);
+int Isdnl1New(void);
+void Isdnl1Free(void);
+int Isdnl2New(void);
+void Isdnl2Free(void);
+int Isdnl3New(void);
+void Isdnl3Free(void);
+void init_tei(struct IsdnCardState *cs, int protocol);
+void release_tei(struct IsdnCardState *cs);
+char *HiSax_getrev(const char *revision);
+int TeiNew(void);
+void TeiFree(void);
diff --git a/drivers/isdn/hisax/hisax_cfg.h b/drivers/isdn/hisax/hisax_cfg.h
new file mode 100644
index 0000000..ca3fe62
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_cfg.h
@@ -0,0 +1,64 @@
+/* $Id: hisax_cfg.h,v 1.1.2.1 2004/01/24 20:47:23 keil Exp $
+ * define of the basic HiSax configuration structures
+ * and pcmcia interface
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define ISDN_CTYPE_16_0			1
+#define ISDN_CTYPE_8_0			2
+#define ISDN_CTYPE_16_3			3
+#define ISDN_CTYPE_PNP			4
+#define ISDN_CTYPE_A1			5
+#define ISDN_CTYPE_ELSA			6
+#define ISDN_CTYPE_ELSA_PNP		7
+#define ISDN_CTYPE_TELESPCMCIA		8
+#define ISDN_CTYPE_IX1MICROR2		9
+#define ISDN_CTYPE_ELSA_PCMCIA		10
+#define ISDN_CTYPE_DIEHLDIVA		11
+#define ISDN_CTYPE_ASUSCOM		12
+#define ISDN_CTYPE_TELEINT		13
+#define ISDN_CTYPE_TELES3C		14
+#define ISDN_CTYPE_SEDLBAUER		15
+#define ISDN_CTYPE_SPORTSTER		16
+#define ISDN_CTYPE_MIC			17
+#define ISDN_CTYPE_ELSA_PCI		18
+#define ISDN_CTYPE_COMPAQ_ISA		19
+#define ISDN_CTYPE_NETJET_S		20
+#define ISDN_CTYPE_TELESPCI		21
+#define ISDN_CTYPE_SEDLBAUER_PCMCIA	22
+#define ISDN_CTYPE_AMD7930		23
+#define ISDN_CTYPE_NICCY		24
+#define ISDN_CTYPE_S0BOX		25
+#define ISDN_CTYPE_A1_PCMCIA		26
+#define ISDN_CTYPE_FRITZPCI		27
+#define ISDN_CTYPE_SEDLBAUER_FAX	28
+#define ISDN_CTYPE_ISURF		29
+#define ISDN_CTYPE_ACERP10		30
+#define ISDN_CTYPE_HSTSAPHIR		31
+#define	ISDN_CTYPE_BKM_A4T		32
+#define	ISDN_CTYPE_SCT_QUADRO		33
+#define ISDN_CTYPE_GAZEL		34
+#define ISDN_CTYPE_HFC_PCI		35
+#define ISDN_CTYPE_W6692		36
+#define ISDN_CTYPE_HFC_SX		37
+#define ISDN_CTYPE_NETJET_U		38
+#define ISDN_CTYPE_HFC_SP_PCMCIA	39
+#define ISDN_CTYPE_DYNAMIC		40
+#define ISDN_CTYPE_ENTERNOW		41
+#define ISDN_CTYPE_COUNT		41
+
+typedef struct IsdnCardState	IsdnCardState_t;
+typedef struct IsdnCard		IsdnCard_t;
+
+struct IsdnCard {
+	int		typ;
+	int 		protocol;	/* EDSS1, 1TR6 or NI1 */
+	unsigned long	para[4];
+	IsdnCardState_t	*cs;
+};
+
+extern void	HiSax_closecard(int);
+extern int	hisax_init_pcmcia(void *, int *, IsdnCard_t *);
diff --git a/drivers/isdn/hisax/hisax_debug.h b/drivers/isdn/hisax/hisax_debug.h
new file mode 100644
index 0000000..ba518a7
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_debug.h
@@ -0,0 +1,81 @@
+/*
+ * Common debugging macros for use with the hisax driver
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * How to use:
+ * 
+ * Before including this file, you need to
+ *   #define __debug_variable my_debug
+ * where my_debug is a variable in your code which
+ * determines the debug bitmask.
+ *
+ * If CONFIG_HISAX_DEBUG is not set, all macros evaluate to nothing
+ *
+ */
+
+#ifndef __HISAX_DEBUG_H__
+#define __HISAX_DEBUG_H__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_HISAX_DEBUG
+
+#define DBG(level, format, arg...) do { \
+if (level & __debug_variable) \
+printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \
+} while (0)
+
+#define DBG_PACKET(level,data,count) \
+  if (level & __debug_variable) dump_packet(__FUNCTION__,data,count)
+
+#define DBG_SKB(level,skb) \
+  if ((level & __debug_variable) && skb) dump_packet(__FUNCTION__,skb->data,skb->len)
+
+
+static void __attribute__((unused))
+dump_packet(const char *name,const u_char *data,int pkt_len)
+{
+#define DUMP_HDR_SIZE 20
+#define DUMP_TLR_SIZE 8
+	if (pkt_len) {
+		int i,len1,len2;
+
+		printk(KERN_DEBUG "%s: length=%d,data=",name,pkt_len);
+
+		if (pkt_len >  DUMP_HDR_SIZE+ DUMP_TLR_SIZE) {
+			len1 = DUMP_HDR_SIZE;
+			len2 = DUMP_TLR_SIZE;
+		} else {
+			len1 = pkt_len > DUMP_HDR_SIZE ? DUMP_HDR_SIZE : pkt_len;
+			len2 = 0;			
+		}
+		for (i = 0; i < len1; ++i) {
+		 	printk ("%.2x", data[i]);
+		}
+		if (len2) {
+		 	printk ("..");
+			for (i = pkt_len-DUMP_TLR_SIZE; i < pkt_len; ++i) {
+				printk ("%.2x", data[i]);
+			}
+		}
+		printk ("\n");
+	}
+#undef DUMP_HDR_SIZE
+#undef DUMP_TLR_SIZE
+}
+
+#else
+
+#define DBG(level, format, arg...) do {} while (0)
+#define DBG_PACKET(level,data,count) do {} while (0)
+#define DBG_SKB(level,skb) do {} while (0)
+
+#endif
+
+#endif
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c
new file mode 100644
index 0000000..b4d795d
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.c
@@ -0,0 +1,1028 @@
+/*
+ * Driver for AVM Fritz!PCI, Fritz!PCI v2, Fritz!PnP ISDN cards
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original avm_pci.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+
+/* TODO:
+ *
+ * o POWER PC
+ * o clean up debugging
+ * o tx_skb at PH_DEACTIVATE time
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+
+#include "hisax_fcpcipnp.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+/* static int hdlcfifosize = 32; */
+module_param(debug, int, 0);
+/* module_param(hdlcfifosize, int, 0); */
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver");
+
+static struct pci_device_id fcpci_ids[] = {
+	{ .vendor      = PCI_VENDOR_ID_AVM,
+	  .device      = PCI_DEVICE_ID_AVM_A1,
+	  .subvendor   = PCI_ANY_ID,
+	  .subdevice   = PCI_ANY_ID,
+	  .driver_data = (unsigned long) "Fritz!Card PCI",
+	},
+	{ .vendor      = PCI_VENDOR_ID_AVM,
+	  .device      = PCI_DEVICE_ID_AVM_A1_V2,
+	  .subvendor   = PCI_ANY_ID,
+	  .subdevice   = PCI_ANY_ID,
+	  .driver_data = (unsigned long) "Fritz!Card PCI v2" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+#ifdef __ISAPNP__
+static struct pnp_device_id fcpnp_ids[] __devinitdata = {
+	{ 
+		.id		= "AVM0900",
+		.driver_data	= (unsigned long) "Fritz!Card PnP",
+	},
+};
+
+MODULE_DEVICE_TABLE(isapnp, fcpnp_ids);
+#endif
+
+static int protocol = 2;       /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+MODULE_LICENSE("GPL");
+
+// ----------------------------------------------------------------------
+
+#define  AVM_INDEX              0x04
+#define  AVM_DATA               0x10
+
+#define	 AVM_IDX_HDLC_1		0x00
+#define	 AVM_IDX_HDLC_2		0x01
+#define	 AVM_IDX_ISAC_FIFO	0x02
+#define	 AVM_IDX_ISAC_REG_LOW	0x04
+#define	 AVM_IDX_ISAC_REG_HIGH	0x06
+
+#define  AVM_STATUS0            0x02
+
+#define  AVM_STATUS0_IRQ_ISAC	0x01
+#define  AVM_STATUS0_IRQ_HDLC	0x02
+#define  AVM_STATUS0_IRQ_TIMER	0x04
+#define  AVM_STATUS0_IRQ_MASK	0x07
+
+#define  AVM_STATUS0_RESET	0x01
+#define  AVM_STATUS0_DIS_TIMER	0x02
+#define  AVM_STATUS0_RES_TIMER	0x04
+#define  AVM_STATUS0_ENA_IRQ	0x08
+#define  AVM_STATUS0_TESTBIT	0x10
+
+#define  AVM_STATUS1            0x03
+#define  AVM_STATUS1_ENA_IOM	0x80
+
+#define  HDLC_FIFO		0x0
+#define  HDLC_STATUS		0x4
+#define  HDLC_CTRL		0x4
+
+#define  HDLC_MODE_ITF_FLG	0x01
+#define  HDLC_MODE_TRANS	0x02
+#define  HDLC_MODE_CCR_7	0x04
+#define  HDLC_MODE_CCR_16	0x08
+#define  HDLC_MODE_TESTLOOP	0x80
+
+#define  HDLC_INT_XPR		0x80
+#define  HDLC_INT_XDU		0x40
+#define  HDLC_INT_RPR		0x20
+#define  HDLC_INT_MASK		0xE0
+
+#define  HDLC_STAT_RME		0x01
+#define  HDLC_STAT_RDO		0x10
+#define  HDLC_STAT_CRCVFRRAB	0x0E
+#define  HDLC_STAT_CRCVFR	0x06
+#define  HDLC_STAT_RML_MASK	0xff00
+
+#define  HDLC_CMD_XRS		0x80
+#define  HDLC_CMD_XME		0x01
+#define  HDLC_CMD_RRS		0x20
+#define  HDLC_CMD_XML_MASK	0xff00
+
+#define  AVM_HDLC_FIFO_1        0x10
+#define  AVM_HDLC_FIFO_2        0x18
+
+#define  AVM_HDLC_STATUS_1      0x14
+#define  AVM_HDLC_STATUS_2      0x1c
+
+#define  AVM_ISACSX_INDEX       0x04
+#define  AVM_ISACSX_DATA        0x08
+
+// ----------------------------------------------------------------------
+// Fritz!PCI
+
+static unsigned char fcpci_read_isac(struct isac *isac, unsigned char offset)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char idx = (offset > 0x2f) ? 
+		AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	val = inb(adapter->io + AVM_DATA + (offset & 0xf));
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, val);
+	return val;
+}
+
+static void fcpci_write_isac(struct isac *isac, unsigned char offset,
+			     unsigned char value)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char idx = (offset > 0x2f) ? 
+		AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+	unsigned long flags;
+
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, value);
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	outb(value, adapter->io + AVM_DATA + (offset & 0xf));
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_read_isac_fifo(struct isac *isac, unsigned char * data, 
+				 int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+	insb(adapter->io + AVM_DATA, data, size);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_write_isac_fifo(struct isac *isac, unsigned char * data, 
+				  int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+	outsb(adapter->io + AVM_DATA, data, size);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	u32 val;
+	int idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(idx, adapter->io + AVM_INDEX);
+	val = inl(adapter->io + AVM_DATA + HDLC_STATUS);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	return val;
+}
+
+static void __fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	int idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outl(idx, adapter->io + AVM_INDEX);
+	outl(bcs->ctrl.ctrl, adapter->io + AVM_DATA + HDLC_CTRL);
+}
+
+static void fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	__fcpci_write_ctrl(bcs, which);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PCI v2
+
+static unsigned char fcpci2_read_isac(struct isac *isac, unsigned char offset)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(offset, adapter->io + AVM_ISACSX_INDEX);
+	val = inl(adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, val);
+
+	return val;
+}
+
+static void fcpci2_write_isac(struct isac *isac, unsigned char offset, 
+			      unsigned char value)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	unsigned long flags;
+
+	DBG(0x1000, " port %#x, value %#x",
+	    offset, value);
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(offset, adapter->io + AVM_ISACSX_INDEX);
+	outl(value, adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_read_isac_fifo(struct isac *isac, unsigned char * data, 
+				  int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(0, adapter->io + AVM_ISACSX_INDEX);
+	for (i = 0; i < size; i++)
+		data[i] = inl(adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_write_isac_fifo(struct isac *isac, unsigned char * data, 
+				   int size)
+{
+	struct fritz_adapter *adapter = isac->priv;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outl(0, adapter->io + AVM_ISACSX_INDEX);
+	for (i = 0; i < size; i++)
+		outl(data[i], adapter->io + AVM_ISACSX_DATA);
+ 	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci2_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	int offset = nr ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+	return inl(adapter->io + offset);
+}
+
+static void fcpci2_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	int offset = bcs->channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outl(bcs->ctrl.ctrl, adapter->io + offset);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PnP (ISAC access as for Fritz!PCI)
+
+static u32 fcpnp_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+	unsigned char idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+	u32 val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	outb(idx, adapter->io + AVM_INDEX);
+	val = inb(adapter->io + AVM_DATA + HDLC_STATUS);
+	if (val & HDLC_INT_RPR)
+		val |= inb(adapter->io + AVM_DATA + HDLC_STATUS + 1) << 8;
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+	return val;
+}
+
+static void __fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x40, "hdlc %c wr%x ctrl %x",
+	    'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+	outb(idx, adapter->io + AVM_INDEX);
+	if (which & 4)
+		outb(bcs->ctrl.sr.mode, 
+		     adapter->io + AVM_DATA + HDLC_STATUS + 2);
+	if (which & 2)
+		outb(bcs->ctrl.sr.xml, 
+		     adapter->io + AVM_DATA + HDLC_STATUS + 1);
+	if (which & 1)
+		outb(bcs->ctrl.sr.cmd,
+		     adapter->io + AVM_DATA + HDLC_STATUS + 0);
+}
+
+static void fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned long flags;
+
+	spin_lock_irqsave(&adapter->hw_lock, flags);
+	__fcpnp_write_ctrl(bcs, which);
+	spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+
+static inline void B_L1L2(struct fritz_bcs *bcs, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+	DBG(2, "pr %#x", pr);
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void hdlc_fill_fifo(struct fritz_bcs *bcs)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	struct sk_buff *skb = bcs->tx_skb;
+	int count;
+	unsigned long flags;
+	unsigned char *p;
+
+	DBG(0x40, "hdlc_fill_fifo");
+
+	if (skb->len == 0)
+		BUG();
+
+	bcs->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+	if (bcs->tx_skb->len > bcs->fifo_size) {
+		count = bcs->fifo_size;
+	} else {
+		count = bcs->tx_skb->len;
+		if (bcs->mode != L1_MODE_TRANS)
+			bcs->ctrl.sr.cmd |= HDLC_CMD_XME;
+	}
+	DBG(0x40, "hdlc_fill_fifo %d/%d", count, bcs->tx_skb->len);
+	p = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt += count;
+	bcs->ctrl.sr.xml = ((count == bcs->fifo_size) ? 0 : count);
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCI:
+		spin_lock_irqsave(&adapter->hw_lock, flags);
+		// sets the correct AVM_INDEX, too
+		__fcpci_write_ctrl(bcs, 3);
+		outsl(adapter->io + AVM_DATA + HDLC_FIFO,
+		      p, (count + 3) / 4);
+		spin_unlock_irqrestore(&adapter->hw_lock, flags);
+		break;
+	case AVM_FRITZ_PCIV2:
+		fcpci2_write_ctrl(bcs, 3);
+		outsl(adapter->io + 
+		      (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+		      p, (count + 3) / 4);
+		break;
+	case AVM_FRITZ_PNP:
+		spin_lock_irqsave(&adapter->hw_lock, flags);
+		// sets the correct AVM_INDEX, too
+		__fcpnp_write_ctrl(bcs, 3);
+		outsb(adapter->io + AVM_DATA, p, count);
+		spin_unlock_irqrestore(&adapter->hw_lock, flags);
+		break;
+	}
+}
+
+static inline void hdlc_empty_fifo(struct fritz_bcs *bcs, int count)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	unsigned char *p;
+	unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+	DBG(0x10, "hdlc_empty_fifo %d", count);
+	if (bcs->rcvidx + count > HSCX_BUFMAX) {
+		DBG(0x10, "hdlc_empty_fifo: incoming packet too large");
+		return;
+	}
+	p = bcs->rcvbuf + bcs->rcvidx;
+	bcs->rcvidx += count;
+	switch (adapter->type) {
+	case AVM_FRITZ_PCI:
+		spin_lock(&adapter->hw_lock);
+		outl(idx, adapter->io + AVM_INDEX);
+		insl(adapter->io + AVM_DATA + HDLC_FIFO, 
+		     p, (count + 3) / 4);
+		spin_unlock(&adapter->hw_lock);
+		break;
+	case AVM_FRITZ_PCIV2:
+		insl(adapter->io + 
+		     (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+		     p, (count + 3) / 4);
+		break;
+	case AVM_FRITZ_PNP:
+		spin_lock(&adapter->hw_lock);
+		outb(idx, adapter->io + AVM_INDEX);
+		insb(adapter->io + AVM_DATA, p, count);
+		spin_unlock(&adapter->hw_lock);
+		break;
+	}
+}
+
+static inline void hdlc_rpr_irq(struct fritz_bcs *bcs, u32 stat)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	struct sk_buff *skb;
+	int len;
+
+	if (stat & HDLC_STAT_RDO) {
+		DBG(0x10, "RDO");
+		bcs->ctrl.sr.xml = 0;
+		bcs->ctrl.sr.cmd |= HDLC_CMD_RRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->rcvidx = 0;
+		return;
+	}
+
+	len = (stat & HDLC_STAT_RML_MASK) >> 8;
+	if (len == 0)
+		len = bcs->fifo_size;
+
+	hdlc_empty_fifo(bcs, len);
+
+	if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+		if (((stat & HDLC_STAT_CRCVFRRAB)== HDLC_STAT_CRCVFR) ||
+		    (bcs->mode == L1_MODE_TRANS)) {
+			skb = dev_alloc_skb(bcs->rcvidx);
+			if (!skb) {
+				printk(KERN_WARNING "HDLC: receive out of memory\n");
+			} else {
+				memcpy(skb_put(skb, bcs->rcvidx), bcs->rcvbuf,
+				       bcs->rcvidx);
+				DBG_SKB(1, skb);
+				B_L1L2(bcs, PH_DATA | INDICATION, skb);
+			}
+			bcs->rcvidx = 0;
+		} else {
+			DBG(0x10, "ch%d invalid frame %#x",
+			    bcs->channel, stat);
+			bcs->rcvidx = 0;
+		}
+	}
+}
+
+static inline void hdlc_xdu_irq(struct fritz_bcs *bcs)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	
+
+	/* Here we lost an TX interrupt, so
+	 * restart transmitting the whole frame.
+	 */
+	bcs->ctrl.sr.xml = 0;
+	bcs->ctrl.sr.cmd |= HDLC_CMD_XRS;
+	adapter->write_ctrl(bcs, 1);
+	bcs->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+
+	if (!bcs->tx_skb) {
+		DBG(0x10, "XDU without skb");
+		adapter->write_ctrl(bcs, 1);
+		return;
+	}
+	/* only hdlc restarts the frame, transparent mode must continue */
+	if (bcs->mode == L1_MODE_HDLC) {
+		skb_push(bcs->tx_skb, bcs->tx_cnt);
+		bcs->tx_cnt = 0;
+	}
+}
+
+static inline void hdlc_xpr_irq(struct fritz_bcs *bcs)
+{
+	struct sk_buff *skb;
+
+	skb = bcs->tx_skb;
+	if (!skb)
+		return;
+
+	if (skb->len) {
+		hdlc_fill_fifo(bcs);
+		return;
+	}
+	bcs->tx_cnt = 0;
+	bcs->tx_skb = NULL;
+	B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+	dev_kfree_skb_irq(skb);
+}
+
+static void hdlc_irq_one(struct fritz_bcs *bcs, u32 stat)
+{
+	DBG(0x10, "ch%d stat %#x", bcs->channel, stat);
+	if (stat & HDLC_INT_RPR) {
+		DBG(0x10, "RPR");
+		hdlc_rpr_irq(bcs, stat);
+	}
+	if (stat & HDLC_INT_XDU) {
+		DBG(0x10, "XDU");
+		hdlc_xdu_irq(bcs);
+		hdlc_xpr_irq(bcs);
+		return;
+	}
+	if (stat & HDLC_INT_XPR) {
+		DBG(0x10, "XPR");
+		hdlc_xpr_irq(bcs);
+	}
+}
+
+static inline void hdlc_irq(struct fritz_adapter *adapter)
+{
+	int nr;
+	u32 stat;
+
+	for (nr = 0; nr < 2; nr++) {
+		stat = adapter->read_hdlc_status(adapter, nr);
+		DBG(0x10, "HDLC %c stat %#x", 'A' + nr, stat);
+		if (stat & HDLC_INT_MASK)
+			hdlc_irq_one(&adapter->bcs[nr], stat);
+	}
+}
+
+static void modehdlc(struct fritz_bcs *bcs, int mode)
+{
+	struct fritz_adapter *adapter = bcs->adapter;
+	
+	DBG(0x40, "hdlc %c mode %d --> %d",
+	    'A' + bcs->channel, bcs->mode, mode);
+
+	if (bcs->mode == mode)
+		return;
+
+	bcs->fifo_size = 32;
+	bcs->ctrl.ctrl = 0;
+	bcs->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+	switch (mode) {
+	case L1_MODE_NULL:
+		bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+		adapter->write_ctrl(bcs, 5);
+		break;
+	case L1_MODE_TRANS:
+	case L1_MODE_HDLC:
+		bcs->rcvidx = 0;
+		bcs->tx_cnt = 0;
+		bcs->tx_skb = NULL;
+		if (mode == L1_MODE_TRANS) {
+			bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+		} else {
+			bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+		}
+		adapter->write_ctrl(bcs, 5);
+		bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
+		adapter->write_ctrl(bcs, 1);
+		bcs->ctrl.sr.cmd = 0;
+		break;
+	}
+	bcs->mode = mode;
+}
+
+static void fritz_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct fritz_bcs *bcs = ifc->priv;
+	struct sk_buff *skb = arg;
+	int mode;
+
+	DBG(0x10, "pr %#x", pr);
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+		if (bcs->tx_skb)
+			BUG();
+		
+		bcs->tx_skb = skb;
+		DBG_SKB(1, skb);
+		hdlc_fill_fifo(bcs);
+		break;
+	case PH_ACTIVATE | REQUEST:
+		mode = (int) arg;
+		DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+		modehdlc(bcs, mode);
+		B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+		modehdlc(bcs, L1_MODE_NULL);
+		B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+		break;
+	}
+}
+
+// ----------------------------------------------------------------------
+
+static irqreturn_t
+fcpci2_irq(int intno, void *dev, struct pt_regs *regs)
+{
+	struct fritz_adapter *adapter = dev;
+	unsigned char val;
+
+	val = inb(adapter->io + AVM_STATUS0);
+	if (!(val & AVM_STATUS0_IRQ_MASK))
+		/* hopefully a shared  IRQ reqest */
+		return IRQ_NONE;
+	DBG(2, "STATUS0 %#x", val);
+	if (val & AVM_STATUS0_IRQ_ISAC)
+		isacsx_irq(&adapter->isac);
+	if (val & AVM_STATUS0_IRQ_HDLC)
+		hdlc_irq(adapter);
+	if (val & AVM_STATUS0_IRQ_ISAC)
+		isacsx_irq(&adapter->isac);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+fcpci_irq(int intno, void *dev, struct pt_regs *regs)
+{
+	struct fritz_adapter *adapter = dev;
+	unsigned char sval;
+
+	sval = inb(adapter->io + 2);
+	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+		/* possibly a shared  IRQ reqest */
+		return IRQ_NONE;
+	DBG(2, "sval %#x", sval);
+	if (!(sval & AVM_STATUS0_IRQ_ISAC))
+		isac_irq(&adapter->isac);
+
+	if (!(sval & AVM_STATUS0_IRQ_HDLC))
+		hdlc_irq(adapter);
+	return IRQ_HANDLED;
+}
+
+// ----------------------------------------------------------------------
+
+static inline void fcpci2_init(struct fritz_adapter *adapter)
+{
+	outb(AVM_STATUS0_RES_TIMER, adapter->io + AVM_STATUS0);
+	outb(AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+}
+
+static inline void fcpci_init(struct fritz_adapter *adapter)
+{
+	outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | 
+	     AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+	outb(AVM_STATUS1_ENA_IOM | adapter->irq, 
+	     adapter->io + AVM_STATUS1);
+	mdelay(10);
+}
+
+// ----------------------------------------------------------------------
+
+static int __devinit fcpcipnp_setup(struct fritz_adapter *adapter)
+{
+	u32 val = 0;
+	int retval;
+
+	DBG(1,"");
+
+	isac_init(&adapter->isac); // FIXME is this okay now
+
+	retval = -EBUSY;
+	if (!request_region(adapter->io, 32, "fcpcipnp"))
+		goto err;
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		retval = request_irq(adapter->irq, fcpci2_irq, SA_SHIRQ, 
+				     "fcpcipnp", adapter);
+		break;
+	case AVM_FRITZ_PCI:
+		retval = request_irq(adapter->irq, fcpci_irq, SA_SHIRQ,
+				     "fcpcipnp", adapter);
+		break;
+	case AVM_FRITZ_PNP:
+		retval = request_irq(adapter->irq, fcpci_irq, 0,
+				     "fcpcipnp", adapter);
+		break;
+	}
+	if (retval)
+		goto err_region;
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+	case AVM_FRITZ_PCI:
+		val = inl(adapter->io);
+		break;
+	case AVM_FRITZ_PNP:
+		val = inb(adapter->io);
+		val |= inb(adapter->io + 1) << 8;
+		break;
+	}
+
+	DBG(1, "stat %#x Class %X Rev %d",
+	    val, val & 0xff, (val>>8) & 0xff);
+
+	spin_lock_init(&adapter->hw_lock);
+	adapter->isac.priv = adapter;
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		adapter->isac.read_isac       = &fcpci2_read_isac;
+		adapter->isac.write_isac      = &fcpci2_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci2_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci2_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpci2_read_hdlc_status;
+		adapter->write_ctrl           = &fcpci2_write_ctrl;
+		break;
+	case AVM_FRITZ_PCI:
+		adapter->isac.read_isac       = &fcpci_read_isac;
+		adapter->isac.write_isac      = &fcpci_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpci_read_hdlc_status;
+		adapter->write_ctrl           = &fcpci_write_ctrl;
+		break;
+	case AVM_FRITZ_PNP:
+		adapter->isac.read_isac       = &fcpci_read_isac;
+		adapter->isac.write_isac      = &fcpci_write_isac;
+		adapter->isac.read_isac_fifo  = &fcpci_read_isac_fifo;
+		adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+		adapter->read_hdlc_status     = &fcpnp_read_hdlc_status;
+		adapter->write_ctrl           = &fcpnp_write_ctrl;
+		break;
+	}
+
+	// Reset
+	outb(0, adapter->io + AVM_STATUS0);
+	mdelay(10);
+	outb(AVM_STATUS0_RESET, adapter->io + AVM_STATUS0);
+	mdelay(10);
+	outb(0, adapter->io + AVM_STATUS0);
+	mdelay(10);
+
+	switch (adapter->type) {
+	case AVM_FRITZ_PCIV2:
+		fcpci2_init(adapter);
+		isacsx_setup(&adapter->isac);
+		break;
+	case AVM_FRITZ_PCI:
+	case AVM_FRITZ_PNP:
+		fcpci_init(adapter);
+		isac_setup(&adapter->isac);
+		break;
+	}
+	val = adapter->read_hdlc_status(adapter, 0);
+	DBG(0x20, "HDLC A STA %x", val);
+	val = adapter->read_hdlc_status(adapter, 1);
+	DBG(0x20, "HDLC B STA %x", val);
+
+	adapter->bcs[0].mode = -1;
+	adapter->bcs[1].mode = -1;
+	modehdlc(&adapter->bcs[0], L1_MODE_NULL);
+	modehdlc(&adapter->bcs[1], L1_MODE_NULL);
+
+	return 0;
+
+ err_region:
+	release_region(adapter->io, 32);
+ err:
+	return retval;
+}
+
+static void __devexit fcpcipnp_release(struct fritz_adapter *adapter)
+{
+	DBG(1,"");
+
+	outb(0, adapter->io + AVM_STATUS0);
+	free_irq(adapter->irq, adapter);
+	release_region(adapter->io, 32);
+}
+
+// ----------------------------------------------------------------------
+
+static struct fritz_adapter * __devinit 
+new_adapter(void)
+{
+	struct fritz_adapter *adapter;
+	struct hisax_b_if *b_if[2];
+	int i;
+
+	adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL);
+	if (!adapter)
+		return NULL;
+
+	memset(adapter, 0, sizeof(struct fritz_adapter));
+
+	adapter->isac.hisax_d_if.owner = THIS_MODULE;
+	adapter->isac.hisax_d_if.ifc.priv = &adapter->isac;
+	adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1;
+	
+	for (i = 0; i < 2; i++) {
+		adapter->bcs[i].adapter = adapter;
+		adapter->bcs[i].channel = i;
+		adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+		adapter->bcs[i].b_if.ifc.l2l1 = fritz_b_l2l1;
+	}
+
+	for (i = 0; i < 2; i++)
+		b_if[i] = &adapter->bcs[i].b_if;
+
+	hisax_register(&adapter->isac.hisax_d_if, b_if, "fcpcipnp", protocol);
+
+	return adapter;
+}
+
+static void delete_adapter(struct fritz_adapter *adapter)
+{
+	hisax_unregister(&adapter->isac.hisax_d_if);
+	kfree(adapter);
+}
+
+static int __devinit fcpci_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *ent)
+{
+	struct fritz_adapter *adapter;
+	int retval;
+
+	retval = -ENOMEM;
+	adapter = new_adapter();
+	if (!adapter)
+		goto err;
+
+	pci_set_drvdata(pdev, adapter);
+
+	if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 
+		adapter->type = AVM_FRITZ_PCIV2;
+	else
+		adapter->type = AVM_FRITZ_PCI;
+
+	retval = pci_enable_device(pdev);
+	if (retval)
+		goto err_free;
+
+	adapter->io = pci_resource_start(pdev, 1);
+	adapter->irq = pdev->irq;
+
+	printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at %s\n",
+	       (char *) ent->driver_data, pci_name(pdev));
+
+	retval = fcpcipnp_setup(adapter);
+	if (retval)
+		goto err_free;
+
+	return 0;
+	
+ err_free:
+	delete_adapter(adapter);
+ err:
+	return retval;
+}
+
+#ifdef __ISAPNP__
+static int __devinit fcpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+	struct fritz_adapter *adapter;
+	int retval;
+
+	if (!pdev)
+		return(-ENODEV);
+
+	retval = -ENOMEM;
+	adapter = new_adapter();
+	if (!adapter)
+		goto err;
+
+	pnp_set_drvdata(pdev, adapter);
+
+	adapter->type = AVM_FRITZ_PNP;
+
+	pnp_disable_dev(pdev);
+	retval = pnp_activate_dev(pdev);
+	if (retval < 0) {
+		printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__,
+			(char *)dev_id->driver_data, retval);
+		goto err_free;
+	}
+	adapter->io = pnp_port_start(pdev, 0);
+	adapter->irq = pnp_irq(pdev, 0);
+
+	printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at IO %#x irq %d\n",
+	       (char *) dev_id->driver_data, adapter->io, adapter->irq);
+
+	retval = fcpcipnp_setup(adapter);
+	if (retval)
+		goto err_free;
+
+	return 0;
+	
+ err_free:
+	delete_adapter(adapter);
+ err:
+	return retval;
+}
+
+static void __devexit fcpnp_remove(struct pnp_dev *pdev)
+{
+	struct fritz_adapter *adapter = pnp_get_drvdata(pdev);
+
+	if (adapter) {
+		fcpcipnp_release(adapter);
+		delete_adapter(adapter);
+	}
+	pnp_disable_dev(pdev);
+}
+
+static struct pnp_driver fcpnp_driver = {
+	.name		= "fcpnp",
+	.probe		= fcpnp_probe,
+	.remove		= __devexit_p(fcpnp_remove),
+	.id_table	= fcpnp_ids,
+};
+#endif
+
+static void __devexit fcpci_remove(struct pci_dev *pdev)
+{
+	struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+	fcpcipnp_release(adapter);
+	pci_disable_device(pdev);
+	delete_adapter(adapter);
+}
+
+static struct pci_driver fcpci_driver = {
+	.name		= "fcpci",
+	.probe		= fcpci_probe,
+	.remove		= __devexit_p(fcpci_remove),
+	.id_table	= fcpci_ids,
+};
+
+static int __init hisax_fcpcipnp_init(void)
+{
+	int retval;
+
+	printk(KERN_INFO "hisax_fcpcipnp: Fritz!Card PCI/PCIv2/PnP ISDN driver v0.0.1\n");
+
+	retval = pci_register_driver(&fcpci_driver);
+	if (retval)
+		goto out;
+#ifdef __ISAPNP__
+	retval = pnp_register_driver(&fcpnp_driver);
+	if (retval < 0)
+		goto out_unregister_pci;
+#endif
+	return 0;
+
+ out_unregister_pci:
+	pci_unregister_driver(&fcpci_driver);
+ out:
+	return retval;
+}
+
+static void __exit hisax_fcpcipnp_exit(void)
+{
+#ifdef __ISAPNP__
+	pnp_unregister_driver(&fcpnp_driver);
+#endif
+	pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(hisax_fcpcipnp_init);
+module_exit(hisax_fcpcipnp_exit);
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.h b/drivers/isdn/hisax/hisax_fcpcipnp.h
new file mode 100644
index 0000000..bd8a22e
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.h
@@ -0,0 +1,58 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include <linux/pci.h>
+
+#define HSCX_BUFMAX	4096
+
+enum {
+	AVM_FRITZ_PCI,
+	AVM_FRITZ_PNP,
+	AVM_FRITZ_PCIV2,
+};
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+	u_char fill __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char cmd  __attribute__((packed));
+#else
+	u_char cmd  __attribute__((packed));
+	u_char xml  __attribute__((packed));
+	u_char mode __attribute__((packed));
+	u_char fill __attribute__((packed));
+#endif
+};
+
+struct fritz_bcs {
+	struct hisax_b_if b_if;
+	struct fritz_adapter *adapter;
+	int mode;
+	int channel;
+
+	union {
+		u_int ctrl;
+		struct hdlc_stat_reg sr;
+	} ctrl;
+	u_int stat;
+	int rcvidx;
+	int fifo_size;
+	u_char rcvbuf[HSCX_BUFMAX]; /* B-Channel receive Buffer */
+	
+	int tx_cnt;		    /* B-Channel transmit counter */
+	struct sk_buff *tx_skb;     /* B-Channel transmit Buffer */
+};
+
+struct fritz_adapter {
+	int type;
+	spinlock_t hw_lock;
+	unsigned int io;
+	unsigned int irq;
+	struct isac isac;
+
+	struct fritz_bcs bcs[2];
+
+	u32  (*read_hdlc_status) (struct fritz_adapter *adapter, int nr);
+	void (*write_ctrl) (struct fritz_bcs *bcs, int which);
+};
+
diff --git a/drivers/isdn/hisax/hisax_if.h b/drivers/isdn/hisax/hisax_if.h
new file mode 100644
index 0000000..4898fce
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_if.h
@@ -0,0 +1,66 @@
+/*
+ * Interface between low level (hardware) drivers and 
+ * HiSax protocol stack
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __HISAX_IF_H__
+#define __HISAX_IF_H__
+
+#include <linux/skbuff.h>
+
+#define REQUEST		0
+#define CONFIRM		1
+#define INDICATION	2
+#define RESPONSE	3
+
+#define PH_ACTIVATE	0x0100
+#define PH_DEACTIVATE	0x0110
+#define PH_DATA		0x0120
+#define PH_PULL		0x0130
+#define PH_DATA_E	0x0140
+
+#define L1_MODE_NULL	0
+#define L1_MODE_TRANS	1
+#define L1_MODE_HDLC	2
+#define L1_MODE_EXTRN	3
+#define L1_MODE_HDLC_56K 4
+#define L1_MODE_MODEM	7
+#define L1_MODE_V32	8
+#define L1_MODE_FAX	9
+
+struct hisax_if {
+	void *priv; // private to driver
+	void (*l1l2)(struct hisax_if *, int pr, void *arg);
+	void (*l2l1)(struct hisax_if *, int pr, void *arg);
+};
+
+struct hisax_b_if {
+	struct hisax_if ifc;
+
+	// private to hisax
+	struct BCState *bcs;
+};
+
+struct hisax_d_if {
+	struct hisax_if ifc;
+
+	// private to hisax
+	struct module *owner;
+	struct IsdnCardState *cs;
+	struct hisax_b_if *b_if[2];
+	struct sk_buff_head erq;
+	long ph_state;
+};
+
+int hisax_register(struct hisax_d_if *hisax_if, struct hisax_b_if *b_if[],
+		   char *name, int protocol);
+void hisax_unregister(struct hisax_d_if *hisax_if);
+
+#endif
diff --git a/drivers/isdn/hisax/hisax_isac.c b/drivers/isdn/hisax/hisax_isac.c
new file mode 100644
index 0000000..f4972f6
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_isac.c
@@ -0,0 +1,897 @@
+/*
+ * Driver for ISAC-S and ISAC-SX 
+ * ISDN Subscriber Access Controller for Terminals
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *              2001 by Karsten Keil       <keil@isdn4linux.de>
+ * 
+ * based upon Karsten Keil's original isac.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ *           SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+/* TODO:
+ * specifically handle level vs edge triggered?
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "hisax_isac.h"
+
+// debugging cruft
+
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 1;
+module_param(debug, int, 0);
+
+static char *ISACVer[] = {
+  "2086/2186 V1.1", 
+  "2085 B1", 
+  "2085 B2",
+  "2085 V2.3"
+};
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("ISAC/ISAC-SX driver");
+MODULE_LICENSE("GPL");
+
+#define DBG_WARN      0x0001
+#define DBG_IRQ       0x0002
+#define DBG_L1M       0x0004
+#define DBG_PR        0x0008
+#define DBG_RFIFO     0x0100
+#define DBG_RPACKET   0x0200
+#define DBG_XFIFO     0x1000
+#define DBG_XPACKET   0x2000
+
+// we need to distinguish ISAC-S and ISAC-SX
+#define TYPE_ISAC        0x00
+#define TYPE_ISACSX      0x01
+
+// registers etc.
+#define ISAC_MASK        0x20
+#define ISAC_ISTA        0x20
+#define ISAC_ISTA_EXI    0x01
+#define ISAC_ISTA_SIN    0x02
+#define ISAC_ISTA_CISQ   0x04
+#define ISAC_ISTA_XPR    0x10
+#define ISAC_ISTA_RSC    0x20
+#define ISAC_ISTA_RPF    0x40
+#define ISAC_ISTA_RME    0x80
+
+#define ISAC_STAR        0x21
+#define ISAC_CMDR        0x21
+#define ISAC_CMDR_XRES   0x01
+#define ISAC_CMDR_XME    0x02
+#define ISAC_CMDR_XTF    0x08
+#define ISAC_CMDR_RRES   0x40
+#define ISAC_CMDR_RMC    0x80
+
+#define ISAC_EXIR        0x24
+#define ISAC_EXIR_MOS    0x04
+#define ISAC_EXIR_XDU    0x40
+#define ISAC_EXIR_XMR    0x80
+
+#define ISAC_ADF2        0x39
+#define ISAC_SPCR        0x30
+#define ISAC_ADF1        0x38
+
+#define ISAC_CIR0        0x31
+#define ISAC_CIX0        0x31
+#define ISAC_CIR0_CIC0   0x02
+#define ISAC_CIR0_CIC1   0x01
+
+#define ISAC_CIR1        0x33
+#define ISAC_CIX1        0x33
+#define ISAC_STCR        0x37
+#define ISAC_MODE        0x22
+
+#define ISAC_RSTA        0x27
+#define ISAC_RSTA_RDO    0x40
+#define ISAC_RSTA_CRC    0x20
+#define ISAC_RSTA_RAB    0x10
+
+#define ISAC_RBCL 0x25
+#define ISAC_RBCH 0x2A
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+#define ISAC_MOSR 0x3a
+#define ISAC_MOCR 0x3a
+#define ISAC_MOR0 0x32
+#define ISAC_MOX0 0x32
+#define ISAC_MOR1 0x34
+#define ISAC_MOX1 0x34
+
+#define ISAC_RBCH_XAC 0x80
+
+#define ISAC_CMD_TIM    0x0
+#define ISAC_CMD_RES    0x1
+#define ISAC_CMD_SSP    0x2
+#define ISAC_CMD_SCP    0x3
+#define ISAC_CMD_AR8    0x8
+#define ISAC_CMD_AR10   0x9
+#define ISAC_CMD_ARL    0xa
+#define ISAC_CMD_DI     0xf
+
+#define ISACSX_MASK       0x60
+#define ISACSX_ISTA       0x60
+#define ISACSX_ISTA_ICD   0x01
+#define ISACSX_ISTA_CIC   0x10
+
+#define ISACSX_MASKD      0x20
+#define ISACSX_ISTAD      0x20
+#define ISACSX_ISTAD_XDU  0x04
+#define ISACSX_ISTAD_XMR  0x08
+#define ISACSX_ISTAD_XPR  0x10
+#define ISACSX_ISTAD_RFO  0x20
+#define ISACSX_ISTAD_RPF  0x40
+#define ISACSX_ISTAD_RME  0x80
+
+#define ISACSX_CMDRD      0x21
+#define ISACSX_CMDRD_XRES 0x01
+#define ISACSX_CMDRD_XME  0x02
+#define ISACSX_CMDRD_XTF  0x08
+#define ISACSX_CMDRD_RRES 0x40
+#define ISACSX_CMDRD_RMC  0x80
+
+#define ISACSX_MODED      0x22
+
+#define ISACSX_RBCLD      0x26
+
+#define ISACSX_RSTAD      0x28
+#define ISACSX_RSTAD_RAB  0x10
+#define ISACSX_RSTAD_CRC  0x20
+#define ISACSX_RSTAD_RDO  0x40
+#define ISACSX_RSTAD_VFR  0x80
+
+#define ISACSX_CIR0       0x2e
+#define ISACSX_CIR0_CIC0  0x08
+#define ISACSX_CIX0       0x2e
+
+#define ISACSX_TR_CONF0   0x30
+
+#define ISACSX_TR_CONF2   0x32
+
+static struct Fsm l1fsm;
+
+enum {
+	ST_L1_RESET,
+	ST_L1_F3_PDOWN,
+	ST_L1_F3_PUP,
+	ST_L1_F3_PEND_DEACT,
+	ST_L1_F4,
+	ST_L1_F5,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1State[] =
+{
+	"ST_L1_RESET",
+	"ST_L1_F3_PDOWN",
+	"ST_L1_F3_PUP",
+	"ST_L1_F3_PEND_DEACT",
+	"ST_L1_F4",
+	"ST_L1_F5",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+enum {
+	EV_PH_DR,           // 0000
+	EV_PH_RES,          // 0001
+	EV_PH_TMA,          // 0010
+	EV_PH_SLD,          // 0011
+	EV_PH_RSY,          // 0100
+	EV_PH_DR6,          // 0101
+	EV_PH_EI,           // 0110
+	EV_PH_PU,           // 0111
+	EV_PH_AR,           // 1000
+	EV_PH_9,            // 1001
+	EV_PH_ARL,          // 1010
+	EV_PH_CVR,          // 1011
+	EV_PH_AI8,          // 1100
+	EV_PH_AI10,         // 1101
+	EV_PH_AIL,          // 1110
+	EV_PH_DC,           // 1111
+	EV_PH_ACTIVATE_REQ,
+	EV_PH_DEACTIVATE_REQ,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+	"EV_PH_DR",           // 0000
+	"EV_PH_RES",          // 0001
+	"EV_PH_TMA",          // 0010
+	"EV_PH_SLD",          // 0011
+	"EV_PH_RSY",          // 0100
+	"EV_PH_DR6",          // 0101
+	"EV_PH_EI",           // 0110
+	"EV_PH_PU",           // 0111
+	"EV_PH_AR",           // 1000
+	"EV_PH_9",            // 1001
+	"EV_PH_ARL",          // 1010
+	"EV_PH_CVR",          // 1011
+	"EV_PH_AI8",          // 1100
+	"EV_PH_AI10",         // 1101
+	"EV_PH_AIL",          // 1110
+	"EV_PH_DC",           // 1111
+	"EV_PH_ACTIVATE_REQ",
+	"EV_PH_DEACTIVATE_REQ",
+	"EV_TIMER3",
+};
+
+static inline void D_L1L2(struct isac *isac, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if;
+
+	DBG(DBG_PR, "pr %#x", pr);
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void ph_command(struct isac *isac, unsigned int command)
+{
+	DBG(DBG_L1M, "ph_command %#x", command);
+	switch (isac->type) {
+	case TYPE_ISAC:
+		isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3);
+		break;
+	case TYPE_ISACSX:
+		isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1));
+		break;
+	}
+}
+
+// ----------------------------------------------------------------------
+
+static void l1_di(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F3_PDOWN);
+}
+
+static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+	ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f4(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F4);
+}
+
+static void l1_go_f5(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F5);
+}
+
+static void l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F6);
+}
+
+static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F6);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmDelTimer(&isac->timer, 0);
+	FsmChangeState(fi, ST_L1_F7);
+	ph_command(isac, ISAC_CMD_AR8);
+	D_L1L2(isac, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F8);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_ar8(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	ph_command(isac, ISAC_CMD_AR8);
+}
+
+static void l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct isac *isac = fi->userdata;
+
+	ph_command(isac, ISAC_CMD_DI);
+	D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+// state machines according to data sheet PSB 2186 / 3186
+
+static struct FsmNode L1FnList[] __initdata =
+{
+	{ST_L1_RESET,         EV_PH_RES,            l1_di},
+	{ST_L1_RESET,         EV_PH_EI,             l1_di},
+	{ST_L1_RESET,         EV_PH_DC,             l1_go_f3pdown},
+	{ST_L1_RESET,         EV_PH_AR,             l1_go_f6},
+	{ST_L1_RESET,         EV_PH_AI8,            l1_go_f7_act_ind},
+
+	{ST_L1_F3_PDOWN,      EV_PH_RES,            l1_di},
+	{ST_L1_F3_PDOWN,      EV_PH_EI,             l1_di},
+	{ST_L1_F3_PDOWN,      EV_PH_AR,             l1_go_f6},
+	{ST_L1_F3_PDOWN,      EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F3_PDOWN,      EV_PH_PU,             l1_go_f4},
+	{ST_L1_F3_PDOWN,      EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F3_PDOWN,      EV_PH_ACTIVATE_REQ,   l1_ar8},
+	{ST_L1_F3_PDOWN,      EV_TIMER3,            l1_timer3},
+	
+	{ST_L1_F3_PEND_DEACT, EV_PH_RES,            l1_di},
+	{ST_L1_F3_PEND_DEACT, EV_PH_EI,             l1_di},
+	{ST_L1_F3_PEND_DEACT, EV_PH_DC,             l1_go_f3pdown},
+	{ST_L1_F3_PEND_DEACT, EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F3_PEND_DEACT, EV_PH_AR,             l1_go_f6},
+	{ST_L1_F3_PEND_DEACT, EV_PH_AI8,            l1_go_f7_act_ind},
+
+	{ST_L1_F4,            EV_PH_RES,            l1_di},
+	{ST_L1_F4,            EV_PH_EI,             l1_di},
+	{ST_L1_F4,            EV_PH_RSY,            l1_go_f5},
+	{ST_L1_F4,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F4,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F4,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F5,            EV_PH_RES,            l1_di},
+	{ST_L1_F5,            EV_PH_EI,             l1_di},
+	{ST_L1_F5,            EV_PH_AR,             l1_go_f6},
+	{ST_L1_F5,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F5,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F5,            EV_PH_DR,             l1_go_f3pend},
+	{ST_L1_F5,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F6,            EV_PH_RES,            l1_di},
+	{ST_L1_F6,            EV_PH_EI,             l1_di},
+	{ST_L1_F6,            EV_PH_RSY,            l1_go_f8},
+	{ST_L1_F6,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F6,            EV_PH_DR6,            l1_go_f3pend},
+	{ST_L1_F6,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F6,            EV_PH_DC,             l1_go_f3pdown},
+
+	{ST_L1_F7,            EV_PH_RES,            l1_di_deact_ind},
+	{ST_L1_F7,            EV_PH_EI,             l1_di_deact_ind},
+	{ST_L1_F7,            EV_PH_AR,             l1_go_f6_deact_ind},
+	{ST_L1_F7,            EV_PH_RSY,            l1_go_f8_deact_ind},
+	{ST_L1_F7,            EV_PH_DR,             l1_go_f3pend_deact_ind},
+
+	{ST_L1_F8,            EV_PH_RES,            l1_di},
+	{ST_L1_F8,            EV_PH_EI,             l1_di},
+	{ST_L1_F8,            EV_PH_AR,             l1_go_f6},
+	{ST_L1_F8,            EV_PH_DR,             l1_go_f3pend},
+	{ST_L1_F8,            EV_PH_AI8,            l1_go_f7_act_ind},
+	{ST_L1_F8,            EV_TIMER3,            l1_timer3},
+	{ST_L1_F8,            EV_PH_DC,             l1_go_f3pdown},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	DBG(DBG_L1M, "%s", buf);
+	va_end(args);
+}
+
+static void isac_version(struct isac *cs)
+{
+	int val;
+
+	val = cs->read_isac(cs, ISAC_RBCH);
+	DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]);
+}
+
+static void isac_empty_fifo(struct isac *isac, int count)
+{
+	// this also works for isacsx, since
+	// CMDR(D) register works the same
+	u_char *ptr;
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		DBG(DBG_WARN, "overrun %d", isac->rcvidx + count);
+		isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+		isac->rcvidx = 0;
+		return;
+	}
+	ptr = isac->rcvbuf + isac->rcvidx;
+	isac->rcvidx += count;
+	isac->read_isac_fifo(isac, ptr, count);
+	isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+	DBG_PACKET(DBG_RFIFO, ptr, count);
+}
+
+static void isac_fill_fifo(struct isac *isac)
+{
+	// this also works for isacsx, since
+	// CMDR(D) register works the same
+
+	int count;
+	unsigned char cmd;
+	u_char *ptr;
+
+	if (!isac->tx_skb)
+		BUG();
+
+	count = isac->tx_skb->len;
+	if (count <= 0)
+		BUG();
+
+	DBG(DBG_IRQ, "count %d", count);
+
+	if (count > 0x20) {
+		count = 0x20;
+		cmd = ISAC_CMDR_XTF;
+	} else {
+		cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME;
+	}
+
+	ptr = isac->tx_skb->data;
+	skb_pull(isac->tx_skb, count);
+	isac->tx_cnt += count;
+	DBG_PACKET(DBG_XFIFO, ptr, count);
+	isac->write_isac_fifo(isac, ptr, count);
+	isac->write_isac(isac, ISAC_CMDR, cmd);
+}
+
+static void isac_retransmit(struct isac *isac)
+{
+	if (!isac->tx_skb) {
+		DBG(DBG_WARN, "no skb");
+		return;
+	}
+	skb_push(isac->tx_skb, isac->tx_cnt);
+	isac->tx_cnt = 0;
+}
+
+
+static inline void isac_cisq_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_CIR0);
+	DBG(DBG_IRQ, "CIR0 %#x", val);
+	if (val & ISAC_CIR0_CIC0) {
+		DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf);
+		FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+	}
+	if (val & ISAC_CIR0_CIC1) {
+		val = isac->read_isac(isac, ISAC_CIR1);
+		DBG(DBG_WARN, "ISAC CIR1 %#x", val );
+	}
+}
+
+static inline void isac_rme_interrupt(struct isac *isac)
+{
+	unsigned char val;
+	int count;
+	struct sk_buff *skb;
+	
+	val = isac->read_isac(isac, ISAC_RSTA);
+	if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB) )
+	     != ISAC_RSTA_CRC) {
+		DBG(DBG_WARN, "RSTA %#x, dropped", val);
+		isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+		goto out;
+	}
+
+	count = isac->read_isac(isac, ISAC_RBCL) & 0x1f;
+	DBG(DBG_IRQ, "RBCL %#x", count);
+	if (count == 0)
+		count = 0x20;
+
+	isac_empty_fifo(isac, count);
+	count = isac->rcvidx;
+	if (count < 1) {
+		DBG(DBG_WARN, "count %d < 1", count);
+		goto out;
+	}
+
+	skb = alloc_skb(count, GFP_ATOMIC);
+	if (!skb) {
+		DBG(DBG_WARN, "no memory, dropping\n");
+		goto out;
+	}
+	memcpy(skb_put(skb, count), isac->rcvbuf, count);
+	DBG_SKB(DBG_RPACKET, skb);
+	D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+	isac->rcvidx = 0;
+}
+
+static inline void isac_xpr_interrupt(struct isac *isac)
+{
+	if (!isac->tx_skb)
+		return;
+
+	if (isac->tx_skb->len > 0) {
+		isac_fill_fifo(isac);
+		return;
+	}
+	dev_kfree_skb_irq(isac->tx_skb);
+	isac->tx_cnt = 0;
+	isac->tx_skb = NULL;
+	D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isac_exi_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_EXIR);
+	DBG(2, "EXIR %#x", val);
+
+	if (val & ISAC_EXIR_XMR) {
+		DBG(DBG_WARN, "ISAC XMR");
+		isac_retransmit(isac);
+	}
+	if (val & ISAC_EXIR_XDU) {
+		DBG(DBG_WARN, "ISAC XDU");
+		isac_retransmit(isac);
+	}
+	if (val & ISAC_EXIR_MOS) {  /* MOS */
+		DBG(DBG_WARN, "MOS");
+		val = isac->read_isac(isac, ISAC_MOSR);
+		DBG(2, "ISAC MOSR %#x", val);
+	}
+}
+
+void isac_irq(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISAC_ISTA);
+	DBG(DBG_IRQ, "ISTA %#x", val);
+
+	if (val & ISAC_ISTA_EXI) {
+		DBG(DBG_IRQ, "EXI");
+		isac_exi_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_XPR) {
+		DBG(DBG_IRQ, "XPR");
+		isac_xpr_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RME) {
+		DBG(DBG_IRQ, "RME");
+		isac_rme_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RPF) {
+		DBG(DBG_IRQ, "RPF");
+		isac_empty_fifo(isac, 0x20);
+	}
+	if (val & ISAC_ISTA_CISQ) {
+		DBG(DBG_IRQ, "CISQ");
+		isac_cisq_interrupt(isac);
+	}
+	if (val & ISAC_ISTA_RSC) {
+		DBG(DBG_WARN, "RSC");
+	}
+	if (val & ISAC_ISTA_SIN) {
+		DBG(DBG_WARN, "SIN");
+	}
+	isac->write_isac(isac, ISAC_MASK, 0xff);
+	isac->write_isac(isac, ISAC_MASK, 0x00);
+}
+
+// ======================================================================
+
+static inline void isacsx_cic_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_CIR0);
+	DBG(DBG_IRQ, "CIR0 %#x", val);
+	if (val & ISACSX_CIR0_CIC0) {
+		DBG(DBG_IRQ, "CODR0 %#x", val >> 4);
+		FsmEvent(&isac->l1m, val >> 4, NULL);
+	}
+}
+
+static inline void isacsx_rme_interrupt(struct isac *isac)
+{
+	int count;
+	struct sk_buff *skb;
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_RSTAD);
+	if ((val & (ISACSX_RSTAD_VFR | 
+		    ISACSX_RSTAD_RDO | 
+		    ISACSX_RSTAD_CRC | 
+		    ISACSX_RSTAD_RAB)) 
+	    != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) {
+		DBG(DBG_WARN, "RSTAD %#x, dropped", val);
+		isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+		goto out;
+	}
+
+	count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f;
+	DBG(DBG_IRQ, "RBCLD %#x", count);
+	if (count == 0)
+		count = 0x20;
+
+	isac_empty_fifo(isac, count);
+	// strip trailing status byte
+	count = isac->rcvidx - 1;
+	if (count < 1) {
+		DBG(DBG_WARN, "count %d < 1", count);
+		goto out;
+	}
+
+	skb = dev_alloc_skb(count);
+	if (!skb) {
+		DBG(DBG_WARN, "no memory, dropping");
+		goto out;
+	}
+	memcpy(skb_put(skb, count), isac->rcvbuf, count);
+	DBG_SKB(DBG_RPACKET, skb);
+	D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+	isac->rcvidx = 0;
+}
+
+static inline void isacsx_xpr_interrupt(struct isac *isac)
+{
+	if (!isac->tx_skb)
+		return;
+
+	if (isac->tx_skb->len > 0) {
+		isac_fill_fifo(isac);
+		return;
+	}
+	dev_kfree_skb_irq(isac->tx_skb);
+	isac->tx_skb = NULL;
+	isac->tx_cnt = 0;
+	D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isacsx_icd_interrupt(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_ISTAD);
+	DBG(DBG_IRQ, "ISTAD %#x", val);
+	if (val & ISACSX_ISTAD_XDU) {
+		DBG(DBG_WARN, "ISTAD XDU");
+		isac_retransmit(isac);
+	}
+	if (val & ISACSX_ISTAD_XMR) {
+		DBG(DBG_WARN, "ISTAD XMR");
+		isac_retransmit(isac);
+	}
+	if (val & ISACSX_ISTAD_XPR) {
+		DBG(DBG_IRQ, "ISTAD XPR");
+		isacsx_xpr_interrupt(isac);
+	}
+	if (val & ISACSX_ISTAD_RFO) {
+		DBG(DBG_WARN, "ISTAD RFO");
+		isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+	}
+	if (val & ISACSX_ISTAD_RME) {
+		DBG(DBG_IRQ, "ISTAD RME");
+		isacsx_rme_interrupt(isac);
+	}
+	if (val & ISACSX_ISTAD_RPF) {
+		DBG(DBG_IRQ, "ISTAD RPF");
+		isac_empty_fifo(isac, 0x20);
+	}
+}
+
+void isacsx_irq(struct isac *isac)
+{
+	unsigned char val;
+
+	val = isac->read_isac(isac, ISACSX_ISTA);
+	DBG(DBG_IRQ, "ISTA %#x", val);
+
+	if (val & ISACSX_ISTA_ICD)
+		isacsx_icd_interrupt(isac);
+	if (val & ISACSX_ISTA_CIC)
+		isacsx_cic_interrupt(isac);
+}
+
+void isac_init(struct isac *isac)
+{
+	isac->tx_skb = NULL;
+	isac->l1m.fsm = &l1fsm;
+	isac->l1m.state = ST_L1_RESET;
+#ifdef CONFIG_HISAX_DEBUG
+	isac->l1m.debug = 1;
+#else
+	isac->l1m.debug = 0;
+#endif
+	isac->l1m.userdata = isac;
+	isac->l1m.printdebug = l1m_debug;
+	FsmInitTimer(&isac->l1m, &isac->timer);
+}
+
+void isac_setup(struct isac *isac)
+{
+	int val, eval;
+
+	isac->type = TYPE_ISAC;
+	isac_version(isac);
+
+	ph_command(isac, ISAC_CMD_RES);
+
+  	isac->write_isac(isac, ISAC_MASK, 0xff);
+  	isac->mocr = 0xaa;
+	if (test_bit(ISAC_IOM1, &isac->flags)) {
+		/* IOM 1 Mode */
+		isac->write_isac(isac, ISAC_ADF2, 0x0);
+		isac->write_isac(isac, ISAC_SPCR, 0xa);
+		isac->write_isac(isac, ISAC_ADF1, 0x2);
+		isac->write_isac(isac, ISAC_STCR, 0x70);
+		isac->write_isac(isac, ISAC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!isac->adf2)
+			isac->adf2 = 0x80;
+		isac->write_isac(isac, ISAC_ADF2, isac->adf2);
+		isac->write_isac(isac, ISAC_SQXR, 0x2f);
+		isac->write_isac(isac, ISAC_SPCR, 0x00);
+		isac->write_isac(isac, ISAC_STCR, 0x70);
+		isac->write_isac(isac, ISAC_MODE, 0xc9);
+		isac->write_isac(isac, ISAC_TIMR, 0x00);
+		isac->write_isac(isac, ISAC_ADF1, 0x00);
+	}
+	val = isac->read_isac(isac, ISAC_STAR);
+	DBG(2, "ISAC STAR %x", val);
+	val = isac->read_isac(isac, ISAC_MODE);
+	DBG(2, "ISAC MODE %x", val);
+	val = isac->read_isac(isac, ISAC_ADF2);
+	DBG(2, "ISAC ADF2 %x", val);
+	val = isac->read_isac(isac, ISAC_ISTA);
+	DBG(2, "ISAC ISTA %x", val);
+	if (val & 0x01) {
+		eval = isac->read_isac(isac, ISAC_EXIR);
+		DBG(2, "ISAC EXIR %x", eval);
+	}
+	val = isac->read_isac(isac, ISAC_CIR0);
+	DBG(2, "ISAC CIR0 %x", val);
+	FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+
+	isac->write_isac(isac, ISAC_MASK, 0x0);
+	// RESET Receiver and Transmitter
+	isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES);
+}
+
+void isacsx_setup(struct isac *isac)
+{
+	isac->type = TYPE_ISACSX;
+	// clear LDD
+	isac->write_isac(isac, ISACSX_TR_CONF0, 0x00);
+	// enable transmitter
+	isac->write_isac(isac, ISACSX_TR_CONF2, 0x00);
+	// transparent mode 0, RAC, stop/go
+	isac->write_isac(isac, ISACSX_MODED,    0xc9);
+	// all HDLC IRQ unmasked
+	isac->write_isac(isac, ISACSX_MASKD,    0x03);
+	// unmask ICD, CID IRQs
+	isac->write_isac(isac, ISACSX_MASK,            
+			 ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
+}
+
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+	struct isac *isac = hisax_d_if->priv;
+	struct sk_buff *skb = arg;
+
+	DBG(DBG_PR, "pr %#x", pr);
+
+	switch (pr) {
+	case PH_ACTIVATE | REQUEST:
+		FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+		break;
+	case PH_DATA | REQUEST:
+		DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len);
+		DBG_SKB(DBG_XPACKET, skb);
+		if (isac->l1m.state != ST_L1_F7) {
+			DBG(1, "L1 wrong state %d\n", isac->l1m.state);
+			dev_kfree_skb(skb);
+			break;
+		}
+		if (isac->tx_skb)
+			BUG();
+
+		isac->tx_skb = skb;
+		isac_fill_fifo(isac);
+		break;
+	}
+}
+
+static int __init hisax_isac_init(void)
+{
+	printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n");
+
+	l1fsm.state_count = L1_STATE_COUNT;
+	l1fsm.event_count = L1_EVENT_COUNT;
+	l1fsm.strState = strL1State;
+	l1fsm.strEvent = strL1Event;
+	return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+}
+
+static void __exit hisax_isac_exit(void)
+{
+	FsmFree(&l1fsm);
+}
+
+EXPORT_SYMBOL(isac_init);
+EXPORT_SYMBOL(isac_d_l2l1);
+
+EXPORT_SYMBOL(isacsx_setup);
+EXPORT_SYMBOL(isacsx_irq);
+
+EXPORT_SYMBOL(isac_setup);
+EXPORT_SYMBOL(isac_irq);
+
+module_init(hisax_isac_init);
+module_exit(hisax_isac_exit);
diff --git a/drivers/isdn/hisax/hisax_isac.h b/drivers/isdn/hisax/hisax_isac.h
new file mode 100644
index 0000000..08890cf
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_isac.h
@@ -0,0 +1,45 @@
+#ifndef __HISAX_ISAC_H__
+#define __HISAX_ISAC_H__
+
+#include <linux/kernel.h>
+#include "fsm.h"
+#include "hisax_if.h"
+
+#define TIMER3_VALUE 7000
+#define MAX_DFRAME_LEN_L1 300
+
+#define ISAC_IOM1	0
+
+struct isac {
+	void *priv;
+
+	u_long flags;
+	struct hisax_d_if hisax_d_if;
+	struct FsmInst l1m;
+	struct FsmTimer timer;
+	u_char mocr;
+	u_char adf2;
+	int    type;
+
+	u_char rcvbuf[MAX_DFRAME_LEN_L1];
+	int rcvidx;
+
+	struct sk_buff *tx_skb;
+	int tx_cnt;
+
+	u_char (*read_isac)      (struct isac *, u_char);
+	void   (*write_isac)     (struct isac *, u_char, u_char);
+	void   (*read_isac_fifo) (struct isac *, u_char *, int);
+	void   (*write_isac_fifo)(struct isac *, u_char *, int);
+};
+
+void isac_init(struct isac *isac);
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+void isac_setup(struct isac *isac);
+void isac_irq(struct isac *isac);
+
+void isacsx_setup(struct isac *isac);
+void isacsx_irq(struct isac *isac);
+
+#endif
diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c
new file mode 100644
index 0000000..5bbbe3e
--- /dev/null
+++ b/drivers/isdn/hisax/hscx.c
@@ -0,0 +1,280 @@
+/* $Id: hscx.c,v 1.24.2.4 2004/01/24 20:47:23 keil Exp $
+ *
+ * HSCX specific routines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hscx.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+int
+HscxVersion(struct IsdnCardState *cs, char *s)
+{
+	int verA, verB;
+
+	verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf;
+	verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf;
+	printk(KERN_INFO "%s HSCX version A: %s  B: %s\n", s,
+	       HSCXVer[verA], HSCXVer[verB]);
+	if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf))
+		return (1);
+	else
+		return (0);
+}
+
+void
+modehscx(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hscx = bcs->hw.hscx.hscx;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "hscx %c mode %d ichan %d",
+			'A' + hscx, mode, bc);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+		test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85);
+	cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30);
+	cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7);
+	cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7);
+
+	/* Switch IOM 1 SSI */
+	if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0))
+		bc = 1 - bc;
+
+	if (bc == 0) {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR,
+			      test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
+	} else {
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, bcs->hw.hscx.tsaxr1);
+		cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, bcs->hw.hscx.tsaxr1);
+	}
+	switch (mode) {
+		case (L1_MODE_NULL):
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f);
+			cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f);
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84);
+			break;
+		case (L1_MODE_TRANS):
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4);
+			break;
+		case (L1_MODE_HDLC):
+			cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+				test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d);
+			cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c);
+			break;
+	}
+	if (mode)
+		cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41);
+	cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+hscx_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	u_long flags;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.hscx.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->hw.hscx.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			modehscx(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			modehscx(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_hscxstate(struct BCState *bcs)
+{
+	modehscx(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+int
+open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for hscx.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_hscx(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_hscxstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = hscx_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void
+clear_pending_hscx_ints(struct IsdnCardState *cs)
+{
+	int val, eval;
+
+	val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
+	debugl1(cs, "HSCX B ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
+		debugl1(cs, "HSCX B EXIR %x", eval);
+	}
+	if (val & 0x02) {
+		eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
+		debugl1(cs, "HSCX A EXIR %x", eval);
+	}
+	val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA);
+	debugl1(cs, "HSCX A ISTA %x", val);
+	val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
+	debugl1(cs, "HSCX B STAR %x", val);
+	val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
+	debugl1(cs, "HSCX A STAR %x", val);
+	/* disable all IRQ */
+	cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF);
+	cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF);
+}
+
+void
+inithscx(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_hscx;
+	cs->bcs[1].BC_SetStack = setstack_hscx;
+	cs->bcs[0].BC_Close = close_hscxstate;
+	cs->bcs[1].BC_Close = close_hscxstate;
+	cs->bcs[0].hw.hscx.hscx = 0;
+	cs->bcs[1].hw.hscx.hscx = 1;
+	cs->bcs[0].hw.hscx.tsaxr0 = 0x2f;
+	cs->bcs[0].hw.hscx.tsaxr1 = 3;
+	cs->bcs[1].hw.hscx.tsaxr0 = 0x2f;
+	cs->bcs[1].hw.hscx.tsaxr1 = 3;
+	modehscx(cs->bcs, 0, 0);
+	modehscx(cs->bcs + 1, 0, 0);
+}
+
+void
+inithscxisac(struct IsdnCardState *cs, int part)
+{
+	if (part & 1) {
+		clear_pending_isac_ints(cs);
+		clear_pending_hscx_ints(cs);
+		initisac(cs);
+		inithscx(cs);
+	}
+	if (part & 2) {
+		/* Reenable all IRQ */
+		cs->writeisac(cs, ISAC_MASK, 0);
+		cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
+		cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
+		/* RESET Receiver and Transmitter */
+		cs->writeisac(cs, ISAC_CMDR, 0x41);
+	}
+}
diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h
new file mode 100644
index 0000000..268bfd3
--- /dev/null
+++ b/drivers/isdn/hisax/hscx.h
@@ -0,0 +1,41 @@
+/* $Id: hscx.h,v 1.8.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * HSCX specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define HSCX_ISTA 0x20
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+#define HSCX_CMDR 0x21
+#define HSCX_EXIR 0x24
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_RSTA 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+extern int HscxVersion(struct IsdnCardState *cs, char *s);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void clear_pending_hscx_ints(struct IsdnCardState *cs);
+extern void inithscx(struct IsdnCardState *cs);
+extern void inithscxisac(struct IsdnCardState *cs, int part);
diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c
new file mode 100644
index 0000000..5fe9d42
--- /dev/null
+++ b/drivers/isdn/hisax/hscx_irq.c
@@ -0,0 +1,292 @@
+/* $Id: hscx_irq.c,v 1.18.2.3 2004/02/11 13:21:34 keil Exp $
+ *
+ * low level b-channel stuff for Siemens HSCX
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This is an include file for fast inline IRQ stuff
+ *
+ */
+
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int hscx)
+{
+	int to = 50;
+
+	while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+		udelay(1);
+		to--;
+	}
+	if (!to)
+		printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+	waitforCEC(cs, hscx);
+	WRITEHSCX(cs, hscx, HSCX_CMDR, data);
+}
+
+
+
+static void
+hscx_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+		WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+hscx_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "hscx_fill_fifo");
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->tx_skb->len;
+
+	waitforXFW(cs, bcs->hw.hscx.hscx);
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static inline void
+hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + hscx;
+	struct sk_buff *skb;
+	int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+	int count;
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = READHSCX(cs, hscx, HSCX_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX invalid frame");
+#ifdef ERROR_STATISTIC
+				bcs->err_inv++;
+#endif
+			}
+			if ((r & 0x40) && bcs->mode) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX RDO mode=%d",
+						bcs->mode);
+#ifdef ERROR_STATISTIC
+				bcs->err_rdo++;
+#endif
+			}
+			if (!(r & 0x20)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX CRC error");
+#ifdef ERROR_STATISTIC
+				bcs->err_crc++;
+#endif
+			}
+			WriteHSCXCMDR(cs, hscx, 0x80);
+		} else {
+			count = READHSCX(cs, hscx, HSCX_RBCL) & (
+				test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+			if (count == 0)
+				count = fifo_size;
+			hscx_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HSCX: receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		hscx_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				hscx_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hscx.count = 0; 
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			hscx_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+hscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+	u_char exval;
+	struct BCState *bcs;
+
+	if (val & 0x01) {
+		bcs = cs->bcs + 1;
+		exval = READHSCX(cs, 1, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == 1)
+				hscx_fill_fifo(bcs);
+			else {
+#ifdef ERROR_STATISTIC
+				bcs->err_tx++;
+#endif
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B EXIR %x", exval);
+	}
+	if (val & 0xf8) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX B interrupt %x", val);
+		hscx_interrupt(cs, val, 1);
+	}
+	if (val & 0x02) {
+		bcs = cs->bcs;
+		exval = READHSCX(cs, 0, HSCX_EXIR);
+		if (exval & 0x40) {
+			if (bcs->mode == L1_MODE_TRANS)
+				hscx_fill_fifo(bcs);
+			else {
+				/* Here we lost an TX interrupt, so
+				   * restart transmitting the whole frame.
+				 */
+#ifdef ERROR_STATISTIC
+				bcs->err_tx++;
+#endif
+				if (bcs->tx_skb) {
+					skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+					bcs->tx_cnt += bcs->hw.hscx.count;
+					bcs->hw.hscx.count = 0;
+				}
+				WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
+			}
+		} else if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A EXIR %x", exval);
+	}
+	if (val & 0x04) {
+		exval = READHSCX(cs, 0, HSCX_ISTA);
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX A interrupt %x", exval);
+		hscx_interrupt(cs, exval, 0);
+	}
+}
diff --git a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c
new file mode 100644
index 0000000..dcf31f8
--- /dev/null
+++ b/drivers/isdn/hisax/icc.c
@@ -0,0 +1,685 @@
+/* $Id: icc.c,v 1.8.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * ICC specific routines
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 1999.6.25 Initial implementation of routines for Siemens ISDN
+ * Communication Controller PEB 2070 based on the ISAC routines
+ * written by Karsten Keil.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "icc.h"
+// #include "arcofi.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 0
+
+static char *ICCVer[] __initdata =
+{"2070 A1/A3", "2070 B1", "2070 B2/B3", "2070 V2.4"};
+
+void __init
+ICCVersion(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readisac(cs, ICC_RBCH);
+	printk(KERN_INFO "%s ICC version (%x): %s\n", s, val, ICCVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, ICC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+icc_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.icc.ph_state) {
+		case (ICC_IND_EI1):
+			ph_command(cs, ICC_CMD_DI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+		case (ICC_IND_DC):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (ICC_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (ICC_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (ICC_IND_FJ):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (ICC_IND_AR):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (ICC_IND_AI):
+			l1_msg(cs, HW_INFO4 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+icc_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		icc_new_ph(cs);		
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+#if ARCOFI_USE
+	if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
+		return;
+	if (test_and_clear_bit(D_RX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+	if (test_and_clear_bit(D_TX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+#endif
+}
+
+void
+icc_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "icc_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "icc_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeisac(cs, ICC_CMDR, 0x80);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ICC_CMDR, 0x80);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "icc_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+icc_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "icc_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ICC_CMDR, more ? 0x8 : 0xa);
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "icc_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "icc_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+void
+icc_interrupt(struct IsdnCardState *cs, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ICC interrupt %x", val);
+	if (val & 0x80) {	/* RME */
+		exval = cs->readisac(cs, ICC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ICC RDO");
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			}
+			if (!(exval & 0x20)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ICC CRC error");
+#ifdef ERROR_STATISTIC
+				cs->err_crc++;
+#endif
+			}
+			cs->writeisac(cs, ICC_CMDR, 0x80);
+		} else {
+			count = cs->readisac(cs, ICC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			icc_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+		}
+		cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		icc_empty_fifo(cs, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				icc_fill_fifo(cs);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb_irq(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			icc_fill_fifo(cs);
+		} else
+			schedule_event(cs, D_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		exval = cs->readisac(cs, ICC_CIR0);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ICC CIR0 %02X", exval );
+		if (exval & 2) {
+			cs->dc.icc.ph_state = (exval >> 2) & 0xf;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ph_state change %x", cs->dc.icc.ph_state);
+			schedule_event(cs, D_L1STATECHANGE);
+		}
+		if (exval & 1) {
+			exval = cs->readisac(cs, ICC_CIR1);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ICC CIR1 %02X", exval );
+		}
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = cs->readisac(cs, ICC_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ICC EXIR %02x", exval);
+		if (exval & 0x80) {  /* XMR */
+			debugl1(cs, "ICC XMR");
+			printk(KERN_WARNING "HiSax: ICC XMR\n");
+		}
+		if (exval & 0x40) {  /* XDU */
+			debugl1(cs, "ICC XDU");
+			printk(KERN_WARNING "HiSax: ICC XDU\n");
+#ifdef ERROR_STATISTIC
+			cs->err_tx++;
+#endif
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) { /* Restart frame */
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+				icc_fill_fifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: ICC XDU no skb\n");
+				debugl1(cs, "ICC XDU no skb");
+			}
+		}
+		if (exval & 0x04) {  /* MOS */
+			v1 = cs->readisac(cs, ICC_MOSR);
+			if (cs->debug & L1_DEB_MONITOR)
+				debugl1(cs, "ICC MOSR %02x", v1);
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!cs->dc.icc.mon_rx) {
+					if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ICC MON RX out of memory!");
+						cs->dc.icc.mocr &= 0xf0;
+						cs->dc.icc.mocr |= 0x0a;
+						cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+						goto afterMONR0;
+					} else
+						cs->dc.icc.mon_rxp = 0;
+				}
+				if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.icc.mocr &= 0xf0;
+					cs->dc.icc.mocr |= 0x0a;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ICC MON RX overflow!");
+					goto afterMONR0;
+				}
+				cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR0);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC MOR0 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+				if (cs->dc.icc.mon_rxp == 1) {
+					cs->dc.icc.mocr |= 0x04;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!cs->dc.icc.mon_rx) {
+					if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ICC MON RX out of memory!");
+						cs->dc.icc.mocr &= 0x0f;
+						cs->dc.icc.mocr |= 0xa0;
+						cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+						goto afterMONR1;
+					} else
+						cs->dc.icc.mon_rxp = 0;
+				}
+				if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.icc.mocr &= 0x0f;
+					cs->dc.icc.mocr |= 0xa0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ICC MON RX overflow!");
+					goto afterMONR1;
+				}
+				cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR1);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC MOR1 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+				cs->dc.icc.mocr |= 0x40;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				cs->dc.icc.mocr &= 0xf0;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				cs->dc.icc.mocr |= 0x0a;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				schedule_event(cs, D_RX_MON0);
+			}
+			if (v1 & 0x40) {
+				cs->dc.icc.mocr &= 0x0f;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				cs->dc.icc.mocr |= 0xa0;
+				cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+				schedule_event(cs, D_RX_MON1);
+			}
+			if (v1 & 0x02) {
+				if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && 
+					(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && 
+					!(v1 & 0x08))) {
+					cs->dc.icc.mocr &= 0xf0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mocr |= 0x0a;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					if (cs->dc.icc.mon_txc &&
+						(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+						schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+					schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				cs->writeisac(cs, ICC_MOX0,
+					cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC %02x -> MOX0", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+			}
+		      AfterMOX0:
+			if (v1 & 0x20) {
+				if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc && 
+					(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) && 
+					!(v1 & 0x80))) {
+					cs->dc.icc.mocr &= 0x0f;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					cs->dc.icc.mocr |= 0xa0;
+					cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+					if (cs->dc.icc.mon_txc &&
+						(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+						schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+					schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				cs->writeisac(cs, ICC_MOX1,
+					cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ICC %02x -> MOX1", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+			}
+		      AfterMOX1:
+#endif
+		}
+	}
+}
+
+static void
+ICC_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	int  val;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				icc_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL |INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			icc_fill_fifo(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.icc.ph_state == ICC_IND_EI1) ||
+				(cs->dc.icc.ph_state == ICC_IND_DR))
+			        ph_command(cs, ICC_CMD_DI);
+			else
+				ph_command(cs, ICC_CMD_RES);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ICC_CMD_DI);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO1 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ICC_CMD_AR);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ICC_CMD_AI);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ICC_SPCR, 0xa);
+					cs->writeisac(cs, ICC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ICC_SPCR, val);
+					cs->writeisac(cs, ICC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ICC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ICC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ICC_ADF1, 0x0);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "icc_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_icc(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = ICC_l1hw;
+}
+
+void 
+DC_Close_icc(struct IsdnCardState *cs) {
+	if (cs->dc.icc.mon_rx) {
+		kfree(cs->dc.icc.mon_rx);
+		cs->dc.icc.mon_rx = NULL;
+	}
+	if (cs->dc.icc.mon_tx) {
+		kfree(cs->dc.icc.mon_tx);
+		cs->dc.icc.mon_tx = NULL;
+	}
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int	rbch, star;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbch = cs->readisac(cs, ICC_RBCH);
+		star = cs->readisac(cs, ICC_STAR);
+		if (cs->debug) 
+			debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
+				rbch, star);
+		if (rbch & ICC_RBCH_XAC) { /* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: ICC D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeisac(cs, ICC_CMDR, 0x01); /* Transmitter reset */
+			cs->irq_func(cs->irq, cs, NULL);
+		}
+	}
+}
+
+void __init
+initicc(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_icc;
+	cs->DC_Close = DC_Close_icc;
+	cs->dc.icc.mon_tx = NULL;
+	cs->dc.icc.mon_rx = NULL;
+  	cs->writeisac(cs, ICC_MASK, 0xff);
+  	cs->dc.icc.mocr = 0xaa;
+	if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+		/* IOM 1 Mode */
+		cs->writeisac(cs, ICC_ADF2, 0x0);
+		cs->writeisac(cs, ICC_SPCR, 0xa);
+		cs->writeisac(cs, ICC_ADF1, 0x2);
+		cs->writeisac(cs, ICC_STCR, 0x70);
+		cs->writeisac(cs, ICC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!cs->dc.icc.adf2)
+			cs->dc.icc.adf2 = 0x80;
+		cs->writeisac(cs, ICC_ADF2, cs->dc.icc.adf2);
+		cs->writeisac(cs, ICC_SQXR, 0xa0);
+		cs->writeisac(cs, ICC_SPCR, 0x20);
+		cs->writeisac(cs, ICC_STCR, 0x70);
+		cs->writeisac(cs, ICC_MODE, 0xca);
+		cs->writeisac(cs, ICC_TIMR, 0x00);
+		cs->writeisac(cs, ICC_ADF1, 0x20);
+	}
+	ph_command(cs, ICC_CMD_RES);
+	cs->writeisac(cs, ICC_MASK, 0x0);
+	ph_command(cs, ICC_CMD_DI);
+}
+
+void __init
+clear_pending_icc_ints(struct IsdnCardState *cs)
+{
+	int val, eval;
+
+	val = cs->readisac(cs, ICC_STAR);
+	debugl1(cs, "ICC STAR %x", val);
+	val = cs->readisac(cs, ICC_MODE);
+	debugl1(cs, "ICC MODE %x", val);
+	val = cs->readisac(cs, ICC_ADF2);
+	debugl1(cs, "ICC ADF2 %x", val);
+	val = cs->readisac(cs, ICC_ISTA);
+	debugl1(cs, "ICC ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->readisac(cs, ICC_EXIR);
+		debugl1(cs, "ICC EXIR %x", eval);
+	}
+	val = cs->readisac(cs, ICC_CIR0);
+	debugl1(cs, "ICC CIR0 %x", val);
+	cs->dc.icc.ph_state = (val >> 2) & 0xf;
+	schedule_event(cs, D_L1STATECHANGE);
+	/* Disable all IRQ */
+	cs->writeisac(cs, ICC_MASK, 0xFF);
+}
+
+void __devinit
+setup_icc(struct IsdnCardState *cs)
+{
+	INIT_WORK(&cs->tqueue, (void *)(void *) icc_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/icc.h b/drivers/isdn/hisax/icc.h
new file mode 100644
index 0000000..b3bb3d5
--- /dev/null
+++ b/drivers/isdn/hisax/icc.h
@@ -0,0 +1,72 @@
+/* $Id: icc.h,v 1.4.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * ICC specific routines
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 1999.7.14 Initial implementation of routines for Siemens ISDN 
+ * Communication Controller PEB 2070 based on the ISAC routines 
+ * written by Karsten Keil.
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define ICC_MASK 0x20
+#define ICC_ISTA 0x20
+#define ICC_STAR 0x21
+#define ICC_CMDR 0x21
+#define ICC_EXIR 0x24
+#define ICC_ADF2 0x39
+#define ICC_SPCR 0x30
+#define ICC_ADF1 0x38
+#define ICC_CIR0 0x31
+#define ICC_CIX0 0x31
+#define ICC_CIR1 0x33
+#define ICC_CIX1 0x33
+#define ICC_STCR 0x37
+#define ICC_MODE 0x22
+#define ICC_RSTA 0x27
+#define ICC_RBCL 0x25
+#define ICC_RBCH 0x2A
+#define ICC_TIMR 0x23
+#define ICC_SQXR 0x3b
+#define ICC_MOSR 0x3a
+#define ICC_MOCR 0x3a
+#define ICC_MOR0 0x32
+#define ICC_MOX0 0x32
+#define ICC_MOR1 0x34
+#define ICC_MOX1 0x34
+
+#define ICC_RBCH_XAC 0x80
+
+#define ICC_CMD_TIM    0x0
+#define ICC_CMD_RES    0x1
+#define ICC_CMD_DU     0x3
+#define ICC_CMD_EI1    0x4
+#define ICC_CMD_SSP    0x5
+#define ICC_CMD_DT     0x6
+#define ICC_CMD_AR     0x8
+#define ICC_CMD_ARL    0xA
+#define ICC_CMD_AI     0xC
+#define ICC_CMD_DI     0xF
+
+#define ICC_IND_DR     0x0
+#define ICC_IND_FJ     0x2
+#define ICC_IND_EI1    0x4
+#define ICC_IND_INT    0x6
+#define ICC_IND_PU     0x7
+#define ICC_IND_AR     0x8
+#define ICC_IND_ARL    0xA
+#define ICC_IND_AI     0xC
+#define ICC_IND_AIL    0xE
+#define ICC_IND_DC     0xF
+
+extern void __init ICCVersion(struct IsdnCardState *cs, char *s);
+extern void initicc(struct IsdnCardState *cs);
+extern void icc_interrupt(struct IsdnCardState *cs, u_char val);
+extern void clear_pending_icc_ints(struct IsdnCardState *cs);
+extern void setup_icc(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/ipac.h b/drivers/isdn/hisax/ipac.h
new file mode 100644
index 0000000..f92a04a
--- /dev/null
+++ b/drivers/isdn/hisax/ipac.h
@@ -0,0 +1,29 @@
+/* $Id: ipac.h,v 1.7.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * IPAC specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define IPAC_CONF	0xC0
+#define IPAC_MASK	0xC1
+#define IPAC_ISTA	0xC1
+#define IPAC_ID		0xC2
+#define IPAC_ACFG	0xC3
+#define IPAC_AOE	0xC4
+#define IPAC_ARX	0xC5
+#define IPAC_ATX	0xC5
+#define IPAC_PITA1	0xC6
+#define IPAC_PITA2	0xC7
+#define IPAC_POTA1	0xC8
+#define IPAC_POTA2	0xC9
+#define IPAC_PCFG	0xCA
+#define IPAC_SCFG	0xCB
+#define IPAC_TIMR2	0xCC
diff --git a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c
new file mode 100644
index 0000000..6485e23
--- /dev/null
+++ b/drivers/isdn/hisax/ipacx.c
@@ -0,0 +1,1004 @@
+/* 
+ *
+ * IPACX specific routines
+ *
+ * Author       Joerg Petersohn
+ * Derived from hisax_isac.c, isac.c, hscx.c and others
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax_if.h"
+#include "hisax.h"
+#include "isdnl1.h"
+#include "ipacx.h"
+
+#define DBUSY_TIMER_VALUE 80
+#define TIMER3_VALUE      7000
+#define MAX_DFRAME_LEN_L1 300
+#define B_FIFO_SIZE       64
+#define D_FIFO_SIZE       32
+
+
+// ipacx interrupt mask values    
+#define _MASK_IMASK     0x2E  // global mask
+#define _MASKB_IMASK    0x0B
+#define _MASKD_IMASK    0x03  // all on
+
+//----------------------------------------------------------
+// local function declarations
+//----------------------------------------------------------
+static void ph_command(struct IsdnCardState *cs, unsigned int command);
+static inline void cic_int(struct IsdnCardState *cs);
+static void dch_l2l1(struct PStack *st, int pr, void *arg);
+static void dbusy_timer_handler(struct IsdnCardState *cs);
+static void ipacx_new_ph(struct IsdnCardState *cs);
+static void dch_bh(struct IsdnCardState *cs);
+static void dch_empty_fifo(struct IsdnCardState *cs, int count);
+static void dch_fill_fifo(struct IsdnCardState *cs);
+static inline void dch_int(struct IsdnCardState *cs);
+static void __devinit dch_setstack(struct PStack *st, struct IsdnCardState *cs);
+static void __devinit dch_init(struct IsdnCardState *cs);
+static void bch_l2l1(struct PStack *st, int pr, void *arg);
+static void bch_empty_fifo(struct BCState *bcs, int count);
+static void bch_fill_fifo(struct BCState *bcs);
+static void bch_int(struct IsdnCardState *cs, u_char hscx);
+static void bch_mode(struct BCState *bcs, int mode, int bc);
+static void bch_close_state(struct BCState *bcs);
+static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs);
+static int bch_setstack(struct PStack *st, struct BCState *bcs);
+static void __devinit bch_init(struct IsdnCardState *cs, int hscx);
+static void __init clear_pending_ints(struct IsdnCardState *cs);
+
+//----------------------------------------------------------
+// Issue Layer 1 command to chip
+//----------------------------------------------------------
+static void 
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug &L1_DEB_ISAC)
+		debugl1(cs, "ph_command (%#x) in (%#x)", command,
+			cs->dc.isac.ph_state);
+//###################################  
+//	printk(KERN_INFO "ph_command (%#x)\n", command);
+//###################################  
+	cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E);
+}
+
+//----------------------------------------------------------
+// Transceiver interrupt handler
+//----------------------------------------------------------
+static inline void 
+cic_int(struct IsdnCardState *cs)
+{
+	u_char event;
+
+	event = cs->readisac(cs, IPACX_CIR0) >> 4;
+	if (cs->debug &L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event);
+//#########################################  
+//	printk(KERN_INFO "cic_int(%x)\n", event);
+//#########################################  
+  cs->dc.isac.ph_state = event;
+  schedule_event(cs, D_L1STATECHANGE);
+}
+
+//==========================================================
+// D channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Command entry point
+//----------------------------------------------------------
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+  u_char cda1_cr, cda2_cr;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug &DEB_DLOG_HEX)     LogFrame(cs, skb->data, skb->len);
+			if (cs->debug &DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG
+				if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+				if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				dch_fill_fifo(cs);
+			}
+			break;
+      
+		case (PH_PULL |INDICATION):
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)     LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+			if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			dch_fill_fifo(cs);
+			break;
+      
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG
+			if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+
+		case (HW_RESET | REQUEST):
+		case (HW_ENABLE | REQUEST):
+			if ((cs->dc.isac.ph_state == IPACX_IND_RES) ||
+				(cs->dc.isac.ph_state == IPACX_IND_DR) ||
+				(cs->dc.isac.ph_state == IPACX_IND_DC))
+			        ph_command(cs, IPACX_CMD_TIM);
+			else
+				ph_command(cs, IPACX_CMD_RES);
+			break;
+
+		case (HW_INFO3 | REQUEST):
+			ph_command(cs, IPACX_CMD_AR8);
+			break;
+
+		case (HW_TESTLOOP | REQUEST):
+      cs->writeisac(cs, IPACX_CDA_TSDP10, 0x80); // Timeslot 0 is B1
+      cs->writeisac(cs, IPACX_CDA_TSDP11, 0x81); // Timeslot 0 is B1
+      cda1_cr = cs->readisac(cs, IPACX_CDA1_CR);
+      cda2_cr = cs->readisac(cs, IPACX_CDA2_CR);
+			if ((long)arg &1) { // loop B1
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x0a); 
+      }
+      else {  // B1 off
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x0a); 
+      }
+			if ((long)arg &2) { // loop B2
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x14); 
+      }
+      else {  // B2 off
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x14); 
+      }
+			break;
+
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			break;
+
+		default:
+			if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_l2l1 unknown %04x", pr);
+			break;
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+	int	rbchd, stard;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbchd = cs->readisac(cs, IPACX_RBCHD);
+		stard = cs->readisac(cs, IPACX_STARD);
+		if (cs->debug) 
+      debugl1(cs, "D-Channel Busy RBCHD %02x STARD %02x", rbchd, stard);
+		if (!(stard &0x40)) { // D-Channel Busy
+			set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+      for (st = cs->stlist; st; st = st->next) {
+				st->l1.l1l2(st, PH_PAUSE | INDICATION, NULL); // flow control on
+			}
+		} else {
+			// seems we lost an interrupt; reset transceiver */
+			clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeisac(cs, IPACX_CMDRD, 0x01); // Tx reset, generates XPR
+		}
+	}
+}
+
+//----------------------------------------------------------
+// L1 state machine intermediate layer to isdnl1 module
+//----------------------------------------------------------
+static void
+ipacx_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.isac.ph_state) {
+		case (IPACX_IND_RES):
+			ph_command(cs, IPACX_CMD_DI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_DC):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+      
+		case (IPACX_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+
+		case (IPACX_IND_RSY):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+
+		case (IPACX_IND_AR):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_AI8):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+			break;
+      
+		case (IPACX_IND_AI10):
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+			break;
+      
+		default:
+			break;
+	}
+}
+
+//----------------------------------------------------------
+// bottom half handler for D channel
+//----------------------------------------------------------
+static void
+dch_bh(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+	
+	if (!cs) return;
+  
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug) debugl1(cs, "D-Channel Busy cleared");
+		for (st = cs->stlist; st; st = st->next) {
+			st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+		}
+	}
+  
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
+		DChannel_proc_rcv(cs);
+  }  
+  
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
+		DChannel_proc_xmt(cs);
+  }  
+  
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+    ipacx_new_ph(cs);
+  }  
+}
+
+//----------------------------------------------------------
+// Fill buffer from receive FIFO
+//----------------------------------------------------------
+static void 
+dch_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+		debugl1(cs, "dch_empty_fifo()");
+
+  // message too large, remove
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug &L1_DEB_WARN)
+			debugl1(cs, "dch_empty_fifo() incoming message too large");
+	  cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+		cs->rcvidx = 0;
+		return;
+	}
+  
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+  
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+  
+	if (cs->debug &L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "dch_empty_fifo() cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+//----------------------------------------------------------
+// Fill transmit FIFO
+//----------------------------------------------------------
+static void 
+dch_fill_fifo(struct IsdnCardState *cs)
+{
+	int count;
+	u_char cmd, *ptr;
+
+	if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+		debugl1(cs, "dch_fill_fifo()");
+    
+	if (!cs->tx_skb) return;
+	count = cs->tx_skb->len;
+	if (count <= 0) return;
+
+	if (count > D_FIFO_SIZE) {
+		count = D_FIFO_SIZE;
+		cmd   = 0x08; // XTF
+	} else {
+		cmd   = 0x0A; // XTF | XME
+	}
+  
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, IPACX_CMDRD, cmd);
+  
+  // set timeout for transmission contol
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "dch_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+  
+	if (cs->debug &L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "dch_fill_fifo() cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+//----------------------------------------------------------
+// D channel interrupt handler
+//----------------------------------------------------------
+static inline void 
+dch_int(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb;
+	u_char istad, rstad;
+	int count;
+
+	istad = cs->readisac(cs, IPACX_ISTAD);
+//##############################################  
+//	printk(KERN_WARNING "dch_int(istad=%02x)\n", istad);
+//##############################################  
+  
+	if (istad &0x80) {  // RME
+	  rstad = cs->readisac(cs, IPACX_RSTAD);
+		if ((rstad &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+			if (!(rstad &0x80))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): invalid frame");
+			if ((rstad &0x40))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): RDO");
+			if (!(rstad &0x20))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): CRC error");
+	    cs->writeisac(cs, IPACX_CMDRD, 0x80);  // RMC
+		} else {  // received frame ok
+			count = cs->readisac(cs, IPACX_RBCLD);
+      if (count) count--; // RSTAB is last byte
+			count &= D_FIFO_SIZE-1;
+			if (count == 0) count = D_FIFO_SIZE;
+			dch_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+	      cs->rcvidx = 0;
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HiSax dch_int(): receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+    }
+	  cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+
+	if (istad &0x40) {  // RPF
+		dch_empty_fifo(cs, D_FIFO_SIZE);
+	}
+
+	if (istad &0x20) {  // RFO
+		if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): RFO");
+	  cs->writeisac(cs, IPACX_CMDRD, 0x40); //RRES
+	}
+  
+  if (istad &0x10) {  // XPR
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+    if (cs->tx_skb) {
+      if (cs->tx_skb->len) {
+        dch_fill_fifo(cs);
+        goto afterXPR;
+      }
+      else {
+        dev_kfree_skb_irq(cs->tx_skb);
+        cs->tx_skb = NULL;
+        cs->tx_cnt = 0;
+      }
+    }
+    if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+      cs->tx_cnt = 0;
+      dch_fill_fifo(cs);
+    } 
+    else {
+      schedule_event(cs, D_XMTBUFREADY);
+    }  
+  }  
+  afterXPR:
+
+	if (istad &0x0C) {  // XDU or XMR
+		if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): XDU");
+	  if (cs->tx_skb) {
+	    skb_push(cs->tx_skb, cs->tx_cnt); // retransmit
+	    cs->tx_cnt = 0;
+			dch_fill_fifo(cs);
+		} else {
+			printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
+			debugl1(cs, "ISAC XDU no skb");
+		}
+  }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+dch_setstack(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = dch_l2l1;
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+dch_init(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n");
+
+	cs->setstack_d      = dch_setstack;
+  
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+
+  cs->writeisac(cs, IPACX_TR_CONF0, 0x00);  // clear LDD
+  cs->writeisac(cs, IPACX_TR_CONF2, 0x00);  // enable transmitter
+  cs->writeisac(cs, IPACX_MODED,    0xC9);  // transparent mode 0, RAC, stop/go
+  cs->writeisac(cs, IPACX_MON_CR,   0x00);  // disable monitor channel
+}
+
+
+//==========================================================
+// B channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Entry point for commands
+//----------------------------------------------------------
+static void
+bch_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.hscx.count = 0;
+				bch_fill_fifo(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "HiSax bch_l2l1(): this shouldn't happen\n");
+			} else {
+				set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->tx_skb = skb;
+				bcs->hw.hscx.count = 0;
+				bch_fill_fifo(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			bch_mode(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bch_mode(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+//----------------------------------------------------------
+// Read B channel fifo to receive buffer
+//----------------------------------------------------------
+static void
+bch_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr, hscx;
+	struct IsdnCardState *cs;
+	int cnt;
+
+	cs = bcs->cs;
+  hscx = bcs->hw.hscx.hscx;
+	if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+		debugl1(cs, "bch_empty_fifo()");
+
+  // message too large, remove
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug &L1_DEB_WARN)
+			debugl1(cs, "bch_empty_fifo() incoming packet too large");
+	  cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+  
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	cnt = count;
+	while (cnt--) *ptr++ = cs->BC_Read_Reg(cs, hscx, IPACX_RFIFOB); 
+	cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+  
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+  
+	if (cs->debug &L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "bch_empty_fifo() B-%d cnt %d", hscx, count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+//----------------------------------------------------------
+// Fill buffer to transmit FIFO
+//----------------------------------------------------------
+static void
+bch_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs;
+	int more, count, cnt;
+	u_char *ptr, *p, hscx;
+
+	cs = bcs->cs;
+	if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+		debugl1(cs, "bch_fill_fifo()");
+
+	if (!bcs->tx_skb)           return;
+	if (bcs->tx_skb->len <= 0)  return;
+
+	hscx = bcs->hw.hscx.hscx;
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > B_FIFO_SIZE) {
+		more  = 1;
+		count = B_FIFO_SIZE;
+	} else {
+		count = bcs->tx_skb->len;
+	}  
+	cnt = count;
+    
+	p = ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	while (cnt--) cs->BC_Write_Reg(cs, hscx, IPACX_XFIFOB, *p++); 
+	cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, (more ? 0x08 : 0x0a));
+  
+	if (cs->debug &L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "chb_fill_fifo() B-%d cnt %d", hscx, count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+//----------------------------------------------------------
+// B channel interrupt handler
+//----------------------------------------------------------
+static void
+bch_int(struct IsdnCardState *cs, u_char hscx)
+{
+	u_char istab;
+	struct BCState *bcs;
+	struct sk_buff *skb;
+	int count;
+	u_char rstab;
+
+	bcs = cs->bcs + hscx;
+	istab = cs->BC_Read_Reg(cs, hscx, IPACX_ISTAB);
+//##############################################  
+//	printk(KERN_WARNING "bch_int(istab=%02x)\n", istab);
+//##############################################  
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag)) return;
+
+	if (istab &0x80) {	// RME
+		rstab = cs->BC_Read_Reg(cs, hscx, IPACX_RSTAB);
+		if ((rstab &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+			if (!(rstab &0x80))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: invalid frame", hscx);
+			if ((rstab &0x40) && (bcs->mode != L1_MODE_NULL))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: RDO mode=%d", hscx, bcs->mode);
+			if (!(rstab &0x20))
+				if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: CRC error", hscx);
+	    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+		} 
+    else {  // received frame ok
+			count = cs->BC_Read_Reg(cs, hscx, IPACX_RBCLB) &(B_FIFO_SIZE-1);
+			if (count == 0) count = B_FIFO_SIZE;
+			bch_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug &L1_DEB_HSCX_FIFO)
+					debugl1(cs, "bch_int Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "HiSax bch_int(): receive frame out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+  
+	if (istab &0x40) {	// RPF
+		bch_empty_fifo(bcs, B_FIFO_SIZE);
+
+		if (bcs->mode == L1_MODE_TRANS) { // queue every chunk
+			// receive transparent audio data
+			if (!(skb = dev_alloc_skb(B_FIFO_SIZE)))
+				printk(KERN_WARNING "HiSax bch_int(): receive transparent out of memory\n");
+			else {
+				memcpy(skb_put(skb, B_FIFO_SIZE), bcs->hw.hscx.rcvbuf, B_FIFO_SIZE);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+  
+	if (istab &0x20) {	// RFO
+		if (cs->debug &L1_DEB_WARN) 
+			debugl1(cs, "bch_int() B-%d: RFO error", hscx);
+		cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x40);  // RRES
+	}
+
+	if (istab &0x10) {	// XPR
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				bch_fill_fifo(bcs);
+				goto afterXPR;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+			}
+			dev_kfree_skb_irq(bcs->tx_skb);
+			bcs->hw.hscx.count = 0;
+			bcs->tx_skb = NULL;
+    		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bch_fill_fifo(bcs);
+		} else {
+			clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+  afterXPR:
+
+	if (istab &0x04) {	// XDU
+    if (bcs->mode == L1_MODE_TRANS) {
+			bch_fill_fifo(bcs);
+    }  
+    else {
+      if (bcs->tx_skb) {  // restart transmitting the whole frame
+        skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+        bcs->tx_cnt += bcs->hw.hscx.count;
+        bcs->hw.hscx.count = 0;
+      }
+	    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x01);  // XRES
+      if (cs->debug &L1_DEB_WARN)
+        debugl1(cs, "bch_int() B-%d XDU error", hscx);
+    }
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_mode(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int hscx = bcs->hw.hscx.hscx;
+
+        bc = bc ? 1 : 0;  // in case bc is greater than 1
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "mode_bch() switch B-% mode %d chan %d", hscx, mode, bc);
+	bcs->mode = mode;
+	bcs->channel = bc;
+  
+  // map controller to according timeslot
+  if (!hscx)
+  {
+    cs->writeisac(cs, IPACX_BCHA_TSDP_BC1, 0x80 | bc);
+    cs->writeisac(cs, IPACX_BCHA_CR,       0x88); 
+  }
+  else
+  {
+    cs->writeisac(cs, IPACX_BCHB_TSDP_BC1, 0x80 | bc);
+    cs->writeisac(cs, IPACX_BCHB_CR,       0x88); 
+  }
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC0);  // rec off
+		    cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x30);  // std adj.
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, 0xFF);  // ints off
+		    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+		    break;
+		case (L1_MODE_TRANS):
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0x88);  // ext transp mode
+		    cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x00);  // xxx00000
+		    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+		    break;
+		case (L1_MODE_HDLC):
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC8);  // transp mode 0
+		    cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x01);  // idle=hdlc flags crc enabled
+		    cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+		    cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+		    break;
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_close_state(struct BCState *bcs)
+{
+	bch_mode(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.hscx.rcvbuf) {
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_open_state(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax open_bchstate(): No memory for hscx.rcvbuf\n");
+			clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax open_bchstate: No memory for bcs->blog\n");
+			clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_setstack(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (bch_open_state(st->l1.hardware, bcs)) return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = bch_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+bch_init(struct IsdnCardState *cs, int hscx)
+{
+	cs->bcs[hscx].BC_SetStack   = bch_setstack;
+	cs->bcs[hscx].BC_Close      = bch_close_state;
+	cs->bcs[hscx].hw.hscx.hscx  = hscx;
+	cs->bcs[hscx].cs            = cs;
+	bch_mode(cs->bcs + hscx, 0, hscx);
+}
+
+
+//==========================================================
+// Shared functions
+//==========================================================
+
+//----------------------------------------------------------
+// Main interrupt handler
+//----------------------------------------------------------
+void 
+interrupt_ipacx(struct IsdnCardState *cs)
+{
+	u_char ista;
+  
+	while ((ista = cs->readisac(cs, IPACX_ISTA))) {
+//#################################################  
+//		printk(KERN_WARNING "interrupt_ipacx(ista=%02x)\n", ista);
+//#################################################  
+    if (ista &0x80) bch_int(cs, 0); // B channel interrupts
+    if (ista &0x40) bch_int(cs, 1);
+    
+    if (ista &0x01) dch_int(cs);    // D channel
+    if (ista &0x10) cic_int(cs);    // Layer 1 state
+  }  
+}
+
+//----------------------------------------------------------
+// Clears chip interrupt status
+//----------------------------------------------------------
+static void __init
+clear_pending_ints(struct IsdnCardState *cs)
+{
+	int ista;
+
+  // all interrupts off
+  cs->writeisac(cs, IPACX_MASK, 0xff);
+	cs->writeisac(cs, IPACX_MASKD, 0xff);
+	cs->BC_Write_Reg(cs, 0, IPACX_MASKB, 0xff);
+	cs->BC_Write_Reg(cs, 1, IPACX_MASKB, 0xff);
+  
+  ista = cs->readisac(cs, IPACX_ISTA); 
+  if (ista &0x80) cs->BC_Read_Reg(cs, 0, IPACX_ISTAB);
+  if (ista &0x40) cs->BC_Read_Reg(cs, 1, IPACX_ISTAB);
+  if (ista &0x10) cs->readisac(cs, IPACX_CIR0);
+  if (ista &0x01) cs->readisac(cs, IPACX_ISTAD); 
+}
+
+//----------------------------------------------------------
+// Does chip configuration work
+// Work to do depends on bit mask in part
+//----------------------------------------------------------
+void __init
+init_ipacx(struct IsdnCardState *cs, int part)
+{
+	if (part &1) {  // initialise chip
+//##################################################  
+//	printk(KERN_INFO "init_ipacx(%x)\n", part);
+//##################################################  
+		clear_pending_ints(cs);
+		bch_init(cs, 0);
+		bch_init(cs, 1);
+		dch_init(cs);
+	}
+	if (part &2) {  // reenable all interrupts and start chip
+		cs->BC_Write_Reg(cs, 0, IPACX_MASKB, _MASKB_IMASK);
+		cs->BC_Write_Reg(cs, 1, IPACX_MASKB, _MASKB_IMASK);
+		cs->writeisac(cs, IPACX_MASKD, _MASKD_IMASK);
+		cs->writeisac(cs, IPACX_MASK, _MASK_IMASK); // global mask register
+
+		// reset HDLC Transmitters/receivers
+		cs->writeisac(cs, IPACX_CMDRD, 0x41); 
+		cs->BC_Write_Reg(cs, 0, IPACX_CMDRB, 0x41);
+		cs->BC_Write_Reg(cs, 1, IPACX_CMDRB, 0x41);
+		ph_command(cs, IPACX_CMD_RES);
+	}
+}
+
+
+void __devinit
+setup_ipacx(struct IsdnCardState *cs)
+{
+	INIT_WORK(&cs->tqueue, (void *)(void *) dch_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
+//----------------- end of file -----------------------
+
diff --git a/drivers/isdn/hisax/ipacx.h b/drivers/isdn/hisax/ipacx.h
new file mode 100644
index 0000000..e8a22e8
--- /dev/null
+++ b/drivers/isdn/hisax/ipacx.h
@@ -0,0 +1,162 @@
+/*
+ *
+ * IPACX specific defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#ifndef INCLUDE_IPACX_H
+#define INCLUDE_IPACX_H
+
+/* D-channel registers   */
+#define IPACX_RFIFOD        0x00    /* RD       */
+#define IPACX_XFIFOD        0x00    /* WR       */
+#define IPACX_ISTAD         0x20    /* RD       */
+#define IPACX_MASKD         0x20    /* WR       */
+#define IPACX_STARD         0x21    /* RD       */
+#define IPACX_CMDRD         0x21    /* WR       */
+#define IPACX_MODED         0x22    /* RD/WR    */
+#define IPACX_EXMD1         0x23    /* RD/WR    */
+#define IPACX_TIMR1         0x24    /* RD/WR    */
+#define IPACX_SAP1          0x25    /* WR       */
+#define IPACX_SAP2          0x26    /* WR       */
+#define IPACX_RBCLD         0x26    /* RD       */
+#define IPACX_RBCHD         0x27    /* RD       */
+#define IPACX_TEI1          0x27    /* WR       */
+#define IPACX_TEI2          0x28    /* WR       */
+#define IPACX_RSTAD         0x28    /* RD       */
+#define IPACX_TMD           0x29    /* RD/WR    */
+#define IPACX_CIR0          0x2E    /* RD       */
+#define IPACX_CIX0          0x2E    /* WR       */
+#define IPACX_CIR1          0x2F    /* RD       */
+#define IPACX_CIX1          0x2F    /* WR       */
+
+/* Transceiver registers    */
+#define IPACX_TR_CONF0      0x30    /* RD/WR    */
+#define IPACX_TR_CONF1      0x31    /* RD/WR    */
+#define IPACX_TR_CONF2      0x32    /* RD/WR    */
+#define IPACX_TR_STA        0x33    /* RD       */
+#define IPACX_TR_CMD        0x34    /* RD/WR    */
+#define IPACX_SQRR1         0x35    /* RD       */
+#define IPACX_SQXR1         0x35    /* WR       */
+#define IPACX_SQRR2         0x36    /* RD       */
+#define IPACX_SQXR2         0x36    /* WR       */
+#define IPACX_SQRR3         0x37    /* RD       */
+#define IPACX_SQXR3         0x37    /* WR       */
+#define IPACX_ISTATR        0x38    /* RD       */
+#define IPACX_MASKTR        0x39    /* RD/WR    */
+#define IPACX_TR_MODE       0x3A    /* RD/WR    */
+#define IPACX_ACFG1         0x3C    /* RD/WR    */
+#define IPACX_ACFG2         0x3D    /* RD/WR    */
+#define IPACX_AOE           0x3E    /* RD/WR    */
+#define IPACX_ARX           0x3F    /* RD       */
+#define IPACX_ATX           0x3F    /* WR       */
+
+/* IOM: Timeslot, DPS, CDA  */
+#define IPACX_CDA10         0x40    /* RD/WR    */
+#define IPACX_CDA11         0x41    /* RD/WR    */
+#define IPACX_CDA20         0x42    /* RD/WR    */
+#define IPACX_CDA21         0x43    /* RD/WR    */
+#define IPACX_CDA_TSDP10    0x44    /* RD/WR    */
+#define IPACX_CDA_TSDP11    0x45    /* RD/WR    */
+#define IPACX_CDA_TSDP20    0x46    /* RD/WR    */
+#define IPACX_CDA_TSDP21    0x47    /* RD/WR    */
+#define IPACX_BCHA_TSDP_BC1 0x48    /* RD/WR    */
+#define IPACX_BCHA_TSDP_BC2 0x49    /* RD/WR    */
+#define IPACX_BCHB_TSDP_BC1 0x4A    /* RD/WR    */
+#define IPACX_BCHB_TSDP_BC2 0x4B    /* RD/WR    */
+#define IPACX_TR_TSDP_BC1   0x4C    /* RD/WR    */
+#define IPACX_TR_TSDP_BC2   0x4D    /* RD/WR    */
+#define IPACX_CDA1_CR       0x4E    /* RD/WR    */
+#define IPACX_CDA2_CR       0x4F    /* RD/WR    */
+
+/* IOM: Contol, Sync transfer, Monitor    */
+#define IPACX_TR_CR         0x50    /* RD/WR    */
+#define IPACX_TRC_CR        0x50    /* RD/WR    */
+#define IPACX_BCHA_CR       0x51    /* RD/WR    */
+#define IPACX_BCHB_CR       0x52    /* RD/WR    */
+#define IPACX_DCI_CR        0x53    /* RD/WR    */
+#define IPACX_DCIC_CR       0x53    /* RD/WR    */
+#define IPACX_MON_CR        0x54    /* RD/WR    */
+#define IPACX_SDS1_CR       0x55    /* RD/WR    */
+#define IPACX_SDS2_CR       0x56    /* RD/WR    */
+#define IPACX_IOM_CR        0x57    /* RD/WR    */
+#define IPACX_STI           0x58    /* RD       */
+#define IPACX_ASTI          0x58    /* WR       */
+#define IPACX_MSTI          0x59    /* RD/WR    */
+#define IPACX_SDS_CONF      0x5A    /* RD/WR    */
+#define IPACX_MCDA          0x5B    /* RD       */
+#define IPACX_MOR           0x5C    /* RD       */
+#define IPACX_MOX           0x5C    /* WR       */
+#define IPACX_MOSR          0x5D    /* RD       */
+#define IPACX_MOCR          0x5E    /* RD/WR    */
+#define IPACX_MSTA          0x5F    /* RD       */
+#define IPACX_MCONF         0x5F    /* WR       */
+
+/* Interrupt and general registers */
+#define IPACX_ISTA          0x60    /* RD       */
+#define IPACX_MASK          0x60    /* WR       */
+#define IPACX_AUXI          0x61    /* RD       */
+#define IPACX_AUXM          0x61    /* WR       */
+#define IPACX_MODE1         0x62    /* RD/WR    */
+#define IPACX_MODE2         0x63    /* RD/WR    */
+#define IPACX_ID            0x64    /* RD       */
+#define IPACX_SRES          0x64    /* WR       */
+#define IPACX_TIMR2         0x65    /* RD/WR    */
+
+/* B-channel registers */
+#define IPACX_OFF_B1        0x70
+#define IPACX_OFF_B2        0x80
+
+#define IPACX_ISTAB         0x00    /* RD       */
+#define IPACX_MASKB         0x00    /* WR       */
+#define IPACX_STARB         0x01    /* RD       */
+#define IPACX_CMDRB         0x01    /* WR       */
+#define IPACX_MODEB         0x02    /* RD/WR    */
+#define IPACX_EXMB          0x03    /* RD/WR    */
+#define IPACX_RAH1          0x05    /* WR       */
+#define IPACX_RAH2          0x06    /* WR       */
+#define IPACX_RBCLB         0x06    /* RD       */
+#define IPACX_RBCHB         0x07    /* RD       */
+#define IPACX_RAL1          0x07    /* WR       */
+#define IPACX_RAL2          0x08    /* WR       */
+#define IPACX_RSTAB         0x08    /* RD       */
+#define IPACX_TMB           0x09    /* RD/WR    */
+#define IPACX_RFIFOB        0x0A    /*- RD      */
+#define IPACX_XFIFOB        0x0A    /*- WR      */
+
+/* Layer 1 Commands */
+#define IPACX_CMD_TIM    0x0
+#define IPACX_CMD_RES    0x1
+#define IPACX_CMD_SSP    0x2
+#define IPACX_CMD_SCP    0x3
+#define IPACX_CMD_AR8    0x8
+#define IPACX_CMD_AR10   0x9
+#define IPACX_CMD_ARL    0xa
+#define IPACX_CMD_DI     0xf
+
+/* Layer 1 Indications */
+#define IPACX_IND_DR     0x0
+#define IPACX_IND_RES    0x1
+#define IPACX_IND_TMA    0x2
+#define IPACX_IND_SLD    0x3
+#define IPACX_IND_RSY    0x4
+#define IPACX_IND_DR6    0x5
+#define IPACX_IND_PU     0x7
+#define IPACX_IND_AR     0x8
+#define IPACX_IND_ARL    0xa
+#define IPACX_IND_CVR    0xb
+#define IPACX_IND_AI8    0xc
+#define IPACX_IND_AI10   0xd
+#define IPACX_IND_AIL    0xe
+#define IPACX_IND_DC     0xf
+
+extern void init_ipacx(struct IsdnCardState *, int);
+extern void interrupt_ipacx(struct IsdnCardState *);
+extern void setup_isac(struct IsdnCardState *);
+
+#endif
diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c
new file mode 100644
index 0000000..20b9499
--- /dev/null
+++ b/drivers/isdn/hisax/isac.c
@@ -0,0 +1,684 @@
+/* $Id: isac.c,v 1.31.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * ISAC specific routines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include "hisax.h"
+#include "isac.h"
+#include "arcofi.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 1
+
+static char *ISACVer[] __devinitdata =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+void
+ISACVersion(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readisac(cs, ISAC_RBCH);
+	printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+isac_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.isac.ph_state) {
+		case (ISAC_IND_RS):
+		case (ISAC_IND_EI):
+			ph_command(cs, ISAC_CMD_DUI);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			break;
+		case (ISAC_IND_DID):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (ISAC_IND_DR):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (ISAC_IND_PU):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (ISAC_IND_RSY):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (ISAC_IND_ARD):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (ISAC_IND_AI8):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+			break;
+		case (ISAC_IND_AI10):
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+isac_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		isac_new_ph(cs);		
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+#if ARCOFI_USE
+	if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
+		return;
+	if (test_and_clear_bit(D_RX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+	if (test_and_clear_bit(D_TX_MON1, &cs->event))
+		arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+#endif
+}
+
+void
+isac_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "isac_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeisac(cs, ISAC_CMDR, 0x80);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	cs->readisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, 0x80);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "isac_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "isac_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > 32) {
+		more = !0;
+		count = 32;
+	}
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeisacfifo(cs, ptr, count);
+	cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa);
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "isac_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "isac_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+void
+isac_interrupt(struct IsdnCardState *cs, u_char val)
+{
+	u_char exval, v1;
+	struct sk_buff *skb;
+	unsigned int count;
+
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ISAC interrupt %x", val);
+	if (val & 0x80) {	/* RME */
+		exval = cs->readisac(cs, ISAC_RSTA);
+		if ((exval & 0x70) != 0x20) {
+			if (exval & 0x40) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC RDO");
+#ifdef ERROR_STATISTIC
+				cs->err_rx++;
+#endif
+			}
+			if (!(exval & 0x20)) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "ISAC CRC error");
+#ifdef ERROR_STATISTIC
+				cs->err_crc++;
+#endif
+			}
+			cs->writeisac(cs, ISAC_CMDR, 0x80);
+		} else {
+			count = cs->readisac(cs, ISAC_RBCL) & 0x1f;
+			if (count == 0)
+				count = 32;
+			isac_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+		}
+		cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		isac_empty_fifo(cs, 32);
+	}
+	if (val & 0x20) {	/* RSC */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC RSC interrupt");
+	}
+	if (val & 0x10) {	/* XPR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				isac_fill_fifo(cs);
+				goto afterXPR;
+			} else {
+				dev_kfree_skb_irq(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			isac_fill_fifo(cs);
+		} else
+			schedule_event(cs, D_XMTBUFREADY);
+	}
+      afterXPR:
+	if (val & 0x04) {	/* CISQ */
+		exval = cs->readisac(cs, ISAC_CIR0);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC CIR0 %02X", exval );
+		if (exval & 2) {
+			cs->dc.isac.ph_state = (exval >> 2) & 0xf;
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ph_state change %x", cs->dc.isac.ph_state);
+			schedule_event(cs, D_L1STATECHANGE);
+		}
+		if (exval & 1) {
+			exval = cs->readisac(cs, ISAC_CIR1);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "ISAC CIR1 %02X", exval );
+		}
+	}
+	if (val & 0x02) {	/* SIN */
+		/* never */
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC SIN interrupt");
+	}
+	if (val & 0x01) {	/* EXI */
+		exval = cs->readisac(cs, ISAC_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "ISAC EXIR %02x", exval);
+		if (exval & 0x80) {  /* XMR */
+			debugl1(cs, "ISAC XMR");
+			printk(KERN_WARNING "HiSax: ISAC XMR\n");
+		}
+		if (exval & 0x40) {  /* XDU */
+			debugl1(cs, "ISAC XDU");
+			printk(KERN_WARNING "HiSax: ISAC XDU\n");
+#ifdef ERROR_STATISTIC
+			cs->err_tx++;
+#endif
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) { /* Restart frame */
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+				isac_fill_fifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
+				debugl1(cs, "ISAC XDU no skb");
+			}
+		}
+		if (exval & 0x04) {  /* MOS */
+			v1 = cs->readisac(cs, ISAC_MOSR);
+			if (cs->debug & L1_DEB_MONITOR)
+				debugl1(cs, "ISAC MOSR %02x", v1);
+#if ARCOFI_USE
+			if (v1 & 0x08) {
+				if (!cs->dc.isac.mon_rx) {
+					if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->dc.isac.mocr &= 0xf0;
+						cs->dc.isac.mocr |= 0x0a;
+						cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+						goto afterMONR0;
+					} else
+						cs->dc.isac.mon_rxp = 0;
+				}
+				if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.isac.mocr &= 0xf0;
+					cs->dc.isac.mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR0;
+				}
+				cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR0 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]);
+				if (cs->dc.isac.mon_rxp == 1) {
+					cs->dc.isac.mocr |= 0x04;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				}
+			}
+		      afterMONR0:
+			if (v1 & 0x80) {
+				if (!cs->dc.isac.mon_rx) {
+					if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+						if (cs->debug & L1_DEB_WARN)
+							debugl1(cs, "ISAC MON RX out of memory!");
+						cs->dc.isac.mocr &= 0x0f;
+						cs->dc.isac.mocr |= 0xa0;
+						cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+						goto afterMONR1;
+					} else
+						cs->dc.isac.mon_rxp = 0;
+				}
+				if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
+					cs->dc.isac.mocr &= 0x0f;
+					cs->dc.isac.mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mon_rxp = 0;
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "ISAC MON RX overflow!");
+					goto afterMONR1;
+				}
+				cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC MOR1 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]);
+				cs->dc.isac.mocr |= 0x40;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+			}
+		      afterMONR1:
+			if (v1 & 0x04) {
+				cs->dc.isac.mocr &= 0xf0;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				cs->dc.isac.mocr |= 0x0a;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				schedule_event(cs, D_RX_MON0);
+			}
+			if (v1 & 0x40) {
+				cs->dc.isac.mocr &= 0x0f;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				cs->dc.isac.mocr |= 0xa0;
+				cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+				schedule_event(cs, D_RX_MON1);
+			}
+			if (v1 & 0x02) {
+				if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && 
+					(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) && 
+					!(v1 & 0x08))) {
+					cs->dc.isac.mocr &= 0xf0;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mocr |= 0x0a;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					if (cs->dc.isac.mon_txc &&
+						(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
+						schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
+					schedule_event(cs, D_TX_MON0);
+					goto AfterMOX0;
+				}
+				cs->writeisac(cs, ISAC_MOX0,
+					cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX0", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]);
+			}
+		      AfterMOX0:
+			if (v1 & 0x20) {
+				if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc && 
+					(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) && 
+					!(v1 & 0x80))) {
+					cs->dc.isac.mocr &= 0x0f;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					cs->dc.isac.mocr |= 0xa0;
+					cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+					if (cs->dc.isac.mon_txc &&
+						(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
+						schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
+					schedule_event(cs, D_TX_MON1);
+					goto AfterMOX1;
+				}
+				cs->writeisac(cs, ISAC_MOX1,
+					cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+				if (cs->debug & L1_DEB_MONITOR)
+					debugl1(cs, "ISAC %02x -> MOX1", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]);
+			}
+		      AfterMOX1:;
+#endif
+		}
+	}
+}
+
+static void
+ISAC_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	int  val;
+
+	switch (pr) {
+		case (PH_DATA |REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				isac_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL |INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+			} else {
+				if (cs->debug & DEB_DLOG_HEX)
+					LogFrame(cs, skb->data, skb->len);
+				if (cs->debug & DEB_DLOG_VERBOSE)
+					dlogframe(cs, skb, 0);
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+				isac_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.isac.ph_state == ISAC_IND_EI) ||
+				(cs->dc.isac.ph_state == ISAC_IND_DR) ||
+				(cs->dc.isac.ph_state == ISAC_IND_RS))
+			        ph_command(cs, ISAC_CMD_TIM);
+			else
+				ph_command(cs, ISAC_CMD_RS);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ISAC_CMD_TIM);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, ISAC_CMD_AR8);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+				/* IOM 1 Mode */
+				if (!val) {
+					cs->writeisac(cs, ISAC_SPCR, 0xa);
+					cs->writeisac(cs, ISAC_ADF1, 0x2);
+				} else {
+					cs->writeisac(cs, ISAC_SPCR, val);
+					cs->writeisac(cs, ISAC_ADF1, 0xa);
+				}
+			} else {
+				/* IOM 2 Mode */
+				cs->writeisac(cs, ISAC_SPCR, val);
+				if (val)
+					cs->writeisac(cs, ISAC_ADF1, 0x8);
+				else
+					cs->writeisac(cs, ISAC_ADF1, 0x0);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isac_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+void
+setstack_isac(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = ISAC_l1hw;
+}
+
+void 
+DC_Close_isac(struct IsdnCardState *cs) {
+	if (cs->dc.isac.mon_rx) {
+		kfree(cs->dc.isac.mon_rx);
+		cs->dc.isac.mon_rx = NULL;
+	}
+	if (cs->dc.isac.mon_tx) {
+		kfree(cs->dc.isac.mon_tx);
+		cs->dc.isac.mon_tx = NULL;
+	}
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int	rbch, star;
+
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbch = cs->readisac(cs, ISAC_RBCH);
+		star = cs->readisac(cs, ISAC_STAR);
+		if (cs->debug) 
+			debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
+				rbch, star);
+		if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeisac(cs, ISAC_CMDR, 0x01); /* Transmitter reset */
+			cs->irq_func(cs->irq, cs, NULL);
+		}
+	}
+}
+
+void __devinit
+initisac(struct IsdnCardState *cs)
+{
+	cs->setstack_d = setstack_isac;
+	cs->DC_Close = DC_Close_isac;
+	cs->dc.isac.mon_tx = NULL;
+	cs->dc.isac.mon_rx = NULL;
+  	cs->writeisac(cs, ISAC_MASK, 0xff);
+  	cs->dc.isac.mocr = 0xaa;
+	if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+		/* IOM 1 Mode */
+		cs->writeisac(cs, ISAC_ADF2, 0x0);
+		cs->writeisac(cs, ISAC_SPCR, 0xa);
+		cs->writeisac(cs, ISAC_ADF1, 0x2);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+	} else {
+		/* IOM 2 Mode */
+		if (!cs->dc.isac.adf2)
+			cs->dc.isac.adf2 = 0x80;
+		cs->writeisac(cs, ISAC_ADF2, cs->dc.isac.adf2);
+		cs->writeisac(cs, ISAC_SQXR, 0x2f);
+		cs->writeisac(cs, ISAC_SPCR, 0x00);
+		cs->writeisac(cs, ISAC_STCR, 0x70);
+		cs->writeisac(cs, ISAC_MODE, 0xc9);
+		cs->writeisac(cs, ISAC_TIMR, 0x00);
+		cs->writeisac(cs, ISAC_ADF1, 0x00);
+	}
+	ph_command(cs, ISAC_CMD_RS);
+	cs->writeisac(cs, ISAC_MASK, 0x0);
+}
+
+void __devinit
+clear_pending_isac_ints(struct IsdnCardState *cs)
+{
+	int val, eval;
+
+	val = cs->readisac(cs, ISAC_STAR);
+	debugl1(cs, "ISAC STAR %x", val);
+	val = cs->readisac(cs, ISAC_MODE);
+	debugl1(cs, "ISAC MODE %x", val);
+	val = cs->readisac(cs, ISAC_ADF2);
+	debugl1(cs, "ISAC ADF2 %x", val);
+	val = cs->readisac(cs, ISAC_ISTA);
+	debugl1(cs, "ISAC ISTA %x", val);
+	if (val & 0x01) {
+		eval = cs->readisac(cs, ISAC_EXIR);
+		debugl1(cs, "ISAC EXIR %x", eval);
+	}
+	val = cs->readisac(cs, ISAC_CIR0);
+	debugl1(cs, "ISAC CIR0 %x", val);
+	cs->dc.isac.ph_state = (val >> 2) & 0xf;
+	schedule_event(cs, D_L1STATECHANGE);
+	/* Disable all IRQ */
+	cs->writeisac(cs, ISAC_MASK, 0xFF);
+}
+
+void __devinit
+setup_isac(struct IsdnCardState *cs)
+{
+	INIT_WORK(&cs->tqueue, (void *)(void *) isac_bh, cs);
+	cs->dbusytimer.function = (void *) dbusy_timer_handler;
+	cs->dbusytimer.data = (long) cs;
+	init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h
new file mode 100644
index 0000000..8f8331e4
--- /dev/null
+++ b/drivers/isdn/hisax/isac.h
@@ -0,0 +1,70 @@
+/* $Id: isac.h,v 1.9.2.2 2004/01/12 22:52:27 keil Exp $
+ *
+ * ISAC specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#define ISAC_MASK 0x20
+#define ISAC_ISTA 0x20
+#define ISAC_STAR 0x21
+#define ISAC_CMDR 0x21
+#define ISAC_EXIR 0x24
+#define ISAC_ADF2 0x39
+#define ISAC_SPCR 0x30
+#define ISAC_ADF1 0x38
+#define ISAC_CIR0 0x31
+#define ISAC_CIX0 0x31
+#define ISAC_CIR1 0x33
+#define ISAC_CIX1 0x33
+#define ISAC_STCR 0x37
+#define ISAC_MODE 0x22
+#define ISAC_RSTA 0x27
+#define ISAC_RBCL 0x25
+#define ISAC_RBCH 0x2A
+#define ISAC_TIMR 0x23
+#define ISAC_SQXR 0x3b
+#define ISAC_MOSR 0x3a
+#define ISAC_MOCR 0x3a
+#define ISAC_MOR0 0x32
+#define ISAC_MOX0 0x32
+#define ISAC_MOR1 0x34
+#define ISAC_MOX1 0x34
+
+#define ISAC_RBCH_XAC 0x80
+
+#define ISAC_CMD_TIM	0x0
+#define ISAC_CMD_RS	0x1
+#define ISAC_CMD_SCZ	0x4
+#define ISAC_CMD_SSZ	0x2
+#define ISAC_CMD_AR8	0x8
+#define ISAC_CMD_AR10	0x9
+#define ISAC_CMD_ARL	0xA
+#define ISAC_CMD_DUI	0xF
+
+#define ISAC_IND_RS	0x1
+#define ISAC_IND_PU	0x7
+#define ISAC_IND_DR	0x0
+#define ISAC_IND_SD	0x2
+#define ISAC_IND_DIS	0x3
+#define ISAC_IND_EI	0x6
+#define ISAC_IND_RSY	0x4
+#define ISAC_IND_ARD	0x8
+#define ISAC_IND_TI	0xA
+#define ISAC_IND_ATI	0xB
+#define ISAC_IND_AI8	0xC
+#define ISAC_IND_AI10	0xD
+#define ISAC_IND_DID	0xF
+
+extern void ISACVersion(struct IsdnCardState *, char *);
+extern void setup_isac(struct IsdnCardState *);
+extern void initisac(struct IsdnCardState *);
+extern void isac_interrupt(struct IsdnCardState *, u_char);
+extern void clear_pending_isac_ints(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c
new file mode 100644
index 0000000..ee081321
--- /dev/null
+++ b/drivers/isdn/hisax/isar.c
@@ -0,0 +1,1909 @@
+/* $Id: isar.c,v 1.22.2.6 2004/02/11 13:21:34 keil Exp $
+ *
+ * isar.c   ISAR (Siemens PSB 7110) specific routines
+ *
+ * Author       Karsten Keil (keil@isdn4linux.de)
+ *
+ * This file is (c) under GNU General Public License
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBG_LOADFIRM	0
+#define DUMP_MBOXFRAME	2
+
+#define DLE	0x10
+#define ETX	0x03
+
+#define FAXMODCNT	13
+const	u_char	faxmodulation[] = {3,24,48,72,73,74,96,97,98,121,122,145,146};
+static	u_int	modmask = 0x1fff;
+static	int	frm_extra_delay = 2;
+static	int	para_TOA = 6;
+const   u_char  *FC1_CMD[] = {"FAE", "FTS", "FRS", "FTM", "FRM", "FTH", "FRH", "CTRL" };
+
+void isar_setup(struct IsdnCardState *cs);
+static void isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para);
+static void ll_deliver_faxstat(struct BCState *bcs, u_char status);
+
+static inline int
+waitforHIA(struct IsdnCardState *cs, int timeout)
+{
+
+	while ((cs->BC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) {
+		udelay(1);
+		timeout--;
+	}
+	if (!timeout)
+		printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n");
+	return(timeout);
+}
+
+
+int
+sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len,
+	u_char *msg)
+{
+	int i;
+	
+	if (!waitforHIA(cs, 4000))
+		return(0);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len);
+#endif
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg);
+	cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len);
+	cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0);
+	if (msg && len) {
+		cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]);
+		for (i=1; i<len; i++)
+			cs->BC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = len;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "sendmbox cnt %d", len);
+				QuickHex(t, &msg[len-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_HIS, his);
+	waitforHIA(cs, 10000);
+	return(1);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg)
+{
+	int i;
+
+	cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0);
+	if (msg && ireg->clsb) {
+		msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX);
+		for (i=1; i < ireg->clsb; i++)
+			 msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX);
+#if DUMP_MBOXFRAME>1
+		if (cs->debug & L1_DEB_HSCX_FIFO) {
+			char tmp[256], *t;
+			
+			i = ireg->clsb;
+			while (i>0) {
+				t = tmp;
+				t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb);
+				QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i);
+				debugl1(cs, tmp);
+				i -= 64;
+			}
+		}
+#endif
+	}
+	cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg)
+{
+	ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS);
+	ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H);
+	ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L);
+#if DUMP_MBOXFRAME
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "irq_stat(%02x,%02x,%d)", ireg->iis, ireg->cmsb,
+			ireg->clsb);
+#endif
+}
+
+int
+waitrecmsg(struct IsdnCardState *cs, u_char *len,
+	u_char *msg, int maxdelay)
+{
+	int timeout = 0;
+	struct isar_reg *ir = cs->bcs[0].hw.isar.reg;
+	
+	
+	while((!(cs->BC_Read_Reg(cs, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) &&
+		(timeout++ < maxdelay))
+		udelay(1);
+	if (timeout >= maxdelay) {
+		printk(KERN_WARNING"isar recmsg IRQSTA timeout\n");
+		return(0);
+	}
+	get_irq_infos(cs, ir);
+	rcv_mbox(cs, ir, msg);
+	*len = ir->clsb;
+	return(1);
+}
+
+int
+ISARVersion(struct IsdnCardState *cs, char *s)
+{
+	int ver;
+	u_char msg[] = ISAR_MSG_HWVER;
+	u_char tmp[64];
+	u_char len;
+	u_long flags;
+	int debug;
+
+	cs->cardmsg(cs, CARD_RESET,  NULL);
+	spin_lock_irqsave(&cs->lock, flags);
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	debug = cs->debug;
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+	if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return(-1);
+	}
+	if (!waitrecmsg(cs, &len, tmp, 100000)) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return(-2);
+	}
+	cs->debug = debug;
+	if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) {
+		if (len == 1) {
+			ver = tmp[0] & 0xf;
+			printk(KERN_INFO "%s ISAR version %d\n", s, ver);
+		} else
+			ver = -3;
+	} else
+		ver = -4;
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return(ver);
+}
+
+int
+isar_load_firmware(struct IsdnCardState *cs, u_char __user *buf)
+{
+	int ret, size, cnt, debug;
+	u_char len, nom, noc;
+	u_short sadr, left, *sp;
+	u_char __user *p = buf;
+	u_char *msg, *tmpmsg, *mp, tmp[64];
+	u_long flags;
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	
+	struct {u_short sadr;
+		u_short len;
+		u_short d_key;
+	} blk_head;
+		
+#define	BLK_HEAD_SIZE 6
+	if (1 != (ret = ISARVersion(cs, "Testing"))) {
+		printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret);
+		return(1);
+	}
+	debug = cs->debug;
+#if DBG_LOADFIRM<2
+	cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+#endif
+	
+	if ((ret = copy_from_user(&size, p, sizeof(int)))) {
+		printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+		return ret;
+	}
+	p += sizeof(int);
+	printk(KERN_DEBUG"isar_load_firmware size: %d\n", size);
+	cnt = 0;
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	if (!(msg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no buffer\n");
+		return (1);
+	}
+	if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) {
+		printk(KERN_ERR"isar_load_firmware no tmp buffer\n");
+		kfree(msg);
+		return (1);
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	/* disable ISAR IRQ */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	while (cnt < size) {
+		if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) {
+			printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+			goto reterror;
+		}
+#ifdef __BIG_ENDIAN
+		sadr = (blk_head.sadr & 0xff)*256 + blk_head.sadr/256;
+		blk_head.sadr = sadr;
+		sadr = (blk_head.len & 0xff)*256 + blk_head.len/256;
+		blk_head.len = sadr;
+		sadr = (blk_head.d_key & 0xff)*256 + blk_head.d_key/256;
+		blk_head.d_key = sadr;
+#endif /* __BIG_ENDIAN */
+		cnt += BLK_HEAD_SIZE;
+		p += BLK_HEAD_SIZE;
+		printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n",
+			blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+		sadr = blk_head.sadr;
+		left = blk_head.len;
+		spin_lock_irqsave(&cs->lock, flags);
+		if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) {
+			printk(KERN_ERR"isar sendmsg dkey failed\n");
+			ret = 1;goto reterr_unlock;
+		}
+		if (!waitrecmsg(cs, &len, tmp, 100000)) {
+			printk(KERN_ERR"isar waitrecmsg dkey failed\n");
+			ret = 1;goto reterr_unlock;
+		}
+		if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) {
+			printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n",
+				ireg->iis, ireg->cmsb, len);
+			ret = 1;goto reterr_unlock;
+		}
+		spin_unlock_irqrestore(&cs->lock, flags);
+		while (left>0) {
+			if (left > 126)
+				noc = 126;
+			else
+				noc = left;
+			nom = 2*noc;
+			mp  = msg;
+			*mp++ = sadr / 256;
+			*mp++ = sadr % 256;
+			left -= noc;
+			*mp++ = noc;
+			if ((ret = copy_from_user(tmpmsg, p, nom))) {
+				printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+				goto reterror;
+			}
+			p += nom;
+			cnt += nom;
+			nom += 3;
+			sp = (u_short *)tmpmsg;
+#if DBG_LOADFIRM
+			printk(KERN_DEBUG"isar: load %3d words at %04x left %d\n",
+				 noc, sadr, left);
+#endif
+			sadr += noc;
+			while(noc) {
+#ifdef __BIG_ENDIAN
+				*mp++ = *sp % 256;
+				*mp++ = *sp / 256;
+#else
+				*mp++ = *sp / 256;
+				*mp++ = *sp % 256;
+#endif /* __BIG_ENDIAN */
+				sp++;
+				noc--;
+			}
+			spin_lock_irqsave(&cs->lock, flags);
+			if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) {
+				printk(KERN_ERR"isar sendmsg prog failed\n");
+				ret = 1;goto reterr_unlock;
+			}
+			if (!waitrecmsg(cs, &len, tmp, 100000)) {
+				printk(KERN_ERR"isar waitrecmsg prog failed\n");
+				ret = 1;goto reterr_unlock;
+			}
+			if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) {
+				printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n",
+					ireg->iis, ireg->cmsb, len);
+				ret = 1;goto reterr_unlock;
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+		}
+		printk(KERN_DEBUG"isar firmware block %5d words loaded\n",
+			blk_head.len);
+	}
+	/* 10ms delay */
+	cnt = 10;
+	while (cnt--)
+		udelay(1000);
+	msg[0] = 0xff;
+	msg[1] = 0xfe;
+	ireg->bstat = 0;
+	spin_lock_irqsave(&cs->lock, flags);
+	if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) {
+		printk(KERN_ERR"isar sendmsg start dsp failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	if (!waitrecmsg(cs, &len, tmp, 100000)) {
+		printk(KERN_ERR"isar waitrecmsg start dsp failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) {
+		printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n",
+			ireg->iis, ireg->cmsb, len);
+		ret = 1;goto reterr_unlock;
+	} else
+		printk(KERN_DEBUG"isar start dsp success\n");
+	/* NORMAL mode entered */
+	/* Enable IRQs of ISAR */
+	cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	cnt = 1000; /* max 1s */
+	while ((!ireg->bstat) && cnt) {
+		udelay(1000);
+		cnt--;
+	}
+	if (!cnt) {
+		printk(KERN_ERR"isar no general status event received\n");
+		ret = 1;goto reterror;
+	} else {
+		printk(KERN_DEBUG"isar general status event %x\n",
+			ireg->bstat);
+	}
+	/* 10ms delay */
+	cnt = 10;
+	while (cnt--)
+		udelay(1000);
+	spin_lock_irqsave(&cs->lock, flags);
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+		printk(KERN_ERR"isar sendmsg self tst failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	cnt = 10000; /* max 100 ms */
+	spin_unlock_irqrestore(&cs->lock, flags);
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	udelay(1000);
+	if (!cnt) {
+		printk(KERN_ERR"isar no self tst response\n");
+		ret = 1;goto reterror;
+	}
+	if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1)
+		&& (ireg->par[0] == 0)) {
+		printk(KERN_DEBUG"isar selftest OK\n");
+	} else {
+		printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n",
+			ireg->cmsb, ireg->clsb, ireg->par[0]);
+		ret = 1;goto reterror;
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	ireg->iis = 0;
+	if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+		printk(KERN_ERR"isar RQST SVN failed\n");
+		ret = 1;goto reterr_unlock;
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	cnt = 30000; /* max 300 ms */
+	while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+		udelay(10);
+		cnt--;
+	}
+	udelay(1000);
+	if (!cnt) {
+		printk(KERN_ERR"isar no SVN response\n");
+		ret = 1;goto reterror;
+	} else {
+		if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1))
+			printk(KERN_DEBUG"isar software version %#x\n",
+				ireg->par[0]);
+		else {
+			printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n",
+				ireg->cmsb, ireg->clsb, cnt);
+			ret = 1;goto reterror;
+		}
+	}
+	spin_lock_irqsave(&cs->lock, flags);
+	cs->debug = debug;
+	isar_setup(cs);
+
+	ret = 0;
+reterr_unlock:
+	spin_unlock_irqrestore(&cs->lock, flags);
+reterror:
+	cs->debug = debug;
+	if (ret)
+		/* disable ISAR IRQ */
+		cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+	kfree(msg);
+	kfree(tmpmsg);
+	return(ret);
+}
+
+extern void BChannel_bh(struct BCState *);
+#define B_LL_NOCARRIER	8
+#define B_LL_CONNECT	9
+#define B_LL_OK		10
+
+static void
+isar_bh(struct BCState *bcs)
+{
+	BChannel_bh(bcs);
+	if (test_and_clear_bit(B_LL_NOCARRIER, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_NOCARR);
+	if (test_and_clear_bit(B_LL_CONNECT, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+	if (test_and_clear_bit(B_LL_OK, &bcs->event))
+		ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_OK);
+}
+
+static void
+send_DLE_ETX(struct BCState *bcs)
+{
+	u_char dleetx[2] = {DLE,ETX};
+	struct sk_buff *skb;
+	
+	if ((skb = dev_alloc_skb(2))) {
+		memcpy(skb_put(skb, 2), dleetx, 2);
+		skb_queue_tail(&bcs->rqueue, skb);
+		schedule_event(bcs, B_RCVBUFREADY);
+	} else {
+		printk(KERN_WARNING "HiSax: skb out of memory\n");
+	}
+}
+
+static inline int
+dle_count(unsigned char *buf, int len)
+{
+	int count = 0;
+
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
+}
+
+static inline void
+insert_dle(unsigned char *dest, unsigned char *src, int count) {
+	/* <DLE> in input stream have to be flagged as <DLE><DLE> */
+	while (count--) {
+		*dest++ = *src;
+		if (*src++ == DLE)
+			*dest++ = DLE;
+	}
+}
+ 
+static void
+isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	u_char *ptr;
+	struct sk_buff *skb;
+	struct isar_reg *ireg = bcs->hw.isar.reg;
+	
+	if (!ireg->clsb) {
+		debugl1(cs, "isar zero len frame");
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		return;
+	}
+	switch (bcs->mode) {
+	case L1_MODE_NULL:
+		debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n",
+			ireg->iis, ireg->cmsb, ireg->clsb);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	case L1_MODE_TRANS:
+	case L1_MODE_V32:
+		if ((skb = dev_alloc_skb(ireg->clsb))) {
+			rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb));
+			skb_queue_tail(&bcs->rqueue, skb);
+			schedule_event(bcs, B_RCVBUFREADY);
+		} else {
+			printk(KERN_WARNING "HiSax: skb out of memory\n");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		}
+		break;
+	case L1_MODE_HDLC:
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+#ifdef ERROR_STATISTIC
+			if (ireg->cmsb & HDLC_ERR_RER)
+				bcs->err_inv++;
+			if (ireg->cmsb & HDLC_ERR_CER)
+				bcs->err_crc++;
+#endif
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD)
+				bcs->hw.isar.rcvidx = 0;
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar frame to short %d",
+							bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2))) {
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				} else {
+					memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2),
+						bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2);
+					skb_queue_tail(&bcs->rqueue, skb);
+					schedule_event(bcs, B_RCVBUFREADY);
+				}
+				bcs->hw.isar.rcvidx = 0;
+			}
+		}
+		break;
+	case L1_MODE_FAX:
+		if (bcs->hw.isar.state != STFAX_ACTIV) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: not ACTIV");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+			rcv_mbox(cs, ireg, bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvidx = ireg->clsb +
+				dle_count(bcs->hw.isar.rcvbuf, ireg->clsb);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_rcv_frame: raw(%d) dle(%d)",
+					ireg->clsb, bcs->hw.isar.rcvidx);
+			if ((skb = dev_alloc_skb(bcs->hw.isar.rcvidx))) {
+				insert_dle((u_char *)skb_put(skb, bcs->hw.isar.rcvidx),
+					bcs->hw.isar.rcvbuf, ireg->clsb);
+				skb_queue_tail(&bcs->rqueue, skb);
+				schedule_event(bcs, B_RCVBUFREADY);
+				if (ireg->cmsb & SART_NMD) { /* ABORT */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar_rcv_frame: no more data");
+					bcs->hw.isar.rcvidx = 0;
+					send_DLE_ETX(bcs);
+					sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+						0, NULL);
+					bcs->hw.isar.state = STFAX_ESCAPE;
+					schedule_event(bcs, B_LL_NOCARRIER);
+				}
+			} else {
+				printk(KERN_WARNING "HiSax: skb out of memory\n");
+			}
+			break;
+		}
+		if (bcs->hw.isar.cmd != PCTRL_CMD_FRH) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: unknown fax mode %x",
+					bcs->hw.isar.cmd);
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+			break;
+		}
+		/* PCTRL_CMD_FRH */
+		if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: incoming packet too large");
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			bcs->hw.isar.rcvidx = 0;
+		} else if (ireg->cmsb & HDLC_ERROR) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar frame error %x len %d",
+					ireg->cmsb, ireg->clsb);
+			bcs->hw.isar.rcvidx = 0;
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		} else {
+			if (ireg->cmsb & HDLC_FSD) {
+				bcs->hw.isar.rcvidx = 0;
+			}
+			ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+			bcs->hw.isar.rcvidx += ireg->clsb;
+			rcv_mbox(cs, ireg, ptr);
+			if (ireg->cmsb & HDLC_FED) {
+				int len = bcs->hw.isar.rcvidx +
+					dle_count(bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx);
+				if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar frame to short %d",
+							bcs->hw.isar.rcvidx);
+					printk(KERN_WARNING "ISAR: frame to short %d\n",
+						bcs->hw.isar.rcvidx);
+				} else if (!(skb = dev_alloc_skb(len))) {
+					printk(KERN_WARNING "ISAR: receive out of memory\n");
+				} else {
+					insert_dle((u_char *)skb_put(skb, len),
+						bcs->hw.isar.rcvbuf,
+						bcs->hw.isar.rcvidx);
+					skb_queue_tail(&bcs->rqueue, skb);
+					schedule_event(bcs, B_RCVBUFREADY);
+					send_DLE_ETX(bcs);
+					schedule_event(bcs, B_LL_OK);
+					test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+				}
+				bcs->hw.isar.rcvidx = 0;
+			}
+		}
+		if (ireg->cmsb & SART_NMD) { /* ABORT */
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "isar_rcv_frame: no more data");
+			bcs->hw.isar.rcvidx = 0;
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) |
+				ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			bcs->hw.isar.state = STFAX_ESCAPE;
+			if (test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag)) {
+				send_DLE_ETX(bcs);
+				schedule_event(bcs, B_LL_NOCARRIER);
+			}
+		}
+		break;
+	default:
+		printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
+		cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+		break;
+	}
+}
+
+void
+isar_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int count;
+	u_char msb;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "isar_fill_fifo");
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+	if (!(bcs->hw.isar.reg->bstat & 
+		(bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+		return;
+	if (bcs->tx_skb->len > bcs->hw.isar.mml) {
+		msb = 0;
+		count = bcs->hw.isar.mml;
+	} else {
+		count = bcs->tx_skb->len;
+		msb = HDLC_FED;
+	}
+	ptr = bcs->tx_skb->data;
+	if (!bcs->hw.isar.txcnt) {
+		msb |= HDLC_FST;
+		if ((bcs->mode == L1_MODE_FAX) &&
+			(bcs->hw.isar.cmd == PCTRL_CMD_FTH)) {
+			if (bcs->tx_skb->len > 1) {
+				if ((ptr[0]== 0xff) && (ptr[1] == 0x13))
+					/* last frame */
+					test_and_set_bit(BC_FLG_LASTDATA,
+						&bcs->Flag);
+			}  
+		}
+	}
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.isar.txcnt += count;
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_V32:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				0, count, ptr);
+			break;
+		case L1_MODE_HDLC:
+			sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+				msb, count, ptr);
+			break;
+		case L1_MODE_FAX:
+			if (bcs->hw.isar.state != STFAX_ACTIV) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not ACTIV");
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) { 
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					msb, count, ptr);
+			} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+				sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+					0, count, ptr);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "isar_fill_fifo: not FTH/FTM");
+			}
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "isar_fill_fifo mode(%x) error", bcs->mode);
+			printk(KERN_ERR"isar_fill_fifo mode(%x) error\n", bcs->mode);
+			break;
+	}
+}
+
+inline
+struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath)
+{
+	if ((!dpath) || (dpath == 3))
+		return(NULL);
+	if (cs->bcs[0].hw.isar.dpath == dpath)
+		return(&cs->bcs[0]);
+	if (cs->bcs[1].hw.isar.dpath == dpath)
+		return(&cs->bcs[1]);
+	return(NULL);
+}
+
+void
+send_frames(struct BCState *bcs)
+{
+	if (bcs->tx_skb) {
+		if (bcs->tx_skb->len) {
+			isar_fill_fifo(bcs);
+			return;
+		} else {
+			if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+				(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+				u_long	flags;
+				spin_lock_irqsave(&bcs->aclock, flags);
+				bcs->ackcnt += bcs->hw.isar.txcnt;
+				spin_unlock_irqrestore(&bcs->aclock, flags);
+				schedule_event(bcs, B_ACKPENDING);
+			}
+			if (bcs->mode == L1_MODE_FAX) {
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					if (test_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FTM) {
+					if (test_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+						test_and_set_bit(BC_FLG_LASTDATA, &bcs->Flag);
+						test_and_set_bit(BC_FLG_NMD_DATA, &bcs->Flag);
+					}
+				}
+			}
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->hw.isar.txcnt = 0; 
+			bcs->tx_skb = NULL;
+		}
+	}
+	if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+		bcs->hw.isar.txcnt = 0;
+		test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+		isar_fill_fifo(bcs);
+	} else {
+		if (test_and_clear_bit(BC_FLG_DLEETX, &bcs->Flag)) {
+			if (test_and_clear_bit(BC_FLG_LASTDATA, &bcs->Flag)) {
+				if (test_and_clear_bit(BC_FLG_NMD_DATA, &bcs->Flag)) {
+					u_char dummy = 0;
+					sendmsg(bcs->cs, SET_DPS(bcs->hw.isar.dpath) |
+						ISAR_HIS_SDATA, 0x01, 1, &dummy);
+				}
+				test_and_set_bit(BC_FLG_LL_OK, &bcs->Flag);
+			} else {
+				schedule_event(bcs, B_LL_CONNECT);
+			}
+		}
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		schedule_event(bcs, B_XMTBUFREADY);
+	}
+}
+
+inline void
+check_send(struct IsdnCardState *cs, u_char rdm)
+{
+	struct BCState *bcs;
+	
+	if (rdm & BSTAT_RDM1) {
+		if ((bcs = sel_bcs_isar(cs, 1))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	if (rdm & BSTAT_RDM2) {
+		if ((bcs = sel_bcs_isar(cs, 2))) {
+			if (bcs->mode) {
+				send_frames(bcs);
+			}
+		}
+	}
+	
+}
+
+const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
+			"300", "600", "1200", "2400", "4800", "7200",
+			"9600nt", "9600t", "12000", "14400", "WRONG"};
+const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
+			"Bell103", "V23", "Bell202", "V17", "V29", "V27ter"};
+
+static void
+isar_pump_status_rsp(struct BCState *bcs, struct isar_reg *ireg) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char ril = ireg->par[0];
+	u_char rim;
+
+	if (!test_and_clear_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags))
+		return; 
+	if (ril > 14) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "wrong pstrsp ril=%d",ril);
+		ril = 15;
+	}
+	switch(ireg->par[1]) {
+		case 0:
+			rim = 0;
+			break;
+		case 0x20:
+			rim = 2;
+			break;
+		case 0x40:
+			rim = 3;
+			break;
+		case 0x41:
+			rim = 4;
+			break;
+		case 0x51:
+			rim = 5;
+			break;
+		case 0x61:
+			rim = 6;
+			break;
+		case 0x71:
+			rim = 7;
+			break;
+		case 0x82:
+			rim = 8;
+			break;
+		case 0x92:
+			rim = 9;
+			break;
+		case 0xa2:
+			rim = 10;
+			break;
+		default:
+			rim = 1;
+			break;
+	}
+	sprintf(bcs->hw.isar.conmsg,"%s %s", dmril[ril], dmrim[rim]);
+	bcs->conmsg = bcs->hw.isar.conmsg;
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "pump strsp %s", bcs->conmsg);
+}
+
+static void
+isar_pump_statev_modem(struct BCState *bcs, u_char devt) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+
+	switch(devt) {
+		case PSEV_10MS_TIMER:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev TIMER");
+			break;
+		case PSEV_CON_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CONNECT");
+			l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case PSEV_CON_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev NO CONNECT");
+			sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+			l1_msg_b(bcs->st, PH_DEACTIVATE | REQUEST, NULL);
+			break;
+		case PSEV_V24_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev V24 OFF");
+			break;
+		case PSEV_CTS_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CTS ON");
+			break;
+		case PSEV_CTS_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CTS OFF");
+			break;
+		case PSEV_DCD_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CARRIER ON");
+			test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags); 
+			sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+			break;
+		case PSEV_DCD_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev CARRIER OFF");
+			break;
+		case PSEV_DSR_ON:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev DSR ON");
+			break;
+		case PSEV_DSR_OFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev DSR_OFF");
+			break;
+		case PSEV_REM_RET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev REMOTE RETRAIN");
+			break;
+		case PSEV_REM_REN:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev REMOTE RENEGOTIATE");
+			break;
+		case PSEV_GSTN_CLR:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev GSTN CLEAR", devt);
+			break;
+		default:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "unknown pump stev %x", devt);
+			break;
+	}
+}
+
+static void
+ll_deliver_faxstat(struct BCState *bcs, u_char status)
+{
+        isdn_ctrl ic;
+	struct Channel *chanp = (struct Channel *) bcs->st->lli.userdata;
+ 
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs, "HL->LL FAXIND %x", status);
+	ic.driver = bcs->cs->myid;
+	ic.command = ISDN_STAT_FAXIND;
+	ic.arg = chanp->chan;
+	ic.parm.aux.cmd = status;
+	bcs->cs->iif.statcallb(&ic);
+}
+
+static void
+isar_pump_statev_fax(struct BCState *bcs, u_char devt) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char p1;
+
+	switch(devt) {
+		case PSEV_10MS_TIMER:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev TIMER");
+			break;
+		case PSEV_RSP_READY:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_READY");
+			bcs->hw.isar.state = STFAX_READY;
+			l1_msg_b(bcs->st, PH_ACTIVATE | REQUEST, NULL);
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FRH, 3);
+			} else {
+				isar_pump_cmd(bcs, ISDN_FAX_CLASS1_FTH, 3);
+			}
+			break;
+		case PSEV_LINE_TX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_H:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_H");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_H wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_TX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_TX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_TX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_LINE_RX_B:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev LINE_RX_B");
+				bcs->hw.isar.state = STFAX_CONT;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev LINE_RX_B wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_RSP_CONN:
+			if (bcs->hw.isar.state == STFAX_CONT) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev RSP_CONN");
+				bcs->hw.isar.state = STFAX_ACTIV;
+				test_and_set_bit(ISAR_RATE_REQ, &bcs->hw.isar.reg->Flags);
+				sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+				if (bcs->hw.isar.cmd == PCTRL_CMD_FTH) {
+					/* 1s Flags before data */
+					if (test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag))
+						del_timer(&bcs->hw.isar.ftimer);
+					/* 1000 ms */
+					bcs->hw.isar.ftimer.expires =
+						jiffies + ((1000 * HZ)/1000);
+					test_and_set_bit(BC_FLG_LL_CONN,
+						&bcs->Flag);
+					add_timer(&bcs->hw.isar.ftimer);
+				} else {
+					schedule_event(bcs, B_LL_CONNECT);
+				}
+			} else {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "pump stev RSP_CONN wrong st %x",
+						bcs->hw.isar.state);
+			}
+			break;
+		case PSEV_FLAGS_DET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev FLAGS_DET");
+			break;
+		case PSEV_RSP_DISC:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_DISC");
+			if (bcs->hw.isar.state == STFAX_ESCAPE) {
+				p1 = 5;
+				switch(bcs->hw.isar.newcmd) {
+					case 0:
+						bcs->hw.isar.state = STFAX_READY;
+						break;
+					case PCTRL_CMD_FTM:
+						p1 = 2;
+					case PCTRL_CMD_FTH:
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							PCTRL_CMD_SILON, 1, &p1);
+						bcs->hw.isar.state = STFAX_SILDET;
+						break;
+					case PCTRL_CMD_FRM:
+						if (frm_extra_delay)
+							mdelay(frm_extra_delay);
+					case PCTRL_CMD_FRH:
+						p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod;
+						bcs->hw.isar.newmod = 0;
+						bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+						bcs->hw.isar.newcmd = 0;
+						sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+							bcs->hw.isar.cmd, 1, &p1);
+						bcs->hw.isar.state = STFAX_LINE;
+						bcs->hw.isar.try_mod = 3;
+						break;
+					default:
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "RSP_DISC unknown newcmd %x", bcs->hw.isar.newcmd);
+						break;
+				}
+			} else if (bcs->hw.isar.state == STFAX_ACTIV) {
+				if (test_and_clear_bit(BC_FLG_LL_OK, &bcs->Flag)) {
+					schedule_event(bcs, B_LL_OK);
+				} else if (bcs->hw.isar.cmd == PCTRL_CMD_FRM) {
+					send_DLE_ETX(bcs);
+					schedule_event(bcs, B_LL_NOCARRIER);
+				} else {
+					ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+				}
+				bcs->hw.isar.state = STFAX_READY;
+			} else {
+				bcs->hw.isar.state = STFAX_READY;
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			}
+			break;
+		case PSEV_RSP_SILDET:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILDET");
+			if (bcs->hw.isar.state == STFAX_SILDET) {
+				p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.cmd = bcs->hw.isar.newcmd;
+				bcs->hw.isar.newcmd = 0;
+				sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+					bcs->hw.isar.cmd, 1, &p1);
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.try_mod = 3;
+			}
+			break;
+		case PSEV_RSP_SILOFF:
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_SILOFF");
+			break;
+		case PSEV_RSP_FCERR:
+			if (bcs->hw.isar.state == STFAX_LINE) {
+				if (cs->debug & L1_DEB_HSCX)
+					debugl1(cs, "pump stev RSP_FCERR try %d",
+						bcs->hw.isar.try_mod);
+				if (bcs->hw.isar.try_mod--) {
+					sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
+						bcs->hw.isar.cmd, 1,
+						&bcs->hw.isar.mod);
+					break;
+				}
+			}
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "pump stev RSP_FCERR");
+			bcs->hw.isar.state = STFAX_ESCAPE;
+			sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+			ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_FCERROR);
+			break;
+		default:
+			break;
+	}
+}
+
+static char debbuf[128];
+
+void
+isar_int_main(struct IsdnCardState *cs)
+{
+	struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+	struct BCState *bcs;
+
+	get_irq_infos(cs, ireg);
+	switch (ireg->iis & ISAR_IIS_MSCMSD) {
+		case ISAR_IIS_RDATA:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				isar_rcv_frame(cs, bcs);
+			} else {
+				debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_GSTEV:
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			ireg->bstat |= ireg->cmsb;
+			check_send(cs, ireg->cmsb);
+			break;
+		case ISAR_IIS_BSTEV:
+#ifdef ERROR_STATISTIC
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				if (ireg->cmsb == BSTEV_TBO)
+					bcs->err_tx++;
+				if (ireg->cmsb == BSTEV_RBO)
+					bcs->err_rdo++;
+			}
+#endif
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "Buffer STEV dpath%d msb(%x)",
+					ireg->iis>>6, ireg->cmsb);
+			cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			break;
+		case ISAR_IIS_PSTEV:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				rcv_mbox(cs, ireg, (u_char *)ireg->par);
+				if (bcs->mode == L1_MODE_V32) {
+					isar_pump_statev_modem(bcs, ireg->cmsb);
+				} else if (bcs->mode == L1_MODE_FAX) {
+					isar_pump_statev_fax(bcs, ireg->cmsb);
+				} else if (ireg->cmsb == PSEV_10MS_TIMER) {
+					if (cs->debug & L1_DEB_HSCX)
+						debugl1(cs, "pump stev TIMER");
+				} else {
+					if (cs->debug & L1_DEB_WARN)
+						debugl1(cs, "isar IIS_PSTEV pmode %d stat %x",
+							bcs->mode, ireg->cmsb);
+				}
+			} else {
+				debugl1(cs, "isar spurious IIS_PSTEV %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_PSTRSP:
+			if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+				rcv_mbox(cs, ireg, (u_char *)ireg->par);
+				isar_pump_status_rsp(bcs, ireg);
+			} else {
+				debugl1(cs, "isar spurious IIS_PSTRSP %x/%x/%x",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+				cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+			}
+			break;
+		case ISAR_IIS_DIAG:
+		case ISAR_IIS_BSTRSP:
+		case ISAR_IIS_IOM2RSP:
+			rcv_mbox(cs, ireg, (u_char *)ireg->par);
+			if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO))
+				== L1_DEB_HSCX) {
+				u_char *tp=debbuf;
+
+				tp += sprintf(debbuf, "msg iis(%x) msb(%x)",
+					ireg->iis, ireg->cmsb);
+				QuickHex(tp, (u_char *)ireg->par, ireg->clsb);
+				debugl1(cs, debbuf);
+			}
+			break;
+		case ISAR_IIS_INVMSG:
+			rcv_mbox(cs, ireg, debbuf);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "invalid msg his:%x",
+					ireg->cmsb);
+			break;
+		default:
+			rcv_mbox(cs, ireg, debbuf);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)",
+					ireg->iis, ireg->cmsb, ireg->clsb);
+			break;
+	}
+}
+
+static void
+ftimer_handler(struct BCState *bcs) {
+	if (bcs->cs->debug)
+		debugl1(bcs->cs, "ftimer flags %04x",
+			bcs->Flag);
+	test_and_clear_bit(BC_FLG_FTI_RUN, &bcs->Flag);
+	if (test_and_clear_bit(BC_FLG_LL_CONN, &bcs->Flag)) {
+		schedule_event(bcs, B_LL_CONNECT);
+	}
+	if (test_and_clear_bit(BC_FLG_FTI_FTS, &bcs->Flag)) {
+		schedule_event(bcs, B_LL_OK);
+	}
+}
+
+static void
+setup_pump(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl, param[6];
+
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
+			break;
+		case L1_MODE_V32:
+			ctrl = PMOD_DATAMODEM;
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				ctrl |= PCTRL_ORIG;
+				param[5] = PV32P6_CTN;
+			} else {
+				param[5] = PV32P6_ATN;
+			}
+			param[0] = para_TOA; /* 6 db */
+			param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B |
+				   PV32P2_V22C | PV32P2_V21 | PV32P2_BEL; 
+			param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B;
+			param[3] = PV32P4_UT144;
+			param[4] = PV32P5_UT144;
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
+			break;
+		case L1_MODE_FAX:
+			ctrl = PMOD_FAX;
+			if (test_bit(BC_FLG_ORIG, &bcs->Flag)) {
+				ctrl |= PCTRL_ORIG;
+				param[1] = PFAXP2_CTN;
+			} else {
+				param[1] = PFAXP2_ATN;
+			}
+			param[0] = para_TOA; /* 6 db */
+			sendmsg(cs, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
+			bcs->hw.isar.state = STFAX_NULL;
+			bcs->hw.isar.newcmd = 0;
+			bcs->hw.isar.newmod = 0;
+			test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag);
+			break;
+	}
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+static void
+setup_sart(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl, param[2];
+	
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0,
+				NULL);
+			break;
+		case L1_MODE_TRANS:
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2,
+				"\0\0");
+			break;
+		case L1_MODE_HDLC:
+			param[0] = 0;
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1,
+				param);
+			break;
+		case L1_MODE_V32:
+			ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
+			param[0] = S_P1_CHS_8;
+			param[1] = S_P2_BFT_DEF;
+			sendmsg(cs, dps | ISAR_HIS_SARTCFG, ctrl, 2,
+				param);
+			break;
+		case L1_MODE_FAX:
+			/* SART must not configured with FAX */
+			break;
+	}
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+static void
+setup_iom2(struct BCState *bcs) {
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0};
+	
+	if (bcs->channel)
+		msg[1] = msg[3] = 1;
+	switch (bcs->mode) {
+		case L1_MODE_NULL:
+			cmsb = 0;
+			/* dummy slot */
+			msg[1] = msg[3] = bcs->hw.isar.dpath + 2;
+			break;
+		case L1_MODE_TRANS:
+		case L1_MODE_HDLC:
+			break;
+		case L1_MODE_V32:
+		case L1_MODE_FAX:
+			cmsb |= IOM_CTRL_ALAW | IOM_CTRL_RCV;
+			break;
+	}
+	sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg);
+	udelay(1000);
+	sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL);
+	udelay(1000);
+}
+
+int
+modeisar(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	/* Here we are selecting the best datapath for requested mode */
+	if(bcs->mode == L1_MODE_NULL) { /* New Setup */
+		bcs->channel = bc;
+		switch (mode) {
+			case L1_MODE_NULL: /* init */
+				if (!bcs->hw.isar.dpath)
+					/* no init for dpath 0 */
+					return(0);
+				break;
+			case L1_MODE_TRANS:
+			case L1_MODE_HDLC:
+				/* best is datapath 2 */
+				if (!test_and_set_bit(ISAR_DP2_USE, 
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 2;
+				else if (!test_and_set_bit(ISAR_DP1_USE,
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 1;
+				else {
+					printk(KERN_WARNING"isar modeisar both pathes in use\n");
+					return(1);
+				}
+				break;
+			case L1_MODE_V32:
+			case L1_MODE_FAX:
+				/* only datapath 1 */
+				if (!test_and_set_bit(ISAR_DP1_USE, 
+					&bcs->hw.isar.reg->Flags))
+					bcs->hw.isar.dpath = 1;
+				else {
+					printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n");
+					debugl1(cs, "isar modeisar analog funktions only with DP1");
+					return(1);
+				}
+				break;
+		}
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "isar dp%d mode %d->%d ichan %d",
+			bcs->hw.isar.dpath, bcs->mode, mode, bc);
+	bcs->mode = mode;
+	setup_pump(bcs);
+	setup_iom2(bcs);
+	setup_sart(bcs);
+	if (bcs->mode == L1_MODE_NULL) {
+		/* Clear resources */
+		if (bcs->hw.isar.dpath == 1)
+			test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags);
+		else if (bcs->hw.isar.dpath == 2)
+			test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags);
+		bcs->hw.isar.dpath = 0;
+	}
+	return(0);
+}
+
+static void
+isar_pump_cmd(struct BCState *bcs, u_char cmd, u_char para) 
+{
+	struct IsdnCardState *cs = bcs->cs;
+	u_char dps = SET_DPS(bcs->hw.isar.dpath);
+	u_char ctrl = 0, nom = 0, p1 = 0;
+
+	switch(cmd) {
+		case ISDN_FAX_CLASS1_FTM:
+			test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FTH:
+			test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FTH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FTH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FTH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRM:
+			test_and_clear_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRM;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRM) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRM;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAX_CLASS1_FRH:
+			test_and_set_bit(BC_FLG_FRH_WAIT, &bcs->Flag);
+			if (bcs->hw.isar.state == STFAX_READY) {
+				p1 = para;
+				ctrl = PCTRL_CMD_FRH;
+				nom = 1;
+				bcs->hw.isar.state = STFAX_LINE;
+				bcs->hw.isar.cmd = ctrl;
+				bcs->hw.isar.mod = para;
+				bcs->hw.isar.newmod = 0;
+				bcs->hw.isar.newcmd = 0;
+				bcs->hw.isar.try_mod = 3; 
+			} else if ((bcs->hw.isar.state == STFAX_ACTIV) &&
+				(bcs->hw.isar.cmd == PCTRL_CMD_FRH) &&
+				(bcs->hw.isar.mod == para)) {
+				ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_CONNECT);
+			} else {
+				bcs->hw.isar.newmod = para;
+				bcs->hw.isar.newcmd = PCTRL_CMD_FRH;
+				nom = 0;
+				ctrl = PCTRL_CMD_ESC;
+				bcs->hw.isar.state = STFAX_ESCAPE; 
+			}
+			break;
+		case ISDN_FAXPUMP_HALT:
+			bcs->hw.isar.state = STFAX_NULL;
+			nom = 0;
+			ctrl = PCTRL_CMD_HALT;
+			break;
+	}
+	if (ctrl)
+		sendmsg(cs, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
+}
+
+void
+isar_setup(struct IsdnCardState *cs)
+{
+	u_char msg;
+	int i;
+	
+	/* Dpath 1, 2 */
+	msg = 61;
+	for (i=0; i<2; i++) {
+		/* Buffer Config */
+		sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+			ISAR_HIS_P12CFG, 4, 1, &msg);
+		cs->bcs[i].hw.isar.mml = msg;
+		cs->bcs[i].mode = 0;
+		cs->bcs[i].hw.isar.dpath = i + 1;
+		modeisar(&cs->bcs[i], 0, 0);
+		INIT_WORK(&cs->bcs[i].tqueue, (void *)(void *) isar_bh, &cs->bcs[i]);
+	}
+}
+
+void
+isar_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	int ret;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				if (bcs->cs->debug & L1_DEB_HSCX)
+					debugl1(bcs->cs, "DRQ set BC_FLG_BUSY");
+				bcs->hw.isar.txcnt = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n");
+			} else {
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				if (bcs->cs->debug & L1_DEB_HSCX)
+					debugl1(bcs->cs, "PUI set BC_FLG_BUSY");
+				bcs->tx_skb = skb;
+				bcs->hw.isar.txcnt = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			bcs->hw.isar.conmsg[0] = 0;
+			if (test_bit(FLG_ORIG, &st->l2.flag))
+				test_and_set_bit(BC_FLG_ORIG, &bcs->Flag);
+			else
+				test_and_clear_bit(BC_FLG_ORIG, &bcs->Flag);
+			switch(st->l1.mode) {
+				case L1_MODE_TRANS:
+				case L1_MODE_HDLC:
+					ret = modeisar(bcs, st->l1.mode, st->l1.bc);
+					spin_unlock_irqrestore(&bcs->cs->lock, flags);
+					if (ret)
+						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
+					else
+						l1_msg_b(st, PH_ACTIVATE | REQUEST, arg);
+					break;
+				case L1_MODE_V32:
+				case L1_MODE_FAX:
+					ret = modeisar(bcs, st->l1.mode, st->l1.bc);
+					spin_unlock_irqrestore(&bcs->cs->lock, flags);
+					if (ret)
+						l1_msg_b(st, PH_DEACTIVATE | REQUEST, arg);
+					break;
+				default:
+					spin_unlock_irqrestore(&bcs->cs->lock, flags);
+					break;
+			}
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			switch(st->l1.mode) {
+				case L1_MODE_TRANS:
+				case L1_MODE_HDLC:
+				case L1_MODE_V32:
+					break;
+				case L1_MODE_FAX:
+					isar_pump_cmd(bcs, ISDN_FAXPUMP_HALT, 0);
+					break;
+			}
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "PDAC clear BC_FLG_BUSY");
+			modeisar(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+void
+close_isarstate(struct BCState *bcs)
+{
+	modeisar(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.isar.rcvbuf) {
+			kfree(bcs->hw.isar.rcvbuf);
+			bcs->hw.isar.rcvbuf = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			if (bcs->cs->debug & L1_DEB_HSCX)
+				debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
+		}
+	}
+	del_timer(&bcs->hw.isar.ftimer);
+}
+
+int
+open_isarstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for isar.rcvbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "openisar clear BC_FLG_BUSY");
+	bcs->event = 0;
+	bcs->hw.isar.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_isar(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_isarstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = isar_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+int
+isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
+	u_long adr;
+	int features, i;
+	struct BCState *bcs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "isar_auxcmd cmd/ch %x/%d", ic->command, ic->arg);
+	switch (ic->command) {
+		case (ISDN_CMD_FAXCMD):
+			bcs = cs->channel[ic->arg].bcs;
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "isar_auxcmd cmd/subcmd %d/%d",
+					ic->parm.aux.cmd, ic->parm.aux.subcmd);
+			switch(ic->parm.aux.cmd) {
+				case ISDN_FAX_CLASS1_CTRL:
+					if (ic->parm.aux.subcmd == ETX)
+						test_and_set_bit(BC_FLG_DLEETX,
+							&bcs->Flag);
+					break;
+				case ISDN_FAX_CLASS1_FTS:
+					if (ic->parm.aux.subcmd == AT_QUERY) {
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_OK;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_QUERY) {
+						strcpy(ic->parm.aux.para, "0-255");
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_VALUE) {
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "isar_auxcmd %s=%d",
+								FC1_CMD[ic->parm.aux.cmd], ic->parm.aux.para[0]);
+						if (bcs->hw.isar.state == STFAX_READY) {
+							if (! ic->parm.aux.para[0]) {
+								ic->command = ISDN_STAT_FAXIND;
+								ic->parm.aux.cmd = ISDN_FAX_CLASS1_OK;
+								cs->iif.statcallb(ic);
+								return(0);
+							}
+							if (! test_and_set_bit(BC_FLG_FTI_RUN, &bcs->Flag)) {
+								/* n*10 ms */
+								bcs->hw.isar.ftimer.expires =
+									jiffies + ((ic->parm.aux.para[0] * 10 * HZ)/1000);
+								test_and_set_bit(BC_FLG_FTI_FTS, &bcs->Flag);
+								add_timer(&bcs->hw.isar.ftimer);
+								return(0);
+							} else {
+								if (cs->debug)
+									debugl1(cs, "isar FTS=%d and FTI busy",
+										ic->parm.aux.para[0]);
+							}
+						} else {
+							if (cs->debug)
+								debugl1(cs, "isar FTS=%d and isar.state not ready(%x)",
+									ic->parm.aux.para[0],bcs->hw.isar.state);
+						}
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR;
+						cs->iif.statcallb(ic);
+					}
+					break;
+				case ISDN_FAX_CLASS1_FRM:
+				case ISDN_FAX_CLASS1_FRH:
+				case ISDN_FAX_CLASS1_FTM:
+				case ISDN_FAX_CLASS1_FTH:
+					if (ic->parm.aux.subcmd == AT_QUERY) {
+						sprintf(ic->parm.aux.para,
+							"%d", bcs->hw.isar.mod);
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_QUERY) {
+						char *p = ic->parm.aux.para;
+						for(i=0;i<FAXMODCNT;i++)
+							if ((1<<i) & modmask)
+								p += sprintf(p, "%d,", faxmodulation[i]);
+						p--;
+						*p=0;
+						ic->command = ISDN_STAT_FAXIND;
+						ic->parm.aux.cmd = ISDN_FAX_CLASS1_QUERY;
+						cs->iif.statcallb(ic);
+						return(0);
+					} else if (ic->parm.aux.subcmd == AT_EQ_VALUE) {
+						if (cs->debug & L1_DEB_HSCX)
+							debugl1(cs, "isar_auxcmd %s=%d",
+								FC1_CMD[ic->parm.aux.cmd], ic->parm.aux.para[0]);
+						for(i=0;i<FAXMODCNT;i++)
+							if (faxmodulation[i]==ic->parm.aux.para[0])
+								break;
+						if ((i < FAXMODCNT) && ((1<<i) & modmask) && 
+							test_bit(BC_FLG_INIT, &bcs->Flag)) {
+							isar_pump_cmd(bcs,
+								ic->parm.aux.cmd,
+								ic->parm.aux.para[0]);
+							return(0);
+						}
+					}
+					/* wrong modulation or not activ */
+					/* fall through */
+				default:
+					ic->command = ISDN_STAT_FAXIND;
+					ic->parm.aux.cmd = ISDN_FAX_CLASS1_ERROR;
+					cs->iif.statcallb(ic);
+			}
+			break;
+		case (ISDN_CMD_IOCTL):
+			switch (ic->arg) {
+				case 9: /* load firmware */
+					features = ISDN_FEATURE_L2_MODEM |
+						ISDN_FEATURE_L2_FAX |
+						ISDN_FEATURE_L3_FCLASS1;
+					memcpy(&adr, ic->parm.num, sizeof(ulong));
+					if (isar_load_firmware(cs, (u_char __user *)adr))
+						return(1);
+					else 
+						ll_run(cs, features);
+					break;
+				case 20:
+					features = *(unsigned int *) ic->parm.num;
+					printk(KERN_DEBUG "HiSax: max modulation old(%04x) new(%04x)\n",
+						modmask, features);
+					modmask = features;
+					break;
+				case 21:
+					features = *(unsigned int *) ic->parm.num;
+					printk(KERN_DEBUG "HiSax: FRM extra delay old(%d) new(%d) ms\n",
+						frm_extra_delay, features);
+					if (features >= 0)
+						frm_extra_delay = features;
+					break;
+				case 22:
+					features = *(unsigned int *) ic->parm.num;
+					printk(KERN_DEBUG "HiSax: TOA old(%d) new(%d) db\n",
+						para_TOA, features);
+					if (features >= 0 && features < 32)
+						para_TOA = features;
+					break;
+				default:
+					printk(KERN_DEBUG "HiSax: invalid ioctl %d\n",
+					       (int) ic->arg);
+					return(-EINVAL);
+			}
+			break;
+		default:
+			return(-EINVAL);
+	}
+	return(0);
+}
+
+void __devinit
+initisar(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_isar;
+	cs->bcs[1].BC_SetStack = setstack_isar;
+	cs->bcs[0].BC_Close = close_isarstate;
+	cs->bcs[1].BC_Close = close_isarstate;
+	cs->bcs[0].hw.isar.ftimer.function = (void *) ftimer_handler;
+	cs->bcs[0].hw.isar.ftimer.data = (long) &cs->bcs[0];
+	init_timer(&cs->bcs[0].hw.isar.ftimer);
+	cs->bcs[1].hw.isar.ftimer.function = (void *) ftimer_handler;
+	cs->bcs[1].hw.isar.ftimer.data = (long) &cs->bcs[1];
+	init_timer(&cs->bcs[1].hw.isar.ftimer);
+}
diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h
new file mode 100644
index 0000000..bf76765
--- /dev/null
+++ b/drivers/isdn/hisax/isar.h
@@ -0,0 +1,222 @@
+/* $Id: isar.h,v 1.11.2.2 2004/01/12 22:52:27 keil Exp $
+ *
+ * ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+ 
+#define ISAR_IRQMSK	0x04
+#define ISAR_IRQSTA	0x04
+#define ISAR_IRQBIT	0x75
+#define ISAR_CTRL_H	0x61
+#define ISAR_CTRL_L	0x60
+#define ISAR_IIS	0x58
+#define ISAR_IIA	0x58
+#define ISAR_HIS	0x50
+#define ISAR_HIA	0x50
+#define ISAR_MBOX	0x4c
+#define ISAR_WADR	0x4a
+#define ISAR_RADR	0x48 
+
+#define ISAR_HIS_VNR		0x14
+#define ISAR_HIS_DKEY		0x02
+#define ISAR_HIS_FIRM		0x1e
+#define ISAR_HIS_STDSP		0x08
+#define ISAR_HIS_DIAG		0x05
+#define ISAR_HIS_WAITSTATE	0x27
+#define ISAR_HIS_TIMERIRQ	0x25
+#define ISAR_HIS_P0CFG		0x3c
+#define ISAR_HIS_P12CFG		0x24
+#define ISAR_HIS_SARTCFG	0x25	
+#define ISAR_HIS_PUMPCFG	0x26	
+#define ISAR_HIS_PUMPCTRL	0x2a	
+#define ISAR_HIS_IOM2CFG	0x27
+#define ISAR_HIS_IOM2REQ	0x07
+#define ISAR_HIS_IOM2CTRL	0x2b
+#define ISAR_HIS_BSTREQ		0x0c
+#define ISAR_HIS_PSTREQ		0x0e
+#define ISAR_HIS_SDATA		0x20
+#define ISAR_HIS_DPS1		0x40
+#define ISAR_HIS_DPS2		0x80
+#define SET_DPS(x)		((x<<6) & 0xc0)
+
+#define ISAR_CMD_TIMERIRQ_OFF	0x20
+#define ISAR_CMD_TIMERIRQ_ON	0x21
+
+
+#define ISAR_IIS_MSCMSD		0x3f
+#define ISAR_IIS_VNR		0x15
+#define ISAR_IIS_DKEY		0x03
+#define ISAR_IIS_FIRM		0x1f
+#define ISAR_IIS_STDSP		0x09
+#define ISAR_IIS_DIAG		0x25
+#define ISAR_IIS_GSTEV		0x00
+#define ISAR_IIS_BSTEV		0x28
+#define ISAR_IIS_BSTRSP		0x2c
+#define ISAR_IIS_PSTRSP		0x2e
+#define ISAR_IIS_PSTEV		0x2a
+#define ISAR_IIS_IOM2RSP	0x27
+#define ISAR_IIS_RDATA		0x20
+#define ISAR_IIS_INVMSG		0x3f
+
+#define ISAR_CTRL_SWVER	0x10
+#define ISAR_CTRL_STST	0x40
+
+#define ISAR_MSG_HWVER	{0x20, 0, 1}
+
+#define ISAR_DP1_USE	1
+#define ISAR_DP2_USE	2
+#define ISAR_RATE_REQ	3
+
+#define PMOD_DISABLE	0
+#define PMOD_FAX	1
+#define PMOD_DATAMODEM	2
+#define PMOD_HALFDUPLEX	3
+#define PMOD_V110	4
+#define PMOD_DTMF	5
+#define PMOD_DTMF_TRANS	6
+#define PMOD_BYPASS	7
+
+#define PCTRL_ORIG	0x80
+#define PV32P2_V23R	0x40
+#define PV32P2_V22A	0x20
+#define PV32P2_V22B	0x10
+#define PV32P2_V22C	0x08
+#define PV32P2_V21	0x02
+#define PV32P2_BEL	0x01
+
+// LSB MSB in ISAR doc wrong !!! Arghhh
+#define PV32P3_AMOD	0x80
+#define PV32P3_V32B	0x02
+#define PV32P3_V23B	0x01
+#define PV32P4_48	0x11
+#define PV32P5_48	0x05
+#define PV32P4_UT48	0x11
+#define PV32P5_UT48	0x0d
+#define PV32P4_96	0x11
+#define PV32P5_96	0x03
+#define PV32P4_UT96	0x11
+#define PV32P5_UT96	0x0f
+#define PV32P4_B96	0x91
+#define PV32P5_B96	0x0b
+#define PV32P4_UTB96	0xd1
+#define PV32P5_UTB96	0x0f
+#define PV32P4_120	0xb1
+#define PV32P5_120	0x09
+#define PV32P4_UT120	0xf1
+#define PV32P5_UT120	0x0f
+#define PV32P4_144	0x99
+#define PV32P5_144	0x09
+#define PV32P4_UT144	0xf9
+#define PV32P5_UT144	0x0f
+#define PV32P6_CTN	0x01
+#define PV32P6_ATN	0x02
+
+#define PFAXP2_CTN	0x01
+#define PFAXP2_ATN	0x04
+
+#define PSEV_10MS_TIMER	0x02
+#define PSEV_CON_ON	0x18
+#define PSEV_CON_OFF	0x19
+#define PSEV_V24_OFF	0x20
+#define PSEV_CTS_ON	0x21
+#define PSEV_CTS_OFF	0x22
+#define PSEV_DCD_ON	0x23
+#define PSEV_DCD_OFF	0x24
+#define PSEV_DSR_ON	0x25
+#define PSEV_DSR_OFF	0x26
+#define PSEV_REM_RET	0xcc
+#define PSEV_REM_REN	0xcd
+#define PSEV_GSTN_CLR	0xd4
+
+#define PSEV_RSP_READY	0xbc
+#define PSEV_LINE_TX_H	0xb3
+#define PSEV_LINE_TX_B	0xb2
+#define PSEV_LINE_RX_H	0xb1
+#define PSEV_LINE_RX_B	0xb0
+#define PSEV_RSP_CONN	0xb5
+#define PSEV_RSP_DISC	0xb7
+#define PSEV_RSP_FCERR	0xb9
+#define PSEV_RSP_SILDET	0xbe
+#define PSEV_RSP_SILOFF	0xab
+#define PSEV_FLAGS_DET	0xba
+
+#define PCTRL_CMD_FTH	0xa7
+#define PCTRL_CMD_FRH	0xa5
+#define PCTRL_CMD_FTM	0xa8
+#define PCTRL_CMD_FRM	0xa6
+#define PCTRL_CMD_SILON	0xac
+#define PCTRL_CMD_CONT	0xa2
+#define PCTRL_CMD_ESC	0xa4
+#define PCTRL_CMD_SILOFF 0xab
+#define PCTRL_CMD_HALT	0xa9
+
+#define PCTRL_LOC_RET	0xcf
+#define PCTRL_LOC_REN	0xce
+
+#define SMODE_DISABLE	0
+#define SMODE_V14	2
+#define SMODE_HDLC	3
+#define SMODE_BINARY	4
+#define SMODE_FSK_V14	5
+
+#define SCTRL_HDMC_BOTH	0x00
+#define SCTRL_HDMC_DTX	0x80
+#define SCTRL_HDMC_DRX	0x40
+#define S_P1_OVSP	0x40
+#define S_P1_SNP	0x20
+#define S_P1_EOP	0x10
+#define S_P1_EDP	0x08
+#define S_P1_NSB	0x04
+#define S_P1_CHS_8	0x03
+#define S_P1_CHS_7	0x02
+#define S_P1_CHS_6	0x01
+#define S_P1_CHS_5	0x00
+
+#define S_P2_BFT_DEF	0x10
+
+#define IOM_CTRL_ENA	0x80
+#define IOM_CTRL_NOPCM	0x00
+#define IOM_CTRL_ALAW	0x02
+#define IOM_CTRL_ULAW	0x04
+#define IOM_CTRL_RCV	0x01
+
+#define IOM_P1_TXD	0x10
+
+#define HDLC_FED	0x40
+#define HDLC_FSD	0x20
+#define HDLC_FST	0x20
+#define HDLC_ERROR	0x1c
+#define HDLC_ERR_FAD	0x10
+#define HDLC_ERR_RER	0x08
+#define HDLC_ERR_CER	0x04
+#define SART_NMD	0x01
+
+#define BSTAT_RDM0	0x1
+#define BSTAT_RDM1	0x2
+#define BSTAT_RDM2	0x4
+#define BSTAT_RDM3	0x8
+#define BSTEV_TBO	0x1f
+#define BSTEV_RBO	0x2f
+
+/* FAX State Machine */
+#define STFAX_NULL	0
+#define STFAX_READY	1
+#define STFAX_LINE	2
+#define STFAX_CONT	3
+#define STFAX_ACTIV	4
+#define STFAX_ESCAPE	5
+#define STFAX_SILDET	6
+
+#define ISDN_FAXPUMP_HALT	100
+
+extern int ISARVersion(struct IsdnCardState *cs, char *s);
+extern void isar_int_main(struct IsdnCardState *cs);
+extern void initisar(struct IsdnCardState *cs);
+extern void isar_fill_fifo(struct BCState *bcs);
+extern int isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic);
diff --git a/drivers/isdn/hisax/isdnhdlc.c b/drivers/isdn/hisax/isdnhdlc.c
new file mode 100644
index 0000000..cbdf54c
--- /dev/null
+++ b/drivers/isdn/hisax/isdnhdlc.c
@@ -0,0 +1,628 @@
+/*
+ * isdnhdlc.c  --  General purpose ISDN HDLC decoder.
+ *
+ *Copyright (C) 2002	Wolfgang Mües      <wolfgang@iksw-muees.de>
+ *		2001 	Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 	Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *
+ *      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/module.h>
+#include <linux/init.h>
+#include <linux/crc-ccitt.h>
+#include "isdnhdlc.h"
+
+/*-------------------------------------------------------------------*/
+
+MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
+	      "Frode Isaksen <fisaksen@bewan.com>, "
+	      "Kai Germaschewski <kai.germaschewski@gmx.de>");
+MODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
+MODULE_LICENSE("GPL");
+
+/*-------------------------------------------------------------------*/
+
+/* bit swap table.
+ * Very handy for devices with different bit order,
+ * and neccessary for each transparent B-channel access for all
+ * devices which works with this HDLC decoder without bit reversal.
+ */
+const unsigned char isdnhdlc_bit_rev_tab[256] = {
+	0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,
+	0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,
+	0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,
+	0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,
+	0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,
+	0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,
+	0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,
+	0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,
+	0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,
+	0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,
+	0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,
+	0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,
+	0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,
+	0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,
+	0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,
+	0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF
+};
+
+enum {
+	HDLC_FAST_IDLE,HDLC_GET_FLAG_B0,HDLC_GETFLAG_B1A6,HDLC_GETFLAG_B7,
+	HDLC_GET_DATA,HDLC_FAST_FLAG
+};
+
+enum {
+	HDLC_SEND_DATA,HDLC_SEND_CRC1,HDLC_SEND_FAST_FLAG,
+	HDLC_SEND_FIRST_FLAG,HDLC_SEND_CRC2,HDLC_SEND_CLOSING_FLAG,
+	HDLC_SEND_IDLE1,HDLC_SEND_FAST_IDLE,HDLC_SENDFLAG_B0,
+	HDLC_SENDFLAG_B1A6,HDLC_SENDFLAG_B7,STOPPED
+};
+
+void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->state = HDLC_GET_DATA;
+	hdlc->do_adapt56 = do_adapt56;
+	hdlc->dchannel = 0;
+	hdlc->crc = 0;
+	hdlc->cbin = 0;
+	hdlc->shift_reg = 0;
+	hdlc->ffvalue = 0;
+	hdlc->dstpos = 0;
+}
+
+void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc, int is_d_channel, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->do_closing = 0;
+	hdlc->ffvalue = 0;
+	if (is_d_channel) {
+		hdlc->dchannel = 1;
+		hdlc->state = HDLC_SEND_FIRST_FLAG;
+	} else {
+		hdlc->dchannel = 0;
+		hdlc->state = HDLC_SEND_FAST_FLAG;
+		hdlc->ffvalue = 0x7e;
+	}
+	hdlc->cbin = 0x7e;
+	hdlc->bit_shift = 0;
+	if(do_adapt56){
+		hdlc->do_adapt56 = 1;
+		hdlc->data_bits = 0;
+		hdlc->state = HDLC_SENDFLAG_B0;
+	} else {
+		hdlc->do_adapt56 = 0;
+		hdlc->data_bits = 8;
+	}
+	hdlc->shift_reg = 0;
+}
+
+/*
+  isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
+
+  The source buffer is scanned for valid HDLC frames looking for
+  flags (01111110) to indicate the start of a frame. If the start of
+  the frame is found, the bit stuffing is removed (0 after 5 1's).
+  When a new flag is found, the complete frame has been received
+  and the CRC is checked.
+  If a valid frame is found, the function returns the frame length
+  excluding the CRC with the bit HDLC_END_OF_FRAME set.
+  If the beginning of a valid frame is found, the function returns
+  the length.
+  If a framing error is found (too many 1s and not a flag) the function
+  returns the length with the bit HDLC_FRAMING_ERROR set.
+  If a CRC error is found the function returns the length with the
+  bit HDLC_CRC_ERROR set.
+  If the frame length exceeds the destination buffer size, the function
+  returns the length with the bit HDLC_LENGTH_ERROR set.
+
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (decoded) from the source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of decoded bytes in the destination buffer and status
+  flag.
+ */
+int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
+		     int slen, int *count, unsigned char *dst, int dsize)
+{
+	int status=0;
+
+	static const unsigned char fast_flag[]={
+		0x00,0x00,0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f
+	};
+
+	static const unsigned char fast_flag_value[]={
+		0x00,0x7e,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f
+	};
+
+	static const unsigned char fast_abort[]={
+		0x00,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff
+	};
+
+	*count = slen;
+
+	while(slen > 0){
+		if(hdlc->bit_shift==0){
+			hdlc->cbin = *src++;
+			slen--;
+			hdlc->bit_shift = 8;
+			if(hdlc->do_adapt56){
+				hdlc->bit_shift --;
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			return 0;
+		case HDLC_FAST_IDLE:
+			if(hdlc->cbin == 0xff){
+				hdlc->bit_shift = 0;
+				break;
+			}
+			hdlc->state = HDLC_GET_FLAG_B0;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->bit_shift = 8;
+			break;
+		case HDLC_GET_FLAG_B0:
+			if(!(hdlc->cbin & 0x80)) {
+				hdlc->state = HDLC_GETFLAG_B1A6;
+				hdlc->hdlc_bits1 = 0;
+			} else {
+				if(!hdlc->do_adapt56){
+					if(++hdlc->hdlc_bits1 >=8 ) if(hdlc->bit_shift==1)
+						hdlc->state = HDLC_FAST_IDLE;
+				}
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B1A6:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				if(hdlc->hdlc_bits1==6){
+					hdlc->state = HDLC_GETFLAG_B7;
+				}
+			} else {
+				hdlc->hdlc_bits1 = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B7:
+			if(hdlc->cbin & 0x80) {
+				hdlc->state = HDLC_GET_FLAG_B0;
+			} else {
+				hdlc->state = HDLC_GET_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->shift_reg = 0;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_bits = 0;
+				hdlc->data_received = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GET_DATA:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				switch(hdlc->hdlc_bits1){
+				case 6:
+					break;
+				case 7:
+					if(hdlc->data_received) {
+						// bad frame
+						status = -HDLC_FRAMING_ERROR;
+					}
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_abort[hdlc->bit_shift+1]){
+							hdlc->state = HDLC_FAST_IDLE;
+							hdlc->bit_shift=1;
+							break;
+						}
+					} else {
+						hdlc->state = HDLC_GET_FLAG_B0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->shift_reg |= 0x80;
+					hdlc->data_bits++;
+					break;
+				}
+			} else {
+				switch(hdlc->hdlc_bits1){
+				case 5:
+					break;
+				case 6:
+					if(hdlc->data_received){
+						if (hdlc->dstpos < 2) {
+							status = -HDLC_FRAMING_ERROR;
+						} else if (hdlc->crc != 0xf0b8){
+							// crc error
+							status = -HDLC_CRC_ERROR;
+						} else {
+							// remove CRC
+							hdlc->dstpos -= 2;
+							// good frame
+							status = hdlc->dstpos;
+						}
+					}
+					hdlc->crc = 0xffff;
+					hdlc->shift_reg = 0;
+					hdlc->data_bits = 0;
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_flag[hdlc->bit_shift]){
+							hdlc->ffvalue = fast_flag_value[hdlc->bit_shift];
+							hdlc->state = HDLC_FAST_FLAG;
+							hdlc->ffbit_shift = hdlc->bit_shift;
+							hdlc->bit_shift = 1;
+						} else {
+							hdlc->state = HDLC_GET_DATA;
+							hdlc->data_received = 0;
+						}
+					} else {
+						hdlc->state = HDLC_GET_DATA;
+						hdlc->data_received = 0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->data_bits++;
+					break;
+				}
+				hdlc->hdlc_bits1 = 0;
+			}
+			if (status) {
+				hdlc->dstpos = 0;
+				*count -= slen;
+				hdlc->cbin <<= 1;
+				hdlc->bit_shift--;
+				return status;
+			}
+			if(hdlc->data_bits==8){
+				hdlc->data_bits = 0;
+				hdlc->data_received = 1;
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+
+				// good byte received
+				if (hdlc->dstpos < dsize) {
+					dst[hdlc->dstpos++] = hdlc->shift_reg;
+				} else {
+					// frame too long
+					status = -HDLC_LENGTH_ERROR;
+					hdlc->dstpos = 0;
+				}
+			}
+			hdlc->cbin <<= 1;
+			hdlc->bit_shift--;
+			break;
+		case HDLC_FAST_FLAG:
+			if(hdlc->cbin==hdlc->ffvalue){
+				hdlc->bit_shift = 0;
+				break;
+			} else {
+				if(hdlc->cbin == 0xff){
+					hdlc->state = HDLC_FAST_IDLE;
+					hdlc->bit_shift=0;
+				} else if(hdlc->ffbit_shift==8){
+					hdlc->state = HDLC_GETFLAG_B7;
+					break;
+				} else {
+					hdlc->shift_reg = fast_abort[hdlc->ffbit_shift-1];
+					hdlc->hdlc_bits1 = hdlc->ffbit_shift-2;
+					if(hdlc->hdlc_bits1<0)hdlc->hdlc_bits1 = 0;
+					hdlc->data_bits = hdlc->ffbit_shift-1;
+					hdlc->state = HDLC_GET_DATA;
+					hdlc->data_received = 0;
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	}
+	*count -= slen;
+	return 0;
+}
+
+/*
+  isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
+
+  The bit stream starts with a beginning flag (01111110). After
+  that each byte is added to the bit stream with bit stuffing added
+  (0 after 5 1's).
+  When the last byte has been removed from the source buffer, the
+  CRC (2 bytes is added) and the frame terminates with the ending flag.
+  For the dchannel, the idle character (all 1's) is also added at the end.
+  If this function is called with empty source buffer (slen=0), flags or
+  idle character will be generated.
+
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (encoded) from source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of encoded bytes in the destination buffer
+*/
+int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
+		unsigned short slen, int *count,
+		unsigned char *dst, int dsize)
+{
+	static const unsigned char xfast_flag_value[] = {
+		0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e
+	};
+
+	int len = 0;
+
+	*count = slen;
+
+	while (dsize > 0) {
+		if(hdlc->bit_shift==0){
+			if(slen && !hdlc->do_closing){
+				hdlc->shift_reg = *src++;
+				slen--;
+				if (slen == 0)
+					hdlc->do_closing = 1;  /* closing sequence, CRC + flag(s) */
+				hdlc->bit_shift = 8;
+			} else {
+				if(hdlc->state == HDLC_SEND_DATA){
+					if(hdlc->data_received){
+						hdlc->state = HDLC_SEND_CRC1;
+						hdlc->crc ^= 0xffff;
+						hdlc->bit_shift = 8;
+						hdlc->shift_reg = hdlc->crc & 0xff;
+					} else if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+					}
+				}
+
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			while (dsize--)
+				*dst++ = 0xff;
+
+			return dsize;
+		case HDLC_SEND_FAST_FLAG:
+			hdlc->do_closing = 0;
+			if(slen == 0){
+				*dst++ = hdlc->ffvalue;
+				len++;
+				dsize--;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits);
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SENDFLAG_B0:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->state = HDLC_SENDFLAG_B1A6;
+			break;
+		case HDLC_SENDFLAG_B1A6:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->cbin++;
+			if(++hdlc->hdlc_bits1 == 6)
+				hdlc->state = HDLC_SENDFLAG_B7;
+			break;
+		case HDLC_SENDFLAG_B7:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(slen == 0){
+				hdlc->state = HDLC_SENDFLAG_B0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SEND_FIRST_FLAG:
+			hdlc->data_received = 1;
+			if(hdlc->data_bits==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->shift_reg & 0x01)
+				hdlc->cbin++;
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+			}
+			break;
+		case HDLC_SEND_DATA:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			break;
+		case HDLC_SEND_CRC1:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = (hdlc->crc >> 8);
+				hdlc->state = HDLC_SEND_CRC2;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CRC2:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = 0x7e;
+				hdlc->state = HDLC_SEND_CLOSING_FLAG;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CLOSING_FLAG:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->cbin++;
+			}
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->ffvalue = xfast_flag_value[hdlc->data_bits];
+				if(hdlc->dchannel){
+					hdlc->ffvalue = 0x7e;
+					hdlc->state = HDLC_SEND_IDLE1;
+					hdlc->bit_shift = 8-hdlc->data_bits;
+					if(hdlc->bit_shift==0)
+						hdlc->state = HDLC_SEND_FAST_IDLE;
+				} else {
+					if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+						hdlc->data_received = 0;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+						hdlc->data_received = 0;
+					}
+					// Finished with this frame, send flags
+					if (dsize > 1) dsize = 1;
+				}
+			}
+			break;
+		case HDLC_SEND_IDLE1:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->cbin++;
+			hdlc->data_bits++;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_FAST_IDLE;
+				hdlc->bit_shift = 0;
+			}
+			break;
+		case HDLC_SEND_FAST_IDLE:
+			hdlc->do_closing = 0;
+			hdlc->cbin = 0xff;
+			hdlc->data_bits = 8;
+			if(hdlc->bit_shift == 8){
+				hdlc->cbin = 0x7e;
+				hdlc->state = HDLC_SEND_FIRST_FLAG;
+			} else {
+				*dst++ = hdlc->cbin;
+				hdlc->bit_shift = hdlc->data_bits = 0;
+				len++;
+				dsize = 0;
+			}
+			break;
+		default:
+			break;
+		}
+		if(hdlc->do_adapt56){
+			if(hdlc->data_bits==7){
+				hdlc->cbin <<= 1;
+				hdlc->cbin++;
+				hdlc->data_bits++;
+			}
+		}
+		if(hdlc->data_bits==8){
+			*dst++ = hdlc->cbin;
+			hdlc->data_bits = 0;
+			len++;
+			dsize--;
+		}
+	}
+	*count -= slen;
+
+	return len;
+}
+
+EXPORT_SYMBOL(isdnhdlc_bit_rev_tab);
+EXPORT_SYMBOL(isdnhdlc_rcv_init);
+EXPORT_SYMBOL(isdnhdlc_decode);
+EXPORT_SYMBOL(isdnhdlc_out_init);
+EXPORT_SYMBOL(isdnhdlc_encode);
diff --git a/drivers/isdn/hisax/isdnhdlc.h b/drivers/isdn/hisax/isdnhdlc.h
new file mode 100644
index 0000000..2693159
--- /dev/null
+++ b/drivers/isdn/hisax/isdnhdlc.h
@@ -0,0 +1,72 @@
+/*
+ * isdnhdlc.h  --  General purpose ISDN HDLC decoder.
+ *
+ * Implementation of a HDLC decoder/encoder in software.
+ * Neccessary because some ISDN devices don't have HDLC
+ * controllers. Also included: a bit reversal table.
+ *
+ *Copyright (C) 2002    Wolfgang Mües      <wolfgang@iksw-muees.de>
+ *		2001 	Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 	Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *
+ *      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 __ISDNHDLC_H__
+#define __ISDNHDLC_H__
+
+struct isdnhdlc_vars {
+	int bit_shift;
+	int hdlc_bits1;
+	int data_bits;
+	int ffbit_shift; 	// encoding only
+	int state;
+	int dstpos;
+
+	unsigned short crc;
+
+	unsigned char cbin;
+	unsigned char shift_reg;
+	unsigned char ffvalue;
+
+	int data_received:1; 	// set if transferring data
+	int dchannel:1; 	// set if D channel (send idle instead of flags)
+	int do_adapt56:1; 	// set if 56K adaptation
+        int do_closing:1; 	// set if in closing phase (need to send CRC + flag
+};
+
+
+/*
+  The return value from isdnhdlc_decode is
+  the frame length, 0 if no complete frame was decoded,
+  or a negative error number
+*/
+#define HDLC_FRAMING_ERROR     1
+#define HDLC_CRC_ERROR         2
+#define HDLC_LENGTH_ERROR      3
+
+extern const unsigned char isdnhdlc_bit_rev_tab[256];
+
+extern void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56);
+
+extern int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src, int slen,int *count,
+	                    unsigned char *dst, int dsize);
+
+extern void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc,int is_d_channel,int do_adapt56);
+
+extern int isdnhdlc_encode (struct isdnhdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count,
+	                    unsigned char *dst,int dsize);
+
+#endif /* __ISDNHDLC_H__ */
diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c
new file mode 100644
index 0000000..4d08d27
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl1.c
@@ -0,0 +1,932 @@
+/* $Id: isdnl1.c,v 2.46.2.5 2004/02/11 13:21:34 keil Exp $
+ *
+ * common low level stuff for Siemens Chipsetbased isdn cards
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ */
+
+const char *l1_revision = "$Revision: 2.46.2.5 $";
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl1.h"
+
+#define TIMER3_VALUE 7000
+
+static struct Fsm l1fsm_b;
+static struct Fsm l1fsm_s;
+
+enum {
+	ST_L1_F2,
+	ST_L1_F3,
+	ST_L1_F4,
+	ST_L1_F5,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1S_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1SState[] =
+{
+	"ST_L1_F2",
+	"ST_L1_F3",
+	"ST_L1_F4",
+	"ST_L1_F5",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+#ifdef HISAX_UINTERFACE
+static
+struct Fsm l1fsm_u =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L1_RESET,
+	ST_L1_DEACT,
+	ST_L1_SYNC2,
+	ST_L1_TRANS,
+};
+
+#define L1U_STATE_COUNT (ST_L1_TRANS+1)
+
+static char *strL1UState[] =
+{
+	"ST_L1_RESET",
+	"ST_L1_DEACT",
+	"ST_L1_SYNC2",
+	"ST_L1_TRANS",
+};
+#endif
+
+enum {
+	ST_L1_NULL,
+	ST_L1_WAIT_ACT,
+	ST_L1_WAIT_DEACT,
+	ST_L1_ACTIV,
+};
+
+#define L1B_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strL1BState[] =
+{
+	"ST_L1_NULL",
+	"ST_L1_WAIT_ACT",
+	"ST_L1_WAIT_DEACT",
+	"ST_L1_ACTIV",
+};
+
+enum {
+	EV_PH_ACTIVATE,
+	EV_PH_DEACTIVATE,
+	EV_RESET_IND,
+	EV_DEACT_CNF,
+	EV_DEACT_IND,
+	EV_POWER_UP,
+	EV_RSYNC_IND, 
+	EV_INFO2_IND,
+	EV_INFO4_IND,
+	EV_TIMER_DEACT,
+	EV_TIMER_ACT,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+	"EV_PH_ACTIVATE",
+	"EV_PH_DEACTIVATE",
+	"EV_RESET_IND",
+	"EV_DEACT_CNF",
+	"EV_DEACT_IND",
+	"EV_POWER_UP",
+	"EV_RSYNC_IND", 
+	"EV_INFO2_IND",
+	"EV_INFO4_IND",
+	"EV_TIMER_DEACT",
+	"EV_TIMER_ACT",
+	"EV_TIMER3",
+};
+
+void
+debugl1(struct IsdnCardState *cs, char *fmt, ...)
+{
+	va_list args;
+	char tmp[8];
+	
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
+}
+
+static void
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs = st->l1.hardware;
+	char tmp[8];
+	
+	va_start(args, fmt);
+	sprintf(tmp, "Card%d ", cs->cardnr + 1);
+	VHiSax_putstatus(cs, tmp, fmt, args);
+	va_end(args);
+}
+
+void
+L1activated(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+			st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+		else
+			st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
+		st = st->next;
+	}
+}
+
+void
+L1deactivated(struct IsdnCardState *cs)
+{
+	struct PStack *st;
+
+	st = cs->stlist;
+	while (st) {
+		if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+		st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
+		st = st->next;
+	}
+	test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+}
+
+void
+DChannel_proc_xmt(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+
+	if (cs->tx_skb)
+		return;
+
+	stptr = cs->stlist;
+	while (stptr != NULL) {
+		if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
+			stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
+			break;
+		} else
+			stptr = stptr->next;
+	}
+}
+
+void
+DChannel_proc_rcv(struct IsdnCardState *cs)
+{
+	struct sk_buff *skb, *nskb;
+	struct PStack *stptr = cs->stlist;
+	int found, tei, sapi;
+
+	if (stptr)
+		if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
+			FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL);	
+	while ((skb = skb_dequeue(&cs->rq))) {
+#ifdef L2FRAME_DEBUG		/* psa */
+		if (cs->debug & L1_DEB_LAPD)
+			Logl2Frame(cs, skb, "PH_DATA", 1);
+#endif
+		stptr = cs->stlist;
+		if (skb->len<3) {
+			debugl1(cs, "D-channel frame too short(%d)",skb->len);
+			dev_kfree_skb(skb);
+			return;
+		}
+		if ((skb->data[0] & 1) || !(skb->data[1] &1)) {
+			debugl1(cs, "D-channel frame wrong EA0/EA1");
+			dev_kfree_skb(skb);
+			return;
+		}
+		sapi = skb->data[0] >> 2;
+		tei = skb->data[1] >> 1;
+		if (cs->debug & DEB_DLOG_HEX)
+			LogFrame(cs, skb->data, skb->len);
+		if (cs->debug & DEB_DLOG_VERBOSE)
+			dlogframe(cs, skb, 1);
+		if (tei == GROUP_TEI) {
+			if (sapi == CTRL_SAPI) { /* sapi 0 */
+				while (stptr != NULL) {
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
+					else
+						printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
+					stptr = stptr->next;
+				}
+			} else if (sapi == TEI_SAPI) {
+				while (stptr != NULL) {
+					if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+						stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb);
+					else
+						printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
+					stptr = stptr->next;
+				}
+			}
+			dev_kfree_skb(skb);
+		} else if (sapi == CTRL_SAPI) { /* sapi 0 */
+			found = 0;
+			while (stptr != NULL)
+				if (tei == stptr->l2.tei) {
+					stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
+					found = !0;
+					break;
+				} else
+					stptr = stptr->next;
+			if (!found)
+				dev_kfree_skb(skb);
+		} else
+			dev_kfree_skb(skb);
+	}
+}
+
+static void
+BChannel_proc_xmt(struct BCState *bcs)
+{
+	struct PStack *st = bcs->st;
+
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		debugl1(bcs->cs, "BC_BUSY Error");
+		return;
+	}
+
+	if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
+		st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+	if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
+		if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) {
+			st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+		}
+	}
+}
+
+static void
+BChannel_proc_rcv(struct BCState *bcs)
+{
+	struct sk_buff *skb;
+
+	if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) {
+		FsmDelTimer(&bcs->st->l1.timer, 4);
+		FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
+	}
+	while ((skb = skb_dequeue(&bcs->rqueue))) {
+		bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
+	}
+}
+
+static void
+BChannel_proc_ack(struct BCState *bcs)
+{
+	u_long	flags;
+	int	ack;
+
+	spin_lock_irqsave(&bcs->aclock, flags);
+	ack = bcs->ackcnt;
+	bcs->ackcnt = 0;
+	spin_unlock_irqrestore(&bcs->aclock, flags);
+	if (ack)
+		lli_writewakeup(bcs->st, ack);
+}
+
+void
+BChannel_bh(struct BCState *bcs)
+{
+	if (!bcs)
+		return;
+	if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event))
+		BChannel_proc_rcv(bcs);
+	if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event))
+		BChannel_proc_xmt(bcs);
+	if (test_and_clear_bit(B_ACKPENDING, &bcs->event))
+		BChannel_proc_ack(bcs);
+}
+
+void
+HiSax_addlist(struct IsdnCardState *cs,
+	      struct PStack *st)
+{
+	st->next = cs->stlist;
+	cs->stlist = st;
+}
+
+void
+HiSax_rmlist(struct IsdnCardState *cs,
+	     struct PStack *st)
+{
+	struct PStack *p;
+
+	FsmDelTimer(&st->l1.timer, 0);
+	if (cs->stlist == st)
+		cs->stlist = st->next;
+	else {
+		p = cs->stlist;
+		while (p)
+			if (p->next == st) {
+				p->next = st->next;
+				return;
+			} else
+				p = p->next;
+	}
+}
+
+void
+init_bcstate(struct IsdnCardState *cs, int bc)
+{
+	struct BCState *bcs = cs->bcs + bc;
+
+	bcs->cs = cs;
+	bcs->channel = bc;
+	INIT_WORK(&bcs->tqueue, (void *)(void *) BChannel_bh, bcs);
+	spin_lock_init(&bcs->aclock);
+	bcs->BC_SetStack = NULL;
+	bcs->BC_Close = NULL;
+	bcs->Flag = 0;
+}
+
+#ifdef L2FRAME_DEBUG		/* psa */
+
+char *
+l2cmd(u_char cmd)
+{
+	switch (cmd & ~0x10) {
+		case 1:
+			return "RR";
+		case 5:
+			return "RNR";
+		case 9:
+			return "REJ";
+		case 0x6f:
+			return "SABME";
+		case 0x0f:
+			return "DM";
+		case 3:
+			return "UI";
+		case 0x43:
+			return "DISC";
+		case 0x63:
+			return "UA";
+		case 0x87:
+			return "FRMR";
+		case 0xaf:
+			return "XID";
+		default:
+			if (!(cmd & 1))
+				return "I";
+			else
+				return "invalid command";
+	}
+}
+
+static char tmpdeb[32];
+
+char *
+l2frames(u_char * ptr)
+{
+	switch (ptr[2] & ~0x10) {
+		case 1:
+		case 5:
+		case 9:
+			sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+			break;
+		case 0x6f:
+		case 0x0f:
+		case 3:
+		case 0x43:
+		case 0x63:
+		case 0x87:
+		case 0xaf:
+			sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+			break;
+		default:
+			if (!(ptr[2] & 1)) {
+				sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+				break;
+			} else
+				return "invalid command";
+	}
+
+
+	return tmpdeb;
+}
+
+void
+Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
+{
+	u_char *ptr;
+
+	ptr = skb->data;
+
+	if (ptr[0] & 1 || !(ptr[1] & 1))
+		debugl1(cs, "Address not LAPD");
+	else
+		debugl1(cs, "%s %s: %s%c (sapi %d, tei %d)",
+			(dir ? "<-" : "->"), buf, l2frames(ptr),
+			((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
+}
+#endif
+
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3);
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+}
+
+static void
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_F3);
+	FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+	test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+}
+
+static void
+l1_power_up_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
+		FsmChangeState(fi, ST_L1_F4);
+		st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+		FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+		test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+	} else
+		FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F5);
+}
+
+static void
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+		FsmChangeState(fi, ST_L1_SYNC2);
+	else
+#endif
+		FsmChangeState(fi, ST_L1_F6);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+		FsmChangeState(fi, ST_L1_TRANS);
+	else
+#endif
+		FsmChangeState(fi, ST_L1_F7);
+	st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+	if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
+		FsmDelTimer(&st->l1.timer, 4);
+	if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
+		if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags))
+			FsmDelTimer(&st->l1.timer, 3);
+		FsmRestartTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
+		test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+	}
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);	
+	if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+		L1deactivated(st->l1.hardware);
+
+#ifdef HISAX_UINTERFACE
+	if (!test_bit(FLG_L1_UINT, &st->l1.Flags))
+#endif
+	if (st->l1.l1m.state != ST_L1_F6) {
+		FsmChangeState(fi, ST_L1_F3);
+		st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+	}
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	
+	test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+	test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1activated(st->l1.hardware);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	
+	test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+	L1deactivated(st->l1.hardware);
+	st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL);
+}
+
+static void
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+                
+	st->l1.l1hw(st, HW_RESET | REQUEST, NULL);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) {
+		test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+		L1deactivated(st->l1.hardware);
+	}
+}
+
+static struct FsmNode L1SFnList[] __initdata =
+{
+	{ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
+	{ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
+	{ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
+	{ST_L1_F3, EV_RESET_IND, l1_reset},
+	{ST_L1_F4, EV_RESET_IND, l1_reset},
+	{ST_L1_F5, EV_RESET_IND, l1_reset},
+	{ST_L1_F6, EV_RESET_IND, l1_reset},
+	{ST_L1_F7, EV_RESET_IND, l1_reset},
+	{ST_L1_F8, EV_RESET_IND, l1_reset},
+	{ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F3, EV_POWER_UP, l1_power_up_s},
+	{ST_L1_F4, EV_RSYNC_IND, l1_go_F5},
+	{ST_L1_F6, EV_RSYNC_IND, l1_go_F8},
+	{ST_L1_F7, EV_RSYNC_IND, l1_go_F8},
+	{ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F3, EV_TIMER3, l1_timer3},
+	{ST_L1_F4, EV_TIMER3, l1_timer3},
+	{ST_L1_F5, EV_TIMER3, l1_timer3},
+	{ST_L1_F6, EV_TIMER3, l1_timer3},
+	{ST_L1_F8, EV_TIMER3, l1_timer3},
+	{ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
+	{ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
+};
+
+#define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode))
+
+#ifdef HISAX_UINTERFACE
+static void
+l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_RESET);
+	FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+	test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+	st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+}
+
+static void
+l1_power_up_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+}
+
+static void
+l1_info0_ind(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+l1_activate_u(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+                
+	st->l1.l1hw(st, HW_INFO1 | REQUEST, NULL);
+}
+
+static struct FsmNode L1UFnList[] __initdata =
+{
+	{ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u},
+	{ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u},
+	{ST_L1_DEACT, EV_POWER_UP, l1_power_up_u},
+	{ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind},
+	{ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_RESET, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_DEACT, EV_TIMER3, l1_timer3},
+	{ST_L1_SYNC2, EV_TIMER3, l1_timer3},
+	{ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act},
+	{ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact},
+};
+
+#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode))
+
+#endif
+
+static void
+l1b_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_WAIT_ACT);
+	FsmRestartTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
+}
+
+static void
+l1b_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_WAIT_DEACT);
+	FsmRestartTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
+}
+
+static void
+l1b_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_ACTIV);
+	st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+}
+
+static void
+l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L1_NULL);
+	st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+}
+
+static struct FsmNode L1BFnList[] __initdata =
+{
+	{ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate},
+	{ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act},
+	{ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate},
+	{ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact},
+};
+
+#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
+
+int __init 
+Isdnl1New(void)
+{
+	int retval;
+
+	l1fsm_s.state_count = L1S_STATE_COUNT;
+	l1fsm_s.event_count = L1_EVENT_COUNT;
+	l1fsm_s.strEvent = strL1Event;
+	l1fsm_s.strState = strL1SState;
+	retval = FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT);
+	if (retval)
+		return retval;
+
+	l1fsm_b.state_count = L1B_STATE_COUNT;
+	l1fsm_b.event_count = L1_EVENT_COUNT;
+	l1fsm_b.strEvent = strL1Event;
+	l1fsm_b.strState = strL1BState;
+	retval = FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
+	if (retval) {
+		FsmFree(&l1fsm_s);
+		return retval;
+	}
+#ifdef HISAX_UINTERFACE
+	l1fsm_u.state_count = L1U_STATE_COUNT;
+	l1fsm_u.event_count = L1_EVENT_COUNT;
+	l1fsm_u.strEvent = strL1Event;
+	l1fsm_u.strState = strL1UState;
+	retval = FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT);
+	if (retval) {
+		FsmFree(&l1fsm_s);
+		FsmFree(&l1fsm_b);
+		return retval;
+	}
+#endif
+	return 0;
+}
+
+void Isdnl1Free(void)
+{
+#ifdef HISAX_UINTERFACE
+	FsmFree(&l1fsm_u);
+#endif
+	FsmFree(&l1fsm_s);
+	FsmFree(&l1fsm_b);
+}
+
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+		case (PH_PULL | REQUEST):
+		case (PH_PULL |INDICATION):
+			st->l1.l1hw(st, pr, arg);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			if (cs->debug)
+				debugl1(cs, "PH_ACTIVATE_REQ %s",
+					st->l1.l1m.fsm->strState[st->l1.l1m.state]);
+			if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
+				st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+			else {
+				test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+				FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
+			}
+			break;
+		case (PH_TESTLOOP | REQUEST):
+			if (1 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B1");
+			if (2 & (long) arg)
+				debugl1(cs, "PH_TEST_LOOP B2");
+			if (!(3 & (long) arg))
+				debugl1(cs, "PH_TEST_LOOP DISABLED");
+			st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+			break;
+		default:
+			if (cs->debug)
+				debugl1(cs, "dch_l2l1 msg %04X unhandled", pr);
+			break;
+	}
+}
+
+void
+l1_msg(struct IsdnCardState *cs, int pr, void *arg) {
+	struct PStack *st;
+
+	st = cs->stlist;
+	
+	while (st) {
+		switch(pr) {
+			case (HW_RESET | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
+				break;
+			case (HW_DEACTIVATE | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+				break;
+			case (HW_DEACTIVATE | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+				break;
+			case (HW_POWERUP | CONFIRM):
+				FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
+				break;
+			case (HW_RSYNC | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
+				break;
+			case (HW_INFO2 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+				break;
+			case (HW_INFO4_P8 | INDICATION):
+			case (HW_INFO4_P10 | INDICATION):
+				FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+				break;
+			default:
+				if (cs->debug)
+					debugl1(cs, "l1msg %04X unhandled", pr);
+				break;
+		}
+		st = st->next;
+	}
+}
+
+void
+l1_msg_b(struct PStack *st, int pr, void *arg) {
+	switch(pr) {
+		case (PH_ACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL);
+			break;
+	}
+}
+
+void
+setstack_HiSax(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.hardware = cs;
+	st->protocol = cs->protocol;
+	st->l1.l1m.fsm = &l1fsm_s;
+	st->l1.l1m.state = ST_L1_F3;
+	st->l1.Flags = 0;
+#ifdef HISAX_UINTERFACE
+	if (test_bit(FLG_HW_L1_UINT, &cs->HW_Flags)) {
+		st->l1.l1m.fsm = &l1fsm_u;
+		st->l1.l1m.state = ST_L1_RESET;
+		st->l1.Flags = FLG_L1_UINT;
+	}
+#endif
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+	setstack_tei(st);
+	setstack_manager(st);
+	st->l1.stlistp = &(cs->stlist);
+	st->l2.l2l1  = dch_l2l1;
+	if (cs->setstack_d)
+		cs->setstack_d(st, cs);
+}
+
+void
+setstack_l1_B(struct PStack *st)
+{
+	struct IsdnCardState *cs = st->l1.hardware;
+
+	st->l1.l1m.fsm = &l1fsm_b;
+	st->l1.l1m.state = ST_L1_NULL;
+	st->l1.l1m.debug = cs->debug;
+	st->l1.l1m.userdata = st;
+	st->l1.l1m.userint = 0;
+	st->l1.l1m.printdebug = l1m_debug;
+	st->l1.Flags = 0;
+	FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+}
diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h
new file mode 100644
index 0000000..0e88cfa
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl1.h
@@ -0,0 +1,32 @@
+/* $Id: isdnl1.h,v 2.12.2.3 2004/02/11 13:21:34 keil Exp $
+ *
+ * Layer 1 defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define D_RCVBUFREADY	0
+#define D_XMTBUFREADY	1
+#define D_L1STATECHANGE	2
+#define D_CLEARBUSY	3
+#define D_RX_MON0	4
+#define D_RX_MON1	5
+#define D_TX_MON0	6
+#define D_TX_MON1	7
+#define E_RCVBUFREADY	8
+
+#define B_RCVBUFREADY	0
+#define B_XMTBUFREADY	1
+#define B_ACKPENDING	2
+
+extern void debugl1(struct IsdnCardState *cs, char *fmt, ...);
+extern void DChannel_proc_xmt(struct IsdnCardState *cs);
+extern void DChannel_proc_rcv(struct IsdnCardState *cs);
+extern void l1_msg(struct IsdnCardState *cs, int pr, void *arg);
+extern void l1_msg_b(struct PStack *st, int pr, void *arg);
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
+#endif
diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c
new file mode 100644
index 0000000..d311b5f
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl2.c
@@ -0,0 +1,1860 @@
+/* $Id: isdnl2.c,v 2.30.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl2.h"
+
+const char *l2_revision = "$Revision: 2.30.2.4 $";
+
+static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
+
+static struct Fsm l2fsm;
+
+enum {
+	ST_L2_1,
+	ST_L2_2,
+	ST_L2_3,
+	ST_L2_4,
+	ST_L2_5,
+	ST_L2_6,
+	ST_L2_7,
+	ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+static char *strL2State[] =
+{
+	"ST_L2_1",
+	"ST_L2_2",
+	"ST_L2_3",
+	"ST_L2_4",
+	"ST_L2_5",
+	"ST_L2_6",
+	"ST_L2_7",
+	"ST_L2_8",
+};
+
+enum {
+	EV_L2_UI,
+	EV_L2_SABME,
+	EV_L2_DISC,
+	EV_L2_DM,
+	EV_L2_UA,
+	EV_L2_FRMR,
+	EV_L2_SUPER,
+	EV_L2_I,
+	EV_L2_DL_DATA,
+	EV_L2_ACK_PULL,
+	EV_L2_DL_UNIT_DATA,
+	EV_L2_DL_ESTABLISH_REQ,
+	EV_L2_DL_RELEASE_REQ,
+	EV_L2_MDL_ASSIGN,
+	EV_L2_MDL_REMOVE,
+	EV_L2_MDL_ERROR,
+	EV_L1_DEACTIVATE,
+	EV_L2_T200,
+	EV_L2_T203,
+	EV_L2_SET_OWN_BUSY,
+	EV_L2_CLEAR_OWN_BUSY,
+	EV_L2_FRAME_ERROR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1)
+
+static char *strL2Event[] =
+{
+	"EV_L2_UI",
+	"EV_L2_SABME",
+	"EV_L2_DISC",
+	"EV_L2_DM",
+	"EV_L2_UA",
+	"EV_L2_FRMR",
+	"EV_L2_SUPER",
+	"EV_L2_I",
+	"EV_L2_DL_DATA",
+	"EV_L2_ACK_PULL",
+	"EV_L2_DL_UNIT_DATA",
+	"EV_L2_DL_ESTABLISH_REQ",
+	"EV_L2_DL_RELEASE_REQ",
+	"EV_L2_MDL_ASSIGN",
+	"EV_L2_MDL_REMOVE",
+	"EV_L2_MDL_ERROR",
+	"EV_L1_DEACTIVATE",
+	"EV_L2_T200",
+	"EV_L2_T203",
+	"EV_L2_SET_OWN_BUSY",
+	"EV_L2_CLEAR_OWN_BUSY",
+	"EV_L2_FRAME_ERROR",
+};
+
+static int l2addrsize(struct Layer2 *l2);
+
+static void
+set_peer_busy(struct Layer2 *l2) {
+	test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+	if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue))
+		test_and_set_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+clear_peer_busy(struct Layer2 *l2) {
+	if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
+		test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+InitWin(struct Layer2 *l2)
+{
+	int i;
+
+	for (i = 0; i < MAX_WINDOW; i++)
+		l2->windowar[i] = NULL;
+}
+
+static int
+freewin1(struct Layer2 *l2)
+{
+	int i, cnt = 0;
+
+	for (i = 0; i < MAX_WINDOW; i++) {
+		if (l2->windowar[i]) {
+			cnt++;
+			dev_kfree_skb(l2->windowar[i]);
+			l2->windowar[i] = NULL;
+		}
+	}
+	return cnt;
+}
+
+inline void
+freewin(struct PStack *st)
+{
+	freewin1(&st->l2);
+}
+
+static void
+ReleaseWin(struct Layer2 *l2)
+{
+	int cnt;
+
+	if((cnt = freewin1(l2)))
+		printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt);
+}
+
+inline unsigned int
+cansend(struct PStack *st)
+{
+	unsigned int p1;
+
+	if(test_bit(FLG_MOD128, &st->l2.flag))
+		p1 = (st->l2.vs - st->l2.va) % 128;
+	else
+		p1 = (st->l2.vs - st->l2.va) % 8;
+	return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag));
+}
+
+inline void
+clear_exception(struct Layer2 *l2)
+{
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	test_and_clear_bit(FLG_REJEXC, &l2->flag);
+	test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
+	clear_peer_busy(l2);
+}
+
+inline int
+l2headersize(struct Layer2 *l2, int ui)
+{
+	return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+		(test_bit(FLG_LAPD, &l2->flag) ? 2 : 1));
+}
+
+inline int
+l2addrsize(struct Layer2 *l2)
+{
+	return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *l2, u_char * header, int rsp)
+{
+	u_char *ptr = header;
+	int crbit = rsp;
+
+	if (test_bit(FLG_LAPD, &l2->flag)) {
+		*ptr++ = (l2->sap << 2) | (rsp ? 2 : 0);
+		*ptr++ = (l2->tei << 1) | 1;
+		return (2);
+	} else {
+		if (test_bit(FLG_ORIG, &l2->flag))
+			crbit = !crbit;
+		if (crbit)
+			*ptr++ = 1;
+		else
+			*ptr++ = 3;
+		return (1);
+	}
+}
+
+inline static void
+enqueue_super(struct PStack *st,
+	      struct sk_buff *skb)
+{
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len;
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+}
+
+#define enqueue_ui(a, b) enqueue_super(a, b)
+
+inline int
+IsUI(u_char * data)
+{
+	return ((data[0] & 0xef) == UI);
+}
+
+inline int
+IsUA(u_char * data)
+{
+	return ((data[0] & 0xef) == UA);
+}
+
+inline int
+IsDM(u_char * data)
+{
+	return ((data[0] & 0xef) == DM);
+}
+
+inline int
+IsDISC(u_char * data)
+{
+	return ((data[0] & 0xef) == DISC);
+}
+
+inline int
+IsRR(u_char * data, struct PStack *st)
+{
+	if (test_bit(FLG_MOD128, &st->l2.flag))
+		return (data[0] == RR);
+	else
+		return ((data[0] & 0xf) == 1);
+}
+
+inline int
+IsSFrame(u_char * data, struct PStack *st)
+{
+	register u_char d = *data;
+	
+	if (!test_bit(FLG_MOD128, &st->l2.flag))
+		d &= 0xf;
+	return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c));
+}
+
+inline int
+IsSABME(u_char * data, struct PStack *st)
+{
+	u_char d = data[0] & ~0x10;
+
+	return (test_bit(FLG_MOD128, &st->l2.flag) ? d == SABME : d == SABM);
+}
+
+inline int
+IsREJ(u_char * data, struct PStack *st)
+{
+	return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == REJ : (data[0] & 0xf) == REJ);
+}
+
+inline int
+IsFRMR(u_char * data)
+{
+	return ((data[0] & 0xef) == FRMR);
+}
+
+inline int
+IsRNR(u_char * data, struct PStack *st)
+{
+	return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == RNR : (data[0] & 0xf) == RNR);
+}
+
+int
+iframe_error(struct PStack *st, struct sk_buff *skb)
+{
+	int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1);
+	int rsp = *skb->data & 0x2;
+
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (rsp)
+		return 'L';
+
+
+	if (skb->len < i)
+		return 'N';
+
+	if ((skb->len - i) > st->l2.maxlen)
+		return 'O';
+
+
+	return 0;
+}
+
+int
+super_error(struct PStack *st, struct sk_buff *skb)
+{
+	if (skb->len != l2addrsize(&st->l2) +
+	    (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1))
+		return 'N';
+
+	return 0;
+}
+
+int
+unnum_error(struct PStack *st, struct sk_buff *skb, int wantrsp)
+{
+	int rsp = (*skb->data & 0x2) >> 1;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (rsp != wantrsp)
+		return 'L';
+
+	if (skb->len != l2addrsize(&st->l2) + 1)
+		return 'N';
+
+	return 0;
+}
+
+int
+UI_error(struct PStack *st, struct sk_buff *skb)
+{
+	int rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (rsp)
+		return 'L';
+
+	if (skb->len > st->l2.maxlen + l2addrsize(&st->l2) + 1)
+		return 'O';
+
+	return 0;
+}
+
+int
+FRMR_error(struct PStack *st, struct sk_buff *skb)
+{
+	int headers = l2addrsize(&st->l2) + 1;
+	u_char *datap = skb->data + headers;
+	int rsp = *skb->data & 0x2;
+
+	if (test_bit(FLG_ORIG, &st->l2.flag))
+		rsp = !rsp;
+
+	if (!rsp)
+		return 'L';
+
+	if (test_bit(FLG_MOD128, &st->l2.flag)) {
+		if (skb->len < headers + 5)
+			return 'N';
+		else
+			l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x",
+				datap[0], datap[1], datap[2],
+				datap[3], datap[4]);
+	} else {
+		if (skb->len < headers + 3)
+			return 'N';
+		else
+			l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x",
+				datap[0], datap[1], datap[2]);
+	}
+
+	return 0;
+}
+
+static unsigned int
+legalnr(struct PStack *st, unsigned int nr)
+{
+        struct Layer2 *l2 = &st->l2;
+
+	if(test_bit(FLG_MOD128, &l2->flag))
+		return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128);
+	else
+		return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8);
+}
+
+static void
+setva(struct PStack *st, unsigned int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	int len;
+	u_long flags;
+
+	spin_lock_irqsave(&l2->lock, flags);
+	while (l2->va != nr) {
+		(l2->va)++;
+		if(test_bit(FLG_MOD128, &l2->flag))
+			l2->va %= 128;
+		else
+			l2->va %= 8;
+		len = l2->windowar[l2->sow]->len;
+		if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type)
+			len = -1;
+		dev_kfree_skb(l2->windowar[l2->sow]);
+		l2->windowar[l2->sow] = NULL;
+		l2->sow = (l2->sow + 1) % l2->window;
+		spin_unlock_irqrestore(&l2->lock, flags);
+		if (test_bit(FLG_LLI_L2WAKEUP, &st->lli.flag) && (len >=0))
+			lli_writewakeup(st, len);
+		spin_lock_irqsave(&l2->lock, flags);
+	}
+	spin_unlock_irqrestore(&l2->lock, flags);
+}
+
+static void
+send_uframe(struct PStack *st, u_char cmd, u_char cr)
+{
+	struct sk_buff *skb;
+	u_char tmp[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&st->l2, tmp, cr);
+	tmp[i++] = cmd;
+	if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
+		printk(KERN_WARNING "isdl2 can't alloc sbbuff for send_uframe\n");
+		return;
+	}
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(st, skb);
+}
+
+inline u_char
+get_PollFlag(struct PStack * st, struct sk_buff * skb)
+{
+	return (skb->data[l2addrsize(&(st->l2))] & 0x10);
+}
+
+inline void
+FreeSkb(struct sk_buff *skb)
+{
+	dev_kfree_skb(skb);
+}
+
+
+inline u_char
+get_PollFlagFree(struct PStack *st, struct sk_buff *skb)
+{
+	u_char PF;
+
+	PF = get_PollFlag(st, skb);
+	FreeSkb(skb);
+	return (PF);
+}
+
+inline void
+start_t200(struct PStack *st, int i)
+{
+	FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+}
+
+inline void
+restart_t200(struct PStack *st, int i)
+{
+	FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i);
+	test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+}
+
+inline void
+stop_t200(struct PStack *st, int i)
+{
+	if(test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+		FsmDelTimer(&st->l2.t200, i);
+}
+
+inline void
+st5_dl_release_l2l3(struct PStack *st)
+{
+		int pr;
+
+		if(test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+			pr = DL_RELEASE | CONFIRM;
+		else
+			pr = DL_RELEASE | INDICATION;
+
+		st->l2.l2l3(st, pr, NULL);
+}
+
+inline void
+lapb_dl_release_l2l3(struct PStack *st, int f)
+{
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st->l2.l2l3(st, DL_RELEASE | f, NULL);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+	struct PStack *st = fi->userdata;
+	u_char cmd;
+
+	clear_exception(&st->l2);
+	st->l2.rc = 0;
+	cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10;
+	send_uframe(st, cmd, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	restart_t200(st, 1);
+	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+	freewin(st);
+	FsmChangeState(fi, ST_L2_5);
+}
+
+static void
+l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
+
+	if (get_PollFlagFree(st, skb))
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C');
+	else
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D');
+}
+
+static void
+l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
+
+	if (get_PollFlagFree(st, skb))
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+	else {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+}
+
+static void
+l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct PStack *st = fi->userdata;
+
+	if (get_PollFlagFree(st, skb))
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+	else {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+	}
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_go_st3(struct FsmInst *fi, int event, void *arg)
+{
+	FsmChangeState(fi, ST_L2_3); 
+}
+
+static void
+l2_mdl_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L2_3); 
+	st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+}
+
+static void
+l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&st->l2.ui_queue, skb);
+	FsmChangeState(fi, ST_L2_2);
+	st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+}
+
+static void
+l2_queue_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&st->l2.ui_queue, skb);
+}
+
+static void
+tx_ui(struct PStack *st)
+{
+	struct sk_buff *skb;
+	u_char header[MAX_HEADER_LEN];
+	int i;
+
+	i = sethdraddr(&(st->l2), header, CMD);
+	header[i++] = UI;
+	while ((skb = skb_dequeue(&st->l2.ui_queue))) {
+		memcpy(skb_push(skb, i), header, i);
+		enqueue_ui(st, skb);
+	}
+}
+
+static void
+l2_send_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&st->l2.ui_queue, skb);
+	tx_ui(st);
+}
+
+static void
+l2_got_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_pull(skb, l2headersize(&st->l2, 1));
+	st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
+/*	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *		in states 1-3 for broadcast
+ */
+
+
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+}
+
+static void
+l2_l3_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+}
+
+static void
+l2_pend_rel(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	test_and_set_bit(FLG_PEND_REL, &st->l2.flag);
+}
+
+static void
+l2_disconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	freewin(st);
+	FsmChangeState(fi, ST_L2_6);
+	st->l2.rc = 0;
+	send_uframe(st, DISC | 0x10, CMD);
+	FsmDelTimer(&st->l2.t203, 1);
+	restart_t200(st, 2);
+}
+
+static void
+l2_start_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+	clear_exception(&st->l2);
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+
+	st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+}
+
+static void
+l2_send_UA(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+}
+
+static void
+l2_send_DM(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(st, DM | get_PollFlagFree(st, skb), RSP);
+}
+
+static void
+l2_restart_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int est = 0, state;
+
+	state = fi->state;
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F');
+
+	if (st->l2.vs != st->l2.va) {
+		skb_queue_purge(&st->l2.i_queue);
+		est = 1;
+	}
+
+	clear_exception(&st->l2);
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.vr = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	stop_t200(st, 3);
+	FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+
+	if (est)
+		st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+
+	if ((ST_L2_7==state) || (ST_L2_8 == state))
+		if (skb_queue_len(&st->l2.i_queue) && cansend(st))
+			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_stop_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	FsmChangeState(fi, ST_L2_4);
+	FsmDelTimer(&st->l2.t203, 3);
+	stop_t200(st, 4);
+
+	send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+	skb_queue_purge(&st->l2.i_queue);
+	freewin(st);
+	lapb_dl_release_l2l3(st, INDICATION);
+}
+
+static void
+l2_connected(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int pr=-1;
+
+	if (!get_PollFlag(st, skb)) {
+		l2_mdl_error_ua(fi, event, arg);
+		return;
+	}
+	FreeSkb(skb);
+
+	if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+		l2_disconnect(fi, event, arg);
+
+	if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
+		pr = DL_ESTABLISH | CONFIRM;
+	} else if (st->l2.vs != st->l2.va) {
+		skb_queue_purge(&st->l2.i_queue);
+		pr = DL_ESTABLISH | INDICATION;
+	}
+
+	stop_t200(st, 5);
+
+	st->l2.vr = 0;
+	st->l2.vs = 0;
+	st->l2.va = 0;
+	st->l2.sow = 0;
+	FsmChangeState(fi, ST_L2_7);
+	FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
+
+	if (pr != -1)
+		st->l2.l2l3(st, pr, NULL);
+
+	if (skb_queue_len(&st->l2.i_queue) && cansend(st))
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_released(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!get_PollFlag(st, skb)) {
+		l2_mdl_error_ua(fi, event, arg);
+		return;
+	}
+	FreeSkb(skb);
+
+	stop_t200(st, 6);
+	lapb_dl_release_l2l3(st, CONFIRM);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!get_PollFlagFree(st, skb)) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+}
+
+static void
+l2_st5_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (get_PollFlagFree(st, skb)) {
+		stop_t200(st, 7);
+	 	if (!test_bit(FLG_L3_INIT, &st->l2.flag))
+			skb_queue_purge(&st->l2.i_queue);
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st5_dl_release_l2l3(st);
+		FsmChangeState(fi, ST_L2_4);
+	}
+}
+
+static void
+l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (get_PollFlagFree(st, skb)) {
+		stop_t200(st, 8);
+		lapb_dl_release_l2l3(st, CONFIRM);
+		FsmChangeState(fi, ST_L2_4);
+	}
+}
+
+inline void
+enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf)
+{
+	struct sk_buff *skb;
+	struct Layer2 *l2;
+	u_char tmp[MAX_HEADER_LEN];
+	int i;
+
+	l2 = &st->l2;
+	i = sethdraddr(l2, tmp, cr);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		tmp[i++] = typ;
+		tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0);
+	} else
+		tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0);
+	if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
+		printk(KERN_WARNING "isdl2 can't alloc sbbuff for enquiry_cr\n");
+		return;
+	}
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(st, skb);
+}
+
+inline void
+enquiry_response(struct PStack *st)
+{
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, RSP, 1);
+	else
+		enquiry_cr(st, RR, RSP, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+}
+
+inline void
+transmit_enquiry(struct PStack *st)
+{
+	if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+		enquiry_cr(st, RNR, CMD, 1);
+	else
+		enquiry_cr(st, RR, CMD, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	start_t200(st, 9);
+}
+
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J');
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+invoke_retransmission(struct PStack *st, unsigned int nr)
+{
+	struct Layer2 *l2 = &st->l2;
+	u_int p1;
+	u_long flags;
+
+	spin_lock_irqsave(&l2->lock, flags);
+	if (l2->vs != nr) {
+		while (l2->vs != nr) {
+			(l2->vs)--;
+			if(test_bit(FLG_MOD128, &l2->flag)) {
+				l2->vs %= 128;
+				p1 = (l2->vs - l2->va) % 128;
+			} else {
+				l2->vs %= 8;
+				p1 = (l2->vs - l2->va) % 8;
+			}
+			p1 = (p1 + l2->sow) % l2->window;
+			if (test_bit(FLG_LAPB, &l2->flag))
+				st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0);
+			skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+			l2->windowar[p1] = NULL;
+		}
+		spin_unlock_irqrestore(&l2->lock, flags);
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		return;
+	}
+	spin_unlock_irqrestore(&l2->lock, flags);
+}
+
+static void
+l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, rsp, typ = RR;
+	unsigned int nr;
+	struct Layer2 *l2 = &st->l2;
+
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+	if (IsRNR(skb->data, st)) {
+		set_peer_busy(l2);
+		typ = RNR;
+	} else
+		clear_peer_busy(l2);
+	if (IsREJ(skb->data, st))
+		typ = REJ;
+
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		PollFlag = (skb->data[1] & 0x1) == 0x1;
+		nr = skb->data[1] >> 1;
+	} else {
+		PollFlag = (skb->data[0] & 0x10);
+		nr = (skb->data[0] >> 5) & 0x7;
+	}
+	FreeSkb(skb);
+
+	if (PollFlag) {
+		if (rsp)
+			st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A');
+		else
+			enquiry_response(st);
+	}
+	if (legalnr(st, nr)) {
+		if (typ == REJ) {
+			setva(st, nr);
+			invoke_retransmission(st, nr);
+			stop_t200(st, 10);
+			if (FsmAddTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 6))
+				l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ");
+		} else if ((nr == l2->vs) && (typ == RR)) {
+			setva(st, nr);
+			stop_t200(st, 11);
+			FsmRestartTimer(&st->l2.t203, st->l2.T203,
+					EV_L2_T203, NULL, 7);
+		} else if ((l2->va != nr) || (typ == RNR)) {
+			setva(st, nr);
+			if(typ != RR) FsmDelTimer(&st->l2.t203, 9);
+			restart_t200(st, 12);
+		}
+		if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
+			st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+	} else
+		nrerrorrecovery(fi);
+}
+
+static void
+l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	if (!test_bit(FLG_L3_INIT, &st->l2.flag))
+		skb_queue_tail(&st->l2.i_queue, skb);
+	else
+		FreeSkb(skb);
+}
+
+static void
+l2_feed_i_pull(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	skb_queue_tail(&st->l2.i_queue, skb);
+	st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_feed_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+	skb_queue_tail(&st->l2.i_queue, skb);
+}
+
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct Layer2 *l2 = &(st->l2);
+	int PollFlag, ns, i;
+	unsigned int nr;
+
+	i = l2addrsize(l2);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		PollFlag = ((skb->data[i + 1] & 0x1) == 0x1);
+		ns = skb->data[i] >> 1;
+		nr = (skb->data[i + 1] >> 1) & 0x7f;
+	} else {
+		PollFlag = (skb->data[i] & 0x10);
+		ns = (skb->data[i] >> 1) & 0x7;
+		nr = (skb->data[i] >> 5) & 0x7;
+	}
+	if (test_bit(FLG_OWN_BUSY, &l2->flag)) {
+		FreeSkb(skb);
+		if(PollFlag) enquiry_response(st);
+	} else if (l2->vr == ns) {
+		(l2->vr)++;
+		if(test_bit(FLG_MOD128, &l2->flag))
+			l2->vr %= 128;
+		else
+			l2->vr %= 8;
+		test_and_clear_bit(FLG_REJEXC, &l2->flag);
+
+		if (PollFlag)
+			enquiry_response(st);
+		else
+			test_and_set_bit(FLG_ACK_PEND, &l2->flag);
+		skb_pull(skb, l2headersize(l2, 0));
+		st->l2.l2l3(st, DL_DATA | INDICATION, skb);
+	} else {
+		/* n(s)!=v(r) */
+		FreeSkb(skb);
+		if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+			if (PollFlag)
+				enquiry_response(st);
+		} else {
+			enquiry_cr(st, REJ, RSP, PollFlag);
+			test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+		}
+	}
+
+	if (legalnr(st, nr)) {
+		if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) {
+			if (nr == st->l2.vs) {
+				stop_t200(st, 13);
+				FsmRestartTimer(&st->l2.t203, st->l2.T203,
+						EV_L2_T203, NULL, 7);
+			} else if (nr != st->l2.va)
+				restart_t200(st, 14);
+		}
+		setva(st, nr);
+	} else {
+		nrerrorrecovery(fi);
+		return;
+	}
+
+	if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+	if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
+		enquiry_cr(st, RR, RSP, 0);
+}
+
+static void
+l2_got_tei(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->l2.tei = (long) arg;
+
+	if (fi->state == ST_L2_3) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+	} else
+		FsmChangeState(fi, ST_L2_4);
+	if (skb_queue_len(&st->l2.ui_queue))
+		tx_ui(st);
+}
+
+static void
+l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
+		FsmChangeState(fi, ST_L2_4);
+		test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+		skb_queue_purge(&st->l2.i_queue);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
+		if (test_bit(FLG_LAPB, &st->l2.flag))
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+		st5_dl_release_l2l3(st);
+	} else {
+		st->l2.rc++;
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM)
+			    | 0x10, CMD);
+	}
+}
+
+static void
+l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+	} else if (st->l2.rc == st->l2.N200) {
+		FsmChangeState(fi, ST_L2_4);
+		test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H');
+		lapb_dl_release_l2l3(st, CONFIRM);
+	} else {
+		st->l2.rc++;
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
+			    NULL, 9);
+		send_uframe(st, DISC | 0x10, CMD);
+	}
+}
+
+static void
+l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+	st->l2.rc = 0;
+	FsmChangeState(fi, ST_L2_8);
+
+	transmit_enquiry(st);
+	st->l2.rc++;
+}
+
+static void
+l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+	if (st->l2.rc == st->l2.N200) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'I');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	} else {
+		transmit_enquiry(st);
+		st->l2.rc++;
+	}
+}
+
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &st->l2.flag) &&
+		test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+		FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9);
+		return;
+	}
+	FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(st);
+	st->l2.rc = 0;
+}
+
+static void
+l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb, *oskb;
+	struct Layer2 *l2 = &st->l2;
+	u_char header[MAX_HEADER_LEN];
+	int i;
+	int unsigned p1;
+	u_long flags;
+
+	if (!cansend(st))
+		return;
+
+	skb = skb_dequeue(&l2->i_queue);
+	if (!skb)
+		return;
+
+	spin_lock_irqsave(&l2->lock, flags);
+	if(test_bit(FLG_MOD128, &l2->flag))
+		p1 = (l2->vs - l2->va) % 128;
+	else
+		p1 = (l2->vs - l2->va) % 8;
+	p1 = (p1 + l2->sow) % l2->window;
+	if (l2->windowar[p1]) {
+		printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n",
+		       p1);
+		dev_kfree_skb(l2->windowar[p1]);
+	}
+	l2->windowar[p1] = skb_clone(skb, GFP_ATOMIC);
+
+	i = sethdraddr(&st->l2, header, CMD);
+
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		header[i++] = l2->vs << 1;
+		header[i++] = l2->vr << 1;
+		l2->vs = (l2->vs + 1) % 128;
+	} else {
+		header[i++] = (l2->vr << 5) | (l2->vs << 1);
+		l2->vs = (l2->vs + 1) % 8;
+	}
+	spin_unlock_irqrestore(&l2->lock, flags);
+	p1 = skb->data - skb->head;
+	if (p1 >= i)
+		memcpy(skb_push(skb, i), header, i);
+	else {
+		printk(KERN_WARNING
+		"isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+		oskb = skb;
+		skb = alloc_skb(oskb->len + i, GFP_ATOMIC);
+		memcpy(skb_put(skb, i), header, i);
+		memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
+		FreeSkb(oskb);
+	}
+	st->l2.l2l1(st, PH_PULL | INDICATION, skb);
+	test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
+		FsmDelTimer(&st->l2.t203, 13);
+		FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
+	}
+	if (skb_queue_len(&l2->i_queue) && cansend(st))
+		st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, rsp, rnr = 0;
+	unsigned int nr;
+	struct Layer2 *l2 = &st->l2;
+
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+
+	if (IsRNR(skb->data, st)) {
+		set_peer_busy(l2);
+		rnr = 1;
+	} else
+		clear_peer_busy(l2);
+
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		PollFlag = (skb->data[1] & 0x1) == 0x1;
+		nr = skb->data[1] >> 1;
+	} else {
+		PollFlag = (skb->data[0] & 0x10);
+		nr = (skb->data[0] >> 5) & 0x7;
+	}
+	FreeSkb(skb);
+
+	if (rsp && PollFlag) {
+		if (legalnr(st, nr)) {
+			if (rnr) {
+				restart_t200(st, 15);
+			} else {
+				stop_t200(st, 16);
+				FsmAddTimer(&l2->t203, l2->T203,
+					    EV_L2_T203, NULL, 5);
+				setva(st, nr);
+			}
+			invoke_retransmission(st, nr);
+			FsmChangeState(fi, ST_L2_7);
+			if (skb_queue_len(&l2->i_queue) && cansend(st))
+				st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+		} else
+			nrerrorrecovery(fi);
+	} else {
+		if (!rsp && PollFlag)
+			enquiry_response(st);
+		if (legalnr(st, nr)) {
+			setva(st, nr);
+		} else
+			nrerrorrecovery(fi);
+	}
+}
+
+static void
+l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_pull(skb, l2addrsize(&st->l2) + 1);
+
+	if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) ||		/* I or S */
+	    (IsUA(skb->data) && (fi->state == ST_L2_7))) {
+		st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+	}
+	FreeSkb(skb);
+}
+
+static void
+l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	st->l2.tei = -1;
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	st->l2.tei = -1;
+	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	st->l2.tei = -1;
+	stop_t200(st, 17);
+	st5_dl_release_l2l3(st);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	st->l2.tei = -1;
+	stop_t200(st, 18);
+	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	st->l2.tei = -1;
+	stop_t200(st, 17);
+	FsmDelTimer(&st->l2.t203, 19);
+	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+		st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+}
+
+static void
+l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	stop_t200(st, 19);
+	st5_dl_release_l2l3(st);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.ui_queue);
+	stop_t200(st, 20);
+	st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	freewin(st);
+	stop_t200(st, 19);
+	FsmDelTimer(&st->l2.t203, 19);
+	st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+	FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if(!test_and_set_bit(FLG_OWN_BUSY, &st->l2.flag)) {
+		enquiry_cr(st, RNR, RSP, 0);
+		test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	}
+}
+
+static void
+l2_clear_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if(!test_and_clear_bit(FLG_OWN_BUSY, &st->l2.flag)) {
+		enquiry_cr(st, RR, RSP, 0);
+		test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+	}
+}
+
+static void
+l2_frame_error(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, arg);
+}
+
+static void
+l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	st->ma.layer(st, MDL_ERROR | INDICATION, arg);
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static struct FsmNode L2FnList[] __initdata =
+{
+	{ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign},
+	{ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3},
+	{ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish},
+	{ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3},
+	{ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+	{ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+	{ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release},
+	{ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel},
+	{ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+	{ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+	{ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest},
+	{ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull},
+	{ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue},
+	{ST_L2_1, EV_L2_DL_UNIT_DATA, l2_queue_ui_assign},
+	{ST_L2_2, EV_L2_DL_UNIT_DATA, l2_queue_ui},
+	{ST_L2_3, EV_L2_DL_UNIT_DATA, l2_queue_ui},
+	{ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_5, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_6, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_8, EV_L2_DL_UNIT_DATA, l2_send_ui},
+	{ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove},
+	{ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove},
+	{ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove},
+	{ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove},
+	{ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove},
+	{ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_4, EV_L2_SABME, l2_start_multi},
+	{ST_L2_5, EV_L2_SABME, l2_send_UA},
+	{ST_L2_6, EV_L2_SABME, l2_send_DM},
+	{ST_L2_7, EV_L2_SABME, l2_restart_multi},
+	{ST_L2_8, EV_L2_SABME, l2_restart_multi},
+	{ST_L2_4, EV_L2_DISC, l2_send_DM},
+	{ST_L2_5, EV_L2_DISC, l2_send_DM},
+	{ST_L2_6, EV_L2_DISC, l2_send_UA},
+	{ST_L2_7, EV_L2_DISC, l2_stop_multi},
+	{ST_L2_8, EV_L2_DISC, l2_stop_multi},
+	{ST_L2_4, EV_L2_UA, l2_mdl_error_ua},
+	{ST_L2_5, EV_L2_UA, l2_connected},
+	{ST_L2_6, EV_L2_UA, l2_released},
+	{ST_L2_7, EV_L2_UA, l2_mdl_error_ua},
+	{ST_L2_8, EV_L2_UA, l2_mdl_error_ua},
+	{ST_L2_4, EV_L2_DM, l2_reestablish},
+	{ST_L2_5, EV_L2_DM, l2_st5_dm_release},
+	{ST_L2_6, EV_L2_DM, l2_st6_dm_release},
+	{ST_L2_7, EV_L2_DM, l2_mdl_error_dm},
+	{ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm},
+	{ST_L2_1, EV_L2_UI, l2_got_ui},
+	{ST_L2_2, EV_L2_UI, l2_got_ui},
+	{ST_L2_3, EV_L2_UI, l2_got_ui},
+	{ST_L2_4, EV_L2_UI, l2_got_ui},
+	{ST_L2_5, EV_L2_UI, l2_got_ui},
+	{ST_L2_6, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_UI, l2_got_ui},
+	{ST_L2_8, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
+	{ST_L2_8, EV_L2_FRMR, l2_got_FRMR},
+	{ST_L2_7, EV_L2_SUPER, l2_st7_got_super},
+	{ST_L2_8, EV_L2_SUPER, l2_st8_got_super},
+	{ST_L2_7, EV_L2_I, l2_got_iframe},
+	{ST_L2_8, EV_L2_I, l2_got_iframe},
+	{ST_L2_5, EV_L2_T200, l2_st5_tout_200},
+	{ST_L2_6, EV_L2_T200, l2_st6_tout_200},
+	{ST_L2_7, EV_L2_T200, l2_st7_tout_200},
+	{ST_L2_8, EV_L2_T200, l2_st8_tout_200},
+	{ST_L2_7, EV_L2_T203, l2_st7_tout_203},
+	{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+	{ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+	{ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+	{ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+	{ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+	{ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error},
+	{ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error},
+	{ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error},
+	{ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+	{ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+	{ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+	{ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove},
+	{ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove},
+	{ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+	{ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da},
+	{ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da},
+	{ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static void
+isdnl2_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *datap;
+	int ret = 1, len;
+	int c = 0;
+
+	switch (pr) {
+		case (PH_DATA | INDICATION):
+			datap = skb->data;
+			len = l2addrsize(&st->l2);
+			if (skb->len > len)
+				datap += len;
+			else {
+				FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'N');
+				FreeSkb(skb);
+				return;
+			}
+			if (!(*datap & 1)) {	/* I-Frame */
+				if(!(c = iframe_error(st, skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb);
+			} else if (IsSFrame(datap, st)) {	/* S-Frame */
+				if(!(c = super_error(st, skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb);
+			} else if (IsUI(datap)) {
+				if(!(c = UI_error(st, skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
+			} else if (IsSABME(datap, st)) {
+				if(!(c = unnum_error(st, skb, CMD)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_SABME, skb);
+			} else if (IsUA(datap)) {
+				if(!(c = unnum_error(st, skb, RSP)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb);
+			} else if (IsDISC(datap)) {
+				if(!(c = unnum_error(st, skb, CMD)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb);
+			} else if (IsDM(datap)) {
+				if(!(c = unnum_error(st, skb, RSP)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb);
+			} else if (IsFRMR(datap)) {
+				if(!(c = FRMR_error(st,skb)))
+					ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb);
+			} else {
+				FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'L');
+				FreeSkb(skb);
+				ret = 0;
+			}
+			if(c) {
+				FreeSkb(skb);
+				FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *)(long)c);
+				ret = 0;
+			}
+			if (ret)
+				FreeSkb(skb);
+			break;
+		case (PH_PULL | CONFIRM):
+			FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+			break;
+		case (PH_PAUSE | INDICATION):
+			test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
+		case (PH_PAUSE | CONFIRM):
+			test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+			break;
+		case (PH_ACTIVATE | CONFIRM):
+		case (PH_ACTIVATE | INDICATION):
+			test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag);
+			if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+				FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg);
+			break;
+		case (PH_DEACTIVATE | INDICATION):
+		case (PH_DEACTIVATE | CONFIRM):
+			test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
+			FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
+			break;
+		default:
+			l2m_debug(&st->l2.l2m, "l2 unknown pr %04x", pr);
+			break;
+	}
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
+				dev_kfree_skb((struct sk_buff *) arg);
+			}
+			break;
+		case (DL_UNIT_DATA | REQUEST):
+			if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) {
+				dev_kfree_skb((struct sk_buff *) arg);
+			}
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg);
+				}
+			} else {
+				if (test_bit(FLG_LAPD, &st->l2.flag) ||
+					test_bit(FLG_ORIG, &st->l2.flag)) {
+					test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
+				}
+				st->l2.l2l1(st, PH_ACTIVATE, NULL);
+			}
+			break;
+		case (DL_RELEASE | REQUEST):
+			if (test_bit(FLG_LAPB, &st->l2.flag)) {
+				st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+			}
+			FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg);
+			break;
+		case (MDL_ASSIGN | REQUEST):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+			break;
+		case (MDL_REMOVE | REQUEST):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
+			break;
+		case (MDL_ERROR | RESPONSE):
+			FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg);
+			break;
+	}
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+	FsmDelTimer(&st->l2.t200, 21);
+	FsmDelTimer(&st->l2.t203, 16);
+	skb_queue_purge(&st->l2.i_queue);
+	skb_queue_purge(&st->l2.ui_queue);
+	ReleaseWin(&st->l2);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l2.debug_id, fmt, args);
+	va_end(args);
+}
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+	spin_lock_init(&st->l2.lock);
+	st->l1.l1l2 = isdnl2_l1l2;
+	st->l3.l3l2 = isdnl2_l3l2;
+
+	skb_queue_head_init(&st->l2.i_queue);
+	skb_queue_head_init(&st->l2.ui_queue);
+	InitWin(&st->l2);
+	st->l2.debug = 0;
+
+	st->l2.l2m.fsm = &l2fsm;
+	if (test_bit(FLG_LAPB, &st->l2.flag))
+		st->l2.l2m.state = ST_L2_4;
+	else
+	st->l2.l2m.state = ST_L2_1;
+	st->l2.l2m.debug = 0;
+	st->l2.l2m.userdata = st;
+	st->l2.l2m.userint = 0;
+	st->l2.l2m.printdebug = l2m_debug;
+	strcpy(st->l2.debug_id, debug_id);
+
+	FsmInitTimer(&st->l2.l2m, &st->l2.t200);
+	FsmInitTimer(&st->l2.l2m, &st->l2.t203);
+}
+
+static void
+transl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+		case (DL_UNIT_DATA | REQUEST):
+			st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+			break;
+	}
+}
+
+void
+setstack_transl2(struct PStack *st)
+{
+	st->l3.l3l2 = transl2_l3l2;
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+int __init
+Isdnl2New(void)
+{
+	l2fsm.state_count = L2_STATE_COUNT;
+	l2fsm.event_count = L2_EVENT_COUNT;
+	l2fsm.strEvent = strL2Event;
+	l2fsm.strState = strL2State;
+	return FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+	FsmFree(&l2fsm);
+}
diff --git a/drivers/isdn/hisax/isdnl2.h b/drivers/isdn/hisax/isdnl2.h
new file mode 100644
index 0000000..0cdab1b
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl2.h
@@ -0,0 +1,26 @@
+/* $Id: isdnl2.h,v 1.3.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * Layer 2 defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define RR     0x01
+#define RNR    0x05
+#define REJ    0x09
+#define SABME  0x6f
+#define SABM   0x2f
+#define DM     0x0f
+#define UI     0x03
+#define DISC   0x43
+#define UA     0x63
+#define FRMR   0x87
+#define XID    0xaf
+
+#define CMD    0
+#define RSP    1
+
+#define LC_FLUSH_WAIT 1
+
diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c
new file mode 100644
index 0000000..f571b5d1
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl3.c
@@ -0,0 +1,610 @@
+/* $Id: isdnl3.c,v 2.22.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl3.h"
+#include <linux/config.h>
+
+const char *l3_revision = "$Revision: 2.22.2.3 $";
+
+static struct Fsm l3fsm;
+
+enum {
+	ST_L3_LC_REL,
+	ST_L3_LC_ESTAB_WAIT,
+	ST_L3_LC_REL_DELAY, 
+	ST_L3_LC_REL_WAIT,
+	ST_L3_LC_ESTAB,
+};
+
+#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1)
+
+static char *strL3State[] =
+{
+	"ST_L3_LC_REL",
+	"ST_L3_LC_ESTAB_WAIT",
+	"ST_L3_LC_REL_DELAY",
+	"ST_L3_LC_REL_WAIT",
+	"ST_L3_LC_ESTAB",
+};
+
+enum {
+	EV_ESTABLISH_REQ,
+	EV_ESTABLISH_IND,
+	EV_ESTABLISH_CNF,
+	EV_RELEASE_REQ,
+	EV_RELEASE_CNF,
+	EV_RELEASE_IND,
+	EV_TIMEOUT,
+};
+
+#define L3_EVENT_COUNT (EV_TIMEOUT+1)
+
+static char *strL3Event[] =
+{
+	"EV_ESTABLISH_REQ",
+	"EV_ESTABLISH_IND",
+	"EV_ESTABLISH_CNF",
+	"EV_RELEASE_REQ",
+	"EV_RELEASE_CNF",
+	"EV_RELEASE_IND",
+	"EV_TIMEOUT",
+};
+
+static void
+l3m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args);
+	va_end(args);
+}
+
+u_char *
+findie(u_char * p, int size, u_char ie, int wanted_set)
+{
+	int l, codeset, maincodeset;
+	u_char *pend = p + size;
+
+	/* skip protocol discriminator, callref and message type */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	p++;
+	codeset = 0;
+	maincodeset = 0;
+	/* while there are bytes left... */
+	while (p < pend) {
+		if ((*p & 0xf0) == 0x90) {
+			codeset = *p & 0x07;
+			if (!(*p & 0x08))
+				maincodeset = codeset;
+		}
+		if (*p & 0x80)
+			p++;
+		else {
+			if (codeset == wanted_set) {
+				if (*p == ie)
+                                  { /* improved length check (Werner Cornelius) */
+                                    if ((pend - p) < 2) 
+                                      return(NULL); 
+                                    if (*(p+1) > (pend - (p+2))) 
+                                      return(NULL); 
+                                    return (p);
+                                  }           
+                                  
+				if (*p > ie)
+					return (NULL);
+			}
+			p++;
+			l = *p++;
+			p += l;
+			codeset = maincodeset;
+		}
+	}
+	return (NULL);
+}
+
+int
+getcallref(u_char * p)
+{
+	int l, cr = 0;
+
+	p++;			/* prot discr */
+	if (*p & 0xfe)		/* wrong callref BRI only 1 octet*/
+		return(-2);
+	l = 0xf & *p++;		/* callref length */
+	if (!l)			/* dummy CallRef */
+		return(-1);
+	cr = *p++;
+	return (cr);
+}
+
+static int OrigCallRef = 0;
+
+int
+newcallref(void)
+{
+	if (OrigCallRef == 127)
+		OrigCallRef = 1;
+	else
+		OrigCallRef++;
+	return (OrigCallRef);
+}
+
+void
+newl3state(struct l3_process *pc, int state)
+{
+	if (pc->debug & L3_DEB_STATE)
+		l3_debug(pc->st, "newstate cr %d %d --> %d", 
+			 pc->callref & 0x7F,
+			 pc->state, state);
+	pc->state = state;
+}
+
+static void
+L3ExpireTimer(struct L3Timer *t)
+{
+	t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
+}
+
+void
+L3InitTimer(struct l3_process *pc, struct L3Timer *t)
+{
+	t->pc = pc;
+	t->tl.function = (void *) L3ExpireTimer;
+	t->tl.data = (long) t;
+	init_timer(&t->tl);
+}
+
+void
+L3DelTimer(struct L3Timer *t)
+{
+	del_timer(&t->tl);
+}
+
+int
+L3AddTimer(struct L3Timer *t,
+	   int millisec, int event)
+{
+	if (timer_pending(&t->tl)) {
+		printk(KERN_WARNING "L3AddTimer: timer already active!\n");
+		return -1;
+	}
+	init_timer(&t->tl);
+	t->event = event;
+	t->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&t->tl);
+	return 0;
+}
+
+void
+StopAllL3Timer(struct l3_process *pc)
+{
+	L3DelTimer(&pc->timer);
+}
+
+struct sk_buff *
+l3_alloc_skb(int len)
+{
+	struct sk_buff *skb;
+
+	if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No skb for D-channel\n");
+		return (NULL);
+	}
+	skb_reserve(skb, MAX_HEADER_LEN);
+	return (skb);
+}
+
+static void
+no_l3_proto(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	HiSax_putstatus(st->l1.hardware, "L3", "no D protocol");
+	if (skb) {
+		dev_kfree_skb(skb);
+	}
+}
+
+static int
+no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic)
+{
+	printk(KERN_WARNING "HiSax: no specific protocol handler for proto %lu\n",ic->arg & 0xFF);
+	return(-1);
+}
+
+#ifdef	CONFIG_HISAX_EURO
+extern void setstack_dss1(struct PStack *st);
+#endif
+
+#ifdef  CONFIG_HISAX_NI1
+extern void setstack_ni1(struct PStack *st);
+#endif
+
+#ifdef	CONFIG_HISAX_1TR6
+extern void setstack_1tr6(struct PStack *st);
+#endif
+
+struct l3_process
+*getl3proc(struct PStack *st, int cr)
+{
+	struct l3_process *p = st->l3.proc;
+
+	while (p)
+		if (p->callref == cr)
+			return (p);
+		else
+			p = p->next;
+	return (NULL);
+}
+
+struct l3_process
+*new_l3_process(struct PStack *st, int cr)
+{
+	struct l3_process *p, *np;
+
+	if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr);
+		return (NULL);
+	}
+	if (!st->l3.proc)
+		st->l3.proc = p;
+	else {
+		np = st->l3.proc;
+		while (np->next)
+			np = np->next;
+		np->next = p;
+	}
+	p->next = NULL;
+	p->debug = st->l3.debug;
+	p->callref = cr;
+	p->state = 0;
+	p->chan = NULL;
+	p->st = st;
+	p->N303 = st->l3.N303;
+	L3InitTimer(p, &p->timer);
+	return (p);
+};
+
+void
+release_l3_process(struct l3_process *p)
+{
+	struct l3_process *np, *pp = NULL;
+
+	if (!p)
+		return;
+	np = p->st->l3.proc;
+	while (np) {
+		if (np == p) {
+			StopAllL3Timer(p);
+			if (pp)
+				pp->next = np->next;
+			else if (!(p->st->l3.proc = np->next) &&
+				!test_bit(FLG_PTP, &p->st->l2.flag)) {
+				if (p->debug)
+					l3_debug(p->st, "release_l3_process: last process");
+				if (!skb_queue_len(&p->st->l3.squeue)) {
+					if (p->debug)
+						l3_debug(p->st, "release_l3_process: release link");
+					if (p->st->protocol != ISDN_PTYPE_NI1)
+						FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL);
+					else
+						FsmEvent(&p->st->l3.l3m, EV_RELEASE_IND, NULL);
+				} else {
+					if (p->debug)
+						l3_debug(p->st, "release_l3_process: not release link");
+				}
+			} 
+			kfree(p);
+			return;
+		}
+		pp = np;
+		np = np->next;
+	}
+	printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref);
+	l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref);
+};
+
+static void
+l3ml3p(struct PStack *st, int pr)
+{
+	struct l3_process *p = st->l3.proc;
+	struct l3_process *np;
+
+	while (p) {
+		/* p might be kfreed under us, so we need to save where we want to go on */
+		np = p->next;
+		st->l3.l3ml3(st, pr, p);
+		p = np;
+	}
+}
+
+void
+setstack_l3dc(struct PStack *st, struct Channel *chanp)
+{
+	char tmp[64];
+
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+        FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer);
+	strcpy(st->l3.debug_id, "L3DC ");
+	st->lli.l4l3_proto = no_l3_proto_spec;
+
+#ifdef	CONFIG_HISAX_EURO
+	if (st->protocol == ISDN_PTYPE_EURO) {
+		setstack_dss1(st);
+	} else
+#endif
+#ifdef  CONFIG_HISAX_NI1
+	if (st->protocol == ISDN_PTYPE_NI1) {
+		setstack_ni1(st);
+	} else
+#endif
+#ifdef	CONFIG_HISAX_1TR6
+	if (st->protocol == ISDN_PTYPE_1TR6) {
+		setstack_1tr6(st);
+	} else
+#endif
+	if (st->protocol == ISDN_PTYPE_LEASED) {
+		st->lli.l4l3 = no_l3_proto;
+		st->l2.l2l3 = no_l3_proto;
+                st->l3.l3ml3 = no_l3_proto;
+		printk(KERN_INFO "HiSax: Leased line mode\n");
+	} else {
+		st->lli.l4l3 = no_l3_proto;
+		st->l2.l2l3 = no_l3_proto;
+                st->l3.l3ml3 = no_l3_proto;
+		sprintf(tmp, "protocol %s not supported",
+			(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
+			(st->protocol == ISDN_PTYPE_EURO) ? "euro" :
+			(st->protocol == ISDN_PTYPE_NI1) ? "ni1" :
+			"unknown");
+		printk(KERN_WARNING "HiSax: %s\n", tmp);
+		st->protocol = -1;
+	}
+}
+
+void
+isdnl3_trans(struct PStack *st, int pr, void *arg) {
+	st->l3.l3l2(st, pr, arg);
+}
+
+void
+releasestack_isdnl3(struct PStack *st)
+{
+	while (st->l3.proc)
+		release_l3_process(st->l3.proc);
+	if (st->l3.global) {
+		StopAllL3Timer(st->l3.global);
+		kfree(st->l3.global);
+		st->l3.global = NULL;
+	}
+	FsmDelTimer(&st->l3.l3m_timer, 54);
+	skb_queue_purge(&st->l3.squeue);
+}
+
+void
+setstack_l3bc(struct PStack *st, struct Channel *chanp)
+{
+
+	st->l3.proc   = NULL;
+	st->l3.global = NULL;
+	skb_queue_head_init(&st->l3.squeue);
+	st->l3.l3m.fsm = &l3fsm;
+	st->l3.l3m.state = ST_L3_LC_REL;
+	st->l3.l3m.debug = 1;
+	st->l3.l3m.userdata = st;
+	st->l3.l3m.userint = 0;
+	st->l3.l3m.printdebug = l3m_debug;
+	strcpy(st->l3.debug_id, "L3BC ");
+	st->lli.l4l3 = isdnl3_trans;
+}
+
+#define DREL_TIMER_VALUE 40000
+
+static void
+lc_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
+	st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lc_connect(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int dequeued = 0;
+
+	FsmChangeState(fi, ST_L3_LC_ESTAB);
+	while ((skb = skb_dequeue(&st->l3.squeue))) {
+		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+		dequeued++;
+	}
+	if ((!st->l3.proc) &&  dequeued) {
+		if (st->l3.debug)
+			l3_debug(st, "lc_connect: release link");
+		FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+	} else
+		l3ml3p(st, DL_ESTABLISH | INDICATION);
+}
+
+static void
+lc_connected(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int dequeued = 0;
+
+	FsmDelTimer(&st->l3.l3m_timer, 51);
+	FsmChangeState(fi, ST_L3_LC_ESTAB);
+	while ((skb = skb_dequeue(&st->l3.squeue))) {
+		st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+		dequeued++;
+	}
+	if ((!st->l3.proc) &&  dequeued) {
+		if (st->l3.debug)
+			l3_debug(st, "lc_connected: release link");
+		FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+	} else
+		l3ml3p(st, DL_ESTABLISH | CONFIRM);
+}
+
+static void
+lc_start_delay(struct FsmInst *fi, int event, void *arg)
+{
+       struct PStack *st = fi->userdata;
+
+       FsmChangeState(fi, ST_L3_LC_REL_DELAY);
+       FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
+}
+
+static void
+lc_start_delay_check(struct FsmInst *fi, int event, void *arg)
+/* 20/09/00 - GE timer not user for NI-1 as layer 2 should stay up */
+{
+       struct PStack *st = fi->userdata;
+
+       FsmChangeState(fi, ST_L3_LC_REL_DELAY);
+       /* 19/09/00 - GE timer not user for NI-1 */
+       if (st->protocol != ISDN_PTYPE_NI1) 
+       		FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
+}
+
+static void
+lc_release_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (test_bit(FLG_L2BLOCK, &st->l2.flag)) {
+		if (st->l3.debug)
+			l3_debug(st, "lc_release_req: l2 blocked");
+		/* restart release timer */
+		FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51);
+	} else {
+		FsmChangeState(fi, ST_L3_LC_REL_WAIT);
+		st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+	}
+}
+
+static void
+lc_release_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmDelTimer(&st->l3.l3m_timer, 52);
+	FsmChangeState(fi, ST_L3_LC_REL);
+	skb_queue_purge(&st->l3.squeue);
+	l3ml3p(st, DL_RELEASE | INDICATION);
+}
+
+static void
+lc_release_cnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	FsmChangeState(fi, ST_L3_LC_REL);
+	skb_queue_purge(&st->l3.squeue);
+	l3ml3p(st, DL_RELEASE | CONFIRM);
+}
+
+
+/* *INDENT-OFF* */
+static struct FsmNode L3FnList[] __initdata =
+{
+	{ST_L3_LC_REL,		EV_ESTABLISH_REQ,	lc_activate},
+	{ST_L3_LC_REL,		EV_ESTABLISH_IND,	lc_connect},
+	{ST_L3_LC_REL,		EV_ESTABLISH_CNF,	lc_connect},
+	{ST_L3_LC_ESTAB_WAIT,	EV_ESTABLISH_CNF,	lc_connected},
+	{ST_L3_LC_ESTAB_WAIT,	EV_RELEASE_REQ,		lc_start_delay},
+	{ST_L3_LC_ESTAB_WAIT,	EV_RELEASE_IND,		lc_release_ind},
+	{ST_L3_LC_ESTAB,	EV_RELEASE_IND,		lc_release_ind},
+	{ST_L3_LC_ESTAB,	EV_RELEASE_REQ,		lc_start_delay_check},
+        {ST_L3_LC_REL_DELAY,    EV_RELEASE_IND,         lc_release_ind},
+        {ST_L3_LC_REL_DELAY,    EV_ESTABLISH_REQ,       lc_connected},
+        {ST_L3_LC_REL_DELAY,    EV_TIMEOUT,             lc_release_req},
+	{ST_L3_LC_REL_WAIT,	EV_RELEASE_CNF,		lc_release_cnf},
+	{ST_L3_LC_REL_WAIT,	EV_ESTABLISH_REQ,	lc_activate},
+};
+/* *INDENT-ON* */
+
+#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode))
+
+void
+l3_msg(struct PStack *st, int pr, void *arg)
+{
+	switch (pr) {
+		case (DL_DATA | REQUEST):
+			if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
+				st->l3.l3l2(st, pr, arg);
+			} else {
+				struct sk_buff *skb = arg;
+
+				skb_queue_tail(&st->l3.squeue, skb);
+				FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); 
+			}
+			break;
+		case (DL_ESTABLISH | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL);
+			break;
+		case (DL_ESTABLISH | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL);
+			break;
+		case (DL_RELEASE | INDICATION):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL);
+			break;
+		case (DL_RELEASE | CONFIRM):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL);
+			break;
+		case (DL_RELEASE | REQUEST):
+			FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+			break;
+	}
+}
+
+int __init
+Isdnl3New(void)
+{
+	l3fsm.state_count = L3_STATE_COUNT;
+	l3fsm.event_count = L3_EVENT_COUNT;
+	l3fsm.strEvent = strL3Event;
+	l3fsm.strState = strL3State;
+	return FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+}
+
+void
+Isdnl3Free(void)
+{
+	FsmFree(&l3fsm);
+}
diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h
new file mode 100644
index 0000000..1dbe029
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl3.h
@@ -0,0 +1,37 @@
+/* $Id: isdnl3.h,v 2.6.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define SBIT(state) (1<<state)
+#define ALL_STATES  0x03ffffff
+
+#define PROTO_DIS_EURO	0x08
+
+#define L3_DEB_WARN	0x01
+#define L3_DEB_PROTERR	0x02
+#define L3_DEB_STATE	0x04
+#define L3_DEB_CHARGE	0x08
+#define L3_DEB_CHECK	0x10
+#define L3_DEB_SI	0x20
+
+struct stateentry {
+	int state;
+	int primitive;
+	void (*rout) (struct l3_process *, u8, void *);
+};
+
+#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args)
+
+extern void newl3state(struct l3_process *pc, int state);
+extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t);
+extern void L3DelTimer(struct L3Timer *t);
+extern int L3AddTimer(struct L3Timer *t, int millisec, int event);
+extern void StopAllL3Timer(struct l3_process *pc);
+extern struct sk_buff *l3_alloc_skb(int len);
+extern struct l3_process *new_l3_process(struct PStack *st, int cr);
+extern void release_l3_process(struct l3_process *p);
+extern struct l3_process *getl3proc(struct PStack *st, int cr);
+extern void l3_msg(struct PStack *st, int pr, void *arg);
diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c
new file mode 100644
index 0000000..af5171d
--- /dev/null
+++ b/drivers/isdn/hisax/isurf.c
@@ -0,0 +1,306 @@
+/* $Id: isurf.c,v 1.12.2.4 2004/01/13 21:46:03 keil Exp $
+ *
+ * low level stuff for Siemens I-Surf/I-Talk cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+static const char *ISurf_revision = "$Revision: 1.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISURF_ISAR_RESET	1
+#define ISURF_ISAC_RESET	2
+#define ISURF_ISAR_EA		4
+#define ISURF_ARCOFI_RESET	8
+#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET)
+
+#define ISURF_ISAR_OFFSET	0
+#define ISURF_ISAC_OFFSET	0x100
+#define ISURF_IOMEM_SIZE	0x400
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readb(cs->hw.isurf.isac + offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeb(value, cs->hw.isurf.isac + offset); mb();
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	register int i;
+	for (i = 0; i < size; i++)
+		data[i] = readb(cs->hw.isurf.isac);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	register int i;
+	for (i = 0; i < size; i++){
+		writeb(data[i], cs->hw.isurf.isac);mb();
+	}
+}
+
+/* ISAR access routines
+ * mode = 0 access with IRQ on
+ * mode = 1 access with IRQ off
+ * mode = 2 access with IRQ off and using last offset
+ */
+  
+static u_char
+ReadISAR(struct IsdnCardState *cs, int mode, u_char offset)
+{	
+	return(readb(cs->hw.isurf.isar + offset));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+	writeb(value, cs->hw.isurf.isar + offset);mb();
+}
+
+static irqreturn_t
+isurf_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	int cnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
+      Start_ISAR:
+	if (val & ISAR_IRQSTA)
+		isar_int_main(cs);
+	val = readb(cs->hw.isurf.isac + ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
+	if ((val & ISAR_IRQSTA) && --cnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "ISAR IntStat after IntRoutine");
+		goto Start_ISAR;
+	}
+	val = readb(cs->hw.isurf.isac + ISAC_ISTA);
+	if (val && --cnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (!cnt)
+		printk(KERN_WARNING "ISurf IRQ LOOP\n");
+
+	writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
+	writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK);mb();
+	writeb(0, cs->hw.isurf.isac + ISAC_MASK);mb();
+	writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_isurf(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.isurf.reset, 1);
+	iounmap(cs->hw.isurf.isar);
+	release_mem_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
+}
+
+static void
+reset_isurf(struct IsdnCardState *cs, u_char chips)
+{
+	printk(KERN_INFO "ISurf: resetting card\n");
+
+	byteout(cs->hw.isurf.reset, chips); /* Reset On */
+	mdelay(10);
+	byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
+	mdelay(10);
+}
+
+static int
+ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_isurf(cs, ISURF_RESET);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_isurf(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_isurf(cs, ISURF_RESET);
+			clear_pending_isac_ints(cs);
+			writeb(0, cs->hw.isurf.isar+ISAR_IRQBIT);mb();
+			initisac(cs);
+			initisar(cs);
+			/* Reenable ISAC IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			/* RESET Receiver and Transmitter */
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static int
+isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
+	int ret;
+	u_long flags;
+
+	if ((ic->command == ISDN_CMD_IOCTL) && (ic->arg == 9)) {
+		ret = isar_auxcmd(cs, ic);
+		spin_lock_irqsave(&cs->lock, flags);
+		if (!ret) {
+			reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET |
+				ISURF_ARCOFI_RESET);
+			initisac(cs);
+			cs->writeisac(cs, ISAC_MASK, 0);
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
+		}
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return(ret);
+	}
+	return(isar_auxcmd(cs, ic));
+}
+
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_c __initdata = NULL;
+#endif
+
+int __init
+setup_isurf(struct IsdnCard *card)
+{
+	int ver;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, ISurf_revision);
+	printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp));
+	
+ 	if (cs->typ != ISDN_CTYPE_ISURF)
+ 		return(0);
+	if (card->para[1] && card->para[2]) {
+		cs->hw.isurf.reset = card->para[1];
+		cs->hw.isurf.phymem = card->para[2];
+		cs->irq = card->para[0];
+	} else {
+#ifdef __ISAPNP__
+		if (isapnp_present()) {
+			struct pnp_dev *pnp_d = NULL;
+			int err;
+
+			cs->subtyp = 0;
+			if ((pnp_c = pnp_find_card(
+				ISAPNP_VENDOR('S', 'I', 'E'),
+				ISAPNP_FUNCTION(0x0010), pnp_c))) {
+				if (!(pnp_d = pnp_find_dev(pnp_c,
+					ISAPNP_VENDOR('S', 'I', 'E'),
+					ISAPNP_FUNCTION(0x0010), pnp_d))) {
+					printk(KERN_ERR "ISurfPnP: PnP error card found, no device\n");
+					return (0);
+				}
+				pnp_disable_dev(pnp_d);
+				err = pnp_activate_dev(pnp_d);
+				cs->hw.isurf.reset = pnp_port_start(pnp_d, 0);
+				cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1);
+				cs->irq = pnp_irq(pnp_d, 0);
+				if (!cs->irq || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) {
+					printk(KERN_ERR "ISurfPnP:some resources are missing %d/%x/%lx\n",
+						cs->irq, cs->hw.isurf.reset, cs->hw.isurf.phymem);
+					pnp_disable_dev(pnp_d);
+					return(0);
+				}
+			} else {
+				printk(KERN_INFO "ISurfPnP: no ISAPnP card found\n");
+				return(0);
+			}
+		} else {
+			printk(KERN_INFO "ISurfPnP: no ISAPnP bus found\n");
+			return(0);
+		}
+#else
+		printk(KERN_WARNING "HiSax: %s port/mem not set\n",
+			CardType[card->typ]);
+		return (0);
+#endif
+	}
+	if (!request_region(cs->hw.isurf.reset, 1, "isurf isdn")) {
+		printk(KERN_WARNING
+			"HiSax: %s config port %x already in use\n",
+			CardType[card->typ],
+			cs->hw.isurf.reset);
+			return (0);
+	}
+	if (!request_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE, "isurf iomem")) {
+		printk(KERN_WARNING
+			"HiSax: %s memory region %lx-%lx already in use\n",
+			CardType[card->typ],
+			cs->hw.isurf.phymem,
+			cs->hw.isurf.phymem + ISURF_IOMEM_SIZE);
+		release_region(cs->hw.isurf.reset, 1);
+		return (0);
+	}
+	cs->hw.isurf.isar = ioremap(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
+	cs->hw.isurf.isac = cs->hw.isurf.isar + ISURF_ISAC_OFFSET;
+	printk(KERN_INFO
+	       "ISurf: defined at 0x%x 0x%lx IRQ %d\n",
+	       cs->hw.isurf.reset,
+	       cs->hw.isurf.phymem,
+	       cs->irq);
+
+	setup_isac(cs);
+	cs->cardmsg = &ISurf_card_msg;
+	cs->irq_func = &isurf_interrupt;
+	cs->auxcmd = &isurf_auxcmd;
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r;
+	cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r;
+	test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+	ISACVersion(cs, "ISurf:");
+	cs->BC_Read_Reg = &ReadISAR;
+	cs->BC_Write_Reg = &WriteISAR;
+	cs->BC_Send_Data = &isar_fill_fifo;
+	ver = ISARVersion(cs, "ISurf:");
+	if (ver < 0) {
+		printk(KERN_WARNING
+			"ISurf: wrong ISAR version (ret = %d)\n", ver);
+		release_io_isurf(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c
new file mode 100644
index 0000000..b843b75
--- /dev/null
+++ b/drivers/isdn/hisax/ix1_micro.c
@@ -0,0 +1,318 @@
+/* $Id: ix1_micro.c,v 2.12.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for ITK ix1-micro Rev.2 isdn cards
+ * derived from the original file teles3.c from Karsten Keil
+ *
+ * Author       Klaus-Peter Nischke
+ * Copyright    by Klaus-Peter Nischke, ITK AG
+ *                                   <klaus@nischke.do.eunet.de>
+ *              by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Klaus-Peter Nischke
+ * Deusener Str. 287
+ * 44369 Dortmund
+ * Germany
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *ix1_revision = "$Revision: 2.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SPECIAL_PORT_OFFSET 3
+
+#define ISAC_COMMAND_OFFSET 2
+#define ISAC_DATA_OFFSET 0
+#define HSCX_COMMAND_OFFSET 2
+#define HSCX_DATA_OFFSET 1
+
+#define TIMEOUT 50
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.ix1.hscx_ale,
+			cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.ix1.hscx_ale,
+		 cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \
+		cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+ix1micro_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0);
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0);
+	writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_ix1micro(struct IsdnCardState *cs)
+{
+	if (cs->hw.ix1.cfg_reg)
+		release_region(cs->hw.ix1.cfg_reg, 4);
+}
+
+static void
+ix1_reset(struct IsdnCardState *cs)
+{
+	int cnt;
+
+	/* reset isac */
+	cnt = 3 * (HZ / 10) + 1;
+	while (cnt--) {
+		byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1);
+		HZDELAY(1);	/* wait >=10 ms */
+	}
+	byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0);
+}
+
+static int
+ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			ix1_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_ix1micro(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			ix1_reset(cs);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id itk_ids[] __initdata = {
+	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
+	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25), 
+	  (unsigned long) "ITK micro 2" },
+	{ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
+	  ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29), 
+	  (unsigned long) "ITK micro 2." },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __initdata = &itk_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+
+int __init
+setup_ix1micro(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, ix1_revision);
+	printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_IX1MICROR2)
+		return (0);
+
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1]) {
+						printk(KERN_ERR "ITK PnP:some resources are missing %ld/%lx\n",
+							card->para[0], card->para[1]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "ITK PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "ITK PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	/* IO-Ports */
+	cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET;
+	cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET;
+	cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET;
+	cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET;
+	cs->hw.ix1.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (cs->hw.ix1.cfg_reg) {
+		if (!request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg")) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       cs->hw.ix1.cfg_reg,
+			       cs->hw.ix1.cfg_reg + 4);
+			return (0);
+		}
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d io:0x%X\n",
+		CardType[cs->typ], cs->irq, cs->hw.ix1.cfg_reg);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &ix1_card_msg;
+	cs->irq_func = &ix1micro_interrupt;
+	ISACVersion(cs, "ix1-Micro:");
+	if (HscxVersion(cs, "ix1-Micro:")) {
+		printk(KERN_WARNING
+		    "ix1-Micro: wrong HSCX versions check IO address\n");
+		release_io_ix1micro(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c
new file mode 100644
index 0000000..f05d527
--- /dev/null
+++ b/drivers/isdn/hisax/jade.c
@@ -0,0 +1,318 @@
+/* $Id: jade.c,v 1.9.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * JADE stuff (derived from original hscx.c)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hscx.h"
+#include "jade.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+
+int __init
+JadeVersion(struct IsdnCardState *cs, char *s)
+{
+    int ver,i;
+    int to = 50;
+    cs->BC_Write_Reg(cs, -1, 0x50, 0x19);
+    i=0;
+    while (to) {
+    	udelay(1);
+	ver = cs->BC_Read_Reg(cs, -1, 0x60);
+	to--;
+	if (ver)
+    	    break;
+	if (!to) {
+	    printk(KERN_INFO "%s JADE version not obtainable\n", s);
+    	    return (0);
+        }
+    }
+    /* Wait for the JADE */
+    udelay(10);
+    /* Read version */
+    ver = cs->BC_Read_Reg(cs, -1, 0x60);
+    printk(KERN_INFO "%s JADE version: %d\n", s, ver);
+    return (1);
+}
+
+/* Write to indirect accessible jade register set */
+static void
+jade_write_indirect(struct IsdnCardState *cs, u_char reg, u_char value)
+{
+    int to = 50;
+    u_char ret;
+
+    /* Write the data */
+    cs->BC_Write_Reg(cs, -1, COMM_JADE+1, value);
+    /* Say JADE we wanna write indirect reg 'reg' */
+    cs->BC_Write_Reg(cs, -1, COMM_JADE, reg);
+    to = 50;
+    /* Wait for RDY goes high */
+    while (to) {
+    	udelay(1);
+	ret = cs->BC_Read_Reg(cs, -1, COMM_JADE);
+	to--;
+	if (ret & 1)
+	    /* Got acknowledge */
+	    break;
+	if (!to) {
+    	    printk(KERN_INFO "Can not see ready bit from JADE DSP (reg=0x%X, value=0x%X)\n", reg, value);
+	    return;
+	}
+    }
+}
+
+
+
+void
+modejade(struct BCState *bcs, int mode, int bc)
+{
+    struct IsdnCardState *cs = bcs->cs;
+    int jade = bcs->hw.hscx.hscx;
+
+    if (cs->debug & L1_DEB_HSCX) {
+	char tmp[40];
+	sprintf(tmp, "jade %c mode %d ichan %d",
+		'A' + jade, mode, bc);
+	debugl1(cs, tmp);
+    }
+    bcs->mode = mode;
+    bcs->channel = bc;
+	
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (mode == L1_MODE_TRANS ? jadeMODE_TMO:0x00));
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR0, (jadeCCR0_PU|jadeCCR0_ITF));
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR1, 0x00);
+
+    jade_write_indirect(cs, jade_HDLC1SERRXPATH, 0x08);
+    jade_write_indirect(cs, jade_HDLC2SERRXPATH, 0x08);
+    jade_write_indirect(cs, jade_HDLC1SERTXPATH, 0x00);
+    jade_write_indirect(cs, jade_HDLC2SERTXPATH, 0x00);
+
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_XCCR, 0x07);
+    cs->BC_Write_Reg(cs, jade, jade_HDLC_RCCR, 0x07);
+
+    if (bc == 0) {
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x00);
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x00);
+    } else {
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x04);
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x04);
+    }
+    switch (mode) {
+	case (L1_MODE_NULL):
+		cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, jadeMODE_TMO);
+		break;
+	case (L1_MODE_TRANS):
+		cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_TMO|jadeMODE_RAC|jadeMODE_XAC));
+		break;
+	case (L1_MODE_HDLC):
+		cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_RAC|jadeMODE_XAC));
+		break;
+    }
+    if (mode) {
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_RCMD, (jadeRCMD_RRES|jadeRCMD_RMC));
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_XCMD, jadeXCMD_XRES);
+	/* Unmask ints */
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0xF8);
+    }
+    else
+	/* Mask ints */
+	cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0x00);
+}
+
+static void
+jade_l2l1(struct PStack *st, int pr, void *arg)
+{
+    struct BCState *bcs = st->l1.bcs;
+    struct sk_buff *skb = arg;
+    u_long flags;
+
+    switch (pr) {
+	case (PH_DATA | REQUEST):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		if (bcs->tx_skb) {
+			skb_queue_tail(&bcs->squeue, skb);
+		} else {
+			bcs->tx_skb = skb;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.hscx.count = 0;
+			bcs->cs->BC_Send_Data(bcs);
+		}
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		break;
+	case (PH_PULL | INDICATION):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		if (bcs->tx_skb) {
+			printk(KERN_WARNING "jade_l2l1: this shouldn't happen\n");
+		} else {
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->tx_skb = skb;
+			bcs->hw.hscx.count = 0;
+			bcs->cs->BC_Send_Data(bcs);
+		}
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		break;
+	case (PH_PULL | REQUEST):
+		if (!bcs->tx_skb) {
+		    test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		    st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+		} else
+		    test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+		break;
+	case (PH_ACTIVATE | REQUEST):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+		modejade(bcs, st->l1.mode, st->l1.bc);
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		l1_msg_b(st, pr, arg);
+		break;
+	case (PH_DEACTIVATE | REQUEST):
+		l1_msg_b(st, pr, arg);
+		break;
+	case (PH_DEACTIVATE | CONFIRM):
+		spin_lock_irqsave(&bcs->cs->lock, flags);
+		test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		modejade(bcs, 0, st->l1.bc);
+		spin_unlock_irqrestore(&bcs->cs->lock, flags);
+		st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+		break;
+    }
+}
+
+void
+close_jadestate(struct BCState *bcs)
+{
+    modejade(bcs, 0, bcs->channel);
+    if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+	if (bcs->hw.hscx.rcvbuf) {
+		kfree(bcs->hw.hscx.rcvbuf);
+		bcs->hw.hscx.rcvbuf = NULL;
+	}
+	if (bcs->blog) {
+		kfree(bcs->blog);
+		bcs->blog = NULL;
+	}
+	skb_queue_purge(&bcs->rqueue);
+	skb_queue_purge(&bcs->squeue);
+	if (bcs->tx_skb) {
+		dev_kfree_skb_any(bcs->tx_skb);
+		bcs->tx_skb = NULL;
+		test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	}
+    }
+}
+
+static int
+open_jadestate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for hscx.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+				"HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.hscx.rcvbuf);
+			bcs->hw.hscx.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.hscx.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+
+int
+setstack_jade(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_jadestate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = jade_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void __init
+clear_pending_jade_ints(struct IsdnCardState *cs)
+{
+	int val;
+	char tmp[64];
+
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00);
+
+	val = cs->BC_Read_Reg(cs, 1, jade_HDLC_ISR);
+	sprintf(tmp, "jade B ISTA %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 0, jade_HDLC_ISR);
+	sprintf(tmp, "jade A ISTA %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 1, jade_HDLC_STAR);
+	sprintf(tmp, "jade B STAR %x", val);
+	debugl1(cs, tmp);
+	val = cs->BC_Read_Reg(cs, 0, jade_HDLC_STAR);
+	sprintf(tmp, "jade A STAR %x", val);
+	debugl1(cs, tmp);
+	/* Unmask ints */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0xF8);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0xF8);
+}
+
+void __init
+initjade(struct IsdnCardState *cs)
+{
+	cs->bcs[0].BC_SetStack = setstack_jade;
+	cs->bcs[1].BC_SetStack = setstack_jade;
+	cs->bcs[0].BC_Close = close_jadestate;
+	cs->bcs[1].BC_Close = close_jadestate;
+	cs->bcs[0].hw.hscx.hscx = 0;
+	cs->bcs[1].hw.hscx.hscx = 1;
+
+	/* Stop DSP audio tx/rx */
+	jade_write_indirect(cs, 0x11, 0x0f);
+	jade_write_indirect(cs, 0x17, 0x2f);
+
+	/* Transparent Mode, RxTx inactive, No Test, No RFS/TFS */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_MODE, jadeMODE_TMO);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_MODE, jadeMODE_TMO);
+	/* Power down, 1-Idle, RxTx least significant bit first */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_CCR0, 0x00);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_CCR0, 0x00);
+	/* Mask all interrupts */
+	cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR,  0x00);
+	cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR,  0x00);
+	/* Setup host access to hdlc controller */
+	jade_write_indirect(cs, jade_HDLCCNTRACCESS, (jadeINDIRECT_HAH1|jadeINDIRECT_HAH2));
+	/* Unmask HDLC int (don´t forget DSP int later on)*/
+	cs->BC_Write_Reg(cs, -1,jade_INT, (jadeINT_HDLC1|jadeINT_HDLC2));
+
+	/* once again TRANSPARENT */	
+	modejade(cs->bcs, 0, 0);
+	modejade(cs->bcs + 1, 0, 0);
+}
+
diff --git a/drivers/isdn/hisax/jade.h b/drivers/isdn/hisax/jade.h
new file mode 100644
index 0000000..fa29444
--- /dev/null
+++ b/drivers/isdn/hisax/jade.h
@@ -0,0 +1,135 @@
+/* $Id: jade.h,v 1.5.2.3 2004/01/14 16:04:48 keil Exp $
+ *
+ * JADE specific defines
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+#ifndef	__JADE_H__
+#define	__JADE_H__
+
+/* Special registers for access to indirect accessible JADE regs */
+#define	DIRECT_IO_JADE	0x0000	/* Jade direct io access area */
+#define	COMM_JADE	0x0040	/* Jade communication area */	   	
+
+/********************************************************************/
+/* JADE-HDLC registers         									    */
+/********************************************************************/
+#define jade_HDLC_RFIFO	   				0x00				   /* R */
+#define jade_HDLC_XFIFO	   				0x00				   /* W */
+
+#define	jade_HDLC_STAR	   				0x20				   /* R */
+	#define	jadeSTAR_XDOV				0x80
+	#define	jadeSTAR_XFW 				0x40 /* Does not work*/
+	#define	jadeSTAR_XCEC 				0x20
+	#define	jadeSTAR_RCEC				0x10
+	#define	jadeSTAR_BSY 				0x08
+	#define	jadeSTAR_RNA 				0x04
+	#define	jadeSTAR_STR 				0x02
+	#define	jadeSTAR_STX				0x01
+
+#define	jade_HDLC_XCMD	   				0x20				   /* W */
+	#define	jadeXCMD_XF				0x80
+	#define	jadeXCMD_XME				0x40
+	#define	jadeXCMD_XRES				0x20
+	#define	jadeXCMD_STX				0x01
+
+#define	jade_HDLC_RSTA	   				0x21				   /* R */
+    #define	jadeRSTA_VFR				0x80
+    #define	jadeRSTA_RDO				0x40
+    #define	jadeRSTA_CRC				0x20
+    #define	jadeRSTA_RAB				0x10
+    #define	jadeRSTA_MASK			   	0xF0
+
+#define	jade_HDLC_MODE					0x22				   /* RW*/
+    #define	jadeMODE_TMO				0x80
+    #define	jadeMODE_RAC				0x40
+    #define	jadeMODE_XAC				0x20
+    #define	jadeMODE_TLP				0x10
+    #define	jadeMODE_ERFS				0x02
+    #define	jadeMODE_ETFS				0x01
+
+#define	jade_HDLC_RBCH					0x24				   /* R */
+
+#define	jade_HDLC_RBCL	 				0x25				   /* R */
+#define	jade_HDLC_RCMD	 				0x25				   /* W */
+	#define	jadeRCMD_RMC 				0x80
+	#define	jadeRCMD_RRES				0x40
+	#define	jadeRCMD_RMD				0x20
+	#define	jadeRCMD_STR				0x02
+
+#define	jade_HDLC_CCR0					0x26				   /* RW*/
+	#define	jadeCCR0_PU  				0x80
+	#define	jadeCCR0_ITF				0x40
+	#define	jadeCCR0_C32				0x20
+	#define	jadeCCR0_CRL				0x10
+	#define	jadeCCR0_RCRC				0x08
+	#define	jadeCCR0_XCRC				0x04
+	#define	jadeCCR0_RMSB				0x02
+	#define	jadeCCR0_XMSB				0x01
+
+#define	jade_HDLC_CCR1					0x27				   /* RW*/
+    #define	jadeCCR1_RCS0				0x80
+    #define	jadeCCR1_RCONT				0x40
+    #define	jadeCCR1_RFDIS				0x20
+    #define	jadeCCR1_XCS0				0x10
+    #define	jadeCCR1_XCONT				0x08
+    #define	jadeCCR1_XFDIS				0x04
+
+#define	jade_HDLC_TSAR					0x28				   /* RW*/
+#define	jade_HDLC_TSAX					0x29				   /* RW*/
+#define	jade_HDLC_RCCR					0x2A				   /* RW*/
+#define	jade_HDLC_XCCR					0x2B				   /* RW*/
+
+#define	jade_HDLC_ISR 					0x2C				   /* R */
+#define	jade_HDLC_IMR 					0x2C				   /* W */
+	#define	jadeISR_RME					0x80
+	#define	jadeISR_RPF					0x40
+	#define	jadeISR_RFO					0x20
+	#define	jadeISR_XPR					0x10
+	#define	jadeISR_XDU					0x08
+	#define	jadeISR_ALLS				0x04
+
+#define jade_INT            			0x75
+    #define jadeINT_HDLC1   			0x02
+    #define jadeINT_HDLC2   			0x01
+    #define jadeINT_DSP				0x04
+#define jade_INTR            			0x70
+
+/********************************************************************/
+/* Indirect accessible JADE registers of common interest		   	*/
+/********************************************************************/
+#define	jade_CHIPVERSIONNR				0x00 /* Does not work*/
+
+#define	jade_HDLCCNTRACCESS				0x10		
+	#define	jadeINDIRECT_HAH1			0x02
+	#define	jadeINDIRECT_HAH2			0x01
+
+#define	jade_HDLC1SERRXPATH				0x1D
+#define	jade_HDLC1SERTXPATH				0x1E
+#define	jade_HDLC2SERRXPATH				0x1F
+#define	jade_HDLC2SERTXPATH				0x20
+	#define	jadeINDIRECT_SLIN1			0x10
+	#define	jadeINDIRECT_SLIN0			0x08
+	#define	jadeINDIRECT_LMOD1			0x04
+	#define	jadeINDIRECT_LMOD0			0x02
+	#define	jadeINDIRECT_HHR			0x01
+	#define	jadeINDIRECT_HHX			0x01
+
+#define	jade_RXAUDIOCH1CFG				0x11
+#define	jade_RXAUDIOCH2CFG				0x14
+#define	jade_TXAUDIOCH1CFG				0x17
+#define	jade_TXAUDIOCH2CFG				0x1A
+
+extern int JadeVersion(struct IsdnCardState *cs, char *s);
+extern void modejade(struct BCState *bcs, int mode, int bc);
+extern void clear_pending_jade_ints(struct IsdnCardState *cs);
+extern void initjade(struct IsdnCardState *cs);
+
+#endif	/* __JADE_H__ */
diff --git a/drivers/isdn/hisax/jade_irq.c b/drivers/isdn/hisax/jade_irq.c
new file mode 100644
index 0000000..0856340
--- /dev/null
+++ b/drivers/isdn/hisax/jade_irq.c
@@ -0,0 +1,236 @@
+/* $Id: jade_irq.c,v 1.7.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Low level JADE IRQ stuff (derived from original hscx_irq.c)
+ *
+ * Author       Roland Klabunde
+ * Copyright    by Roland Klabunde   <R.Klabunde@Berkom.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int jade, int reg)
+{
+  	int to = 50;
+  	int mask = (reg == jade_HDLC_XCMD ? jadeSTAR_XCEC : jadeSTAR_RCEC);
+  	while ((READJADE(cs, jade, jade_HDLC_STAR) & mask) && to) {
+  		udelay(1);
+  		to--;
+  	}
+  	if (!to)
+  		printk(KERN_WARNING "HiSax: waitforCEC (jade) timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int jade)
+{
+  	/* Does not work on older jade versions, don't care */
+}
+
+static inline void
+WriteJADECMDR(struct IsdnCardState *cs, int jade, int reg, u_char data)
+{
+	waitforCEC(cs, jade, reg);
+	WRITEJADE(cs, jade, reg, data);
+}
+
+
+
+static void
+jade_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "jade_empty_fifo");
+
+	if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "jade_empty_fifo: incoming packet too large");
+		WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC);
+		bcs->hw.hscx.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+	bcs->hw.hscx.rcvidx += count;
+	READJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "jade_empty_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+jade_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	int fifo_size = 32;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "jade_fill_fifo");
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > fifo_size) {
+		more = !0;
+		count = fifo_size;
+	} else
+		count = bcs->tx_skb->len;
+
+	waitforXFW(cs, bcs->hw.hscx.hscx);
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.hscx.count += count;
+	WRITEJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+	WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, more ? jadeXCMD_XF : (jadeXCMD_XF|jadeXCMD_XME));
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "jade_fill_fifo %c cnt %d",
+			     bcs->hw.hscx.hscx ? 'B' : 'A', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+
+static inline void
+jade_interrupt(struct IsdnCardState *cs, u_char val, u_char jade)
+{
+	u_char r;
+	struct BCState *bcs = cs->bcs + jade;
+	struct sk_buff *skb;
+	int fifo_size = 32;
+	int count;
+	int i_jade = (int) jade; /* To satisfy the compiler */
+	
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+		return;
+
+	if (val & 0x80) {	/* RME */
+		r = READJADE(cs, i_jade, jade_HDLC_RSTA);
+		if ((r & 0xf0) != 0xa0) {
+			if (!(r & 0x80))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "JADE %s invalid frame", (jade ? "B":"A"));
+			if ((r & 0x40) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "JADE %c RDO mode=%d", 'A'+jade, bcs->mode);
+			if (!(r & 0x20))
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "JADE %c CRC error", 'A'+jade);
+			WriteJADECMDR(cs, jade, jade_HDLC_RCMD, jadeRCMD_RMC);
+		} else {
+			count = READJADE(cs, i_jade, jade_HDLC_RBCL) & 0x1F;
+			if (count == 0)
+				count = fifo_size;
+			jade_empty_fifo(bcs, count);
+			if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "HX Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "JADE %s receive out of memory\n", (jade ? "B":"A"));
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.hscx.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & 0x40) {	/* RPF */
+		jade_empty_fifo(bcs, fifo_size);
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(fifo_size)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.hscx.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & 0x10) {	/* XPR */
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				jade_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.hscx.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.hscx.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.hscx.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			jade_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static inline void
+jade_int_main(struct IsdnCardState *cs, u_char val, int jade)
+{
+	struct BCState *bcs;
+	bcs = cs->bcs + jade;
+	
+	if (val & jadeISR_RFO) {
+		/* handled with RDO */
+		val &= ~jadeISR_RFO;
+	}
+	if (val & jadeISR_XDU) {
+		/* relevant in HDLC mode only */
+		/* don't reset XPR here */
+		if (bcs->mode == 1)
+			jade_fill_fifo(bcs);
+		else {
+			/* Here we lost an TX interrupt, so
+			   * restart transmitting the whole frame.
+			 */
+			if (bcs->tx_skb) {
+			   	skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+				bcs->tx_cnt += bcs->hw.hscx.count;
+				bcs->hw.hscx.count = 0;
+			}
+			WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, jadeXCMD_XRES);
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "JADE %c EXIR %x Lost TX", 'A'+jade, val);
+		}
+	}
+	if (val & (jadeISR_RME|jadeISR_RPF|jadeISR_XPR)) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "JADE %c interrupt %x", 'A'+jade, val);
+		jade_interrupt(cs, val, jade);
+	}
+}
diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c
new file mode 100644
index 0000000..d6c1c8f
--- /dev/null
+++ b/drivers/isdn/hisax/l3_1tr6.c
@@ -0,0 +1,955 @@
+/* $Id: l3_1tr6.c,v 2.15.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * German 1TR6 D-channel protocol
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include "hisax.h"
+#include "l3_1tr6.h"
+#include "isdnl3.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *l3_1tr6_revision = "$Revision: 2.15.2.3 $";
+
+#define MsgHead(ptr, cref, mty, dis) \
+	*ptr++ = dis; \
+	*ptr++ = 0x1; \
+	*ptr++ = cref ^ 0x80; \
+	*ptr++ = mty
+
+static void
+l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, pc->callref, mt, pd);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+	l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb)
+{
+	dev_kfree_skb(skb);
+	if (pc->st->l3.debug & L3_DEB_WARN)
+		l3_debug(pc->st, msg);
+	l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+	u_char *teln;
+	u_char *eaz;
+	u_char channel = 0;
+	int l;
+
+	MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1);
+	teln = pc->para.setup.phone;
+	pc->para.spv = 0;
+	if (!isdigit(*teln)) {
+		switch (0x5f & *teln) {
+			case 'S':
+				pc->para.spv = 1;
+				break;
+			case 'C':
+				channel = 0x08;
+			case 'P':
+				channel |= 0x80;
+				teln++;
+				if (*teln == '1')
+					channel |= 0x01;
+				else
+					channel |= 0x02;
+				break;
+			default:
+				if (pc->st->l3.debug & L3_DEB_WARN)
+					l3_debug(pc->st, "Wrong MSN Code");
+				break;
+		}
+		teln++;
+	}
+	if (channel) {
+		*p++ = 0x18;	/* channel indicator */
+		*p++ = 1;
+		*p++ = channel;
+	}
+	if (pc->para.spv) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.setup.si2;	/* 0 for all Services */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV (default) */
+		*p++ = pc->para.setup.si1;	/* 0 for all Services */
+		*p++ = pc->para.setup.si2;	/* 0 for all Services */
+	}
+	eaz = pc->para.setup.eazmsn;
+	if (*eaz) {
+		*p++ = WE0_origAddr;
+		*p++ = strlen(eaz) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*eaz)
+			*p++ = *eaz++ & 0x7f;
+	}
+	*p++ = WE0_destAddr;
+	*p++ = strlen(teln) + 1;
+	/* Classify as AnyPref. */
+	*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+	while (*teln)
+		*p++ = *teln++ & 0x7f;
+
+	*p++ = WE_Shift_F6;
+	/* Codesatz 6 fuer Service */
+	*p++ = WE6_serviceInd;
+	*p++ = 2;		/* len=2 info,info2 */
+	*p++ = pc->para.setup.si1;
+	*p++ = pc->para.setup.si2;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+
+	/* Channel Identification */
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->para.bchannel = p[2] & 0x3))
+				bcfound++;
+	} else {
+		l3_1tr6_error(pc, "missing setup chanID", skb);
+		return;
+	}
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_serviceInd, 6))) {
+		pc->para.setup.si1 = p[2];
+		pc->para.setup.si2 = p[3];
+	} else {
+		l3_1tr6_error(pc, "missing setup SI", skb);
+		return;
+	}
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_destAddr, 0)))
+		iecpy(pc->para.setup.eazmsn, p, 1);
+	else
+		pc->para.setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_origAddr, 0))) {
+		iecpy(pc->para.setup.phone, p, 1);
+	} else
+		pc->para.setup.phone[0] = 0;
+
+	p = skb->data;
+	pc->para.spv = 0;
+	if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) {
+		if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
+			pc->para.spv = 1;
+	}
+	dev_kfree_skb(skb);
+
+	/* Signal all services, linklevel takes care of Service-Indicator */
+	if (bcfound) {
+		if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) {
+			sprintf(tmp, "non-digital call: %s -> %s",
+				pc->para.setup.phone,
+				pc->para.setup.eazmsn);
+			l3_debug(pc->st, tmp);
+		}
+		newl3state(pc, 6);
+		pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+	} else
+		release_l3_process(pc);
+}
+
+static void
+l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	newl3state(pc, 2);
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "setup_ack wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "setup_ack wrong WE0_chanID", skb);
+			return;
+		}
+		pc->para.bchannel = p[2] & 0x3;
+	} else {
+		l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+		if (p[1] != 1) {
+			l3_1tr6_error(pc, "call sent wrong chanID len", skb);
+			return;
+		}
+		if ((p[2] & 0xf4) != 0x80) {
+			l3_1tr6_error(pc, "call sent wrong WE0_chanID", skb);
+			return;
+		}
+		if ((pc->state == 2) && (pc->para.bchannel != (p[2] & 0x3))) {
+			l3_1tr6_error(pc, "call sent wrong chanID value", skb);
+			return;
+		}
+		pc->para.bchannel = p[2] & 0x3;
+	} else {
+		l3_1tr6_error(pc, "missing call sent WE0_chanID", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	newl3state(pc, 3);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+	struct sk_buff *skb = arg;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+		}
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
+		}
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->st, "charging info not found");
+	dev_kfree_skb(skb);
+
+}
+
+static void
+l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+}
+
+static void
+l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	L3DelTimer(&pc->timer);	/* T310 */
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connect date", skb);
+		return;
+	}
+	newl3state(pc, 10);
+	dev_kfree_skb(skb);
+	pc->para.chargeinfo = 0;
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_cause, 0))) {
+		if (p[1] > 0) {
+			pc->para.cause = p[2];
+			if (p[1] > 1)
+				pc->para.loc = p[3];
+			else
+				pc->para.loc = 0;
+		} else {
+			pc->para.cause = 0;
+			pc->para.loc = 0;
+		}
+	} else {
+		pc->para.cause = NO_CAUSE;
+		l3_1tr6_error(pc, "missing REL cause", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	dev_kfree_skb(skb);
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->para.cause = NO_CAUSE;
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int i, tmpcharge = 0;
+	char a_charge[8], tmp[32];
+
+	StopAllL3Timer(pc);
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+		iecpy(a_charge, p, 1);
+		for (i = 0; i < strlen(a_charge); i++) {
+			tmpcharge *= 10;
+			tmpcharge += a_charge[i] & 0xf;
+		}
+		if (tmpcharge > pc->para.chargeinfo) {
+			pc->para.chargeinfo = tmpcharge;
+			pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+		}
+		if (pc->st->l3.debug & L3_DEB_CHARGE) {
+			sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+			l3_debug(pc->st, tmp);
+		}
+	} else if (pc->st->l3.debug & L3_DEB_CHARGE)
+		l3_debug(pc->st, "charging info not found");
+
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, WE0_cause, 0))) {
+		if (p[1] > 0) {
+			pc->para.cause = p[2];
+			if (p[1] > 1)
+				pc->para.loc = p[3];
+			else
+				pc->para.loc = 0;
+		} else {
+			pc->para.cause = 0;
+			pc->para.loc = 0;
+		}
+	} else {
+		if (pc->st->l3.debug & L3_DEB_WARN)
+			l3_debug(pc->st, "cause not found");
+		pc->para.cause = NO_CAUSE;
+	}
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	newl3state(pc, 12);
+	pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+}
+
+
+static void
+l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+
+	if (!findie(skb->data, skb->len, WE6_date, 6)) {
+		l3_1tr6_error(pc, "missing connack date", skb);
+		return;
+	}
+	dev_kfree_skb(skb);
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	newl3state(pc, 7);
+	l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1);
+}
+
+static void
+l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[24];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1);
+	if (pc->para.spv) {	/* SPV ? */
+		/* NSF SPV */
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_SPV;	/* SPV */
+		*p++ = pc->para.setup.si1;
+		*p++ = pc->para.setup.si2;
+		*p++ = WE0_netSpecFac;
+		*p++ = 4;	/* Laenge */
+		*p++ = 0;
+		*p++ = FAC_Activate;	/* aktiviere SPV */
+		*p++ = pc->para.setup.si1;
+		*p++ = pc->para.setup.si2;
+	}
+	newl3state(pc, 8);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x10;
+	u_char clen = 1;
+
+	if (pc->para.cause > 0)
+		cause = pc->para.cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+		case 0x10:
+			clen = 0;
+			break;
+                case 0x11:
+                        cause = CAUSE_UserBusy;
+                        break;
+		case 0x15:
+			cause = CAUSE_CallRejected;
+			break;
+	}
+	StopAllL3Timer(pc);
+	MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause | 0x80;
+	newl3state(pc, 11);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3_1tr6_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		pc->para.cause = 0;
+		l3_1tr6_disconnect_req(pc, 0, NULL);
+	}
+}
+
+static void
+l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 0x90;
+	u_char clen = 1;
+
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+	/* Map DSS1 causes */
+	switch (cause & 0x7f) {
+		case 0x10:
+			clen = 0;
+			break;
+		case 0x15:
+			cause = CAUSE_CallRejected;
+			break;
+	}
+	MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1);
+	*p++ = WE0_cause;
+	*p++ = clen;		/* Laenge */
+	if (clen)
+		*p++ = cause;
+	newl3state(pc, 19);
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 0xE6;
+	l3_1tr6_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+	newl3state(pc, 19);
+}
+
+static void
+l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	release_l3_process(pc);
+}
+
+static void
+l3_1tr6_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+        pc->para.cause = CAUSE_LocalProcErr;
+        l3_1tr6_disconnect_req(pc, pr, NULL);
+        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+        newl3state(pc, 0);
+        pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
+        pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+        release_l3_process(pc);
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstl[] =
+{
+	{SBIT(0),
+	 CC_SETUP | REQUEST, l3_1tr6_setup_req},
+   	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) |
+    	 SBIT(10),
+    	 CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE | REQUEST, l3_1tr6_release_req},
+	{SBIT(6),
+	 CC_IGNORE | REQUEST, l3_1tr6_reset},
+	{SBIT(6),
+	 CC_REJECT | REQUEST, l3_1tr6_disconnect_req},
+	{SBIT(6),
+	 CC_ALERTING | REQUEST, l3_1tr6_alert_req},
+	{SBIT(6) | SBIT(7),
+	 CC_SETUP | RESPONSE, l3_1tr6_setup_rsp},
+	{SBIT(1),
+	 CC_T303, l3_1tr6_t303},
+	{SBIT(2),
+	 CC_T304, l3_1tr6_t304},
+	{SBIT(3),
+	 CC_T310, l3_1tr6_t310},
+	{SBIT(8),
+	 CC_T313, l3_1tr6_t313},
+	{SBIT(11),
+	 CC_T305, l3_1tr6_t305},
+	{SBIT(19),
+	 CC_T308_1, l3_1tr6_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3_1tr6_t308_2},
+};
+
+#define DOWNSTL_LEN \
+	(sizeof(downstl) / sizeof(struct stateentry))
+
+static struct stateentry datastln1[] =
+{
+	{SBIT(0),
+	 MT_N1_INVALID, l3_1tr6_invalid},
+	{SBIT(0),
+	 MT_N1_SETUP, l3_1tr6_setup},
+	{SBIT(1),
+	 MT_N1_SETUP_ACK, l3_1tr6_setup_ack},
+	{SBIT(1) | SBIT(2),
+	 MT_N1_CALL_SENT, l3_1tr6_call_sent},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+	 MT_N1_DISC, l3_1tr6_disc},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_ALERT, l3_1tr6_alert},
+	{SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_N1_CONN, l3_1tr6_connect},
+	{SBIT(2),
+	 MT_N1_INFO, l3_1tr6_info_s2},
+	{SBIT(8),
+	 MT_N1_CONN_ACK, l3_1tr6_connect_ack},
+	{SBIT(10),
+	 MT_N1_INFO, l3_1tr6_info},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+	 MT_N1_REL, l3_1tr6_rel},
+	{SBIT(19),
+	 MT_N1_REL, l3_1tr6_rel_ack},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+	 SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+	 MT_N1_REL_ACK, l3_1tr6_invalid},
+	{SBIT(19),
+	 MT_N1_REL_ACK, l3_1tr6_rel_ack}
+};
+
+#define DATASTLN1_LEN \
+	(sizeof(datastln1) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+        {SBIT(2),
+         DL_ESTABLISH | INDICATION, l3_1tr6_dl_reset},
+        {ALL_STATES,
+         DL_RELEASE | INDICATION, l3_1tr6_dl_release},
+};
+ 
+#define MANSLLEN \
+        (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+static void
+up1tr6(struct PStack *st, int pr, void *arg)
+{
+	int i, mt, cr;
+	struct l3_process *proc;
+	struct sk_buff *skb = arg;
+	char tmp[80];
+
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+	}
+	if (skb->len < 4) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 len only %d", skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				skb->data[0], skb->len);
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	if (skb->data[1] != 1) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			sprintf(tmp, "up1tr6 CR len not 1");
+			l3_debug(st, tmp);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	cr = skb->data[2];
+	mt = skb->data[3];
+	if (skb->data[0] == PROTO_DIS_N0) {
+		dev_kfree_skb(skb);
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "up1tr6%s N0 mt %x unhandled",
+			     (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt);
+			l3_debug(st, tmp);
+		}
+	} else if (skb->data[0] == PROTO_DIS_N1) {
+		if (!(proc = getl3proc(st, cr))) {
+			if (mt == MT_N1_SETUP) { 
+				if (cr < 128) {
+					if (!(proc = new_l3_process(st, cr))) {
+						if (st->l3.debug & L3_DEB_PROTERR) {
+							sprintf(tmp, "up1tr6 no roc mem");
+							l3_debug(st, tmp);
+						}
+						dev_kfree_skb(skb);
+						return;
+					}
+				} else {
+					dev_kfree_skb(skb);
+					return;
+				}
+			} else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) ||
+				(mt == MT_N1_CANC_ACK) || (mt == MT_N1_CANC_REJ) ||
+				(mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) ||
+				(mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) ||
+				(mt == MT_N1_INFO)) {
+				dev_kfree_skb(skb);
+				return;
+			} else {
+				if (!(proc = new_l3_process(st, cr))) {
+					if (st->l3.debug & L3_DEB_PROTERR) {
+						sprintf(tmp, "up1tr6 no roc mem");
+						l3_debug(st, tmp);
+					}
+					dev_kfree_skb(skb);
+					return;
+				}
+				mt = MT_N1_INVALID;
+			}
+		}
+		for (i = 0; i < DATASTLN1_LEN; i++)
+			if ((mt == datastln1[i].primitive) &&
+			    ((1 << proc->state) & datastln1[i].state))
+				break;
+		if (i == DATASTLN1_LEN) {
+			dev_kfree_skb(skb);
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+					proc->state, mt);
+				l3_debug(st, tmp);
+			}
+			return;
+		} else {
+			if (st->l3.debug & L3_DEB_STATE) {
+				sprintf(tmp, "up1tr6%sstate %d mt %x",
+				  (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+					proc->state, mt);
+				l3_debug(st, tmp);
+			}
+			datastln1[i].rout(proc, pr, skb);
+		}
+	}
+}
+
+static void
+down1tr6(struct PStack *st, int pr, void *arg)
+{
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
+	char tmp[80];
+
+	if ((DL_ESTABLISH | REQUEST)== pr) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if ((CC_SETUP | REQUEST) == pr) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if (!(proc = new_l3_process(st, cr))) {
+			return;
+		} else {
+			proc->chan = chan;
+			chan->proc = proc;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+
+	for (i = 0; i < DOWNSTL_LEN; i++)
+		if ((pr == downstl[i].primitive) &&
+		    ((1 << proc->state) & downstl[i].state))
+			break;
+	if (i == DOWNSTL_LEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d unhandled",
+				proc->state, pr);
+			l3_debug(st, tmp);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			sprintf(tmp, "down1tr6 state %d prim %d",
+				proc->state, pr);
+			l3_debug(st, tmp);
+		}
+		downstl[i].rout(proc, pr, arg);
+	}
+}
+
+static void
+man1tr6(struct PStack *st, int pr, void *arg)
+{
+        int i;
+        struct l3_process *proc = arg;
+ 
+        if (!proc) {
+                printk(KERN_ERR "HiSax man1tr6 without proc pr=%04x\n", pr);
+                return;
+        }
+        for (i = 0; i < MANSLLEN; i++)
+                if ((pr == manstatelist[i].primitive) &&
+                    ((1 << proc->state) & manstatelist[i].state))
+                        break;
+        if (i == MANSLLEN) {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d man1tr6 state %d prim %d unhandled",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+        } else {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d man1tr6 state %d prim %d",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+                manstatelist[i].rout(proc, pr, arg);
+        }
+}
+ 
+void
+setstack_1tr6(struct PStack *st)
+{
+	char tmp[64];
+
+	st->lli.l4l3 = down1tr6;
+	st->l2.l2l3 = up1tr6;
+	st->l3.l3ml3 = man1tr6;
+	st->l3.N303 = 0;
+
+	strcpy(tmp, l3_1tr6_revision);
+	printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3_1tr6.h b/drivers/isdn/hisax/l3_1tr6.h
new file mode 100644
index 0000000..43215c0
--- /dev/null
+++ b/drivers/isdn/hisax/l3_1tr6.h
@@ -0,0 +1,164 @@
+/* $Id: l3_1tr6.h,v 2.2.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * German 1TR6 D-channel protocol defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef l3_1tr6
+#define l3_1tr6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 0x61
+#define MT_N0_CANC_IND 0x62
+#define MT_N0_FAC_STA 0x63
+#define MT_N0_STA_ACK 0x64
+#define MT_N0_STA_REJ 0x65
+#define MT_N0_FAC_INF 0x66
+#define MT_N0_INF_ACK 0x67
+#define MT_N0_INF_REJ 0x68
+#define MT_N0_CLOSE   0x75
+#define MT_N0_CLO_ACK 0x77
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+#define MT_N1_INVALID 0
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SPV 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAccessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+#define T303	4000
+#define T304	20000
+#define T305	4000
+#define T308	4000
+#define T310	120000
+#define T313	4000
+#define T318	4000
+#define T319	4000
+
+#endif
diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c
new file mode 100644
index 0000000..ec92308
--- /dev/null
+++ b/drivers/isdn/hisax/l3dss1.c
@@ -0,0 +1,3238 @@
+/* $Id: l3dss1.c,v 2.32.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * EURO/DSS1 D-channel protocol
+ *
+ * German 1TR6 D-channel protocol
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3dss1.h"
+#include <linux/ctype.h>
+#include <linux/config.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *dss1_revision = "$Revision: 2.32.2.3 $";
+
+#define EXT_BEARER_CAPS 1
+
+#define	MsgHead(ptr, cref, mty) \
+	*ptr++ = 0x8; \
+	if (cref == -1) { \
+		*ptr++ = 0x0; \
+	} else { \
+		*ptr++ = 0x1; \
+		*ptr++ = cref^0x80; \
+	} \
+	*ptr++ = mty
+
+
+/**********************************************/
+/* get a new invoke id for remote operations. */
+/* Only a return value != 0 is valid          */
+/**********************************************/
+static unsigned char new_invoke_id(struct PStack *p)
+{
+	unsigned char retval;
+	int i;
+  
+	i = 32; /* maximum search depth */
+
+	retval = p->prot.dss1.last_invoke_id + 1; /* try new id */
+	while ((i) && (p->prot.dss1.invoke_used[retval >> 3] == 0xFF)) {
+		p->prot.dss1.last_invoke_id = (retval & 0xF8) + 8;
+		i--;
+	}  
+	if (i) {
+		while (p->prot.dss1.invoke_used[retval >> 3] & (1 << (retval & 7)))
+		retval++; 
+	} else
+		retval = 0;
+	p->prot.dss1.last_invoke_id = retval;
+	p->prot.dss1.invoke_used[retval >> 3] |= (1 << (retval & 7));
+	return(retval);  
+} /* new_invoke_id */
+
+/*************************/
+/* free a used invoke id */
+/*************************/
+static void free_invoke_id(struct PStack *p, unsigned char id)
+{
+
+  if (!id) return; /* 0 = invalid value */
+
+  p->prot.dss1.invoke_used[id >> 3] &= ~(1 << (id & 7));
+} /* free_invoke_id */  
+
+
+/**********************************************************/
+/* create a new l3 process and fill in dss1 specific data */
+/**********************************************************/
+static struct l3_process
+*dss1_new_l3_process(struct PStack *st, int cr)
+{  struct l3_process *proc;
+
+   if (!(proc = new_l3_process(st, cr))) 
+     return(NULL);
+
+   proc->prot.dss1.invoke_id = 0;
+   proc->prot.dss1.remote_operation = 0;
+   proc->prot.dss1.uus1_data[0] = '\0';
+   
+   return(proc);
+} /* dss1_new_l3_process */
+
+/************************************************/
+/* free a l3 process and all dss1 specific data */
+/************************************************/ 
+static void
+dss1_release_l3_process(struct l3_process *p)
+{
+   free_invoke_id(p->st,p->prot.dss1.invoke_id);
+   release_l3_process(p);
+} /* dss1_release_l3_process */
+ 
+/********************************************************/
+/* search a process with invoke id id and dummy callref */
+/********************************************************/
+static struct l3_process *
+l3dss1_search_dummy_proc(struct PStack *st, int id)
+{ struct l3_process *pc = st->l3.proc; /* start of processes */
+
+  if (!id) return(NULL);
+
+  while (pc)
+   { if ((pc->callref == -1) && (pc->prot.dss1.invoke_id == id))
+       return(pc);
+     pc = pc->next;
+   } 
+  return(NULL);
+} /* l3dss1_search_dummy_proc */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return result is delivered. id specifies the invoke id.   */
+/*******************************************************************/ 
+static void 
+l3dss1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3dss1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = DSS1_STAT_INVOKE_RES;
+     ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+     ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+     ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+     ic.parm.dss1_io.timeout= 0;
+     ic.parm.dss1_io.datalen = nlen;
+     ic.parm.dss1_io.data = p;
+     free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+     pc->prot.dss1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     dss1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
+} /* l3dss1_dummy_return_result */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return error is delivered. id specifies the invoke id.    */
+/*******************************************************************/ 
+static void 
+l3dss1_dummy_error_return(struct PStack *st, int id, ulong error)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3dss1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = DSS1_STAT_INVOKE_ERR;
+     ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+     ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+     ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+     ic.parm.dss1_io.timeout= error;
+     ic.parm.dss1_io.datalen = 0;
+     ic.parm.dss1_io.data = NULL;
+     free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+     pc->prot.dss1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     dss1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
+} /* l3dss1_error_return */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a invoke is delivered. id specifies the invoke id.          */
+/*******************************************************************/ 
+static void 
+l3dss1_dummy_invoke(struct PStack *st, int cr, int id, 
+                    int ident, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  
+  l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
+               (cr == -1) ? "local" : "broadcast",id,ident,nlen);
+  if (cr >= -1) return; /* ignore local data */
+
+  cs = st->l1.hardware;
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = DSS1_STAT_INVOKE_BRD;
+  ic.parm.dss1_io.hl_id = id;
+  ic.parm.dss1_io.ll_id = 0;
+  ic.parm.dss1_io.proc = ident;
+  ic.parm.dss1_io.timeout= 0;
+  ic.parm.dss1_io.datalen = nlen;
+  ic.parm.dss1_io.data = p;
+
+  cs->iif.statcallb(&ic);
+} /* l3dss1_dummy_invoke */
+
+static void
+l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
+                      int cr, u_char * p)
+{
+	int qd_len = 0;
+	unsigned char nlen = 0, ilen, cp_tag;
+	int ident, id;
+	ulong err_ret;
+
+	if (pc) 
+		st = pc->st; /* valid Stack */
+	else
+		if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
+
+	p++;
+	qd_len = *p++;
+	if (qd_len == 0) {
+		l3_debug(st, "qd_len == 0");
+		return;
+	}
+	if ((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+		l3_debug(st, "supplementary service != 0x11");
+		return;
+	}
+	while (qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++;
+		qd_len--;
+	}
+	if (qd_len < 2) {
+		l3_debug(st, "qd_len < 2");
+		return;
+	}
+	p++;
+	qd_len--;
+	if ((*p & 0xE0) != 0xA0) {	/* class and form */
+		l3_debug(st, "class and form != 0xA0");
+		return;
+	}
+       
+        cp_tag = *p & 0x1F; /* remember tag value */
+
+        p++;
+	qd_len--;
+	if (qd_len < 1) 
+          { l3_debug(st, "qd_len < 1");
+	    return;
+	  }
+	if (*p & 0x80) 
+          { /* length format indefinite or limited */
+	    nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
+            if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
+                (nlen > 1))   
+	     { l3_debug(st, "length format error or not implemented");
+	       return;
+             }
+            if (nlen == 1)
+	     { nlen = *p++; /* complete length */
+               qd_len--;
+             } 
+            else
+	     { qd_len -= 2; /* trailing null bytes */
+               if ((*(p+qd_len)) || (*(p+qd_len+1)))
+		{ l3_debug(st,"length format indefinite error");
+                  return;
+                }
+               nlen = qd_len;
+             }
+	  }
+        else
+	  { nlen = *p++;
+	    qd_len--;
+          } 
+	if (qd_len < nlen) 
+          { l3_debug(st, "qd_len < nlen");
+	    return;
+	  }
+	qd_len -= nlen;
+
+	if (nlen < 2) 
+          { l3_debug(st, "nlen < 2");
+	    return;
+	  }
+        if (*p != 0x02) 
+          {  /* invoke identifier tag */
+	     l3_debug(st, "invoke identifier tag !=0x02");
+	     return;
+	  }
+	p++;
+	nlen--;
+	if (*p & 0x80) 
+          { /* length format */
+	    l3_debug(st, "invoke id length format 2");
+	    return;
+	  }
+	ilen = *p++;
+	nlen--;
+	if (ilen > nlen || ilen == 0) 
+          { l3_debug(st, "ilen > nlen || ilen == 0");
+	    return;
+	  }
+	nlen -= ilen;
+	id = 0;
+	while (ilen > 0) 
+          { id = (id << 8) | (*p++ & 0xFF);	/* invoke identifier */
+	    ilen--;
+	  }
+
+	switch (cp_tag) {	/* component tag */
+		case 1:	/* invoke */
+				if (nlen < 2) {
+					l3_debug(st, "nlen < 2 22");
+					return;
+				}
+				if (*p != 0x02) {	/* operation value */
+					l3_debug(st, "operation value !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(st, "ilen > nlen || ilen == 0 22");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);
+					ilen--;
+				}
+
+                                if (!pc) 
+			         { l3dss1_dummy_invoke(st, cr, id, ident, p, nlen);
+                                   return;
+                                 } 
+#if HISAX_DE_AOC
+			{
+
+#define FOO1(s,a,b) \
+	    while(nlen > 1) {		\
+		    int ilen = p[1];	\
+		    if(nlen < ilen+2) {	\
+			    l3_debug(st, "FOO1  nlen < ilen+2"); \
+			    return;		\
+		    }			\
+		    nlen -= ilen+2;		\
+		    if((*p & 0xFF) == (a)) {	\
+			    int nlen = ilen;	\
+			    p += 2;		\
+			    b;		\
+		    } else {		\
+			    p += ilen+2;	\
+		    }			\
+	    }
+
+				switch (ident) {
+					case 0x22:	/* during */
+						FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+														     ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+														     if (ident > pc->para.chargeinfo) {
+														     pc->para.chargeinfo = ident;
+														     st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
+									}
+														     if (st->l3.debug & L3_DEB_CHARGE) {
+														     if (*(p + 2) == 0) {
+														     l3_debug(st, "charging info during %d", pc->para.chargeinfo);
+									}
+								   else {
+														     l3_debug(st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+									}
+								    )))))
+							break;
+					case 0x24:	/* final */
+						FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( {
+							       ident = 0;
+							nlen = (nlen)?nlen:0; /* Make gcc happy */
+							while (ilen > 0) {
+																      ident = (ident << 8) | *p++;
+								  ilen--;
+									}
+																      if (ident > pc->para.chargeinfo) {
+																      pc->para.chargeinfo = ident;
+																      st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
+									}
+																      if (st->l3.debug & L3_DEB_CHARGE) {
+																      l3_debug(st, "charging info final %d", pc->para.chargeinfo);
+									}
+									}
+								   ))))))
+							break;
+					default:
+                                                       l3_debug(st, "invoke break invalid ident %02x",ident);
+						break;
+				}
+#undef FOO1
+
+			}
+#else  /* not HISAX_DE_AOC */
+                        l3_debug(st, "invoke break");
+#endif /* not HISAX_DE_AOC */
+			break;
+		case 2:	/* return result */
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1) 
+                             l3dss1_dummy_return_result(st, id, p, nlen);
+                           return; 
+                         }   
+                        if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
+                          { /* Diversion successful */
+                            free_invoke_id(st,pc->prot.dss1.invoke_id);
+                            pc->prot.dss1.remote_result = 0; /* success */     
+                            pc->prot.dss1.invoke_id = 0;
+                            pc->redir_result = pc->prot.dss1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
+                        else
+                          l3_debug(st,"return error unknown identifier");
+			break;
+		case 3:	/* return error */
+                            err_ret = 0;
+	                    if (nlen < 2) 
+                              { l3_debug(st, "return error nlen < 2");
+	                        return;
+	                      }
+                            if (*p != 0x02) 
+                              { /* result tag */
+	                        l3_debug(st, "invoke error tag !=0x02");
+	                        return;
+	                      }
+	                    p++;
+	                    nlen--;
+	                    if (*p > 4) 
+                              { /* length format */
+	                        l3_debug(st, "invoke return errlen > 4 ");
+	                        return;
+	                      }
+	                    ilen = *p++;
+	                    nlen--;
+	                    if (ilen > nlen || ilen == 0) 
+                              { l3_debug(st, "error return ilen > nlen || ilen == 0");
+	                        return;
+	                       }
+	                    nlen -= ilen;
+	                    while (ilen > 0) 
+                             { err_ret = (err_ret << 8) | (*p++ & 0xFF);	/* error value */
+	                       ilen--;
+	                     }
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1)
+                             l3dss1_dummy_error_return(st, id, err_ret);
+                           return; 
+                         }   
+                        if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
+                          { /* Deflection error */
+                            free_invoke_id(st,pc->prot.dss1.invoke_id);
+                            pc->prot.dss1.remote_result = err_ret; /* result */
+                            pc->prot.dss1.invoke_id = 0; 
+                            pc->redir_result = pc->prot.dss1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);  
+                          } /* Deflection error */
+                        else
+                          l3_debug(st,"return result unknown identifier");
+			break;
+		default:
+			l3_debug(st, "facility default break tag=0x%02x",cp_tag);
+			break;
+	}
+}
+
+static void
+l3dss1_message(struct l3_process *pc, u_char mt)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, pc->callref, mt);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, mt);
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_status_send(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	MsgHead(p, pc->callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = pc->para.cause | 0x80;
+
+	*p++ = IE_CALL_STATE;
+	*p++ = 0x1;
+	*p++ = pc->state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	/* This routine is called if here was no SETUP made (checks in dss1up and in
+	 * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+	 * MT_STATUS_ENQUIRE in the NULL state is handled too
+	 */
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	switch (pc->para.cause) {
+		case 81:	/* invalid callreference */
+		case 88:	/* incomp destination */
+		case 96:	/* mandory IE missing */
+		case 100:       /* invalid IE contents */
+		case 101:	/* incompatible Callstate */
+			MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+			*p++ = IE_CAUSE;
+			*p++ = 0x2;
+			*p++ = 0x80;
+			*p++ = pc->para.cause | 0x80;
+			break;
+		default:
+			printk(KERN_ERR "HiSax l3dss1_msg_without_setup wrong cause %d\n",
+				pc->para.cause);
+			return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	dss1_release_l3_process(pc);
+}
+
+static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_HLC,
+		IE_USER_USER, -1};
+static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1};
+static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, 
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL,
+		IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1};
+static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1};
+static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY,
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
+static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL,
+		IE_CALLED_PN, -1};
+static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS |
+		IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1};
+static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY,
+		IE_SIGNAL, IE_USER_USER, -1};
+/* a RELEASE_COMPLETE with errors don't require special actions 
+static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
+*/
+static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY,
+		IE_DISPLAY, -1};
+static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER  | IE_MANDATORY,
+		IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS,
+		IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN,
+		IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR,
+		IE_LLC, IE_HLC, IE_USER_USER, -1};
+static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY,
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1};
+static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE |
+		IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1};
+static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_DISPLAY, IE_FACILITY, -1};
+static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+/* not used 
+ * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY,
+ *		IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+ * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1};
+ * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND |
+ *		IE_MANDATORY, -1};
+ */
+static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1};
+static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1};
+static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1};
+
+struct ie_len {
+	int ie;
+	int len;
+};
+
+static
+struct ie_len max_ie_len[] = {
+	{IE_SEGMENT, 4},
+	{IE_BEARER, 12},
+	{IE_CAUSE, 32},
+	{IE_CALL_ID, 10},
+	{IE_CALL_STATE, 3},
+	{IE_CHANNEL_ID,	34},
+	{IE_FACILITY, 255},
+	{IE_PROGRESS, 4},
+	{IE_NET_FAC, 255},
+	{IE_NOTIFY, 3},
+	{IE_DISPLAY, 82},
+	{IE_DATE, 8},
+	{IE_KEYPAD, 34},
+	{IE_SIGNAL, 3},
+	{IE_INFORATE, 6},
+	{IE_E2E_TDELAY, 11},
+	{IE_TDELAY_SEL, 5},
+	{IE_PACK_BINPARA, 3},
+	{IE_PACK_WINSIZE, 4},
+	{IE_PACK_SIZE, 4},
+	{IE_CUG, 7},
+	{IE_REV_CHARGE, 3},
+	{IE_CALLING_PN, 24},
+	{IE_CALLING_SUB, 23},
+	{IE_CALLED_PN, 24},
+	{IE_CALLED_SUB, 23},
+	{IE_REDIR_NR, 255},
+	{IE_TRANS_SEL, 255},
+	{IE_RESTART_IND, 3},
+	{IE_LLC, 18},
+	{IE_HLC, 5},
+	{IE_USER_USER, 131},
+	{-1,0},
+};
+
+static int
+getmax_ie_len(u_char ie) {
+	int i = 0;
+	while (max_ie_len[i].ie != -1) {
+		if (max_ie_len[i].ie == ie)
+			return(max_ie_len[i].len);
+		i++;
+	}
+	return(255);
+}
+
+static int
+ie_in_set(struct l3_process *pc, u_char ie, int *checklist) {
+	int ret = 1;
+
+	while (*checklist != -1) {
+		if ((*checklist & 0xff) == ie) {
+			if (ie & 0x80)
+				return(-ret);
+			else
+				return(ret);
+		}
+		ret++;
+		checklist++;
+	}
+	return(0);
+}
+
+static int
+check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist)
+{
+	int *cl = checklist;
+	u_char mt;
+	u_char *p, ie;
+	int l, newpos, oldpos;
+	int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+	u_char codeset = 0;
+	u_char old_codeset = 0;
+	u_char codelock = 1;
+	
+	p = skb->data;
+	/* skip cr */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	mt = *p++;
+	oldpos = 0;
+	while ((p - skb->data) < skb->len) {
+		if ((*p & 0xf0) == 0x90) { /* shift codeset */
+			old_codeset = codeset;
+			codeset = *p & 7;
+			if (*p & 0x08)
+				codelock = 0;
+			else
+				codelock = 1;
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+					codelock ? " locking ": " ", old_codeset, codeset);
+			p++;
+			continue;
+		}
+		if (!codeset) { /* only codeset 0 */
+			if ((newpos = ie_in_set(pc, *p, cl))) {
+				if (newpos > 0) {
+					if (newpos < oldpos)
+						err_seq++;
+					else
+						oldpos = newpos;
+				}
+			} else {
+				if (ie_in_set(pc, *p, comp_required))
+					err_compr++;
+				else
+					err_ureg++;
+			}
+		}
+		ie = *p++;
+		if (ie & 0x80) {
+			l = 1;
+		} else {
+			l = *p++;
+			p += l;
+			l += 2;
+		}
+		if (!codeset && (l > getmax_ie_len(ie)))
+			err_len++;
+		if (!codelock) {
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift back codeset %d->%d",
+					codeset, old_codeset);
+			codeset = old_codeset;
+			codelock = 1;
+		}
+	}
+	if (err_compr | err_ureg | err_len | err_seq) {
+		if (pc->debug & L3_DEB_CHECK)
+			l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
+				mt, err_compr, err_ureg, err_len, err_seq);
+		if (err_compr)
+			return(ERR_IE_COMPREHENSION);
+		if (err_ureg)
+			return(ERR_IE_UNRECOGNIZED);
+		if (err_len)
+			return(ERR_IE_LENGTH);
+		if (err_seq)
+			return(ERR_IE_SEQUENCE);
+	} 
+	return(0);
+}
+
+/* verify if a message type exists and contain no IE error */
+static int
+l3dss1_check_messagetype_validity(struct l3_process *pc, int mt, void *arg)
+{
+	switch (mt) {
+		case MT_ALERTING:
+		case MT_CALL_PROCEEDING:
+		case MT_CONNECT:
+		case MT_CONNECT_ACKNOWLEDGE:
+		case MT_DISCONNECT:
+		case MT_INFORMATION:
+		case MT_FACILITY:
+		case MT_NOTIFY:
+		case MT_PROGRESS:
+		case MT_RELEASE:
+		case MT_RELEASE_COMPLETE:
+		case MT_SETUP:
+		case MT_SETUP_ACKNOWLEDGE:
+		case MT_RESUME_ACKNOWLEDGE:
+		case MT_RESUME_REJECT:
+		case MT_SUSPEND_ACKNOWLEDGE:
+		case MT_SUSPEND_REJECT:
+		case MT_USER_INFORMATION:
+		case MT_RESTART:
+		case MT_RESTART_ACKNOWLEDGE:
+		case MT_CONGESTION_CONTROL:
+		case MT_STATUS:
+		case MT_STATUS_ENQUIRY:
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) OK", mt);
+			break;
+		case MT_RESUME: /* RESUME only in user->net */
+		case MT_SUSPEND: /* SUSPEND only in user->net */
+		default:
+			if (pc->debug & (L3_DEB_CHECK | L3_DEB_WARN))
+				l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) fail", mt);
+			pc->para.cause = 97;
+			l3dss1_status_send(pc, 0, NULL);
+			return(1);
+	}
+	return(0);
+}
+
+static void
+l3dss1_std_ie_err(struct l3_process *pc, int ret) {
+
+	if (pc->debug & L3_DEB_CHECK)
+		l3_debug(pc->st, "check_infoelements ret %d", ret);
+	switch(ret) {
+		case 0: 
+			break;
+		case ERR_IE_COMPREHENSION:
+			pc->para.cause = 96;
+			l3dss1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_UNRECOGNIZED:
+			pc->para.cause = 99;
+			l3dss1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_LENGTH:
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_SEQUENCE:
+		default:
+			break;
+	}
+}
+
+static int
+l3dss1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		p++;
+		if (*p != 1) { /* len for BRI = 1 */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid len %d", *p);
+			return (-2);
+		}
+		p++;
+		if (*p & 0x60) { /* only base rate interface */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid %x", *p);
+			return (-3);
+		}
+		return(*p & 0x3);
+	} else
+		return(-1);
+}
+
+static int
+l3dss1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
+	u_char l, i=0;
+	u_char *p;
+
+	p = skb->data;
+	pc->para.cause = 31;
+	pc->para.loc = 0;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		l = *p++;
+		if (l>30)
+			return(1);
+		if (l) {
+			pc->para.loc = *p++;
+			l--;
+		} else {
+			return(2);
+		}
+		if (l && !(pc->para.loc & 0x80)) {
+			l--;
+			p++; /* skip recommendation */
+		}
+		if (l) {
+			pc->para.cause = *p++;
+			l--;
+			if (!(pc->para.cause & 0x80))
+				return(3);
+		} else
+			return(4);
+		while (l && (i<6)) {
+			pc->para.diag[i++] = *p++;
+			l--;
+		}
+	} else
+		return(-1);
+	return(0);
+}
+
+static void
+l3dss1_msg_with_uus(struct l3_process *pc, u_char cmd)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, cmd);
+
+        if (pc->prot.dss1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.dss1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.dss1.uus1_data);
+           p += strlen(pc->prot.dss1.uus1_data);
+           pc->prot.dss1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3dss1_msg_with_uus */
+
+static void
+l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	if (!pc->prot.dss1.uus1_data[0]) 
+		l3dss1_message(pc, MT_RELEASE);
+	else
+		l3dss1_msg_with_uus(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3dss1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
+	} else if (ret < 0)
+		pc->para.cause = NO_CAUSE;
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	dss1_release_l3_process(pc);
+}
+
+#if EXT_BEARER_CAPS
+
+static u_char *
+EncodeASyncParams(u_char * p, u_char si2)
+{				// 7c 06 88  90 21 42 00 bb
+
+	p[0] = 0;
+	p[1] = 0x40;		// Intermediate rate: 16 kbit/s jj 2000.02.19
+	p[2] = 0x80;
+	if (si2 & 32)		// 7 data bits
+
+		p[2] += 16;
+	else			// 8 data bits
+
+		p[2] += 24;
+
+	if (si2 & 16)		// 2 stop bits
+
+		p[2] += 96;
+	else			// 1 stop bit
+
+		p[2] += 32;
+
+	if (si2 & 8)		// even parity
+
+		p[2] += 2;
+	else			// no parity
+
+		p[2] += 3;
+
+	switch (si2 & 0x07) {
+		case 0:
+			p[0] = 66;	// 1200 bit/s
+
+			break;
+		case 1:
+			p[0] = 88;	// 1200/75 bit/s
+
+			break;
+		case 2:
+			p[0] = 87;	// 75/1200 bit/s
+
+			break;
+		case 3:
+			p[0] = 67;	// 2400 bit/s
+
+			break;
+		case 4:
+			p[0] = 69;	// 4800 bit/s
+
+			break;
+		case 5:
+			p[0] = 72;	// 9600 bit/s
+
+			break;
+		case 6:
+			p[0] = 73;	// 14400 bit/s
+
+			break;
+		case 7:
+			p[0] = 75;	// 19200 bit/s
+
+			break;
+	}
+	return p + 3;
+}
+
+static  u_char
+EncodeSyncParams(u_char si2, u_char ai)
+{
+
+	switch (si2) {
+		case 0:
+			return ai + 2;	// 1200 bit/s
+
+		case 1:
+			return ai + 24;		// 1200/75 bit/s
+
+		case 2:
+			return ai + 23;		// 75/1200 bit/s
+
+		case 3:
+			return ai + 3;	// 2400 bit/s
+
+		case 4:
+			return ai + 5;	// 4800 bit/s
+
+		case 5:
+			return ai + 8;	// 9600 bit/s
+
+		case 6:
+			return ai + 9;	// 14400 bit/s
+
+		case 7:
+			return ai + 11;		// 19200 bit/s
+
+		case 8:
+			return ai + 14;		// 48000 bit/s
+
+		case 9:
+			return ai + 15;		// 56000 bit/s
+
+		case 15:
+			return ai + 40;		// negotiate bit/s
+
+		default:
+			break;
+	}
+	return ai;
+}
+
+
+static u_char
+DecodeASyncParams(u_char si2, u_char * p)
+{
+	u_char info;
+
+	switch (p[5]) {
+		case 66:	// 1200 bit/s
+
+			break;	// si2 don't change
+
+		case 88:	// 1200/75 bit/s
+
+			si2 += 1;
+			break;
+		case 87:	// 75/1200 bit/s
+
+			si2 += 2;
+			break;
+		case 67:	// 2400 bit/s
+
+			si2 += 3;
+			break;
+		case 69:	// 4800 bit/s
+
+			si2 += 4;
+			break;
+		case 72:	// 9600 bit/s
+
+			si2 += 5;
+			break;
+		case 73:	// 14400 bit/s
+
+			si2 += 6;
+			break;
+		case 75:	// 19200 bit/s
+
+			si2 += 7;
+			break;
+	}
+
+	info = p[7] & 0x7f;
+	if ((info & 16) && (!(info & 8)))	// 7 data bits
+
+		si2 += 32;	// else 8 data bits
+
+	if ((info & 96) == 96)	// 2 stop bits
+
+		si2 += 16;	// else 1 stop bit
+
+	if ((info & 2) && (!(info & 1)))	// even parity
+
+		si2 += 8;	// else no parity
+
+	return si2;
+}
+
+
+static u_char
+DecodeSyncParams(u_char si2, u_char info)
+{
+	info &= 0x7f;
+	switch (info) {
+		case 40:	// bit/s negotiation failed  ai := 165 not 175!
+
+			return si2 + 15;
+		case 15:	// 56000 bit/s failed, ai := 0 not 169 !
+
+			return si2 + 9;
+		case 14:	// 48000 bit/s
+
+			return si2 + 8;
+		case 11:	// 19200 bit/s
+
+			return si2 + 7;
+		case 9:	// 14400 bit/s
+
+			return si2 + 6;
+		case 8:	// 9600  bit/s
+
+			return si2 + 5;
+		case 5:	// 4800  bit/s
+
+			return si2 + 4;
+		case 3:	// 2400  bit/s
+
+			return si2 + 3;
+		case 23:	// 75/1200 bit/s
+
+			return si2 + 2;
+		case 24:	// 1200/75 bit/s
+
+			return si2 + 1;
+		default:	// 1200 bit/s
+
+			return si2;
+	}
+}
+
+static u_char
+DecodeSI2(struct sk_buff *skb)
+{
+	u_char *p;		//, *pend=skb->data + skb->len;
+
+	if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+		switch (p[4] & 0x0f) {
+			case 0x01:
+				if (p[1] == 0x04)	// sync. Bitratenadaption
+
+					return DecodeSyncParams(160, p[5]);	// V.110/X.30
+
+				else if (p[1] == 0x06)	// async. Bitratenadaption
+
+					return DecodeASyncParams(192, p);	// V.110/X.30
+
+				break;
+			case 0x08:	// if (p[5] == 0x02) // sync. Bitratenadaption
+				if (p[1] > 3) 
+					return DecodeSyncParams(176, p[5]);	// V.120
+				break;
+		}
+	}
+	return 0;
+}
+
+#endif
+
+
+static void
+l3dss1_setup_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+	u_char channel = 0;
+
+        u_char send_keypad;
+	u_char screen = 0x80;
+	u_char *teln;
+	u_char *msn;
+	u_char *sub;
+	u_char *sp;
+	int l;
+
+	MsgHead(p, pc->callref, MT_SETUP);
+
+	teln = pc->para.setup.phone;
+#ifndef CONFIG_HISAX_NO_KEYPAD
+        send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0; 
+#else
+	send_keypad = 0;
+#endif
+#ifndef CONFIG_HISAX_NO_SENDCOMPLETE
+	if (!send_keypad)
+		*p++ = 0xa1;		/* complete indicator */
+#endif
+	/*
+	 * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+	 */
+	switch (pc->para.setup.si1) {
+	case 1:	                  /* Telephony                                */
+		*p++ = IE_BEARER;
+		*p++ = 0x3;	  /* Length                                   */
+		*p++ = 0x90;	  /* Coding Std. CCITT, 3.1 kHz audio         */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		*p++ = 0xa3;	  /* A-Law Audio                              */
+		break;
+	case 5:	                  /* Datatransmission 64k, BTX                */
+	case 7:	                  /* Datatransmission 64k                     */
+	default:
+		*p++ = IE_BEARER;
+		*p++ = 0x2;	  /* Length                                   */
+		*p++ = 0x88;	  /* Coding Std. CCITT, unrestr. dig. Inform. */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		break;
+	}
+
+	if (send_keypad) {
+		*p++ = IE_KEYPAD;
+		*p++ = strlen(teln);
+		while (*teln)
+			*p++ = (*teln++) & 0x7F;
+	}
+	  
+	/*
+	 * What about info2? Mapping to High-Layer-Compatibility?
+	 */
+	if ((*teln) && (!send_keypad)) {
+		/* parse number for special things */
+		if (!isdigit(*teln)) {
+			switch (0x5f & *teln) {
+				case 'C':
+					channel = 0x08;
+				case 'P':
+					channel |= 0x80;
+					teln++;
+					if (*teln == '1')
+						channel |= 0x01;
+					else
+						channel |= 0x02;
+					break;
+				case 'R':
+					screen = 0xA0;
+					break;
+				case 'D':
+					screen = 0x80;
+					break;
+				
+			        default:
+					if (pc->debug & L3_DEB_WARN)
+						l3_debug(pc->st, "Wrong MSN Code");
+					break;
+			}
+			teln++;
+		}
+	}
+	if (channel) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = channel;
+	}
+	msn = pc->para.setup.eazmsn;
+	sub = NULL;
+	sp = msn;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
+	if (*msn) {
+		*p++ = IE_CALLING_PN;
+		*p++ = strlen(msn) + (screen ? 2 : 1);
+		/* Classify as AnyPref. */
+		if (screen) {
+			*p++ = 0x01;	/* Ext = '0'B, Type = '000'B, Plan = '0001'B. */
+			*p++ = screen;
+		} else
+			*p++ = 0x81;	/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*msn)
+			*p++ = *msn++ & 0x7f;
+	}
+	if (sub) {
+		*sub++ = '.';
+		*p++ = IE_CALLING_SUB;
+		*p++ = strlen(sub) + 2;
+		*p++ = 0x80;	/* NSAP coded */
+		*p++ = 0x50;	/* local IDI format */
+		while (*sub)
+			*p++ = *sub++ & 0x7f;
+	}
+	sub = NULL;
+	sp = teln;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
+	
+        if (!send_keypad) {      
+		*p++ = IE_CALLED_PN;
+		*p++ = strlen(teln) + 1;
+		/* Classify as AnyPref. */
+		*p++ = 0x81;		/* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+		while (*teln)
+			*p++ = *teln++ & 0x7f;
+		
+		if (sub) {
+			*sub++ = '.';
+			*p++ = IE_CALLED_SUB;
+			*p++ = strlen(sub) + 2;
+			*p++ = 0x80;	/* NSAP coded */
+			*p++ = 0x50;	/* local IDI format */
+			while (*sub)
+				*p++ = *sub++ & 0x7f;
+		}
+        }
+#if EXT_BEARER_CAPS
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x04;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
+
+		*p++ = IE_LLC;
+		*p++ = 0x05;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x28;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+		*p++ = 0x82;
+	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x06;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+#ifndef CONFIG_HISAX_NO_LLC
+	} else {
+	  switch (pc->para.setup.si1) {
+		case 1:	                /* Telephony                                */
+			*p++ = IE_LLC;
+			*p++ = 0x3;	/* Length                                   */
+			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio         */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			*p++ = 0xa3;	/* A-Law Audio                              */
+			break;
+		case 5:	                /* Datatransmission 64k, BTX                */
+		case 7:	                /* Datatransmission 64k                     */
+		default:
+			*p++ = IE_LLC;
+			*p++ = 0x2;	/* Length                                   */
+			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			break;
+	  }
+#endif
+	}
+#endif
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 3);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 2);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret;
+	u_char cause = 0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	} 
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+		l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+	ret = check_infoelements(pc, skb, ie_DISCONNECT);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
+		cause = 99;
+	ret = pc->state;
+	newl3state(pc, 12);
+	if (cause)
+		newl3state(pc, 19);
+       	if (11 != ret)
+		pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+       	else if (!cause)
+		   l3dss1_release_req(pc, pr, NULL);
+	if (cause) {
+		l3dss1_message_cause(pc, MT_RELEASE, cause);
+		L3AddTimer(&pc->timer, T308, CC_T308_1);
+	}
+}
+
+static void
+l3dss1_connect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_CONNECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	/* here should inserted COLP handling KKe */
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3dss1_alerting(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_ALERTING);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+	int id;
+	int err = 0;
+
+	/*
+	 * Bearer Capabilities
+	 */
+	p = skb->data;
+	/* only the first occurence 'll be detected ! */
+	if ((p = findie(p, skb->len, 0x04, 0))) {
+		if ((p[1] < 2) || (p[1] > 11))
+			err = 1;
+		else {
+			pc->para.setup.si2 = 0;
+			switch (p[2] & 0x7f) {
+				case 0x00: /* Speech */
+				case 0x10: /* 3.1 Khz audio */
+					pc->para.setup.si1 = 1;
+					break;
+				case 0x08: /* Unrestricted digital information */
+					pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#if EXT_BEARER_CAPS
+					pc->para.setup.si2 = DecodeSI2(skb);
+#endif
+					break;
+				case 0x09: /* Restricted digital information */
+					pc->para.setup.si1 = 2;
+					break;
+				case 0x11:
+					/* Unrestr. digital information  with 
+					 * tones/announcements ( or 7 kHz audio
+					 */
+					pc->para.setup.si1 = 3;
+					break;
+				case 0x18: /* Video */
+					pc->para.setup.si1 = 4;
+					break;
+				default:
+					err = 2;
+					break;
+			}
+			switch (p[3] & 0x7f) {
+				case 0x40: /* packed mode */
+					pc->para.setup.si1 = 8;
+					break;
+				case 0x10: /* 64 kbit */
+				case 0x11: /* 2*64 kbit */
+				case 0x13: /* 384 kbit */
+				case 0x15: /* 1536 kbit */
+				case 0x17: /* 1920 kbit */
+					pc->para.moderate = p[3] & 0x7f;
+					break;
+				default:
+					err = 3;
+					break;
+			}
+		}
+		if (pc->debug & L3_DEB_SI)
+			l3_debug(pc->st, "SI=%d, AI=%d",
+				pc->para.setup.si1, pc->para.setup.si2);
+		if (err) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
+					p[1], p[2], p[3]);
+			pc->para.cause = 100;
+			l3dss1_msg_without_setup(pc, pr, NULL);
+			return;
+		}
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bearer capabilities");
+		/* ETS 300-104 1.3.3 */
+		pc->para.cause = 96;
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/*
+	 * Channel Identification
+	 */
+	if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+		if ((pc->para.bchannel = id)) {
+			if ((3 == id) && (0x10 == pc->para.moderate)) {
+				if (pc->debug & L3_DEB_WARN)
+					l3_debug(pc->st, "setup with wrong chid %x",
+						id);
+				pc->para.cause = 100;
+				l3dss1_msg_without_setup(pc, pr, NULL);
+				return;
+			}
+			bcfound++;
+		} else 
+                   { if (pc->debug & L3_DEB_WARN)
+			 l3_debug(pc->st, "setup without bchannel, call waiting");
+                     bcfound++;
+                   } 
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup with wrong chid ret %d", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_SETUP);
+	if (ERR_IE_COMPREHENSION == err) {
+		pc->para.cause = 96;
+		l3dss1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x70, 0)))
+		iecpy(pc->para.setup.eazmsn, p, 1);
+	else
+		pc->para.setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x71, 0))) {
+		/* Called party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.eazmsn, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong called subaddress");
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6c, 0))) {
+		pc->para.setup.plan = p[2];
+		if (p[2] & 0x80) {
+			iecpy(pc->para.setup.phone, p, 1);
+			pc->para.setup.screen = 0;
+		} else {
+			iecpy(pc->para.setup.phone, p, 2);
+			pc->para.setup.screen = p[3];
+		}
+	} else {
+		pc->para.setup.phone[0] = 0;
+		pc->para.setup.plan = 0;
+		pc->para.setup.screen = 0;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6d, 0))) {
+		/* Calling party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.phone, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong calling subaddress");
+	}
+	newl3state(pc, 6);
+	if (err) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, err);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+}
+
+static void
+l3dss1_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 16;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	StopAllL3Timer(pc);
+
+	MsgHead(p, pc->callref, MT_DISCONNECT);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+        if (pc->prot.dss1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.dss1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.dss1.uus1_data);
+           p += strlen(pc->prot.dss1.uus1_data);
+           pc->prot.dss1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 11);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3dss1_setup_rsp(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+        if (!pc->para.bchannel) 
+	 { if (pc->debug & L3_DEB_WARN)
+	       l3_debug(pc->st, "D-chan connect for waiting call");
+           l3dss1_disconnect_req(pc, pr, arg);
+           return;
+         }
+	newl3state(pc, 8);
+	l3dss1_message(pc, MT_CONNECT);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3dss1_connect_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	newl3state(pc, 10);
+	L3DelTimer(&pc->timer);
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 21;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_release(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret, cause=0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3dss1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "REL get_cause ret(%d)", ret);
+	} else if (ret<0)
+		pc->para.cause = NO_CAUSE;
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+		l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+	if ((ret<0) && (pc->state != 11))
+		cause = 96;
+	else if (ret>0)
+		cause = 100;
+	ret = check_infoelements(pc, skb, ie_RELEASE);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
+		cause = 99;  
+	if (cause)
+		l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
+	else
+		l3dss1_message(pc, MT_RELEASE_COMPLETE);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_alert_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	newl3state(pc, 7);
+	if (!pc->prot.dss1.uus1_data[0]) 
+		l3dss1_message(pc, MT_ALERTING);
+	else
+		l3dss1_msg_with_uus(pc, MT_ALERTING); 
+}
+
+static void
+l3dss1_proceed_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 9);
+	l3dss1_message(pc, MT_CALL_PROCEEDING);
+	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
+}
+
+static void
+l3dss1_setup_ack_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 25);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T302, CC_T302);
+	l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
+/********************************************/
+/* deliver a incoming display message to HL */
+/********************************************/
+static void
+l3dss1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
+{       u_char len;
+        isdn_ctrl ic; 
+	struct IsdnCardState *cs;
+        char *p; 
+
+        if (*infp++ != IE_DISPLAY) return;
+        if ((len = *infp++) > 80) return; /* total length <= 82 */
+	if (!pc->chan) return;
+
+	p = ic.parm.display; 
+        while (len--)
+	  *p++ = *infp++;
+	*p = '\0';
+	ic.command = ISDN_STAT_DISPLAY;
+	cs = pc->st->l1.hardware;
+	ic.driver = cs->myid;
+	ic.arg = pc->chan->chan; 
+	cs->iif.statcallb(&ic);
+} /* l3dss1_deliver_display */
+
+
+static void
+l3dss1_progress(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
+		if (p[1] != 2) {
+			err = 1;
+			pc->para.cause = 100;
+		} else if (!(p[2] & 0x70)) {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+				case 0x84:
+				case 0x85:
+				case 0x87:
+				case 0x8a:
+					switch (p[3]) {
+						case 0x81:
+						case 0x82:
+						case 0x83:
+						case 0x84:
+						case 0x88:
+							break;
+						default:
+							err = 2;
+							pc->para.cause = 100;
+							break;
+					}
+					break;
+				default:
+					err = 3;
+					pc->para.cause = 100;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 4;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "progress error %d", err);
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_PROGRESS);
+	if (err)
+		l3dss1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+}
+
+static void
+l3dss1_notify(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
+		if (p[1] != 1) {
+			err = 1;
+			pc->para.cause = 100;
+		} else {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+					break;
+				default:
+					pc->para.cause = 100;
+					err = 2;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 3;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "notify error %d", err);
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_NOTIFY);
+	if (err)
+		l3dss1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+}
+
+static void
+l3dss1_status_enq(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+
+	ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
+	l3dss1_std_ie_err(pc, ret);
+	pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+        l3dss1_status_send(pc, pr, NULL);
+}
+
+static void
+l3dss1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+	u_char *p;
+	char tmp[32];
+
+	ret = check_infoelements(pc, skb, ie_INFORMATION);
+	if (ret)
+		l3dss1_std_ie_err(pc, ret);
+	if (pc->state == 25) { /* overlap receiving */
+		L3DelTimer(&pc->timer);
+		p = skb->data;
+		if ((p = findie(p, skb->len, 0x70, 0))) {
+			iecpy(tmp, p, 1);
+			strcat(pc->para.setup.eazmsn, tmp);
+			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+		}
+		L3AddTimer(&pc->timer, T302, CC_T302);
+	}
+}
+
+/******************************/
+/* handle deflection requests */
+/******************************/
+static void l3dss1_redir_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+        u_char *subp;
+        u_char len_phone = 0;
+        u_char len_sub = 0;
+	int l; 
+
+
+        strcpy(pc->prot.dss1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
+        if (!pc->chan->setup.phone[0])
+          { pc->para.cause = -1;
+            l3dss1_disconnect_req(pc,pr,arg); /* disconnect immediately */
+            return;
+          } /* only uus */
+ 
+        if (pc->prot.dss1.invoke_id) 
+          free_invoke_id(pc->st,pc->prot.dss1.invoke_id);
+ 
+        if (!(pc->prot.dss1.invoke_id = new_invoke_id(pc->st))) 
+          return;
+
+        MsgHead(p, pc->callref, MT_FACILITY);
+
+        for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
+        if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subaddress element */ 
+
+	*p++ = 0x1c;   /* Facility info element */
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
+        *p++ = 0x91;  /* remote operations protocol */
+        *p++ = 0xa1;  /* invoke component */
+	  
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
+        *p++ = 0x02;  /* invoke id tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = pc->prot.dss1.invoke_id;  /* invoke id */ 
+        *p++ = 0x02;  /* operation value tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = 0x0D;  /* Call Deflect */
+	  
+        *p++ = 0x30;  /* sequence phone number */
+        *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
+	  
+        *p++ = 0x30;  /* Deflected to UserNumber */
+        *p++ = len_phone+2+len_sub; /* length */
+        *p++ = 0x80; /* NumberDigits */
+	*p++ = len_phone; /* length */
+        for (l = 0; l < len_phone; l++)
+	 *p++ = pc->chan->setup.phone[l];
+
+        if (len_sub)
+	  { *p++ = 0x04; /* called party subaddress */
+            *p++ = len_sub - 2;
+            while (*subp) *p++ = *subp++;
+          }
+
+        *p++ = 0x01; /* screening identifier */
+        *p++ = 0x01;
+        *p++ = pc->chan->setup.screen;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l))) return;
+	memcpy(skb_put(skb, l), tmp, l);
+
+        l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3dss1_redir_req */
+
+/********************************************/
+/* handle deflection request in early state */
+/********************************************/
+static void l3dss1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
+{
+  l3dss1_proceed_req(pc,pr,arg);
+  l3dss1_redir_req(pc,pr,arg);
+} /* l3dss1_redir_req_early */
+
+/***********************************************/
+/* handle special commands for this protocol.  */
+/* Examples are call independant services like */
+/* remote operations with dummy  callref.      */
+/***********************************************/
+static int l3dss1_cmd_global(struct PStack *st, isdn_ctrl *ic)
+{ u_char id;
+  u_char temp[265];
+  u_char *p = temp;
+  int i, l, proc_len; 
+  struct sk_buff *skb;
+  struct l3_process *pc = NULL;
+
+  switch (ic->arg)
+   { case DSS1_CMD_INVOKE:
+       if (ic->parm.dss1_io.datalen < 0) return(-2); /* invalid parameter */ 
+
+       for (proc_len = 1, i = ic->parm.dss1_io.proc >> 8; i; i++) 
+         i = i >> 8; /* add one byte */    
+       l = ic->parm.dss1_io.datalen + proc_len + 8; /* length excluding ie header */
+       if (l > 255) 
+         return(-2); /* too long */
+
+       if (!(id = new_invoke_id(st))) 
+         return(0); /* first get a invoke id -> return if no available */
+       
+       i = -1; 
+       MsgHead(p, i, MT_FACILITY); /* build message head */
+       *p++ = 0x1C; /* Facility IE */
+       *p++ = l; /* length of ie */
+       *p++ = 0x91; /* remote operations */
+       *p++ = 0xA1; /* invoke */
+       *p++ = l - 3; /* length of invoke */
+       *p++ = 0x02; /* invoke id tag */
+       *p++ = 0x01; /* length is 1 */
+       *p++ = id; /* invoke id */
+       *p++ = 0x02; /* operation */
+       *p++ = proc_len; /* length of operation */
+       
+       for (i = proc_len; i; i--)
+         *p++ = (ic->parm.dss1_io.proc >> (i-1)) & 0xFF;
+       memcpy(p, ic->parm.dss1_io.data, ic->parm.dss1_io.datalen); /* copy data */
+       l = (p - temp) + ic->parm.dss1_io.datalen; /* total length */         
+
+       if (ic->parm.dss1_io.timeout > 0)
+        if (!(pc = dss1_new_l3_process(st, -1)))
+          { free_invoke_id(st, id);
+            return(-2);
+          } 
+       pc->prot.dss1.ll_id = ic->parm.dss1_io.ll_id; /* remember id */ 
+       pc->prot.dss1.proc = ic->parm.dss1_io.proc; /* and procedure */
+
+       if (!(skb = l3_alloc_skb(l))) 
+         { free_invoke_id(st, id);
+           if (pc) dss1_release_l3_process(pc);
+           return(-2);
+         }
+       memcpy(skb_put(skb, l), temp, l);
+       
+       if (pc)
+        { pc->prot.dss1.invoke_id = id; /* remember id */
+          L3AddTimer(&pc->timer, ic->parm.dss1_io.timeout, CC_TDSS1_IO | REQUEST);
+        }
+       
+       l3_msg(st, DL_DATA | REQUEST, skb);
+       ic->parm.dss1_io.hl_id = id; /* return id */
+       return(0);
+
+     case DSS1_CMD_INVOKE_ABORT:
+       if ((pc = l3dss1_search_dummy_proc(st, ic->parm.dss1_io.hl_id)))
+	{ L3DelTimer(&pc->timer); /* remove timer */
+          dss1_release_l3_process(pc);
+          return(0); 
+        } 
+       else
+	{ l3_debug(st, "l3dss1_cmd_global abort unknown id");
+          return(-2);
+        } 
+       break;
+    
+     default: 
+       l3_debug(st, "l3dss1_cmd_global unknown cmd 0x%lx", ic->arg);
+       return(-1);  
+   } /* switch ic-> arg */
+  return(-1);
+} /* l3dss1_cmd_global */
+
+static void 
+l3dss1_io_timer(struct l3_process *pc)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs = pc->st->l1.hardware;
+
+  L3DelTimer(&pc->timer); /* remove timer */
+
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = DSS1_STAT_INVOKE_ERR;
+  ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+  ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+  ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+  ic.parm.dss1_io.timeout= -1;
+  ic.parm.dss1_io.datalen = 0;
+  ic.parm.dss1_io.data = NULL;
+  free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+  pc->prot.dss1.invoke_id = 0; /* reset id */
+
+  cs->iif.statcallb(&ic);
+
+  dss1_release_l3_process(pc); 
+} /* l3dss1_io_timer */
+
+static void
+l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int callState = 0;
+	p = skb->data;
+
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++)
+			callState = *p;
+	}
+	if (callState == 0) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
+		 * set down layer 3 without sending any message
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		dss1_release_l3_process(pc);
+	} else {
+		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+	}
+}
+
+static void
+l3dss1_dummy(struct l3_process *pc, u_char pr, void *arg)
+{
+}
+
+static void
+l3dss1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 28; /* invalid number */
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3dss1_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
+		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		dss1_release_l3_process(pc);
+	}
+}
+
+static void
+l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+
+}
+
+static void
+l3dss1_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+	u_char cause = 16;
+
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 19);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3dss1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3dss1_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+	newl3state(pc, 19);
+	L3DelTimer(&pc->timer);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+}
+
+static void
+l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 19);
+	l3dss1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int ret; 
+	u_char cause = 0, callState = 0;
+	
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	}
+	if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++) {
+			callState = *p;
+			if (!ie_in_set(pc, *p, l3_valid_states))
+				cause = 100;
+		} else
+			cause = 100;
+	} else
+		cause = 96;
+	if (!cause) { /*  no error before */
+		ret = check_infoelements(pc, skb, ie_STATUS);
+		if (ERR_IE_COMPREHENSION == ret)
+			cause = 96;
+		else if (ERR_IE_UNRECOGNIZED == ret)
+			cause = 99;
+	}
+	if (cause) {
+		u_char tmp;
+		
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
+		tmp = pc->para.cause;
+		pc->para.cause = cause;
+		l3dss1_status_send(pc, 0, NULL);
+		if (cause == 99)
+			pc->para.cause = tmp;
+		else
+			return;
+	}
+	cause = pc->para.cause;
+	if (((cause & 0x7f) == 111) && (callState == 0)) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
+		 * if received MT_STATUS with cause == 111 and call
+		 * state == 0, then we must set down layer 3
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		dss1_release_l3_process(pc);
+	}
+}
+
+static void
+l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+	
+	ret = check_infoelements(pc, skb, ie_FACILITY);
+	l3dss1_std_ie_err(pc, ret);
+ 	  {
+		u_char *p;
+		if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+			l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+}
+
+static void
+l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->chan->setup.phone;
+
+	MsgHead(p, pc->callref, MT_SUSPEND);
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 15);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 0);
+	pc->para.cause = NO_CAUSE;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	/* We don't handle suspend_ack for IE errors now */
+	if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, ret);
+}
+
+static void
+l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->para.setup.phone;
+
+	MsgHead(p, pc->callref, MT_RESUME);
+
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 17);
+	L3AddTimer(&pc->timer, T318, CC_T318);
+}
+
+static void
+l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3dss1_get_channel_id(pc, skb)) > 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "resume ack with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3dss1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "resume ack without chid (ret %d)", id);
+		pc->para.cause = 96;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, ret);
+}
+
+static void
+l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3dss1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3dss1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3dss1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 0);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3dss1_std_ie_err(pc, ret);
+	dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[32];
+	u_char *p;
+	u_char ri, ch = 0, chan = 0;
+	int l;
+	struct sk_buff *skb = arg;
+	struct l3_process *up;
+
+	newl3state(pc, 2);
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+		ri = p[2];
+		l3_debug(pc->st, "Restart %x", ri);
+	} else {
+		l3_debug(pc->st, "Restart without restart IE");
+		ri = 0x86;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		chan = p[2] & 3;
+		ch = p[2];
+		if (pc->st->l3.debug)
+			l3_debug(pc->st, "Restart for channel %d", chan);
+	}
+	newl3state(pc, 2);
+	up = pc->st->l3.proc;
+	while (up) {
+		if ((ri & 7) == 7)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		else if (up->para.bchannel == chan)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		up = up->next;
+	}
+	p = tmp;
+	MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+	if (chan) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = ch | 0x80;
+	}
+	*p++ = 0x79;		/* RESTART Ind */
+	*p++ = 1;
+	*p++ = ri;
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 0);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+        pc->para.cause = 0x29;          /* Temporary failure */
+        pc->para.loc = 0;
+        l3dss1_disconnect_req(pc, pr, NULL);
+        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+        newl3state(pc, 0);
+        pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
+        pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+        release_l3_process(pc);
+}
+
+static void
+l3dss1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
+{
+        L3DelTimer(&pc->timer);
+        L3AddTimer(&pc->timer, T309, CC_T309);
+        l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
+}
+ 
+static void
+l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+ 
+ 	pc->para.cause = 0x1F; /* normal, unspecified */
+	l3dss1_status_send(pc, 0, NULL);
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+	{SBIT(0),
+	 CC_SETUP | REQUEST, l3dss1_setup_req},
+	{SBIT(0),
+	 CC_RESUME | REQUEST, l3dss1_resume_req},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25),
+	 CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE | REQUEST, l3dss1_release_req},
+	{ALL_STATES,
+	 CC_RESTART | REQUEST, l3dss1_restart},
+	{SBIT(6) | SBIT(25),
+	 CC_IGNORE | REQUEST, l3dss1_reset},
+	{SBIT(6) | SBIT(25),
+	 CC_REJECT | REQUEST, l3dss1_reject_req},
+	{SBIT(6) | SBIT(25),
+	 CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req},
+	{SBIT(6),
+	 CC_MORE_INFO | REQUEST, l3dss1_setup_ack_req},
+	{SBIT(25),
+	 CC_MORE_INFO | REQUEST, l3dss1_dummy},
+	{SBIT(6) | SBIT(9) | SBIT(25),
+	 CC_ALERTING | REQUEST, l3dss1_alert_req},
+	{SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
+	 CC_SETUP | RESPONSE, l3dss1_setup_rsp},
+	{SBIT(10),
+	 CC_SUSPEND | REQUEST, l3dss1_suspend_req},
+        {SBIT(7) | SBIT(9) | SBIT(25),
+         CC_REDIR | REQUEST, l3dss1_redir_req},
+        {SBIT(6),
+         CC_REDIR | REQUEST, l3dss1_redir_req_early},
+        {SBIT(9) | SBIT(25),
+         CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
+	{SBIT(25),
+	 CC_T302, l3dss1_t302},
+	{SBIT(1),
+	 CC_T303, l3dss1_t303},
+	{SBIT(2),
+	 CC_T304, l3dss1_t304},
+	{SBIT(3),
+	 CC_T310, l3dss1_t310},
+	{SBIT(8),
+	 CC_T313, l3dss1_t313},
+	{SBIT(11),
+	 CC_T305, l3dss1_t305},
+	{SBIT(15),
+	 CC_T319, l3dss1_t319},
+	{SBIT(17),
+	 CC_T318, l3dss1_t318},
+	{SBIT(19),
+	 CC_T308_1, l3dss1_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3dss1_t308_2},
+	{SBIT(10),
+	 CC_T309, l3dss1_dl_release},
+};
+
+#define DOWNSLLEN \
+	(sizeof(downstatelist) / sizeof(struct stateentry))
+
+static struct stateentry datastatelist[] =
+{
+	{ALL_STATES,
+	 MT_STATUS_ENQUIRY, l3dss1_status_enq},
+	{ALL_STATES,
+	 MT_FACILITY, l3dss1_facility},
+	{SBIT(19),
+	 MT_STATUS, l3dss1_release_ind},
+	{ALL_STATES,
+	 MT_STATUS, l3dss1_status},
+	{SBIT(0),
+	 MT_SETUP, l3dss1_setup},
+	{SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) |
+	 SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_SETUP, l3dss1_dummy},
+	{SBIT(1) | SBIT(2),
+	 MT_CALL_PROCEEDING, l3dss1_call_proc},
+	{SBIT(1),
+	 MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack},
+	{SBIT(2) | SBIT(3),
+	 MT_ALERTING, l3dss1_alerting},
+	{SBIT(2) | SBIT(3),
+	 MT_PROGRESS, l3dss1_progress},
+	{SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_INFORMATION, l3dss1_information},
+	{SBIT(10) | SBIT(11) | SBIT(15),
+	 MT_NOTIFY, l3dss1_notify},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25),
+	 MT_RELEASE, l3dss1_release},
+	{SBIT(19),  MT_RELEASE, l3dss1_release_ind},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25),
+	 MT_DISCONNECT, l3dss1_disconnect},
+	{SBIT(19),
+	 MT_DISCONNECT, l3dss1_dummy},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_CONNECT, l3dss1_connect},
+	{SBIT(8),
+	 MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
+	{SBIT(15),
+	 MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack},
+	{SBIT(15),
+	 MT_SUSPEND_REJECT, l3dss1_suspend_rej},
+	{SBIT(17),
+	 MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack},
+	{SBIT(17),
+	 MT_RESUME_REJECT, l3dss1_resume_rej},
+};
+
+#define DATASLLEN \
+	(sizeof(datastatelist) / sizeof(struct stateentry))
+
+static struct stateentry globalmes_list[] =
+{
+	{ALL_STATES,
+	 MT_STATUS, l3dss1_status},
+	{SBIT(0),
+	 MT_RESTART, l3dss1_global_restart},
+/*	{SBIT(1),
+	 MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
+*/
+};
+#define GLOBALM_LEN \
+	(sizeof(globalmes_list) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+        {SBIT(2),
+         DL_ESTABLISH | INDICATION, l3dss1_dl_reset},
+        {SBIT(10),
+         DL_ESTABLISH | CONFIRM, l3dss1_dl_reest_status},
+        {SBIT(10),
+         DL_RELEASE | INDICATION, l3dss1_dl_reestablish},
+        {ALL_STATES,
+         DL_RELEASE | INDICATION, l3dss1_dl_release},
+};
+
+#define MANSLLEN \
+        (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	int i;
+	struct l3_process *proc = st->l3.global;
+
+	proc->callref = skb->data[2]; /* cr flag */
+	for (i = 0; i < GLOBALM_LEN; i++)
+		if ((mt == globalmes_list[i].primitive) &&
+		    ((1 << proc->state) & globalmes_list[i].state))
+			break;
+	if (i == GLOBALM_LEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1 global state %d mt %x unhandled",
+				proc->state, mt);
+		}
+		MsgHead(p, proc->callref, MT_STATUS);
+		*p++ = IE_CAUSE;
+		*p++ = 0x2;
+		*p++ = 0x80;
+		*p++ = 81 |0x80;	/* invalid cr */
+		*p++ = 0x14;		/* CallState */
+		*p++ = 0x1;
+		*p++ = proc->state & 0x3f;
+		l = p - tmp;
+		if (!(skb = l3_alloc_skb(l)))
+			return;
+		memcpy(skb_put(skb, l), tmp, l);
+		l3_msg(proc->st, DL_DATA | REQUEST, skb);
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1 global %d mt %x",
+				proc->state, mt);
+		}
+		globalmes_list[i].rout(proc, mt, skb);
+	}
+}
+
+static void
+dss1up(struct PStack *st, int pr, void *arg)
+{
+	int i, mt, cr, cause, callState;
+	char *ptr;
+	u_char *p;
+	struct sk_buff *skb = arg;
+	struct l3_process *proc;
+
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | CONFIRM):
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+                default:
+                        printk(KERN_ERR "HiSax dss1up unknown pr=%04x\n", pr);
+                        return;
+	}
+	if (skb->len < 3) {
+		l3_debug(st, "dss1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (skb->data[0] != PROTO_DIS_EURO) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			l3_debug(st, "dss1up%sunexpected discriminator %x message len %d",
+				 (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				 skb->data[0], skb->len);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	cr = getcallref(skb->data);
+	if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
+		l3_debug(st, "dss1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+	mt = skb->data[skb->data[1] + 2];
+	if (st->l3.debug & L3_DEB_STATE)
+		l3_debug(st, "dss1up cr %d", cr);
+	if (cr == -2) {  /* wrong Callref */
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "dss1up wrong Callref");
+		dev_kfree_skb(skb);
+		return;
+	} else if (cr == -1) {	/* Dummy Callref */
+		if (mt == MT_FACILITY)
+			if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+				l3dss1_parse_facility(st, NULL, 
+					(pr == (DL_DATA | INDICATION)) ? -1 : -2, p); 
+				dev_kfree_skb(skb);
+				return;  
+			}
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "dss1up dummy Callref (no facility msg or ie)");
+		dev_kfree_skb(skb);
+		return;
+	} else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
+		(((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) {	/* Global CallRef */
+		if (st->l3.debug & L3_DEB_STATE)
+			l3_debug(st, "dss1up Global CallRef");
+		global_handler(st, mt, skb);
+		dev_kfree_skb(skb);
+		return;
+	} else if (!(proc = getl3proc(st, cr))) {
+		/* No transaction process exist, that means no call with
+		 * this callreference is active
+		 */
+		if (mt == MT_SETUP) {
+			/* Setup creates a new transaction process */
+			if (skb->data[2] & 0x80) {
+				/* Setup with wrong CREF flag */
+				if (st->l3.debug & L3_DEB_STATE)
+					l3_debug(st, "dss1up wrong CRef flag");
+				dev_kfree_skb(skb);
+				return;
+			}
+			if (!(proc = dss1_new_l3_process(st, cr))) {
+				/* May be to answer with RELEASE_COMPLETE and
+				 * CAUSE 0x2f "Resource unavailable", but this
+				 * need a new_l3_process too ... arghh
+				 */
+				dev_kfree_skb(skb);
+				return;
+			}
+		} else if (mt == MT_STATUS) {
+			cause = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				cause = *ptr & 0x7f;
+			}
+			callState = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				callState = *ptr;
+			}
+			/* ETS 300-104 part 2.4.1
+			 * if setup has not been made and a message type
+			 * MT_STATUS is received with call state == 0,
+			 * we must send nothing
+			 */
+			if (callState != 0) {
+				/* ETS 300-104 part 2.4.2
+				 * if setup has not been made and a message type
+				 * MT_STATUS is received with call state != 0,
+				 * we must send MT_RELEASE_COMPLETE cause 101
+				 */
+				if ((proc = dss1_new_l3_process(st, cr))) {
+					proc->para.cause = 101;
+					l3dss1_msg_without_setup(proc, 0, NULL);
+				}
+			}
+			dev_kfree_skb(skb);
+			return;
+		} else if (mt == MT_RELEASE_COMPLETE) {
+			dev_kfree_skb(skb);
+			return;
+		} else {
+			/* ETS 300-104 part 2
+			 * if setup has not been made and a message type
+			 * (except MT_SETUP and RELEASE_COMPLETE) is received,
+			 * we must send MT_RELEASE_COMPLETE cause 81 */
+			dev_kfree_skb(skb);
+			if ((proc = dss1_new_l3_process(st, cr))) {
+				proc->para.cause = 81;
+				l3dss1_msg_without_setup(proc, 0, NULL);
+			}
+			return;
+		}
+	}
+	if (l3dss1_check_messagetype_validity(proc, mt, skb)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) 
+	  l3dss1_deliver_display(proc, pr, p); /* Display IE included */
+	for (i = 0; i < DATASLLEN; i++)
+		if ((mt == datastatelist[i].primitive) &&
+		    ((1 << proc->state) & datastatelist[i].state))
+			break;
+	if (i == DATASLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1up%sstate %d mt %#x unhandled",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
+			proc->para.cause = 101;
+			l3dss1_status_send(proc, pr, skb);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1up%sstate %d mt %x",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		datastatelist[i].rout(proc, pr, skb);
+	}
+	dev_kfree_skb(skb);
+	return;
+}
+
+static void
+dss1down(struct PStack *st, int pr, void *arg)
+{
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
+
+	if ((DL_ESTABLISH | REQUEST) == pr) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if ((proc = dss1_new_l3_process(st, cr))) {
+			proc->chan = chan;
+			chan->proc = proc;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+	if (!proc) {
+		printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr);
+		return;
+	}
+
+	if ( pr == (CC_TDSS1_IO | REQUEST)) {
+		l3dss1_io_timer(proc); /* timer expires */ 
+		return;
+	}  
+
+	for (i = 0; i < DOWNSLLEN; i++)
+		if ((pr == downstatelist[i].primitive) &&
+		    ((1 << proc->state) & downstatelist[i].state))
+			break;
+	if (i == DOWNSLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1down state %d prim %#x unhandled",
+				proc->state, pr);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "dss1down state %d prim %#x",
+				proc->state, pr);
+		}
+		downstatelist[i].rout(proc, pr, arg);
+	}
+}
+
+static void
+dss1man(struct PStack *st, int pr, void *arg)
+{
+        int i;
+        struct l3_process *proc = arg;
+ 
+        if (!proc) {
+                printk(KERN_ERR "HiSax dss1man without proc pr=%04x\n", pr);
+                return;
+        }
+        for (i = 0; i < MANSLLEN; i++)
+                if ((pr == manstatelist[i].primitive) &&
+                    ((1 << proc->state) & manstatelist[i].state))
+                        break;
+        if (i == MANSLLEN) {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d dss1man state %d prim %#x unhandled",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+        } else {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d dss1man state %d prim %#x",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+                manstatelist[i].rout(proc, pr, arg);
+        }
+}
+ 
+void
+setstack_dss1(struct PStack *st)
+{
+	char tmp[64];
+	int i;
+
+	st->lli.l4l3 = dss1down;
+	st->lli.l4l3_proto = l3dss1_cmd_global;
+	st->l2.l2l3 = dss1up;
+	st->l3.l3ml3 = dss1man;
+	st->l3.N303 = 1;
+	st->prot.dss1.last_invoke_id = 0;
+	st->prot.dss1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
+	i = 1;
+	while (i < 32) 
+		st->prot.dss1.invoke_used[i++] = 0;   
+
+	if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n");
+	} else {
+		st->l3.global->state = 0;
+		st->l3.global->callref = 0;
+		st->l3.global->next = NULL;
+		st->l3.global->debug = L3_DEB_WARN;
+		st->l3.global->st = st;
+		st->l3.global->N303 = 1;
+		st->l3.global->prot.dss1.invoke_id = 0; 
+
+		L3InitTimer(st->l3.global, &st->l3.global->timer);
+	}
+	strcpy(tmp, dss1_revision);
+	printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h
new file mode 100644
index 0000000..6da47f0
--- /dev/null
+++ b/drivers/isdn/hisax/l3dss1.h
@@ -0,0 +1,124 @@
+/* $Id: l3dss1.h,v 1.10.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * DSS1 (Euro) D-channel protocol defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef l3dss1_process
+
+#define T302	15000
+#define T303	4000
+#define T304	30000
+#define T305	30000
+#define T308	4000
+/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */
+/* This makes some tests easier and quicker */
+#define T309	40000
+#define T310	30000
+#define T313	4000
+#define T318	4000
+#define T319	4000
+
+/*
+ * Message-Types
+ */
+
+#define MT_ALERTING		0x01
+#define MT_CALL_PROCEEDING	0x02
+#define MT_CONNECT		0x07
+#define MT_CONNECT_ACKNOWLEDGE	0x0f
+#define MT_PROGRESS		0x03
+#define MT_SETUP		0x05
+#define MT_SETUP_ACKNOWLEDGE	0x0d
+#define MT_RESUME		0x26
+#define MT_RESUME_ACKNOWLEDGE	0x2e
+#define MT_RESUME_REJECT	0x22
+#define MT_SUSPEND		0x25
+#define MT_SUSPEND_ACKNOWLEDGE	0x2d
+#define MT_SUSPEND_REJECT	0x21
+#define MT_USER_INFORMATION	0x20
+#define MT_DISCONNECT		0x45
+#define MT_RELEASE		0x4d
+#define MT_RELEASE_COMPLETE	0x5a
+#define MT_RESTART		0x46
+#define MT_RESTART_ACKNOWLEDGE	0x4e
+#define MT_SEGMENT		0x60
+#define MT_CONGESTION_CONTROL	0x79
+#define MT_INFORMATION		0x7b
+#define MT_FACILITY		0x62
+#define MT_NOTIFY		0x6e
+#define MT_STATUS		0x7d
+#define MT_STATUS_ENQUIRY	0x75
+
+#define IE_SEGMENT	0x00
+#define IE_BEARER	0x04
+#define IE_CAUSE	0x08
+#define IE_CALL_ID	0x10
+#define IE_CALL_STATE	0x14
+#define IE_CHANNEL_ID	0x18
+#define IE_FACILITY	0x1c
+#define IE_PROGRESS	0x1e
+#define IE_NET_FAC	0x20
+#define IE_NOTIFY	0x27
+#define IE_DISPLAY	0x28
+#define IE_DATE		0x29
+#define IE_KEYPAD	0x2c
+#define IE_SIGNAL	0x34
+#define IE_INFORATE	0x40
+#define IE_E2E_TDELAY	0x42
+#define IE_TDELAY_SEL	0x43
+#define IE_PACK_BINPARA	0x44
+#define IE_PACK_WINSIZE	0x45
+#define IE_PACK_SIZE	0x46
+#define IE_CUG		0x47
+#define	IE_REV_CHARGE	0x4a
+#define IE_CONNECT_PN	0x4c
+#define IE_CONNECT_SUB	0x4d
+#define IE_CALLING_PN	0x6c
+#define IE_CALLING_SUB	0x6d
+#define IE_CALLED_PN	0x70
+#define IE_CALLED_SUB	0x71
+#define IE_REDIR_NR	0x74
+#define IE_TRANS_SEL	0x78
+#define IE_RESTART_IND	0x79
+#define IE_LLC		0x7c
+#define IE_HLC		0x7d
+#define IE_USER_USER	0x7e
+#define IE_ESCAPE	0x7f
+#define IE_SHIFT	0x90
+#define IE_MORE_DATA	0xa0
+#define IE_COMPLETE	0xa1
+#define IE_CONGESTION	0xb0
+#define IE_REPEAT	0xd0
+
+#define IE_MANDATORY	0x0100
+/* mandatory not in every case */
+#define IE_MANDATORY_1	0x0200
+
+#define ERR_IE_COMPREHENSION	 1
+#define ERR_IE_UNRECOGNIZED	-1
+#define ERR_IE_LENGTH		-2
+#define ERR_IE_SEQUENCE		-3
+
+#else /* only l3dss1_process */
+
+/* l3dss1 specific data in l3 process */
+typedef struct
+  { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */
+    ulong ll_id; /* remebered ll id */
+    u8 remote_operation; /* handled remote operation, 0 = not active */ 
+    int proc; /* rememered procedure */  
+    ulong remote_result; /* result of remote operation for statcallb */
+    char uus1_data[35]; /* data send during alerting or disconnect */
+  } dss1_proc_priv;
+
+/* l3dss1 specific data in protocol stack */
+typedef struct
+  { unsigned char last_invoke_id; /* last used value for invoking */
+    unsigned char invoke_used[32]; /* 256 bits for 256 values */
+  } dss1_stk_priv;        
+
+#endif /* only l3dss1_process */
diff --git a/drivers/isdn/hisax/l3ni1.c b/drivers/isdn/hisax/l3ni1.c
new file mode 100644
index 0000000..3ab3a54
--- /dev/null
+++ b/drivers/isdn/hisax/l3ni1.c
@@ -0,0 +1,3189 @@
+/* $Id: l3ni1.c,v 2.8.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * NI1 D-channel protocol
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 2000.6.6 Initial implementation of routines for US NI1 
+ * Layer 3 protocol based on the EURO/DSS1 D-channel protocol 
+ * driver written by Karsten Keil et al.  
+ * NI-1 Hall of Fame - Thanks to.... 
+ * Ragnar Paulson - for some handy code fragments
+ * Will Scales - beta tester extraordinaire
+ * Brett Whittacre - beta tester and remote devel system in Vegas
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3ni1.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+const char *ni1_revision = "$Revision: 2.8.2.3 $";
+
+#define EXT_BEARER_CAPS 1
+
+#define	MsgHead(ptr, cref, mty) \
+	*ptr++ = 0x8; \
+	if (cref == -1) { \
+		*ptr++ = 0x0; \
+	} else { \
+		*ptr++ = 0x1; \
+		*ptr++ = cref^0x80; \
+	} \
+	*ptr++ = mty
+
+
+/**********************************************/
+/* get a new invoke id for remote operations. */
+/* Only a return value != 0 is valid          */
+/**********************************************/
+static unsigned char new_invoke_id(struct PStack *p)
+{
+	unsigned char retval;
+	int i;
+  
+	i = 32; /* maximum search depth */
+
+	retval = p->prot.ni1.last_invoke_id + 1; /* try new id */
+	while ((i) && (p->prot.ni1.invoke_used[retval >> 3] == 0xFF)) {
+		p->prot.ni1.last_invoke_id = (retval & 0xF8) + 8;
+		i--;
+	}  
+	if (i) {
+		while (p->prot.ni1.invoke_used[retval >> 3] & (1 << (retval & 7)))
+		retval++; 
+	} else
+		retval = 0;
+	p->prot.ni1.last_invoke_id = retval;
+	p->prot.ni1.invoke_used[retval >> 3] |= (1 << (retval & 7));
+	return(retval);  
+} /* new_invoke_id */
+
+/*************************/
+/* free a used invoke id */
+/*************************/
+static void free_invoke_id(struct PStack *p, unsigned char id)
+{
+
+  if (!id) return; /* 0 = invalid value */
+
+  p->prot.ni1.invoke_used[id >> 3] &= ~(1 << (id & 7));
+} /* free_invoke_id */  
+
+
+/**********************************************************/
+/* create a new l3 process and fill in ni1 specific data */
+/**********************************************************/
+static struct l3_process
+*ni1_new_l3_process(struct PStack *st, int cr)
+{  struct l3_process *proc;
+
+   if (!(proc = new_l3_process(st, cr))) 
+     return(NULL);
+
+   proc->prot.ni1.invoke_id = 0;
+   proc->prot.ni1.remote_operation = 0;
+   proc->prot.ni1.uus1_data[0] = '\0';
+   
+   return(proc);
+} /* ni1_new_l3_process */
+
+/************************************************/
+/* free a l3 process and all ni1 specific data */
+/************************************************/ 
+static void
+ni1_release_l3_process(struct l3_process *p)
+{
+   free_invoke_id(p->st,p->prot.ni1.invoke_id);
+   release_l3_process(p);
+} /* ni1_release_l3_process */
+ 
+/********************************************************/
+/* search a process with invoke id id and dummy callref */
+/********************************************************/
+static struct l3_process *
+l3ni1_search_dummy_proc(struct PStack *st, int id)
+{ struct l3_process *pc = st->l3.proc; /* start of processes */
+
+  if (!id) return(NULL);
+
+  while (pc)
+   { if ((pc->callref == -1) && (pc->prot.ni1.invoke_id == id))
+       return(pc);
+     pc = pc->next;
+   } 
+  return(NULL);
+} /* l3ni1_search_dummy_proc */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return result is delivered. id specifies the invoke id.   */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3ni1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = NI1_STAT_INVOKE_RES;
+     ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+     ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+     ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+     ic.parm.ni1_io.timeout= 0;
+     ic.parm.ni1_io.datalen = nlen;
+     ic.parm.ni1_io.data = p;
+     free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+     pc->prot.ni1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     ni1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
+} /* l3ni1_dummy_return_result */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return error is delivered. id specifies the invoke id.    */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_error_return(struct PStack *st, int id, ulong error)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+  struct l3_process *pc = NULL; 
+
+  if ((pc = l3ni1_search_dummy_proc(st, id)))
+   { L3DelTimer(&pc->timer); /* remove timer */
+
+     cs = pc->st->l1.hardware;
+     ic.driver = cs->myid;
+     ic.command = ISDN_STAT_PROT;
+     ic.arg = NI1_STAT_INVOKE_ERR;
+     ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+     ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+     ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+     ic.parm.ni1_io.timeout= error;
+     ic.parm.ni1_io.datalen = 0;
+     ic.parm.ni1_io.data = NULL;
+     free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+     pc->prot.ni1.invoke_id = 0; /* reset id */
+
+     cs->iif.statcallb(&ic);
+     ni1_release_l3_process(pc); 
+   }
+  else
+   l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
+} /* l3ni1_error_return */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a invoke is delivered. id specifies the invoke id.          */
+/*******************************************************************/ 
+static void 
+l3ni1_dummy_invoke(struct PStack *st, int cr, int id, 
+                    int ident, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs;
+
+  l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
+               (cr == -1) ? "local" : "broadcast",id,ident,nlen);
+  if (cr >= -1) return; /* ignore local data */
+
+  cs = st->l1.hardware;
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = NI1_STAT_INVOKE_BRD;
+  ic.parm.ni1_io.hl_id = id;
+  ic.parm.ni1_io.ll_id = 0;
+  ic.parm.ni1_io.proc = ident;
+  ic.parm.ni1_io.timeout= 0;
+  ic.parm.ni1_io.datalen = nlen;
+  ic.parm.ni1_io.data = p;
+
+  cs->iif.statcallb(&ic);
+} /* l3ni1_dummy_invoke */
+
+static void
+l3ni1_parse_facility(struct PStack *st, struct l3_process *pc,
+                      int cr, u_char * p)
+{
+	int qd_len = 0;
+	unsigned char nlen = 0, ilen, cp_tag;
+	int ident, id;
+	ulong err_ret;
+
+	if (pc) 
+		st = pc->st; /* valid Stack */
+	else
+		if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
+
+	p++;
+	qd_len = *p++;
+	if (qd_len == 0) {
+		l3_debug(st, "qd_len == 0");
+		return;
+	}
+	if ((*p & 0x1F) != 0x11) {	/* Service discriminator, supplementary service */
+		l3_debug(st, "supplementary service != 0x11");
+		return;
+	}
+	while (qd_len > 0 && !(*p & 0x80)) {	/* extension ? */
+		p++;
+		qd_len--;
+	}
+	if (qd_len < 2) {
+		l3_debug(st, "qd_len < 2");
+		return;
+	}
+	p++;
+	qd_len--;
+	if ((*p & 0xE0) != 0xA0) {	/* class and form */
+		l3_debug(st, "class and form != 0xA0");
+		return;
+	}
+       
+        cp_tag = *p & 0x1F; /* remember tag value */
+
+        p++;
+	qd_len--;
+	if (qd_len < 1) 
+          { l3_debug(st, "qd_len < 1");
+	    return;
+	  }
+	if (*p & 0x80) 
+          { /* length format indefinite or limited */
+	    nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
+            if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
+                (nlen > 1))   
+	     { l3_debug(st, "length format error or not implemented");
+	       return;
+             }
+            if (nlen == 1)
+	     { nlen = *p++; /* complete length */
+               qd_len--;
+             } 
+            else
+	     { qd_len -= 2; /* trailing null bytes */
+               if ((*(p+qd_len)) || (*(p+qd_len+1)))
+		{ l3_debug(st,"length format indefinite error");
+                  return;
+                }
+               nlen = qd_len;
+             }
+	  }
+        else
+	  { nlen = *p++;
+	    qd_len--;
+          } 
+	if (qd_len < nlen) 
+          { l3_debug(st, "qd_len < nlen");
+	    return;
+	  }
+	qd_len -= nlen;
+
+	if (nlen < 2) 
+          { l3_debug(st, "nlen < 2");
+	    return;
+	  }
+        if (*p != 0x02) 
+          {  /* invoke identifier tag */
+	     l3_debug(st, "invoke identifier tag !=0x02");
+	     return;
+	  }
+	p++;
+	nlen--;
+	if (*p & 0x80) 
+          { /* length format */
+	    l3_debug(st, "invoke id length format 2");
+	    return;
+	  }
+	ilen = *p++;
+	nlen--;
+	if (ilen > nlen || ilen == 0) 
+          { l3_debug(st, "ilen > nlen || ilen == 0");
+	    return;
+	  }
+	nlen -= ilen;
+	id = 0;
+	while (ilen > 0) 
+          { id = (id << 8) | (*p++ & 0xFF);	/* invoke identifier */
+	    ilen--;
+	  }
+
+	switch (cp_tag) {	/* component tag */
+		case 1:	/* invoke */
+				if (nlen < 2) {
+					l3_debug(st, "nlen < 2 22");
+					return;
+				}
+				if (*p != 0x02) {	/* operation value */
+					l3_debug(st, "operation value !=0x02");
+					return;
+				}
+				p++;
+				nlen--;
+				ilen = *p++;
+				nlen--;
+				if (ilen > nlen || ilen == 0) {
+					l3_debug(st, "ilen > nlen || ilen == 0 22");
+					return;
+				}
+				nlen -= ilen;
+				ident = 0;
+				while (ilen > 0) {
+					ident = (ident << 8) | (*p++ & 0xFF);
+					ilen--;
+				}
+
+				if (!pc) 
+				{
+					l3ni1_dummy_invoke(st, cr, id, ident, p, nlen);
+					return;
+				} 
+				l3_debug(st, "invoke break");
+				break;
+		case 2:	/* return result */
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1) 
+                             l3ni1_dummy_return_result(st, id, p, nlen);
+                           return; 
+                         }   
+                        if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+                          { /* Diversion successful */
+                            free_invoke_id(st,pc->prot.ni1.invoke_id);
+                            pc->prot.ni1.remote_result = 0; /* success */     
+                            pc->prot.ni1.invoke_id = 0;
+                            pc->redir_result = pc->prot.ni1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);                                  } /* Diversion successful */
+                        else
+                          l3_debug(st,"return error unknown identifier");
+			break;
+		case 3:	/* return error */
+                            err_ret = 0;
+	                    if (nlen < 2) 
+                              { l3_debug(st, "return error nlen < 2");
+	                        return;
+	                      }
+                            if (*p != 0x02) 
+                              { /* result tag */
+	                        l3_debug(st, "invoke error tag !=0x02");
+	                        return;
+	                      }
+	                    p++;
+	                    nlen--;
+	                    if (*p > 4) 
+                              { /* length format */
+	                        l3_debug(st, "invoke return errlen > 4 ");
+	                        return;
+	                      }
+	                    ilen = *p++;
+	                    nlen--;
+	                    if (ilen > nlen || ilen == 0) 
+                              { l3_debug(st, "error return ilen > nlen || ilen == 0");
+	                        return;
+	                       }
+	                    nlen -= ilen;
+	                    while (ilen > 0) 
+                             { err_ret = (err_ret << 8) | (*p++ & 0xFF);	/* error value */
+	                       ilen--;
+	                     }
+			 /* if no process available handle separately */ 
+                        if (!pc)
+			 { if (cr == -1)
+                             l3ni1_dummy_error_return(st, id, err_ret);
+                           return; 
+                         }   
+                        if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+                          { /* Deflection error */
+                            free_invoke_id(st,pc->prot.ni1.invoke_id);
+                            pc->prot.ni1.remote_result = err_ret; /* result */
+                            pc->prot.ni1.invoke_id = 0; 
+                            pc->redir_result = pc->prot.ni1.remote_result; 
+                            st->l3.l3l4(st, CC_REDIR | INDICATION, pc);  
+                          } /* Deflection error */
+                        else
+                          l3_debug(st,"return result unknown identifier");
+			break;
+		default:
+			l3_debug(st, "facility default break tag=0x%02x",cp_tag);
+			break;
+	}
+}
+
+static void
+l3ni1_message(struct l3_process *pc, u_char mt)
+{
+	struct sk_buff *skb;
+	u_char *p;
+
+	if (!(skb = l3_alloc_skb(4)))
+		return;
+	p = skb_put(skb, 4);
+	MsgHead(p, pc->callref, mt);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_message_plus_chid(struct l3_process *pc, u_char mt)
+/* sends an l3 messages plus channel id -  added GE 05/09/00 */
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	u_char chid;
+
+	chid = (u_char)(pc->para.bchannel & 0x03) | 0x88;
+	MsgHead(p, pc->callref, mt);
+	*p++ = IE_CHANNEL_ID;
+	*p++ = 0x01;
+	*p++ = chid;
+
+	if (!(skb = l3_alloc_skb(7)))
+		return;
+	memcpy(skb_put(skb, 7), tmp, 7);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, mt);
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_status_send(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	MsgHead(p, pc->callref, MT_STATUS);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = pc->para.cause | 0x80;
+
+	*p++ = IE_CALL_STATE;
+	*p++ = 0x1;
+	*p++ = pc->state & 0x3f;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	/* This routine is called if here was no SETUP made (checks in ni1up and in
+	 * l3ni1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+	 * MT_STATUS_ENQUIRE in the NULL state is handled too
+	 */
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+
+	switch (pc->para.cause) {
+		case 81:	/* invalid callreference */
+		case 88:	/* incomp destination */
+		case 96:	/* mandory IE missing */
+		case 100:       /* invalid IE contents */
+		case 101:	/* incompatible Callstate */
+			MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+			*p++ = IE_CAUSE;
+			*p++ = 0x2;
+			*p++ = 0x80;
+			*p++ = pc->para.cause | 0x80;
+			break;
+		default:
+			printk(KERN_ERR "HiSax l3ni1_msg_without_setup wrong cause %d\n",
+				pc->para.cause);
+			return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	ni1_release_l3_process(pc);
+}
+
+static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_HLC,
+		IE_USER_USER, -1};
+static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1};
+static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, 
+		IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL,
+		IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1};
+static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1};
+static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY,
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
+static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL,
+		IE_CALLED_PN, -1};
+static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS |
+		IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1};
+static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY,
+		IE_SIGNAL, IE_USER_USER, -1};
+/* a RELEASE_COMPLETE with errors don't require special actions 
+static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
+*/
+static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY,
+		IE_DISPLAY, -1};
+static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER  | IE_MANDATORY,
+		IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS,
+		IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN,
+		IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR,
+		IE_LLC, IE_HLC, IE_USER_USER, -1};
+static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY,
+		IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1};
+static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE |
+		IE_MANDATORY, IE_DISPLAY, -1};
+static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1};
+static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_DISPLAY, IE_FACILITY, -1};
+static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+/* not used 
+ * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY,
+ *		IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
+ * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1};
+ * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND |
+ *		IE_MANDATORY, -1};
+ */
+static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1};
+static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1};
+static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1};
+
+struct ie_len {
+	int ie;
+	int len;
+};
+
+static
+struct ie_len max_ie_len[] = {
+	{IE_SEGMENT, 4},
+	{IE_BEARER, 12},
+	{IE_CAUSE, 32},
+	{IE_CALL_ID, 10},
+	{IE_CALL_STATE, 3},
+	{IE_CHANNEL_ID,	34},
+	{IE_FACILITY, 255},
+	{IE_PROGRESS, 4},
+	{IE_NET_FAC, 255},
+	{IE_NOTIFY, 3},
+	{IE_DISPLAY, 82},
+	{IE_DATE, 8},
+	{IE_KEYPAD, 34},
+	{IE_SIGNAL, 3},
+	{IE_INFORATE, 6},
+	{IE_E2E_TDELAY, 11},
+	{IE_TDELAY_SEL, 5},
+	{IE_PACK_BINPARA, 3},
+	{IE_PACK_WINSIZE, 4},
+	{IE_PACK_SIZE, 4},
+	{IE_CUG, 7},
+	{IE_REV_CHARGE, 3},
+	{IE_CALLING_PN, 24},
+	{IE_CALLING_SUB, 23},
+	{IE_CALLED_PN, 24},
+	{IE_CALLED_SUB, 23},
+	{IE_REDIR_NR, 255},
+	{IE_TRANS_SEL, 255},
+	{IE_RESTART_IND, 3},
+	{IE_LLC, 18},
+	{IE_HLC, 5},
+	{IE_USER_USER, 131},
+	{-1,0},
+};
+
+static int
+getmax_ie_len(u_char ie) {
+	int i = 0;
+	while (max_ie_len[i].ie != -1) {
+		if (max_ie_len[i].ie == ie)
+			return(max_ie_len[i].len);
+		i++;
+	}
+	return(255);
+}
+
+static int
+ie_in_set(struct l3_process *pc, u_char ie, int *checklist) {
+	int ret = 1;
+
+	while (*checklist != -1) {
+		if ((*checklist & 0xff) == ie) {
+			if (ie & 0x80)
+				return(-ret);
+			else
+				return(ret);
+		}
+		ret++;
+		checklist++;
+	}
+	return(0);
+}
+
+static int
+check_infoelements(struct l3_process *pc, struct sk_buff *skb, int *checklist)
+{
+	int *cl = checklist;
+	u_char mt;
+	u_char *p, ie;
+	int l, newpos, oldpos;
+	int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+	u_char codeset = 0;
+	u_char old_codeset = 0;
+	u_char codelock = 1;
+	
+	p = skb->data;
+	/* skip cr */
+	p++;
+	l = (*p++) & 0xf;
+	p += l;
+	mt = *p++;
+	oldpos = 0;
+	while ((p - skb->data) < skb->len) {
+		if ((*p & 0xf0) == 0x90) { /* shift codeset */
+			old_codeset = codeset;
+			codeset = *p & 7;
+			if (*p & 0x08)
+				codelock = 0;
+			else
+				codelock = 1;
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+					codelock ? " locking ": " ", old_codeset, codeset);
+			p++;
+			continue;
+		}
+		if (!codeset) { /* only codeset 0 */
+			if ((newpos = ie_in_set(pc, *p, cl))) {
+				if (newpos > 0) {
+					if (newpos < oldpos)
+						err_seq++;
+					else
+						oldpos = newpos;
+				}
+			} else {
+				if (ie_in_set(pc, *p, comp_required))
+					err_compr++;
+				else
+					err_ureg++;
+			}
+		}
+		ie = *p++;
+		if (ie & 0x80) {
+			l = 1;
+		} else {
+			l = *p++;
+			p += l;
+			l += 2;
+		}
+		if (!codeset && (l > getmax_ie_len(ie)))
+			err_len++;
+		if (!codelock) {
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "check IE shift back codeset %d->%d",
+					codeset, old_codeset);
+			codeset = old_codeset;
+			codelock = 1;
+		}
+	}
+	if (err_compr | err_ureg | err_len | err_seq) {
+		if (pc->debug & L3_DEB_CHECK)
+			l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
+				mt, err_compr, err_ureg, err_len, err_seq);
+		if (err_compr)
+			return(ERR_IE_COMPREHENSION);
+		if (err_ureg)
+			return(ERR_IE_UNRECOGNIZED);
+		if (err_len)
+			return(ERR_IE_LENGTH);
+		if (err_seq)
+			return(ERR_IE_SEQUENCE);
+	} 
+	return(0);
+}
+
+/* verify if a message type exists and contain no IE error */
+static int
+l3ni1_check_messagetype_validity(struct l3_process *pc, int mt, void *arg)
+{
+	switch (mt) {
+		case MT_ALERTING:
+		case MT_CALL_PROCEEDING:
+		case MT_CONNECT:
+		case MT_CONNECT_ACKNOWLEDGE:
+		case MT_DISCONNECT:
+		case MT_INFORMATION:
+		case MT_FACILITY:
+		case MT_NOTIFY:
+		case MT_PROGRESS:
+		case MT_RELEASE:
+		case MT_RELEASE_COMPLETE:
+		case MT_SETUP:
+		case MT_SETUP_ACKNOWLEDGE:
+		case MT_RESUME_ACKNOWLEDGE:
+		case MT_RESUME_REJECT:
+		case MT_SUSPEND_ACKNOWLEDGE:
+		case MT_SUSPEND_REJECT:
+		case MT_USER_INFORMATION:
+		case MT_RESTART:
+		case MT_RESTART_ACKNOWLEDGE:
+		case MT_CONGESTION_CONTROL:
+		case MT_STATUS:
+		case MT_STATUS_ENQUIRY:
+			if (pc->debug & L3_DEB_CHECK)
+				l3_debug(pc->st, "l3ni1_check_messagetype_validity mt(%x) OK", mt);
+			break;
+		case MT_RESUME: /* RESUME only in user->net */
+		case MT_SUSPEND: /* SUSPEND only in user->net */
+		default:
+			if (pc->debug & (L3_DEB_CHECK | L3_DEB_WARN))
+				l3_debug(pc->st, "l3ni1_check_messagetype_validity mt(%x) fail", mt);
+			pc->para.cause = 97;
+			l3ni1_status_send(pc, 0, NULL);
+			return(1);
+	}
+	return(0);
+}
+
+static void
+l3ni1_std_ie_err(struct l3_process *pc, int ret) {
+
+	if (pc->debug & L3_DEB_CHECK)
+		l3_debug(pc->st, "check_infoelements ret %d", ret);
+	switch(ret) {
+		case 0: 
+			break;
+		case ERR_IE_COMPREHENSION:
+			pc->para.cause = 96;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_UNRECOGNIZED:
+			pc->para.cause = 99;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_LENGTH:
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, 0, NULL);
+			break;
+		case ERR_IE_SEQUENCE:
+		default:
+			break;
+	}
+}
+
+static int
+l3ni1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
+	u_char *p;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		p++;
+		if (*p != 1) { /* len for BRI = 1 */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid len %d", *p);
+			return (-2);
+		}
+		p++;
+		if (*p & 0x60) { /* only base rate interface */
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "wrong chid %x", *p);
+			return (-3);
+		}
+		return(*p & 0x3);
+	} else
+		return(-1);
+}
+
+static int
+l3ni1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
+	u_char l, i=0;
+	u_char *p;
+
+	p = skb->data;
+	pc->para.cause = 31;
+	pc->para.loc = 0;
+	if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+		p++;
+		l = *p++;
+		if (l>30)
+			return(1);
+		if (l) {
+			pc->para.loc = *p++;
+			l--;
+		} else {
+			return(2);
+		}
+		if (l && !(pc->para.loc & 0x80)) {
+			l--;
+			p++; /* skip recommendation */
+		}
+		if (l) {
+			pc->para.cause = *p++;
+			l--;
+			if (!(pc->para.cause & 0x80))
+				return(3);
+		} else
+			return(4);
+		while (l && (i<6)) {
+			pc->para.diag[i++] = *p++;
+			l--;
+		}
+	} else
+		return(-1);
+	return(0);
+}
+
+static void
+l3ni1_msg_with_uus(struct l3_process *pc, u_char cmd)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+
+	MsgHead(p, pc->callref, cmd);
+
+        if (pc->prot.ni1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.ni1.uus1_data);
+           p += strlen(pc->prot.ni1.uus1_data);
+           pc->prot.ni1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_msg_with_uus */
+
+static void
+l3ni1_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	StopAllL3Timer(pc);
+	newl3state(pc, 19);
+	if (!pc->prot.ni1.uus1_data[0]) 
+		l3ni1_message(pc, MT_RELEASE);
+	else
+		l3ni1_msg_with_uus(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
+	} else if (ret < 0)
+		pc->para.cause = NO_CAUSE;
+	StopAllL3Timer(pc);
+	newl3state(pc, 0);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+	ni1_release_l3_process(pc);
+}
+
+#if EXT_BEARER_CAPS
+
+static u_char *
+EncodeASyncParams(u_char * p, u_char si2)
+{				// 7c 06 88  90 21 42 00 bb
+
+	p[0] = 0;
+	p[1] = 0x40;		// Intermediate rate: 16 kbit/s jj 2000.02.19
+	p[2] = 0x80;
+	if (si2 & 32)		// 7 data bits
+
+		p[2] += 16;
+	else			// 8 data bits
+
+		p[2] += 24;
+
+	if (si2 & 16)		// 2 stop bits
+
+		p[2] += 96;
+	else			// 1 stop bit
+
+		p[2] += 32;
+
+	if (si2 & 8)		// even parity
+
+		p[2] += 2;
+	else			// no parity
+
+		p[2] += 3;
+
+	switch (si2 & 0x07) {
+		case 0:
+			p[0] = 66;	// 1200 bit/s
+
+			break;
+		case 1:
+			p[0] = 88;	// 1200/75 bit/s
+
+			break;
+		case 2:
+			p[0] = 87;	// 75/1200 bit/s
+
+			break;
+		case 3:
+			p[0] = 67;	// 2400 bit/s
+
+			break;
+		case 4:
+			p[0] = 69;	// 4800 bit/s
+
+			break;
+		case 5:
+			p[0] = 72;	// 9600 bit/s
+
+			break;
+		case 6:
+			p[0] = 73;	// 14400 bit/s
+
+			break;
+		case 7:
+			p[0] = 75;	// 19200 bit/s
+
+			break;
+	}
+	return p + 3;
+}
+
+static u_char
+EncodeSyncParams(u_char si2, u_char ai)
+{
+
+	switch (si2) {
+		case 0:
+			return ai + 2;	// 1200 bit/s
+
+		case 1:
+			return ai + 24;		// 1200/75 bit/s
+
+		case 2:
+			return ai + 23;		// 75/1200 bit/s
+
+		case 3:
+			return ai + 3;	// 2400 bit/s
+
+		case 4:
+			return ai + 5;	// 4800 bit/s
+
+		case 5:
+			return ai + 8;	// 9600 bit/s
+
+		case 6:
+			return ai + 9;	// 14400 bit/s
+
+		case 7:
+			return ai + 11;		// 19200 bit/s
+
+		case 8:
+			return ai + 14;		// 48000 bit/s
+
+		case 9:
+			return ai + 15;		// 56000 bit/s
+
+		case 15:
+			return ai + 40;		// negotiate bit/s
+
+		default:
+			break;
+	}
+	return ai;
+}
+
+
+static u_char
+DecodeASyncParams(u_char si2, u_char * p)
+{
+	u_char info;
+
+	switch (p[5]) {
+		case 66:	// 1200 bit/s
+
+			break;	// si2 don't change
+
+		case 88:	// 1200/75 bit/s
+
+			si2 += 1;
+			break;
+		case 87:	// 75/1200 bit/s
+
+			si2 += 2;
+			break;
+		case 67:	// 2400 bit/s
+
+			si2 += 3;
+			break;
+		case 69:	// 4800 bit/s
+
+			si2 += 4;
+			break;
+		case 72:	// 9600 bit/s
+
+			si2 += 5;
+			break;
+		case 73:	// 14400 bit/s
+
+			si2 += 6;
+			break;
+		case 75:	// 19200 bit/s
+
+			si2 += 7;
+			break;
+	}
+
+	info = p[7] & 0x7f;
+	if ((info & 16) && (!(info & 8)))	// 7 data bits
+
+		si2 += 32;	// else 8 data bits
+
+	if ((info & 96) == 96)	// 2 stop bits
+
+		si2 += 16;	// else 1 stop bit
+
+	if ((info & 2) && (!(info & 1)))	// even parity
+
+		si2 += 8;	// else no parity
+
+	return si2;
+}
+
+
+static u_char
+DecodeSyncParams(u_char si2, u_char info)
+{
+	info &= 0x7f;
+	switch (info) {
+		case 40:	// bit/s negotiation failed  ai := 165 not 175!
+
+			return si2 + 15;
+		case 15:	// 56000 bit/s failed, ai := 0 not 169 !
+
+			return si2 + 9;
+		case 14:	// 48000 bit/s
+
+			return si2 + 8;
+		case 11:	// 19200 bit/s
+
+			return si2 + 7;
+		case 9:	// 14400 bit/s
+
+			return si2 + 6;
+		case 8:	// 9600  bit/s
+
+			return si2 + 5;
+		case 5:	// 4800  bit/s
+
+			return si2 + 4;
+		case 3:	// 2400  bit/s
+
+			return si2 + 3;
+		case 23:	// 75/1200 bit/s
+
+			return si2 + 2;
+		case 24:	// 1200/75 bit/s
+
+			return si2 + 1;
+		default:	// 1200 bit/s
+
+			return si2;
+	}
+}
+
+static u_char
+DecodeSI2(struct sk_buff *skb)
+{
+	u_char *p;		//, *pend=skb->data + skb->len;
+
+	if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+		switch (p[4] & 0x0f) {
+			case 0x01:
+				if (p[1] == 0x04)	// sync. Bitratenadaption
+
+					return DecodeSyncParams(160, p[5]);	// V.110/X.30
+
+				else if (p[1] == 0x06)	// async. Bitratenadaption
+
+					return DecodeASyncParams(192, p);	// V.110/X.30
+
+				break;
+			case 0x08:	// if (p[5] == 0x02) // sync. Bitratenadaption
+				if (p[1] > 3) 
+					return DecodeSyncParams(176, p[5]);	// V.120
+				break;
+		}
+	}
+	return 0;
+}
+
+#endif
+
+
+static void
+l3ni1_setup_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+
+	u_char *teln;
+	u_char *sub;
+	u_char *sp;
+	int l;
+
+	MsgHead(p, pc->callref, MT_SETUP);
+
+	teln = pc->para.setup.phone;
+
+	*p++ = 0xa1;		/* complete indicator */
+	/*
+	 * Set Bearer Capability, Map info from 1TR6-convention to NI1
+	 */
+	switch (pc->para.setup.si1) {
+	case 1:	                  /* Telephony                                */
+		*p++ = IE_BEARER;
+		*p++ = 0x3;	  /* Length                                   */
+		*p++ = 0x90;	  /* 3.1khz Audio      			      */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		*p++ = 0xa2;	  /* u-Law Audio                              */
+		break;
+	case 5:	                  /* Datatransmission 64k, BTX                */
+	case 7:	                  /* Datatransmission 64k                     */
+	default:
+		*p++ = IE_BEARER;
+		*p++ = 0x2;	  /* Length                                   */
+		*p++ = 0x88;	  /* Coding Std. CCITT, unrestr. dig. Inform. */
+		*p++ = 0x90;	  /* Circuit-Mode 64kbps                      */
+		break;
+	}
+
+	sub = NULL;
+	sp = teln;
+	while (*sp) {
+		if ('.' == *sp) {
+			sub = sp;
+			*sp = 0;
+		} else
+			sp++;
+	}
+	
+	*p++ = IE_KEYPAD;
+	*p++ = strlen(teln);
+	while (*teln)
+		*p++ = (*teln++) & 0x7F;
+
+	if (sub)
+		*sub++ = '.';
+	
+#if EXT_BEARER_CAPS
+	if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) {	// sync. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x04;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+	} else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) {	// sync. Bitratenadaption, V.120
+
+		*p++ = IE_LLC;
+		*p++ = 0x05;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x28;
+		*p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+		*p++ = 0x82;
+	} else if (pc->para.setup.si2 >= 192) {		// async. Bitratenadaption, V.110/X.30
+
+		*p++ = IE_LLC;
+		*p++ = 0x06;
+		*p++ = 0x88;
+		*p++ = 0x90;
+		*p++ = 0x21;
+		p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+	} else {
+	  switch (pc->para.setup.si1) {
+		case 1:	                /* Telephony                                */
+			*p++ = IE_LLC;
+			*p++ = 0x3;	/* Length                                   */
+			*p++ = 0x90;	/* Coding Std. CCITT, 3.1 kHz audio         */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			*p++ = 0xa2;	/* u-Law Audio                              */
+			break;
+		case 5:	                /* Datatransmission 64k, BTX                */
+		case 7:	                /* Datatransmission 64k                     */
+		default:
+			*p++ = IE_LLC;
+			*p++ = 0x2;	/* Length                                   */
+			*p++ = 0x88;	/* Coding Std. CCITT, unrestr. dig. Inform. */
+			*p++ = 0x90;	/* Circuit-Mode 64kbps                      */
+			break;
+	  }
+	}
+#endif
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+{
+		return;
+}
+	memcpy(skb_put(skb, l), tmp, l);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T303, CC_T303);
+	newl3state(pc, 1);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_call_proc(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 3);
+	L3AddTimer(&pc->timer, T310, CC_T310);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup answer with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 2);
+	L3AddTimer(&pc->timer, T304, CC_T304);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3ni1_disconnect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret;
+	u_char cause = 0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	} 
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+		l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	ret = check_infoelements(pc, skb, ie_DISCONNECT);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
+		cause = 99;
+	ret = pc->state;
+	newl3state(pc, 12);
+	if (cause)
+		newl3state(pc, 19);
+       	if (11 != ret)
+		pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+       	else if (!cause)
+		   l3ni1_release_req(pc, pr, NULL);
+	if (cause) {
+		l3ni1_message_cause(pc, MT_RELEASE, cause);
+		L3AddTimer(&pc->timer, T308, CC_T308_1);
+	}
+}
+
+static void
+l3ni1_connect(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_CONNECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T310 */
+	newl3state(pc, 10);
+	pc->para.chargeinfo = 0;
+	/* here should inserted COLP handling KKe */
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3ni1_alerting(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_ALERTING);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);	/* T304 */
+	newl3state(pc, 4);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	int bcfound = 0;
+	char tmp[80];
+	struct sk_buff *skb = arg;
+	int id;
+	int err = 0;
+
+	/*
+	 * Bearer Capabilities
+	 */
+	p = skb->data;
+	/* only the first occurence 'll be detected ! */
+	if ((p = findie(p, skb->len, 0x04, 0))) {
+		if ((p[1] < 2) || (p[1] > 11))
+			err = 1;
+		else {
+			pc->para.setup.si2 = 0;
+			switch (p[2] & 0x7f) {
+				case 0x00: /* Speech */
+				case 0x10: /* 3.1 Khz audio */
+					pc->para.setup.si1 = 1;
+					break;
+				case 0x08: /* Unrestricted digital information */
+					pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#if EXT_BEARER_CAPS
+					pc->para.setup.si2 = DecodeSI2(skb);
+#endif
+					break;
+				case 0x09: /* Restricted digital information */
+					pc->para.setup.si1 = 2;
+					break;
+				case 0x11:
+					/* Unrestr. digital information  with 
+					 * tones/announcements ( or 7 kHz audio
+					 */
+					pc->para.setup.si1 = 3;
+					break;
+				case 0x18: /* Video */
+					pc->para.setup.si1 = 4;
+					break;
+				default:
+					err = 2;
+					break;
+			}
+			switch (p[3] & 0x7f) {
+				case 0x40: /* packed mode */
+					pc->para.setup.si1 = 8;
+					break;
+				case 0x10: /* 64 kbit */
+				case 0x11: /* 2*64 kbit */
+				case 0x13: /* 384 kbit */
+				case 0x15: /* 1536 kbit */
+				case 0x17: /* 1920 kbit */
+					pc->para.moderate = p[3] & 0x7f;
+					break;
+				default:
+					err = 3;
+					break;
+			}
+		}
+		if (pc->debug & L3_DEB_SI)
+			l3_debug(pc->st, "SI=%d, AI=%d",
+				pc->para.setup.si1, pc->para.setup.si2);
+		if (err) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
+					p[1], p[2], p[3]);
+			pc->para.cause = 100;
+			l3ni1_msg_without_setup(pc, pr, NULL);
+			return;
+		}
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup without bearer capabilities");
+		/* ETS 300-104 1.3.3 */
+		pc->para.cause = 96;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/*
+	 * Channel Identification
+	 */
+	if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+		if ((pc->para.bchannel = id)) {
+			if ((3 == id) && (0x10 == pc->para.moderate)) {
+				if (pc->debug & L3_DEB_WARN)
+					l3_debug(pc->st, "setup with wrong chid %x",
+						id);
+				pc->para.cause = 100;
+				l3ni1_msg_without_setup(pc, pr, NULL);
+				return;
+			}
+			bcfound++;
+		} else 
+                   { if (pc->debug & L3_DEB_WARN)
+			 l3_debug(pc->st, "setup without bchannel, call waiting");
+                     bcfound++;
+                   } 
+	} else {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "setup with wrong chid ret %d", id);
+		if (id == -1)
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_SETUP);
+	if (ERR_IE_COMPREHENSION == err) {
+		pc->para.cause = 96;
+		l3ni1_msg_without_setup(pc, pr, NULL);
+		return;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x70, 0)))
+		iecpy(pc->para.setup.eazmsn, p, 1);
+	else
+		pc->para.setup.eazmsn[0] = 0;
+
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x71, 0))) {
+		/* Called party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.eazmsn, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong called subaddress");
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6c, 0))) {
+		pc->para.setup.plan = p[2];
+		if (p[2] & 0x80) {
+			iecpy(pc->para.setup.phone, p, 1);
+			pc->para.setup.screen = 0;
+		} else {
+			iecpy(pc->para.setup.phone, p, 2);
+			pc->para.setup.screen = p[3];
+		}
+	} else {
+		pc->para.setup.phone[0] = 0;
+		pc->para.setup.plan = 0;
+		pc->para.setup.screen = 0;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, 0x6d, 0))) {
+		/* Calling party subaddress */
+		if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+			tmp[0] = '.';
+			iecpy(&tmp[1], p, 2);
+			strcat(pc->para.setup.phone, tmp);
+		} else if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "wrong calling subaddress");
+	}
+	newl3state(pc, 6);
+	if (err) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, err);
+	pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+}
+
+static void
+l3ni1_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16+40];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 16;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	StopAllL3Timer(pc);
+
+	MsgHead(p, pc->callref, MT_DISCONNECT);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+        if (pc->prot.ni1.uus1_data[0])
+	 { *p++ = IE_USER_USER; /* UUS info element */
+           *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+           *p++ = 0x04; /* IA5 chars */
+           strcpy(p,pc->prot.ni1.uus1_data);
+           p += strlen(pc->prot.ni1.uus1_data);
+           pc->prot.ni1.uus1_data[0] = '\0';   
+         } 
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 11);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3ni1_setup_rsp(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+        if (!pc->para.bchannel) 
+	 { if (pc->debug & L3_DEB_WARN)
+	       l3_debug(pc->st, "D-chan connect for waiting call");
+           l3ni1_disconnect_req(pc, pr, arg);
+           return;
+         }
+	newl3state(pc, 8);
+	if (pc->debug & L3_DEB_WARN)
+		l3_debug(pc->st, "D-chan connect for waiting call");
+	l3ni1_message_plus_chid(pc, MT_CONNECT); /* GE 05/09/00 */ 
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3ni1_connect_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	newl3state(pc, 10);
+	L3DelTimer(&pc->timer);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3ni1_reject_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	u_char cause = 21;
+
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_release(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	u_char *p;
+	int ret, cause=0;
+
+	StopAllL3Timer(pc);
+	if ((ret = l3ni1_get_cause(pc, skb))>0) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "REL get_cause ret(%d)", ret);
+	} else if (ret<0)
+		pc->para.cause = NO_CAUSE;
+	if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+		l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+	if ((ret<0) && (pc->state != 11))
+		cause = 96;
+	else if (ret>0)
+		cause = 100;
+	ret = check_infoelements(pc, skb, ie_RELEASE);
+	if (ERR_IE_COMPREHENSION == ret)
+		cause = 96;
+	else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
+		cause = 99;  
+	if (cause)
+		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
+	else
+		l3ni1_message(pc, MT_RELEASE_COMPLETE);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	newl3state(pc, 0);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_alert_req(struct l3_process *pc, u_char pr,
+		 void *arg)
+{
+	newl3state(pc, 7);
+	if (!pc->prot.ni1.uus1_data[0]) 
+		l3ni1_message(pc, MT_ALERTING);
+	else
+		l3ni1_msg_with_uus(pc, MT_ALERTING); 
+}
+
+static void
+l3ni1_proceed_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 9);
+	l3ni1_message(pc, MT_CALL_PROCEEDING);
+	pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc); 
+}
+
+static void
+l3ni1_setup_ack_req(struct l3_process *pc, u_char pr,
+		   void *arg)
+{
+	newl3state(pc, 25);
+	L3DelTimer(&pc->timer);
+	L3AddTimer(&pc->timer, T302, CC_T302);
+	l3ni1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
+/********************************************/
+/* deliver a incoming display message to HL */
+/********************************************/
+static void
+l3ni1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
+{       u_char len;
+        isdn_ctrl ic; 
+	struct IsdnCardState *cs;
+        char *p; 
+
+        if (*infp++ != IE_DISPLAY) return;
+        if ((len = *infp++) > 80) return; /* total length <= 82 */
+	if (!pc->chan) return;
+
+	p = ic.parm.display; 
+        while (len--)
+	  *p++ = *infp++;
+	*p = '\0';
+	ic.command = ISDN_STAT_DISPLAY;
+	cs = pc->st->l1.hardware;
+	ic.driver = cs->myid;
+	ic.arg = pc->chan->chan; 
+	cs->iif.statcallb(&ic);
+} /* l3ni1_deliver_display */
+
+
+static void
+l3ni1_progress(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
+		if (p[1] != 2) {
+			err = 1;
+			pc->para.cause = 100;
+		} else if (!(p[2] & 0x70)) {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+				case 0x84:
+				case 0x85:
+				case 0x87:
+				case 0x8a:
+					switch (p[3]) {
+						case 0x81:
+						case 0x82:
+						case 0x83:
+						case 0x84:
+						case 0x88:
+							break;
+						default:
+							err = 2;
+							pc->para.cause = 100;
+							break;
+					}
+					break;
+				default:
+					err = 3;
+					pc->para.cause = 100;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 4;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "progress error %d", err);
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_PROGRESS);
+	if (err)
+		l3ni1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+}
+
+static void
+l3ni1_notify(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int err = 0;
+	u_char *p;
+
+	if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
+		if (p[1] != 1) {
+			err = 1;
+			pc->para.cause = 100;
+		} else {
+			switch (p[2]) {
+				case 0x80:
+				case 0x81:
+				case 0x82:
+					break;
+				default:
+					pc->para.cause = 100;
+					err = 2;
+					break;
+			}
+		}
+	} else {
+		pc->para.cause = 96;
+		err = 3;
+	}
+	if (err) {	
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "notify error %d", err);
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	/* Now we are on none mandatory IEs */
+	err = check_infoelements(pc, skb, ie_NOTIFY);
+	if (err)
+		l3ni1_std_ie_err(pc, err);
+	if (ERR_IE_COMPREHENSION != err)
+		pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+}
+
+static void
+l3ni1_status_enq(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+
+	ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
+	l3ni1_std_ie_err(pc, ret);
+	pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+        l3ni1_status_send(pc, pr, NULL);
+}
+
+static void
+l3ni1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+	int ret;
+	struct sk_buff *skb = arg;
+	u_char *p;
+	char tmp[32];
+
+	ret = check_infoelements(pc, skb, ie_INFORMATION);
+	if (ret)
+		l3ni1_std_ie_err(pc, ret);
+	if (pc->state == 25) { /* overlap receiving */
+		L3DelTimer(&pc->timer);
+		p = skb->data;
+		if ((p = findie(p, skb->len, 0x70, 0))) {
+			iecpy(tmp, p, 1);
+			strcat(pc->para.setup.eazmsn, tmp);
+			pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+		}
+		L3AddTimer(&pc->timer, T302, CC_T302);
+	}
+}
+
+/******************************/
+/* handle deflection requests */
+/******************************/
+static void l3ni1_redir_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[128];
+	u_char *p = tmp;
+        u_char *subp;
+        u_char len_phone = 0;
+        u_char len_sub = 0;
+	int l; 
+
+
+        strcpy(pc->prot.ni1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
+        if (!pc->chan->setup.phone[0])
+          { pc->para.cause = -1;
+            l3ni1_disconnect_req(pc,pr,arg); /* disconnect immediately */
+            return;
+          } /* only uus */
+ 
+        if (pc->prot.ni1.invoke_id) 
+          free_invoke_id(pc->st,pc->prot.ni1.invoke_id);
+ 
+        if (!(pc->prot.ni1.invoke_id = new_invoke_id(pc->st))) 
+          return;
+
+        MsgHead(p, pc->callref, MT_FACILITY);
+
+        for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
+        if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subaddress element */ 
+
+	*p++ = 0x1c;   /* Facility info element */
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
+        *p++ = 0x91;  /* remote operations protocol */
+        *p++ = 0xa1;  /* invoke component */
+	  
+        *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
+        *p++ = 0x02;  /* invoke id tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = pc->prot.ni1.invoke_id;  /* invoke id */ 
+        *p++ = 0x02;  /* operation value tag, integer */
+	*p++ = 0x01;  /* length */
+        *p++ = 0x0D;  /* Call Deflect */
+	  
+        *p++ = 0x30;  /* sequence phone number */
+        *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
+	  
+        *p++ = 0x30;  /* Deflected to UserNumber */
+        *p++ = len_phone+2+len_sub; /* length */
+        *p++ = 0x80; /* NumberDigits */
+	*p++ = len_phone; /* length */
+        for (l = 0; l < len_phone; l++)
+	 *p++ = pc->chan->setup.phone[l];
+
+        if (len_sub)
+	  { *p++ = 0x04; /* called party subaddress */
+            *p++ = len_sub - 2;
+            while (*subp) *p++ = *subp++;
+          }
+
+        *p++ = 0x01; /* screening identifier */
+        *p++ = 0x01;
+        *p++ = pc->chan->setup.screen;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l))) return;
+	memcpy(skb_put(skb, l), tmp, l);
+
+        l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_redir_req */
+
+/********************************************/
+/* handle deflection request in early state */
+/********************************************/
+static void l3ni1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
+{
+  l3ni1_proceed_req(pc,pr,arg);
+  l3ni1_redir_req(pc,pr,arg);
+} /* l3ni1_redir_req_early */
+
+/***********************************************/
+/* handle special commands for this protocol.  */
+/* Examples are call independant services like */
+/* remote operations with dummy  callref.      */
+/***********************************************/
+static int l3ni1_cmd_global(struct PStack *st, isdn_ctrl *ic)
+{ u_char id;
+  u_char temp[265];
+  u_char *p = temp;
+  int i, l, proc_len; 
+  struct sk_buff *skb;
+  struct l3_process *pc = NULL;
+
+  switch (ic->arg)
+   { case NI1_CMD_INVOKE:
+       if (ic->parm.ni1_io.datalen < 0) return(-2); /* invalid parameter */ 
+
+       for (proc_len = 1, i = ic->parm.ni1_io.proc >> 8; i; i++) 
+         i = i >> 8; /* add one byte */    
+       l = ic->parm.ni1_io.datalen + proc_len + 8; /* length excluding ie header */
+       if (l > 255) 
+         return(-2); /* too long */
+
+       if (!(id = new_invoke_id(st))) 
+         return(0); /* first get a invoke id -> return if no available */
+       
+       i = -1; 
+       MsgHead(p, i, MT_FACILITY); /* build message head */
+       *p++ = 0x1C; /* Facility IE */
+       *p++ = l; /* length of ie */
+       *p++ = 0x91; /* remote operations */
+       *p++ = 0xA1; /* invoke */
+       *p++ = l - 3; /* length of invoke */
+       *p++ = 0x02; /* invoke id tag */
+       *p++ = 0x01; /* length is 1 */
+       *p++ = id; /* invoke id */
+       *p++ = 0x02; /* operation */
+       *p++ = proc_len; /* length of operation */
+       
+       for (i = proc_len; i; i--)
+         *p++ = (ic->parm.ni1_io.proc >> (i-1)) & 0xFF;
+       memcpy(p, ic->parm.ni1_io.data, ic->parm.ni1_io.datalen); /* copy data */
+       l = (p - temp) + ic->parm.ni1_io.datalen; /* total length */         
+
+       if (ic->parm.ni1_io.timeout > 0)
+        if (!(pc = ni1_new_l3_process(st, -1)))
+          { free_invoke_id(st, id);
+            return(-2);
+          } 
+       pc->prot.ni1.ll_id = ic->parm.ni1_io.ll_id; /* remember id */ 
+       pc->prot.ni1.proc = ic->parm.ni1_io.proc; /* and procedure */
+
+       if (!(skb = l3_alloc_skb(l))) 
+         { free_invoke_id(st, id);
+           if (pc) ni1_release_l3_process(pc);
+           return(-2);
+         }
+       memcpy(skb_put(skb, l), temp, l);
+       
+       if (pc)
+        { pc->prot.ni1.invoke_id = id; /* remember id */
+          L3AddTimer(&pc->timer, ic->parm.ni1_io.timeout, CC_TNI1_IO | REQUEST);
+        }
+       
+       l3_msg(st, DL_DATA | REQUEST, skb);
+       ic->parm.ni1_io.hl_id = id; /* return id */
+       return(0);
+
+     case NI1_CMD_INVOKE_ABORT:
+       if ((pc = l3ni1_search_dummy_proc(st, ic->parm.ni1_io.hl_id)))
+	{ L3DelTimer(&pc->timer); /* remove timer */
+          ni1_release_l3_process(pc);
+          return(0); 
+        } 
+       else
+	{ l3_debug(st, "l3ni1_cmd_global abort unknown id");
+          return(-2);
+        } 
+       break;
+    
+     default: 
+       l3_debug(st, "l3ni1_cmd_global unknown cmd 0x%lx", ic->arg);
+       return(-1);  
+   } /* switch ic-> arg */
+  return(-1);
+} /* l3ni1_cmd_global */
+
+static void 
+l3ni1_io_timer(struct l3_process *pc)
+{ isdn_ctrl ic;
+  struct IsdnCardState *cs = pc->st->l1.hardware;
+
+  L3DelTimer(&pc->timer); /* remove timer */
+
+  ic.driver = cs->myid;
+  ic.command = ISDN_STAT_PROT;
+  ic.arg = NI1_STAT_INVOKE_ERR;
+  ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+  ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+  ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+  ic.parm.ni1_io.timeout= -1;
+  ic.parm.ni1_io.datalen = 0;
+  ic.parm.ni1_io.data = NULL;
+  free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+  pc->prot.ni1.invoke_id = 0; /* reset id */
+
+  cs->iif.statcallb(&ic);
+
+  ni1_release_l3_process(pc); 
+} /* l3ni1_io_timer */
+
+static void
+l3ni1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int callState = 0;
+	p = skb->data;
+
+	if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++)
+			callState = *p;
+	}
+	if (callState == 0) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
+		 * set down layer 3 without sending any message
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		ni1_release_l3_process(pc);
+	} else {
+		pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+	}
+}
+
+static void
+l3ni1_dummy(struct l3_process *pc, u_char pr, void *arg)
+{
+}
+
+static void
+l3ni1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 28; /* invalid number */
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+	if (pc->N303 > 0) {
+		pc->N303--;
+		L3DelTimer(&pc->timer);
+		l3ni1_setup_req(pc, pr, arg);
+	} else {
+		L3DelTimer(&pc->timer);
+		l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
+		pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+		ni1_release_l3_process(pc);
+	}
+}
+
+static void
+l3ni1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+
+}
+
+static void
+l3ni1_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	struct sk_buff *skb;
+	u_char cause = 16;
+
+	L3DelTimer(&pc->timer);
+	if (pc->para.cause != NO_CAUSE)
+		cause = pc->para.cause;
+
+	MsgHead(p, pc->callref, MT_RELEASE);
+
+	*p++ = IE_CAUSE;
+	*p++ = 0x2;
+	*p++ = 0x80;
+	*p++ = cause | 0x80;
+
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 19);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.loc = 0;
+	pc->para.cause = 102;
+	l3ni1_disconnect_req(pc, pr, NULL);
+	pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3ni1_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+	newl3state(pc, 19);
+	L3DelTimer(&pc->timer);
+	l3ni1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_2);
+}
+
+static void
+l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 19);
+	l3ni1_message(pc, MT_RELEASE);
+	L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->para.cause = 102;	/* Timer expiry */
+	pc->para.loc = 0;	/* local */
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+}
+
+static void
+l3ni1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char *p;
+	struct sk_buff *skb = arg;
+	int ret; 
+	u_char cause = 0, callState = 0;
+	
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
+		if (ret < 0)
+			cause = 96;
+		else if (ret > 0)
+			cause = 100;
+	}
+	if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
+		p++;
+		if (1 == *p++) {
+			callState = *p;
+			if (!ie_in_set(pc, *p, l3_valid_states))
+				cause = 100;
+		} else
+			cause = 100;
+	} else
+		cause = 96;
+	if (!cause) { /*  no error before */
+		ret = check_infoelements(pc, skb, ie_STATUS);
+		if (ERR_IE_COMPREHENSION == ret)
+			cause = 96;
+		else if (ERR_IE_UNRECOGNIZED == ret)
+			cause = 99;
+	}
+	if (cause) {
+		u_char tmp;
+		
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
+		tmp = pc->para.cause;
+		pc->para.cause = cause;
+		l3ni1_status_send(pc, 0, NULL);
+		if (cause == 99)
+			pc->para.cause = tmp;
+		else
+			return;
+	}
+	cause = pc->para.cause;
+	if (((cause & 0x7f) == 111) && (callState == 0)) {
+		/* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
+		 * if received MT_STATUS with cause == 111 and call
+		 * state == 0, then we must set down layer 3
+		 */
+		pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+		newl3state(pc, 0);
+		ni1_release_l3_process(pc);
+	}
+}
+
+static void
+l3ni1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+	
+	ret = check_infoelements(pc, skb, ie_FACILITY);
+	l3ni1_std_ie_err(pc, ret);
+ 	  {
+		u_char *p;
+		if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+			l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+	}
+}
+
+static void
+l3ni1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->chan->setup.phone;
+
+	MsgHead(p, pc->callref, MT_SUSPEND);
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 15);
+	L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3ni1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	L3DelTimer(&pc->timer);
+	newl3state(pc, 0);
+	pc->para.cause = NO_CAUSE;
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+	/* We don't handle suspend_ack for IE errors now */
+	if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_req(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb;
+	u_char tmp[32];
+	u_char *p = tmp;
+	u_char i, l;
+	u_char *msg = pc->para.setup.phone;
+
+	MsgHead(p, pc->callref, MT_RESUME);
+
+	l = *msg++;
+	if (l && (l <= 10)) {	/* Max length 10 octets */
+		*p++ = IE_CALL_ID;
+		*p++ = l;
+		for (i = 0; i < l; i++)
+			*p++ = *msg++;
+	} else if (l) {
+		l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
+		return;
+	}
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+	newl3state(pc, 17);
+	L3AddTimer(&pc->timer, T318, CC_T318);
+}
+
+static void
+l3ni1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int id, ret;
+
+	if ((id = l3ni1_get_channel_id(pc, skb)) > 0) {
+		if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+			if (pc->debug & L3_DEB_WARN)
+				l3_debug(pc->st, "resume ack with wrong chid %x", id);
+			pc->para.cause = 100;
+			l3ni1_status_send(pc, pr, NULL);
+			return;
+		}
+		pc->para.bchannel = id;
+	} else if (1 == pc->state) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "resume ack without chid (ret %d)", id);
+		pc->para.cause = 96;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+	newl3state(pc, 10);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int ret;
+
+	if ((ret = l3ni1_get_cause(pc, skb))) {
+		if (pc->debug & L3_DEB_WARN)
+			l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
+		if (ret < 0) 
+			pc->para.cause = 96;
+		else
+			pc->para.cause = 100;
+		l3ni1_status_send(pc, pr, NULL);
+		return;
+	}
+	ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
+	if (ERR_IE_COMPREHENSION == ret) {
+		l3ni1_std_ie_err(pc, ret);
+		return;
+	}
+	L3DelTimer(&pc->timer);
+	pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+	newl3state(pc, 0);
+	if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+		l3ni1_std_ie_err(pc, ret);
+	ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+	u_char tmp[32];
+	u_char *p;
+	u_char ri, ch = 0, chan = 0;
+	int l;
+	struct sk_buff *skb = arg;
+	struct l3_process *up;
+
+	newl3state(pc, 2);
+	L3DelTimer(&pc->timer);
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+		ri = p[2];
+		l3_debug(pc->st, "Restart %x", ri);
+	} else {
+		l3_debug(pc->st, "Restart without restart IE");
+		ri = 0x86;
+	}
+	p = skb->data;
+	if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+		chan = p[2] & 3;
+		ch = p[2];
+		if (pc->st->l3.debug)
+			l3_debug(pc->st, "Restart for channel %d", chan);
+	}
+	newl3state(pc, 2);
+	up = pc->st->l3.proc;
+	while (up) {
+		if ((ri & 7) == 7)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		else if (up->para.bchannel == chan)
+			up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+		
+		up = up->next;
+	}
+	p = tmp;
+	MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+	if (chan) {
+		*p++ = IE_CHANNEL_ID;
+		*p++ = 1;
+		*p++ = ch | 0x80;
+	}
+	*p++ = 0x79;		/* RESTART Ind */
+	*p++ = 1;
+	*p++ = ri;
+	l = p - tmp;
+	if (!(skb = l3_alloc_skb(l)))
+		return;
+	memcpy(skb_put(skb, l), tmp, l);
+	newl3state(pc, 0);
+	l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+        pc->para.cause = 0x29;          /* Temporary failure */
+        pc->para.loc = 0;
+        l3ni1_disconnect_req(pc, pr, NULL);
+        pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+        newl3state(pc, 0);
+        pc->para.cause = 0x1b;          /* Destination out of order */
+        pc->para.loc = 0;
+        pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+        release_l3_process(pc);
+}
+
+static void
+l3ni1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
+{
+        L3DelTimer(&pc->timer);
+        L3AddTimer(&pc->timer, T309, CC_T309);
+        l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
+}
+ 
+static void
+l3ni1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
+{
+	L3DelTimer(&pc->timer);
+ 
+ 	pc->para.cause = 0x1F; /* normal, unspecified */
+	l3ni1_status_send(pc, 0, NULL);
+}
+
+static void l3ni1_SendSpid( struct l3_process *pc, u_char pr, struct sk_buff *skb, int iNewState )
+{
+	u_char         * p;
+	char           * pSPID;
+	struct Channel * pChan = pc->st->lli.userdata;
+	int              l;
+
+	if ( skb )
+		dev_kfree_skb( skb);
+
+	if ( !( pSPID = strchr( pChan->setup.eazmsn, ':' ) ) )
+	{
+		printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn );
+		newl3state( pc, 0 );
+		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+		return;
+	}
+
+	l = strlen( ++pSPID );
+	if ( !( skb = l3_alloc_skb( 5+l ) ) )
+	{
+		printk( KERN_ERR "HiSax can't get memory to send SPID\n" );
+		return;
+	}
+
+	p = skb_put( skb, 5 );
+	*p++ = PROTO_DIS_EURO;
+	*p++ = 0;
+	*p++ = MT_INFORMATION;
+	*p++ = IE_SPID;
+	*p++ = l;
+
+	memcpy( skb_put( skb, l ), pSPID, l );
+
+	newl3state( pc, iNewState );
+
+	L3DelTimer( &pc->timer );
+	L3AddTimer( &pc->timer, TSPID, CC_TSPID );
+
+	pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb );
+}
+
+static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg )
+{
+	l3ni1_SendSpid( pc, pr, arg, 20 );
+}
+
+void l3ni1_spid_epid( struct l3_process *pc, u_char pr, void *arg )
+{
+	struct sk_buff *skb = arg;
+
+	if ( skb->data[ 1 ] == 0 )
+		if ( skb->data[ 3 ] == IE_ENDPOINT_ID )
+		{
+			L3DelTimer( &pc->timer );
+			newl3state( pc, 0 );
+			l3_msg( pc->st, DL_ESTABLISH | CONFIRM, NULL );
+		}
+	dev_kfree_skb( skb);
+}
+
+static void l3ni1_spid_tout( struct l3_process *pc, u_char pr, void *arg )
+{
+	if ( pc->state < 22 )
+		l3ni1_SendSpid( pc, pr, arg, pc->state+1 );
+	else
+	{
+		L3DelTimer( &pc->timer );
+		dev_kfree_skb( arg);
+
+		printk( KERN_ERR "SPID not accepted\n" );
+		newl3state( pc, 0 );
+		pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+	}
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+	{SBIT(0),
+	 CC_SETUP | REQUEST, l3ni1_setup_req},
+	{SBIT(0),
+	 CC_RESUME | REQUEST, l3ni1_resume_req},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25),
+	 CC_DISCONNECT | REQUEST, l3ni1_disconnect_req},
+	{SBIT(12),
+	 CC_RELEASE | REQUEST, l3ni1_release_req},
+	{ALL_STATES,
+	 CC_RESTART | REQUEST, l3ni1_restart},
+	{SBIT(6) | SBIT(25),
+	 CC_IGNORE | REQUEST, l3ni1_reset},
+	{SBIT(6) | SBIT(25),
+	 CC_REJECT | REQUEST, l3ni1_reject_req},
+	{SBIT(6) | SBIT(25),
+	 CC_PROCEED_SEND | REQUEST, l3ni1_proceed_req},
+	{SBIT(6),
+	 CC_MORE_INFO | REQUEST, l3ni1_setup_ack_req},
+	{SBIT(25),
+	 CC_MORE_INFO | REQUEST, l3ni1_dummy},
+	{SBIT(6) | SBIT(9) | SBIT(25),
+	 CC_ALERTING | REQUEST, l3ni1_alert_req},
+	{SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
+	 CC_SETUP | RESPONSE, l3ni1_setup_rsp},
+	{SBIT(10),
+	 CC_SUSPEND | REQUEST, l3ni1_suspend_req},
+        {SBIT(7) | SBIT(9) | SBIT(25),
+         CC_REDIR | REQUEST, l3ni1_redir_req},
+        {SBIT(6),
+         CC_REDIR | REQUEST, l3ni1_redir_req_early},
+        {SBIT(9) | SBIT(25),
+         CC_DISCONNECT | REQUEST, l3ni1_disconnect_req},
+	{SBIT(25),
+	 CC_T302, l3ni1_t302},
+	{SBIT(1),
+	 CC_T303, l3ni1_t303},
+	{SBIT(2),
+	 CC_T304, l3ni1_t304},
+	{SBIT(3),
+	 CC_T310, l3ni1_t310},
+	{SBIT(8),
+	 CC_T313, l3ni1_t313},
+	{SBIT(11),
+	 CC_T305, l3ni1_t305},
+	{SBIT(15),
+	 CC_T319, l3ni1_t319},
+	{SBIT(17),
+	 CC_T318, l3ni1_t318},
+	{SBIT(19),
+	 CC_T308_1, l3ni1_t308_1},
+	{SBIT(19),
+	 CC_T308_2, l3ni1_t308_2},
+	{SBIT(10),
+	 CC_T309, l3ni1_dl_release},
+	{ SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ),
+	 CC_TSPID, l3ni1_spid_tout },
+};
+
+#define DOWNSLLEN \
+	(sizeof(downstatelist) / sizeof(struct stateentry))
+
+static struct stateentry datastatelist[] =
+{
+	{ALL_STATES,
+	 MT_STATUS_ENQUIRY, l3ni1_status_enq},
+	{ALL_STATES,
+	 MT_FACILITY, l3ni1_facility},
+	{SBIT(19),
+	 MT_STATUS, l3ni1_release_ind},
+	{ALL_STATES,
+	 MT_STATUS, l3ni1_status},
+	{SBIT(0),
+	 MT_SETUP, l3ni1_setup},
+	{SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) |
+	 SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_SETUP, l3ni1_dummy},
+	{SBIT(1) | SBIT(2),
+	 MT_CALL_PROCEEDING, l3ni1_call_proc},
+	{SBIT(1),
+	 MT_SETUP_ACKNOWLEDGE, l3ni1_setup_ack},
+	{SBIT(2) | SBIT(3),
+	 MT_ALERTING, l3ni1_alerting},
+	{SBIT(2) | SBIT(3),
+	 MT_PROGRESS, l3ni1_progress},
+	{SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_INFORMATION, l3ni1_information},
+	{SBIT(10) | SBIT(11) | SBIT(15),
+	 MT_NOTIFY, l3ni1_notify},
+	{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
+	 SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
+	 MT_RELEASE_COMPLETE, l3ni1_release_cmpl},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25),
+	 MT_RELEASE, l3ni1_release},
+	{SBIT(19),  MT_RELEASE, l3ni1_release_ind},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25),
+	 MT_DISCONNECT, l3ni1_disconnect},
+	{SBIT(19),
+	 MT_DISCONNECT, l3ni1_dummy},
+	{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
+	 MT_CONNECT, l3ni1_connect},
+	{SBIT(8),
+	 MT_CONNECT_ACKNOWLEDGE, l3ni1_connect_ack},
+	{SBIT(15),
+	 MT_SUSPEND_ACKNOWLEDGE, l3ni1_suspend_ack},
+	{SBIT(15),
+	 MT_SUSPEND_REJECT, l3ni1_suspend_rej},
+	{SBIT(17),
+	 MT_RESUME_ACKNOWLEDGE, l3ni1_resume_ack},
+	{SBIT(17),
+	 MT_RESUME_REJECT, l3ni1_resume_rej},
+};
+
+#define DATASLLEN \
+	(sizeof(datastatelist) / sizeof(struct stateentry))
+
+static struct stateentry globalmes_list[] =
+{
+	{ALL_STATES,
+	 MT_STATUS, l3ni1_status},
+	{SBIT(0),
+	 MT_RESTART, l3ni1_global_restart},
+/*	{SBIT(1),
+	 MT_RESTART_ACKNOWLEDGE, l3ni1_restart_ack},
+*/
+	{ SBIT( 0 ), MT_DL_ESTABLISHED, l3ni1_spid_send },
+	{ SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ), MT_INFORMATION, l3ni1_spid_epid },
+};
+#define GLOBALM_LEN \
+	(sizeof(globalmes_list) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+        {SBIT(2),
+         DL_ESTABLISH | INDICATION, l3ni1_dl_reset},
+        {SBIT(10),
+         DL_ESTABLISH | CONFIRM, l3ni1_dl_reest_status},
+        {SBIT(10),
+         DL_RELEASE | INDICATION, l3ni1_dl_reestablish},
+        {ALL_STATES,
+         DL_RELEASE | INDICATION, l3ni1_dl_release},
+};
+
+#define MANSLLEN \
+        (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+	u_char tmp[16];
+	u_char *p = tmp;
+	int l;
+	int i;
+	struct l3_process *proc = st->l3.global;
+
+	if ( skb )	
+		proc->callref = skb->data[2]; /* cr flag */
+	else
+		proc->callref = 0;
+	for (i = 0; i < GLOBALM_LEN; i++)
+		if ((mt == globalmes_list[i].primitive) &&
+		    ((1 << proc->state) & globalmes_list[i].state))
+			break;
+	if (i == GLOBALM_LEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1 global state %d mt %x unhandled",
+				proc->state, mt);
+		}
+		MsgHead(p, proc->callref, MT_STATUS);
+		*p++ = IE_CAUSE;
+		*p++ = 0x2;
+		*p++ = 0x80;
+		*p++ = 81 |0x80;	/* invalid cr */
+		*p++ = 0x14;		/* CallState */
+		*p++ = 0x1;
+		*p++ = proc->state & 0x3f;
+		l = p - tmp;
+		if (!(skb = l3_alloc_skb(l)))
+			return;
+		memcpy(skb_put(skb, l), tmp, l);
+		l3_msg(proc->st, DL_DATA | REQUEST, skb);
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1 global %d mt %x",
+				proc->state, mt);
+		}
+		globalmes_list[i].rout(proc, mt, skb);
+	}
+}
+
+static void
+ni1up(struct PStack *st, int pr, void *arg)
+{
+	int i, mt, cr, cause, callState;
+	char *ptr;
+	u_char *p;
+	struct sk_buff *skb = arg;
+	struct l3_process *proc;
+
+	switch (pr) {
+		case (DL_DATA | INDICATION):
+		case (DL_UNIT_DATA | INDICATION):
+			break;
+		case (DL_ESTABLISH | INDICATION):
+		case (DL_RELEASE | INDICATION):
+		case (DL_RELEASE | CONFIRM):
+			l3_msg(st, pr, arg);
+			return;
+			break;
+
+		case (DL_ESTABLISH | CONFIRM):
+			global_handler( st, MT_DL_ESTABLISHED, NULL );
+			return;
+
+		default:
+			printk(KERN_ERR "HiSax ni1up unknown pr=%04x\n", pr);
+			return;
+	}
+	if (skb->len < 3) {
+		l3_debug(st, "ni1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (skb->data[0] != PROTO_DIS_EURO) {
+		if (st->l3.debug & L3_DEB_PROTERR) {
+			l3_debug(st, "ni1up%sunexpected discriminator %x message len %d",
+				 (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				 skb->data[0], skb->len);
+		}
+		dev_kfree_skb(skb);
+		return;
+	}
+	cr = getcallref(skb->data);
+	if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
+		l3_debug(st, "ni1up frame too short(%d)", skb->len);
+		dev_kfree_skb(skb);
+		return;
+	}
+	mt = skb->data[skb->data[1] + 2];
+	if (st->l3.debug & L3_DEB_STATE)
+		l3_debug(st, "ni1up cr %d", cr);
+	if (cr == -2) {  /* wrong Callref */
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "ni1up wrong Callref");
+		dev_kfree_skb(skb);
+		return;
+	} else if (cr == -1) {	/* Dummy Callref */
+		if (mt == MT_FACILITY)
+		{
+			if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+				l3ni1_parse_facility(st, NULL, 
+					(pr == (DL_DATA | INDICATION)) ? -1 : -2, p); 
+				dev_kfree_skb(skb);
+				return;  
+			}
+		}
+		else
+		{
+			global_handler(st, mt, skb);
+			return;
+		}
+				
+		if (st->l3.debug & L3_DEB_WARN)
+			l3_debug(st, "ni1up dummy Callref (no facility msg or ie)");
+		dev_kfree_skb(skb);
+		return;
+	} else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
+		(((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) {	/* Global CallRef */
+		if (st->l3.debug & L3_DEB_STATE)
+			l3_debug(st, "ni1up Global CallRef");
+		global_handler(st, mt, skb);
+		dev_kfree_skb(skb);
+		return;
+	} else if (!(proc = getl3proc(st, cr))) {
+		/* No transaction process exist, that means no call with
+		 * this callreference is active
+		 */
+		if (mt == MT_SETUP) {
+			/* Setup creates a new transaction process */
+			if (skb->data[2] & 0x80) {
+				/* Setup with wrong CREF flag */
+				if (st->l3.debug & L3_DEB_STATE)
+					l3_debug(st, "ni1up wrong CRef flag");
+				dev_kfree_skb(skb);
+				return;
+			}
+			if (!(proc = ni1_new_l3_process(st, cr))) {
+				/* May be to answer with RELEASE_COMPLETE and
+				 * CAUSE 0x2f "Resource unavailable", but this
+				 * need a new_l3_process too ... arghh
+				 */
+				dev_kfree_skb(skb);
+				return;
+			}
+		} else if (mt == MT_STATUS) {
+			cause = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				cause = *ptr & 0x7f;
+			}
+			callState = 0;
+			if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+				ptr++;
+				if (*ptr++ == 2)
+					ptr++;
+				callState = *ptr;
+			}
+			/* ETS 300-104 part 2.4.1
+			 * if setup has not been made and a message type
+			 * MT_STATUS is received with call state == 0,
+			 * we must send nothing
+			 */
+			if (callState != 0) {
+				/* ETS 300-104 part 2.4.2
+				 * if setup has not been made and a message type
+				 * MT_STATUS is received with call state != 0,
+				 * we must send MT_RELEASE_COMPLETE cause 101
+				 */
+				if ((proc = ni1_new_l3_process(st, cr))) {
+					proc->para.cause = 101;
+					l3ni1_msg_without_setup(proc, 0, NULL);
+				}
+			}
+			dev_kfree_skb(skb);
+			return;
+		} else if (mt == MT_RELEASE_COMPLETE) {
+			dev_kfree_skb(skb);
+			return;
+		} else {
+			/* ETS 300-104 part 2
+			 * if setup has not been made and a message type
+			 * (except MT_SETUP and RELEASE_COMPLETE) is received,
+			 * we must send MT_RELEASE_COMPLETE cause 81 */
+			dev_kfree_skb(skb);
+			if ((proc = ni1_new_l3_process(st, cr))) {
+				proc->para.cause = 81;
+				l3ni1_msg_without_setup(proc, 0, NULL);
+			}
+			return;
+		}
+	}
+	if (l3ni1_check_messagetype_validity(proc, mt, skb)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) 
+	  l3ni1_deliver_display(proc, pr, p); /* Display IE included */
+	for (i = 0; i < DATASLLEN; i++)
+		if ((mt == datastatelist[i].primitive) &&
+		    ((1 << proc->state) & datastatelist[i].state))
+			break;
+	if (i == DATASLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1up%sstate %d mt %#x unhandled",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
+			proc->para.cause = 101;
+			l3ni1_status_send(proc, pr, skb);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1up%sstate %d mt %x",
+				(pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+				proc->state, mt);
+		}
+		datastatelist[i].rout(proc, pr, skb);
+	}
+	dev_kfree_skb(skb);
+	return;
+}
+
+static void
+ni1down(struct PStack *st, int pr, void *arg)
+{
+	int i, cr;
+	struct l3_process *proc;
+	struct Channel *chan;
+
+	if ((DL_ESTABLISH | REQUEST) == pr) {
+		l3_msg(st, pr, NULL);
+		return;
+	} else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+		chan = arg;
+		cr = newcallref();
+		cr |= 0x80;
+		if ((proc = ni1_new_l3_process(st, cr))) {
+			proc->chan = chan;
+			chan->proc = proc;
+			memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+			proc->callref = cr;
+		}
+	} else {
+		proc = arg;
+	}
+	if (!proc) {
+		printk(KERN_ERR "HiSax ni1down without proc pr=%04x\n", pr);
+		return;
+	}
+
+	if ( pr == (CC_TNI1_IO | REQUEST)) {
+		l3ni1_io_timer(proc); /* timer expires */ 
+		return;
+	}  
+
+	for (i = 0; i < DOWNSLLEN; i++)
+		if ((pr == downstatelist[i].primitive) &&
+		    ((1 << proc->state) & downstatelist[i].state))
+			break;
+	if (i == DOWNSLLEN) {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1down state %d prim %#x unhandled",
+				proc->state, pr);
+		}
+	} else {
+		if (st->l3.debug & L3_DEB_STATE) {
+			l3_debug(st, "ni1down state %d prim %#x",
+				proc->state, pr);
+		}
+		downstatelist[i].rout(proc, pr, arg);
+	}
+}
+
+static void
+ni1man(struct PStack *st, int pr, void *arg)
+{
+        int i;
+        struct l3_process *proc = arg;
+
+        if (!proc) {
+                printk(KERN_ERR "HiSax ni1man without proc pr=%04x\n", pr);
+                return;
+        }
+        for (i = 0; i < MANSLLEN; i++)
+                if ((pr == manstatelist[i].primitive) &&
+                    ((1 << proc->state) & manstatelist[i].state))
+                        break;
+        if (i == MANSLLEN) {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d ni1man state %d prim %#x unhandled",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+        } else {
+                if (st->l3.debug & L3_DEB_STATE) {
+                        l3_debug(st, "cr %d ni1man state %d prim %#x",
+                                proc->callref & 0x7f, proc->state, pr);
+                }
+                manstatelist[i].rout(proc, pr, arg);
+        }
+}
+ 
+void
+setstack_ni1(struct PStack *st)
+{
+	char tmp[64];
+	int i;
+
+	st->lli.l4l3 = ni1down;
+	st->lli.l4l3_proto = l3ni1_cmd_global;
+	st->l2.l2l3 = ni1up;
+	st->l3.l3ml3 = ni1man;
+	st->l3.N303 = 1;
+	st->prot.ni1.last_invoke_id = 0;
+	st->prot.ni1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
+	i = 1;
+	while (i < 32) 
+		st->prot.ni1.invoke_used[i++] = 0;   
+
+	if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+		printk(KERN_ERR "HiSax can't get memory for ni1 global CR\n");
+	} else {
+		st->l3.global->state = 0;
+		st->l3.global->callref = 0;
+		st->l3.global->next = NULL;
+		st->l3.global->debug = L3_DEB_WARN;
+		st->l3.global->st = st;
+		st->l3.global->N303 = 1;
+		st->l3.global->prot.ni1.invoke_id = 0; 
+
+		L3InitTimer(st->l3.global, &st->l3.global->timer);
+	}
+	strcpy(tmp, ni1_revision);
+	printk(KERN_INFO "HiSax: National ISDN-1 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3ni1.h b/drivers/isdn/hisax/l3ni1.h
new file mode 100644
index 0000000..4066da2
--- /dev/null
+++ b/drivers/isdn/hisax/l3ni1.h
@@ -0,0 +1,136 @@
+/* $Id: l3ni1.h,v 2.3.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * NI1 D-channel protocol
+ *
+ * Author       Matt Henderson & Guy Ellis
+ * Copyright    by Traverse Technologies Pty Ltd, www.travers.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 2000.6.6 Initial implementation of routines for US NI1 
+ * Layer 3 protocol based on the EURO/DSS1 D-channel protocol 
+ * driver written by Karsten Keil et al.  Thanks also for the 
+ * code provided by Ragnar Paulson.
+ *
+ */
+
+#ifndef l3ni1_process
+
+#define T302	15000
+#define T303	4000
+#define T304	30000
+#define T305	30000
+#define T308	4000
+/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */
+/* This makes some tests easier and quicker */
+#define T309	40000
+#define T310	30000
+#define T313	4000
+#define T318	4000
+#define T319	4000
+#define TSPID	5000 /* was 2000 - Guy Ellis */
+
+/*
+ * Message-Types
+ */
+
+#define MT_ALERTING		0x01
+#define MT_CALL_PROCEEDING	0x02
+#define MT_CONNECT		0x07
+#define MT_CONNECT_ACKNOWLEDGE	0x0f
+#define MT_PROGRESS		0x03
+#define MT_SETUP		0x05
+#define MT_SETUP_ACKNOWLEDGE	0x0d
+#define MT_RESUME		0x26
+#define MT_RESUME_ACKNOWLEDGE	0x2e
+#define MT_RESUME_REJECT	0x22
+#define MT_SUSPEND		0x25
+#define MT_SUSPEND_ACKNOWLEDGE	0x2d
+#define MT_SUSPEND_REJECT	0x21
+#define MT_USER_INFORMATION	0x20
+#define MT_DISCONNECT		0x45
+#define MT_RELEASE		0x4d
+#define MT_RELEASE_COMPLETE	0x5a
+#define MT_RESTART		0x46
+#define MT_RESTART_ACKNOWLEDGE	0x4e
+#define MT_SEGMENT		0x60
+#define MT_CONGESTION_CONTROL	0x79
+#define MT_INFORMATION		0x7b
+#define MT_FACILITY		0x62
+#define MT_NOTIFY		0x6e
+#define MT_STATUS		0x7d
+#define MT_STATUS_ENQUIRY	0x75
+#define MT_DL_ESTABLISHED	0xfe
+
+#define IE_SEGMENT	0x00
+#define IE_BEARER	0x04
+#define IE_CAUSE	0x08
+#define IE_CALL_ID	0x10
+#define IE_CALL_STATE	0x14
+#define IE_CHANNEL_ID	0x18
+#define IE_FACILITY	0x1c
+#define IE_PROGRESS	0x1e
+#define IE_NET_FAC	0x20
+#define IE_NOTIFY	0x27
+#define IE_DISPLAY	0x28
+#define IE_DATE		0x29
+#define IE_KEYPAD	0x2c
+#define IE_SIGNAL	0x34
+#define IE_SPID		0x3a
+#define IE_ENDPOINT_ID	0x3b
+#define IE_INFORATE	0x40
+#define IE_E2E_TDELAY	0x42
+#define IE_TDELAY_SEL	0x43
+#define IE_PACK_BINPARA	0x44
+#define IE_PACK_WINSIZE	0x45
+#define IE_PACK_SIZE	0x46
+#define IE_CUG		0x47
+#define	IE_REV_CHARGE	0x4a
+#define IE_CONNECT_PN	0x4c
+#define IE_CONNECT_SUB	0x4d
+#define IE_CALLING_PN	0x6c
+#define IE_CALLING_SUB	0x6d
+#define IE_CALLED_PN	0x70
+#define IE_CALLED_SUB	0x71
+#define IE_REDIR_NR	0x74
+#define IE_TRANS_SEL	0x78
+#define IE_RESTART_IND	0x79
+#define IE_LLC		0x7c
+#define IE_HLC		0x7d
+#define IE_USER_USER	0x7e
+#define IE_ESCAPE	0x7f
+#define IE_SHIFT	0x90
+#define IE_MORE_DATA	0xa0
+#define IE_COMPLETE	0xa1
+#define IE_CONGESTION	0xb0
+#define IE_REPEAT	0xd0
+
+#define IE_MANDATORY	0x0100
+/* mandatory not in every case */
+#define IE_MANDATORY_1	0x0200
+
+#define ERR_IE_COMPREHENSION	 1
+#define ERR_IE_UNRECOGNIZED	-1
+#define ERR_IE_LENGTH		-2
+#define ERR_IE_SEQUENCE		-3
+
+#else /* only l3ni1_process */
+
+/* l3ni1 specific data in l3 process */
+typedef struct
+  { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */
+    ulong ll_id; /* remebered ll id */
+    u8 remote_operation; /* handled remote operation, 0 = not active */ 
+    int proc; /* rememered procedure */  
+    ulong remote_result; /* result of remote operation for statcallb */
+    char uus1_data[35]; /* data send during alerting or disconnect */
+  } ni1_proc_priv;
+
+/* l3dni1 specific data in protocol stack */
+typedef struct
+  { unsigned char last_invoke_id; /* last used value for invoking */
+    unsigned char invoke_used[32]; /* 256 bits for 256 values */
+  } ni1_stk_priv;        
+
+#endif /* only l3dni1_process */
diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c
new file mode 100644
index 0000000..d4f86d6
--- /dev/null
+++ b/drivers/isdn/hisax/lmgr.c
@@ -0,0 +1,50 @@
+/* $Id: lmgr.c,v 1.7.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * Layermanagement module
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "hisax.h"
+
+static void
+error_handling_dchan(struct PStack *st, int Error)
+{
+	switch (Error) {
+		case 'C':
+		case 'D':
+		case 'G':
+		case 'H':
+			st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL);
+			break;
+	}
+}
+
+static void
+hisax_manager(struct PStack *st, int pr, void *arg)
+{
+	long Code;
+
+	switch (pr) {
+		case (MDL_ERROR | INDICATION):
+			Code = (long) arg;
+			HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR",
+				" %c %s", (char)Code, 
+				test_bit(FLG_LAPD, &st->l2.flag) ?
+				"D-channel" : "B-channel");
+			if (test_bit(FLG_LAPD, &st->l2.flag))
+				error_handling_dchan(st, Code);
+			break;
+	}
+}
+
+void
+setstack_manager(struct PStack *st)
+{
+	st->ma.layer = hisax_manager;
+}
diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c
new file mode 100644
index 0000000..3ac4484
--- /dev/null
+++ b/drivers/isdn/hisax/mic.c
@@ -0,0 +1,239 @@
+/* $Id: mic.c,v 1.12.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for mic cards
+ *
+ * Author       Stephan von Krawczynski
+ * Copyright    by Stephan von Krawczynski <skraw@ithnet.com>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *mic_revision = "$Revision: 1.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define MIC_ISAC	2
+#define MIC_HSCX	1
+#define MIC_ADR		7
+
+/* CARD_ADR (Write) */
+#define MIC_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.mic.adr,
+			cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.mic.adr,
+		 cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \
+		cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+mic_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_mic(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->hw.mic.cfg_reg)
+		release_region(cs->hw.mic.cfg_reg, bytecnt);
+}
+
+static int
+mic_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_mic(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscx(cs); /* /RTSA := ISAC RST */
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_mic(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, mic_revision);
+	printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_MIC)
+		return (0);
+
+	bytecnt = 8;
+	cs->hw.mic.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR;
+	cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC;
+	cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX;
+
+	if (!request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.mic.cfg_reg,
+		       cs->hw.mic.cfg_reg + bytecnt);
+		return (0);
+	}
+	printk(KERN_INFO "mic: defined at 0x%x IRQ %d\n",
+		cs->hw.mic.cfg_reg, cs->irq);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &mic_card_msg;
+	cs->irq_func = &mic_interrupt;
+	ISACVersion(cs, "mic:");
+	if (HscxVersion(cs, "mic:")) {
+		printk(KERN_WARNING
+		    "mic: wrong HSCX versions check IO address\n");
+		release_io_mic(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c
new file mode 100644
index 0000000..fe61d26
--- /dev/null
+++ b/drivers/isdn/hisax/netjet.c
@@ -0,0 +1,996 @@
+/* $Id: netjet.c,v 1.29.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * low level stuff for Traverse Technologie NETJet ISDN cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Traverse Technologies Australia for documents and information
+ *
+ * 16-Apr-2002 - led code added - Guy Ellis (guy@traverse.com.au)
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include <asm/io.h>
+#include "netjet.h"
+
+const char *NETjet_revision = "$Revision: 1.29.2.4 $";
+
+/* Interface functions */
+
+u_char
+NETjet_ReadIC(struct IsdnCardState *cs, u_char offset)
+{
+	u_char ret;
+	
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2));
+	return(ret);
+}
+
+void
+NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	cs->hw.njet.auxd |= (offset>>4) & 3;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value);
+}
+
+void
+NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	insb(cs->hw.njet.isac, data, size);
+}
+
+void 
+NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+	cs->hw.njet.auxd &= 0xfc;
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+	outsb(cs->hw.njet.isac, data, size);
+}
+
+void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill)
+{
+	u_int mask=0x000000ff, val = 0, *p=pos;
+	u_int i;
+	
+	val |= fill;
+	if (chan) {
+		val  <<= 8;
+		mask <<= 8;
+	}
+	mask ^= 0xffffffff;
+	for (i=0; i<cnt; i++) {
+		*p   &= mask;
+		*p++ |= val;
+		if (p > bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send;
+	}
+}
+
+void
+mode_tiger(struct BCState *bcs, int mode, int bc)
+{
+	struct IsdnCardState *cs = bcs->cs;
+        u_char led;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "Tiger mode %d bchan %d/%d",
+			mode, bc, bcs->channel);
+	bcs->mode = mode;
+	bcs->channel = bc;
+	switch (mode) {
+		case (L1_MODE_NULL):
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_TXSIZE, bc, 0xff);
+			if (cs->debug & L1_DEB_HSCX)
+				debugl1(cs, "Tiger stat rec %d/%d send %d",
+					bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err,
+					bcs->hw.tiger.s_tot); 
+			if ((cs->bcs[0].mode == L1_MODE_NULL) &&
+				(cs->bcs[1].mode == L1_MODE_NULL)) {
+				cs->hw.njet.dmactrl = 0;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+			}
+                        if (cs->typ == ISDN_CTYPE_NETJET_S)
+                        {
+                                // led off
+                                led = bc & 0x01;
+                                led = 0x01 << (6 + led); // convert to mask
+                                led = ~led;
+                                cs->hw.njet.auxd &= led;
+                                byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+                        }
+			break;
+		case (L1_MODE_TRANS):
+			break;
+		case (L1_MODE_HDLC_56K):
+		case (L1_MODE_HDLC):
+			fill_mem(bcs, bcs->hw.tiger.send,
+				NETJET_DMA_TXSIZE, bc, 0xff);
+			bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot = 0;
+			bcs->hw.tiger.r_bitcnt = 0;
+			bcs->hw.tiger.r_one = 0;
+			bcs->hw.tiger.r_err = 0;
+			bcs->hw.tiger.s_tot = 0;
+			if (! cs->hw.njet.dmactrl) {
+				fill_mem(bcs, bcs->hw.tiger.send,
+					NETJET_DMA_TXSIZE, !bc, 0xff);
+				cs->hw.njet.dmactrl = 1;
+				byteout(cs->hw.njet.base + NETJET_DMACTRL,
+					cs->hw.njet.dmactrl);
+				byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x0f);
+			/* was 0x3f now 0x0f for TJ300 and TJ320  GE 13/07/00 */
+			}
+			bcs->hw.tiger.sendp = bcs->hw.tiger.send;
+			bcs->hw.tiger.free = NETJET_DMA_TXSIZE;
+			test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+                        if (cs->typ == ISDN_CTYPE_NETJET_S)
+                        {
+                                // led on
+                                led = bc & 0x01;
+                                led = 0x01 << (6 + led); // convert to mask
+                                cs->hw.njet.auxd |= led;
+                                byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+                        }
+			break;
+	}
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "tiger: set %x %x %x  %x/%x  pulse=%d",
+			bytein(cs->hw.njet.base + NETJET_DMACTRL),
+			bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+			bytein(cs->hw.njet.base + NETJET_IRQSTAT0),
+			inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+			inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+			bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+}
+
+static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) {
+	char tmp[128];
+	char *t = tmp;
+	int i=count,j;
+	u_char *p = buf;
+
+	t += sprintf(t, "tiger %s(%4d)", s, count);
+	while (i>0) {
+		if (i>16)
+			j=16;
+		else
+			j=i;
+		QuickHex(t, p, j);
+		debugl1(cs, tmp);
+		p += j;
+		i -= j;
+		t = tmp;
+		t += sprintf(t, "tiger %s      ", s);
+	}
+}
+
+// macro for 64k
+
+#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \
+			bitcnt++;\
+			s_val >>= 1;\
+			if (val & 1) {\
+				s_one++;\
+				s_val |= 0x80;\
+			} else {\
+				s_one = 0;\
+				s_val &= 0x7f;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			if (s_one == 5) {\
+				s_val >>= 1;\
+				s_val &= 0x7f;\
+				bitcnt++;\
+				s_one = 0;\
+			}\
+			if (bitcnt==8) {\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			val >>= 1;\
+		}
+
+static int make_raw_data(struct BCState *bcs) {
+// this make_raw is for 64k
+	register u_int i,s_cnt=0;
+	register u_char j;
+	register u_char val;
+	register u_char s_one = 0;
+	register u_char s_val = 0;
+	register u_char bitcnt = 0;
+	u_int fcs;
+	
+	if (!bcs->tx_skb) {
+		debugl1(bcs->cs, "tiger make_raw: NULL skb");
+		return(1);
+	}
+	bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE;
+	fcs = PPP_INITFCS;
+	for (i=0; i<bcs->tx_skb->len; i++) {
+		val = bcs->tx_skb->data[i];
+		fcs = PPP_FCS (fcs, val);
+		MAKE_RAW_BYTE;
+	}
+	fcs ^= 0xffff;
+	val = fcs & 0xff;
+	MAKE_RAW_BYTE;
+	val = (fcs>>8) & 0xff;
+	MAKE_RAW_BYTE;
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==8) {
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger make_raw: in %ld out %d.%d",
+			bcs->tx_skb->len, s_cnt, bitcnt);
+	if (bitcnt) {
+		while (8>bitcnt++) {
+			s_val >>= 1;
+			s_val |= 0x80;
+		}
+		bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+		bcs->hw.tiger.sendbuf[s_cnt++] = 0xff;	// NJ<->NJ thoughput bug fix
+	}
+	bcs->hw.tiger.sendcnt = s_cnt;
+	bcs->tx_cnt -= bcs->tx_skb->len;
+	bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+	return(0);
+}
+
+// macro for 56k
+
+#define MAKE_RAW_BYTE_56K for (j=0; j<8; j++) { \
+			bitcnt++;\
+			s_val >>= 1;\
+			if (val & 1) {\
+				s_one++;\
+				s_val |= 0x80;\
+			} else {\
+				s_one = 0;\
+				s_val &= 0x7f;\
+			}\
+			if (bitcnt==7) {\
+				s_val >>= 1;\
+				s_val |= 0x80;\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			if (s_one == 5) {\
+				s_val >>= 1;\
+				s_val &= 0x7f;\
+				bitcnt++;\
+				s_one = 0;\
+			}\
+			if (bitcnt==7) {\
+				s_val >>= 1;\
+				s_val |= 0x80;\
+				bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+				bitcnt = 0;\
+			}\
+			val >>= 1;\
+		}
+
+static int make_raw_data_56k(struct BCState *bcs) {
+// this make_raw is for 56k
+	register u_int i,s_cnt=0;
+	register u_char j;
+	register u_char val;
+	register u_char s_one = 0;
+	register u_char s_val = 0;
+	register u_char bitcnt = 0;
+	u_int fcs;
+	
+	if (!bcs->tx_skb) {
+		debugl1(bcs->cs, "tiger make_raw_56k: NULL skb");
+		return(1);
+	}
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==7) {
+			s_val >>= 1;
+			s_val |= 0x80;
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	};
+	fcs = PPP_INITFCS;
+	for (i=0; i<bcs->tx_skb->len; i++) {
+		val = bcs->tx_skb->data[i];
+		fcs = PPP_FCS (fcs, val);
+		MAKE_RAW_BYTE_56K;
+	}
+	fcs ^= 0xffff;
+	val = fcs & 0xff;
+	MAKE_RAW_BYTE_56K;
+	val = (fcs>>8) & 0xff;
+	MAKE_RAW_BYTE_56K;
+	val = HDLC_FLAG_VALUE;
+	for (j=0; j<8; j++) { 
+		bitcnt++;
+		s_val >>= 1;
+		if (val & 1)
+			s_val |= 0x80;
+		else
+			s_val &= 0x7f;
+		if (bitcnt==7) {
+			s_val >>= 1;
+			s_val |= 0x80;
+			bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+			bitcnt = 0;
+		}
+		val >>= 1;
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger make_raw_56k: in %ld out %d.%d",
+			bcs->tx_skb->len, s_cnt, bitcnt);
+	if (bitcnt) {
+		while (8>bitcnt++) {
+			s_val >>= 1;
+			s_val |= 0x80;
+		}
+		bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+		bcs->hw.tiger.sendbuf[s_cnt++] = 0xff;	// NJ<->NJ thoughput bug fix
+	}
+	bcs->hw.tiger.sendcnt = s_cnt;
+	bcs->tx_cnt -= bcs->tx_skb->len;
+	bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+	return(0);
+}
+
+static void got_frame(struct BCState *bcs, int count) {
+	struct sk_buff *skb;
+		
+	if (!(skb = dev_alloc_skb(count)))
+		printk(KERN_WARNING "TIGER: receive out of memory\n");
+	else {
+		memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count);
+		skb_queue_tail(&bcs->rqueue, skb);
+	}
+	test_and_set_bit(B_RCVBUFREADY, &bcs->event);
+	schedule_work(&bcs->tqueue);
+	
+	if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME)
+		printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec");
+}
+
+
+
+static void read_raw(struct BCState *bcs, u_int *buf, int cnt){
+	int i;
+	register u_char j;
+	register u_char val;
+	u_int  *pend = bcs->hw.tiger.rec +NETJET_DMA_RXSIZE -1;
+	register u_char state = bcs->hw.tiger.r_state;
+	register u_char r_one = bcs->hw.tiger.r_one;
+	register u_char r_val = bcs->hw.tiger.r_val;
+	register u_int bitcnt = bcs->hw.tiger.r_bitcnt;
+	u_int *p = buf;
+	int bits;
+	u_char mask;
+
+        if (bcs->mode == L1_MODE_HDLC) { // it's 64k
+		mask = 0xff;
+		bits = 8;
+	}
+	else { // it's 56K
+		mask = 0x7f;
+		bits = 7;
+	};
+	for (i=0;i<cnt;i++) {
+		val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff);
+		p++;
+		if (p > pend)
+			p = bcs->hw.tiger.rec;
+		if ((val & mask) == mask) {
+			state = HDLC_ZERO_SEARCH;
+			bcs->hw.tiger.r_tot++;
+			bitcnt = 0;
+			r_one = 0;
+			continue;
+		}
+		for (j=0;j<bits;j++) {
+			if (state == HDLC_ZERO_SEARCH) {
+				if (val & 1) {
+					r_one++;
+				} else {
+					r_one=0;
+					state= HDLC_FLAG_SEARCH;
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: zBit(%d,%d,%d) %x",
+							bcs->hw.tiger.r_tot,i,j,val);
+				}
+			} else if (state == HDLC_FLAG_SEARCH) { 
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						state=HDLC_FLAG_FOUND;
+						if (bcs->cs->debug & L1_DEB_HSCX)
+							debugl1(bcs->cs,"tiger read_raw: flag(%d,%d,%d) %x",
+								bcs->hw.tiger.r_tot,i,j,val);
+					}
+					r_one=0;
+				}
+			} else if (state ==  HDLC_FLAG_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						bitcnt=0;
+						r_val=0;
+						r_one=0;
+						val >>= 1;
+						continue;
+					} else if (r_one!=5) {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state != HDLC_ZERO_SEARCH) &&
+					!(bitcnt & 7)) {
+					state=HDLC_FRAME_FOUND;
+					bcs->hw.tiger.r_fcs = PPP_INITFCS;
+					bcs->hw.tiger.rcvbuf[0] = r_val;
+					bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
+							bcs->hw.tiger.r_tot,i,j,r_val,val,
+							bcs->cs->hw.njet.irqstat0);
+				}
+			} else if (state ==  HDLC_FRAME_FOUND) {
+				if (val & 1) {
+					r_one++;
+					if (r_one>6) {
+						state=HDLC_ZERO_SEARCH;
+						bitcnt=0;
+					} else {
+						r_val >>= 1;
+						r_val |= 0x80;
+						bitcnt++;
+					}
+				} else {
+					if (r_one==6) {
+						r_val=0; 
+						r_one=0;
+						bitcnt++;
+						if (bitcnt & 7) {
+							debugl1(bcs->cs, "tiger: frame not byte aligned");
+							state=HDLC_FLAG_SEARCH;
+							bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+							bcs->err_inv++;
+#endif
+						} else {
+							if (bcs->cs->debug & L1_DEB_HSCX)
+								debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x",
+									i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
+							if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
+								got_frame(bcs, (bitcnt>>3)-3);
+							} else {
+								if (bcs->cs->debug) {
+									debugl1(bcs->cs, "tiger FCS error");
+									printframe(bcs->cs, bcs->hw.tiger.rcvbuf,
+										(bitcnt>>3)-1, "rec");
+									bcs->hw.tiger.r_err++;
+								}
+#ifdef ERROR_STATISTIC
+							bcs->err_crc++;
+#endif
+							}
+							state=HDLC_FLAG_FOUND;
+						}
+						bitcnt=0;
+					} else if (r_one==5) {
+						val >>= 1;
+						r_one=0;
+						continue;
+					} else {
+						r_val >>= 1;
+						r_val &= 0x7f;
+						bitcnt++;
+					}
+					r_one=0;	
+				}
+				if ((state == HDLC_FRAME_FOUND) &&
+					!(bitcnt & 7)) {
+					if ((bitcnt>>3)>=HSCX_BUFMAX) {
+						debugl1(bcs->cs, "tiger: frame too big");
+						r_val=0; 
+						state=HDLC_FLAG_SEARCH;
+						bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+						bcs->err_inv++;
+#endif
+					} else {
+						bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val;
+						bcs->hw.tiger.r_fcs = 
+							PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+					}
+				}
+			}
+			val >>= 1;
+		}
+		bcs->hw.tiger.r_tot++;
+	}
+	bcs->hw.tiger.r_state = state;
+	bcs->hw.tiger.r_one = r_one;
+	bcs->hw.tiger.r_val = r_val;
+	bcs->hw.tiger.r_bitcnt = bitcnt;
+}
+
+void read_tiger(struct IsdnCardState *cs) {
+	u_int *p;
+	int cnt = NETJET_DMA_RXSIZE/2;
+	
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) {
+		debugl1(cs,"tiger warn read double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+#ifdef ERROR_STATISTIC
+		if (cs->bcs[0].mode)
+			cs->bcs[0].err_rdo++;
+		if (cs->bcs[1].mode)
+			cs->bcs[1].err_rdo++;
+#endif
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ);
+	}	
+	if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1)
+		p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.rec + cnt - 1;
+	if ((cs->bcs[0].mode == L1_MODE_HDLC) || (cs->bcs[0].mode == L1_MODE_HDLC_56K))
+		read_raw(cs->bcs, p, cnt);
+
+	if ((cs->bcs[1].mode == L1_MODE_HDLC) || (cs->bcs[1].mode == L1_MODE_HDLC_56K))
+		read_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ;
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
+
+void netjet_fill_dma(struct BCState *bcs)
+{
+	register u_int *p, *sp;
+	register int cnt;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma1: c%d %4x", bcs->channel,
+			bcs->Flag);
+	if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag))
+		return;
+	if (bcs->mode == L1_MODE_HDLC) { // it's 64k
+		if (make_raw_data(bcs))
+			return;		
+	}
+	else { // it's 56k
+		if (make_raw_data_56k(bcs))
+			return;		
+	};
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma2: c%d %4x", bcs->channel,
+			bcs->Flag);
+	if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		sp = bcs->hw.tiger.sendp;
+		if (p == bcs->hw.tiger.s_end)
+			p = bcs->hw.tiger.send -1;
+		if (sp == bcs->hw.tiger.s_end)
+			sp = bcs->hw.tiger.send -1;
+		cnt = p - sp;
+		if (cnt <0) {
+			write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+		} else {
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			p++;
+			cnt++;
+			if (p > bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+			write_raw(bcs, p, bcs->hw.tiger.free - cnt);
+		}
+	} else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) {
+		p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+		cnt = bcs->hw.tiger.s_end - p;
+		if (cnt < 2) {
+			p = bcs->hw.tiger.send + 1;
+			cnt = NETJET_DMA_TXSIZE/2 - 2;
+		} else {
+			p++;
+			p++;
+			if (cnt <= (NETJET_DMA_TXSIZE/2))
+				cnt += NETJET_DMA_TXSIZE/2;
+			cnt--;
+			cnt--;
+		}
+		write_raw(bcs, p, cnt);
+	}
+	if (bcs->cs->debug & L1_DEB_HSCX)
+		debugl1(bcs->cs,"tiger fill_dma3: c%d %4x", bcs->channel,
+			bcs->Flag);
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
+	u_int mask, val, *p=buf;
+	u_int i, s_cnt;
+        
+        if (cnt <= 0)
+        	return;
+	if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+		if (bcs->hw.tiger.sendcnt> cnt) {
+			s_cnt = cnt;
+			bcs->hw.tiger.sendcnt -= cnt;
+		} else {
+			s_cnt = bcs->hw.tiger.sendcnt;
+			bcs->hw.tiger.sendcnt = 0;
+		}
+		if (bcs->channel)
+			mask = 0xffff00ff;
+		else
+			mask = 0xffffff00;
+		for (i=0; i<s_cnt; i++) {
+			val = bcs->channel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) :
+				(bcs->hw.tiger.sp[i]);
+			*p   &= mask;
+			*p++ |= val;
+			if (p>bcs->hw.tiger.s_end)
+				p = bcs->hw.tiger.send;
+		}
+		bcs->hw.tiger.s_tot += s_cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: c%d %p-%p %d/%d %d %x", bcs->channel,
+				buf, p, s_cnt, cnt,
+				bcs->hw.tiger.sendcnt, bcs->cs->hw.njet.irqstat0);
+		if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
+			printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd");
+		bcs->hw.tiger.sp += s_cnt;
+		bcs->hw.tiger.sendp = p;
+		if (!bcs->hw.tiger.sendcnt) {
+			if (!bcs->tx_skb) {
+				debugl1(bcs->cs,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->tx_skb->len;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_any(bcs->tx_skb);
+				bcs->tx_skb = NULL;
+			}
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->hw.tiger.free = cnt - s_cnt;
+			if (bcs->hw.tiger.free > (NETJET_DMA_TXSIZE/2))
+				test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+			else {
+				test_and_clear_bit(BC_FLG_HALF, &bcs->Flag);
+				test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
+			}
+			if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+				netjet_fill_dma(bcs);
+			} else {
+				mask ^= 0xffffffff;
+				if (s_cnt < cnt) {
+					for (i=s_cnt; i<cnt;i++) {
+						*p++ |= mask;
+						if (p>bcs->hw.tiger.s_end)
+							p = bcs->hw.tiger.send;
+					}
+					if (bcs->cs->debug & L1_DEB_HSCX)
+						debugl1(bcs->cs, "tiger write_raw: fill rest %d",
+							cnt - s_cnt);
+				}
+				test_and_set_bit(B_XMTBUFREADY, &bcs->event);
+				schedule_work(&bcs->tqueue);
+			}
+		}
+	} else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		bcs->hw.tiger.free += cnt;
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill half");
+	} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+		test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+		fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+		if (bcs->cs->debug & L1_DEB_HSCX)
+			debugl1(bcs->cs,"tiger write_raw: fill full");
+	}
+}
+
+void write_tiger(struct IsdnCardState *cs) {
+	u_int *p, cnt = NETJET_DMA_TXSIZE/2;
+	
+	if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) {
+		debugl1(cs,"tiger warn write double dma %x/%x",
+			cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+#ifdef ERROR_STATISTIC
+		if (cs->bcs[0].mode)
+			cs->bcs[0].err_tx++;
+		if (cs->bcs[1].mode)
+			cs->bcs[1].err_tx++;
+#endif
+		return;
+	} else {
+		cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE;
+		cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE);
+	}	
+	if (cs->hw.njet.irqstat0  & NETJET_IRQM0_WRITE_1)
+		p = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1;
+	else
+		p = cs->bcs[0].hw.tiger.send + cnt - 1;
+	if ((cs->bcs[0].mode == L1_MODE_HDLC) || (cs->bcs[0].mode == L1_MODE_HDLC_56K))
+		write_raw(cs->bcs, p, cnt);
+	if ((cs->bcs[1].mode == L1_MODE_HDLC) || (cs->bcs[1].mode == L1_MODE_HDLC_56K))
+		write_raw(cs->bcs + 1, p, cnt);
+	cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE;
+}
+
+static void
+tiger_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct BCState *bcs = st->l1.bcs;
+	struct sk_buff *skb = arg;
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n");
+			} else {
+				bcs->tx_skb = skb;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			mode_tiger(bcs, st->l1.mode, st->l1.bc);
+			/* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG */
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			bcs->cs->cardmsg(bcs->cs, MDL_BC_ASSIGN, (void *)(&st->l1.bc));
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			/* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG */
+			bcs->cs->cardmsg(bcs->cs, MDL_BC_RELEASE, (void *)(&st->l1.bc));
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			mode_tiger(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+
+void
+close_tigerstate(struct BCState *bcs)
+{
+	mode_tiger(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.tiger.rcvbuf) {
+			kfree(bcs->hw.tiger.rcvbuf);
+			bcs->hw.tiger.rcvbuf = NULL;
+		}
+		if (bcs->hw.tiger.sendbuf) {
+			kfree(bcs->hw.tiger.sendbuf);
+			bcs->hw.tiger.sendbuf = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_tigerstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.rcvbuf\n");
+			return (1);
+		}
+		if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for tiger.sendbuf\n");
+			return (1);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	bcs->hw.tiger.sendcnt = 0;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+int
+setstack_tiger(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_tigerstate(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = tiger_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+ 
+void __init
+inittiger(struct IsdnCardState *cs)
+{
+	if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.send\n");
+		return;
+	}
+	cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE/2 - 1;
+	cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1;
+	cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send;
+	cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq;
+	cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end;
+	
+	memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int));
+	debugl1(cs, "tiger: send buf %p - %p", cs->bcs[0].hw.tiger.send,
+		cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.send),
+		cs->hw.njet.base + NETJET_DMA_READ_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq),
+		cs->hw.njet.base + NETJET_DMA_READ_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end),
+		cs->hw.njet.base + NETJET_DMA_READ_END);
+	if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_RXSIZE * sizeof(unsigned int),
+		GFP_KERNEL | GFP_DMA))) {
+		printk(KERN_WARNING
+		       "HiSax: No memory for tiger.rec\n");
+		return;
+	}
+	debugl1(cs, "tiger: rec buf %p - %p", cs->bcs[0].hw.tiger.rec,
+		cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1);
+	cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec;
+	memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_RXSIZE * sizeof(unsigned int));
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec),
+		cs->hw.njet.base + NETJET_DMA_WRITE_START);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE/2 - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_IRQ);
+	outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1),
+		cs->hw.njet.base + NETJET_DMA_WRITE_END);
+	debugl1(cs, "tiger: dmacfg  %x/%x  pulse=%d",
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+		inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+		bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+	cs->hw.njet.last_is0 = 0;
+	cs->bcs[0].BC_SetStack = setstack_tiger;
+	cs->bcs[1].BC_SetStack = setstack_tiger;
+	cs->bcs[0].BC_Close = close_tigerstate;
+	cs->bcs[1].BC_Close = close_tigerstate;
+}
+
+void
+releasetiger(struct IsdnCardState *cs)
+{
+	if (cs->bcs[0].hw.tiger.send) {
+		kfree(cs->bcs[0].hw.tiger.send);
+		cs->bcs[0].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.send) {
+		cs->bcs[1].hw.tiger.send = NULL;
+	}
+	if (cs->bcs[0].hw.tiger.rec) {
+		kfree(cs->bcs[0].hw.tiger.rec);
+		cs->bcs[0].hw.tiger.rec = NULL;
+	}
+	if (cs->bcs[1].hw.tiger.rec) {
+		cs->bcs[1].hw.tiger.rec = NULL;
+	}
+}
+
+void
+release_io_netjet(struct IsdnCardState *cs)
+{
+	byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0);
+	releasetiger(cs);
+	release_region(cs->hw.njet.base, 256);
+}
+
diff --git a/drivers/isdn/hisax/netjet.h b/drivers/isdn/hisax/netjet.h
new file mode 100644
index 0000000..1080508
--- /dev/null
+++ b/drivers/isdn/hisax/netjet.h
@@ -0,0 +1,72 @@
+/* $Id: netjet.h,v 2.8.2.2 2004/01/12 22:52:28 keil Exp $
+ *
+ * NETjet common header file
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Matt Henderson,
+ *                 Traverse Technologies P/L www.traverse.com.au
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+extern const char *CardType[];
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define NETJET_CTRL	0x00
+#define NETJET_DMACTRL	0x01
+#define NETJET_AUXCTRL	0x02
+#define NETJET_AUXDATA	0x03
+#define NETJET_IRQMASK0 0x04
+#define NETJET_IRQMASK1 0x05
+#define NETJET_IRQSTAT0 0x06
+#define NETJET_IRQSTAT1 0x07
+#define NETJET_DMA_READ_START	0x08
+#define NETJET_DMA_READ_IRQ	0x0c
+#define NETJET_DMA_READ_END	0x10
+#define NETJET_DMA_READ_ADR	0x14
+#define NETJET_DMA_WRITE_START	0x18
+#define NETJET_DMA_WRITE_IRQ	0x1c
+#define NETJET_DMA_WRITE_END	0x20
+#define NETJET_DMA_WRITE_ADR	0x24
+#define NETJET_PULSE_CNT	0x28
+
+#define NETJET_ISAC_OFF	0xc0
+#define NETJET_ISACIRQ	0x10
+#define NETJET_IRQM0_READ	0x0c
+#define NETJET_IRQM0_READ_1	0x04
+#define NETJET_IRQM0_READ_2	0x08
+#define NETJET_IRQM0_WRITE	0x03
+#define NETJET_IRQM0_WRITE_1	0x01
+#define NETJET_IRQM0_WRITE_2	0x02
+
+#define NETJET_DMA_TXSIZE 512
+#define NETJET_DMA_RXSIZE 128
+
+#define HDLC_ZERO_SEARCH 0
+#define HDLC_FLAG_SEARCH 1
+#define HDLC_FLAG_FOUND  2
+#define HDLC_FRAME_FOUND 3
+#define HDLC_NULL 4
+#define HDLC_PART 5
+#define HDLC_FULL 6
+
+#define HDLC_FLAG_VALUE	0x7e
+
+u_char NETjet_ReadIC(struct IsdnCardState *cs, u_char offset);
+void NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value);
+void NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size);
+void NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size);
+
+void read_tiger(struct IsdnCardState *cs);
+void write_tiger(struct IsdnCardState *cs);
+
+void netjet_fill_dma(struct BCState *bcs);
+void netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs);
+void inittiger(struct IsdnCardState *cs);
+void release_io_netjet(struct IsdnCardState *cs);
+
diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c
new file mode 100644
index 0000000..cf77d83
--- /dev/null
+++ b/drivers/isdn/hisax/niccy.c
@@ -0,0 +1,389 @@
+/* $Id: niccy.c,v 1.21.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
+ * compatible (SAGEM cybermodem)
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ * 
+ * Thanks to Dr. Neuhaus and SAGEM for information
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+const char *niccy_revision = "$Revision: 1.21.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_PCI_DATA	0
+#define HSCX_PCI_DATA	1
+#define ISAC_PCI_ADDR	2
+#define HSCX_PCI_ADDR	3
+#define ISAC_PNP	0
+#define HSCX_PNP	1
+
+/* SUB Types */
+#define NICCY_PNP	1
+#define NICCY_PCI	2
+
+/* PCI stuff */
+#define PCI_IRQ_CTRL_REG	0x38
+#define PCI_IRQ_ENABLE		0x1f00
+#define PCI_IRQ_DISABLE		0xff0000
+#define PCI_IRQ_ASSERT		0x800000
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.niccy.hscx_ale,
+			cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.niccy.hscx_ale,
+		 cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \
+		cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+niccy_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (cs->subtyp == NICCY_PCI) {
+		int ival;
+		ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_NONE;
+		}
+		outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	}
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0);
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0);
+	writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_niccy(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == NICCY_PCI) {
+		int val;
+		
+		val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		val &= PCI_IRQ_DISABLE;
+		outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		release_region(cs->hw.niccy.cfg_reg, 0x40);
+		release_region(cs->hw.niccy.isac, 4);
+	} else {
+		release_region(cs->hw.niccy.isac, 2);
+		release_region(cs->hw.niccy.isac_ale, 2);
+	}
+}
+
+static void
+niccy_reset(struct IsdnCardState *cs)
+{
+	if (cs->subtyp == NICCY_PCI) {
+		int val;
+
+		val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+		val |= PCI_IRQ_ENABLE;
+		outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+	}
+	inithscxisac(cs, 3);
+}
+
+static int
+niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			niccy_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_niccy(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			niccy_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *niccy_dev __initdata = NULL;
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __init
+setup_niccy(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, niccy_revision);
+	printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NICCY)
+		return (0);
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d = NULL;
+		int err;
+
+		if ((pnp_c = pnp_find_card(
+			ISAPNP_VENDOR('S', 'D', 'A'),
+			ISAPNP_FUNCTION(0x0150), pnp_c))) {
+			if (!(pnp_d = pnp_find_dev(pnp_c,
+				ISAPNP_VENDOR('S', 'D', 'A'),
+				ISAPNP_FUNCTION(0x0150), pnp_d))) {
+				printk(KERN_ERR "NiccyPnP: PnP error card found, no device\n");
+				return (0);
+			}
+			pnp_disable_dev(pnp_d);
+			err = pnp_activate_dev(pnp_d);
+			if (err<0) {
+				printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+					__FUNCTION__, err);
+				return(0);
+			}
+			card->para[1] = pnp_port_start(pnp_d, 0);
+			card->para[2] = pnp_port_start(pnp_d, 1);
+			card->para[0] = pnp_irq(pnp_d, 0);
+			if (!card->para[0] || !card->para[1] || !card->para[2]) {
+				printk(KERN_ERR "NiccyPnP:some resources are missing %ld/%lx/%lx\n",
+					card->para[0], card->para[1], card->para[2]);
+				pnp_disable_dev(pnp_d);
+				return(0);
+			}
+		} else {
+			printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n");
+		}
+	}
+#endif
+	if (card->para[1]) {
+		cs->hw.niccy.isac = card->para[1] + ISAC_PNP;
+		cs->hw.niccy.hscx = card->para[1] + HSCX_PNP;
+		cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP;
+		cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP;
+		cs->hw.niccy.cfg_reg = 0;
+		cs->subtyp = NICCY_PNP;
+		cs->irq = card->para[0];
+		if (!request_region(cs->hw.niccy.isac, 2, "niccy data")) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 1);
+			return (0);
+		}
+		if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) {
+			printk(KERN_WARNING
+				"HiSax: %s address port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac_ale,
+				cs->hw.niccy.isac_ale + 1);
+			release_region(cs->hw.niccy.isac, 2);
+			return (0);
+		}
+	} else {
+#ifdef CONFIG_PCI
+		u_int pci_ioaddr;
+		cs->subtyp = 0;
+		if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM,
+			PCI_DEVICE_ID_SATSAGEM_NICCY, niccy_dev))) {
+			if (pci_enable_device(niccy_dev))
+				return(0);
+			/* get IRQ */
+			if (!niccy_dev->irq) {
+				printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->irq = niccy_dev->irq;
+			cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0);
+			if (!cs->hw.niccy.cfg_reg) {
+				printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
+				return(0);
+			}
+			pci_ioaddr = pci_resource_start(niccy_dev, 1);
+			if (!pci_ioaddr) {
+				printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			cs->subtyp = NICCY_PCI;
+		} else {
+			printk(KERN_WARNING "Niccy: No PCI card found\n");
+			return(0);
+		}
+		cs->irq_flags |= SA_SHIRQ;
+		cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
+		cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
+		cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
+		cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
+		if (!request_region(cs->hw.niccy.isac, 4, "niccy")) {
+			printk(KERN_WARNING
+				"HiSax: %s data port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.isac,
+				cs->hw.niccy.isac + 4);
+			return (0);
+		}
+		if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) {
+			printk(KERN_WARNING
+			       "HiSax: %s pci port %x-%x already in use\n",
+				CardType[card->typ],
+				cs->hw.niccy.cfg_reg,
+				cs->hw.niccy.cfg_reg + 0x40);
+			release_region(cs->hw.niccy.isac, 4);
+			return (0);
+		}
+#else
+		printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
+		printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}
+	printk(KERN_INFO "HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n",
+		CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI",
+		cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &niccy_card_msg;
+	cs->irq_func = &niccy_interrupt;
+	ISACVersion(cs, "Niccy:");
+	if (HscxVersion(cs, "Niccy:")) {
+		printk(KERN_WARNING
+		    "Niccy: wrong HSCX versions check IO address\n");
+		release_io_niccy(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c
new file mode 100644
index 0000000..fd66469
--- /dev/null
+++ b/drivers/isdn/hisax/nj_s.c
@@ -0,0 +1,278 @@
+/* $Id: nj_s.c,v 2.13.2.4 2004/01/16 01:53:48 keil Exp $
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+const char *NETjet_S_revision = "$Revision: 2.13.2.4 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static irqreturn_t
+netjet_s_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, s1val, s0val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	s1val = bytein(cs->hw.njet.base + NETJET_IRQSTAT1);
+	if (!(s1val & NETJET_ISACIRQ)) {
+		val = NETjet_ReadIC(cs, ISAC_ISTA);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", s1val, val);
+		if (val) {
+			isac_interrupt(cs, val);
+			NETjet_WriteIC(cs, ISAC_MASK, 0xFF);
+			NETjet_WriteIC(cs, ISAC_MASK, 0x0);
+		}
+		s1val = 1;
+	} else
+		s1val = 0;
+	/* 
+	 * read/write stat0 is better, because lower IRQ rate
+	 * Note the IRQ is on for 125 us if a condition match
+	 * thats long on modern CPU and so the IRQ is reentered
+	 * all the time.
+	 */
+	s0val = bytein(cs->hw.njet.base + NETJET_IRQSTAT0);
+	if ((s0val | s1val)==0) { // shared IRQ
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	} 
+	if (s0val)
+		byteout(cs->hw.njet.base + NETJET_IRQSTAT0, s0val);
+	/* start new code 13/07/00 GE */
+	/* set bits in sval to indicate which page is free */
+	if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+		/* the 2nd write page is free */
+		s0val = 0x08;
+	else	/* the 1st write page is free */
+		s0val = 0x04;	
+	if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+		/* the 2nd read page is free */
+		s0val |= 0x02;
+	else	/* the 1st read page is free */
+		s0val |= 0x01;	
+	if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+	{
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			printk(KERN_WARNING "nj LOCK_ATOMIC s0val %x->%x\n",
+				cs->hw.njet.last_is0, s0val);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		cs->hw.njet.irqstat0 = s0val;
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) != 
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+			/* we have a read dma int */
+			read_tiger(cs);
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+			/* we have a write dma int */
+			write_tiger(cs);
+		/* end new code 13/07/00 GE */
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+reset_netjet_s(struct IsdnCardState *cs)
+{
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	/* now edge triggered for TJ320 GE 13/07/00 */
+	/* see comment in IRQ function */
+	if (cs->subtyp) /* TJ320 */
+		cs->hw.njet.ctrl_reg = 0x40;  /* Reset Off and status read clear */
+	else
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	cs->hw.njet.auxd = 0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_S_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_netjet_s(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_INIT:
+			reset_netjet_s(cs);
+			inittiger(cs);
+			spin_lock_irqsave(&cs->lock, flags);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_netjet __initdata = NULL;
+
+int __init
+setup_netjet_s(struct IsdnCard *card)
+{
+	int bytecnt,cfg;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, NETjet_S_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETjet-S driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET_S)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+#ifdef CONFIG_PCI
+
+	for ( ;; )
+	{
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			pci_set_master(dev_netjet);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "NETjet-S: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "NETjet-S: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+			/* the TJ300 and TJ320 must be detected, the IRQ handling is different
+			 * unfortunatly the chips use the same device ID, but the TJ320 has
+			 * the bit20 in status PCI cfg register set
+			 */
+			pci_read_config_dword(dev_netjet, 0x04, &cfg);
+			if (cfg & 0x00100000)
+				cs->subtyp = 1; /* TJ320 */
+			else
+				cs->subtyp = 0; /* TJ300 */
+			/* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG www.formula-n.com */
+			if ((dev_netjet->subsystem_vendor == 0x55) &&
+				(dev_netjet->subsystem_device == 0x02)) {
+				printk(KERN_WARNING "Netjet: You tried to load this driver with an incompatible TigerJet-card\n");
+				printk(KERN_WARNING "Use type=41 for Formula-n enter:now ISDN PCI and compatible\n");
+				return(0);
+			}
+			/* end new code */
+		} else {
+			printk(KERN_WARNING "NETjet-S: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+
+		cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.auxd = 0xC0;
+		cs->hw.njet.dmactrl = 0;
+
+		byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+		byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		switch ( ( ( NETjet_ReadIC( cs, ISAC_RBCH ) >> 5 ) & 3 ) )
+		{
+			case 0 :
+				break;
+
+			case 3 :
+				printk( KERN_WARNING "NETjet-S: NETspider-U PCI card found\n" );
+				continue;
+
+			default :
+				printk( KERN_WARNING "NETjet-S: No PCI card found\n" );
+				return 0;
+                }
+                break;
+	}
+#else
+
+	printk(KERN_WARNING "NETjet-S: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETjet-S: unable to config NETJET-S PCI\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"NETjet-S: %s card configured at %#lx IRQ %d\n",
+		cs->subtyp ? "TJ320" : "TJ300", cs->hw.njet.base, cs->irq);
+	if (!request_region(cs->hw.njet.base, bytecnt, "netjet-s isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %#lx-%#lx already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	}
+	cs->readisac  = &NETjet_ReadIC;
+	cs->writeisac = &NETjet_WriteIC;
+	cs->readisacfifo  = &NETjet_ReadICfifo;
+	cs->writeisacfifo = &NETjet_WriteICfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	setup_isac(cs);
+	cs->cardmsg = &NETjet_S_card_msg;
+	cs->irq_func = &netjet_s_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ISACVersion(cs, "NETjet-S:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c
new file mode 100644
index 0000000..3d6441e
--- /dev/null
+++ b/drivers/isdn/hisax/nj_u.c
@@ -0,0 +1,244 @@
+/* $Id: nj_u.c,v 2.14.2.3 2004/01/13 14:31:26 keil Exp $ 
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "icc.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+const char *NETjet_U_revision = "$Revision: 2.14.2.3 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+	return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static irqreturn_t
+netjet_u_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val, sval;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
+		NETJET_ISACIRQ)) {
+		val = NETjet_ReadIC(cs, ICC_ISTA);
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "tiger: i1 %x %x", sval, val);
+		if (val) {
+			icc_interrupt(cs, val);
+			NETjet_WriteIC(cs, ICC_MASK, 0xFF);
+			NETjet_WriteIC(cs, ICC_MASK, 0x0);
+		}
+	}
+	/* start new code 13/07/00 GE */
+	/* set bits in sval to indicate which page is free */
+	if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+		/* the 2nd write page is free */
+		sval = 0x08;
+	else	/* the 1st write page is free */
+		sval = 0x04;	
+	if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+		inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+		/* the 2nd read page is free */
+		sval = sval | 0x02;
+	else	/* the 1st read page is free */
+		sval = sval | 0x01;	
+	if (sval != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+	{
+		if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return IRQ_HANDLED;
+		}
+		cs->hw.njet.irqstat0 = sval;
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) != 
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+			/* we have a read dma int */
+			read_tiger(cs);
+		if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+			(cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+			/* we have a write dma int */
+			write_tiger(cs);
+		/* end new code 13/07/00 GE */
+		test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+reset_netjet_u(struct IsdnCardState *cs)
+{
+	cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	cs->hw.njet.ctrl_reg = 0x40;  /* Reset Off and status read clear */
+	/* now edge triggered for TJ320 GE 13/07/00 */
+	byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+	mdelay(10);
+	cs->hw.njet.auxd = 0xC0;
+	cs->hw.njet.dmactrl = 0;
+	byteout(cs->hw.njet.auxa, 0);
+	byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+	byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+	byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_U_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_netjet_u(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_netjet(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inittiger(cs);
+			reset_netjet_u(cs);
+			clear_pending_icc_ints(cs);
+			initicc(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ICC_MASK, 0);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_netjet __initdata = NULL;
+
+int __init
+setup_netjet_u(struct IsdnCard *card)
+{
+	int bytecnt;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+#ifdef CONFIG_PCI
+#endif
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, NETjet_U_revision);
+	printk(KERN_INFO "HiSax: Traverse Tech. NETspider-U driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_NETJET_U)
+		return(0);
+	test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+#ifdef CONFIG_PCI
+
+	for ( ;; )
+	{
+		if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+			PCI_DEVICE_ID_TIGERJET_300,  dev_netjet))) {
+			if (pci_enable_device(dev_netjet))
+				return(0);
+			pci_set_master(dev_netjet);
+			cs->irq = dev_netjet->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "NETspider-U: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+			if (!cs->hw.njet.base) {
+				printk(KERN_WARNING "NETspider-U: No IO-Adr for PCI card found\n");
+				return(0);
+			}
+		} else {
+			printk(KERN_WARNING "NETspider-U: No PCI card found\n");
+			return(0);
+		}
+
+		cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+		cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+		mdelay(10);
+
+		cs->hw.njet.ctrl_reg = 0xff;  /* Reset On */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.ctrl_reg = 0x00;  /* Reset Off and status read clear */
+		byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+		mdelay(10);
+
+		cs->hw.njet.auxd = 0xC0;
+		cs->hw.njet.dmactrl = 0;
+
+		byteout(cs->hw.njet.auxa, 0);
+		byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+		byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+		byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+		switch ( ( ( NETjet_ReadIC( cs, ICC_RBCH ) >> 5 ) & 3 ) )
+		{
+			case 3 :
+				break;
+
+			case 0 :
+				printk( KERN_WARNING "NETspider-U: NETjet-S PCI card found\n" );
+				continue;
+
+			default :
+				printk( KERN_WARNING "NETspider-U: No PCI card found\n" );
+				return 0;
+                }
+                break;
+	}
+#else
+
+	printk(KERN_WARNING "NETspider-U: NO_PCI_BIOS\n");
+	printk(KERN_WARNING "NETspider-U: unable to config NETspider-U PCI\n");
+	return (0);
+
+#endif /* CONFIG_PCI */
+
+	bytecnt = 256;
+
+	printk(KERN_INFO
+		"NETspider-U: PCI card configured at %#lx IRQ %d\n",
+		cs->hw.njet.base, cs->irq);
+	if (!request_region(cs->hw.njet.base, bytecnt, "netspider-u isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %#lx-%#lx already in use\n",
+		       CardType[card->typ],
+		       cs->hw.njet.base,
+		       cs->hw.njet.base + bytecnt);
+		return (0);
+	}
+	setup_icc(cs);
+	cs->readisac  = &NETjet_ReadIC;
+	cs->writeisac = &NETjet_WriteIC;
+	cs->readisacfifo  = &NETjet_ReadICfifo;
+	cs->writeisacfifo = &NETjet_WriteICfifo;
+	cs->BC_Read_Reg  = &dummyrr;
+	cs->BC_Write_Reg = &dummywr;
+	cs->BC_Send_Data = &netjet_fill_dma;
+	cs->cardmsg = &NETjet_U_card_msg;
+	cs->irq_func = &netjet_u_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ICCVersion(cs, "NETspider-U:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c
new file mode 100644
index 0000000..170fcd4
--- /dev/null
+++ b/drivers/isdn/hisax/q931.c
@@ -0,0 +1,1522 @@
+/* $Id: q931.c,v 1.12.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * code to decode ITU Q.931 call control messages
+ *
+ * Author       Jan den Ouden
+ * Copyright    by Jan den Ouden
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Changelog:
+ *
+ * Pauline Middelink    general improvements
+ * Beat Doebeli         cause texts, display information element
+ * Karsten Keil         cause texts, display information element for 1TR6
+ *
+ */
+
+
+#include "hisax.h"
+#include "l3_1tr6.h"
+
+void
+iecpy(u_char * dest, u_char * iestart, int ieoffset)
+{
+	u_char *p;
+	int l;
+
+	p = iestart + ieoffset + 2;
+	l = iestart[1] - ieoffset;
+	while (l--)
+		*dest++ = *p++;
+	*dest++ = '\0';
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+	u_char nr;
+	char *descr;
+} mtlist[] = {
+
+	{
+		0x1, "ALERTING"
+	},
+	{
+		0x2, "CALL PROCEEDING"
+	},
+	{
+		0x7, "CONNECT"
+	},
+	{
+		0xf, "CONNECT ACKNOWLEDGE"
+	},
+	{
+		0x3, "PROGRESS"
+	},
+	{
+		0x5, "SETUP"
+	},
+	{
+		0xd, "SETUP ACKNOWLEDGE"
+	},
+	{
+		0x24, "HOLD"
+	},
+	{
+		0x28, "HOLD ACKNOWLEDGE"
+	},
+	{
+		0x30, "HOLD REJECT"
+	},
+	{
+		0x31, "RETRIEVE"
+	},
+	{
+		0x33, "RETRIEVE ACKNOWLEDGE"
+	},
+	{
+		0x37, "RETRIEVE REJECT"
+	},
+	{
+		0x26, "RESUME"
+	},
+	{
+		0x2e, "RESUME ACKNOWLEDGE"
+	},
+	{
+		0x22, "RESUME REJECT"
+	},
+	{
+		0x25, "SUSPEND"
+	},
+	{
+		0x2d, "SUSPEND ACKNOWLEDGE"
+	},
+	{
+		0x21, "SUSPEND REJECT"
+	},
+	{
+		0x20, "USER INFORMATION"
+	},
+	{
+		0x45, "DISCONNECT"
+	},
+	{
+		0x4d, "RELEASE"
+	},
+	{
+		0x5a, "RELEASE COMPLETE"
+	},
+	{
+		0x46, "RESTART"
+	},
+	{
+		0x4e, "RESTART ACKNOWLEDGE"
+	},
+	{
+		0x60, "SEGMENT"
+	},
+	{
+		0x79, "CONGESTION CONTROL"
+	},
+	{
+		0x7b, "INFORMATION"
+	},
+	{
+		0x62, "FACILITY"
+	},
+	{
+		0x6e, "NOTIFY"
+	},
+	{
+		0x7d, "STATUS"
+	},
+	{
+		0x75, "STATUS ENQUIRY"
+	}
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+static
+struct MessageType mt_n0[] =
+{
+	{MT_N0_REG_IND, "REGister INDication"},
+	{MT_N0_CANC_IND, "CANCel INDication"},
+	{MT_N0_FAC_STA, "FACility STAtus"},
+	{MT_N0_STA_ACK, "STAtus ACKnowledge"},
+	{MT_N0_STA_REJ, "STAtus REJect"},
+	{MT_N0_FAC_INF, "FACility INFormation"},
+	{MT_N0_INF_ACK, "INFormation ACKnowledge"},
+	{MT_N0_INF_REJ, "INFormation REJect"},
+	{MT_N0_CLOSE, "CLOSE"},
+	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
+};
+
+#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType))
+
+static
+struct MessageType mt_n1[] =
+{
+	{MT_N1_ESC, "ESCape"},
+	{MT_N1_ALERT, "ALERT"},
+	{MT_N1_CALL_SENT, "CALL SENT"},
+	{MT_N1_CONN, "CONNect"},
+	{MT_N1_CONN_ACK, "CONNect ACKnowledge"},
+	{MT_N1_SETUP, "SETUP"},
+	{MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
+	{MT_N1_RES, "RESume"},
+	{MT_N1_RES_ACK, "RESume ACKnowledge"},
+	{MT_N1_RES_REJ, "RESume REJect"},
+	{MT_N1_SUSP, "SUSPend"},
+	{MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
+	{MT_N1_SUSP_REJ, "SUSPend REJect"},
+	{MT_N1_USER_INFO, "USER INFO"},
+	{MT_N1_DET, "DETach"},
+	{MT_N1_DISC, "DISConnect"},
+	{MT_N1_REL, "RELease"},
+	{MT_N1_REL_ACK, "RELease ACKnowledge"},
+	{MT_N1_CANC_ACK, "CANCel ACKnowledge"},
+	{MT_N1_CANC_REJ, "CANCel REJect"},
+	{MT_N1_CON_CON, "CONgestion CONtrol"},
+	{MT_N1_FAC, "FACility"},
+	{MT_N1_FAC_ACK, "FACility ACKnowledge"},
+	{MT_N1_FAC_CAN, "FACility CANcel"},
+	{MT_N1_FAC_REG, "FACility REGister"},
+	{MT_N1_FAC_REJ, "FACility REJect"},
+	{MT_N1_INFO, "INFOrmation"},
+	{MT_N1_REG_ACK, "REGister ACKnowledge"},
+	{MT_N1_REG_REJ, "REGister REJect"},
+	{MT_N1_STAT, "STATus"}
+};
+
+#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
+
+
+static int
+prbits(char *dest, u_char b, int start, int len)
+{
+	char *dp = dest;
+
+	b = b << (8 - start);
+	while (len--) {
+		if (b & 0x80)
+			*dp++ = '1';
+		else
+			*dp++ = '0';
+		b = b << 1;
+	}
+	return (dp - dest);
+}
+
+static
+u_char *
+skipext(u_char * p)
+{
+	while (!(*p++ & 0x80));
+	return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ *         not yet written...
+ */
+
+static
+struct CauseValue {
+	u_char nr;
+	char *edescr;
+	char *ddescr;
+} cvlist[] = {
+
+	{
+		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+	},
+	{
+		0x02, "No route to specified transit network", ""
+	},
+	{
+		0x03, "No route to destination", ""
+	},
+	{
+		0x04, "Send special information tone", ""
+	},
+	{
+		0x05, "Misdialled trunk prefix", ""
+	},
+	{
+		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+	},
+	{
+		0x07, "Channel awarded and being delivered in an established channel", ""
+	},
+	{
+		0x08, "Preemption", ""
+	},
+	{
+		0x09, "Preemption - circuit reserved for reuse", ""
+	},
+	{
+		0x10, "Normal call clearing", "Normale Ausloesung"
+	},
+	{
+		0x11, "User busy", "TNB besetzt"
+	},
+	{
+		0x12, "No user responding", ""
+	},
+	{
+		0x13, "No answer from user (user alerted)", ""
+	},
+	{
+		0x14, "Subscriber absent", ""
+	},
+	{
+		0x15, "Call rejected", ""
+	},
+	{
+		0x16, "Number changed", ""
+	},
+	{
+		0x1a, "non-selected user clearing", ""
+	},
+	{
+		0x1b, "Destination out of order", ""
+	},
+	{
+		0x1c, "Invalid number format (address incomplete)", ""
+	},
+	{
+		0x1d, "Facility rejected", ""
+	},
+	{
+		0x1e, "Response to Status enquiry", ""
+	},
+	{
+		0x1f, "Normal, unspecified", ""
+	},
+	{
+		0x22, "No circuit/channel available", ""
+	},
+	{
+		0x26, "Network out of order", ""
+	},
+	{
+		0x27, "Permanent frame mode connection out-of-service", ""
+	},
+	{
+		0x28, "Permanent frame mode connection operational", ""
+	},
+	{
+		0x29, "Temporary failure", ""
+	},
+	{
+		0x2a, "Switching equipment congestion", ""
+	},
+	{
+		0x2b, "Access information discarded", ""
+	},
+	{
+		0x2c, "Requested circuit/channel not available", ""
+	},
+	{
+		0x2e, "Precedence call blocked", ""
+	},
+	{
+		0x2f, "Resource unavailable, unspecified", ""
+	},
+	{
+		0x31, "Quality of service unavailable", ""
+	},
+	{
+		0x32, "Requested facility not subscribed", ""
+	},
+	{
+		0x35, "Outgoing calls barred within CUG", ""
+	},
+	{
+		0x37, "Incoming calls barred within CUG", ""
+	},
+	{
+		0x39, "Bearer capability not authorized", ""
+	},
+	{
+		0x3a, "Bearer capability not presently available", ""
+	},
+	{
+		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+	},
+	{
+		0x3f, "Service or option not available, unspecified", ""
+	},
+	{
+		0x41, "Bearer capability not implemented", ""
+	},
+	{
+		0x42, "Channel type not implemented", ""
+	},
+	{
+		0x43, "Requested facility not implemented", ""
+	},
+	{
+		0x44, "Only restricted digital information bearer capability is available", ""
+	},
+	{
+		0x4f, "Service or option not implemented", ""
+	},
+	{
+		0x51, "Invalid call reference value", ""
+	},
+	{
+		0x52, "Identified channel does not exist", ""
+	},
+	{
+		0x53, "A suspended call exists, but this call identity does not", ""
+	},
+	{
+		0x54, "Call identity in use", ""
+	},
+	{
+		0x55, "No call suspended", ""
+	},
+	{
+		0x56, "Call having the requested call identity has been cleared", ""
+	},
+	{
+		0x57, "User not member of CUG", ""
+	},
+	{
+		0x58, "Incompatible destination", ""
+	},
+	{
+		0x5a, "Non-existent CUG", ""
+	},
+	{
+		0x5b, "Invalid transit network selection", ""
+	},
+	{
+		0x5f, "Invalid message, unspecified", ""
+	},
+	{
+		0x60, "Mandatory information element is missing", ""
+	},
+	{
+		0x61, "Message type non-existent or not implemented", ""
+	},
+	{
+		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+	},
+	{
+		0x63, "Information element/parameter non-existent or not implemented", ""
+	},
+	{
+		0x64, "Invalid information element contents", ""
+	},
+	{
+		0x65, "Message not compatible with call state", ""
+	},
+	{
+		0x66, "Recovery on timer expiry", ""
+	},
+	{
+		0x67, "Parameter non-existent or not implemented - passed on", ""
+	},
+	{
+		0x6e, "Message with unrecognized parameter discarded", ""
+	},
+	{
+		0x6f, "Protocol error, unspecified", ""
+	},
+	{
+		0x7f, "Interworking, unspecified", ""
+	},
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, u_char * p)
+{
+	u_char *end;
+	char *dp = dest;
+	int i, cause;
+
+	end = p + p[1] + 1;
+	p += 2;
+	dp += sprintf(dp, "    coding ");
+	dp += prbits(dp, *p, 7, 2);
+	dp += sprintf(dp, " location ");
+	dp += prbits(dp, *p, 4, 4);
+	*dp++ = '\n';
+	p = skipext(p);
+
+	cause = 0x7f & *p++;
+
+	/* locate cause value */
+	for (i = 0; i < CVSIZE; i++)
+		if (cvlist[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == CVSIZE)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
+
+	while (!0) {
+		if (p > end)
+			break;
+		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
+		dp += sprintf(dp, " rej %d ", *p & 0x7f);
+		if (*p & 0x80) {
+			*dp++ = '\n';
+			break;
+		} else
+			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+	}
+	return (dp - dest);
+
+}
+
+static
+struct MessageType cause_1tr6[] =
+{
+	{CAUSE_InvCRef, "Invalid Call Reference"},
+	{CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
+	{CAUSE_CIDunknown, "Caller Identity unknown"},
+	{CAUSE_CIDinUse, "Caller Identity in Use"},
+	{CAUSE_NoChans, "No Channels available"},
+	{CAUSE_FacNotImpl, "Facility Not Implemented"},
+	{CAUSE_FacNotSubscr, "Facility Not Subscribed"},
+	{CAUSE_OutgoingBarred, "Outgoing calls barred"},
+	{CAUSE_UserAccessBusy, "User Access Busy"},
+	{CAUSE_NegativeGBG, "Negative GBG"},
+	{CAUSE_UnknownGBG, "Unknown  GBG"},
+	{CAUSE_NoSPVknown, "No SPV known"},
+	{CAUSE_DestNotObtain, "Destination not obtainable"},
+	{CAUSE_NumberChanged, "Number changed"},
+	{CAUSE_OutOfOrder, "Out Of Order"},
+	{CAUSE_NoUserResponse, "No User Response"},
+	{CAUSE_UserBusy, "User Busy"},
+	{CAUSE_IncomingBarred, "Incoming Barred"},
+	{CAUSE_CallRejected, "Call Rejected"},
+	{CAUSE_NetworkCongestion, "Network Congestion"},
+	{CAUSE_RemoteUser, "Remote User initiated"},
+	{CAUSE_LocalProcErr, "Local Procedure Error"},
+	{CAUSE_RemoteProcErr, "Remote Procedure Error"},
+	{CAUSE_RemoteUserSuspend, "Remote User Suspend"},
+	{CAUSE_RemoteUserResumed, "Remote User Resumed"},
+	{CAUSE_UserInfoDiscarded, "User Info Discarded"}
+};
+
+int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
+
+static int
+prcause_1tr6(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int i, cause;
+
+	p++;
+	if (0 == *p) {
+		dp += sprintf(dp, "   OK (cause length=0)\n");
+		return (dp - dest);
+	} else if (*p > 1) {
+		dp += sprintf(dp, "    coding ");
+		dp += prbits(dp, p[2], 7, 2);
+		dp += sprintf(dp, " location ");
+		dp += prbits(dp, p[2], 4, 4);
+		*dp++ = '\n';
+	}
+	p++;
+	cause = 0x7f & *p;
+
+	/* locate cause value */
+	for (i = 0; i < cause_1tr6_len; i++)
+		if (cause_1tr6[i].nr == cause)
+			break;
+
+	/* display cause value if it exists */
+	if (i == cause_1tr6_len)
+		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+	else
+		dp += sprintf(dp, "  cause value %x : %s \n", cause, cause_1tr6[i].descr);
+
+	return (dp - dest);
+
+}
+
+static int
+prchident(char *dest, u_char * p)
+{
+	char *dp = dest;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static int
+prcalled(char *dest, u_char * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prcalling(char *dest, u_char * p)
+{
+	int l;
+	char *dp = dest;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p & 0x80)) {
+		dp += sprintf(dp, "    octet 3a ");
+		dp += prbits(dp, *++p, 8, 8);
+		*dp++ = '\n';
+		l--;
+	};
+	p++;
+
+	dp += sprintf(dp, "    number digits ");
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, u_char * p)
+{
+	char *dp = dest, ch;
+
+	p += 2;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p++, 8, 8);
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if ((*p++ & 0x1f) == 0x18) {
+		dp += sprintf(dp, "    octet 4.1 ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 1 */
+	if ((*p & 0x60) == 0x20) {
+		ch = ' ';
+		do {
+			dp += sprintf(dp, "    octet 5%c ", ch);
+			dp += prbits(dp, *p, 8, 8);
+			*dp++ = '\n';
+			if (ch == ' ')
+				ch = 'a';
+			else
+				ch++;
+		}
+		while (!(*p++ & 0x80));
+	}
+	/* check for user information layer 2 */
+	if ((*p & 0x60) == 0x40) {
+		dp += sprintf(dp, "    octet 6  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	/* check for user information layer 3 */
+	if ((*p & 0x60) == 0x60) {
+		dp += sprintf(dp, "    octet 7  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
+
+static
+int
+prbearer_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	u_char len;
+
+	p++;
+	len = *p++;
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p, 8, 8);
+	switch (*p++) {
+		case 0x80:
+			dp += sprintf(dp, " Speech");
+			break;
+		case 0x88:
+			dp += sprintf(dp, " Unrestricted digital information");
+			break;
+		case 0x90:
+			dp += sprintf(dp, " 3.1 kHz audio");
+			break;
+		default:
+			dp += sprintf(dp, " Unknown information-transfer capability");
+	}
+	*dp++ = '\n';
+	dp += sprintf(dp, "    octet 4  ");
+	dp += prbits(dp, *p, 8, 8);
+	switch (*p++) {
+		case 0x90:
+			dp += sprintf(dp, " 64 kbps, circuit mode");
+			break;
+		case 0xc0:
+			dp += sprintf(dp, " Packet mode");
+			break;
+		default:
+			dp += sprintf(dp, " Unknown transfer mode");
+	}
+	*dp++ = '\n';
+	if (len > 2) {
+		dp += sprintf(dp, "    octet 5  ");
+		dp += prbits(dp, *p, 8, 8);
+		switch (*p++) {
+			case 0x21:
+				dp += sprintf(dp, " Rate adaption\n");
+				dp += sprintf(dp, "    octet 5a ");
+				dp += prbits(dp, *p, 8, 8);
+				break;
+			case 0xa2:
+				dp += sprintf(dp, " u-law");
+				break;
+			default:
+				dp += sprintf(dp, " Unknown UI layer 1 protocol");
+		}
+		*dp++ = '\n';
+	}
+	return (dp - dest);
+}
+
+static int
+general(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static int
+general_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the information element */
+	while (l--) {
+		dp += sprintf(dp, "    octet %d%c ", octet, ch);
+		dp += prbits(dp, *p, 8, 8);
+		*dp++ = '\n';
+
+		/* last octet in group? */
+		if (*p++ & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+		else
+			ch++;
+	}
+	return (dp - dest);
+}
+
+static int
+prcharge(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++ - 1;
+	dp += sprintf(dp, "    GEA ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "  Anzahl: ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+static int
+prtext(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    ");
+	/* Iterate over all octets in the * information element */
+	while (l--)
+		*dp++ = *p++;
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static int
+prfeatureind(char *dest, u_char * p)
+{
+	char *dp = dest;
+
+	p += 2; /* skip id, len */
+	dp += sprintf(dp, "    octet 3  ");
+	dp += prbits(dp, *p, 8, 8);
+	*dp++ = '\n';
+	if (!(*p++ & 80)) {
+		dp += sprintf(dp, "    octet 4  ");
+		dp += prbits(dp, *p++, 8, 8);
+		*dp++ = '\n';
+	}
+	dp += sprintf(dp, "    Status:  ");
+	switch (*p) {
+		case 0:
+			dp += sprintf(dp, "Idle");
+			break;
+		case 1:
+			dp += sprintf(dp, "Active");
+			break;
+		case 2:
+			dp += sprintf(dp, "Prompt");
+			break;
+		case 3:
+			dp += sprintf(dp, "Pending");
+			break;
+		default:
+			dp += sprintf(dp, "(Reserved)");
+			break;
+	}
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+static
+struct DTag { /* Display tags */
+	u_char nr;
+	char *descr;
+} dtaglist[] = {
+	{ 0x82, "Continuation" },
+	{ 0x83, "Called address" },
+	{ 0x84, "Cause" },
+	{ 0x85, "Progress indicator" },
+	{ 0x86, "Notification indicator" },
+	{ 0x87, "Prompt" },
+	{ 0x88, "Accumlated digits" },
+	{ 0x89, "Status" },
+	{ 0x8a, "Inband" },
+	{ 0x8b, "Calling address" },
+	{ 0x8c, "Reason" },
+	{ 0x8d, "Calling party name" },
+	{ 0x8e, "Called party name" },
+	{ 0x8f, "Orignal called name" },
+	{ 0x90, "Redirecting name" },
+	{ 0x91, "Connected name" },
+	{ 0x92, "Originating restrictions" },
+	{ 0x93, "Date & time of day" },
+	{ 0x94, "Call Appearance ID" },
+	{ 0x95, "Feature address" },
+	{ 0x96, "Redirection name" },
+	{ 0x9e, "Text" },
+};
+#define DTAGSIZE sizeof(dtaglist)/sizeof(struct DTag)
+
+static int
+disptext_ni1(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l, tag, len, i;
+
+	p++;
+	l = *p++ - 1;
+	if (*p++ != 0x80) {
+		dp += sprintf(dp, "    Unknown display type\n");
+		return (dp - dest);
+	}
+	/* Iterate over all tag,length,text fields */
+	while (l > 0) {
+		tag = *p++;
+		len = *p++;
+		l -= len + 2;
+		/* Don't space or skip */
+		if ((tag == 0x80) || (tag == 0x81)) p++;
+		else {
+			for (i = 0; i < DTAGSIZE; i++)
+				if (tag == dtaglist[i].nr)
+					break;
+
+			/* When not found, give appropriate msg */
+			if (i != DTAGSIZE) {
+				dp += sprintf(dp, "    %s: ", dtaglist[i].descr);
+				while (len--)
+					*dp++ = *p++;
+			} else {
+				dp += sprintf(dp, "    (unknown display tag %2x): ", tag);
+				while (len--)
+					*dp++ = *p++;
+			}
+			dp += sprintf(dp, "\n");
+                }
+	}
+	return (dp - dest);
+}
+static int
+display(char *dest, u_char * p)
+{
+	char *dp = dest;
+	char ch = ' ';
+	int l, octet = 3;
+
+	p++;
+	l = *p++;
+	/* Iterate over all octets in the * display-information element */
+	dp += sprintf(dp, "   \"");
+	while (l--) {
+		dp += sprintf(dp, "%c", *p++);
+
+		/* last octet in group? */
+		if (*p & 0x80) {
+			octet++;
+			ch = ' ';
+		} else if (ch == ' ')
+			ch = 'a';
+
+		else
+			ch++;
+	}
+	*dp++ = '\"';
+	*dp++ = '\n';
+	return (dp - dest);
+}
+
+int
+prfacility(char *dest, u_char * p)
+{
+	char *dp = dest;
+	int l, l2;
+
+	p++;
+	l = *p++;
+	dp += sprintf(dp, "    octet 3 ");
+	dp += prbits(dp, *p++, 8, 8);
+	dp += sprintf(dp, "\n");
+	l -= 1;
+
+	while (l > 0) {
+		dp += sprintf(dp, "   octet 4 ");
+		dp += prbits(dp, *p++, 8, 8);
+		dp += sprintf(dp, "\n");
+		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
+		l -= 2;
+		dp += sprintf(dp, "   contents ");
+		while (l2--) {
+			dp += sprintf(dp, "%2x ", *p++);
+			l--;
+		}
+		dp += sprintf(dp, "\n");
+	}
+
+	return (dp - dest);
+}
+
+static
+struct InformationElement {
+	u_char nr;
+	char *descr;
+	int (*f) (char *, u_char *);
+} ielist[] = {
+
+	{
+		0x00, "Segmented message", general
+	},
+	{
+		0x04, "Bearer capability", prbearer
+	},
+	{
+		0x08, "Cause", prcause
+	},
+	{
+		0x10, "Call identity", general
+	},
+	{
+		0x14, "Call state", general
+	},
+	{
+		0x18, "Channel identification", prchident
+	},
+	{
+		0x1c, "Facility", prfacility
+	},
+	{
+		0x1e, "Progress indicator", general
+	},
+	{
+		0x20, "Network-specific facilities", general
+	},
+	{
+		0x27, "Notification indicator", general
+	},
+	{
+		0x28, "Display", display
+	},
+	{
+		0x29, "Date/Time", general
+	},
+	{
+		0x2c, "Keypad facility", general
+	},
+	{
+		0x34, "Signal", general
+	},
+	{
+		0x40, "Information rate", general
+	},
+	{
+		0x42, "End-to-end delay", general
+	},
+	{
+		0x43, "Transit delay selection and indication", general
+	},
+	{
+		0x44, "Packet layer binary parameters", general
+	},
+	{
+		0x45, "Packet layer window size", general
+	},
+	{
+		0x46, "Packet size", general
+	},
+	{
+		0x47, "Closed user group", general
+	},
+	{
+		0x4a, "Reverse charge indication", general
+	},
+	{
+		0x6c, "Calling party number", prcalling
+	},
+	{
+		0x6d, "Calling party subaddress", general
+	},
+	{
+		0x70, "Called party number", prcalled
+	},
+	{
+		0x71, "Called party subaddress", general
+	},
+	{
+		0x74, "Redirecting number", general
+	},
+	{
+		0x78, "Transit network selection", general
+	},
+	{
+		0x79, "Restart indicator", general
+	},
+	{
+		0x7c, "Low layer compatibility", general
+	},
+	{
+		0x7d, "High layer compatibility", general
+	},
+	{
+		0x7e, "User-user", general
+	},
+	{
+		0x7f, "Escape for extension", general
+	},
+};
+
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1[] = {
+	{ 0x04, "Bearer Capability", prbearer_ni1 },
+	{ 0x08, "Cause", prcause },
+	{ 0x14, "Call State", general_ni1 },
+	{ 0x18, "Channel Identification", prchident },
+	{ 0x1e, "Progress Indicator", general_ni1 },
+	{ 0x27, "Notification Indicator", general_ni1 },
+	{ 0x2c, "Keypad Facility", prtext },
+	{ 0x32, "Information Request", general_ni1 },
+	{ 0x34, "Signal", general_ni1 },
+	{ 0x38, "Feature Activation", general_ni1 },
+	{ 0x39, "Feature Indication", prfeatureind },
+	{ 0x3a, "Service Profile Identification (SPID)", prtext },
+	{ 0x3b, "Endpoint Identifier", general_ni1 },
+	{ 0x6c, "Calling Party Number", prcalling },
+	{ 0x6d, "Calling Party Subaddress", general_ni1 },
+	{ 0x70, "Called Party Number", prcalled },
+	{ 0x71, "Called Party Subaddress", general_ni1 },
+	{ 0x74, "Redirecting Number", general_ni1 },
+	{ 0x78, "Transit Network Selection", general_ni1 },
+	{ 0x7c, "Low Layer Compatibility", general_ni1 },
+	{ 0x7d, "High Layer Compatibility", general_ni1 },
+};
+
+
+#define IESIZE_NI1 sizeof(ielist_ni1)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs5[] = {
+	{ 0x1d, "Operator system access", general_ni1 },
+	{ 0x2a, "Display text", disptext_ni1 },
+};
+
+#define IESIZE_NI1_CS5 sizeof(ielist_ni1_cs5)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs6[] = {
+	{ 0x7b, "Call appearance", general_ni1 },
+};
+
+#define IESIZE_NI1_CS6 sizeof(ielist_ni1_cs6)/sizeof(struct InformationElement)
+
+static struct InformationElement we_0[] =
+{
+	{WE0_cause, "Cause", prcause_1tr6},
+	{WE0_connAddr, "Connecting Address", prcalled},
+	{WE0_callID, "Call IDentity", general},
+	{WE0_chanID, "Channel IDentity", general},
+	{WE0_netSpecFac, "Network Specific Facility", general},
+	{WE0_display, "Display", general},
+	{WE0_keypad, "Keypad", general},
+	{WE0_origAddr, "Origination Address", prcalled},
+	{WE0_destAddr, "Destination Address", prcalled},
+	{WE0_userInfo, "User Info", general}
+};
+
+#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement))
+
+static struct InformationElement we_6[] =
+{
+	{WE6_serviceInd, "Service Indicator", general},
+	{WE6_chargingInfo, "Charging Information", prcharge},
+	{WE6_date, "Date", prtext},
+	{WE6_facSelect, "Facility Select", general},
+	{WE6_facStatus, "Facility Status", general},
+	{WE6_statusCalled, "Status Called", general},
+	{WE6_addTransAttr, "Additional Transmission Attributes", general}
+};
+#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement))
+
+int
+QuickHex(char *txt, u_char * p, int cnt)
+{
+	register int i;
+	register char *t = txt;
+	register u_char w;
+
+	for (i = 0; i < cnt; i++) {
+		*t++ = ' ';
+		w = (p[i] >> 4) & 0x0f;
+		if (w < 10)
+			*t++ = '0' + w;
+		else
+			*t++ = 'A' - 10 + w;
+		w = p[i] & 0x0f;
+		if (w < 10)
+			*t++ = '0' + w;
+		else
+			*t++ = 'A' - 10 + w;
+	}
+	*t++ = 0;
+	return (t - txt);
+}
+
+void
+LogFrame(struct IsdnCardState *cs, u_char * buf, int size)
+{
+	char *dp;
+
+	if (size < 1)
+		return;
+	dp = cs->dlog;
+	if (size < MAX_DLOG_SPACE / 3 - 10) {
+		*dp++ = 'H';
+		*dp++ = 'E';
+		*dp++ = 'X';
+		*dp++ = ':';
+		dp += QuickHex(dp, buf, size);
+		dp--;
+		*dp++ = '\n';
+		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+	} else
+		HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
+}
+
+void
+dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
+{
+	u_char *bend, *buf;
+	char *dp;
+	unsigned char pd, cr_l, cr, mt;
+	unsigned char sapi, tei, ftyp;
+	int i, cset = 0, cs_old = 0, cs_fest = 0;
+	int size, finish = 0;
+
+	if (skb->len < 3)
+		return;
+	/* display header */
+	dp = cs->dlog;
+	dp += jiftime(dp, jiffies);
+	*dp++ = ' ';
+	sapi = skb->data[0] >> 2;
+	tei  = skb->data[1] >> 1;
+	ftyp = skb->data[2];
+	buf = skb->data;
+	dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
+	size = skb->len;
+	
+	if (tei == GROUP_TEI) {
+		if (sapi == CTRL_SAPI) { /* sapi 0 */
+			if (ftyp == 3) {
+				dp += sprintf(dp, "broadcast\n");
+				buf += 3;
+				size -= 3;
+			} else {
+				dp += sprintf(dp, "no UI broadcast\n");
+				finish = 1;
+			}
+		} else if (sapi == TEI_SAPI) {
+			dp += sprintf(dp, "tei management\n");
+			finish = 1;
+		} else {
+			dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
+			finish = 1;
+		}
+	} else {
+		if (sapi == CTRL_SAPI) {
+			if (!(ftyp & 1)) { /* IFrame */
+				dp += sprintf(dp, "with tei %d\n", tei);
+				buf += 4;
+				size -= 4;
+			} else {
+				dp += sprintf(dp, "SFrame with tei %d\n", tei);
+				finish = 1;
+			}
+		} else {
+			dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
+			finish = 1;
+		}
+	}
+	bend = skb->data + skb->len;
+	if (buf >= bend) {
+		dp += sprintf(dp, "frame too short\n");
+		finish = 1;
+	}
+	if (finish) {
+		*dp = 0;
+		HiSax_putstatus(cs, NULL, cs->dlog);
+		return;
+	}
+	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
+		/* locate message type */
+		pd = *buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		if (pd == PROTO_DIS_N0) {	/* N0 */
+			for (i = 0; i < MT_N0_LEN; i++)
+				if (mt_n0[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == MT_N0_LEN)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n0[i].descr);
+		} else {	/* N1 */
+			for (i = 0; i < MT_N1_LEN; i++)
+				if (mt_n1[i].nr == mt)
+					break;
+			/* display message type if it exists */
+			if (i == MT_N1_LEN)
+				dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt);
+			else
+				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+					      size, mt_n1[i].descr);
+		}
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						cs_old = cset;
+						cset = *buf & 7;
+						cs_fest = *buf & 8;
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			if (cset == 0) {
+				for (i = 0; i < WE_0_LEN; i++)
+					if (*buf == we_0[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != WE_0_LEN) {
+					dp += sprintf(dp, "  %s\n", we_0[i].descr);
+					dp += we_0[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			} else if (cset == 6) {
+				for (i = 0; i < WE_6_LEN; i++)
+					if (*buf == we_6[i].nr)
+						break;
+
+				/* When found, give appropriate msg */
+				if (i != WE_6_LEN) {
+					dp += sprintf(dp, "  %s\n", we_6[i].descr);
+					dp += we_6[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			} else
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+			/* Skip to next element */
+			if (cs_fest == 8) {
+				cset = cs_old;
+				cs_old = 0;
+				cs_fest = 0;
+			}
+			buf += buf[1] + 2;
+		}
+	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_NI1)) {	/* NI-1 */
+		/* locate message type */
+		buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		for (i = 0; i < MTSIZE; i++)
+			if (mtlist[i].nr == mt)
+				break;
+
+		/* display message type if it exists */
+		if (i == MTSIZE)
+			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mt);
+		else
+			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mtlist[i].descr);
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						cs_old = cset;
+						cset = *buf & 7;
+						cs_fest = *buf & 8;
+						break;
+					default:
+						dp += sprintf(dp, "  Unknown single-octet IE %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			if (cset == 0) {
+				for (i = 0; i < IESIZE; i++)
+					if (*buf == ielist_ni1[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1[i].descr);
+					dp += ielist_ni1[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else if (cset == 5) {
+				for (i = 0; i < IESIZE_NI1_CS5; i++)
+					if (*buf == ielist_ni1_cs5[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE_NI1_CS5) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1_cs5[i].descr);
+					dp += ielist_ni1_cs5[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else if (cset == 6) {
+				for (i = 0; i < IESIZE_NI1_CS6; i++)
+					if (*buf == ielist_ni1_cs6[i].nr)
+						break;
+
+				/* When not found, give appropriate msg */
+				if (i != IESIZE_NI1_CS6) {
+					dp += sprintf(dp, "  %s\n", ielist_ni1_cs6[i].descr);
+					dp += ielist_ni1_cs6[i].f(dp, buf);
+				} else
+					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+			} else
+				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+
+			/* Skip to next element */
+			if (cs_fest == 8) {
+				cset = cs_old;
+				cs_old = 0;
+				cs_fest = 0;
+			}
+			buf += buf[1] + 2;
+		}
+	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_EURO)) { /* EURO */
+		/* locate message type */
+		buf++;
+		cr_l = *buf++;
+		if (cr_l)
+			cr = *buf++;
+		else
+			cr = 0;
+		mt = *buf++;
+		for (i = 0; i < MTSIZE; i++)
+			if (mtlist[i].nr == mt)
+				break;
+
+		/* display message type if it exists */
+		if (i == MTSIZE)
+			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mt);
+		else
+			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+			    cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+				      size, mtlist[i].descr);
+
+		/* display each information element */
+		while (buf < bend) {
+			/* Is it a single octet information element? */
+			if (*buf & 0x80) {
+				switch ((*buf >> 4) & 7) {
+					case 1:
+						dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
+						break;
+					case 3:
+						dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
+						break;
+					case 5:
+						dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
+						break;
+					case 2:
+						if (*buf == 0xa0) {
+							dp += sprintf(dp, "  More data\n");
+							break;
+						}
+						if (*buf == 0xa1) {
+							dp += sprintf(dp, "  Sending complete\n");
+						}
+						break;
+						/* fall through */
+					default:
+						dp += sprintf(dp, "  Reserved %x\n", *buf);
+						break;
+				}
+				buf++;
+				continue;
+			}
+			/* No, locate it in the table */
+			for (i = 0; i < IESIZE; i++)
+				if (*buf == ielist[i].nr)
+					break;
+
+			/* When not found, give appropriate msg */
+			if (i != IESIZE) {
+				dp += sprintf(dp, "  %s\n", ielist[i].descr);
+				dp += ielist[i].f(dp, buf);
+			} else
+				dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
+
+			/* Skip to next element */
+			buf += buf[1] + 2;
+		}
+	} else {
+		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
+	}
+	*dp = 0;
+	HiSax_putstatus(cs, NULL, cs->dlog);
+}
diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c
new file mode 100644
index 0000000..f3c4813
--- /dev/null
+++ b/drivers/isdn/hisax/s0box.c
@@ -0,0 +1,266 @@
+/* $Id: s0box.c,v 2.6.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Creatix S0BOX
+ *
+ * Author       Enrik Berkhan
+ * Copyright    by Enrik Berkhan <enrik@starfleet.inka.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *s0box_revision = "$Revision: 2.6.2.4 $";
+
+static inline void
+writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)&0x7f,padr);
+	outb_p(0x16,padr+2);
+	outb_p(val,padr);
+	outb_p(0x17,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+}
+
+static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf,
+			 0, 0, 0, 0, 0, 0, 0, 0,
+			 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ;
+
+static inline u_char
+readreg(unsigned int padr, signed int addr, u_char off) {
+	register u_char n1, n2;
+
+	outb_p(0x1c,padr+2);
+	outb_p(0x14,padr+2);
+	outb_p((addr+off)|0x80,padr);
+	outb_p(0x16,padr+2);
+	outb_p(0x17,padr+2);
+	n1 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x16,padr+2);
+	n2 = (inb_p(padr+1) >> 3) & 0x17;
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return nibtab[n1] | (nibtab[n2] << 4);
+}
+
+static inline void
+read_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	register u_char n1, n2;
+	
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr|0x80, padr);
+	outb_p(0x16, padr+2);
+	for (i=0; i<size; i++) {
+		outb_p(0x17, padr+2);
+		n1 = (inb_p(padr+1) >> 3) & 0x17;
+		outb_p(0x16,padr+2);
+		n2 = (inb_p(padr+1) >> 3) & 0x17;
+		*(data++)=nibtab[n1] | (nibtab[n2] << 4);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+static inline void
+write_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+	int i;
+	outb_p(0x1c, padr+2);
+	outb_p(0x14, padr+2);
+	outb_p(adr&0x7f, padr);
+	for (i=0; i<size; i++) {
+		outb_p(0x16, padr+2);
+		outb_p(*(data++), padr);
+		outb_p(0x17, padr+2);
+	}
+	outb_p(0x14,padr+2);
+	outb_p(0x1c,padr+2);
+	return;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 5
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+	int count = 0;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	count++;
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (count >= MAXCOUNT)
+		printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+	writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_s0box(struct IsdnCardState *cs)
+{
+	release_region(cs->hw.teles3.cfg_reg, 8);
+}
+
+static int
+S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			break;
+		case CARD_RELEASE:
+			release_io_s0box(cs);
+			break;
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case CARD_TEST:
+			break;
+	}
+	return(0);
+}
+
+int __init
+setup_s0box(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, s0box_revision);
+	printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_S0BOX)
+		return (0);
+
+	cs->hw.teles3.cfg_reg = card->para[1];
+	cs->hw.teles3.hscx[0] = -0x20;
+	cs->hw.teles3.hscx[1] = 0x0;
+	cs->hw.teles3.isac = 0x20;
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.teles3.cfg_reg,8, "S0Box parallel I/O")) {
+		printk(KERN_WARNING
+		       "HiSax: %s ports %x-%x already in use\n",
+		       CardType[cs->typ],
+                       cs->hw.teles3.cfg_reg,
+                       cs->hw.teles3.cfg_reg + 7);
+		return 0;
+	}
+	printk(KERN_INFO
+		"HiSax: %s config irq:%d isac:0x%x  cfg:0x%x\n",
+		CardType[cs->typ], cs->irq,
+		cs->hw.teles3.isac, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+		"HiSax: hscx A:0x%x  hscx B:0x%x\n",
+		cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &S0Box_card_msg;
+	cs->irq_func = &s0box_interrupt;
+	ISACVersion(cs, "S0Box:");
+	if (HscxVersion(cs, "S0Box:")) {
+		printk(KERN_WARNING
+		       "S0Box: wrong HSCX versions check IO address\n");
+		release_io_s0box(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c
new file mode 100644
index 0000000..9e6d3d6
--- /dev/null
+++ b/drivers/isdn/hisax/saphir.c
@@ -0,0 +1,300 @@
+/* $Id: saphir.c,v 1.10.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for HST Saphir 1
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    HST High Soft Tech GmbH
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+static char *saphir_rev = "$Revision: 1.10.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_DATA	0
+#define HSCX_DATA	1
+#define ADDRESS_REG	2
+#define IRQ_REG		3
+#define SPARE_REG	4
+#define RESET_REG	5
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx,
+		offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx,
+		offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.saphir.ale, \
+		cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+saphir_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	/* Watchdog */
+	if (cs->hw.saphir.timer.function) 
+		mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
+	else
+		printk(KERN_WARNING "saphir: Spurious timer!\n");
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0);
+	writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+SaphirWatchDog(struct IsdnCardState *cs)
+{
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+        /* 5 sec WatchDog, so read at least every 4 sec */
+	cs->readisac(cs, ISAC_RBCH);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
+}
+
+void
+release_io_saphir(struct IsdnCardState *cs)
+{
+	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, 0xff);
+	del_timer(&cs->hw.saphir.timer);
+	cs->hw.saphir.timer.function = NULL;
+	if (cs->hw.saphir.cfg_reg)
+		release_region(cs->hw.saphir.cfg_reg, 6);
+}
+
+static int
+saphir_reset(struct IsdnCardState *cs)
+{
+	u_char irq_val;
+
+	switch(cs->irq) {
+		case 5: irq_val = 0;
+			break;
+		case 3: irq_val = 1;
+			break;
+		case 11:
+			irq_val = 2;
+			break;
+		case 12:
+			irq_val = 3;
+			break;
+		case 15:
+			irq_val = 4;
+			break;
+		default:
+			printk(KERN_WARNING "HiSax: saphir wrong IRQ %d\n",
+				cs->irq);
+			return (1);
+	}
+	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
+	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1);
+	mdelay(10);
+	byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0);
+	mdelay(10);
+	byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
+	byteout(cs->hw.saphir.cfg_reg + SPARE_REG, 0x02);
+	return (0);
+}
+
+static int
+saphir_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			saphir_reset(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_saphir(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+
+int __init
+setup_saphir(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, saphir_rev);
+	printk(KERN_INFO "HiSax: HST Saphir driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_HSTSAPHIR)
+		return (0);
+
+	/* IO-Ports */
+	cs->hw.saphir.cfg_reg = card->para[1];
+	cs->hw.saphir.isac = card->para[1] + ISAC_DATA;
+	cs->hw.saphir.hscx = card->para[1] + HSCX_DATA;
+	cs->hw.saphir.ale = card->para[1] + ADDRESS_REG;
+	cs->irq = card->para[0];
+	if (!request_region(cs->hw.saphir.cfg_reg, 6, "saphir")) {
+		printk(KERN_WARNING
+			"HiSax: %s config port %x-%x already in use\n",
+			CardType[card->typ],
+			cs->hw.saphir.cfg_reg,
+			cs->hw.saphir.cfg_reg + 5);
+		return (0);
+	}
+
+	printk(KERN_INFO "HiSax: %s config irq:%d io:0x%X\n",
+		CardType[cs->typ], cs->irq, cs->hw.saphir.cfg_reg);
+
+	setup_isac(cs);
+	cs->hw.saphir.timer.function = (void *) SaphirWatchDog;
+	cs->hw.saphir.timer.data = (long) cs;
+	init_timer(&cs->hw.saphir.timer);
+	cs->hw.saphir.timer.expires = jiffies + 4*HZ;
+	add_timer(&cs->hw.saphir.timer);
+	if (saphir_reset(cs)) {
+		release_io_saphir(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &saphir_card_msg;
+	cs->irq_func = &saphir_interrupt;
+	ISACVersion(cs, "saphir:");
+	if (HscxVersion(cs, "saphir:")) {
+		printk(KERN_WARNING
+		    "saphir: wrong HSCX versions check IO address\n");
+		release_io_saphir(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c
new file mode 100644
index 0000000..8390f16
--- /dev/null
+++ b/drivers/isdn/hisax/sedlbauer.c
@@ -0,0 +1,833 @@
+/* $Id: sedlbauer.c,v 1.34.2.6 2004/01/24 20:47:24 keil Exp $
+ *
+ * low level stuff for Sedlbauer cards
+ * includes support for the Sedlbauer speed star (speed star II),
+ * support for the Sedlbauer speed fax+,
+ * support for the Sedlbauer ISDN-Controller PC/104 and
+ * support for the Sedlbauer speed pci
+ * derived from the original file asuscom.c from Karsten Keil
+ *
+ * Author       Marcus Niemann
+ * Copyright    by Marcus Niemann    <niemann@www-bib.fh-bielefeld.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to  Karsten Keil
+ *            Sedlbauer AG for informations
+ *            Edgar Toernig
+ *
+ */
+
+/* Supported cards:
+ * Card:	Chip:		Configuration:	Comment:
+ * ---------------------------------------------------------------------
+ * Speed Card	ISAC_HSCX	DIP-SWITCH
+ * Speed Win	ISAC_HSCX	ISAPNP
+ * Speed Fax+	ISAC_ISAR	ISAPNP		Full analog support
+ * Speed Star	ISAC_HSCX	CARDMGR
+ * Speed Win2	IPAC		ISAPNP
+ * ISDN PC/104	IPAC		DIP-SWITCH
+ * Speed Star2	IPAC		CARDMGR
+ * Speed PCI	IPAC		PCI PNP
+ * Speed Fax+ 	ISAC_ISAR	PCI PNP		Full analog support
+ *
+ * Important:
+ * For the sedlbauer speed fax+ to work properly you have to download
+ * the firmware onto the card.
+ * For example: hisaxctrl <DriverID> 9 ISAR.BIN
+*/
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+const char *Sedlbauer_revision = "$Revision: 1.34.2.6 $";
+
+const char *Sedlbauer_Types[] =
+	{"None", "speed card/win", "speed star", "speed fax+",
+	"speed win II / ISDN PC/104", "speed star II", "speed pci",
+	"speed fax+ pyramid", "speed fax+ pci", "HST Saphir III"};
+
+#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID	0x51
+#define PCI_SUBVENDOR_HST_SAPHIR3	0x52
+#define PCI_SUBVENDOR_SEDLBAUER_PCI	0x53
+#define PCI_SUBVENDOR_SPEEDFAX_PCI	0x54
+#define PCI_SUB_ID_SEDLBAUER		0x01
+
+#define SEDL_SPEED_CARD_WIN	1
+#define SEDL_SPEED_STAR 	2
+#define SEDL_SPEED_FAX		3
+#define SEDL_SPEED_WIN2_PC104 	4
+#define SEDL_SPEED_STAR2 	5
+#define SEDL_SPEED_PCI   	6
+#define SEDL_SPEEDFAX_PYRAMID	7
+#define SEDL_SPEEDFAX_PCI	8
+#define HST_SAPHIR3		9
+
+#define SEDL_CHIP_TEST		0
+#define SEDL_CHIP_ISAC_HSCX	1
+#define SEDL_CHIP_ISAC_ISAR	2
+#define SEDL_CHIP_IPAC		3
+
+#define SEDL_BUS_ISA		1
+#define SEDL_BUS_PCI		2
+#define	SEDL_BUS_PCMCIA		3
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SEDL_HSCX_ISA_RESET_ON	0
+#define SEDL_HSCX_ISA_RESET_OFF	1
+#define SEDL_HSCX_ISA_ISAC	2
+#define SEDL_HSCX_ISA_HSCX	3
+#define SEDL_HSCX_ISA_ADR	4
+
+#define SEDL_HSCX_PCMCIA_RESET	0
+#define SEDL_HSCX_PCMCIA_ISAC	1
+#define SEDL_HSCX_PCMCIA_HSCX	2
+#define SEDL_HSCX_PCMCIA_ADR	4
+
+#define SEDL_ISAR_ISA_ISAC		4
+#define SEDL_ISAR_ISA_ISAR		6
+#define SEDL_ISAR_ISA_ADR		8
+#define SEDL_ISAR_ISA_ISAR_RESET_ON	10
+#define SEDL_ISAR_ISA_ISAR_RESET_OFF	12
+
+#define SEDL_IPAC_ANY_ADR		0
+#define SEDL_IPAC_ANY_IPAC		2
+
+#define SEDL_IPAC_PCI_BASE		0
+#define SEDL_IPAC_PCI_ADR		0xc0
+#define SEDL_IPAC_PCI_IPAC		0xc8
+#define SEDL_ISAR_PCI_ADR		0xc8
+#define SEDL_ISAR_PCI_ISAC		0xd0
+#define SEDL_ISAR_PCI_ISAR		0xe0
+#define SEDL_ISAR_PCI_ISAR_RESET_ON	0x01
+#define SEDL_ISAR_PCI_ISAR_RESET_OFF	0x18
+#define SEDL_ISAR_PCI_LED1		0x08
+#define SEDL_ISAR_PCI_LED2		0x10
+
+#define SEDL_RESET      0x3	/* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+
+	byteout(ale, off);
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	insb(adr, data, size);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	byteout(ale, off);
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	byteout(ale, off);
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+	writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.sedl.adr,
+			cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.sedl.adr,
+		 cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* ISAR access routines
+ * mode = 0 access with IRQ on
+ * mode = 1 access with IRQ off
+ * mode = 2 access with IRQ off and using last offset
+ */
+
+static u_char
+ReadISAR(struct IsdnCardState *cs, int mode, u_char offset)
+{	
+	if (mode == 0)
+		return (readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset));
+	else if (mode == 1)
+		byteout(cs->hw.sedl.adr, offset);
+	return(bytein(cs->hw.sedl.hscx));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+	if (mode == 0)
+		writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset, value);
+	else {
+		if (mode == 1)
+			byteout(cs->hw.sedl.adr, offset);
+		byteout(cs->hw.sedl.hscx, value);
+	}
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \
+		cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+sedlbauer_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) {
+		/* The card tends to generate interrupts while being removed
+		   causing us to just crash the kernel. bad. */
+		spin_unlock_irqrestore(&cs->lock, flags);
+		printk(KERN_WARNING "Sedlbauer: card not available!\n");
+		return IRQ_NONE;
+	}
+
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char ista, val, icnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+Start_IPAC:
+	if (cs->debug & L1_DEB_IPAC)
+		debugl1(cs, "IPAC ISTA %02X", ista);
+	if (ista & 0x0f) {
+		val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+		if (ista & 0x01)
+			val |= 0x01;
+		if (ista & 0x04)
+			val |= 0x02;
+		if (ista & 0x08)
+			val |= 0x04;
+		if (val)
+			hscx_int_main(cs, val);
+	}
+	if (ista & 0x20) {
+		val = 0xfe & readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA | 0x80);
+		if (val) {
+			isac_interrupt(cs, val);
+		}
+	}
+	if (ista & 0x10) {
+		val = 0x01;
+		isac_interrupt(cs, val);
+	}
+	ista  = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+	if ((ista & 0x3f) && icnt) {
+		icnt--;
+		goto Start_IPAC;
+	}
+	if (!icnt)
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Sedlbauer IRQ LOOP");
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	int cnt = 5;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+      Start_ISAR:
+	if (val & ISAR_IRQSTA)
+		isar_int_main(cs);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+	if ((val & ISAR_IRQSTA) && --cnt) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "ISAR IntStat after IntRoutine");
+		goto Start_ISAR;
+	}
+	val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+	if (val && --cnt) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (!cnt)
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "Sedlbauer IRQ LOOP");
+
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, ISAR_IRQMSK);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_sedlbauer(struct IsdnCardState *cs)
+{
+	int bytecnt = 8;
+
+	if (cs->subtyp == SEDL_SPEED_FAX) {
+		bytecnt = 16;
+	} else if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+		bytecnt = 256;
+	}
+	if (cs->hw.sedl.cfg_reg)
+		release_region(cs->hw.sedl.cfg_reg, bytecnt);
+}
+
+static void
+reset_sedlbauer(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "Sedlbauer: resetting card\n");
+
+	if (!((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) &&
+	   (cs->hw.sedl.chip == SEDL_CHIP_ISAC_HSCX))) {
+		if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
+			mdelay(2);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
+			mdelay(10);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0);
+			writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12);
+		} else if ((cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) &&
+			(cs->hw.sedl.bus == SEDL_BUS_PCI)) {
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+			mdelay(2);
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			mdelay(10);
+		} else {		
+			byteout(cs->hw.sedl.reset_on, SEDL_RESET);	/* Reset On */
+			mdelay(2);
+			byteout(cs->hw.sedl.reset_off, 0);	/* Reset Off */
+			mdelay(10);
+		}
+	}
+}
+
+static int
+Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sedlbauer(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				spin_lock_irqsave(&cs->lock, flags);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.isac,
+					ISAC_MASK, 0xFF);
+				reset_sedlbauer(cs);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.isac,
+					ISAC_MASK, 0xFF);
+				spin_unlock_irqrestore(&cs->lock, flags);
+			}
+			release_io_sedlbauer(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sedlbauer(cs);
+			if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+				clear_pending_isac_ints(cs);
+				writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+					ISAR_IRQBIT, 0);
+				initisac(cs);
+				initisar(cs);
+				/* Reenable all IRQ */
+				cs->writeisac(cs, ISAC_MASK, 0);
+				/* RESET Receiver and Transmitter */
+				cs->writeisac(cs, ISAC_CMDR, 0x41);
+			} else {
+				inithscxisac(cs, 3);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+		case MDL_INFO_CONN:
+			if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
+				return(0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((long) arg)
+				cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED2;
+			else
+				cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED1;
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case MDL_INFO_REL:
+			if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
+				return(0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((long) arg)
+				cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED2;
+			else
+				cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED1;
+			byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_sedl __devinitdata = NULL;
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id sedl_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x01),
+	  ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x01), 
+	  (unsigned long) "Speed win" },
+	{ ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02),
+	  ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02), 
+	  (unsigned long) "Speed Fax+" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &sedl_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_sedlbauer(struct IsdnCard *card)
+{
+	int bytecnt, ver, val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u16 sub_vendor_id, sub_id;
+
+	strcpy(tmp, Sedlbauer_revision);
+	printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
+	
+ 	if (cs->typ == ISDN_CTYPE_SEDLBAUER) {
+ 		cs->subtyp = SEDL_SPEED_CARD_WIN;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) {	
+ 		cs->subtyp = SEDL_SPEED_STAR;
+		cs->hw.sedl.bus = SEDL_BUS_PCMCIA;
+		cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ 	} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_FAX) {	
+ 		cs->subtyp = SEDL_SPEED_FAX;
+		cs->hw.sedl.bus = SEDL_BUS_ISA;
+		cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+ 	} else
+		return (0);
+
+	bytecnt = 8;
+	if (card->para[1]) {
+		cs->hw.sedl.cfg_reg = card->para[1];
+		cs->irq = card->para[0];
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			bytecnt = 16;
+		}
+	} else {
+#ifdef __ISAPNP__
+		if (isapnp_present()) {
+			struct pnp_dev *pnp_d;
+			while(ipid->card_vendor) {
+				if ((pnp_c = pnp_find_card(ipid->card_vendor,
+					ipid->card_device, pnp_c))) {
+					pnp_d = NULL;
+					if ((pnp_d = pnp_find_dev(pnp_c,
+						ipid->vendor, ipid->function, pnp_d))) {
+						int err;
+
+						printk(KERN_INFO "HiSax: %s detected\n",
+							(char *)ipid->driver_data);
+						pnp_disable_dev(pnp_d);
+						err = pnp_activate_dev(pnp_d);
+						if (err<0) {
+							printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+								__FUNCTION__, err);
+							return(0);
+						}
+						card->para[1] = pnp_port_start(pnp_d, 0);
+						card->para[0] = pnp_irq(pnp_d, 0);
+
+						if (!card->para[0] || !card->para[1]) {
+							printk(KERN_ERR "Sedlbauer PnP:some resources are missing %ld/%lx\n",
+								card->para[0], card->para[1]);
+							pnp_disable_dev(pnp_d);
+							return(0);
+						}
+						cs->hw.sedl.cfg_reg = card->para[1];
+						cs->irq = card->para[0];
+						if (ipid->function == ISAPNP_FUNCTION(0x2)) {
+							cs->subtyp = SEDL_SPEED_FAX;
+							cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+							bytecnt = 16;
+						} else {
+							cs->subtyp = SEDL_SPEED_CARD_WIN;
+							cs->hw.sedl.chip = SEDL_CHIP_TEST;
+						}
+						goto ready;
+					} else {
+						printk(KERN_ERR "Sedlbauer PnP: PnP error card found, no device\n");
+						return(0);
+					}
+				}
+				ipid++;
+				pnp_c = NULL;
+			} 
+			if (!ipid->card_vendor) {
+				printk(KERN_INFO "Sedlbauer PnP: no ISAPnP card found\n");
+			}
+		}
+#endif
+/* Probe for Sedlbauer speed pci */
+#ifdef CONFIG_PCI
+		if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+				PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) {
+			if (pci_enable_device(dev_sedl))
+				return(0);
+			cs->irq = dev_sedl->irq;
+			if (!cs->irq) {
+				printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n");
+				return(0);
+			}
+			cs->hw.sedl.cfg_reg = pci_resource_start(dev_sedl, 0);
+		} else {
+			printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
+			return(0);
+		}
+		cs->irq_flags |= SA_SHIRQ;
+		cs->hw.sedl.bus = SEDL_BUS_PCI;
+		sub_vendor_id = dev_sedl->subsystem_vendor;
+		sub_id = dev_sedl->subsystem_device;
+		printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n",
+			sub_vendor_id, sub_id);
+		printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n",
+			cs->hw.sedl.cfg_reg);
+		if (sub_id != PCI_SUB_ID_SEDLBAUER) {
+			printk(KERN_ERR "Sedlbauer: unknown sub id %#x\n", sub_id);
+			return(0);
+		}
+		if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) {
+			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+			cs->subtyp = SEDL_SPEEDFAX_PYRAMID;
+		} else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) {
+			cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+			cs->subtyp = SEDL_SPEEDFAX_PCI;
+		} else if (sub_vendor_id == PCI_SUBVENDOR_HST_SAPHIR3) {
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+			cs->subtyp = HST_SAPHIR3;
+		} else if (sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER_PCI) {
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+			cs->subtyp = SEDL_SPEED_PCI;
+		} else {
+			printk(KERN_ERR "Sedlbauer: unknown sub vendor id %#x\n",
+				sub_vendor_id);
+			return(0);
+		}
+		bytecnt = 256;
+		cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON;
+		cs->hw.sedl.reset_off = SEDL_ISAR_PCI_ISAR_RESET_OFF;
+		byteout(cs->hw.sedl.cfg_reg, 0xff);
+		byteout(cs->hw.sedl.cfg_reg, 0x00);
+		byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd);
+		byteout(cs->hw.sedl.cfg_reg+ 5, 0x02);
+		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+		mdelay(2);
+		byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+		mdelay(10);
+#else
+		printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
+		return (0);
+#endif /* CONFIG_PCI */
+	}	
+ready:	
+	/* In case of the sedlbauer pcmcia card, this region is in use,
+	 * reserved for us by the card manager. So we do not check it
+	 * here, it would fail.
+	 */
+	if (cs->hw.sedl.bus != SEDL_BUS_PCMCIA &&
+		!request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn")) {
+		printk(KERN_WARNING
+			"HiSax: %s config port %x-%x already in use\n",
+			CardType[card->typ],
+			cs->hw.sedl.cfg_reg,
+			cs->hw.sedl.cfg_reg + bytecnt);
+			return (0);
+	}
+
+	printk(KERN_INFO
+	       "Sedlbauer: defined at 0x%x-0x%x IRQ %d\n",
+	       cs->hw.sedl.cfg_reg,
+	       cs->hw.sedl.cfg_reg + bytecnt,
+	       cs->irq);
+
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sedl_card_msg;
+
+/*
+ * testing ISA and PCMCIA Cards for IPAC, default is ISAC
+ * do not test for PCI card, because ports are different
+ * and PCI card uses only IPAC (for the moment)
+ */	
+	if (cs->hw.sedl.bus != SEDL_BUS_PCI) {
+		val = readreg(cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR,
+			cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC, IPAC_ID);
+		printk(KERN_DEBUG "Sedlbauer: testing IPAC version %x\n", val);
+	        if ((val == 1) || (val == 2)) {
+			/* IPAC */
+			cs->subtyp = SEDL_SPEED_WIN2_PC104;
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->subtyp = SEDL_SPEED_STAR2;
+			}
+			cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+		} else {
+			/* ISAC_HSCX oder ISAC_ISAR */
+			if (cs->hw.sedl.chip == SEDL_CHIP_TEST) {
+				cs->hw.sedl.chip = SEDL_CHIP_ISAC_HSCX;
+			}
+		}
+	}
+
+/*
+ * hw.sedl.chip is now properly set
+ */
+	printk(KERN_INFO "Sedlbauer: %s detected\n",
+		Sedlbauer_Types[cs->subtyp]);
+
+	setup_isac(cs);
+	if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+		if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR;
+			cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+			cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+		} else {
+	                cs->hw.sedl.adr  = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR;
+			cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+			cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+		}
+		test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+		cs->readisac = &ReadISAC_IPAC;
+		cs->writeisac = &WriteISAC_IPAC;
+		cs->readisacfifo = &ReadISACfifo_IPAC;
+		cs->writeisacfifo = &WriteISACfifo_IPAC;
+		cs->irq_func = &sedlbauer_interrupt_ipac;
+		val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ID);
+		printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val);
+	} else {
+		/* ISAC_HSCX oder ISAC_ISAR */
+		cs->readisac = &ReadISAC;
+		cs->writeisac = &WriteISAC;
+		cs->readisacfifo = &ReadISACfifo;
+		cs->writeisacfifo = &WriteISACfifo;
+		if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+			if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_PCI_ISAR;
+			} else {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAR;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAR_RESET_ON;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg +
+							SEDL_ISAR_ISA_ISAR_RESET_OFF;
+			}
+			cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar;
+			cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar;
+			test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+			cs->irq_func = &sedlbauer_interrupt_isar;
+			cs->auxcmd = &isar_auxcmd;
+			ISACVersion(cs, "Sedlbauer:");
+			cs->BC_Read_Reg = &ReadISAR;
+			cs->BC_Write_Reg = &WriteISAR;
+			cs->BC_Send_Data = &isar_fill_fifo;
+			bytecnt = 3;
+			while (bytecnt) {
+				ver = ISARVersion(cs, "Sedlbauer:");
+				if (ver < 0)
+					printk(KERN_WARNING
+						"Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
+				else
+					break;
+				reset_sedlbauer(cs);
+				bytecnt--;
+			}
+			if (!bytecnt) {
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+		} else {
+			if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+				cs->irq_flags |= SA_SHIRQ;
+			} else {
+				cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ADR;
+				cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ISAC;
+				cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_HSCX;
+				cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON;
+				cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF;
+			}
+			cs->irq_func = &sedlbauer_interrupt;
+			ISACVersion(cs, "Sedlbauer:");
+		
+			if (HscxVersion(cs, "Sedlbauer:")) {
+				printk(KERN_WARNING
+					"Sedlbauer: wrong HSCX versions check IO address\n");
+				release_io_sedlbauer(cs);
+				return (0);
+			}
+		}
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
new file mode 100644
index 0000000..4496512
--- /dev/null
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -0,0 +1,640 @@
+/*======================================================================
+
+    A Sedlbauer PCMCIA client driver
+
+    This driver is for the Sedlbauer Speed Star and Speed Star II, 
+    which are ISDN PCMCIA Cards.
+    
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Modifications from dummy_cs.c are Copyright (C) 1999-2001 Marcus Niemann
+    <maniemann@users.sourceforge.net>. All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in
+    which case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Sedlbauer cards");
+MODULE_AUTHOR("Marcus Niemann");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); 
+static char *version =
+"sedlbauer_cs.c 1.1a 2001/01/28 15:04:04 (M.Niemann)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2;        /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card
+   insertion and ejection events.  They are invoked from the sedlbauer
+   event handler. 
+*/
+
+static void sedlbauer_config(dev_link_t *link);
+static void sedlbauer_release(dev_link_t *link);
+static int sedlbauer_event(event_t event, int priority,
+		       event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *sedlbauer_attach(void);
+static void sedlbauer_detach(dev_link_t *);
+
+/*
+   You'll also need to prototype all the functions that will actually
+   be used to talk to your device.  See 'memory_cs' for a good example
+   of a fully self-sufficient driver; the other drivers rely more or
+   less on other parts of the kernel.
+*/
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "sedlbauer_cs";
+
+/*
+   A linked list of "instances" of the sedlbauer device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   To simplify the data structure handling, we actually include the
+   dev_link_t structure in the device's private data structure.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally shouldn't be allocated dynamically.
+
+   In this case, we also provide a flag to indicate if a device is
+   "stopped" due to a power management event, or card ejection.  The
+   device IO routines can use a flag like this to throttle IO to a
+   card that is not ready to accept it.
+*/
+   
+typedef struct local_info_t {
+    dev_link_t		link;
+    dev_node_t		node;
+    int			stop;
+    int			cardnr;
+} local_info_t;
+
+/*======================================================================
+
+    sedlbauer_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+    
+======================================================================*/
+
+static dev_link_t *sedlbauer_attach(void)
+{
+    local_info_t *local;
+    dev_link_t *link;
+    client_reg_t client_reg;
+    int ret;
+    
+    DEBUG(0, "sedlbauer_attach()\n");
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) return NULL;
+    memset(local, 0, sizeof(local_info_t));
+    local->cardnr = -1;
+    link = &local->link; link->priv = local;
+    
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->irq.Handler = NULL;
+
+    /*
+      General socket configuration defaults can go here.  In this
+      client, we assume very little, and rely on the CIS for almost
+      everything.  In most clients, many details (i.e., number, sizes,
+      and attributes of IO windows) are fixed by the nature of the
+      device, and can be hard-wired here.
+    */
+
+    /* from old sedl_cs 
+    */
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 8;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.IOAddrLines = 3;
+
+
+    link->conf.Attributes = 0;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &sedlbauer_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+	cs_error(link->handle, RegisterClient, ret);
+	sedlbauer_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* sedlbauer_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void sedlbauer_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "sedlbauer_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    /*
+       If the device is currently configured and active, we won't
+       actually delete it yet.  Instead, it is marked so that when
+       the release() function is called, that will trigger a proper
+       detach().
+    */
+    if (link->state & DEV_CONFIG) {
+#ifdef PCMCIA_DEBUG
+	printk(KERN_DEBUG "sedlbauer_cs: detach postponed, '%s' "
+	       "still locked\n", link->dev->dev_name);
+#endif
+	link->state |= DEV_STALE_LINK;
+	return;
+    }
+
+    /* Break the link with Card Services */
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, and free it */
+    *linkp = link->next;
+    /* This points to the parent local_info_t struct */
+    kfree(link->priv);
+} /* sedlbauer_detach */
+
+/*======================================================================
+
+    sedlbauer_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    device available to the system.
+    
+======================================================================*/
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void sedlbauer_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    local_info_t *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    int last_fn, last_ret;
+    u8 buf[64];
+    config_info_t conf;
+    win_req_t req;
+    memreq_t map;
+    IsdnCard_t  icard;
+
+    DEBUG(0, "sedlbauer_config(0x%p)\n", link);
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /* Look up the current Vcc */
+    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+    link->conf.Vcc = conf.Vcc;
+
+    /*
+      In this loop, we scan the CIS for configuration table entries,
+      each of which describes a valid card configuration, including
+      voltage, IO window, memory window, and interrupt settings.
+
+      We make no assumptions about the card to be configured: we use
+      just the information available in the CIS.  In an ideal world,
+      this would work for any PCMCIA card, but it requires a complete
+      and accurate CIS.  In practice, a driver usually "knows" most of
+      these things without consulting the CIS, and most client drivers
+      will only use the CIS to fill in implementation-defined details.
+    */
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    while (1) {
+	cistpl_cftable_entry_t dflt = { 0 };
+	cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+	if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+		pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+	    goto next_entry;
+
+	if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
+	if (cfg->index == 0) goto next_entry;
+	link->conf.ConfigIndex = cfg->index;
+	
+	/* Does this card need audio output? */
+	if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+	    link->conf.Attributes |= CONF_ENABLE_SPKR;
+	    link->conf.Status = CCSR_AUDIO_ENA;
+	}
+	
+	/* Use power settings for Vcc and Vpp if present */
+	/*  Note that the CIS values need to be rescaled */
+	if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+	    if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
+		goto next_entry;
+	} else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+	    if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000)
+		goto next_entry;
+	}
+	    
+	if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+	    link->conf.Vpp1 = link->conf.Vpp2 =
+		cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+	else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
+	    link->conf.Vpp1 = link->conf.Vpp2 =
+		dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
+	
+	/* Do we need to allocate an interrupt? */
+	if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+	    link->conf.Attributes |= CONF_ENABLE_IRQ;
+	
+	/* IO window settings */
+	link->io.NumPorts1 = link->io.NumPorts2 = 0;
+	if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+	    cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+	    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	    if (!(io->flags & CISTPL_IO_8BIT))
+		link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+	    if (!(io->flags & CISTPL_IO_16BIT))
+		link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+/* new in dummy.cs 2001/01/28 MN 
+            link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+*/
+	    link->io.BasePort1 = io->win[0].base;
+	    link->io.NumPorts1 = io->win[0].len;
+	    if (io->nwin > 1) {
+		link->io.Attributes2 = link->io.Attributes1;
+		link->io.BasePort2 = io->win[1].base;
+		link->io.NumPorts2 = io->win[1].len;
+	    }
+	    /* This reserves IO space but doesn't actually enable it */
+	    if (pcmcia_request_io(link->handle, &link->io) != 0)
+		goto next_entry;
+	}
+
+	/*
+	  Now set up a common memory window, if needed.  There is room
+	  in the dev_link_t structure for one memory window handle,
+	  but if the base addresses need to be saved, or if multiple
+	  windows are needed, the info should go in the private data
+	  structure for this device.
+
+	  Note that the memory window base is a physical address, and
+	  needs to be mapped to virtual space with ioremap() before it
+	  is used.
+	*/
+	if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
+	    cistpl_mem_t *mem =
+		(cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
+	    req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+	    req.Attributes |= WIN_ENABLE;
+	    req.Base = mem->win[0].host_addr;
+	    req.Size = mem->win[0].len;
+/* new in dummy.cs 2001/01/28 MN 
+            if (req.Size < 0x1000)
+                req.Size = 0x1000;
+*/
+	    req.AccessSpeed = 0;
+	    if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
+		goto next_entry;
+	    map.Page = 0; map.CardOffset = mem->win[0].card_addr;
+	    if (pcmcia_map_mem_page(link->win, &map) != 0)
+		goto next_entry;
+	}
+	/* If we got this far, we're cool! */
+	break;
+	
+    next_entry:
+/* new in dummy.cs 2001/01/28 MN 
+        if (link->io.NumPorts1)
+           pcmcia_release_io(link->handle, &link->io);
+*/
+	CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+    }
+    
+    /*
+       Allocate an interrupt line.  Note that this does not assign a
+       handler to the interrupt, unless the 'Handler' member of the
+       irq structure is initialized.
+    */
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+	CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+	
+    /*
+       This actually configures the PCMCIA socket -- setting up
+       the I/O windows and the interrupt mapping, and putting the
+       card and host interface into "Memory and IO" mode.
+    */
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+    /*
+      At this point, the dev_node_t structure(s) need to be
+      initialized and arranged in a linked list at link->dev.
+    */
+    sprintf(dev->node.dev_name, "sedlbauer");
+    dev->node.major = dev->node.minor = 0;
+    link->dev = &dev->node;
+
+    /* Finally, report what we've done */
+    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+	   dev->node.dev_name, link->conf.ConfigIndex,
+	   link->conf.Vcc/10, link->conf.Vcc%10);
+    if (link->conf.Vpp1)
+	printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+	printk(", irq %d", link->irq.AssignedIRQ);
+    if (link->io.NumPorts1)
+	printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+	       link->io.BasePort1+link->io.NumPorts1-1);
+    if (link->io.NumPorts2)
+	printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+	       link->io.BasePort2+link->io.NumPorts2-1);
+    if (link->win)
+	printk(", mem 0x%06lx-0x%06lx", req.Base,
+	       req.Base+req.Size-1);
+    printk("\n");
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = protocol;
+    icard.typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+    
+    last_ret = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->stop), &icard);
+    if (last_ret < 0) {
+    	printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n",
+    		last_ret, link->io.BasePort1);
+    	sedlbauer_release(link);
+    } else
+    	((local_info_t*)link->priv)->cardnr = last_ret;
+
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    sedlbauer_release(link);
+
+} /* sedlbauer_config */
+
+/*======================================================================
+
+    After a card is removed, sedlbauer_release() will unregister the
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+static void sedlbauer_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+    DEBUG(0, "sedlbauer_release(0x%p)\n", link);
+
+    if (local) {
+    	if (local->cardnr >= 0) {
+    	    /* no unregister function with hisax */
+	    HiSax_closecard(local->cardnr);
+	}
+    }
+    /* Unlink the device chain */
+    link->dev = NULL;
+
+    /*
+      In a normal driver, additional code may be needed to release
+      other kernel data structures associated with this device. 
+    */
+    
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win)
+	pcmcia_release_window(link->win);
+    pcmcia_release_configuration(link->handle);
+    if (link->io.NumPorts1)
+	pcmcia_release_io(link->handle, &link->io);
+    if (link->irq.AssignedIRQ)
+	pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+    
+    if (link->state & DEV_STALE_LINK)
+	sedlbauer_detach(link);
+    
+} /* sedlbauer_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.
+
+    When a CARD_REMOVAL event is received, we immediately set a
+    private flag to block future accesses to this device.  All the
+    functions that actually access the device should check this flag
+    to make sure the card is still present.
+    
+======================================================================*/
+
+static int sedlbauer_event(event_t event, int priority,
+		       event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    local_info_t *dev = link->priv;
+    
+    DEBUG(1, "sedlbauer_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG) {
+	    ((local_info_t *)link->priv)->stop = 1;
+	    sedlbauer_release(link);
+	}
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	sedlbauer_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	/* Mark the device as stopped, to block IO until later */
+	dev->stop = 1;
+	if (link->state & DEV_CONFIG)
+	    pcmcia_release_configuration(link->handle);
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG)
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	dev->stop = 0;
+	/*
+	  In a normal driver, additional code may go here to restore
+	  the device state and restart IO. 
+	*/
+	break;
+    }
+    return 0;
+} /* sedlbauer_event */
+
+static struct pcmcia_driver sedlbauer_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "sedlbauer_cs",
+	},
+	.attach		= sedlbauer_attach,
+	.detach		= sedlbauer_detach,
+};
+
+static int __init init_sedlbauer_cs(void)
+{
+	return pcmcia_register_driver(&sedlbauer_driver);
+}
+
+static void __exit exit_sedlbauer_cs(void)
+{
+	pcmcia_unregister_driver(&sedlbauer_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_sedlbauer_cs);
+module_exit(exit_sedlbauer_cs);
diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c
new file mode 100644
index 0000000..132840b
--- /dev/null
+++ b/drivers/isdn/hisax/sportster.c
@@ -0,0 +1,270 @@
+/* $Id: sportster.c,v 1.16.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for USR Sportster internal TA
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
+ *
+ *
+ */
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *sportster_revision = "$Revision: 1.16.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define	 SPORTSTER_ISAC		0xC000
+#define	 SPORTSTER_HSCXA	0x0000
+#define	 SPORTSTER_HSCXB	0x4000
+#define	 SPORTSTER_RES_IRQ	0x8000
+#define	 SPORTSTER_RESET	0x80
+#define	 SPORTSTER_INTE		0x40
+
+static inline int
+calc_off(unsigned int base, unsigned int off)
+{
+	return(base + ((off & 0xfc)<<8) + ((off & 3)<<1));
+}
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.isac, offset)));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.isac, offset), value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.spt.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.spt.isac, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg))
+#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+sportster_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = READHSCX(cs, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = ReadISAC(cs, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = READHSCX(cs, 1, HSCX_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = ReadISAC(cs, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	/* get a new irq impulse if there any pending */
+	bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_sportster(struct IsdnCardState *cs)
+{
+	int i, adr;
+
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0);
+	for (i=0; i<64; i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		release_region(adr, 8);
+	}
+}
+
+void
+reset_sportster(struct IsdnCardState *cs)
+{
+	cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	mdelay(10);
+	cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
+	byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+	mdelay(10);
+}
+
+static int
+Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sportster(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_sportster(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_sportster(cs);
+			inithscxisac(cs, 1);
+			cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
+			byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+			inithscxisac(cs, 2);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static int __init
+get_io_range(struct IsdnCardState *cs)
+{
+	int i, j, adr;
+	
+	for (i=0;i<64;i++) {
+		adr = cs->hw.spt.cfg_reg + i *1024;
+		if (!request_region(adr, 8, "sportster")) {
+			printk(KERN_WARNING
+				"HiSax: %s config port %x-%x already in use\n",
+				CardType[cs->typ], adr, adr + 8);
+			break;
+		} 
+	}
+	if (i==64)
+		return(1);
+	else {
+		for (j=0; j<i; j++) {
+			adr = cs->hw.spt.cfg_reg + j *1024;
+			release_region(adr, 8);
+		}
+		return(0);
+	}
+}
+
+int __init
+setup_sportster(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, sportster_revision);
+	printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_SPORTSTER)
+		return (0);
+
+	cs->hw.spt.cfg_reg = card->para[1];
+	cs->irq = card->para[0];
+	if (!get_io_range(cs))
+		return (0);
+	cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC;
+	cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA;
+	cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB;
+	
+	switch(cs->irq) {
+		case 5:	cs->hw.spt.res_irq = 1;
+			break;
+		case 7:	cs->hw.spt.res_irq = 2;
+			break;
+		case 10:cs->hw.spt.res_irq = 3;
+			break;
+		case 11:cs->hw.spt.res_irq = 4;
+			break;
+		case 12:cs->hw.spt.res_irq = 5;
+			break;
+		case 14:cs->hw.spt.res_irq = 6;
+			break;
+		case 15:cs->hw.spt.res_irq = 7;
+			break;
+		default:release_io_sportster(cs);
+			printk(KERN_WARNING "Sportster: wrong IRQ\n");
+			return(0);
+	}
+	printk(KERN_INFO "HiSax: %s config irq:%d cfg:0x%X\n",
+		CardType[cs->typ], cs->irq, cs->hw.spt.cfg_reg);
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Sportster_card_msg;
+	cs->irq_func = &sportster_interrupt;
+	ISACVersion(cs, "Sportster:");
+	if (HscxVersion(cs, "Sportster:")) {
+		printk(KERN_WARNING
+		       "Sportster: wrong HSCX versions check IO address\n");
+		release_io_sportster(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h
new file mode 100644
index 0000000..e8177b0
--- /dev/null
+++ b/drivers/isdn/hisax/st5481.h
@@ -0,0 +1,535 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _ST5481_H_
+#define _ST5481_H_
+
+#include <linux/config.h>
+
+// USB IDs, the Product Id is in the range 0x4810-0x481F
+
+#define ST_VENDOR_ID 0x0483
+#define ST5481_PRODUCT_ID 0x4810
+#define ST5481_PRODUCT_ID_MASK 0xFFF0
+
+// ST5481 endpoints when using alternative setting 3 (2B+D).
+// To get the endpoint address, OR with 0x80 for IN endpoints.
+
+#define EP_CTRL   0x00U /* Control endpoint */
+#define EP_INT    0x01U /* Interrupt endpoint */
+#define EP_B1_OUT 0x02U /* B1 channel out */
+#define EP_B1_IN  0x03U /* B1 channel in */
+#define EP_B2_OUT 0x04U /* B2 channel out */
+#define EP_B2_IN  0x05U /* B2 channel in */
+#define EP_D_OUT  0x06U /* D channel out */
+#define EP_D_IN   0x07U /* D channel in */
+  
+// Number of isochronous packets. With 20 packets we get
+// 50 interrupts/sec for each endpoint.
+
+#define NUM_ISO_PACKETS_D      20
+#define NUM_ISO_PACKETS_B      20
+
+// Size of each isochronous packet.
+// In outgoing direction we need to match ISDN data rates:
+// D:  2 bytes / msec -> 16 kbit / s
+// B: 16 bytes / msec -> 64 kbit / s
+#define SIZE_ISO_PACKETS_D_IN  16
+#define SIZE_ISO_PACKETS_D_OUT 2
+#define SIZE_ISO_PACKETS_B_IN  32
+#define SIZE_ISO_PACKETS_B_OUT 8
+
+// If we overrun/underrun, we send one packet with +/- 2 bytes
+#define B_FLOW_ADJUST 2
+
+// Registers that are written using vendor specific device request
+// on endpoint 0. 
+
+#define LBA			0x02 /* S loopback */
+#define SET_DEFAULT		0x06 /* Soft reset */
+#define LBB			0x1D /* S maintenance loopback */
+#define STT			0x1e /* S force transmission signals */
+#define SDA_MIN			0x20 /* SDA-sin minimal value */
+#define SDA_MAX			0x21 /* SDA-sin maximal value */
+#define SDELAY_VALUE		0x22 /* Delay between Tx and Rx clock */
+#define IN_D_COUNTER		0x36 /* D receive channel fifo counter */
+#define OUT_D_COUNTER		0x37 /* D transmit channel fifo counter */
+#define IN_B1_COUNTER		0x38 /* B1 receive channel fifo counter */
+#define OUT_B1_COUNTER		0x39 /* B1 transmit channel fifo counter */
+#define IN_B2_COUNTER		0x3a /* B2 receive channel fifo counter */
+#define OUT_B2_COUNTER		0x3b /* B2 transmit channel fifo counter */
+#define FFCTRL_IN_D		0x3C /* D receive channel fifo threshold low */
+#define FFCTRH_IN_D		0x3D /* D receive channel fifo threshold high */
+#define FFCTRL_OUT_D		0x3E /* D transmit channel fifo threshold low */
+#define FFCTRH_OUT_D		0x3F /* D transmit channel fifo threshold high */
+#define FFCTRL_IN_B1		0x40 /* B1 receive channel fifo threshold low */
+#define FFCTRH_IN_B1		0x41 /* B1 receive channel fifo threshold high */
+#define FFCTRL_OUT_B1		0x42 /* B1 transmit channel fifo threshold low */
+#define FFCTRH_OUT_B1		0x43 /* B1 transmit channel fifo threshold high */
+#define FFCTRL_IN_B2		0x44 /* B2 receive channel fifo threshold low */
+#define FFCTRH_IN_B2		0x45 /* B2 receive channel fifo threshold high */
+#define FFCTRL_OUT_B2		0x46 /* B2 transmit channel fifo threshold low */
+#define FFCTRH_OUT_B2		0x47 /* B2 transmit channel fifo threshold high */
+#define MPMSK			0x4A /* Multi purpose interrupt MASK register */
+#define	FFMSK_D			0x4c /* D fifo interrupt MASK register */
+#define	FFMSK_B1		0x4e /* B1 fifo interrupt MASK register */
+#define	FFMSK_B2		0x50 /* B2 fifo interrupt MASK register */
+#define GPIO_DIR		0x52 /* GPIO pins direction registers */
+#define GPIO_OUT		0x53 /* GPIO pins output register */
+#define GPIO_IN			0x54 /* GPIO pins input register */ 
+#define TXCI			0x56 /* CI command to be transmitted */
+
+
+// Format of the interrupt packet received on endpoint 1:
+//
+// +--------+--------+--------+--------+--------+--------+
+// !MPINT   !FFINT_D !FFINT_B1!FFINT_B2!CCIST   !GPIO_INT!
+// +--------+--------+--------+--------+--------+--------+
+
+// Offsets in the interrupt packet
+
+#define MPINT			0
+#define FFINT_D			1
+#define FFINT_B1		2
+#define FFINT_B2		3
+#define CCIST			4
+#define GPIO_INT		5
+#define INT_PKT_SIZE            6
+
+// MPINT
+#define LSD_INT                 0x80 /* S line activity detected */
+#define RXCI_INT		0x40 /* Indicate primitive arrived */
+#define	DEN_INT			0x20 /* Signal enabling data out of D Tx fifo */
+#define DCOLL_INT		0x10 /* D channel collision */
+#define AMIVN_INT		0x04 /* AMI violation number reached 2 */
+#define INFOI_INT		0x04 /* INFOi changed */
+#define DRXON_INT               0x02 /* Reception channel active */
+#define GPCHG_INT               0x01 /* GPIO pin value changed */
+
+// FFINT_x
+#define IN_OVERRUN		0x80 /* In fifo overrun */
+#define OUT_UNDERRUN		0x40 /* Out fifo underrun */
+#define IN_UP			0x20 /* In fifo thresholdh up-crossed */
+#define IN_DOWN			0x10 /* In fifo thresholdl down-crossed */
+#define OUT_UP			0x08 /* Out fifo thresholdh up-crossed */
+#define OUT_DOWN		0x04 /* Out fifo thresholdl down-crossed */
+#define IN_COUNTER_ZEROED	0x02 /* In down-counter reached 0 */
+#define OUT_COUNTER_ZEROED	0x01 /* Out down-counter reached 0 */
+
+#define ANY_REC_INT	(IN_OVERRUN+IN_UP+IN_DOWN+IN_COUNTER_ZEROED)
+#define ANY_XMIT_INT	(OUT_UNDERRUN+OUT_UP+OUT_DOWN+OUT_COUNTER_ZEROED)
+
+
+// Level 1 commands that are sent using the TXCI device request
+#define ST5481_CMD_DR		 0x0 /* Deactivation Request */
+#define ST5481_CMD_RES		 0x1 /* state machine RESet */
+#define ST5481_CMD_TM1		 0x2 /* Test Mode 1 */
+#define ST5481_CMD_TM2		 0x3 /* Test Mode 2 */
+#define ST5481_CMD_PUP		 0x7 /* Power UP */
+#define ST5481_CMD_AR8		 0x8 /* Activation Request class 1 */
+#define ST5481_CMD_AR10		 0x9 /* Activation Request class 2 */
+#define ST5481_CMD_ARL		 0xA /* Activation Request Loopback */
+#define ST5481_CMD_PDN		 0xF /* Power DoWn */
+
+// Turn on/off the LEDs using the GPIO device request.
+// To use the B LEDs, number_of_leds must be set to 4
+#define B1_LED		0x10U
+#define B2_LED		0x20U
+#define GREEN_LED	0x40U
+#define RED_LED	        0x80U
+
+// D channel out states
+enum {
+	ST_DOUT_NONE,
+
+	ST_DOUT_SHORT_INIT,
+	ST_DOUT_SHORT_WAIT_DEN,
+
+	ST_DOUT_LONG_INIT,
+	ST_DOUT_LONG_WAIT_DEN,
+	ST_DOUT_NORMAL,
+
+	ST_DOUT_WAIT_FOR_UNDERRUN,
+        ST_DOUT_WAIT_FOR_NOT_BUSY,
+	ST_DOUT_WAIT_FOR_STOP,
+	ST_DOUT_WAIT_FOR_RESET,
+};
+
+#define DOUT_STATE_COUNT (ST_DOUT_WAIT_FOR_RESET + 1)
+
+// D channel out events
+enum {
+	EV_DOUT_START_XMIT,
+	EV_DOUT_COMPLETE,
+	EV_DOUT_DEN,
+	EV_DOUT_RESETED,
+	EV_DOUT_STOPPED,
+	EV_DOUT_COLL,
+	EV_DOUT_UNDERRUN,
+};
+
+#define DOUT_EVENT_COUNT (EV_DOUT_UNDERRUN + 1)
+
+// ----------------------------------------------------------------------
+
+enum {
+	ST_L1_F3,
+	ST_L1_F4,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+// The first 16 entries match the Level 1 indications that 
+// are found at offset 4 (CCIST) in the interrupt packet
+
+enum {
+	EV_IND_DP,  // 0000 Deactivation Pending
+	EV_IND_1,   // 0001
+	EV_IND_2,   // 0010
+	EV_IND_3,   // 0011
+	EV_IND_RSY, // 0100 ReSYnchronizing
+	EV_IND_5,   // 0101
+	EV_IND_6,   // 0110
+	EV_IND_7,   // 0111
+	EV_IND_AP,  // 1000 Activation Pending
+	EV_IND_9,   // 1001
+	EV_IND_10,  // 1010
+	EV_IND_11,  // 1011
+	EV_IND_AI8, // 1100 Activation Indication class 8
+	EV_IND_AI10,// 1101 Activation Indication class 10
+	EV_IND_AIL, // 1110 Activation Indication Loopback
+	EV_IND_DI,  // 1111 Deactivation Indication
+	EV_PH_ACTIVATE_REQ,
+	EV_PH_DEACTIVATE_REQ,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+#define ERR(format, arg...) \
+printk(KERN_ERR "%s:%s: " format "\n" , __FILE__,  __FUNCTION__ , ## arg)
+
+#define WARN(format, arg...) \
+printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__,  __FUNCTION__ , ## arg)
+
+#define INFO(format, arg...) \
+printk(KERN_INFO "%s:%s: " format "\n" , __FILE__,  __FUNCTION__ , ## arg)
+
+#include "isdnhdlc.h"
+#include "fsm.h"
+#include "hisax_if.h"
+#include <linux/skbuff.h>
+
+/* ======================================================================
+ * FIFO handling
+ */
+
+/* Generic FIFO structure */
+struct fifo {
+	u_char r,w,count,size;
+	spinlock_t lock;
+};
+
+/*
+ * Init an FIFO
+ */
+static inline void fifo_init(struct fifo *fifo, int size)
+{
+	fifo->r = fifo->w = fifo->count = 0;
+	fifo->size = size;
+	spin_lock_init(&fifo->lock);
+}
+
+/*
+ * Add an entry to the FIFO
+ */
+static inline int fifo_add(struct fifo *fifo)
+{
+	unsigned long flags;
+	int index;
+
+	if (!fifo) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&fifo->lock, flags);
+	if (fifo->count == fifo->size) {
+		// FIFO full
+		index = -1;
+	} else {
+		// Return index where to get the next data to add to the FIFO
+		index = fifo->w++ & (fifo->size-1);
+		fifo->count++;
+	}
+	spin_unlock_irqrestore(&fifo->lock, flags);
+	return index;
+}
+
+/*
+ * Remove an entry from the FIFO with the index returned.
+ */
+static inline int fifo_remove(struct fifo *fifo)
+{
+	unsigned long flags;
+	int index;
+
+	if (!fifo) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&fifo->lock, flags);
+	if (!fifo->count) {
+		// FIFO empty
+		index = -1;
+	} else {
+		// Return index where to get the next data from the FIFO
+		index = fifo->r++ & (fifo->size-1);
+		fifo->count--;
+	}
+	spin_unlock_irqrestore(&fifo->lock, flags);
+
+	return index;
+}
+
+/* ======================================================================
+ * control pipe
+ */
+typedef void (*ctrl_complete_t)(void *);
+
+typedef struct ctrl_msg {
+	struct usb_ctrlrequest dr;
+	ctrl_complete_t complete;
+	void *context;
+} ctrl_msg; 
+
+/* FIFO of ctrl messages waiting to be sent */
+#define MAX_EP0_MSG 16
+struct ctrl_msg_fifo {
+	struct fifo f;
+	struct ctrl_msg data[MAX_EP0_MSG];
+};	
+
+#define MAX_DFRAME_LEN_L1	300
+#define HSCX_BUFMAX	4096
+
+struct st5481_ctrl {
+	struct ctrl_msg_fifo msg_fifo;
+	unsigned long busy;
+	struct urb *urb;
+};
+
+struct st5481_intr {
+  //	struct evt_fifo evt_fifo;
+	struct urb *urb;
+};
+
+struct st5481_d_out {
+	struct isdnhdlc_vars hdlc_state;
+	struct urb *urb[2]; /* double buffering */
+	unsigned long busy;
+	struct sk_buff *tx_skb;
+	struct FsmInst fsm;
+};
+
+struct st5481_b_out {
+	struct isdnhdlc_vars hdlc_state;
+	struct urb *urb[2]; /* double buffering */
+	u_char flow_event;
+	u_long busy;
+	struct sk_buff *tx_skb;
+};
+
+struct st5481_in {
+	struct isdnhdlc_vars hdlc_state;
+	struct urb *urb[2]; /* double buffering */
+	int mode;
+	int bufsize;
+	unsigned int num_packets;
+	unsigned int packet_size;
+	unsigned char ep, counter;
+	unsigned char *rcvbuf;
+	struct st5481_adapter *adapter;
+	struct hisax_if *hisax_if;
+};
+
+int st5481_setup_in(struct st5481_in *in);
+void st5481_release_in(struct st5481_in *in);
+void st5481_in_mode(struct st5481_in *in, int mode);
+
+struct st5481_bcs {
+	struct hisax_b_if b_if;
+	struct st5481_adapter *adapter;
+	struct st5481_in b_in;
+	struct st5481_b_out b_out;
+	int channel;
+	int mode;
+};
+
+struct st5481_adapter {
+	struct list_head list;
+	int number_of_leds;
+	struct usb_device *usb_dev;
+	struct hisax_d_if hisax_d_if;
+
+	struct st5481_ctrl ctrl;
+	struct st5481_intr intr;
+	struct st5481_in d_in;
+	struct st5481_d_out d_out;
+
+	unsigned char leds;
+	unsigned int led_counter;
+
+	unsigned long event;
+
+	struct FsmInst l1m;
+	struct FsmTimer timer;
+
+	struct st5481_bcs bcs[2];
+};
+
+#define TIMER3_VALUE 7000
+
+/* ======================================================================
+ *
+ */
+
+/*
+ * Submit an URB with error reporting. This is a macro so
+ * the __FUNCTION__ returns the caller function name.
+ */
+#define SUBMIT_URB(urb, mem_flags) \
+({ \
+	int status; \
+	if ((status = usb_submit_urb(urb, mem_flags)) < 0) { \
+		WARN("usb_submit_urb failed,status=%d", status); \
+	} \
+        status; \
+})
+
+/*
+ * USB double buffering, return the URB index (0 or 1).
+ */
+static inline int get_buf_nr(struct urb *urbs[], struct urb *urb)
+{
+        return (urbs[0]==urb ? 0 : 1); 
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* B Channel */
+
+int  st5481_setup_b(struct st5481_bcs *bcs);
+void st5481_release_b(struct st5481_bcs *bcs);
+void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+/* D Channel */
+
+int  st5481_setup_d(struct st5481_adapter *adapter);
+void st5481_release_d(struct st5481_adapter *adapter);
+void st5481_b_l2l1(struct hisax_if *b_if, int pr, void *arg);
+int  st5481_d_init(void);
+void st5481_d_exit(void);
+
+/* USB */
+void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command);
+int st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev, 
+			   unsigned int pipe, int num_packets,
+			   int packet_size, int buf_size,
+			   usb_complete_t complete, void *context);
+void st5481_release_isocpipes(struct urb* urb[2]);
+
+int  st5481_isoc_flatten(struct urb *urb);
+void st5481_usb_pipe_reset(struct st5481_adapter *adapter,
+		    u_char pipe, ctrl_complete_t complete, void *context);
+void st5481_usb_ctrl_msg(struct st5481_adapter *adapter,
+		  u8 request, u8 requesttype, u16 value, u16 index,
+		  ctrl_complete_t complete, void *context);
+void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter,
+			 u8 request, u16 value,
+			 ctrl_complete_t complete, void *context);
+int  st5481_setup_usb(struct st5481_adapter *adapter);
+void st5481_release_usb(struct st5481_adapter *adapter);
+void st5481_start(struct st5481_adapter *adapter);
+void st5481_stop(struct st5481_adapter *adapter);
+
+// ----------------------------------------------------------------------
+// debugging macros
+
+#define __debug_variable st5481_debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+
+extern int st5481_debug;
+
+#define DBG_ISO_PACKET(level,urb) \
+  if (level & __debug_variable) dump_iso_packet(__FUNCTION__,urb)
+
+static void __attribute__((unused))
+dump_iso_packet(const char *name, struct urb *urb)
+{
+	int i,j;
+	int len,ofs;
+	u_char *data;
+
+	printk(KERN_DEBUG "%s: packets=%d,errors=%d\n",
+	       name,urb->number_of_packets,urb->error_count);
+	for (i = 0; i  < urb->number_of_packets; ++i) {
+		if (urb->pipe & USB_DIR_IN) {
+			len = urb->iso_frame_desc[i].actual_length;
+		} else {
+			len = urb->iso_frame_desc[i].length;
+		}
+		ofs = urb->iso_frame_desc[i].offset;
+		printk(KERN_DEBUG "len=%.2d,ofs=%.3d ",len,ofs);
+		if (len) {
+			data = urb->transfer_buffer+ofs;
+			for (j=0; j < len; j++) {
+				printk ("%.2x", data[j]);
+			}
+		}
+		printk("\n");
+	}
+}
+
+static inline const char *ST5481_CMD_string(int evt)
+{
+	static char s[16];
+
+	switch (evt) {
+	case ST5481_CMD_DR: return "DR";
+	case ST5481_CMD_RES: return "RES";
+	case ST5481_CMD_TM1: return "TM1";
+	case ST5481_CMD_TM2: return "TM2";
+	case ST5481_CMD_PUP: return "PUP";
+	case ST5481_CMD_AR8: return "AR8";
+	case ST5481_CMD_AR10: return "AR10";
+	case ST5481_CMD_ARL: return "ARL";
+	case ST5481_CMD_PDN: return "PDN";
+	};
+	
+	sprintf(s,"0x%x",evt);
+	return s;
+}	
+
+#else
+
+#define DBG_ISO_PACKET(level,urb) do {} while (0)
+
+#endif
+
+
+
+#endif 
diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c
new file mode 100644
index 0000000..2fcd093
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_b.c
@@ -0,0 +1,374 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include "st5481.h"
+
+static inline void B_L1L2(struct st5481_bcs *bcs, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+	ifc->l1l2(ifc, pr, arg);
+}
+
+/*
+ * Encode and transmit next frame.
+ */
+static void usb_b_out(struct st5481_bcs *bcs,int buf_nr)
+{
+	struct st5481_b_out *b_out = &bcs->b_out;
+	struct st5481_adapter *adapter = bcs->adapter;
+	struct urb *urb;
+	unsigned int packet_size,offset;
+	int len,buf_size,bytes_sent;
+	int i;
+	struct sk_buff *skb;
+	
+	if (test_and_set_bit(buf_nr, &b_out->busy)) {
+		DBG(4,"ep %d urb %d busy",(bcs->channel+1)*2,buf_nr);
+		return;
+	}
+	urb = b_out->urb[buf_nr];
+
+	// Adjust isoc buffer size according to flow state
+	if(b_out->flow_event & (OUT_DOWN | OUT_UNDERRUN)) {
+		buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST;
+		packet_size = SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST;
+		DBG(4,"B%d,adjust flow,add %d bytes",bcs->channel+1,B_FLOW_ADJUST);
+	} else if(b_out->flow_event & OUT_UP){
+		buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST;
+		packet_size = SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST;
+		DBG(4,"B%d,adjust flow,remove %d bytes",bcs->channel+1,B_FLOW_ADJUST);
+	} else {
+		buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT;
+		packet_size = 8;
+	}
+	b_out->flow_event = 0;
+
+	len = 0;
+	while (len < buf_size) {
+		if ((skb = b_out->tx_skb)) {
+			DBG_SKB(0x100, skb);
+			DBG(4,"B%d,len=%d",bcs->channel+1,skb->len);
+			
+			if (bcs->mode == L1_MODE_TRANS) {	
+				bytes_sent = buf_size - len;
+				if (skb->len < bytes_sent)
+					bytes_sent = skb->len;
+				{	/* swap tx bytes to get hearable audio data */
+					register unsigned char *src  = skb->data;
+					register unsigned char *dest = urb->transfer_buffer+len;
+					register unsigned int count;
+					for (count = 0; count < bytes_sent; count++)
+						*dest++ = isdnhdlc_bit_rev_tab[*src++];
+				}
+				len += bytes_sent;
+			} else {
+				len += isdnhdlc_encode(&b_out->hdlc_state,
+						       skb->data, skb->len, &bytes_sent,
+						       urb->transfer_buffer+len, buf_size-len);
+			}
+
+			skb_pull(skb, bytes_sent);
+
+			if (!skb->len) {
+				// Frame sent
+				b_out->tx_skb = NULL;
+				B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+				dev_kfree_skb_any(skb);
+
+/* 				if (!(bcs->tx_skb = skb_dequeue(&bcs->sq))) { */
+/* 					st5481B_sched_event(bcs, B_XMTBUFREADY); */
+/* 				} */
+			}
+		} else {
+			if (bcs->mode == L1_MODE_TRANS) {
+				memset(urb->transfer_buffer+len, 0xff, buf_size-len);
+				len = buf_size;
+			} else {
+				// Send flags
+				len += isdnhdlc_encode(&b_out->hdlc_state,
+						       NULL, 0, &bytes_sent,
+						       urb->transfer_buffer+len, buf_size-len);
+			}
+		}	
+	}
+
+	// Prepare the URB
+	for (i = 0, offset = 0; offset < len; i++) {
+		urb->iso_frame_desc[i].offset = offset;
+		urb->iso_frame_desc[i].length = packet_size;
+		offset += packet_size;
+		packet_size = SIZE_ISO_PACKETS_B_OUT;
+	}
+	urb->transfer_buffer_length = len;
+	urb->number_of_packets = i;
+	urb->dev = adapter->usb_dev;
+
+	DBG_ISO_PACKET(0x200,urb);
+
+	SUBMIT_URB(urb, GFP_NOIO);
+}
+
+/*
+ * Start transfering (flags or data) on the B channel, since
+ * FIFO counters has been set to a non-zero value.
+ */
+static void st5481B_start_xfer(void *context)
+{
+	struct st5481_bcs *bcs = context;
+
+	DBG(4,"B%d",bcs->channel+1);
+
+	// Start transmitting (flags or data) on B channel
+
+	usb_b_out(bcs,0);
+	usb_b_out(bcs,1);
+}
+
+/*
+ * If the adapter has only 2 LEDs, the green
+ * LED will blink with a rate depending
+ * on the number of channels opened.
+ */
+static void led_blink(struct st5481_adapter *adapter)
+{
+	u_char leds = adapter->leds;
+
+	// 50 frames/sec for each channel
+	if (++adapter->led_counter % 50) {
+		return;
+	}
+
+	if (adapter->led_counter % 100) {
+		leds |= GREEN_LED;
+	} else {
+		leds &= ~GREEN_LED;
+	}
+	
+	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, leds, NULL, NULL);
+}
+
+static void usb_b_out_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_bcs *bcs = urb->context;
+	struct st5481_b_out *b_out = &bcs->b_out;
+	struct st5481_adapter *adapter = bcs->adapter;
+	int buf_nr;
+	
+	buf_nr = get_buf_nr(b_out->urb, urb);
+	test_and_clear_bit(buf_nr, &b_out->busy);
+
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+			if (b_out->busy == 0) {
+				st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL);
+			}
+		} else {
+			DBG(1,"urb killed"); 
+			return; // Give up
+		}
+	}
+
+	usb_b_out(bcs,buf_nr);
+
+	if (adapter->number_of_leds == 2)
+		led_blink(adapter);
+}
+
+/*
+ * Start or stop the transfer on the B channel.
+ */
+static void st5481B_mode(struct st5481_bcs *bcs, int mode)
+{
+	struct st5481_b_out *b_out = &bcs->b_out;
+	struct st5481_adapter *adapter = bcs->adapter;
+
+	DBG(4,"B%d,mode=%d", bcs->channel + 1, mode);
+
+	if (bcs->mode == mode)
+		return;
+
+	bcs->mode = mode;
+
+	// Cancel all USB transfers on this B channel
+	usb_unlink_urb(b_out->urb[0]);
+	usb_unlink_urb(b_out->urb[1]);
+	b_out->busy = 0;
+
+	st5481_in_mode(&bcs->b_in, mode);
+	if (bcs->mode != L1_MODE_NULL) {
+		// Open the B channel
+		if (bcs->mode != L1_MODE_TRANS) {
+			isdnhdlc_out_init(&b_out->hdlc_state, 0, bcs->mode == L1_MODE_HDLC_56K);
+		}
+		st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2, NULL, NULL);
+	
+		// Enable B channel interrupts
+		st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2), 
+				    OUT_UP+OUT_DOWN+OUT_UNDERRUN, NULL, NULL);
+
+		// Enable B channel FIFOs
+		st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 32, st5481B_start_xfer, bcs);
+		if (adapter->number_of_leds == 4) {
+			if (bcs->channel == 0) {
+				adapter->leds |= B1_LED;
+			} else {
+				adapter->leds |= B2_LED;
+			}
+		}
+	} else {
+		// Disble B channel interrupts
+		st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2), 0, NULL, NULL);
+
+		// Disable B channel FIFOs
+		st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 0, NULL, NULL);
+
+		if (adapter->number_of_leds == 4) {
+			if (bcs->channel == 0) {
+				adapter->leds &= ~B1_LED;
+			} else {
+				adapter->leds &= ~B2_LED;
+			}
+		} else {
+			st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+		}
+		if (b_out->tx_skb) {
+			dev_kfree_skb_any(b_out->tx_skb);
+			b_out->tx_skb = NULL;
+		}
+		
+	}
+}
+
+static int st5481_setup_b_out(struct st5481_bcs *bcs)
+{
+	struct usb_device *dev = bcs->adapter->usb_dev;
+	struct usb_interface *intf;
+	struct usb_host_interface *altsetting = NULL;
+	struct usb_host_endpoint *endpoint;
+  	struct st5481_b_out *b_out = &bcs->b_out;
+
+	DBG(4,"");
+
+	intf = usb_ifnum_to_if(dev, 0);
+	if (intf)
+		altsetting = usb_altnum_to_altsetting(intf, 3);
+	if (!altsetting)
+		return -ENXIO;
+
+	// Allocate URBs and buffers for the B channel out
+	endpoint = &altsetting->endpoint[EP_B1_OUT - 1 + bcs->channel * 2];
+
+	DBG(4,"endpoint address=%02x,packet size=%d",
+	    endpoint->desc.bEndpointAddress, le16_to_cpu(endpoint->desc.wMaxPacketSize));
+
+	// Allocate memory for 8000bytes/sec + extra bytes if underrun
+	return st5481_setup_isocpipes(b_out->urb, dev, 
+				      usb_sndisocpipe(dev, endpoint->desc.bEndpointAddress),
+				      NUM_ISO_PACKETS_B, SIZE_ISO_PACKETS_B_OUT,
+				      NUM_ISO_PACKETS_B * SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST,
+				      usb_b_out_complete, bcs);
+}
+
+static void st5481_release_b_out(struct st5481_bcs *bcs)
+{
+	struct st5481_b_out *b_out = &bcs->b_out;
+
+	DBG(4,"");
+
+	st5481_release_isocpipes(b_out->urb);
+}
+
+int st5481_setup_b(struct st5481_bcs *bcs)
+{
+	int retval;
+
+	DBG(4,"");
+
+	retval = st5481_setup_b_out(bcs);
+	if (retval)
+		goto err;
+	bcs->b_in.bufsize = HSCX_BUFMAX;
+	bcs->b_in.num_packets = NUM_ISO_PACKETS_B;
+	bcs->b_in.packet_size = SIZE_ISO_PACKETS_B_IN;
+	bcs->b_in.ep = (bcs->channel ? EP_B2_IN : EP_B1_IN) | USB_DIR_IN;
+	bcs->b_in.counter = bcs->channel ? IN_B2_COUNTER : IN_B1_COUNTER;
+	bcs->b_in.adapter = bcs->adapter;
+	bcs->b_in.hisax_if = &bcs->b_if.ifc;
+	retval = st5481_setup_in(&bcs->b_in);
+	if (retval)
+		goto err_b_out;
+
+
+	return 0;
+
+ err_b_out:
+	st5481_release_b_out(bcs);
+ err:
+	return retval;
+}
+
+/*
+ * Release buffers and URBs for the B channels
+ */
+void st5481_release_b(struct st5481_bcs *bcs)
+{
+	DBG(4,"");
+
+	st5481_release_in(&bcs->b_in);
+	st5481_release_b_out(bcs);
+}
+
+/*
+ * st5481_b_l2l1 is the entry point for upper layer routines that want to
+ * transmit on the B channel.  PH_DATA | REQUEST is a normal packet that
+ * we either start transmitting (if idle) or queue (if busy).
+ * PH_PULL | REQUEST can be called to request a callback message
+ * (PH_PULL | CONFIRM)
+ * once the link is idle.  After a "pull" callback, the upper layer
+ * routines can use PH_PULL | INDICATION to send data.
+ */
+void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+	struct st5481_bcs *bcs = ifc->priv;
+	struct sk_buff *skb = arg;
+	int mode;
+
+	DBG(4, "");
+
+	switch (pr) {
+	case PH_DATA | REQUEST:
+		if (bcs->b_out.tx_skb)
+			BUG();
+		
+		bcs->b_out.tx_skb = skb;
+		break;
+	case PH_ACTIVATE | REQUEST:
+		mode = (int) arg;
+		DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+		st5481B_mode(bcs, mode);
+		B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+		st5481B_mode(bcs, L1_MODE_NULL);
+		B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+		break;
+	default:
+		WARN("pr %#x\n", pr);
+	}
+}
diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c
new file mode 100644
index 0000000..071b1d3
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_d.c
@@ -0,0 +1,776 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include "st5481.h"
+
+static void ph_connect(struct st5481_adapter *adapter);
+static void ph_disconnect(struct st5481_adapter *adapter);
+
+static struct Fsm l1fsm;
+
+static char *strL1State[] =
+{
+	"ST_L1_F3",
+	"ST_L1_F4",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+static char *strL1Event[] =
+{
+	"EV_IND_DP",  
+	"EV_IND_1",   
+	"EV_IND_2",   
+	"EV_IND_3",   
+	"EV_IND_RSY", 
+	"EV_IND_5",   
+	"EV_IND_6",   
+	"EV_IND_7",   
+	"EV_IND_AP",  
+	"EV_IND_9",   
+	"EV_IND_10",  
+	"EV_IND_11",  
+	"EV_IND_AI8",
+	"EV_IND_AI10",
+	"EV_IND_AIL",
+	"EV_IND_DI",  
+	"EV_PH_ACTIVATE_REQ",
+	"EV_PH_DEACTIVATE_REQ",
+	"EV_TIMER3",
+};
+
+static inline void D_L1L2(struct st5481_adapter *adapter, int pr, void *arg)
+{
+	struct hisax_if *ifc = (struct hisax_if *) &adapter->hisax_d_if;
+
+	ifc->l1l2(ifc, pr, arg);
+}
+
+static void
+l1_go_f3(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	if (fi->state == ST_L1_F7)
+		ph_disconnect(adapter);
+	
+	FsmChangeState(fi, ST_L1_F3);
+	D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	if (fi->state == ST_L1_F7)
+		ph_disconnect(adapter);
+
+	FsmChangeState(fi, ST_L1_F6);
+}
+
+static void
+l1_go_f7(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	FsmDelTimer(&adapter->timer, 0);
+	ph_connect(adapter);
+	FsmChangeState(fi, ST_L1_F7);
+	D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	if (fi->state == ST_L1_F7)
+		ph_disconnect(adapter);
+
+	FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	st5481_ph_command(adapter, ST5481_CMD_DR);
+	FsmChangeState(fi, ST_L1_F3);
+	D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_ignore(struct FsmInst *fi, int event, void *arg)
+{
+}
+
+static void
+l1_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fi->userdata;
+
+	st5481_ph_command(adapter, ST5481_CMD_DR);
+	st5481_ph_command(adapter, ST5481_CMD_PUP);
+	FsmRestartTimer(&adapter->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	st5481_ph_command(adapter, ST5481_CMD_AR8);
+	FsmChangeState(fi, ST_L1_F4);
+}
+
+static struct FsmNode L1FnList[] __initdata =
+{
+	{ST_L1_F3, EV_IND_DP,            l1_ignore},
+	{ST_L1_F3, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F3, EV_IND_AI8,           l1_go_f7},
+	{ST_L1_F3, EV_IND_AI10,          l1_go_f7},
+	{ST_L1_F3, EV_PH_ACTIVATE_REQ,   l1_activate},
+
+	{ST_L1_F4, EV_TIMER3,            l1_timer3},
+	{ST_L1_F4, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F4, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F4, EV_IND_AI8,           l1_go_f7},
+	{ST_L1_F4, EV_IND_AI10,          l1_go_f7},
+
+	{ST_L1_F6, EV_TIMER3,            l1_timer3},
+	{ST_L1_F6, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F6, EV_IND_AP,            l1_ignore},
+	{ST_L1_F6, EV_IND_AI8,           l1_go_f7},
+	{ST_L1_F6, EV_IND_AI10,          l1_go_f7},
+	{ST_L1_F7, EV_IND_RSY,           l1_go_f8},
+
+	{ST_L1_F7, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F7, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F7, EV_IND_AI8,           l1_ignore},
+	{ST_L1_F7, EV_IND_AI10,          l1_ignore},
+	{ST_L1_F7, EV_IND_RSY,           l1_go_f8},
+
+	{ST_L1_F8, EV_TIMER3,            l1_timer3},
+	{ST_L1_F8, EV_IND_DP,            l1_go_f3},
+	{ST_L1_F8, EV_IND_AP,            l1_go_f6},
+	{ST_L1_F8, EV_IND_AI8,           l1_go_f8},
+	{ST_L1_F8, EV_IND_AI10,          l1_go_f8},
+	{ST_L1_F8, EV_IND_RSY,           l1_ignore},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	DBG(8, "%s", buf);
+	va_end(args);
+}
+
+/* ======================================================================
+ * D-Channel out
+ */
+
+/*
+  D OUT state machine:
+  ====================
+
+  Transmit short frame (< 16 bytes of encoded data):
+
+  L1 FRAME    D_OUT_STATE           USB                  D CHANNEL
+  --------    -----------           ---                  ---------
+ 
+              FIXME
+
+ -> [xx..xx]  SHORT_INIT            -> [7Exx..xxC1C27EFF]
+              SHORT_WAIT_DEN        <> OUT_D_COUNTER=16 
+                                                 
+              END_OF_SHORT          <- DEN_EVENT         -> 7Exx
+                                                          xxxx 
+                                                          xxxx
+							  xxxx 
+							  xxxx
+							  xxxx
+							  C1C1 
+							  7EFF 
+              WAIT_FOR_RESET_IDLE   <- D_UNDERRUN        <- (8ms)                        
+              IDLE                  <> Reset pipe
+
+              
+
+  Transmit long frame (>= 16 bytes of encoded data):
+
+  L1 FRAME    D_OUT_STATE           USB                  D CHANNEL
+  --------    -----------           ---                  ---------
+
+ -> [xx...xx] IDLE
+              WAIT_FOR_STOP         <> OUT_D_COUNTER=0
+              WAIT_FOR_RESET        <> Reset pipe
+	      STOP
+	      INIT_LONG_FRAME       -> [7Exx..xx]
+              WAIT_DEN              <> OUT_D_COUNTER=16 
+              OUT_NORMAL            <- DEN_EVENT       -> 7Exx
+              END_OF_FRAME_BUSY     -> [xxxx]             xxxx 
+              END_OF_FRAME_NOT_BUSY -> [xxxx]             xxxx
+				    -> [xxxx]		  xxxx 
+				    -> [C1C2]		  xxxx
+				    -> [7EFF]		  xxxx
+							  xxxx 
+							  xxxx 
+                                                          ....
+							  xxxx
+							  C1C2
+							  7EFF
+	                 	    <- D_UNDERRUN      <- (> 8ms)                        
+              WAIT_FOR_STOP         <> OUT_D_COUNTER=0
+              WAIT_FOR_RESET        <> Reset pipe
+	      STOP
+
+*/          
+
+static struct Fsm dout_fsm;
+
+static char *strDoutState[] =
+{
+	"ST_DOUT_NONE",
+
+	"ST_DOUT_SHORT_INIT",
+	"ST_DOUT_SHORT_WAIT_DEN",
+
+	"ST_DOUT_LONG_INIT",
+	"ST_DOUT_LONG_WAIT_DEN",
+	"ST_DOUT_NORMAL",
+
+	"ST_DOUT_WAIT_FOR_UNDERRUN",
+        "ST_DOUT_WAIT_FOR_NOT_BUSY",
+	"ST_DOUT_WAIT_FOR_STOP",
+	"ST_DOUT_WAIT_FOR_RESET",
+};
+
+static char *strDoutEvent[] =
+{
+	"EV_DOUT_START_XMIT",
+	"EV_DOUT_COMPLETE",
+	"EV_DOUT_DEN",
+	"EV_DOUT_RESETED",
+	"EV_DOUT_STOPPED",
+	"EV_DOUT_COLL",
+	"EV_DOUT_UNDERRUN",
+};
+
+static void dout_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	DBG(0x2, "%s", buf);
+	va_end(args);
+}
+
+static void dout_stop_event(void *context)
+{
+	struct st5481_adapter *adapter = context;
+
+	FsmEvent(&adapter->d_out.fsm, EV_DOUT_STOPPED, NULL);
+}
+
+/*
+ * Start the transfer of a D channel frame.
+ */
+static void usb_d_out(struct st5481_adapter *adapter, int buf_nr)
+{
+	struct st5481_d_out *d_out = &adapter->d_out;
+	struct urb *urb;
+	unsigned int num_packets, packet_offset;
+	int len, buf_size, bytes_sent;
+	struct sk_buff *skb;
+	struct usb_iso_packet_descriptor *desc;
+
+	if (d_out->fsm.state != ST_DOUT_NORMAL)
+		return;
+
+	if (test_and_set_bit(buf_nr, &d_out->busy)) {
+		DBG(2, "ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
+		return;
+	}
+	urb = d_out->urb[buf_nr];
+
+	skb = d_out->tx_skb;
+
+	buf_size = NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT;
+	
+	if (skb) {
+		len = isdnhdlc_encode(&d_out->hdlc_state,
+				      skb->data, skb->len, &bytes_sent,
+				      urb->transfer_buffer, buf_size);
+		skb_pull(skb,bytes_sent);
+	} else {
+		// Send flags or idle
+		len = isdnhdlc_encode(&d_out->hdlc_state,
+				      NULL, 0, &bytes_sent,
+				      urb->transfer_buffer, buf_size);
+	}
+	
+	if (len < buf_size) {
+		FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
+	}
+	if (skb && !skb->len) {
+		d_out->tx_skb = NULL;
+		D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
+		dev_kfree_skb_any(skb);
+	}
+
+	// Prepare the URB
+	urb->transfer_buffer_length = len;
+	num_packets = 0;
+	packet_offset = 0;
+	while (packet_offset < len) {
+		desc = &urb->iso_frame_desc[num_packets];
+		desc->offset = packet_offset;
+		desc->length = SIZE_ISO_PACKETS_D_OUT;
+		if (len - packet_offset < desc->length)
+			desc->length = len - packet_offset;
+		num_packets++;
+		packet_offset += desc->length;
+	}
+	urb->number_of_packets = num_packets;
+
+	// Prepare the URB
+	urb->dev = adapter->usb_dev;
+	// Need to transmit the next buffer 2ms after the DEN_EVENT
+	urb->transfer_flags = 0;
+	urb->start_frame = usb_get_current_frame_number(adapter->usb_dev)+2;
+
+	DBG_ISO_PACKET(0x20,urb);
+
+	if (usb_submit_urb(urb, GFP_KERNEL) < 0) {
+		// There is another URB queued up
+		urb->transfer_flags = URB_ISO_ASAP;
+		SUBMIT_URB(urb, GFP_KERNEL);
+	}	
+}
+
+static void fifo_reseted(void *context)
+{
+	struct st5481_adapter *adapter = context;
+
+	FsmEvent(&adapter->d_out.fsm, EV_DOUT_RESETED, NULL);
+}
+
+static void usb_d_out_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_adapter *adapter = urb->context;
+	struct st5481_d_out *d_out = &adapter->d_out;
+	int buf_nr;
+	
+	DBG(2, "");
+
+	buf_nr = get_buf_nr(d_out->urb, urb);
+	test_and_clear_bit(buf_nr, &d_out->busy);
+
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+			if (d_out->busy == 0) {
+				st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
+			}
+			return;
+		} else {
+			DBG(1,"urb killed"); 
+			return; // Give up
+		}
+	}
+
+	FsmEvent(&adapter->d_out.fsm, EV_DOUT_COMPLETE, (void *) buf_nr);
+}
+
+/* ====================================================================== */
+
+static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg)
+{
+	// FIXME unify?
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+	struct urb *urb;
+	int len, bytes_sent;
+	struct sk_buff *skb;
+	int buf_nr = 0;
+
+	skb = d_out->tx_skb;
+
+	DBG(2,"len=%d",skb->len);
+
+	isdnhdlc_out_init(&d_out->hdlc_state, 1, 0);
+
+	if (test_and_set_bit(buf_nr, &d_out->busy)) {
+		WARN("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
+		return;
+	}
+	urb = d_out->urb[buf_nr];
+
+	DBG_SKB(0x10, skb);
+	len = isdnhdlc_encode(&d_out->hdlc_state,
+			      skb->data, skb->len, &bytes_sent,
+			      urb->transfer_buffer, 16);
+	skb_pull(skb, bytes_sent);
+
+	if(len < 16)
+		FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_INIT);
+	else
+		FsmChangeState(&d_out->fsm, ST_DOUT_LONG_INIT);
+
+	if (skb->len == 0) {
+		d_out->tx_skb = NULL;
+		D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
+		dev_kfree_skb_any(skb);
+	}
+
+// Prepare the URB
+	urb->transfer_buffer_length = len;
+
+	urb->iso_frame_desc[0].offset = 0;
+	urb->iso_frame_desc[0].length = len;
+	urb->number_of_packets = 1;
+
+	// Prepare the URB
+	urb->dev = adapter->usb_dev;
+	urb->transfer_flags = URB_ISO_ASAP;
+
+	DBG_ISO_PACKET(0x20,urb);
+	SUBMIT_URB(urb, GFP_KERNEL);
+}
+
+static void dout_short_fifo(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_WAIT_DEN);
+	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
+}
+
+static void dout_end_short_frame(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
+}
+
+static void dout_long_enable_fifo(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+    
+	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
+	FsmChangeState(&d_out->fsm, ST_DOUT_LONG_WAIT_DEN);
+}
+
+static void dout_long_den(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_NORMAL);
+	usb_d_out(adapter, 0);
+	usb_d_out(adapter, 1);
+}
+
+static void dout_reset(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_RESET);
+	st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
+}
+
+static void dout_stop(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_STOP);
+	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 0, dout_stop_event, adapter);
+}
+
+static void dout_underrun(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	if (test_bit(0, &d_out->busy) || test_bit(1, &d_out->busy)) {
+		FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_NOT_BUSY);
+	}  else {
+		dout_stop(fsm, event, arg);
+	}
+}
+
+static void dout_check_busy(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	if (!test_bit(0, &d_out->busy) && !test_bit(1, &d_out->busy))
+		dout_stop(fsm, event, arg);
+}
+
+static void dout_reseted(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
+	// FIXME locking
+	if (d_out->tx_skb)
+		FsmEvent(&d_out->fsm, EV_DOUT_START_XMIT, NULL);
+}
+
+static void dout_complete(struct FsmInst *fsm, int event, void *arg)
+{
+	struct st5481_adapter *adapter = fsm->userdata;
+	int buf_nr = (int) arg;
+
+	usb_d_out(adapter, buf_nr);
+}
+
+static void dout_ignore(struct FsmInst *fsm, int event, void *arg)
+{
+}
+
+static struct FsmNode DoutFnList[] __initdata =
+{
+	{ST_DOUT_NONE,                   EV_DOUT_START_XMIT,   dout_start_xmit},
+
+	{ST_DOUT_SHORT_INIT,             EV_DOUT_COMPLETE,     dout_short_fifo},
+
+	{ST_DOUT_SHORT_WAIT_DEN,         EV_DOUT_DEN,          dout_end_short_frame},
+	{ST_DOUT_SHORT_WAIT_DEN,         EV_DOUT_UNDERRUN,     dout_underrun},
+
+	{ST_DOUT_LONG_INIT,              EV_DOUT_COMPLETE,     dout_long_enable_fifo},
+
+	{ST_DOUT_LONG_WAIT_DEN,          EV_DOUT_DEN,          dout_long_den},
+	{ST_DOUT_LONG_WAIT_DEN,          EV_DOUT_UNDERRUN,     dout_underrun},
+
+	{ST_DOUT_NORMAL,                 EV_DOUT_UNDERRUN,     dout_underrun},
+	{ST_DOUT_NORMAL,                 EV_DOUT_COMPLETE,     dout_complete},
+
+	{ST_DOUT_WAIT_FOR_UNDERRUN,      EV_DOUT_UNDERRUN,     dout_underrun},
+	{ST_DOUT_WAIT_FOR_UNDERRUN,      EV_DOUT_COMPLETE,     dout_ignore},
+
+	{ST_DOUT_WAIT_FOR_NOT_BUSY,      EV_DOUT_COMPLETE,     dout_check_busy},
+
+	{ST_DOUT_WAIT_FOR_STOP,          EV_DOUT_STOPPED,      dout_reset},
+
+	{ST_DOUT_WAIT_FOR_RESET,         EV_DOUT_RESETED,      dout_reseted},
+};
+
+void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+	struct st5481_adapter *adapter = hisax_d_if->priv;
+	struct sk_buff *skb = arg;
+
+	switch (pr) {
+	case PH_ACTIVATE | REQUEST:
+		FsmEvent(&adapter->l1m, EV_PH_ACTIVATE_REQ, NULL);
+		break;
+	case PH_DEACTIVATE | REQUEST:
+		FsmEvent(&adapter->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+		break;
+	case PH_DATA | REQUEST:
+		DBG(2, "PH_DATA REQUEST len %d", skb->len);
+		if (adapter->d_out.tx_skb)
+			BUG();
+
+		adapter->d_out.tx_skb = skb;
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL);
+		break;
+	default:
+		WARN("pr %#x\n", pr);
+		break;
+	}
+}
+
+/* ======================================================================
+ */
+
+/*
+ * Start receiving on the D channel since entered state F7.
+ */
+static void ph_connect(struct st5481_adapter *adapter)
+{
+	struct st5481_d_out *d_out = &adapter->d_out;
+	struct st5481_in *d_in = &adapter->d_in;
+
+	DBG(8,"");
+		
+	FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
+
+	//	st5481_usb_device_ctrl_msg(adapter, FFMSK_D, OUT_UNDERRUN, NULL, NULL);
+	st5481_usb_device_ctrl_msg(adapter, FFMSK_D, 0xfc, NULL, NULL);
+	st5481_in_mode(d_in, L1_MODE_HDLC);
+
+#ifdef LOOPBACK
+	// Turn loopback on (data sent on B and D looped back)
+	st5481_usb_device_ctrl_msg(cs, LBB, 0x04, NULL, NULL);
+#endif
+
+	st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, NULL, NULL);
+
+	// Turn on the green LED to tell that we are in state F7
+	adapter->leds |= GREEN_LED;
+	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+}
+
+/*
+ * Stop receiving on the D channel since not in state F7.
+ */
+static void ph_disconnect(struct st5481_adapter *adapter)
+{
+	DBG(8,"");
+
+	st5481_in_mode(&adapter->d_in, L1_MODE_NULL);
+
+	// Turn off the green LED to tell that we left state F7
+	adapter->leds &= ~GREEN_LED;
+	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+}
+
+static int st5481_setup_d_out(struct st5481_adapter *adapter)
+{
+	struct usb_device *dev = adapter->usb_dev;
+	struct usb_interface *intf;
+	struct usb_host_interface *altsetting = NULL;
+	struct usb_host_endpoint *endpoint;
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	DBG(2,"");
+
+	intf = usb_ifnum_to_if(dev, 0);
+	if (intf)
+		altsetting = usb_altnum_to_altsetting(intf, 3);
+	if (!altsetting)
+		return -ENXIO;
+
+	// Allocate URBs and buffers for the D channel out
+	endpoint = &altsetting->endpoint[EP_D_OUT-1];
+
+	DBG(2,"endpoint address=%02x,packet size=%d",
+	    endpoint->desc.bEndpointAddress, le16_to_cpu(endpoint->desc.wMaxPacketSize));
+
+	return st5481_setup_isocpipes(d_out->urb, dev, 
+				      usb_sndisocpipe(dev, endpoint->desc.bEndpointAddress),
+				      NUM_ISO_PACKETS_D, SIZE_ISO_PACKETS_D_OUT,
+				      NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT,
+				      usb_d_out_complete, adapter);
+}
+
+static void st5481_release_d_out(struct st5481_adapter *adapter)
+{
+	struct st5481_d_out *d_out = &adapter->d_out;
+
+	DBG(2,"");
+
+	st5481_release_isocpipes(d_out->urb);
+}
+
+int st5481_setup_d(struct st5481_adapter *adapter)
+{
+	int retval;
+
+	DBG(2,"");
+
+	retval = st5481_setup_d_out(adapter);
+	if (retval)
+		goto err;
+	adapter->d_in.bufsize = MAX_DFRAME_LEN_L1;
+	adapter->d_in.num_packets = NUM_ISO_PACKETS_D;
+	adapter->d_in.packet_size = SIZE_ISO_PACKETS_D_IN;
+	adapter->d_in.ep = EP_D_IN | USB_DIR_IN;
+	adapter->d_in.counter = IN_D_COUNTER;
+	adapter->d_in.adapter = adapter;
+	adapter->d_in.hisax_if = &adapter->hisax_d_if.ifc;
+	retval = st5481_setup_in(&adapter->d_in);
+	if (retval)
+		goto err_d_out;
+
+	adapter->l1m.fsm = &l1fsm;
+	adapter->l1m.state = ST_L1_F3;
+	adapter->l1m.debug = 1;
+	adapter->l1m.userdata = adapter;
+	adapter->l1m.printdebug = l1m_debug;
+	FsmInitTimer(&adapter->l1m, &adapter->timer);
+
+	adapter->d_out.fsm.fsm = &dout_fsm;
+	adapter->d_out.fsm.state = ST_DOUT_NONE;
+	adapter->d_out.fsm.debug = 1;
+	adapter->d_out.fsm.userdata = adapter;
+	adapter->d_out.fsm.printdebug = dout_debug;
+
+	return 0;
+
+ err_d_out:
+	st5481_release_d_out(adapter);
+ err:
+	return retval;
+}
+
+void st5481_release_d(struct st5481_adapter *adapter)
+{
+	DBG(2,"");
+
+	st5481_release_in(&adapter->d_in);
+	st5481_release_d_out(adapter);
+}
+
+/* ======================================================================
+ * init / exit
+ */
+
+int __init st5481_d_init(void)
+{
+	int retval;
+
+	l1fsm.state_count = L1_STATE_COUNT;
+	l1fsm.event_count = L1_EVENT_COUNT;
+	l1fsm.strEvent = strL1Event;
+	l1fsm.strState = strL1State;
+	retval = FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+	if (retval)
+		goto err;
+
+	dout_fsm.state_count = DOUT_STATE_COUNT;
+	dout_fsm.event_count = DOUT_EVENT_COUNT;
+	dout_fsm.strEvent = strDoutEvent;
+	dout_fsm.strState = strDoutState;
+	retval = FsmNew(&dout_fsm, DoutFnList, ARRAY_SIZE(DoutFnList));
+	if (retval)
+		goto err_l1;
+
+	return 0;
+
+ err_l1:
+	FsmFree(&l1fsm);
+ err:
+	return retval;
+}
+
+// can't be __exit
+void st5481_d_exit(void)
+{
+	FsmFree(&l1fsm);
+	FsmFree(&dout_fsm);
+}
diff --git a/drivers/isdn/hisax/st5481_hdlc.c b/drivers/isdn/hisax/st5481_hdlc.c
new file mode 100644
index 0000000..680f42e
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_hdlc.c
@@ -0,0 +1,580 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/crc-ccitt.h>
+#include "st5481_hdlc.h"
+
+
+enum {
+	HDLC_FAST_IDLE,HDLC_GET_FLAG_B0,HDLC_GETFLAG_B1A6,HDLC_GETFLAG_B7,
+	HDLC_GET_DATA,HDLC_FAST_FLAG
+};
+
+enum {
+	HDLC_SEND_DATA,HDLC_SEND_CRC1,HDLC_SEND_FAST_FLAG,
+	HDLC_SEND_FIRST_FLAG,HDLC_SEND_CRC2,HDLC_SEND_CLOSING_FLAG,
+	HDLC_SEND_IDLE1,HDLC_SEND_FAST_IDLE,HDLC_SENDFLAG_B0,
+	HDLC_SENDFLAG_B1A6,HDLC_SENDFLAG_B7,STOPPED
+};
+
+void 
+hdlc_rcv_init(struct hdlc_vars *hdlc, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->state = HDLC_GET_DATA;
+	hdlc->do_adapt56 = do_adapt56;
+	hdlc->dchannel = 0;
+	hdlc->crc = 0;
+	hdlc->cbin = 0;
+	hdlc->shift_reg = 0;
+	hdlc->ffvalue = 0;
+	hdlc->dstpos = 0;
+}
+
+void 
+hdlc_out_init(struct hdlc_vars *hdlc, int is_d_channel, int do_adapt56)
+{
+   	hdlc->bit_shift = 0;
+	hdlc->hdlc_bits1 = 0;
+	hdlc->data_bits = 0;
+	hdlc->ffbit_shift = 0;
+	hdlc->data_received = 0;
+	hdlc->do_closing = 0;
+	hdlc->ffvalue = 0;
+	if (is_d_channel) {
+		hdlc->dchannel = 1;
+		hdlc->state = HDLC_SEND_FIRST_FLAG;
+	} else {
+		hdlc->dchannel = 0;
+		hdlc->state = HDLC_SEND_FAST_FLAG;
+		hdlc->ffvalue = 0x7e;
+	} 
+	hdlc->cbin = 0x7e;
+	hdlc->bit_shift = 0;
+	if(do_adapt56){
+		hdlc->do_adapt56 = 1;		
+		hdlc->data_bits = 0;
+		hdlc->state = HDLC_SENDFLAG_B0;
+	} else {
+		hdlc->do_adapt56 = 0;		
+		hdlc->data_bits = 8;
+	}
+	hdlc->shift_reg = 0;
+}
+
+/*
+  hdlc_decode - decodes HDLC frames from a transparent bit stream.
+
+  The source buffer is scanned for valid HDLC frames looking for
+  flags (01111110) to indicate the start of a frame. If the start of
+  the frame is found, the bit stuffing is removed (0 after 5 1's).
+  When a new flag is found, the complete frame has been received
+  and the CRC is checked.
+  If a valid frame is found, the function returns the frame length 
+  excluding the CRC with the bit HDLC_END_OF_FRAME set.
+  If the beginning of a valid frame is found, the function returns
+  the length. 
+  If a framing error is found (too many 1s and not a flag) the function 
+  returns the length with the bit HDLC_FRAMING_ERROR set.
+  If a CRC error is found the function returns the length with the
+  bit HDLC_CRC_ERROR set.
+  If the frame length exceeds the destination buffer size, the function
+  returns the length with the bit HDLC_LENGTH_ERROR set.
+
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (decoded) from the source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of decoded bytes in the destination buffer and status
+  flag.
+ */
+int hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src,
+		int slen, int *count, unsigned char *dst, int dsize)
+{
+	int status=0;
+
+	static const unsigned char fast_flag[]={
+		0x00,0x00,0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f
+	};
+
+	static const unsigned char fast_flag_value[]={
+		0x00,0x7e,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f
+	};
+
+	static const unsigned char fast_abort[]={
+		0x00,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff
+	};
+
+	*count = slen;
+
+	while(slen > 0){
+		if(hdlc->bit_shift==0){
+			hdlc->cbin = *src++;
+			slen--;
+			hdlc->bit_shift = 8;
+			if(hdlc->do_adapt56){
+				hdlc->bit_shift --;
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			return 0;
+		case HDLC_FAST_IDLE:
+			if(hdlc->cbin == 0xff){
+				hdlc->bit_shift = 0;
+				break;
+			}
+			hdlc->state = HDLC_GET_FLAG_B0;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->bit_shift = 8;
+			break;
+		case HDLC_GET_FLAG_B0:
+			if(!(hdlc->cbin & 0x80)) {
+				hdlc->state = HDLC_GETFLAG_B1A6;
+				hdlc->hdlc_bits1 = 0;
+			} else {
+				if(!hdlc->do_adapt56){
+					if(++hdlc->hdlc_bits1 >=8 ) if(hdlc->bit_shift==1)
+						hdlc->state = HDLC_FAST_IDLE;
+				}
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B1A6:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				if(hdlc->hdlc_bits1==6){
+					hdlc->state = HDLC_GETFLAG_B7;
+				}
+			} else {
+				hdlc->hdlc_bits1 = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GETFLAG_B7:
+			if(hdlc->cbin & 0x80) {
+				hdlc->state = HDLC_GET_FLAG_B0;
+			} else {
+				hdlc->state = HDLC_GET_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->shift_reg = 0;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_bits = 0;
+				hdlc->data_received = 0;
+			}
+			hdlc->cbin<<=1;
+			hdlc->bit_shift --;
+			break;
+		case HDLC_GET_DATA:
+			if(hdlc->cbin & 0x80){
+				hdlc->hdlc_bits1++;
+				switch(hdlc->hdlc_bits1){
+				case 6:
+					break;
+				case 7:
+					if(hdlc->data_received) {
+						// bad frame
+						status = -HDLC_FRAMING_ERROR;
+					}
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_abort[hdlc->bit_shift+1]){
+							hdlc->state = HDLC_FAST_IDLE;
+							hdlc->bit_shift=1;
+							break;
+						}
+					} else {
+						hdlc->state = HDLC_GET_FLAG_B0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->shift_reg |= 0x80;
+					hdlc->data_bits++;
+					break;
+				}
+			} else {
+				switch(hdlc->hdlc_bits1){
+				case 5:
+					break;
+				case 6:
+					if(hdlc->data_received){
+						if (hdlc->dstpos < 2) {
+							status = -HDLC_FRAMING_ERROR;
+						} else if (hdlc->crc != 0xf0b8){
+							// crc error
+							status = -HDLC_CRC_ERROR;
+						} else {
+							// remove CRC
+							hdlc->dstpos -= 2;
+							// good frame
+							status = hdlc->dstpos;
+						}
+					}
+					hdlc->crc = 0xffff;
+					hdlc->shift_reg = 0;
+					hdlc->data_bits = 0;
+					if(!hdlc->do_adapt56){
+						if(hdlc->cbin==fast_flag[hdlc->bit_shift]){
+							hdlc->ffvalue = fast_flag_value[hdlc->bit_shift];
+							hdlc->state = HDLC_FAST_FLAG;
+							hdlc->ffbit_shift = hdlc->bit_shift;
+							hdlc->bit_shift = 1;
+						} else {
+							hdlc->state = HDLC_GET_DATA;
+							hdlc->data_received = 0;
+						}
+					} else {
+						hdlc->state = HDLC_GET_DATA;
+						hdlc->data_received = 0;
+					}
+					break;
+				default:
+					hdlc->shift_reg>>=1;
+					hdlc->data_bits++;
+					break;
+				}
+				hdlc->hdlc_bits1 = 0;
+			}
+			if (status) {
+				hdlc->dstpos = 0;
+				*count -= slen;
+				hdlc->cbin <<= 1;
+				hdlc->bit_shift--;
+				return status;
+			}
+			if(hdlc->data_bits==8){
+				hdlc->data_bits = 0;
+				hdlc->data_received = 1;
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+
+				// good byte received
+				if (dsize--) {
+					dst[hdlc->dstpos++] = hdlc->shift_reg;
+				} else {
+					// frame too long
+					status = -HDLC_LENGTH_ERROR;
+					hdlc->dstpos = 0;
+				}
+			}
+			hdlc->cbin <<= 1;
+			hdlc->bit_shift--;
+			break;
+		case HDLC_FAST_FLAG:
+			if(hdlc->cbin==hdlc->ffvalue){
+				hdlc->bit_shift = 0;
+				break;
+			} else {
+				if(hdlc->cbin == 0xff){
+					hdlc->state = HDLC_FAST_IDLE;
+					hdlc->bit_shift=0;
+				} else if(hdlc->ffbit_shift==8){
+					hdlc->state = HDLC_GETFLAG_B7;
+					break;
+				} else {
+					hdlc->shift_reg = fast_abort[hdlc->ffbit_shift-1];
+					hdlc->hdlc_bits1 = hdlc->ffbit_shift-2;
+					if(hdlc->hdlc_bits1<0)hdlc->hdlc_bits1 = 0;
+					hdlc->data_bits = hdlc->ffbit_shift-1;
+					hdlc->state = HDLC_GET_DATA;
+					hdlc->data_received = 0;
+				}
+			}
+			break;
+		default:
+			break;
+		}
+	}
+	*count -= slen;
+	return 0;
+}
+
+/*
+  hdlc_encode - encodes HDLC frames to a transparent bit stream.
+
+  The bit stream starts with a beginning flag (01111110). After
+  that each byte is added to the bit stream with bit stuffing added
+  (0 after 5 1's).
+  When the last byte has been removed from the source buffer, the
+  CRC (2 bytes is added) and the frame terminates with the ending flag.
+  For the dchannel, the idle character (all 1's) is also added at the end.
+  If this function is called with empty source buffer (slen=0), flags or
+  idle character will be generated.
+ 
+  src - source buffer
+  slen - source buffer length
+  count - number of bytes removed (encoded) from source buffer
+  dst _ destination buffer
+  dsize - destination buffer size
+  returns - number of encoded bytes in the destination buffer
+*/
+int hdlc_encode(struct hdlc_vars *hdlc, const unsigned char *src, 
+		unsigned short slen, int *count,
+		unsigned char *dst, int dsize)
+{
+	static const unsigned char xfast_flag_value[] = {
+		0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e
+	};
+
+	int len = 0;
+
+	*count = slen;
+
+	while (dsize > 0) {
+		if(hdlc->bit_shift==0){	
+			if(slen && !hdlc->do_closing){
+				hdlc->shift_reg = *src++;
+				slen--;
+				if (slen == 0) 
+					hdlc->do_closing = 1;  /* closing sequence, CRC + flag(s) */
+				hdlc->bit_shift = 8;
+			} else {
+				if(hdlc->state == HDLC_SEND_DATA){
+					if(hdlc->data_received){
+						hdlc->state = HDLC_SEND_CRC1;
+						hdlc->crc ^= 0xffff;
+						hdlc->bit_shift = 8;
+						hdlc->shift_reg = hdlc->crc & 0xff;
+					} else if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+					}
+				}
+			  
+			}
+		}
+
+		switch(hdlc->state){
+		case STOPPED:
+			while (dsize--)
+				*dst++ = 0xff;
+		  
+			return dsize;
+		case HDLC_SEND_FAST_FLAG:
+			hdlc->do_closing = 0;
+			if(slen == 0){
+				*dst++ = hdlc->ffvalue;
+				len++;
+				dsize--;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits);
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SENDFLAG_B0:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->hdlc_bits1 = 0;
+			hdlc->state = HDLC_SENDFLAG_B1A6;
+			break;
+		case HDLC_SENDFLAG_B1A6:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			hdlc->cbin++;
+			if(++hdlc->hdlc_bits1 == 6)
+				hdlc->state = HDLC_SENDFLAG_B7;
+			break;
+		case HDLC_SENDFLAG_B7:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(slen == 0){
+				hdlc->state = HDLC_SENDFLAG_B0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				hdlc->data_received = 1;
+			}
+			break;
+		case HDLC_SEND_FIRST_FLAG:
+			hdlc->data_received = 1;
+			if(hdlc->data_bits==8){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->shift_reg & 0x01)
+				hdlc->cbin++;
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_DATA;
+				hdlc->crc = 0xffff;
+				hdlc->hdlc_bits1 = 0;
+			}
+			break;
+		case HDLC_SEND_DATA:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->bit_shift==8){
+				hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			break;
+		case HDLC_SEND_CRC1:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = (hdlc->crc >> 8);
+				hdlc->state = HDLC_SEND_CRC2;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CRC2:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->hdlc_bits1++;
+				hdlc->cbin++;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			} else {
+				hdlc->hdlc_bits1 = 0;
+				hdlc->shift_reg >>= 1;
+				hdlc->bit_shift--;
+			}
+			if(hdlc->bit_shift==0){
+				hdlc->shift_reg = 0x7e;
+				hdlc->state = HDLC_SEND_CLOSING_FLAG;
+				hdlc->bit_shift = 8;
+			}
+			break;
+		case HDLC_SEND_CLOSING_FLAG:
+			hdlc->cbin <<= 1;
+			hdlc->data_bits++;
+			if(hdlc->hdlc_bits1 == 5){
+				hdlc->hdlc_bits1 = 0;
+				break;
+			}
+			if(hdlc->shift_reg & 0x01){
+				hdlc->cbin++;
+			}
+			hdlc->shift_reg >>= 1;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->ffvalue = xfast_flag_value[hdlc->data_bits];
+				if(hdlc->dchannel){
+					hdlc->ffvalue = 0x7e;
+					hdlc->state = HDLC_SEND_IDLE1;
+					hdlc->bit_shift = 8-hdlc->data_bits;
+					if(hdlc->bit_shift==0)
+						hdlc->state = HDLC_SEND_FAST_IDLE;
+				} else {
+					if(!hdlc->do_adapt56){
+						hdlc->state = HDLC_SEND_FAST_FLAG;
+						hdlc->data_received = 0;
+					} else {
+						hdlc->state = HDLC_SENDFLAG_B0;
+						hdlc->data_received = 0;
+					}
+					// Finished with this frame, send flags
+					if (dsize > 1) dsize = 1; 
+				}
+			}
+			break;
+		case HDLC_SEND_IDLE1:
+			hdlc->do_closing = 0;
+			hdlc->cbin <<= 1;
+			hdlc->cbin++;
+			hdlc->data_bits++;
+			hdlc->bit_shift--;
+			if(hdlc->bit_shift==0){
+				hdlc->state = HDLC_SEND_FAST_IDLE;
+				hdlc->bit_shift = 0;
+			}
+			break;
+		case HDLC_SEND_FAST_IDLE:
+			hdlc->do_closing = 0;
+			hdlc->cbin = 0xff;
+			hdlc->data_bits = 8;
+			if(hdlc->bit_shift == 8){
+				hdlc->cbin = 0x7e;
+				hdlc->state = HDLC_SEND_FIRST_FLAG;
+			} else {
+				*dst++ = hdlc->cbin;
+				hdlc->bit_shift = hdlc->data_bits = 0;
+				len++;
+				dsize = 0;
+			}
+			break;
+		default:
+			break;
+		}
+		if(hdlc->do_adapt56){
+			if(hdlc->data_bits==7){
+				hdlc->cbin <<= 1;
+				hdlc->cbin++;
+				hdlc->data_bits++;
+			}
+		}
+		if(hdlc->data_bits==8){
+			*dst++ = hdlc->cbin;
+			hdlc->data_bits = 0;
+			len++;
+			dsize--;
+		}
+	}
+	*count -= slen;
+
+	return len;
+}
+
diff --git a/drivers/isdn/hisax/st5481_hdlc.h b/drivers/isdn/hisax/st5481_hdlc.h
new file mode 100644
index 0000000..495432f0
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_hdlc.h
@@ -0,0 +1,62 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __ST5481_HDLC_H__
+#define __ST5481_HDLC_H__
+
+struct hdlc_vars {
+  	int bit_shift; 
+	int hdlc_bits1;
+	int data_bits;
+	int ffbit_shift; // encoding only
+	int state;
+	int dstpos;
+
+	int data_received:1; // set if transferring data
+	int dchannel:1; // set if D channel (send idle instead of flags)
+	int do_adapt56:1; // set if 56K adaptation
+        int do_closing:1; // set if in closing phase (need to send CRC + flag
+
+	unsigned short crc;
+
+	unsigned char cbin; 
+	unsigned char shift_reg;
+	unsigned char ffvalue;
+	
+};
+
+
+/*
+  The return value from hdlc_decode is
+  the frame length, 0 if no complete frame was decoded,
+  or a negative error number
+*/
+
+#define HDLC_FRAMING_ERROR     1
+#define HDLC_CRC_ERROR         2
+#define HDLC_LENGTH_ERROR      3
+
+void 
+hdlc_rcv_init(struct hdlc_vars *hdlc, int do_adapt56);
+
+int
+hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src, int slen,int *count, 
+	    unsigned char *dst, int dsize);
+
+void 
+hdlc_out_init(struct hdlc_vars *hdlc,int is_d_channel,int do_adapt56);
+
+int 
+hdlc_encode(struct hdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count,
+	    unsigned char *dst,int dsize);
+
+#endif
diff --git a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c
new file mode 100644
index 0000000..7aa810d
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_init.c
@@ -0,0 +1,224 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* 
+ * TODO:
+ *
+ * b layer1 delay?
+ * hotplug / unregister issues
+ * mod_inc/dec_use_count
+ * unify parts of d/b channel usb handling
+ * file header
+ * avoid copy to isoc buffer?
+ * improve usb delay?
+ * merge l1 state machines?
+ * clean up debug
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include "st5481.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: driver for ST5481 USB ISDN adapter");
+MODULE_AUTHOR("Frode Isaksen");
+MODULE_LICENSE("GPL");
+
+static int protocol = 2;       /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+static int number_of_leds = 2;       /* 2 LEDs on the adpater default */
+module_param(number_of_leds, int, 0);
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0x1;
+module_param(debug, int, 0);
+int st5481_debug;
+#endif
+
+static LIST_HEAD(adapter_list);
+
+/* ======================================================================
+ * registration/deregistration with the USB layer
+ */
+
+/*
+ * This function will be called when the adapter is plugged
+ * into the USB bus.
+ */
+static int probe_st5481(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct st5481_adapter *adapter;
+	struct hisax_b_if *b_if[2];
+	int retval, i;
+
+	printk(KERN_INFO "st541: found adapter VendorId %04x, ProductId %04x, LEDs %d\n",
+	     le16_to_cpu(dev->descriptor.idVendor),
+	     le16_to_cpu(dev->descriptor.idProduct),
+	     number_of_leds);
+
+	adapter = kmalloc(sizeof(struct st5481_adapter), GFP_KERNEL);
+	if (!adapter)
+		return -ENOMEM;
+
+	memset(adapter, 0, sizeof(struct st5481_adapter));
+
+	adapter->number_of_leds = number_of_leds;
+	adapter->usb_dev = dev;
+
+	adapter->hisax_d_if.owner = THIS_MODULE;
+	adapter->hisax_d_if.ifc.priv = adapter;
+	adapter->hisax_d_if.ifc.l2l1 = st5481_d_l2l1;
+
+	for (i = 0; i < 2; i++) {
+		adapter->bcs[i].adapter = adapter;
+		adapter->bcs[i].channel = i;
+		adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+		adapter->bcs[i].b_if.ifc.l2l1 = st5481_b_l2l1;
+	}
+	list_add(&adapter->list, &adapter_list);
+
+	retval = st5481_setup_usb(adapter);
+	if (retval < 0)
+		goto err;
+
+	retval = st5481_setup_d(adapter);
+	if (retval < 0)
+		goto err_usb;
+
+	retval = st5481_setup_b(&adapter->bcs[0]);
+	if (retval < 0)
+		goto err_d;
+
+	retval = st5481_setup_b(&adapter->bcs[1]);
+	if (retval < 0)
+		goto err_b;
+
+	for (i = 0; i < 2; i++)
+		b_if[i] = &adapter->bcs[i].b_if;
+
+	hisax_register(&adapter->hisax_d_if, b_if, "st5481_usb", protocol);
+	st5481_start(adapter);
+
+	usb_set_intfdata(intf, adapter);
+	return 0;
+
+ err_b:
+	st5481_release_b(&adapter->bcs[0]);
+ err_d:
+	st5481_release_d(adapter);
+ err_usb:
+	st5481_release_usb(adapter);
+ err:
+	return -EIO;
+}
+
+/*
+ * This function will be called when the adapter is removed
+ * from the USB bus.
+ */
+static void disconnect_st5481(struct usb_interface *intf)
+{
+	struct st5481_adapter *adapter = usb_get_intfdata(intf);
+
+	DBG(1,"");
+
+	usb_set_intfdata(intf, NULL);
+	if (!adapter)
+		return;
+	
+	list_del(&adapter->list);
+
+	st5481_stop(adapter);
+	st5481_release_b(&adapter->bcs[1]);
+	st5481_release_b(&adapter->bcs[0]);
+	st5481_release_d(adapter);
+	// we would actually better wait for completion of outstanding urbs
+	mdelay(2);
+	st5481_release_usb(adapter);
+
+	hisax_unregister(&adapter->hisax_d_if);
+
+	kfree(adapter);
+}
+
+/*
+ * The last 4 bits in the Product Id is set with 4 pins on the chip.
+ */
+static struct usb_device_id st5481_ids[] = {
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x0) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x1) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x2) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x3) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x4) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x5) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x6) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x7) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x8) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x9) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xA) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xB) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xC) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xD) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xE) },
+	{ USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xF) },
+	{ }
+};
+MODULE_DEVICE_TABLE (usb, st5481_ids);
+
+static struct usb_driver st5481_usb_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"st5481_usb",
+	.probe =	probe_st5481,
+	.disconnect =	disconnect_st5481,
+	.id_table =	st5481_ids,
+};
+
+static int __init st5481_usb_init(void)
+{
+	int retval;
+
+#ifdef CONFIG_HISAX_DEBUG
+	st5481_debug = debug;
+#endif
+
+	printk(KERN_INFO "hisax_st5481: ST5481 USB ISDN driver $Revision: 2.4.2.3 $\n");
+
+	retval = st5481_d_init();
+	if (retval < 0)
+		goto out;
+
+	retval = usb_register(&st5481_usb_driver);
+	if (retval < 0)
+		goto out_d_exit;
+
+	return 0;
+
+ out_d_exit:
+	st5481_d_exit();
+ out:
+	return retval;
+}
+
+static void __exit st5481_usb_exit(void)
+{
+	usb_deregister(&st5481_usb_driver);
+	st5481_d_exit();
+}
+
+module_init(st5481_usb_init);
+module_exit(st5481_usb_exit);
diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c
new file mode 100644
index 0000000..2369180
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_usb.c
@@ -0,0 +1,650 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author       Frode Isaksen
+ * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
+ *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include "st5481.h"
+
+/* ======================================================================
+ * control pipe
+ */
+
+/*
+ * Send the next endpoint 0 request stored in the FIFO.
+ * Called either by the completion or by usb_ctrl_msg.
+ */
+static void usb_next_ctrl_msg(struct urb *urb,
+			      struct st5481_adapter *adapter)
+{
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	int r_index;
+
+	if (test_and_set_bit(0, &ctrl->busy)) {
+		return;
+	}
+
+	if ((r_index = fifo_remove(&ctrl->msg_fifo.f)) < 0) {
+		test_and_clear_bit(0,&ctrl->busy);
+		return;
+	} 
+	urb->setup_packet = 
+		(unsigned char *)&ctrl->msg_fifo.data[r_index];
+	
+	DBG(1,"request=0x%02x,value=0x%04x,index=%x",
+	    ((struct ctrl_msg *)urb->setup_packet)->dr.bRequest,
+	    ((struct ctrl_msg *)urb->setup_packet)->dr.wValue,
+	    ((struct ctrl_msg *)urb->setup_packet)->dr.wIndex);
+
+	// Prepare the URB
+	urb->dev = adapter->usb_dev;
+
+	SUBMIT_URB(urb, GFP_ATOMIC);
+}
+
+/*
+ * Asynchronous endpoint 0 request (async version of usb_control_msg).
+ * The request will be queued up in a FIFO if the endpoint is busy.
+ */
+void usb_ctrl_msg(struct st5481_adapter *adapter,
+		  u8 request, u8 requesttype, u16 value, u16 index,
+		  ctrl_complete_t complete, void *context)
+{
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	int w_index;
+	struct ctrl_msg *ctrl_msg;
+	
+	if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) {
+		WARN("control msg FIFO full");
+		return;
+	}
+	ctrl_msg = &ctrl->msg_fifo.data[w_index]; 
+   
+	ctrl_msg->dr.bRequestType = requesttype;
+	ctrl_msg->dr.bRequest = request;
+	ctrl_msg->dr.wValue = cpu_to_le16p(&value);
+	ctrl_msg->dr.wIndex = cpu_to_le16p(&index);
+	ctrl_msg->dr.wLength = 0;
+	ctrl_msg->complete = complete;
+	ctrl_msg->context = context;
+
+	usb_next_ctrl_msg(ctrl->urb, adapter);
+}
+
+/*
+ * Asynchronous endpoint 0 device request.
+ */
+void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter,
+			 u8 request, u16 value,
+			 ctrl_complete_t complete, void *context)
+{
+	usb_ctrl_msg(adapter, request, 
+		     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 
+		     value, 0, complete, context);
+}
+
+/*
+ * Asynchronous pipe reset (async version of usb_clear_halt).
+ */
+void st5481_usb_pipe_reset(struct st5481_adapter *adapter,
+		    u_char pipe,
+		    ctrl_complete_t complete, void *context)
+{
+	DBG(1,"pipe=%02x",pipe);
+
+	usb_ctrl_msg(adapter,
+		     USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RECIP_ENDPOINT,
+		     0, pipe, complete, context);
+}
+
+
+/*
+  Physical level functions
+*/
+
+void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command)
+{
+	DBG(8,"command=%s", ST5481_CMD_string(command));
+
+	st5481_usb_device_ctrl_msg(adapter, TXCI, command, NULL, NULL);
+}
+
+/*
+ * The request on endpoint 0 has completed.
+ * Call the user provided completion routine and try
+ * to send the next request.
+ */
+static void usb_ctrl_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_adapter *adapter = urb->context;
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	struct ctrl_msg *ctrl_msg;
+	
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+		} else {
+			DBG(1,"urb killed");
+			return; // Give up
+		}
+	}
+
+	ctrl_msg = (struct ctrl_msg *)urb->setup_packet;
+	
+	if (ctrl_msg->dr.bRequest == USB_REQ_CLEAR_FEATURE) {
+	        /* Special case handling for pipe reset */
+		le16_to_cpus(&ctrl_msg->dr.wIndex);
+
+		/* toggle is reset on clear */
+		usb_settoggle(adapter->usb_dev, 
+			      ctrl_msg->dr.wIndex & ~USB_DIR_IN, 
+			      (ctrl_msg->dr.wIndex & USB_DIR_IN) == 0,
+			      0);
+
+
+	}
+	
+	if (ctrl_msg->complete)
+		ctrl_msg->complete(ctrl_msg->context);
+
+	clear_bit(0, &ctrl->busy);
+	
+	// Try to send next control message
+	usb_next_ctrl_msg(urb, adapter);
+	return;
+}
+
+/* ======================================================================
+ * interrupt pipe
+ */
+
+/*
+ * The interrupt endpoint will be called when any
+ * of the 6 registers changes state (depending on masks).
+ * Decode the register values and schedule a private event.
+ * Called at interrupt.
+ */
+static void usb_int_complete(struct urb *urb, struct pt_regs *regs)
+{
+	u8 *data = urb->transfer_buffer;
+	u8 irqbyte;
+	struct st5481_adapter *adapter = urb->context;
+	int j;
+	int status;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		DBG(1, "urb shutting down with status: %d", urb->status);
+		return;
+	default:
+		WARN("nonzero urb status received: %d", urb->status);
+		goto exit;
+	}
+
+	
+	DBG_PACKET(1, data, INT_PKT_SIZE);
+		
+	if (urb->actual_length == 0) {
+		goto exit;
+	}
+
+	irqbyte = data[MPINT];
+	if (irqbyte & DEN_INT)
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_DEN, NULL);
+
+	if (irqbyte & DCOLL_INT)
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_COLL, NULL);
+
+	irqbyte = data[FFINT_D];
+	if (irqbyte & OUT_UNDERRUN)
+		FsmEvent(&adapter->d_out.fsm, EV_DOUT_UNDERRUN, NULL);
+
+	if (irqbyte & OUT_DOWN)
+;//		printk("OUT_DOWN\n");
+
+	irqbyte = data[MPINT];
+	if (irqbyte & RXCI_INT)
+		FsmEvent(&adapter->l1m, data[CCIST] & 0x0f, NULL);
+
+	for (j = 0; j < 2; j++)
+		adapter->bcs[j].b_out.flow_event |= data[FFINT_B1 + j];
+
+	urb->actual_length = 0;
+
+exit:
+	status = usb_submit_urb (urb, GFP_ATOMIC);
+	if (status)
+		WARN("usb_submit_urb failed with result %d", status);
+}
+
+/* ======================================================================
+ * initialization
+ */
+
+int st5481_setup_usb(struct st5481_adapter *adapter)
+{
+	struct usb_device *dev = adapter->usb_dev;
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+	struct st5481_intr *intr = &adapter->intr;
+	struct usb_interface *intf;
+	struct usb_host_interface *altsetting = NULL;
+	struct usb_host_endpoint *endpoint;
+	int status;
+	struct urb *urb;
+	u8 *buf;
+	
+	DBG(1,"");
+	
+	if ((status = usb_reset_configuration (dev)) < 0) {
+		WARN("reset_configuration failed,status=%d",status);
+		return status;
+	}
+
+	intf = usb_ifnum_to_if(dev, 0);
+	if (intf)
+		altsetting = usb_altnum_to_altsetting(intf, 3);
+	if (!altsetting)
+		return -ENXIO;
+
+	// Check if the config is sane
+	if ( altsetting->desc.bNumEndpoints != 7 ) {
+		WARN("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints);
+		return -EINVAL;
+	}
+
+	// The descriptor is wrong for some early samples of the ST5481 chip
+	altsetting->endpoint[3].desc.wMaxPacketSize = __constant_cpu_to_le16(32);
+	altsetting->endpoint[4].desc.wMaxPacketSize = __constant_cpu_to_le16(32);
+
+	// Use alternative setting 3 on interface 0 to have 2B+D
+	if ((status = usb_set_interface (dev, 0, 3)) < 0) {
+		WARN("usb_set_interface failed,status=%d",status);
+		return status;
+	}
+
+	// Allocate URB for control endpoint
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		return -ENOMEM;
+	}
+	ctrl->urb = urb;
+	
+	// Fill the control URB
+	usb_fill_control_urb (urb, dev, 
+			  usb_sndctrlpipe(dev, 0),
+			  NULL, NULL, 0, usb_ctrl_complete, adapter);
+
+		
+	fifo_init(&ctrl->msg_fifo.f, ARRAY_SIZE(ctrl->msg_fifo.data));
+
+	// Allocate URBs and buffers for interrupt endpoint
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) { 
+		return -ENOMEM;
+	}
+	intr->urb = urb;
+	
+	buf = kmalloc(INT_PKT_SIZE, GFP_KERNEL);
+	if (!buf) {
+		return -ENOMEM;
+	}
+
+	endpoint = &altsetting->endpoint[EP_INT-1];
+				
+	// Fill the interrupt URB
+	usb_fill_int_urb(urb, dev,
+		     usb_rcvintpipe(dev, endpoint->desc.bEndpointAddress),
+		     buf, INT_PKT_SIZE,
+		     usb_int_complete, adapter,
+		     endpoint->desc.bInterval);
+		
+	return 0;
+}
+
+/*
+ * Release buffers and URBs for the interrupt and control
+ * endpoint.
+ */
+void st5481_release_usb(struct st5481_adapter *adapter)
+{
+	struct st5481_intr *intr = &adapter->intr;
+	struct st5481_ctrl *ctrl = &adapter->ctrl;
+
+	DBG(1,"");
+
+	// Stop and free Control and Interrupt URBs
+	usb_unlink_urb(ctrl->urb);
+	if (ctrl->urb->transfer_buffer)
+		kfree(ctrl->urb->transfer_buffer);
+	usb_free_urb(ctrl->urb);
+
+	usb_unlink_urb(intr->urb);
+	if (intr->urb->transfer_buffer)
+		kfree(intr->urb->transfer_buffer);
+	usb_free_urb(intr->urb);
+}
+
+/*
+ *  Initialize the adapter.
+ */
+void st5481_start(struct st5481_adapter *adapter)
+{
+	static const u8 init_cmd_table[]={
+		SET_DEFAULT,0,
+		STT,0,
+		SDA_MIN,0x0d,
+		SDA_MAX,0x29,
+		SDELAY_VALUE,0x14,
+		GPIO_DIR,0x01,		
+		GPIO_OUT,RED_LED,
+//		FFCTRL_OUT_D,4,
+//		FFCTRH_OUT_D,12,
+		FFCTRL_OUT_B1,6,
+		FFCTRH_OUT_B1,20,
+		FFCTRL_OUT_B2,6,
+		FFCTRH_OUT_B2,20,
+		MPMSK,RXCI_INT+DEN_INT+DCOLL_INT,
+		0
+	};	
+	struct st5481_intr *intr = &adapter->intr;
+	int i = 0;
+	u8 request,value;
+
+	DBG(8,"");
+
+	adapter->leds = RED_LED; 
+
+	// Start receiving on the interrupt endpoint
+	SUBMIT_URB(intr->urb, GFP_KERNEL); 
+
+	while ((request = init_cmd_table[i++])) {
+		value = init_cmd_table[i++];
+		st5481_usb_device_ctrl_msg(adapter, request, value, NULL, NULL);
+	}
+	st5481_ph_command(adapter, ST5481_CMD_PUP);
+}
+
+/*
+ * Reset the adapter to default values.
+ */
+void st5481_stop(struct st5481_adapter *adapter)
+{
+	DBG(8,"");
+
+	st5481_usb_device_ctrl_msg(adapter, SET_DEFAULT, 0, NULL, NULL);
+}
+
+/* ======================================================================
+ * isochronous USB  helpers
+ */
+
+static void
+fill_isoc_urb(struct urb *urb, struct usb_device *dev,
+	      unsigned int pipe, void *buf, int num_packets, 
+	      int packet_size, usb_complete_t complete,
+	      void *context) 
+{
+	int k;
+
+	spin_lock_init(&urb->lock);
+	urb->dev=dev;
+	urb->pipe=pipe;
+	urb->transfer_buffer=buf;
+	urb->number_of_packets = num_packets;
+	urb->transfer_buffer_length=num_packets*packet_size;
+	urb->actual_length = 0;
+	urb->complete=complete;
+	urb->context=context;
+	urb->transfer_flags=URB_ISO_ASAP;
+	for (k = 0; k < num_packets; k++) {
+		urb->iso_frame_desc[k].offset = packet_size * k;
+		urb->iso_frame_desc[k].length = packet_size;
+		urb->iso_frame_desc[k].actual_length = 0;
+	}
+}
+
+int
+st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev, 
+			   unsigned int pipe, int num_packets,
+			   int packet_size, int buf_size,
+			   usb_complete_t complete, void *context)
+{
+	int j, retval;
+	unsigned char *buf;
+
+	for (j = 0; j < 2; j++) {
+		retval = -ENOMEM;
+		urb[j] = usb_alloc_urb(num_packets, GFP_KERNEL);
+		if (!urb[j])
+			goto err;
+
+		// Allocate memory for 2000bytes/sec (16Kb/s)
+		buf = kmalloc(buf_size, GFP_KERNEL);
+		if (!buf)
+			goto err;
+			
+		// Fill the isochronous URB
+		fill_isoc_urb(urb[j], dev, pipe, buf, 
+			      num_packets, packet_size, complete,
+			      context);
+	}
+	return 0;
+
+ err:
+	for (j = 0; j < 2; j++) {
+		if (urb[j]) {
+			if (urb[j]->transfer_buffer)
+				kfree(urb[j]->transfer_buffer);
+			usb_free_urb(urb[j]);
+		}
+	}
+	return retval;
+}
+
+void st5481_release_isocpipes(struct urb* urb[2])
+{
+	int j;
+
+	for (j = 0; j < 2; j++) {
+		usb_unlink_urb(urb[j]);
+		if (urb[j]->transfer_buffer)
+			kfree(urb[j]->transfer_buffer);			
+		usb_free_urb(urb[j]);
+	}
+}
+
+/*
+ * Decode frames received on the B/D channel.
+ * Note that this function will be called continously
+ * with 64Kbit/s / 16Kbit/s of data and hence it will be 
+ * called 50 times per second with 20 ISOC descriptors. 
+ * Called at interrupt.
+ */
+static void usb_in_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct st5481_in *in = urb->context;
+	unsigned char *ptr;
+	struct sk_buff *skb;
+	int len, count, status;
+
+	if (unlikely(urb->status < 0)) {
+		if (urb->status != -ENOENT && urb->status != -ESHUTDOWN) {
+			WARN("urb status %d",urb->status);
+		} else {
+			DBG(1,"urb killed");
+			return; // Give up
+		}
+	}
+
+	DBG_ISO_PACKET(0x80,urb);
+
+	len = st5481_isoc_flatten(urb);
+	ptr = urb->transfer_buffer;
+	while (len > 0) {
+		if (in->mode == L1_MODE_TRANS) {
+			memcpy(in->rcvbuf, ptr, len);
+			status = len;
+			len = 0;
+		} else {
+			status = isdnhdlc_decode(&in->hdlc_state, ptr, len, &count,
+				in->rcvbuf, in->bufsize);
+			ptr += count;
+			len -= count;
+		}
+		
+		if (status > 0) {
+			// Good frame received
+			DBG(4,"count=%d",status);
+			DBG_PACKET(0x400, in->rcvbuf, status);
+			if (!(skb = dev_alloc_skb(status))) {
+				WARN("receive out of memory\n");
+				break;
+			}
+			memcpy(skb_put(skb, status), in->rcvbuf, status);
+			in->hisax_if->l1l2(in->hisax_if, PH_DATA | INDICATION, skb);
+		} else if (status == -HDLC_CRC_ERROR) {
+			INFO("CRC error");
+		} else if (status == -HDLC_FRAMING_ERROR) {
+			INFO("framing error");
+		} else if (status == -HDLC_LENGTH_ERROR) {
+			INFO("length error");
+		}
+	}
+
+	// Prepare URB for next transfer
+	urb->dev = in->adapter->usb_dev;
+	urb->actual_length = 0;
+
+	SUBMIT_URB(urb, GFP_ATOMIC);
+}
+
+int st5481_setup_in(struct st5481_in *in)
+{
+	struct usb_device *dev = in->adapter->usb_dev;
+	int retval;
+
+	DBG(4,"");
+
+	in->rcvbuf = kmalloc(in->bufsize, GFP_KERNEL);
+	retval = -ENOMEM;
+	if (!in->rcvbuf)
+		goto err;
+
+	retval = st5481_setup_isocpipes(in->urb, dev, 
+					usb_rcvisocpipe(dev, in->ep),
+					in->num_packets,  in->packet_size,
+					in->num_packets * in->packet_size,
+					usb_in_complete, in);
+	if (retval)
+		goto err_free;
+	return 0;
+
+ err_free:
+	kfree(in->rcvbuf);
+ err:
+	return retval;
+}
+
+void st5481_release_in(struct st5481_in *in)
+{
+	DBG(2,"");
+
+	st5481_release_isocpipes(in->urb);
+}
+
+/*
+ * Make the transfer_buffer contiguous by
+ * copying from the iso descriptors if necessary. 
+ */
+int st5481_isoc_flatten(struct urb *urb)
+{
+	struct usb_iso_packet_descriptor *pipd,*pend;
+	unsigned char *src,*dst;
+	unsigned int len;
+	
+	if (urb->status < 0) {
+		return urb->status;
+	}
+	for (pipd = &urb->iso_frame_desc[0],
+		     pend = &urb->iso_frame_desc[urb->number_of_packets],
+		     dst = urb->transfer_buffer; 
+	     pipd < pend; 
+	     pipd++) {
+		
+		if (pipd->status < 0) {
+			return (pipd->status);
+		}
+	
+		len = pipd->actual_length;
+		pipd->actual_length = 0;
+		src = urb->transfer_buffer+pipd->offset;
+
+		if (src != dst) {
+			// Need to copy since isoc buffers not full
+			while (len--) {
+				*dst++ = *src++;
+			}			
+		} else {
+			// No need to copy, just update destination buffer
+			dst += len;
+		}
+	}
+	// Return size of flattened buffer
+	return (dst - (unsigned char *)urb->transfer_buffer);
+}
+
+static void st5481_start_rcv(void *context)
+{
+	struct st5481_in *in = context;
+	struct st5481_adapter *adapter = in->adapter;
+
+	DBG(4,"");
+
+	in->urb[0]->dev = adapter->usb_dev;
+	SUBMIT_URB(in->urb[0], GFP_KERNEL);
+
+	in->urb[1]->dev = adapter->usb_dev;
+	SUBMIT_URB(in->urb[1], GFP_KERNEL);
+}
+
+void st5481_in_mode(struct st5481_in *in, int mode)
+{
+	if (in->mode == mode)
+		return;
+
+	in->mode = mode;
+
+	usb_unlink_urb(in->urb[0]);
+	usb_unlink_urb(in->urb[1]);
+
+	if (in->mode != L1_MODE_NULL) {
+		if (in->mode != L1_MODE_TRANS)
+			isdnhdlc_rcv_init(&in->hdlc_state,
+				in->mode == L1_MODE_HDLC_56K);
+		
+		st5481_usb_pipe_reset(in->adapter, in->ep, NULL, NULL);
+		st5481_usb_device_ctrl_msg(in->adapter, in->counter,
+					   in->packet_size,
+					   NULL, NULL);
+		st5481_start_rcv(in);
+	} else {
+		st5481_usb_device_ctrl_msg(in->adapter, in->counter,
+					   0, NULL, NULL);
+	}
+}
+
diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c
new file mode 100644
index 0000000..082726d
--- /dev/null
+++ b/drivers/isdn/hisax/tei.c
@@ -0,0 +1,466 @@
+/* $Id: tei.c,v 2.20.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl2.h"
+#include <linux/init.h>
+#include <linux/random.h>
+
+const char *tei_revision = "$Revision: 2.20.2.3 $";
+
+#define ID_REQUEST	1
+#define ID_ASSIGNED	2
+#define ID_DENIED	3
+#define ID_CHK_REQ	4
+#define ID_CHK_RES	5
+#define ID_REMOVE	6
+#define ID_VERIFY	7
+
+#define TEI_ENTITY_ID	0xf
+
+static struct Fsm teifsm;
+
+void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb);
+
+enum {
+	ST_TEI_NOP,
+	ST_TEI_IDREQ,
+	ST_TEI_IDVERIFY,
+};
+
+#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1)
+
+static char *strTeiState[] =
+{
+	"ST_TEI_NOP",
+	"ST_TEI_IDREQ",
+	"ST_TEI_IDVERIFY",
+};
+
+enum {
+	EV_IDREQ,
+	EV_ASSIGN,
+	EV_DENIED,
+	EV_CHKREQ,
+	EV_REMOVE,
+	EV_VERIFY,
+	EV_T202,
+};
+
+#define TEI_EVENT_COUNT (EV_T202+1)
+
+static char *strTeiEvent[] =
+{
+	"EV_IDREQ",
+	"EV_ASSIGN",
+	"EV_DENIED",
+	"EV_CHKREQ",
+	"EV_REMOVE",
+	"EV_VERIFY",
+	"EV_T202",
+};
+
+unsigned int
+random_ri(void)
+{
+	unsigned int x;
+
+	get_random_bytes(&x, sizeof(x));
+	return (x & 0xffff);
+}
+
+static struct PStack *
+findtei(struct PStack *st, int tei)
+{
+	struct PStack *ptr = *(st->l1.stlistp);
+
+	if (tei == 127)
+		return (NULL);
+
+	while (ptr)
+		if (ptr->l2.tei == tei)
+			return (ptr);
+		else
+			ptr = ptr->next;
+	return (NULL);
+}
+
+static void
+put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei)
+{
+	struct sk_buff *skb;
+	u_char *bp;
+
+	if (!(skb = alloc_skb(8, GFP_ATOMIC))) {
+		printk(KERN_WARNING "HiSax: No skb for TEI manager\n");
+		return;
+	}
+	bp = skb_put(skb, 3);
+	bp[0] = (TEI_SAPI << 2);
+	bp[1] = (GROUP_TEI << 1) | 0x1;
+	bp[2] = UI;
+	bp = skb_put(skb, 5);
+	bp[0] = TEI_ENTITY_ID;
+	bp[1] = ri >> 8;
+	bp[2] = ri & 0xff;
+	bp[3] = m_id;
+	bp[4] = (tei << 1) | 1;
+	st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+}
+
+static void
+tei_id_request(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (st->l2.tei != -1) {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request for allready asigned tei %d",
+			st->l2.tei);
+		return;
+	}
+	st->ma.ri = random_ri();
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"assign request ri %d", st->ma.ri);
+	put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1);
+	st->ma.N202 = 3;
+}
+
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *ost, *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int ri, tei;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity assign ri %d tei %d", ri, tei);
+	if ((ost = findtei(st, tei))) {	/* same tei is in use */
+		if (ri != ost->ma.ri) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"possible duplicate assignment tei %d", tei);
+			ost->l2.l2tei(ost, MDL_ERROR | RESPONSE, NULL);
+		}
+	} else if (ri == st->ma.ri) {
+		FsmDelTimer(&st->ma.t202, 1);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+	}
+}
+
+static void
+tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *ost, *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int tei, ri;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"foreign identity assign ri %d tei %d", ri, tei);
+	if ((ost = findtei(st, tei))) {	/* same tei is in use */
+		if (ri != ost->ma.ri) {	/* and it wasn't our request */
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"possible duplicate assignment tei %d", tei);
+			FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL);
+		}
+	} 
+}
+
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int ri, tei;
+
+	ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity denied ri %d tei %d", ri, tei);
+}
+
+static void
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	int tei;
+
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity check req tei %d", tei);
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 4);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei);
+	}
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct sk_buff *skb = arg;
+	struct IsdnCardState *cs;
+	int tei;
+
+	tei = skb->data[4] >> 1;
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"identity remove tei %d", tei);
+	if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+		FsmDelTimer(&st->ma.t202, 5);
+		FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, NULL);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+	}
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+
+	if (st->ma.debug)
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"id verify request for tei %d", st->l2.tei);
+	put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+	FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY);
+	FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2);
+	st->ma.N202 = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		st->ma.ri = random_ri();
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"assign req(%d) ri %d", 4 - st->ma.N202,
+				st->ma.ri);
+		put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
+		st->l3.l3l2(st, MDL_ERROR | RESPONSE, NULL);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct PStack *st = fi->userdata;
+	struct IsdnCardState *cs;
+
+	if (--st->ma.N202) {
+		if (st->ma.debug)
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"id verify req(%d) for tei %d",
+				3 - st->ma.N202, st->l2.tei);
+		put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+		FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4);
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"verify req for tei %d failed", st->l2.tei);
+		st->l3.l3l2(st, MDL_REMOVE | REQUEST, NULL);
+		cs = (struct IsdnCardState *) st->l1.hardware;
+		cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+		FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_l1l2(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	int mt;
+
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	if (pr == (PH_DATA | INDICATION)) {
+		if (skb->len < 3) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"short mgr frame %ld/3", skb->len);
+		} else if ((skb->data[0] != ((TEI_SAPI << 2) | 2)) ||
+			   (skb->data[1] != ((GROUP_TEI << 1) | 1))) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"wrong mgr sapi/tei %x/%x",
+				skb->data[0], skb->data[1]);
+		} else if ((skb->data[2] & 0xef) != UI) {
+			st->ma.tei_m.printdebug(&st->ma.tei_m,
+				"mgr frame is not ui %x", skb->data[2]);
+		} else {
+			skb_pull(skb, 3);
+			if (skb->len < 5) {
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"short mgr frame %ld/5", skb->len);
+			} else if (skb->data[0] != TEI_ENTITY_ID) {
+				/* wrong management entity identifier, ignore */
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"tei handler wrong entity id %x",
+					skb->data[0]);
+			} else {
+				mt = skb->data[3];
+				if (mt == ID_ASSIGNED)
+					FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb);
+				else if (mt == ID_DENIED)
+					FsmEvent(&st->ma.tei_m, EV_DENIED, skb);
+				else if (mt == ID_CHK_REQ)
+					FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb);
+				else if (mt == ID_REMOVE)
+					FsmEvent(&st->ma.tei_m, EV_REMOVE, skb);
+				else {
+					st->ma.tei_m.printdebug(&st->ma.tei_m,
+						"tei handler wrong mt %x\n", mt);
+				}
+			}
+		}
+	} else {
+		st->ma.tei_m.printdebug(&st->ma.tei_m,
+			"tei handler wrong pr %x\n", pr);
+	}
+	dev_kfree_skb(skb);
+}
+
+static void
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs;
+
+	if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+		if (pr == (MDL_ASSIGN | INDICATION)) {
+			if (st->ma.debug)
+				st->ma.tei_m.printdebug(&st->ma.tei_m,
+					"fixed assign tei %d", st->l2.tei);
+			st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+			cs = (struct IsdnCardState *) st->l1.hardware;
+			cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+		}
+		return;
+	}
+	switch (pr) {
+		case (MDL_ASSIGN | INDICATION):
+			FsmEvent(&st->ma.tei_m, EV_IDREQ, arg);
+			break;
+		case (MDL_ERROR | REQUEST):
+			FsmEvent(&st->ma.tei_m, EV_VERIFY, arg);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	va_list args;
+	struct PStack *st = fi->userdata;
+
+	va_start(args, fmt);
+	VHiSax_putstatus(st->l1.hardware, "tei ", fmt, args);
+	va_end(args);
+}
+
+void
+setstack_tei(struct PStack *st)
+{
+	st->l2.l2tei = tei_l2tei;
+	st->ma.T202 = 2000;	/* T202  2000 milliseconds */
+	st->l1.l1tei = tei_l1l2;
+	st->ma.debug = 1;
+	st->ma.tei_m.fsm = &teifsm;
+	st->ma.tei_m.state = ST_TEI_NOP;
+	st->ma.tei_m.debug = 1;
+	st->ma.tei_m.userdata = st;
+	st->ma.tei_m.userint = 0;
+	st->ma.tei_m.printdebug = tei_debug;
+	FsmInitTimer(&st->ma.tei_m, &st->ma.t202);
+}
+
+void
+init_tei(struct IsdnCardState *cs, int protocol)
+{
+}
+
+void
+release_tei(struct IsdnCardState *cs)
+{
+	struct PStack *st = cs->stlist;
+
+	while (st) {
+		FsmDelTimer(&st->ma.t202, 1);
+		st = st->next;
+	}
+}
+
+static struct FsmNode TeiFnList[] __initdata =
+{
+	{ST_TEI_NOP, EV_IDREQ, tei_id_request},
+	{ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
+	{ST_TEI_NOP, EV_VERIFY, tei_id_verify},
+	{ST_TEI_NOP, EV_REMOVE, tei_id_remove},
+	{ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req},
+	{ST_TEI_IDREQ, EV_T202, tei_id_req_tout},
+	{ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
+	{ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
+	{ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout},
+	{ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
+	{ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
+};
+
+#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode))
+
+int __init
+TeiNew(void)
+{
+	teifsm.state_count = TEI_STATE_COUNT;
+	teifsm.event_count = TEI_EVENT_COUNT;
+	teifsm.strEvent = strTeiEvent;
+	teifsm.strState = strTeiState;
+	return FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
+}
+
+void
+TeiFree(void)
+{
+	FsmFree(&teifsm);
+}
diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c
new file mode 100644
index 0000000..ef8984c5
--- /dev/null
+++ b/drivers/isdn/hisax/teleint.c
@@ -0,0 +1,339 @@
+/* $Id: teleint.c,v 1.16.2.5 2004/01/19 15:31:50 keil Exp $
+ *
+ * low level stuff for TeleInt isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hfc_2bs0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+const char *TeleInt_revision = "$Revision: 1.16.2.5 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+	register u_char ret;
+	int max_delay = 2000;
+
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inactive\n");
+		return (0);
+	}
+	ret = bytein(adr);
+	return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	register int max_delay = 20000;
+	register int i;
+	
+	byteout(ale, off);
+	for (i = 0; i<size; i++) {
+		ret = HFC_BUSY & bytein(ale);
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inactive\n");
+			return;
+		}
+		data[i] = bytein(adr);
+	}
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+	register u_char ret;
+	int max_delay = 2000;
+
+	byteout(ale, off);
+	ret = HFC_BUSY & bytein(ale);
+	while (ret && --max_delay)
+		ret = HFC_BUSY & bytein(ale);
+	if (!max_delay) {
+		printk(KERN_WARNING "TeleInt Busy not inactive\n");
+		return;
+	}
+	byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+	register u_char ret;
+	register int max_delay = 20000;
+	register int i;
+	
+	byteout(ale, off);
+	for (i = 0; i<size; i++) {
+		ret = HFC_BUSY & bytein(ale);
+		while (ret && --max_delay)
+			ret = HFC_BUSY & bytein(ale);
+		if (!max_delay) {
+			printk(KERN_WARNING "TeleInt Busy not inactive\n");
+			return;
+		}
+		byteout(adr, data[i]);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	cs->hw.hfc.cip = offset;
+	return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	cs->hw.hfc.cip = offset;
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	cs->hw.hfc.cip = 0;
+	writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static u_char
+ReadHFC(struct IsdnCardState *cs, int data, u_char reg)
+{
+	register u_char ret;
+
+	if (data) {
+		cs->hw.hfc.cip = reg;
+		byteout(cs->hw.hfc.addr | 1, reg);
+		ret = bytein(cs->hw.hfc.addr);
+		if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+			debugl1(cs, "hfc RD %02x %02x", reg, ret);
+	} else
+		ret = bytein(cs->hw.hfc.addr | 1);
+	return (ret);
+}
+
+static void
+WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+	byteout(cs->hw.hfc.addr | 1, reg);
+	cs->hw.hfc.cip = reg;
+	if (data)
+		byteout(cs->hw.hfc.addr, value);
+	if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+		debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
+}
+
+static irqreturn_t
+TeleInt_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+	if (val) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF);
+	writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+TeleInt_Timer(struct IsdnCardState *cs)
+{
+	int stat = 0;
+	u_long flags;
+	
+	spin_lock_irqsave(&cs->lock, flags);
+	if (cs->bcs[0].mode) {
+		stat |= 1;
+		main_irq_hfc(&cs->bcs[0]);
+	}
+	if (cs->bcs[1].mode) {
+		stat |= 2;
+		main_irq_hfc(&cs->bcs[1]);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	stat = HZ/100;
+	if (!stat)
+		stat = 1;
+	cs->hw.hfc.timer.expires = jiffies + stat;
+	add_timer(&cs->hw.hfc.timer);
+}
+
+void
+release_io_TeleInt(struct IsdnCardState *cs)
+{
+	del_timer(&cs->hw.hfc.timer);
+	releasehfc(cs);
+	if (cs->hw.hfc.addr)
+		release_region(cs->hw.hfc.addr, 2);
+}
+
+static void
+reset_TeleInt(struct IsdnCardState *cs)
+{
+	printk(KERN_INFO "TeleInt: resetting card\n");
+	cs->hw.hfc.cirm |= HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset On */
+	mdelay(10);
+	cs->hw.hfc.cirm &= ~HFC_RESET;
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);	/* Reset Off */
+	mdelay(10);
+}
+
+static int
+TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+	int delay;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_TeleInt(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_TeleInt(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_TeleInt(cs);
+			inithfc(cs);
+			clear_pending_isac_ints(cs);
+			initisac(cs);
+			/* Reenable all IRQ */
+			cs->writeisac(cs, ISAC_MASK, 0);
+			cs->writeisac(cs, ISAC_CMDR, 0x41);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			delay = HZ/100;
+			if (!delay)
+				delay = 1;
+			cs->hw.hfc.timer.expires = jiffies + delay;
+			add_timer(&cs->hw.hfc.timer);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_TeleInt(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, TeleInt_revision);
+	printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELEINT)
+		return (0);
+
+	cs->hw.hfc.addr = card->para[1] & 0x3fe;
+	cs->irq = card->para[0];
+	cs->hw.hfc.cirm = HFC_CIRM;
+	cs->hw.hfc.isac_spcr = 0x00;
+	cs->hw.hfc.cip = 0;
+	cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER;
+	cs->bcs[0].hw.hfc.send = NULL;
+	cs->bcs[1].hw.hfc.send = NULL;
+	cs->hw.hfc.fifosize = 7 * 1024 + 512;
+	cs->hw.hfc.timer.function = (void *) TeleInt_Timer;
+	cs->hw.hfc.timer.data = (long) cs;
+	init_timer(&cs->hw.hfc.timer);
+	if (!request_region(cs->hw.hfc.addr, 2, "TeleInt isdn")) {
+		printk(KERN_WARNING
+		       "HiSax: %s config port %x-%x already in use\n",
+		       CardType[card->typ],
+		       cs->hw.hfc.addr,
+		       cs->hw.hfc.addr + 2);
+		return (0);
+	}
+	/* HW IO = IO */
+	byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff);
+	byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54);
+	switch (cs->irq) {
+		case 3:
+			cs->hw.hfc.cirm |= HFC_INTA;
+			break;
+		case 4:
+			cs->hw.hfc.cirm |= HFC_INTB;
+			break;
+		case 5:
+			cs->hw.hfc.cirm |= HFC_INTC;
+			break;
+		case 7:
+			cs->hw.hfc.cirm |= HFC_INTD;
+			break;
+		case 10:
+			cs->hw.hfc.cirm |= HFC_INTE;
+			break;
+		case 11:
+			cs->hw.hfc.cirm |= HFC_INTF;
+			break;
+		default:
+			printk(KERN_WARNING "TeleInt: wrong IRQ\n");
+			release_io_TeleInt(cs);
+			return (0);
+	}
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);
+	byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt);
+
+	printk(KERN_INFO "TeleInt: defined at 0x%x IRQ %d\n",
+		cs->hw.hfc.addr, cs->irq);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHFC;
+	cs->BC_Write_Reg = &WriteHFC;
+	cs->cardmsg = &TeleInt_card_msg;
+	cs->irq_func = &TeleInt_interrupt;
+	ISACVersion(cs, "TeleInt:");
+	return (1);
+}
diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c
new file mode 100644
index 0000000..5ec5ec3
--- /dev/null
+++ b/drivers/isdn/hisax/teles0.c
@@ -0,0 +1,367 @@
+/* $Id: teles0.c,v 2.15.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Teles Memory IO isdn cards
+ *
+ * Author       Karsten Keil
+ *              based on the teles driver from Jan den Ouden
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "hscx.h"
+
+extern const char *CardType[];
+
+const char *teles0_revision = "$Revision: 2.15.2.4 $";
+
+#define TELES_IOMEM_SIZE	0x400
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readisac(void __iomem *adr, u_char off)
+{
+	return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off);
+}
+
+static inline void
+writeisac(void __iomem *adr, u_char off, u_char data)
+{
+	writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
+}
+
+
+static inline u_char
+readhscx(void __iomem *adr, int hscx, u_char off)
+{
+	return readb(adr + (hscx ? 0x1c0 : 0x180) +
+		     ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+writehscx(void __iomem *adr, int hscx, u_char off, u_char data)
+{
+	writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
+	       ((off & 1) ? 0x1ff : 0) + off); mb();
+}
+
+static inline void
+read_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register int i;
+	register u_char __iomem *ad = adr + 0x100;
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register int i;
+	register u_char __iomem *ad = adr + 0x100;
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
+	}
+}
+
+static inline void
+read_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	register int i;
+	register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
+	for (i = 0; i < size; i++)
+		data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	int i;
+	register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
+	for (i = 0; i < size; i++) {
+		writeb(data[i], ad); mb();
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+teles0_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+	int count = 0;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	count++;
+	val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (val && count < 5) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if (val && count < 5) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_teles0(struct IsdnCardState *cs)
+{
+	if (cs->hw.teles0.cfg_reg)
+		release_region(cs->hw.teles0.cfg_reg, 8);
+	iounmap(cs->hw.teles0.membase);
+	release_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
+}
+
+static int
+reset_teles0(struct IsdnCardState *cs)
+{
+	u_char cfval;
+
+	if (cs->hw.teles0.cfg_reg) {
+		switch (cs->irq) {
+			case 2:
+			case 9:
+				cfval = 0x00;
+				break;
+			case 3:
+				cfval = 0x02;
+				break;
+			case 4:
+				cfval = 0x04;
+				break;
+			case 5:
+				cfval = 0x06;
+				break;
+			case 10:
+				cfval = 0x08;
+				break;
+			case 11:
+				cfval = 0x0A;
+				break;
+			case 12:
+				cfval = 0x0C;
+				break;
+			case 15:
+				cfval = 0x0E;
+				break;
+			default:
+				return(1);
+		}
+		cfval |= ((cs->hw.teles0.phymem >> 9) & 0xF0);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval);
+		HZDELAY(HZ / 10 + 1);
+		byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
+		HZDELAY(HZ / 10 + 1);
+	}
+	writeb(0, cs->hw.teles0.membase + 0x80); mb();
+	HZDELAY(HZ / 5 + 1);
+	writeb(1, cs->hw.teles0.membase + 0x80); mb();
+	HZDELAY(HZ / 5 + 1);
+	return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_teles0(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles0(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+int __init
+setup_teles0(struct IsdnCard *card)
+{
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, teles0_revision);
+	printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0))
+		return (0);
+
+	if (cs->typ == ISDN_CTYPE_16_0)
+		cs->hw.teles0.cfg_reg = card->para[2];
+	else			/* 8.0 */
+		cs->hw.teles0.cfg_reg = 0;
+
+	if (card->para[1] < 0x10000) {
+		card->para[1] <<= 4;
+		printk(KERN_INFO
+		   "Teles0: membase configured DOSish, assuming 0x%lx\n",
+		       (unsigned long) card->para[1]);
+	}
+	cs->irq = card->para[0];
+	if (cs->hw.teles0.cfg_reg) {
+		if (!request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg")) {
+			printk(KERN_WARNING
+			  "HiSax: %s config port %x-%x already in use\n",
+			       CardType[card->typ],
+			       cs->hw.teles0.cfg_reg,
+			       cs->hw.teles0.cfg_reg + 8);
+			return (0);
+		}
+	}
+	if (cs->hw.teles0.cfg_reg) {
+		if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       cs->hw.teles0.cfg_reg + 0, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
+			return (0);
+		}
+		if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       cs->hw.teles0.cfg_reg + 1, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
+			return (0);
+		}
+		val = bytein(cs->hw.teles0.cfg_reg + 2);	/* 0x1e=without AB
+								   * 0x1f=with AB
+								   * 0x1c 16.3 ???
+								 */
+		if (val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+			       cs->hw.teles0.cfg_reg + 2, val);
+			release_region(cs->hw.teles0.cfg_reg, 8);
+			return (0);
+		}
+	}
+	/* 16.0 and 8.0 designed for IOM1 */
+	test_and_set_bit(HW_IOM1, &cs->HW_Flags);
+	cs->hw.teles0.phymem = card->para[1];
+	if (!request_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE, "teles iomem")) {
+		printk(KERN_WARNING
+			"HiSax: %s memory region %lx-%lx already in use\n",
+			CardType[card->typ],
+			cs->hw.teles0.phymem,
+			cs->hw.teles0.phymem + TELES_IOMEM_SIZE);
+		if (cs->hw.teles0.cfg_reg)
+			release_region(cs->hw.teles0.cfg_reg, 8);
+		return (0);
+	}
+	cs->hw.teles0.membase = ioremap(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:%p cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase, cs->hw.teles0.cfg_reg);
+	if (reset_teles0(cs)) {
+		printk(KERN_WARNING "Teles0: wrong IRQ\n");
+		release_io_teles0(cs);
+		return (0);
+	}
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	cs->irq_func = &teles0_interrupt;
+	ISACVersion(cs, "Teles0:");
+	if (HscxVersion(cs, "Teles0:")) {
+		printk(KERN_WARNING
+		 "Teles0: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_teles0(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c
new file mode 100644
index 0000000..c5b1f65
--- /dev/null
+++ b/drivers/isdn/hisax/teles3.c
@@ -0,0 +1,499 @@
+/* $Id: teles3.c,v 2.19.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Teles 16.3 & PNP isdn cards
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *              Beat Doebeli
+ *
+ */
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *teles3_revision = "$Revision: 2.19.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+	return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+	byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+	insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+	outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readreg(cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo(cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo(cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readreg(cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writereg(cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+teles3_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 5
+	struct IsdnCardState *cs = dev_id;
+	u_char val;
+	u_long flags;
+	int count = 0;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
+      Start_HSCX:
+	if (val)
+		hscx_int_main(cs, val);
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
+      Start_ISAC:
+	if (val)
+		isac_interrupt(cs, val);
+	count++;
+	val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_HSCX)
+			debugl1(cs, "HSCX IntStat after IntRoutine");
+		goto Start_HSCX;
+	}
+	val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
+	if (val && count < MAXCOUNT) {
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "ISAC IntStat after IntRoutine");
+		goto Start_ISAC;
+	}
+	if (count >= MAXCOUNT)
+		printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count);
+	writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+	writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+	writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0);
+	writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+	writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+inline static void
+release_ioregs(struct IsdnCardState *cs, int mask)
+{
+	if (mask & 1)
+		release_region(cs->hw.teles3.isac + 32, 32);
+	if (mask & 2)
+		release_region(cs->hw.teles3.hscx[0] + 32, 32);
+	if (mask & 4)
+		release_region(cs->hw.teles3.hscx[1] + 32, 32);
+}
+
+void
+release_io_teles3(struct IsdnCardState *cs)
+{
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		release_region(cs->hw.teles3.hscx[1], 96);
+	} else {
+		if (cs->hw.teles3.cfg_reg) {
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				release_region(cs->hw.teles3.cfg_reg, 1);
+			} else {
+				release_region(cs->hw.teles3.cfg_reg, 8);
+			}
+		}
+		release_ioregs(cs, 0x7);
+	}
+}
+
+static int
+reset_teles3(struct IsdnCardState *cs)
+{
+	u_char irqcfg;
+
+	if (cs->typ != ISDN_CTYPE_TELESPCMCIA) {
+		if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+			switch (cs->irq) {
+				case 2:
+				case 9:
+					irqcfg = 0x00;
+					break;
+				case 3:
+					irqcfg = 0x02;
+					break;
+				case 4:
+					irqcfg = 0x04;
+					break;
+				case 5:
+					irqcfg = 0x06;
+					break;
+				case 10:
+					irqcfg = 0x08;
+					break;
+				case 11:
+					irqcfg = 0x0A;
+					break;
+				case 12:
+					irqcfg = 0x0C;
+					break;
+				case 15:
+					irqcfg = 0x0E;
+					break;
+				default:
+					return(1);
+			}
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg);
+			HZDELAY(HZ / 10 + 1);
+			byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1);
+			HZDELAY(HZ / 10 + 1);
+		} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+			byteout(cs->hw.teles3.cfg_reg, 0xff);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.cfg_reg, 0x00);
+			HZDELAY(2);
+		} else {
+			/* Reset off for 16.3 PnP , thanks to Georg Acher */
+			byteout(cs->hw.teles3.isac + 0x3c, 0);
+			HZDELAY(2);
+			byteout(cs->hw.teles3.isac + 0x3c, 1);
+			HZDELAY(2);
+		}
+	}
+	return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			spin_lock_irqsave(&cs->lock, flags);
+			reset_teles3(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_RELEASE:
+			release_io_teles3(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+#ifdef __ISAPNP__
+
+static struct isapnp_device_id teles_ids[] __devinitdata = {
+	{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110),
+	  ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110), 
+	  (unsigned long) "Teles 16.3 PnP" },
+	{ ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0),
+	  ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0), 
+	  (unsigned long) "Creatix 16.3 PnP" },
+	{ ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002),
+	  ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002), 
+	  (unsigned long) "Compaq ISDN S0" },
+	{ 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &teles_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_teles3(struct IsdnCard *card)
+{
+	u_char val;
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+	strcpy(tmp, teles3_revision);
+	printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
+	if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP)
+	    && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA))
+		return (0);
+
+#ifdef __ISAPNP__
+	if (!card->para[1] && isapnp_present()) {
+		struct pnp_dev *pnp_d;
+		while(ipid->card_vendor) {
+			if ((pnp_c = pnp_find_card(ipid->card_vendor,
+				ipid->card_device, pnp_c))) {
+				pnp_d = NULL;
+				if ((pnp_d = pnp_find_dev(pnp_c,
+					ipid->vendor, ipid->function, pnp_d))) {
+					int err;
+
+					printk(KERN_INFO "HiSax: %s detected\n",
+						(char *)ipid->driver_data);
+					pnp_disable_dev(pnp_d);
+					err = pnp_activate_dev(pnp_d);
+					if (err<0) {
+						printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+							__FUNCTION__, err);
+						return(0);
+					}
+					card->para[3] = pnp_port_start(pnp_d, 2);
+					card->para[2] = pnp_port_start(pnp_d, 1);
+					card->para[1] = pnp_port_start(pnp_d, 0);
+					card->para[0] = pnp_irq(pnp_d, 0);
+					if (!card->para[0] || !card->para[1] || !card->para[2]) {
+						printk(KERN_ERR "Teles PnP:some resources are missing %ld/%lx/%lx\n",
+							card->para[0], card->para[1], card->para[2]);
+						pnp_disable_dev(pnp_d);
+						return(0);
+					}
+					break;
+				} else {
+					printk(KERN_ERR "Teles PnP: PnP error card found, no device\n");
+				}
+			}
+			ipid++;
+			pnp_c = NULL;
+		} 
+		if (!ipid->card_vendor) {
+			printk(KERN_INFO "Teles PnP: no ISAPnP card found\n");
+			return(0);
+		}
+	}
+#endif
+	if (cs->typ == ISDN_CTYPE_16_3) {
+		cs->hw.teles3.cfg_reg = card->para[1];
+		switch (cs->hw.teles3.cfg_reg) {
+			case 0x180:
+			case 0x280:
+			case 0x380:
+				cs->hw.teles3.cfg_reg |= 0xc00;
+				break;
+		}
+		cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420;
+		cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20;
+		cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820;
+	} else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		cs->hw.teles3.cfg_reg = 0;
+		cs->hw.teles3.hscx[0] = card->para[1] - 0x20;
+		cs->hw.teles3.hscx[1] = card->para[1];
+		cs->hw.teles3.isac = card->para[1] + 0x20;
+	} else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+		cs->hw.teles3.cfg_reg = card->para[3];
+		cs->hw.teles3.isac = card->para[2] - 32;
+		cs->hw.teles3.hscx[0] = card->para[1] - 32;
+		cs->hw.teles3.hscx[1] = card->para[1];
+	} else {	/* PNP */
+		cs->hw.teles3.cfg_reg = 0;
+		cs->hw.teles3.isac = card->para[1] - 32;
+		cs->hw.teles3.hscx[0] = card->para[2] - 32;
+		cs->hw.teles3.hscx[1] = card->para[2];
+	}
+	cs->irq = card->para[0];
+	cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+	cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+	cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+	if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+		if (!request_region(cs->hw.teles3.hscx[1], 96, "HiSax Teles PCMCIA")) {
+			printk(KERN_WARNING
+			       "HiSax: %s ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[1],
+			       cs->hw.teles3.hscx[1] + 96);
+			return (0);
+		}
+	} else {
+		if (cs->hw.teles3.cfg_reg) {
+			if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+				if (!request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg")) {
+					printk(KERN_WARNING
+						"HiSax: %s config port %x already in use\n",
+						CardType[card->typ],
+						cs->hw.teles3.cfg_reg);
+					return (0);
+				}
+			} else {
+				if (!request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg")) {
+					printk(KERN_WARNING
+					       "HiSax: %s config port %x-%x already in use\n",
+					       CardType[card->typ],
+					       cs->hw.teles3.cfg_reg,
+						cs->hw.teles3.cfg_reg + 8);
+					return (0);
+				}
+			}
+		}
+		if (!request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac")) {
+			printk(KERN_WARNING
+			   "HiSax: %s isac ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.isac + 32,
+			       cs->hw.teles3.isac + 64);
+			if (cs->hw.teles3.cfg_reg) {
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			}
+			return (0);
+		}
+		if (!request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A")) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx A ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[0] + 32,
+			       cs->hw.teles3.hscx[0] + 64);
+			if (cs->hw.teles3.cfg_reg) {
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			}
+			release_ioregs(cs, 1);
+			return (0);
+		}
+		if (!request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B")) {
+			printk(KERN_WARNING
+			 "HiSax: %s hscx B ports %x-%x already in use\n",
+			       CardType[cs->typ],
+			       cs->hw.teles3.hscx[1] + 32,
+			       cs->hw.teles3.hscx[1] + 64);
+			if (cs->hw.teles3.cfg_reg) {
+				if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+					release_region(cs->hw.teles3.cfg_reg, 1);
+				} else {
+					release_region(cs->hw.teles3.cfg_reg, 8);
+				}
+			}
+			release_ioregs(cs, 3);
+			return (0);
+		}
+	}
+	if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+		if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       cs->hw.teles3.cfg_reg + 0, val);
+			release_io_teles3(cs);
+			return (0);
+		}
+		if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       cs->hw.teles3.cfg_reg + 1, val);
+			release_io_teles3(cs);
+			return (0);
+		}
+		val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB
+							 * 0x1f=with AB
+							 * 0x1c 16.3 ???
+							 * 0x39 16.3 1.1
+							 * 0x38 16.3 1.3
+							 * 0x46 16.3 with AB + Video (Teles-Vision)
+							 */
+		if (val != 0x46 && val != 0x39 && val != 0x38 && val != 0x1c && val != 0x1e && val != 0x1f) {
+			printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+			       cs->hw.teles3.cfg_reg + 2, val);
+			release_io_teles3(cs);
+			return (0);
+		}
+	}
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d isac:0x%X  cfg:0x%X\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg);
+	printk(KERN_INFO
+	       "HiSax: hscx A:0x%X  hscx B:0x%X\n",
+	       cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32);
+
+	setup_isac(cs);
+	if (reset_teles3(cs)) {
+		printk(KERN_WARNING "Teles3: wrong IRQ\n");
+		release_io_teles3(cs);
+		return (0);
+	}
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &Teles_card_msg;
+	cs->irq_func = &teles3_interrupt;
+	ISACVersion(cs, "Teles3:");
+	if (HscxVersion(cs, "Teles3:")) {
+		printk(KERN_WARNING
+		       "Teles3: wrong HSCX versions check IO address\n");
+		release_io_teles3(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
new file mode 100644
index 0000000..63e8e20
--- /dev/null
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -0,0 +1,513 @@
+/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
+/*======================================================================
+
+    A teles S0 PCMCIA client driver
+
+    Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
+    Written by Christof Petig, christof.petig@wtal.de
+    
+    Also inspired by ELSA PCMCIA driver 
+    by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
+    
+    Extentions to new hisax_pcmcia by Karsten Keil
+
+    minor changes to be compatible with kernel 2.4.x
+    by Jan.Schubert@GMX.li
+
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
+MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
+MODULE_LICENSE("GPL");
+
+/*
+   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+   you do not define PCMCIA_DEBUG at all, all the debug code will be
+   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+   be present but disabled -- but it can then be enabled for specific
+   modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"teles_cs.c 2.10 2002/07/30 22:23:34 kkeil";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2;        /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+   The event() function is this driver's Card Services event handler.
+   It will be called by Card Services when an appropriate card status
+   event is received.  The config() and release() entry points are
+   used to configure or release a socket, in response to card insertion
+   and ejection events.  They are invoked from the teles_cs event
+   handler.
+*/
+
+static void teles_cs_config(dev_link_t *link);
+static void teles_cs_release(dev_link_t *link);
+static int teles_cs_event(event_t event, int priority,
+                          event_callback_args_t *args);
+
+/*
+   The attach() and detach() entry points are used to create and destroy
+   "instances" of the driver, where each instance represents everything
+   needed to manage one actual PCMCIA card.
+*/
+
+static dev_link_t *teles_attach(void);
+static void teles_detach(dev_link_t *);
+
+/*
+   The dev_info variable is the "key" that is used to match up this
+   device driver with appropriate cards, through the card configuration
+   database.
+*/
+
+static dev_info_t dev_info = "teles_cs";
+
+/*
+   A linked list of "instances" of the teles_cs device.  Each actual
+   PCMCIA card corresponds to one device instance, and is described
+   by one dev_link_t structure (defined in ds.h).
+
+   You may not want to use a linked list for this -- for example, the
+   memory card driver uses an array of dev_link_t pointers, where minor
+   device numbers are used to derive the corresponding array index.
+*/
+
+static dev_link_t *dev_list = NULL;
+
+/*
+   A dev_link_t structure has fields for most things that are needed
+   to keep track of a socket, but there will usually be some device
+   specific information that also needs to be kept track of.  The
+   'priv' pointer in a dev_link_t structure can be used to point to
+   a device-specific private data structure, like this.
+
+   To simplify the data structure handling, we actually include the
+   dev_link_t structure in the device's private data structure.
+
+   A driver needs to provide a dev_node_t structure for each device
+   on a card.  In some cases, there is only one device per card (for
+   example, ethernet cards, modems).  In other cases, there may be
+   many actual or logical devices (SCSI adapters, memory cards with
+   multiple partitions).  The dev_node_t structures need to be kept
+   in a linked list starting at the 'dev' field of a dev_link_t
+   structure.  We allocate them in the card's private data structure,
+   because they generally shouldn't be allocated dynamically.
+   In this case, we also provide a flag to indicate if a device is
+   "stopped" due to a power management event, or card ejection.  The
+   device IO routines can use a flag like this to throttle IO to a
+   card that is not ready to accept it.
+*/
+
+typedef struct local_info_t {
+    dev_link_t          link;
+    dev_node_t          node;
+    int                 busy;
+    int			cardnr;
+} local_info_t;
+
+/*======================================================================
+
+    teles_attach() creates an "instance" of the driver, allocatingx
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+
+======================================================================*/
+
+static dev_link_t *teles_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    local_info_t *local;
+    int ret;
+
+    DEBUG(0, "teles_attach()\n");
+
+    /* Allocate space for private device-specific data */
+    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+    if (!local) return NULL;
+    memset(local, 0, sizeof(local_info_t));
+    local->cardnr = -1;
+    link = &local->link; link->priv = local;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
+    link->irq.Handler = NULL;
+
+    /*
+      General socket configuration defaults can go here.  In this
+      client, we assume very little, and rely on the CIS for almost
+      everything.  In most clients, many details (i.e., number, sizes,
+      and attributes of IO windows) are fixed by the nature of the
+      device, and can be hard-wired here.
+    */
+    link->io.NumPorts1 = 96;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 5;
+
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+        CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+        CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+        CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &teles_cs_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+        cs_error(link->handle, RegisterClient, ret);
+        teles_detach(link);
+        return NULL;
+    }
+
+    return link;
+} /* teles_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void teles_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+    local_info_t *info = link->priv;
+    int ret;
+
+    DEBUG(0, "teles_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+        if (*linkp == link) break;
+    if (*linkp == NULL)
+        return;
+
+    if (link->state & DEV_CONFIG)
+        teles_cs_release(link);
+
+    /* Break the link with Card Services */
+    if (link->handle) {
+        ret = pcmcia_deregister_client(link->handle);
+	if (ret != CS_SUCCESS)
+	    cs_error(link->handle, DeregisterClient, ret);
+    }
+
+    /* Unlink device structure and free it */
+    *linkp = link->next;
+    kfree(info);
+
+} /* teles_detach */
+
+/*======================================================================
+
+    teles_cs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    device available to the system.
+
+======================================================================*/
+static int get_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_tuple_data(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_first_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+                     cisparse_t *parse)
+{
+    int i = pcmcia_get_next_tuple(handle, tuple);
+    if (i != CS_SUCCESS) return i;
+    return get_tuple(handle, tuple, parse);
+}
+
+static void teles_cs_config(dev_link_t *link)
+{
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    local_info_t *dev;
+    int i, j, last_fn;
+    u_short buf[128];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    IsdnCard_t icard;
+
+    DEBUG(0, "teles_config(0x%p)\n", link);
+    handle = link->handle;
+    dev = link->priv;
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = 255;
+    tuple.TupleOffset = 0;
+    tuple.Attributes = 0;
+    i = first_tuple(handle, &tuple, &parse);
+    if (i != CS_SUCCESS) {
+        last_fn = ParseTuple;
+	goto cs_failed;
+    }
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+    tuple.Attributes = 0;
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    i = first_tuple(handle, &tuple, &parse);
+    while (i == CS_SUCCESS) {
+        if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
+            printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
+            link->conf.ConfigIndex = cf->index;
+            link->io.BasePort1 = cf->io.win[0].base;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+        } else {
+          printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
+          link->conf.ConfigIndex = cf->index;
+          for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
+            link->io.BasePort1 = j;
+            i = pcmcia_request_io(link->handle, &link->io);
+            if (i == CS_SUCCESS) break;
+          }
+          break;
+        }
+        i = next_tuple(handle, &tuple, &parse);
+    }
+
+    if (i != CS_SUCCESS) {
+	last_fn = RequestIO;
+	goto cs_failed;
+    }
+
+    i = pcmcia_request_irq(link->handle, &link->irq);
+    if (i != CS_SUCCESS) {
+        link->irq.AssignedIRQ = 0;
+	last_fn = RequestIRQ;
+        goto cs_failed;
+    }
+
+    i = pcmcia_request_configuration(link->handle, &link->conf);
+    if (i != CS_SUCCESS) {
+      last_fn = RequestConfiguration;
+      goto cs_failed;
+    }
+
+    /* At this point, the dev_node_t structure(s) should be
+       initialized and arranged in a linked list at link->dev. *//*  */
+    sprintf(dev->node.dev_name, "teles");
+    dev->node.major = dev->node.minor = 0x0;
+
+    link->dev = &dev->node;
+
+    /* Finally, report what we've done */
+    printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+           dev->node.dev_name, link->conf.ConfigIndex,
+           link->conf.Vcc/10, link->conf.Vcc%10);
+    if (link->conf.Vpp1)
+        printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+    if (link->conf.Attributes & CONF_ENABLE_IRQ)
+        printk(", irq %d", link->irq.AssignedIRQ);
+    if (link->io.NumPorts1)
+        printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+               link->io.BasePort1+link->io.NumPorts1-1);
+    if (link->io.NumPorts2)
+        printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+               link->io.BasePort2+link->io.NumPorts2-1);
+    printk("\n");
+
+    link->state &= ~DEV_CONFIG_PENDING;
+
+    icard.para[0] = link->irq.AssignedIRQ;
+    icard.para[1] = link->io.BasePort1;
+    icard.protocol = protocol;
+    icard.typ = ISDN_CTYPE_TELESPCMCIA;
+    
+    i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
+    if (i < 0) {
+    	printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
+    		i, link->io.BasePort1);
+    	teles_cs_release(link);
+    } else
+    	((local_info_t*)link->priv)->cardnr = i;
+
+    return;
+cs_failed:
+    cs_error(link->handle, last_fn, i);
+    teles_cs_release(link);
+} /* teles_cs_config */
+
+/*======================================================================
+
+    After a card is removed, teles_cs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void teles_cs_release(dev_link_t *link)
+{
+    local_info_t *local = link->priv;
+
+    DEBUG(0, "teles_cs_release(0x%p)\n", link);
+
+    if (local) {
+    	if (local->cardnr >= 0) {
+    	    /* no unregister function with hisax */
+	    HiSax_closecard(local->cardnr);
+	}
+    }
+    /* Unlink the device chain */
+    link->dev = NULL;
+
+    /* Don't bother checking to see if these succeed or not */
+    if (link->win)
+        pcmcia_release_window(link->win);
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+} /* teles_cs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+
+======================================================================*/
+
+static int teles_cs_event(event_t event, int priority,
+                          event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    local_info_t *dev = link->priv;
+
+    DEBUG(1, "teles_cs_event(%d)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+        link->state &= ~DEV_PRESENT;
+        if (link->state & DEV_CONFIG) {
+            ((local_info_t*)link->priv)->busy = 1;
+	    teles_cs_release(link);
+        }
+        break;
+    case CS_EVENT_CARD_INSERTION:
+        link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+        teles_cs_config(link);
+        break;
+    case CS_EVENT_PM_SUSPEND:
+        link->state |= DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+        /* Mark the device as stopped, to block IO until later */
+        dev->busy = 1;
+        if (link->state & DEV_CONFIG)
+            pcmcia_release_configuration(link->handle);
+        break;
+    case CS_EVENT_PM_RESUME:
+        link->state &= ~DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+        if (link->state & DEV_CONFIG)
+            pcmcia_request_configuration(link->handle, &link->conf);
+        dev->busy = 0;
+        break;
+    }
+    return 0;
+} /* teles_cs_event */
+
+static struct pcmcia_driver teles_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "teles_cs",
+	},
+	.attach		= teles_attach,
+	.detach		= teles_detach,
+};
+
+static int __init init_teles_cs(void)
+{
+	return pcmcia_register_driver(&teles_cs_driver);
+}
+
+static void __exit exit_teles_cs(void)
+{
+	pcmcia_unregister_driver(&teles_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_teles_cs);
+module_exit(exit_teles_cs);
diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c
new file mode 100644
index 0000000..0661c6c
--- /dev/null
+++ b/drivers/isdn/hisax/telespci.c
@@ -0,0 +1,359 @@
+/* $Id: telespci.c,v 2.23.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * low level stuff for Teles PCI isdn cards
+ *
+ * Author       Ton van Rosmalen
+ *              Karsten Keil
+ * Copyright    by Ton van Rosmalen
+ *              by Karsten Keil      <keil@isdn4linux.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+
+extern const char *CardType[];
+const char *telespci_revision = "$Revision: 2.23.2.3 $";
+
+#define ZORAN_PO_RQ_PEN	0x02000000
+#define ZORAN_PO_WR	0x00800000
+#define ZORAN_PO_GID0	0x00000000
+#define ZORAN_PO_GID1	0x00100000
+#define ZORAN_PO_GREG0	0x00000000
+#define ZORAN_PO_GREG1	0x00010000
+#define ZORAN_PO_DMASK	0xFF
+
+#define WRITE_ADDR_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
+#define READ_DATA_ISAC	(ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_DATA_ISAC	(ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_ADDR_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0)
+#define READ_DATA_HSCX	(ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+#define WRITE_DATA_HSCX	(ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+
+#define ZORAN_WAIT_NOBUSY	do { \
+					portdata = readl(adr + 0x200); \
+				} while (portdata & ZORAN_PO_RQ_PEN)
+
+static inline u_char
+readisac(void __iomem *adr, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from ISAC */
+	writel(READ_DATA_ISAC, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writeisac(void __iomem *adr, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	
+	/* set address for ISAC */
+	writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to ISAC */
+	writel(WRITE_DATA_ISAC | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline u_char
+readhscx(void __iomem *adr, int hscx, u_char off)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	
+	/* read data from HSCX */
+	writel(READ_DATA_HSCX, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+	return ((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writehscx(void __iomem *adr, int hscx, u_char off, u_char data)
+{
+	register unsigned int portdata;
+
+	ZORAN_WAIT_NOBUSY;
+	/* set address for HSCX */
+	writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+
+	/* write data to HSCX */
+	writel(WRITE_DATA_HSCX | data, adr + 0x200);
+	ZORAN_WAIT_NOBUSY;
+}
+
+static inline void
+read_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_ISAC, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char)(portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static void
+write_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to ISAC */
+	for (i = 0; i < size; i++) {
+		/* set address for ISAC fifo */
+		writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_ISAC | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+	}
+}
+
+static inline void
+read_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	register unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* read data from HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(READ_DATA_HSCX, adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		data[i] = (u_char) (portdata & ZORAN_PO_DMASK);
+	}
+}
+
+static inline void
+write_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+	unsigned int portdata;
+	register int i;
+
+	ZORAN_WAIT_NOBUSY;
+	/* write data to HSCX */
+	for (i = 0; i < size; i++) {
+		/* set address for HSCX fifo */
+		writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		writel(WRITE_DATA_HSCX | data[i], adr + 0x200);
+		ZORAN_WAIT_NOBUSY;
+		udelay(10);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+	return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+	return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+	writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState *cs = dev_id;
+	u_char hval, ival;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	hval = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+	if (hval)
+		hscx_int_main(cs, hval);
+	ival = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+	if ((hval | ival) == 0) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+	if (ival)
+		isac_interrupt(cs, ival);
+	/* Clear interrupt register for Zoran PCI controller */
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+	writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+	writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+void
+release_io_telespci(struct IsdnCardState *cs)
+{
+	iounmap(cs->hw.teles0.membase);
+}
+
+static int
+TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	u_long flags;
+
+	switch (mt) {
+		case CARD_RESET:
+			return(0);
+		case CARD_RELEASE:
+			release_io_telespci(cs);
+			return(0);
+		case CARD_INIT:
+			spin_lock_irqsave(&cs->lock, flags);
+			inithscxisac(cs, 3);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			return(0);
+		case CARD_TEST:
+			return(0);
+	}
+	return(0);
+}
+
+static struct pci_dev *dev_tel __initdata = NULL;
+
+int __init
+setup_telespci(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	strcpy(tmp, telespci_revision);
+	printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_TELESPCI)
+		return (0);
+#ifdef CONFIG_PCI
+	if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) {
+		if (pci_enable_device(dev_tel))
+			return(0);
+		cs->irq = dev_tel->irq;
+		if (!cs->irq) {
+			printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
+			return(0);
+		}
+		cs->hw.teles0.membase = ioremap(pci_resource_start(dev_tel, 0),
+			PAGE_SIZE);
+		printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
+			pci_resource_start(dev_tel, 0), dev_tel->irq);
+	} else {
+		printk(KERN_WARNING "TelesPCI: No PCI card found\n");
+		return(0);
+	}
+#else
+	printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n");
+	printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n");
+	return (0);
+#endif /* CONFIG_PCI */
+
+	/* Initialize Zoran PCI controller */
+	writel(0x00000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x01000000, cs->hw.teles0.membase + 0x28);
+	writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C);
+	writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+	writel(0x61000000, cs->hw.teles0.membase + 0x40);
+	/* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d mem:%p\n",
+	       CardType[cs->typ], cs->irq,
+	       cs->hw.teles0.membase);
+
+	setup_isac(cs);
+	cs->readisac = &ReadISAC;
+	cs->writeisac = &WriteISAC;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadHSCX;
+	cs->BC_Write_Reg = &WriteHSCX;
+	cs->BC_Send_Data = &hscx_fill_fifo;
+	cs->cardmsg = &TelesPCI_card_msg;
+	cs->irq_func = &telespci_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	ISACVersion(cs, "TelesPCI:");
+	if (HscxVersion(cs, "TelesPCI:")) {
+		printk(KERN_WARNING
+		 "TelesPCI: wrong HSCX versions check IO/MEM addresses\n");
+		release_io_telespci(cs);
+		return (0);
+	}
+	return (1);
+}
diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c
new file mode 100644
index 0000000..d2b6b8e
--- /dev/null
+++ b/drivers/isdn/hisax/w6692.c
@@ -0,0 +1,1096 @@
+/* $Id: w6692.c,v 1.18.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Winbond W6692 specific routines
+ *
+ * Author       Petr Novak
+ * Copyright    by Petr Novak        <petr.novak@i.cz>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include "w6692.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+/* table entry in the PCI devices list */
+typedef struct {
+	int vendor_id;
+	int device_id;
+	char *vendor_name;
+	char *card_name;
+} PCI_ENTRY;
+
+static const PCI_ENTRY id_list[] =
+{
+	{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"},
+	{PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"},
+	{0, 0, "U.S.Robotics", "ISDN PCI Card TA"}
+};
+
+#define W6692_SV_USR   0x16ec
+#define W6692_SD_USR   0x3409
+#define W6692_WINBOND  0
+#define W6692_DYNALINK 1
+#define W6692_USR      2
+
+extern const char *CardType[];
+
+const char *w6692_revision = "$Revision: 1.18.2.4 $";
+
+#define DBUSY_TIMER_VALUE 80
+
+static char *W6692Ver[] __initdata =
+{"W6692 V00", "W6692 V01", "W6692 V10",
+ "W6692 V11"};
+
+static void __init
+W6692Version(struct IsdnCardState *cs, char *s)
+{
+	int val;
+
+	val = cs->readW6692(cs, W_D_RBCH);
+	printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "ph_command %x", command);
+	cs->writeisac(cs, W_CIX, command);
+}
+
+
+static void
+W6692_new_ph(struct IsdnCardState *cs)
+{
+	switch (cs->dc.w6692.ph_state) {
+		case (W_L1CMD_RST):
+			ph_command(cs, W_L1CMD_DRC);
+			l1_msg(cs, HW_RESET | INDICATION, NULL);
+			/* fallthru */
+		case (W_L1IND_CD):
+			l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+			break;
+		case (W_L1IND_DRD):
+			l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+			break;
+		case (W_L1IND_CE):
+			l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+			break;
+		case (W_L1IND_LD):
+			l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+			break;
+		case (W_L1IND_ARD):
+			l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+			break;
+		case (W_L1IND_AI8):
+			l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+			break;
+		case (W_L1IND_AI10):
+			l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+			break;
+		default:
+			break;
+	}
+}
+
+static void
+W6692_bh(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+
+	if (!cs)
+		return;
+	if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy cleared");
+		stptr = cs->stlist;
+		while (stptr != NULL) {
+			stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+			stptr = stptr->next;
+		}
+	}
+	if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+		W6692_new_ph(cs);
+	if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+		DChannel_proc_rcv(cs);
+	if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+		DChannel_proc_xmt(cs);
+/*
+   if (test_and_clear_bit(D_RX_MON1, &cs->event))
+   arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+   if (test_and_clear_bit(D_TX_MON1, &cs->event))
+   arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+ */
+}
+
+static void
+W6692_empty_fifo(struct IsdnCardState *cs, int count)
+{
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "W6692_empty_fifo");
+
+	if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692_empty_fifo overrun %d",
+				cs->rcvidx + count);
+		cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);
+		cs->rcvidx = 0;
+		return;
+	}
+	ptr = cs->rcvbuf + cs->rcvidx;
+	cs->rcvidx += count;
+	cs->readW6692fifo(cs, ptr, count);
+	cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "W6692_empty_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+W6692_fill_fifo(struct IsdnCardState *cs)
+{
+	int count, more;
+	u_char *ptr;
+
+	if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+		debugl1(cs, "W6692_fill_fifo");
+
+	if (!cs->tx_skb)
+		return;
+
+	count = cs->tx_skb->len;
+	if (count <= 0)
+		return;
+
+	more = 0;
+	if (count > W_D_FIFO_THRESH) {
+		more = !0;
+		count = W_D_FIFO_THRESH;
+	}
+	ptr = cs->tx_skb->data;
+	skb_pull(cs->tx_skb, count);
+	cs->tx_cnt += count;
+	cs->writeW6692fifo(cs, ptr, count);
+	cs->writeW6692(cs, W_D_CMDR, more ? W_D_CMDR_XMS : (W_D_CMDR_XMS | W_D_CMDR_XME));
+	if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		debugl1(cs, "W6692_fill_fifo dbusytimer running");
+		del_timer(&cs->dbusytimer);
+	}
+	init_timer(&cs->dbusytimer);
+	cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
+	add_timer(&cs->dbusytimer);
+	if (cs->debug & L1_DEB_ISAC_FIFO) {
+		char *t = cs->dlog;
+
+		t += sprintf(t, "W6692_fill_fifo cnt %d", count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, cs->dlog);
+	}
+}
+
+static void
+W6692B_empty_fifo(struct BCState *bcs, int count)
+{
+	u_char *ptr;
+	struct IsdnCardState *cs = bcs->cs;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "W6692B_empty_fifo");
+
+	if (bcs->hw.w6692.rcvidx + count > HSCX_BUFMAX) {
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692B_empty_fifo: incoming packet too large");
+		cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+		bcs->hw.w6692.rcvidx = 0;
+		return;
+	}
+	ptr = bcs->hw.w6692.rcvbuf + bcs->hw.w6692.rcvidx;
+	bcs->hw.w6692.rcvidx += count;
+	READW6692BFIFO(cs, bcs->channel, ptr, count);
+	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "W6692B_empty_fifo %c cnt %d",
+			     bcs->channel + '1', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+W6692B_fill_fifo(struct BCState *bcs)
+{
+	struct IsdnCardState *cs = bcs->cs;
+	int more, count;
+	u_char *ptr;
+
+	if (!bcs->tx_skb)
+		return;
+	if (bcs->tx_skb->len <= 0)
+		return;
+
+	more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+	if (bcs->tx_skb->len > W_B_FIFO_THRESH) {
+		more = 1;
+		count = W_B_FIFO_THRESH;
+	} else
+		count = bcs->tx_skb->len;
+
+	if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+		debugl1(cs, "W6692B_fill_fifo%s%d", (more ? " ": " last "), count);
+
+	ptr = bcs->tx_skb->data;
+	skb_pull(bcs->tx_skb, count);
+	bcs->tx_cnt -= count;
+	bcs->hw.w6692.count += count;
+	WRITEW6692BFIFO(cs, bcs->channel, ptr, count);
+	cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME));
+	if (cs->debug & L1_DEB_HSCX_FIFO) {
+		char *t = bcs->blog;
+
+		t += sprintf(t, "W6692B_fill_fifo %c cnt %d",
+			     bcs->channel + '1', count);
+		QuickHex(t, ptr, count);
+		debugl1(cs, bcs->blog);
+	}
+}
+
+static void
+W6692B_interrupt(struct IsdnCardState *cs, u_char bchan)
+{
+	u_char val;
+	u_char r;
+	struct BCState *bcs;
+	struct sk_buff *skb;
+	int count;
+
+	bcs = (cs->bcs->channel == bchan) ? cs->bcs : (cs->bcs+1);
+	val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR);
+	debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val);
+
+	if (!test_bit(BC_FLG_INIT, &bcs->Flag)) {
+		debugl1(cs, "W6692B not INIT yet");
+		return;
+	}
+	if (val & W_B_EXI_RME) {	/* RME */
+		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+		if (r & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692 B STAR %x", r);
+			if ((r & W_B_STAR_RDOV) && bcs->mode)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 B RDOV mode=%d",
+						bcs->mode);
+			if (r & W_B_STAR_CRCE)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 B CRC error");
+			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
+		} else {
+			count = cs->BC_Read_Reg(cs, bchan, W_B_RBCL) & (W_B_FIFO_THRESH - 1);
+			if (count == 0)
+				count = W_B_FIFO_THRESH;
+			W6692B_empty_fifo(bcs, count);
+			if ((count = bcs->hw.w6692.rcvidx) > 0) {
+				if (cs->debug & L1_DEB_HSCX_FIFO)
+					debugl1(cs, "W6692 Bchan Frame %d", count);
+				if (!(skb = dev_alloc_skb(count)))
+					printk(KERN_WARNING "W6692: Bchan receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), bcs->hw.w6692.rcvbuf, count);
+					skb_queue_tail(&bcs->rqueue, skb);
+				}
+			}
+		}
+		bcs->hw.w6692.rcvidx = 0;
+		schedule_event(bcs, B_RCVBUFREADY);
+	}
+	if (val & W_B_EXI_RMR) {	/* RMR */
+		W6692B_empty_fifo(bcs, W_B_FIFO_THRESH);
+		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+		if (r & W_B_STAR_RDOV) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692 B RDOV(RMR) mode=%d",bcs->mode);
+			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
+			if (bcs->mode != L1_MODE_TRANS)
+				bcs->hw.w6692.rcvidx = 0;
+		}
+		if (bcs->mode == L1_MODE_TRANS) {
+			/* receive audio data */
+			if (!(skb = dev_alloc_skb(W_B_FIFO_THRESH)))
+				printk(KERN_WARNING "HiSax: receive out of memory\n");
+			else {
+				memcpy(skb_put(skb, W_B_FIFO_THRESH), bcs->hw.w6692.rcvbuf, W_B_FIFO_THRESH);
+				skb_queue_tail(&bcs->rqueue, skb);
+			}
+			bcs->hw.w6692.rcvidx = 0;
+			schedule_event(bcs, B_RCVBUFREADY);
+		}
+	}
+	if (val & W_B_EXI_XDUN) {	/* XDUN */
+		cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692 B EXIR %x Lost TX", val);
+		if (bcs->mode == 1)
+			W6692B_fill_fifo(bcs);
+		else {
+			/* Here we lost an TX interrupt, so
+			   * restart transmitting the whole frame.
+			 */
+			if (bcs->tx_skb) {
+				skb_push(bcs->tx_skb, bcs->hw.w6692.count);
+				bcs->tx_cnt += bcs->hw.w6692.count;
+				bcs->hw.w6692.count = 0;
+			}
+		}
+		return;
+	}
+	if (val & W_B_EXI_XFR) {	/* XFR */
+		r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+		if (r & W_B_STAR_XDOW) {
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692 B STAR %x XDOW", r);
+			cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+			if (bcs->tx_skb && (bcs->mode != 1)) {
+				skb_push(bcs->tx_skb, bcs->hw.w6692.count);
+				bcs->tx_cnt += bcs->hw.w6692.count;
+				bcs->hw.w6692.count = 0;
+			}
+		}
+		if (bcs->tx_skb) {
+			if (bcs->tx_skb->len) {
+				W6692B_fill_fifo(bcs);
+				return;
+			} else {
+				if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+					(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
+					u_long	flags;
+					spin_lock_irqsave(&bcs->aclock, flags);
+					bcs->ackcnt += bcs->hw.w6692.count;
+					spin_unlock_irqrestore(&bcs->aclock, flags);
+					schedule_event(bcs, B_ACKPENDING);
+				}
+				dev_kfree_skb_irq(bcs->tx_skb);
+				bcs->hw.w6692.count = 0;
+				bcs->tx_skb = NULL;
+			}
+		}
+		if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+			bcs->hw.w6692.count = 0;
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			W6692B_fill_fifo(bcs);
+		} else {
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			schedule_event(bcs, B_XMTBUFREADY);
+		}
+	}
+}
+
+static irqreturn_t
+W6692_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	struct IsdnCardState	*cs = dev_id;
+	u_char			val, exval, v1;
+	struct sk_buff		*skb;
+	u_int			count;
+	u_long			flags;
+	int			icnt = 5;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	val = cs->readW6692(cs, W_ISTA);
+	if (!val) {
+		spin_unlock_irqrestore(&cs->lock, flags);
+		return IRQ_NONE;
+	}
+      StartW6692:
+	if (cs->debug & L1_DEB_ISAC)
+		debugl1(cs, "W6692 ISTA %x", val);
+
+	if (val & W_INT_D_RME) {	/* RME */
+		exval = cs->readW6692(cs, W_D_RSTA);
+		if (exval & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
+			if (exval & W_D_RSTA_RDOV)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 RDOV");
+			if (exval & W_D_RSTA_CRCE)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 D-channel CRC error");
+			if (exval & W_D_RSTA_RMB)
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, "W6692 D-channel ABORT");
+			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
+		} else {
+			count = cs->readW6692(cs, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
+			if (count == 0)
+				count = W_D_FIFO_THRESH;
+			W6692_empty_fifo(cs, count);
+			if ((count = cs->rcvidx) > 0) {
+				cs->rcvidx = 0;
+				if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+					printk(KERN_WARNING "HiSax: D receive out of memory\n");
+				else {
+					memcpy(skb_put(skb, count), cs->rcvbuf, count);
+					skb_queue_tail(&cs->rq, skb);
+				}
+			}
+		}
+		cs->rcvidx = 0;
+		schedule_event(cs, D_RCVBUFREADY);
+	}
+	if (val & W_INT_D_RMR) {	/* RMR */
+		W6692_empty_fifo(cs, W_D_FIFO_THRESH);
+	}
+	if (val & W_INT_D_XFR) {	/* XFR */
+		if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+			del_timer(&cs->dbusytimer);
+		if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+			schedule_event(cs, D_CLEARBUSY);
+		if (cs->tx_skb) {
+			if (cs->tx_skb->len) {
+				W6692_fill_fifo(cs);
+				goto afterXFR;
+			} else {
+				dev_kfree_skb_irq(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			}
+		}
+		if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+			cs->tx_cnt = 0;
+			W6692_fill_fifo(cs);
+		} else
+			schedule_event(cs, D_XMTBUFREADY);
+	}
+      afterXFR:
+	if (val & (W_INT_XINT0 | W_INT_XINT1)) {	/* XINT0/1 - never */
+		if (cs->debug & L1_DEB_ISAC)
+			debugl1(cs, "W6692 spurious XINT!");
+	}
+	if (val & W_INT_D_EXI) {	/* EXI */
+		exval = cs->readW6692(cs, W_D_EXIR);
+		if (cs->debug & L1_DEB_WARN)
+			debugl1(cs, "W6692 D_EXIR %02x", exval);
+		if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {	/* Transmit underrun/collision */
+			debugl1(cs, "W6692 D-chan underrun/collision");
+			printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL\n");
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			if (cs->tx_skb) {	/* Restart frame */
+				skb_push(cs->tx_skb, cs->tx_cnt);
+				cs->tx_cnt = 0;
+				W6692_fill_fifo(cs);
+			} else {
+				printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL no skb\n");
+				debugl1(cs, "W6692 XDUN/XCOL no skb");
+				cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST);
+			}
+		}
+		if (exval & W_D_EXI_RDOV) {	/* RDOV */
+			debugl1(cs, "W6692 D-channel RDOV");
+			printk(KERN_WARNING "HiSax: W6692 D-RDOV\n");
+			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST);
+		}
+		if (exval & W_D_EXI_TIN2) {	/* TIN2 - never */
+			debugl1(cs, "W6692 spurious TIN2 interrupt");
+		}
+		if (exval & W_D_EXI_MOC) {	/* MOC - not supported */
+			debugl1(cs, "W6692 spurious MOC interrupt");
+			v1 = cs->readW6692(cs, W_MOSR);
+			debugl1(cs, "W6692 MOSR %02x", v1);
+		}
+		if (exval & W_D_EXI_ISC) {	/* ISC - Level1 change */
+			v1 = cs->readW6692(cs, W_CIR);
+			if (cs->debug & L1_DEB_ISAC)
+				debugl1(cs, "W6692 ISC CIR=0x%02X", v1);
+			if (v1 & W_CIR_ICC) {
+				cs->dc.w6692.ph_state = v1 & W_CIR_COD_MASK;
+				if (cs->debug & L1_DEB_ISAC)
+					debugl1(cs, "ph_state_change %x", cs->dc.w6692.ph_state);
+				schedule_event(cs, D_L1STATECHANGE);
+			}
+			if (v1 & W_CIR_SCC) {
+				v1 = cs->readW6692(cs, W_SQR);
+				debugl1(cs, "W6692 SCC SQR=0x%02X", v1);
+			}
+		}
+		if (exval & W_D_EXI_WEXP) {
+			debugl1(cs, "W6692 spurious WEXP interrupt!");
+		}
+		if (exval & W_D_EXI_TEXP) {
+			debugl1(cs, "W6692 spurious TEXP interrupt!");
+		}
+	}
+	if (val & W_INT_B1_EXI) {
+		debugl1(cs, "W6692 B channel 1 interrupt");
+		W6692B_interrupt(cs, 0);
+	}
+	if (val & W_INT_B2_EXI) {
+		debugl1(cs, "W6692 B channel 2 interrupt");
+		W6692B_interrupt(cs, 1);
+	}
+	val = cs->readW6692(cs, W_ISTA);
+	if (val && icnt) {
+		icnt--;
+		goto StartW6692;
+	}
+	if (!icnt) {
+		printk(KERN_WARNING "W6692 IRQ LOOP\n");
+		cs->writeW6692(cs, W_IMASK, 0xff);
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static void
+W6692_l1hw(struct PStack *st, int pr, void *arg)
+{
+	struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+	struct sk_buff *skb = arg;
+	u_long flags;
+	int val;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+			} else {
+				cs->tx_skb = skb;
+				cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+				if (cs->debug & L1_DEB_LAPD)
+					Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+				W6692_fill_fifo(cs);
+			}
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			spin_lock_irqsave(&cs->lock, flags);
+			if (cs->tx_skb) {
+				if (cs->debug & L1_DEB_WARN)
+					debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+				skb_queue_tail(&cs->sq, skb);
+				spin_unlock_irqrestore(&cs->lock, flags);
+				break;
+			}
+			if (cs->debug & DEB_DLOG_HEX)
+				LogFrame(cs, skb->data, skb->len);
+			if (cs->debug & DEB_DLOG_VERBOSE)
+				dlogframe(cs, skb, 0);
+			cs->tx_skb = skb;
+			cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+			W6692_fill_fifo(cs);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG		/* psa */
+			if (cs->debug & L1_DEB_LAPD)
+				debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+			if (!cs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (HW_RESET | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			if ((cs->dc.w6692.ph_state == W_L1IND_DRD)) {
+				ph_command(cs, W_L1CMD_ECK);
+				spin_unlock_irqrestore(&cs->lock, flags);
+			} else {
+				ph_command(cs, W_L1CMD_RST);
+				cs->dc.w6692.ph_state = W_L1CMD_RST;
+				spin_unlock_irqrestore(&cs->lock, flags);
+				W6692_new_ph(cs);
+			}
+			break;
+		case (HW_ENABLE | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, W_L1CMD_ECK);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_INFO3 | REQUEST):
+			spin_lock_irqsave(&cs->lock, flags);
+			ph_command(cs, W_L1CMD_AR8);
+			spin_unlock_irqrestore(&cs->lock, flags);
+			break;
+		case (HW_TESTLOOP | REQUEST):
+			val = 0;
+			if (1 & (long) arg)
+				val |= 0x0c;
+			if (2 & (long) arg)
+				val |= 0x3;
+			/* !!! not implemented yet */
+			break;
+		case (HW_DEACTIVATE | RESPONSE):
+			skb_queue_purge(&cs->rq);
+			skb_queue_purge(&cs->sq);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_skb = NULL;
+			}
+			if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+				del_timer(&cs->dbusytimer);
+			if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+				schedule_event(cs, D_CLEARBUSY);
+			break;
+		default:
+			if (cs->debug & L1_DEB_WARN)
+				debugl1(cs, "W6692_l1hw unknown %04x", pr);
+			break;
+	}
+}
+
+static void
+setstack_W6692(struct PStack *st, struct IsdnCardState *cs)
+{
+	st->l1.l1hw = W6692_l1hw;
+}
+
+static void
+DC_Close_W6692(struct IsdnCardState *cs)
+{
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+	struct PStack *stptr;
+	int rbch, star;
+	u_long flags;
+
+	spin_lock_irqsave(&cs->lock, flags);
+	if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+		rbch = cs->readW6692(cs, W_D_RBCH);
+		star = cs->readW6692(cs, W_D_STAR);
+		if (cs->debug)
+			debugl1(cs, "D-Channel Busy D_RBCH %02x D_STAR %02x",
+				rbch, star);
+		if (star & W_D_STAR_XBZ) {	/* D-Channel Busy */
+			test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+			stptr = cs->stlist;
+			while (stptr != NULL) {
+				stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+				stptr = stptr->next;
+			}
+		} else {
+			/* discard frame; reset transceiver */
+			test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+			if (cs->tx_skb) {
+				dev_kfree_skb_any(cs->tx_skb);
+				cs->tx_cnt = 0;
+				cs->tx_skb = NULL;
+			} else {
+				printk(KERN_WARNING "HiSax: W6692 D-Channel Busy no skb\n");
+				debugl1(cs, "D-Channel Busy no skb");
+			}
+			cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST);	/* Transmitter reset */
+			spin_unlock_irqrestore(&cs->lock, flags);
+			cs->irq_func(cs->irq, cs, NULL);
+			return;
+		}
+	}
+	spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+static void
+W6692Bmode(struct BCState *bcs, int mode, int bchan)
+{
+	struct IsdnCardState *cs = bcs->cs;
+
+	if (cs->debug & L1_DEB_HSCX)
+		debugl1(cs, "w6692 %c mode %d ichan %d",
+			'1' + bchan, mode, bchan);
+	bcs->mode = mode;
+	bcs->channel = bchan;
+	bcs->hw.w6692.bchan = bchan;
+
+	switch (mode) {
+		case (L1_MODE_NULL):
+			cs->BC_Write_Reg(cs, bchan, W_B_MODE, 0);
+			break;
+		case (L1_MODE_TRANS):
+			cs->BC_Write_Reg(cs, bchan, W_B_MODE, W_B_MODE_MMS);
+			break;
+		case (L1_MODE_HDLC):
+			cs->BC_Write_Reg(cs, bchan, W_B_MODE, W_B_MODE_ITF);
+			cs->BC_Write_Reg(cs, bchan, W_B_ADM1, 0xff);
+			cs->BC_Write_Reg(cs, bchan, W_B_ADM2, 0xff);
+			break;
+	}
+	if (mode)
+		cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RRST |
+				 W_B_CMDR_RACT | W_B_CMDR_XRST);
+	cs->BC_Write_Reg(cs, bchan, W_B_EXIM, 0x00);
+}
+
+static void
+W6692_l2l1(struct PStack *st, int pr, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct BCState *bcs = st->l1.bcs; 
+	u_long flags;
+
+	switch (pr) {
+		case (PH_DATA | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			if (bcs->tx_skb) {
+				skb_queue_tail(&bcs->squeue, skb);
+			} else {
+				bcs->tx_skb = skb;
+				test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+				bcs->hw.w6692.count = 0;
+				bcs->cs->BC_Send_Data(bcs);
+			}
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | INDICATION):
+			if (bcs->tx_skb) {
+				printk(KERN_WARNING "W6692_l2l1: this shouldn't happen\n");
+				break;
+			}
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+			bcs->tx_skb = skb;
+			bcs->hw.w6692.count = 0;
+			bcs->cs->BC_Send_Data(bcs);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			break;
+		case (PH_PULL | REQUEST):
+			if (!bcs->tx_skb) {
+				test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+				st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+			} else
+				test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+			break;
+		case (PH_ACTIVATE | REQUEST):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+			W6692Bmode(bcs, st->l1.mode, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | REQUEST):
+			l1_msg_b(st, pr, arg);
+			break;
+		case (PH_DEACTIVATE | CONFIRM):
+			spin_lock_irqsave(&bcs->cs->lock, flags);
+			test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+			W6692Bmode(bcs, 0, st->l1.bc);
+			spin_unlock_irqrestore(&bcs->cs->lock, flags);
+			st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+			break;
+	}
+}
+
+static void
+close_w6692state(struct BCState *bcs)
+{
+	W6692Bmode(bcs, 0, bcs->channel);
+	if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (bcs->hw.w6692.rcvbuf) {
+			kfree(bcs->hw.w6692.rcvbuf);
+			bcs->hw.w6692.rcvbuf = NULL;
+		}
+		if (bcs->blog) {
+			kfree(bcs->blog);
+			bcs->blog = NULL;
+		}
+		skb_queue_purge(&bcs->rqueue);
+		skb_queue_purge(&bcs->squeue);
+		if (bcs->tx_skb) {
+			dev_kfree_skb_any(bcs->tx_skb);
+			bcs->tx_skb = NULL;
+			test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+		}
+	}
+}
+
+static int
+open_w6692state(struct IsdnCardState *cs, struct BCState *bcs)
+{
+	if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+		if (!(bcs->hw.w6692.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for w6692.rcvbuf\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			return (1);
+		}
+		if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+			printk(KERN_WARNING
+			       "HiSax: No memory for bcs->blog\n");
+			test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+			kfree(bcs->hw.w6692.rcvbuf);
+			bcs->hw.w6692.rcvbuf = NULL;
+			return (2);
+		}
+		skb_queue_head_init(&bcs->rqueue);
+		skb_queue_head_init(&bcs->squeue);
+	}
+	bcs->tx_skb = NULL;
+	test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+	bcs->event = 0;
+	bcs->hw.w6692.rcvidx = 0;
+	bcs->tx_cnt = 0;
+	return (0);
+}
+
+static int
+setstack_w6692(struct PStack *st, struct BCState *bcs)
+{
+	bcs->channel = st->l1.bc;
+	if (open_w6692state(st->l1.hardware, bcs))
+		return (-1);
+	st->l1.bcs = bcs;
+	st->l2.l2l1 = W6692_l2l1;
+	setstack_manager(st);
+	bcs->st = st;
+	setstack_l1_B(st);
+	return (0);
+}
+
+void resetW6692(struct IsdnCardState *cs)
+{
+	cs->writeW6692(cs, W_D_CTL, W_D_CTL_SRST);
+	mdelay(10);
+	cs->writeW6692(cs, W_D_CTL, 0x00);
+	mdelay(10);
+	cs->writeW6692(cs, W_IMASK, 0xff);
+	cs->writeW6692(cs, W_D_SAM, 0xff);
+	cs->writeW6692(cs, W_D_TAM, 0xff);
+	cs->writeW6692(cs, W_D_EXIM, 0x00);
+	cs->writeW6692(cs, W_D_MODE, W_D_MODE_RACT);
+	cs->writeW6692(cs, W_IMASK, 0x18);
+	if (cs->subtyp == W6692_USR) {
+		/* seems that USR implemented some power control features
+		 * Pin 79 is connected to the oscilator circuit so we
+		 * have to handle it here
+		 */
+		cs->writeW6692(cs, W_PCTL, 0x80);
+		cs->writeW6692(cs, W_XDATA, 0x00);
+	}
+}
+
+void __init initW6692(struct IsdnCardState *cs, int part)
+{
+	if (part & 1) {
+		cs->setstack_d = setstack_W6692;
+		cs->DC_Close = DC_Close_W6692;
+		cs->dbusytimer.function = (void *) dbusy_timer_handler;
+		cs->dbusytimer.data = (long) cs;
+		init_timer(&cs->dbusytimer);
+		resetW6692(cs);
+		ph_command(cs, W_L1CMD_RST);
+		cs->dc.w6692.ph_state = W_L1CMD_RST;
+		W6692_new_ph(cs);
+		ph_command(cs, W_L1CMD_ECK);
+
+		cs->bcs[0].BC_SetStack = setstack_w6692;
+		cs->bcs[1].BC_SetStack = setstack_w6692;
+		cs->bcs[0].BC_Close = close_w6692state;
+		cs->bcs[1].BC_Close = close_w6692state;
+		W6692Bmode(cs->bcs, 0, 0);
+		W6692Bmode(cs->bcs + 1, 0, 0);
+	}
+	if (part & 2) {
+		/* Reenable all IRQ */
+		cs->writeW6692(cs, W_IMASK, 0x18);
+		cs->writeW6692(cs, W_D_EXIM, 0x00);
+		cs->BC_Write_Reg(cs, 0, W_B_EXIM, 0x00);
+		cs->BC_Write_Reg(cs, 1, W_B_EXIM, 0x00);
+		/* Reset D-chan receiver and transmitter */
+		cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
+	}
+}
+
+/* Interface functions */
+
+static u_char
+ReadW6692(struct IsdnCardState *cs, u_char offset)
+{
+	return (inb(cs->hw.w6692.iobase + offset));
+}
+
+static void
+WriteW6692(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+	outb(value, cs->hw.w6692.iobase + offset);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	insb(cs->hw.w6692.iobase + W_D_RFIFO, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+	outsb(cs->hw.w6692.iobase + W_D_XFIFO, data, size);
+}
+
+static u_char
+ReadW6692B(struct IsdnCardState *cs, int bchan, u_char offset)
+{
+	return (inb(cs->hw.w6692.iobase + (bchan ? 0x40 : 0) + offset));
+}
+
+static void
+WriteW6692B(struct IsdnCardState *cs, int bchan, u_char offset, u_char value)
+{
+	outb(value, cs->hw.w6692.iobase + (bchan ? 0x40 : 0) + offset);
+}
+
+static int
+w6692_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+	switch (mt) {
+		case CARD_RESET:
+			resetW6692(cs);
+			return (0);
+		case CARD_RELEASE:
+			cs->writeW6692(cs, W_IMASK, 0xff);
+			release_region(cs->hw.w6692.iobase, 256);
+			if (cs->subtyp == W6692_USR) {
+				cs->writeW6692(cs, W_XDATA, 0x04);
+			}
+			return (0);
+		case CARD_INIT:
+			initW6692(cs, 3);
+			return (0);
+		case CARD_TEST:
+			return (0);
+	}
+	return (0);
+}
+
+static int id_idx ;
+
+static struct pci_dev *dev_w6692 __initdata = NULL;
+
+int __init 
+setup_w6692(struct IsdnCard *card)
+{
+	struct IsdnCardState *cs = card->cs;
+	char tmp[64];
+	u_char found = 0;
+	u_char pci_irq = 0;
+	u_int pci_ioaddr = 0;
+
+	strcpy(tmp, w6692_revision);
+	printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp));
+	if (cs->typ != ISDN_CTYPE_W6692)
+		return (0);
+#ifdef CONFIG_PCI
+	while (id_list[id_idx].vendor_id) {
+		dev_w6692 = pci_find_device(id_list[id_idx].vendor_id,
+					    id_list[id_idx].device_id,
+					    dev_w6692);
+		if (dev_w6692) {
+			if (pci_enable_device(dev_w6692))
+				continue;
+			cs->subtyp = id_idx;
+			break;
+		}
+		id_idx++;
+	}
+	if (dev_w6692) {
+		found = 1;
+		pci_irq = dev_w6692->irq;
+		/* I think address 0 is allways the configuration area */
+		/* and address 1 is the real IO space KKe 03.09.99 */
+		pci_ioaddr = pci_resource_start(dev_w6692, 1);
+		/* USR ISDN PCI card TA need some special handling */
+		if (cs->subtyp == W6692_WINBOND) {
+			if ((W6692_SV_USR == dev_w6692->subsystem_vendor) &&
+			    (W6692_SD_USR == dev_w6692->subsystem_device)) {
+				cs->subtyp = W6692_USR;
+			}
+		}
+	}
+	if (!found) {
+		printk(KERN_WARNING "W6692: No PCI card found\n");
+		return (0);
+	}
+	cs->irq = pci_irq;
+	if (!cs->irq) {
+		printk(KERN_WARNING "W6692: No IRQ for PCI card found\n");
+		return (0);
+	}
+	if (!pci_ioaddr) {
+		printk(KERN_WARNING "W6692: NO I/O Base Address found\n");
+		return (0);
+	}
+	cs->hw.w6692.iobase = pci_ioaddr;
+	printk(KERN_INFO "Found: %s %s, I/O base: 0x%x, irq: %d\n",
+	       id_list[cs->subtyp].vendor_name, id_list[cs->subtyp].card_name,
+	       pci_ioaddr, pci_irq);
+	if (!request_region(cs->hw.w6692.iobase, 256, id_list[cs->subtyp].card_name)) {
+		printk(KERN_WARNING
+		       "HiSax: %s I/O ports %x-%x already in use\n",
+		       id_list[cs->subtyp].card_name,
+		       cs->hw.w6692.iobase,
+		       cs->hw.w6692.iobase + 255);
+		return (0);
+	}
+#else
+	printk(KERN_WARNING "HiSax: W6692 and NO_PCI_BIOS\n");
+	printk(KERN_WARNING "HiSax: W6692 unable to config\n");
+	return (0);
+#endif				/* CONFIG_PCI */
+
+	printk(KERN_INFO
+	       "HiSax: %s config irq:%d I/O:%x\n",
+	       id_list[cs->subtyp].card_name, cs->irq,
+	       cs->hw.w6692.iobase);
+
+	INIT_WORK(&cs->tqueue, (void *)(void *) W6692_bh, cs);
+	cs->readW6692 = &ReadW6692;
+	cs->writeW6692 = &WriteW6692;
+	cs->readisacfifo = &ReadISACfifo;
+	cs->writeisacfifo = &WriteISACfifo;
+	cs->BC_Read_Reg = &ReadW6692B;
+	cs->BC_Write_Reg = &WriteW6692B;
+	cs->BC_Send_Data = &W6692B_fill_fifo;
+	cs->cardmsg = &w6692_card_msg;
+	cs->irq_func = &W6692_interrupt;
+	cs->irq_flags |= SA_SHIRQ;
+	W6692Version(cs, "W6692:");
+	printk(KERN_INFO "W6692 ISTA=0x%X\n", ReadW6692(cs, W_ISTA));
+	printk(KERN_INFO "W6692 IMASK=0x%X\n", ReadW6692(cs, W_IMASK));
+	printk(KERN_INFO "W6692 D_EXIR=0x%X\n", ReadW6692(cs, W_D_EXIR));
+	printk(KERN_INFO "W6692 D_EXIM=0x%X\n", ReadW6692(cs, W_D_EXIM));
+	printk(KERN_INFO "W6692 D_RSTA=0x%X\n", ReadW6692(cs, W_D_RSTA));
+	return (1);
+}
diff --git a/drivers/isdn/hisax/w6692.h b/drivers/isdn/hisax/w6692.h
new file mode 100644
index 0000000..c79c81e
--- /dev/null
+++ b/drivers/isdn/hisax/w6692.h
@@ -0,0 +1,184 @@
+/* $Id: w6692.h,v 1.4.2.2 2004/01/12 22:52:29 keil Exp $
+ *
+ * Winbond W6692 specific defines
+ *
+ * Author       Petr Novak
+ * Copyright    by Petr Novak        <petr.novak@i.cz>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* map W6692 functions to ISAC functions */
+#define readW6692	readisac
+#define writeW6692	writeisac
+#define	readW6692fifo	readisacfifo
+#define	writeW6692fifo	writeisacfifo
+
+/* B-channel FIFO read/write routines */
+
+#define READW6692BFIFO(cs,bchan,ptr,count) \
+	insb(cs->hw.w6692.iobase+W_B_RFIFO+(bchan?0x40:0),ptr,count)
+
+#define WRITEW6692BFIFO(cs,bchan,ptr,count) \
+	outsb(cs->hw.w6692.iobase+W_B_XFIFO+(bchan?0x40:0),ptr,count)
+
+/* Specifications of W6692 registers */
+
+#define W_D_RFIFO	0x00	/* R */
+#define W_D_XFIFO	0x04	/* W */
+#define W_D_CMDR	0x08	/* W */
+#define W_D_MODE	0x0c	/* R/W */
+#define W_D_TIMR	0x10	/* R/W */
+#define W_ISTA		0x14	/* R_clr */
+#define W_IMASK		0x18	/* R/W */
+#define W_D_EXIR	0x1c	/* R_clr */
+#define W_D_EXIM	0x20	/* R/W */
+#define W_D_STAR	0x24	/* R */
+#define W_D_RSTA	0x28	/* R */
+#define W_D_SAM		0x2c	/* R/W */
+#define W_D_SAP1	0x30	/* R/W */
+#define W_D_SAP2	0x34	/* R/W */
+#define W_D_TAM		0x38	/* R/W */
+#define W_D_TEI1	0x3c	/* R/W */
+#define W_D_TEI2	0x40	/* R/W */
+#define W_D_RBCH	0x44	/* R */
+#define W_D_RBCL	0x48	/* R */
+#define W_TIMR2		0x4c	/* W */
+#define W_L1_RC		0x50	/* R/W */
+#define W_D_CTL		0x54	/* R/W */
+#define W_CIR		0x58	/* R */
+#define W_CIX		0x5c	/* W */
+#define W_SQR		0x60	/* R */
+#define W_SQX		0x64	/* W */
+#define W_PCTL		0x68	/* R/W */
+#define W_MOR		0x6c	/* R */
+#define W_MOX		0x70	/* R/W */
+#define W_MOSR		0x74	/* R_clr */
+#define W_MOCR		0x78	/* R/W */
+#define W_GCR		0x7c	/* R/W */
+
+#define	W_B_RFIFO	0x80	/* R */
+#define	W_B_XFIFO	0x84	/* W */
+#define	W_B_CMDR	0x88	/* W */
+#define	W_B_MODE	0x8c	/* R/W */
+#define	W_B_EXIR	0x90	/* R_clr */
+#define	W_B_EXIM	0x94	/* R/W */
+#define	W_B_STAR	0x98	/* R */
+#define	W_B_ADM1	0x9c	/* R/W */
+#define	W_B_ADM2	0xa0	/* R/W */
+#define	W_B_ADR1	0xa4	/* R/W */
+#define	W_B_ADR2	0xa8	/* R/W */
+#define	W_B_RBCL	0xac	/* R */
+#define	W_B_RBCH	0xb0	/* R */
+
+#define W_XADDR		0xf4	/* R/W */
+#define W_XDATA		0xf8	/* R/W */
+#define W_EPCTL		0xfc	/* W */
+
+/* W6692 register bits */
+
+#define	W_D_CMDR_XRST	0x01
+#define	W_D_CMDR_XME	0x02
+#define	W_D_CMDR_XMS	0x08
+#define	W_D_CMDR_STT	0x10
+#define	W_D_CMDR_RRST	0x40
+#define	W_D_CMDR_RACK	0x80
+
+#define	W_D_MODE_RLP	0x01
+#define	W_D_MODE_DLP	0x02
+#define	W_D_MODE_MFD	0x04
+#define	W_D_MODE_TEE	0x08
+#define	W_D_MODE_TMS	0x10
+#define	W_D_MODE_RACT	0x40
+#define	W_D_MODE_MMS	0x80
+
+#define W_INT_B2_EXI	0x01
+#define W_INT_B1_EXI	0x02
+#define W_INT_D_EXI	0x04
+#define W_INT_XINT0	0x08
+#define W_INT_XINT1	0x10
+#define W_INT_D_XFR	0x20
+#define W_INT_D_RME	0x40
+#define W_INT_D_RMR	0x80
+
+#define W_D_EXI_WEXP	0x01
+#define W_D_EXI_TEXP	0x02
+#define W_D_EXI_ISC	0x04
+#define W_D_EXI_MOC	0x08
+#define W_D_EXI_TIN2	0x10
+#define W_D_EXI_XCOL	0x20
+#define W_D_EXI_XDUN	0x40
+#define W_D_EXI_RDOV	0x80
+
+#define	W_D_STAR_DRDY	0x10
+#define	W_D_STAR_XBZ	0x20
+#define	W_D_STAR_XDOW	0x80
+
+#define W_D_RSTA_RMB	0x10
+#define W_D_RSTA_CRCE	0x20
+#define W_D_RSTA_RDOV	0x40
+
+#define W_D_CTL_SRST	0x20
+
+#define W_CIR_SCC	0x80
+#define W_CIR_ICC	0x40
+#define W_CIR_COD_MASK	0x0f
+
+#define	W_B_CMDR_XRST	0x01
+#define	W_B_CMDR_XME	0x02
+#define	W_B_CMDR_XMS	0x04
+#define	W_B_CMDR_RACT	0x20
+#define	W_B_CMDR_RRST	0x40
+#define	W_B_CMDR_RACK	0x80
+
+#define	W_B_MODE_FTS0	0x01
+#define	W_B_MODE_FTS1	0x02
+#define	W_B_MODE_SW56	0x04
+#define	W_B_MODE_BSW0	0x08
+#define	W_B_MODE_BSW1	0x10
+#define	W_B_MODE_EPCM	0x20
+#define	W_B_MODE_ITF	0x40
+#define	W_B_MODE_MMS	0x80
+
+#define	W_B_EXI_XDUN	0x01
+#define	W_B_EXI_XFR	0x02
+#define	W_B_EXI_RDOV	0x10
+#define	W_B_EXI_RME	0x20
+#define	W_B_EXI_RMR	0x40
+
+#define	W_B_STAR_XBZ	0x01
+#define	W_B_STAR_XDOW	0x04
+#define	W_B_STAR_RMB	0x10
+#define	W_B_STAR_CRCE	0x20
+#define	W_B_STAR_RDOV	0x40
+
+#define	W_B_RBCH_LOV	0x20
+
+/* W6692 Layer1 commands */
+
+#define	W_L1CMD_ECK	0x00
+#define W_L1CMD_RST	0x01
+#define W_L1CMD_SCP	0x04
+#define W_L1CMD_SSP	0x02
+#define W_L1CMD_AR8	0x08
+#define W_L1CMD_AR10	0x09
+#define W_L1CMD_EAL	0x0a
+#define W_L1CMD_DRC	0x0f
+
+/* W6692 Layer1 indications */
+
+#define W_L1IND_CE	0x07
+#define W_L1IND_DRD	0x00
+#define W_L1IND_LD	0x04
+#define W_L1IND_ARD	0x08
+#define W_L1IND_TI	0x0a
+#define W_L1IND_ATI	0x0b
+#define W_L1IND_AI8	0x0c
+#define W_L1IND_AI10	0x0d
+#define W_L1IND_CD	0x0f
+
+/* FIFO thresholds */
+#define W_D_FIFO_THRESH	64
+#define W_B_FIFO_THRESH	64
diff --git a/drivers/isdn/hysdn/Kconfig b/drivers/isdn/hysdn/Kconfig
new file mode 100644
index 0000000..c6d8a70
--- /dev/null
+++ b/drivers/isdn/hysdn/Kconfig
@@ -0,0 +1,18 @@
+#
+# Config.in for HYSDN ISDN driver
+#
+config HYSDN
+	tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
+	depends on m && PROC_FS && PCI && BROKEN_ON_SMP
+	help
+	  Say Y here if you have one of Hypercope's active PCI ISDN cards
+	  Champ, Ergo and Metro. You will then get a module called hysdn.
+	  Please read the file <file:Documentation/isdn/README.hysdn> for more
+	  information.
+
+config HYSDN_CAPI
+	bool "HYSDN CAPI 2.0 support"
+	depends on HYSDN && ISDN_CAPI
+	help
+	  Say Y here if you like to use Hypercope's CAPI 2.0 interface.
+
diff --git a/drivers/isdn/hysdn/Makefile b/drivers/isdn/hysdn/Makefile
new file mode 100644
index 0000000..da63b63
--- /dev/null
+++ b/drivers/isdn/hysdn/Makefile
@@ -0,0 +1,11 @@
+# Makefile for the hysdn ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_HYSDN)		+= hysdn.o
+
+# Multipart objects.
+
+hysdn-y				:= hysdn_procconf.o hysdn_proclog.o boardergo.o \
+				   hysdn_boot.o hysdn_sched.o hysdn_net.o hysdn_init.o
+hysdn-$(CONFIG_HYSDN_CAPI)	+= hycapi.o
diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c
new file mode 100644
index 0000000..e19a01a
--- /dev/null
+++ b/drivers/isdn/hysdn/boardergo.c
@@ -0,0 +1,453 @@
+/* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 kai Exp $
+ *
+ * Linux driver for HYSDN cards, specific routines for ergo type boards.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
+ * DPRAM interface and layout with only minor differences all related
+ * stuff is done here, not in separate modules.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "hysdn_defs.h"
+#include "boardergo.h"
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/***************************************************/
+/* The cards interrupt handler. Called from system */
+/***************************************************/
+static irqreturn_t
+ergo_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+	hysdn_card *card = dev_id;	/* parameter from irq */
+	tErgDpram *dpr;
+	ulong flags;
+	uchar volatile b;
+
+	if (!card)
+		return IRQ_NONE;		/* error -> spurious interrupt */
+	if (!card->irq_enabled)
+		return IRQ_NONE;		/* other device interrupting or irq switched off */
+
+	save_flags(flags);
+	cli();			/* no further irqs allowed */
+
+	if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
+		restore_flags(flags);	/* restore old state */
+		return IRQ_NONE;		/* no interrupt requested by E1 */
+	}
+	/* clear any pending ints on the board */
+	dpr = card->dpram;
+	b = dpr->ToPcInt;	/* clear for ergo */
+	b |= dpr->ToPcIntMetro;	/* same for metro */
+	b |= dpr->ToHyInt;	/* and for champ */
+
+	/* start kernel task immediately after leaving all interrupts */
+	if (!card->hw_lock)
+		schedule_work(&card->irq_queue);
+	restore_flags(flags);
+	return IRQ_HANDLED;
+}				/* ergo_interrupt */
+
+/******************************************************************************/
+/* ergo_irq_bh is the function called by the immediate kernel task list after */
+/* being activated with queue_task and no interrupts active. This task is the */
+/* only one handling data transfer from or to the card after booting. The task */
+/* may be queued from everywhere (interrupts included).                       */
+/******************************************************************************/
+static void
+ergo_irq_bh(hysdn_card * card)
+{
+	tErgDpram *dpr;
+	int again;
+	ulong flags;
+
+	if (card->state != CARD_STATE_RUN)
+		return;		/* invalid call */
+
+	dpr = card->dpram;	/* point to DPRAM */
+
+	save_flags(flags);
+	cli();
+	if (card->hw_lock) {
+		restore_flags(flags);	/* hardware currently unavailable */
+		return;
+	}
+	card->hw_lock = 1;	/* we now lock the hardware */
+
+	do {
+		sti();		/* reenable other ints */
+		again = 0;	/* assume loop not to be repeated */
+
+		if (!dpr->ToHyFlag) {
+			/* we are able to send a buffer */
+
+			if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
+					   ERG_TO_HY_BUF_SIZE)) {
+				dpr->ToHyFlag = 1;	/* enable tx */
+				again = 1;	/* restart loop */
+			}
+		}		/* we are able to send a buffer */
+		if (dpr->ToPcFlag) {
+			/* a message has arrived for us, handle it */
+
+			if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
+				dpr->ToPcFlag = 0;	/* we worked the data */
+				again = 1;	/* restart loop */
+			}
+		}		/* a message has arrived for us */
+		cli();		/* no further ints */
+		if (again) {
+			dpr->ToHyInt = 1;
+			dpr->ToPcInt = 1;	/* interrupt to E1 for all cards */
+		} else
+			card->hw_lock = 0;	/* free hardware again */
+	} while (again);	/* until nothing more to do */
+
+	restore_flags(flags);
+}				/* ergo_irq_bh */
+
+
+/*********************************************************/
+/* stop the card (hardware reset) and disable interrupts */
+/*********************************************************/
+static void
+ergo_stopcard(hysdn_card * card)
+{
+	ulong flags;
+	uchar val;
+
+	hysdn_net_release(card);	/* first release the net device if existing */
+#ifdef CONFIG_HYSDN_CAPI
+	hycapi_capi_stop(card);
+#endif /* CONFIG_HYSDN_CAPI */
+	save_flags(flags);
+	cli();
+	val = bytein(card->iobase + PCI9050_INTR_REG);	/* get actual value */
+	val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1);	/* mask irq */
+	byteout(card->iobase + PCI9050_INTR_REG, val);
+	card->irq_enabled = 0;
+	byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET);	/* reset E1 processor */
+	card->state = CARD_STATE_UNUSED;
+	card->err_log_state = ERRLOG_STATE_OFF;		/* currently no log active */
+
+	restore_flags(flags);
+}				/* ergo_stopcard */
+
+/**************************************************************************/
+/* enable or disable the cards error log. The event is queued if possible */
+/**************************************************************************/
+static void
+ergo_set_errlog_state(hysdn_card * card, int on)
+{
+	ulong flags;
+
+	if (card->state != CARD_STATE_RUN) {
+		card->err_log_state = ERRLOG_STATE_OFF;		/* must be off */
+		return;
+	}
+	save_flags(flags);
+	cli();
+
+	if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
+	    ((card->err_log_state == ERRLOG_STATE_ON) && on)) {
+		restore_flags(flags);
+		return;		/* nothing to do */
+	}
+	if (on)
+		card->err_log_state = ERRLOG_STATE_START;	/* request start */
+	else
+		card->err_log_state = ERRLOG_STATE_STOP;	/* request stop */
+
+	restore_flags(flags);
+	schedule_work(&card->irq_queue);
+}				/* ergo_set_errlog_state */
+
+/******************************************/
+/* test the cards RAM and return 0 if ok. */
+/******************************************/
+static const char TestText[36] = "This Message is filler, why read it";
+
+static int
+ergo_testram(hysdn_card * card)
+{
+	tErgDpram *dpr = card->dpram;
+
+	memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable));	/* clear all Traps */
+	dpr->ToHyInt = 1;	/* E1 INTR state forced */
+
+	memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+	       sizeof(TestText));
+	if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+		   sizeof(TestText)))
+		return (-1);
+
+	memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+	       sizeof(TestText));
+	if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+		   sizeof(TestText)))
+		return (-1);
+
+	return (0);
+}				/* ergo_testram */
+
+/*****************************************************************************/
+/* this function is intended to write stage 1 boot image to the cards buffer */
+/* this is done in two steps. First the 1024 hi-words are written (offs=0),  */
+/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the   */
+/* PCI-write-buffers flushed and the card is taken out of reset.             */
+/* The function then waits for a reaction of the E1 processor or a timeout.  */
+/* Negative return values are interpreted as errors.                         */
+/*****************************************************************************/
+static int
+ergo_writebootimg(struct HYSDN_CARD *card, uchar * buf, ulong offs)
+{
+	uchar *dst;
+	tErgDpram *dpram;
+	int cnt = (BOOT_IMG_SIZE >> 2);		/* number of words to move and swap (byte order!) */
+	
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
+
+	dst = card->dpram;	/* pointer to start of DPRAM */
+	dst += (offs + ERG_DPRAM_FILL_SIZE);	/* offset in the DPRAM */
+	while (cnt--) {
+		*dst++ = *(buf + 1);	/* high byte */
+		*dst++ = *buf;	/* low byte */
+		dst += 2;	/* point to next longword */
+		buf += 2;	/* buffer only filled with words */
+	}
+
+	/* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
+	/* flush the PCI-write-buffer and take the E1 out of reset */
+	if (offs) {
+		memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE);	/* fill the DPRAM still not cleared */
+		dpram = card->dpram;	/* get pointer to dpram structure */
+		dpram->ToHyNoDpramErrLog = 0xFF;	/* write a dpram register */
+		while (!dpram->ToHyNoDpramErrLog);	/* reread volatile register to flush PCI */
+
+		byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN);	/* start E1 processor */
+		/* the interrupts are still masked */
+
+		sti();
+		msleep_interruptible(20);		/* Timeout 20ms */
+
+		if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
+			if (card->debug_flags & LOG_POF_CARD)
+				hysdn_addlog(card, "ERGO: write bootldr no answer");
+			return (-ERR_BOOTIMG_FAIL);
+		}
+	}			/* start_boot_img */
+	return (0);		/* successful */
+}				/* ergo_writebootimg */
+
+/********************************************************************************/
+/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
+/* using the boot spool mechanism. If everything works fine 0 is returned. In   */
+/* case of errors a negative error value is returned.                           */
+/********************************************************************************/
+static int
+ergo_writebootseq(struct HYSDN_CARD *card, uchar * buf, int len)
+{
+	tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
+	uchar *dst;
+	uchar buflen;
+	int nr_write;
+	uchar tmp_rdptr;
+	uchar wr_mirror;
+	int i;
+
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
+
+	dst = sp->Data;		/* point to data in spool structure */
+	buflen = sp->Len;	/* maximum len of spooled data */
+	wr_mirror = sp->WrPtr;	/* only once read */
+	sti();
+
+	/* try until all bytes written or error */
+	i = 0x1000;		/* timeout value */
+	while (len) {
+
+		/* first determine the number of bytes that may be buffered */
+		do {
+			tmp_rdptr = sp->RdPtr;	/* first read the pointer */
+			i--;	/* decrement timeout */
+		} while (i && (tmp_rdptr != sp->RdPtr));	/* wait for stable pointer */
+
+		if (!i) {
+			if (card->debug_flags & LOG_POF_CARD)
+				hysdn_addlog(card, "ERGO: write boot seq timeout");
+			return (-ERR_BOOTSEQ_FAIL);	/* value not stable -> timeout */
+		}
+		if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
+			nr_write += buflen;	/* now we got number of free bytes - 1 in buffer */
+
+		if (!nr_write)
+			continue;	/* no free bytes in buffer */
+
+		if (nr_write > len)
+			nr_write = len;		/* limit if last few bytes */
+		i = 0x1000;	/* reset timeout value */
+
+		/* now we know how much bytes we may put in the puffer */
+		len -= nr_write;	/* we savely could adjust len before output */
+		while (nr_write--) {
+			*(dst + wr_mirror) = *buf++;	/* output one byte */
+			if (++wr_mirror >= buflen)
+				wr_mirror = 0;
+			sp->WrPtr = wr_mirror;	/* announce the next byte to E1 */
+		}		/* while (nr_write) */
+
+	}			/* while (len) */
+	return (0);
+}				/* ergo_writebootseq */
+
+/***********************************************************************************/
+/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
+/* boot process. If the process has been successful 0 is returned otherwise a     */
+/* negative error code is returned.                                                */
+/***********************************************************************************/
+static int
+ergo_waitpofready(struct HYSDN_CARD *card)
+{
+	tErgDpram *dpr = card->dpram;	/* pointer to DPRAM structure */
+	int timecnt = 10000 / 50;	/* timeout is 10 secs max. */
+	ulong flags;
+	int msg_size;
+	int i;
+
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: waiting for pof ready");
+	while (timecnt--) {
+		/* wait until timeout  */
+
+		if (dpr->ToPcFlag) {
+			/* data has arrived */
+
+			if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
+			    (dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
+			    (dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
+			    ((*(ulong *) dpr->ToPcBuf) != RDY_MAGIC))
+				break;	/* an error occurred */
+
+			/* Check for additional data delivered during SysReady */
+			msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
+			if (msg_size > 0)
+				if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
+					break;
+
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "ERGO: pof boot success");
+			save_flags(flags);
+			cli();
+
+			card->state = CARD_STATE_RUN;	/* now card is running */
+			/* enable the cards interrupt */
+			byteout(card->iobase + PCI9050_INTR_REG,
+				bytein(card->iobase + PCI9050_INTR_REG) |
+			(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
+			card->irq_enabled = 1;	/* we are ready to receive interrupts */
+
+			dpr->ToPcFlag = 0;	/* reset data indicator */
+			dpr->ToHyInt = 1;
+			dpr->ToPcInt = 1;	/* interrupt to E1 for all cards */
+
+			restore_flags(flags);
+			if ((hynet_enable & (1 << card->myid)) 
+			    && (i = hysdn_net_create(card))) 
+			{
+				ergo_stopcard(card);
+				card->state = CARD_STATE_BOOTERR;
+				return (i);
+			}
+#ifdef CONFIG_HYSDN_CAPI
+			if((i = hycapi_capi_create(card))) {
+				printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n");
+			}
+#endif /* CONFIG_HYSDN_CAPI */
+			return (0);	/* success */
+		}		/* data has arrived */
+		sti();
+		msleep_interruptible(50);		/* Timeout 50ms */
+	}			/* wait until timeout */
+
+	if (card->debug_flags & LOG_POF_CARD)
+		hysdn_addlog(card, "ERGO: pof boot ready timeout");
+	return (-ERR_POF_TIMEOUT);
+}				/* ergo_waitpofready */
+
+
+
+/************************************************************************************/
+/* release the cards hardware. Before releasing do a interrupt disable and hardware */
+/* reset. Also unmap dpram.                                                         */
+/* Use only during module release.                                                  */
+/************************************************************************************/
+static void
+ergo_releasehardware(hysdn_card * card)
+{
+	ergo_stopcard(card);	/* first stop the card if not already done */
+	free_irq(card->irq, card);	/* release interrupt */
+	release_region(card->iobase + PCI9050_INTR_REG, 1);	/* release all io ports */
+	release_region(card->iobase + PCI9050_USER_IO, 1);
+	vfree(card->dpram);
+	card->dpram = NULL;	/* release shared mem */
+}				/* ergo_releasehardware */
+
+
+/*********************************************************************************/
+/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
+/* value is returned.                                                            */
+/* Use only during module init.                                                  */
+/*********************************************************************************/
+int
+ergo_inithardware(hysdn_card * card)
+{
+	if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN")) 
+		return (-1);
+	if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) {
+		release_region(card->iobase + PCI9050_INTR_REG, 1);
+		return (-1);	/* ports already in use */
+	}
+	card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
+	if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) {
+		release_region(card->iobase + PCI9050_INTR_REG, 1);
+		release_region(card->iobase + PCI9050_USER_IO, 1);
+		return (-1);
+	}
+
+	ergo_stopcard(card);	/* disable interrupts */
+	if (request_irq(card->irq, ergo_interrupt, SA_SHIRQ, "HYSDN", card)) {
+		ergo_releasehardware(card); /* return the acquired hardware */
+		return (-1);
+	}
+	/* success, now setup the function pointers */
+	card->stopcard = ergo_stopcard;
+	card->releasehardware = ergo_releasehardware;
+	card->testram = ergo_testram;
+	card->writebootimg = ergo_writebootimg;
+	card->writebootseq = ergo_writebootseq;
+	card->waitpofready = ergo_waitpofready;
+	card->set_errlog_state = ergo_set_errlog_state;
+	INIT_WORK(&card->irq_queue, (void *) (void *) ergo_irq_bh, card);
+
+	return (0);
+}				/* ergo_inithardware */
diff --git a/drivers/isdn/hysdn/boardergo.h b/drivers/isdn/hysdn/boardergo.h
new file mode 100644
index 0000000..b56ff08
--- /dev/null
+++ b/drivers/isdn/hysdn/boardergo.h
@@ -0,0 +1,100 @@
+/* $Id: boardergo.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+/************************************************/
+/* defines for the dual port memory of the card */
+/************************************************/
+#define ERG_DPRAM_PAGE_SIZE 0x2000	/* DPRAM occupies a 8K page */
+#define BOOT_IMG_SIZE 4096
+#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
+
+#define ERG_TO_HY_BUF_SIZE  0x0E00	/* 3072 bytes buffer size to card */
+#define ERG_TO_PC_BUF_SIZE  0x0E00	/* 3072 bytes to PC, too */
+
+/* following DPRAM layout copied from OS2-driver boarderg.h */
+typedef struct ErgDpram_tag {
+/*0000 */ uchar ToHyBuf[ERG_TO_HY_BUF_SIZE];
+/*0E00 */ uchar ToPcBuf[ERG_TO_PC_BUF_SIZE];
+
+	/*1C00 */ uchar bSoftUart[SIZE_RSV_SOFT_UART];
+	/* size 0x1B0 */
+
+	/*1DB0 *//* tErrLogEntry */ uchar volatile ErrLogMsg[64];
+	/* size 64 bytes */
+	/*1DB0  ulong ulErrType;               */
+	/*1DB4  ulong ulErrSubtype;            */
+	/*1DB8  ulong ucTextSize;              */
+	/*1DB9  ulong ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
+	/*1DF0 */
+
+/*1DF0 */ word volatile ToHyChannel;
+/*1DF2 */ word volatile ToHySize;
+	/*1DF4 */ uchar volatile ToHyFlag;
+	/* !=0: msg for Hy waiting */
+	/*1DF5 */ uchar volatile ToPcFlag;
+	/* !=0: msg for PC waiting */
+/*1DF6 */ word volatile ToPcChannel;
+/*1DF8 */ word volatile ToPcSize;
+	/*1DFA */ uchar bRes1DBA[0x1E00 - 0x1DFA];
+	/* 6 bytes */
+
+/*1E00 */ uchar bRestOfEntryTbl[0x1F00 - 0x1E00];
+/*1F00 */ ulong TrapTable[62];
+	/*1FF8 */ uchar bRes1FF8[0x1FFB - 0x1FF8];
+	/* low part of reset vetor */
+/*1FFB */ uchar ToPcIntMetro;
+	/* notes:
+	 * - metro has 32-bit boot ram - accessing
+	 *   ToPcInt and ToHyInt would be the same;
+	 *   so we moved ToPcInt to 1FFB.
+	 *   Because on the PC side both vars are
+	 *   readonly (reseting on int from E1 to PC),
+	 *   we can read both vars on both cards
+	 *   without destroying anything.
+	 * - 1FFB is the high byte of the reset vector,
+	 *   so E1 side should NOT change this byte
+	 *   when writing!
+	 */
+/*1FFC */ uchar volatile ToHyNoDpramErrLog;
+	/* note: ToHyNoDpramErrLog is used to inform
+	 *       boot loader, not to use DPRAM based
+	 *       ErrLog; when DOS driver is rewritten
+	 *       this becomes obsolete
+	 */
+/*1FFD */ uchar bRes1FFD;
+	/*1FFE */ uchar ToPcInt;
+	/* E1_intclear; on CHAMP2: E1_intset   */
+	/*1FFF */ uchar ToHyInt;
+	/* E1_intset;   on CHAMP2: E1_intclear */
+} tErgDpram;
+
+/**********************************************/
+/* PCI9050 controller local register offsets: */
+/* copied from boarderg.c                     */
+/**********************************************/
+#define PCI9050_INTR_REG    0x4C	/* Interrupt register */
+#define PCI9050_USER_IO     0x51	/* User I/O  register */
+
+				    /* bitmask for PCI9050_INTR_REG: */
+#define PCI9050_INTR_REG_EN1    0x01	/* 1= enable (def.), 0= disable */
+#define PCI9050_INTR_REG_POL1   0x02	/* 1= active high (def.), 0= active low */
+#define PCI9050_INTR_REG_STAT1  0x04	/* 1= intr. active, 0= intr. not active (def.) */
+#define PCI9050_INTR_REG_ENPCI  0x40	/* 1= PCI interrupts enable (def.) */
+
+				    /* bitmask for PCI9050_USER_IO: */
+#define PCI9050_USER_IO_EN3     0x02	/* 1= disable      , 0= enable (def.) */
+#define PCI9050_USER_IO_DIR3    0x04	/* 1= output (def.), 0= input         */
+#define PCI9050_USER_IO_DAT3    0x08	/* 1= high (def.)  , 0= low           */
+
+#define PCI9050_E1_RESET    (                     PCI9050_USER_IO_DIR3)		/* 0x04 */
+#define PCI9050_E1_RUN      (PCI9050_USER_IO_DAT3|PCI9050_USER_IO_DIR3)		/* 0x0C */
diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c
new file mode 100644
index 0000000..8ee25b2
--- /dev/null
+++ b/drivers/isdn/hysdn/hycapi.c
@@ -0,0 +1,797 @@
+/* $Id: hycapi.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, CAPI2.0-Interface.
+ *
+ * Author    Ulrich Albrecht <u.albrecht@hypercope.de> for Hypercope GmbH
+ * Copyright 2000 by Hypercope GmbH
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define	VER_DRIVER	0
+#define	VER_CARDTYPE	1
+#define	VER_HWID	2
+#define	VER_SERIAL	3
+#define	VER_OPTION	4
+#define	VER_PROTO	5
+#define	VER_PROFILE	6
+#define	VER_CAPI	7
+
+#include "hysdn_defs.h"
+#include <linux/kernelcapi.h>
+
+static char hycapi_revision[]="$Revision: 1.8.6.4 $";
+
+unsigned int hycapi_enable = 0xffffffff; 
+MODULE_PARM(hycapi_enable, "i");
+
+typedef struct _hycapi_appl {
+	unsigned int ctrl_mask;
+	capi_register_params rp;
+	struct sk_buff *listen_req[CAPI_MAXCONTR];
+} hycapi_appl;
+
+static hycapi_appl hycapi_applications[CAPI_MAXAPPL];
+
+static inline int _hycapi_appCheck(int app_id, int ctrl_no)
+{
+	if((ctrl_no <= 0) || (ctrl_no > CAPI_MAXCONTR) || (app_id <= 0) ||
+	   (app_id > CAPI_MAXAPPL))
+	{
+		printk(KERN_ERR "HYCAPI: Invalid request app_id %d for controller %d", app_id, ctrl_no);
+		return -1;
+	}
+	return ((hycapi_applications[app_id-1].ctrl_mask & (1 << (ctrl_no-1))) != 0);
+}
+
+/******************************
+Kernel-Capi callback reset_ctr
+******************************/     
+
+void 
+hycapi_reset_ctr(struct capi_ctr *ctrl)
+{
+	hycapictrl_info *cinfo = ctrl->driverdata;
+
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "HYCAPI hycapi_reset_ctr\n");
+#endif
+	capilib_release(&cinfo->ncci_head);
+	capi_ctr_reseted(ctrl);
+}
+
+/******************************
+Kernel-Capi callback remove_ctr
+******************************/     
+
+void 
+hycapi_remove_ctr(struct capi_ctr *ctrl)
+{
+	int i;
+	hycapictrl_info *cinfo = NULL;
+	hysdn_card *card = NULL;
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "HYCAPI hycapi_remove_ctr\n");
+#endif 
+	cinfo = (hycapictrl_info *)(ctrl->driverdata);
+	if(!cinfo) {
+		printk(KERN_ERR "No hycapictrl_info set!");
+		return;
+	}    
+	card = cinfo->card;
+	capi_ctr_suspend_output(ctrl);
+	for(i=0; i<CAPI_MAXAPPL;i++) {
+		if(hycapi_applications[i].listen_req[ctrl->cnr-1]) {
+			kfree_skb(hycapi_applications[i].listen_req[ctrl->cnr-1]);
+			hycapi_applications[i].listen_req[ctrl->cnr-1] = NULL;
+		}
+	}
+	detach_capi_ctr(ctrl);
+	ctrl->driverdata = NULL;
+	kfree(card->hyctrlinfo);
+
+		
+	card->hyctrlinfo = NULL;
+}
+
+/***********************************************************
+
+Queue a CAPI-message to the controller.
+
+***********************************************************/
+
+static void
+hycapi_sendmsg_internal(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+	hysdn_card *card = cinfo->card;
+
+	spin_lock_irq(&cinfo->lock);
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_send_message\n");    
+#endif
+	cinfo->skbs[cinfo->in_idx++] = skb;	/* add to buffer list */
+	if (cinfo->in_idx >= HYSDN_MAX_CAPI_SKB)
+		cinfo->in_idx = 0;	/* wrap around */
+	cinfo->sk_count++;		/* adjust counter */
+	if (cinfo->sk_count >= HYSDN_MAX_CAPI_SKB) {
+		/* inform upper layers we're full */
+		printk(KERN_ERR "HYSDN Card%d: CAPI-buffer overrun!\n",
+		       card->myid);	
+		capi_ctr_suspend_output(ctrl);
+	}
+	cinfo->tx_skb = skb;
+	spin_unlock_irq(&cinfo->lock);
+	schedule_work(&card->irq_queue);
+}
+
+/***********************************************************
+hycapi_register_internal
+
+Send down the CAPI_REGISTER-Command to the controller.
+This functions will also be used if the adapter has been rebooted to
+re-register any applications in the private list.
+
+************************************************************/
+
+static void 
+hycapi_register_internal(struct capi_ctr *ctrl, __u16 appl,
+			 capi_register_params *rp)
+{
+	char ExtFeatureDefaults[] = "49  /0/0/0/0,*/1,*/2,*/3,*/4,*/5,*/6,*/7,*/8,*/9,*";
+	hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+	hysdn_card *card = cinfo->card;
+	struct sk_buff *skb;
+	__u16 len;
+	__u8 _command = 0xa0, _subcommand = 0x80;
+	__u16 MessageNumber = 0x0000;
+	__u16 MessageBufferSize = 0;
+	int slen = strlen(ExtFeatureDefaults);
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_register_appl\n"); 
+#endif
+	MessageBufferSize = rp->level3cnt * rp->datablkcnt * rp->datablklen; 
+
+	len = CAPI_MSG_BASELEN + 8 + slen + 1;
+	if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+		printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
+		       card->myid);
+		return;
+	}
+	memcpy(skb_put(skb,sizeof(__u16)), &len, sizeof(__u16));
+	memcpy(skb_put(skb,sizeof(__u16)), &appl, sizeof(__u16));
+	memcpy(skb_put(skb,sizeof(__u8)), &_command, sizeof(_command));
+	memcpy(skb_put(skb,sizeof(__u8)), &_subcommand, sizeof(_subcommand));
+	memcpy(skb_put(skb,sizeof(__u16)), &MessageNumber, sizeof(__u16));
+	memcpy(skb_put(skb,sizeof(__u16)), &MessageBufferSize, sizeof(__u16)); 
+	memcpy(skb_put(skb,sizeof(__u16)), &(rp->level3cnt), sizeof(__u16));
+	memcpy(skb_put(skb,sizeof(__u16)), &(rp->datablkcnt), sizeof(__u16));
+	memcpy(skb_put(skb,sizeof(__u16)), &(rp->datablklen), sizeof(__u16));
+	memcpy(skb_put(skb,slen), ExtFeatureDefaults, slen);
+	hycapi_applications[appl-1].ctrl_mask |= (1 << (ctrl->cnr-1));    
+	hycapi_send_message(ctrl, skb);    
+}
+
+/************************************************************
+hycapi_restart_internal
+
+After an adapter has been rebootet, re-register all applications and
+send a LISTEN_REQ (if there has been such a thing )
+
+*************************************************************/
+
+static void hycapi_restart_internal(struct capi_ctr *ctrl)
+{
+	int i;
+	struct sk_buff *skb;
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_WARNING "HYSDN: hycapi_restart_internal");
+#endif
+	for(i=0; i<CAPI_MAXAPPL; i++) {
+		if(_hycapi_appCheck(i+1, ctrl->cnr) == 1) {
+			hycapi_register_internal(ctrl, i+1, 
+						 &hycapi_applications[i].rp);
+			if(hycapi_applications[i].listen_req[ctrl->cnr-1]) {
+				skb = skb_copy(hycapi_applications[i].listen_req[ctrl->cnr-1], GFP_ATOMIC);
+				hycapi_sendmsg_internal(ctrl, skb);
+			}
+		}
+	}
+}
+
+/*************************************************************
+Register an application.
+Error-checking is done for CAPI-compliance.
+
+The application is recorded in the internal list.
+*************************************************************/
+
+void 
+hycapi_register_appl(struct capi_ctr *ctrl, __u16 appl, 
+		     capi_register_params *rp)
+{
+	int MaxLogicalConnections = 0, MaxBDataBlocks = 0, MaxBDataLen = 0;
+	hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+	hysdn_card *card = cinfo->card;
+	int chk = _hycapi_appCheck(appl, ctrl->cnr);
+	if(chk < 0) {
+		return;
+	}
+	if(chk == 1) {
+		printk(KERN_INFO "HYSDN: apl %d already registered\n", appl);
+		return;
+	}
+	MaxBDataBlocks = rp->datablkcnt > CAPI_MAXDATAWINDOW ? CAPI_MAXDATAWINDOW : rp->datablkcnt;
+	rp->datablkcnt = MaxBDataBlocks;
+	MaxBDataLen = rp->datablklen < 1024 ? 1024 : rp->datablklen ;
+	rp->datablklen = MaxBDataLen;
+	
+	MaxLogicalConnections = rp->level3cnt;
+	if (MaxLogicalConnections < 0) {
+		MaxLogicalConnections = card->bchans * -MaxLogicalConnections; 
+	}
+	if (MaxLogicalConnections == 0) {
+		MaxLogicalConnections = card->bchans;
+	}
+	
+	rp->level3cnt = MaxLogicalConnections;
+	memcpy(&hycapi_applications[appl-1].rp, 
+	       rp, sizeof(capi_register_params));
+}
+
+/*********************************************************************
+
+hycapi_release_internal
+
+Send down a CAPI_RELEASE to the controller.
+*********************************************************************/
+
+static void hycapi_release_internal(struct capi_ctr *ctrl, __u16 appl)
+{
+	hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+	hysdn_card *card = cinfo->card;
+	struct sk_buff *skb;
+	__u16 len;
+	__u8 _command = 0xa1, _subcommand = 0x80;
+	__u16 MessageNumber = 0x0000;
+
+	capilib_release_appl(&cinfo->ncci_head, appl);
+
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_release_appl\n");
+#endif
+	len = CAPI_MSG_BASELEN;
+	if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+		printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
+		       card->myid);
+		return;
+	}
+	memcpy(skb_put(skb,sizeof(__u16)), &len, sizeof(__u16));
+	memcpy(skb_put(skb,sizeof(__u16)), &appl, sizeof(__u16));
+	memcpy(skb_put(skb,sizeof(__u8)), &_command, sizeof(_command));
+	memcpy(skb_put(skb,sizeof(__u8)), &_subcommand, sizeof(_subcommand));
+	memcpy(skb_put(skb,sizeof(__u16)), &MessageNumber, sizeof(__u16));    
+	hycapi_send_message(ctrl, skb);    
+	hycapi_applications[appl-1].ctrl_mask &= ~(1 << (ctrl->cnr-1));    
+}
+
+/******************************************************************
+hycapi_release_appl
+
+Release the application from the internal list an remove it's 
+registration at controller-level
+******************************************************************/
+
+void 
+hycapi_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+	int chk;
+
+	chk = _hycapi_appCheck(appl, ctrl->cnr);
+	if(chk<0) {
+		printk(KERN_ERR "HYCAPI: Releasing invalid appl %d on controller %d\n", appl, ctrl->cnr);
+		return;
+	}
+	if(hycapi_applications[appl-1].listen_req[ctrl->cnr-1]) {
+		kfree_skb(hycapi_applications[appl-1].listen_req[ctrl->cnr-1]);
+		hycapi_applications[appl-1].listen_req[ctrl->cnr-1] = NULL;
+	}
+	if(chk == 1)
+	{
+		hycapi_release_internal(ctrl, appl);
+	}
+}
+
+
+/**************************************************************
+Kill a single controller.
+**************************************************************/
+
+int hycapi_capi_release(hysdn_card *card)
+{
+	hycapictrl_info *cinfo = card->hyctrlinfo;
+	struct capi_ctr *ctrl;
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_capi_release\n");
+#endif
+	if(cinfo) {
+		ctrl = &cinfo->capi_ctrl;
+		hycapi_remove_ctr(ctrl);
+	}
+	return 0;
+}
+
+/**************************************************************
+hycapi_capi_stop
+
+Stop CAPI-Output on a card. (e.g. during reboot)
+***************************************************************/
+
+int hycapi_capi_stop(hysdn_card *card)
+{
+	hycapictrl_info *cinfo = card->hyctrlinfo;
+	struct capi_ctr *ctrl;
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_capi_stop\n");
+#endif
+	if(cinfo) {
+		ctrl = &cinfo->capi_ctrl;
+/*		ctrl->suspend_output(ctrl); */
+		capi_ctr_reseted(ctrl);
+	}
+	return 0;
+}
+
+/***************************************************************
+hycapi_send_message
+
+Send a message to the controller.
+
+Messages are parsed for their Command/Subcommand-type, and appropriate
+action's are performed.
+
+Note that we have to muck around with a 64Bit-DATA_REQ as there are
+firmware-releases that do not check the MsgLen-Indication!
+
+***************************************************************/
+
+u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	__u16 appl_id;
+	int _len, _len2;
+	__u8 msghead[64];
+	hycapictrl_info *cinfo = ctrl->driverdata;
+	u16 retval = CAPI_NOERROR;
+
+	appl_id = CAPIMSG_APPID(skb->data);
+	switch(_hycapi_appCheck(appl_id, ctrl->cnr))
+	{
+		case 0:
+/*			printk(KERN_INFO "Need to register\n"); */
+			hycapi_register_internal(ctrl, 
+						 appl_id,
+						 &(hycapi_applications[appl_id-1].rp));
+			break;
+		case 1:
+			break;
+		default:
+			printk(KERN_ERR "HYCAPI: Controller mixup!\n");
+			retval = CAPI_ILLAPPNR;
+			goto out;
+	}
+	switch(CAPIMSG_CMD(skb->data)) {		
+		case CAPI_DISCONNECT_B3_RESP:
+			capilib_free_ncci(&cinfo->ncci_head, appl_id, 
+					  CAPIMSG_NCCI(skb->data));
+			break;
+		case CAPI_DATA_B3_REQ:
+			_len = CAPIMSG_LEN(skb->data);
+			if (_len > 22) {
+				_len2 = _len - 22;
+				memcpy(msghead, skb->data, 22);
+				memcpy(skb->data + _len2, msghead, 22);
+				skb_pull(skb, _len2);
+				CAPIMSG_SETLEN(skb->data, 22);
+				retval = capilib_data_b3_req(&cinfo->ncci_head,
+							     CAPIMSG_APPID(skb->data),
+							     CAPIMSG_NCCI(skb->data),
+							     CAPIMSG_MSGID(skb->data));
+			}
+			break;
+		case CAPI_LISTEN_REQ:
+			if(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1])
+			{
+				kfree_skb(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1]);
+				hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1] = NULL;
+			}
+			if (!(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1] = skb_copy(skb, GFP_ATOMIC))) 
+			{
+				printk(KERN_ERR "HYSDN: memory squeeze in private_listen\n");
+			} 
+			break;
+		default:
+			break;
+	}
+ out:
+	if (retval == CAPI_NOERROR)
+		hycapi_sendmsg_internal(ctrl, skb);
+	else 
+		dev_kfree_skb_any(skb);
+
+	return retval;
+}
+
+/*********************************************************************
+hycapi_read_proc
+
+Informations provided in the /proc/capi-entries.
+
+*********************************************************************/
+
+int hycapi_read_proc(char *page, char **start, off_t off,
+		     int count, int *eof, struct capi_ctr *ctrl)
+{
+	hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+	hysdn_card *card = cinfo->card;
+	int len = 0;
+	char *s;
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_read_proc\n");    
+#endif
+	len += sprintf(page+len, "%-16s %s\n", "name", cinfo->cardname);
+	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->iobase);
+	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+    
+	switch (card->brdtype) {
+		case BD_PCCARD:  s = "HYSDN Hycard"; break;
+		case BD_ERGO: s = "HYSDN Ergo2"; break;
+		case BD_METRO: s = "HYSDN Metro4"; break;
+		case BD_CHAMP2: s = "HYSDN Champ2";	break;
+		case BD_PLEXUS: s = "HYSDN Plexus30"; break;
+		default: s = "???"; break;
+	}
+	len += sprintf(page+len, "%-16s %s\n", "type", s);
+	if ((s = cinfo->version[VER_DRIVER]) != 0)
+		len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
+		len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+	if ((s = cinfo->version[VER_SERIAL]) != 0)
+		len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+    
+	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+    
+	if (off+count >= len)
+		*eof = 1;
+	if (len < off)
+		return 0;
+	*start = page + off;
+	return ((count < len-off) ? count : len-off);
+}
+
+/**************************************************************
+hycapi_load_firmware
+
+This does NOT load any firmware, but the callback somehow is needed
+on capi-interface registration.
+
+**************************************************************/
+
+int hycapi_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_load_firmware\n");    
+#endif
+	return 0;
+}
+
+
+char *hycapi_procinfo(struct capi_ctr *ctrl)
+{
+	hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_proc_info\n");    
+#endif
+	if (!cinfo)
+		return "";
+	sprintf(cinfo->infobuf, "%s %s 0x%x %d %s",
+		cinfo->cardname[0] ? cinfo->cardname : "-",
+		cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+		cinfo->card ? cinfo->card->iobase : 0x0,
+		cinfo->card ? cinfo->card->irq : 0,
+		hycapi_revision
+		);
+	return cinfo->infobuf;
+}
+
+/******************************************************************
+hycapi_rx_capipkt
+
+Receive a capi-message.
+
+All B3_DATA_IND are converted to 64K-extension compatible format.
+New nccis are created if necessary.
+*******************************************************************/
+
+void
+hycapi_rx_capipkt(hysdn_card * card, uchar * buf, word len)
+{
+	struct sk_buff *skb;
+	hycapictrl_info *cinfo = card->hyctrlinfo;
+	struct capi_ctr *ctrl;
+	__u16 ApplId;
+	__u16 MsgLen, info;
+	__u16 len2, CapiCmd;
+	__u32 CP64[2] = {0,0};
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_rx_capipkt\n");    
+#endif
+	if(!cinfo) {
+		return;
+	}
+	ctrl = &cinfo->capi_ctrl;
+	if(len < CAPI_MSG_BASELEN) {
+		printk(KERN_ERR "HYSDN Card%d: invalid CAPI-message, lenght %d!\n",
+		       card->myid, len);
+		return;
+	}	
+	MsgLen = CAPIMSG_LEN(buf);
+	ApplId = CAPIMSG_APPID(buf);
+	CapiCmd = CAPIMSG_CMD(buf);
+	
+	if((CapiCmd == CAPI_DATA_B3_IND) && (MsgLen < 30)) {
+		len2 = len + (30 - MsgLen);
+		if (!(skb = alloc_skb(len2, GFP_ATOMIC))) {
+			printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
+			       card->myid);
+			return;
+		}
+		memcpy(skb_put(skb, MsgLen), buf, MsgLen);
+		memcpy(skb_put(skb, 2*sizeof(__u32)), CP64, 2* sizeof(__u32));
+		memcpy(skb_put(skb, len - MsgLen), buf + MsgLen,
+		       len - MsgLen);
+		CAPIMSG_SETLEN(skb->data, 30);
+	} else {
+		if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+			printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
+			       card->myid);
+			return;
+		}
+		memcpy(skb_put(skb, len), buf, len);
+	}
+	switch(CAPIMSG_CMD(skb->data)) 
+	{
+		case CAPI_CONNECT_B3_CONF:
+/* Check info-field for error-indication: */
+			info = CAPIMSG_U16(skb->data, 12);
+			switch(info)
+			{
+				case 0:
+					capilib_new_ncci(&cinfo->ncci_head, ApplId, CAPIMSG_NCCI(skb->data), 
+							 hycapi_applications[ApplId-1].rp.datablkcnt); 
+					
+					break;
+				case 0x0001:
+					printk(KERN_ERR "HYSDN Card%d: NCPI not supported by current "
+					       "protocol. NCPI ignored.\n", card->myid);
+					break;
+				case 0x2001:
+					printk(KERN_ERR "HYSDN Card%d: Message not supported in"
+					       " current state\n", card->myid);
+					break;
+				case 0x2002:
+					printk(KERN_ERR "HYSDN Card%d: invalid PLCI\n", card->myid);
+					break;		
+				case 0x2004:
+					printk(KERN_ERR "HYSDN Card%d: out of NCCI\n", card->myid);
+					break;				
+				case 0x3008:
+					printk(KERN_ERR "HYSDN Card%d: NCPI not supported\n", 
+					       card->myid);
+					break;	
+				default:
+					printk(KERN_ERR "HYSDN Card%d: Info in CONNECT_B3_CONF: %d\n", 
+					       card->myid, info);
+					break;			
+			}
+			break;
+		case CAPI_CONNECT_B3_IND:
+			capilib_new_ncci(&cinfo->ncci_head, ApplId, 
+					 CAPIMSG_NCCI(skb->data), 
+					 hycapi_applications[ApplId-1].rp.datablkcnt);
+			break;
+	        case CAPI_DATA_B3_CONF:
+			capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+					     CAPIMSG_NCCI(skb->data),
+					     CAPIMSG_MSGID(skb->data));
+			break;
+		default:
+			break;
+	}
+	capi_ctr_handle_message(ctrl, ApplId, skb);
+}
+
+/******************************************************************
+hycapi_tx_capiack
+
+Internally acknowledge a msg sent. This will remove the msg from the
+internal queue.
+
+*******************************************************************/
+
+void hycapi_tx_capiack(hysdn_card * card)
+{
+	hycapictrl_info *cinfo = card->hyctrlinfo;
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_tx_capiack\n");    
+#endif
+	if(!cinfo) {
+		return;
+	}
+	spin_lock_irq(&cinfo->lock);
+	kfree_skb(cinfo->skbs[cinfo->out_idx]);		/* free skb */
+	cinfo->skbs[cinfo->out_idx++] = NULL;
+	if (cinfo->out_idx >= HYSDN_MAX_CAPI_SKB)
+		cinfo->out_idx = 0;	/* wrap around */
+
+	if (cinfo->sk_count-- == HYSDN_MAX_CAPI_SKB)	/* dec usage count */
+		capi_ctr_resume_output(&cinfo->capi_ctrl);
+	spin_unlock_irq(&cinfo->lock);
+}
+
+/***************************************************************
+hycapi_tx_capiget(hysdn_card *card)
+
+This is called when polling for messages to SEND.
+
+****************************************************************/
+
+struct sk_buff *
+hycapi_tx_capiget(hysdn_card *card)
+{
+	hycapictrl_info *cinfo = card->hyctrlinfo;
+	if(!cinfo) {
+		return (struct sk_buff *)NULL;
+	}
+	if (!cinfo->sk_count)
+		return (struct sk_buff *)NULL;	/* nothing available */
+
+	return (cinfo->skbs[cinfo->out_idx]);		/* next packet to send */
+}
+
+
+/**********************************************************
+int hycapi_init()
+
+attach the capi-driver to the kernel-capi.
+
+***********************************************************/
+
+int hycapi_init(void)
+{
+	int i;
+	for(i=0;i<CAPI_MAXAPPL;i++) {
+		memset(&(hycapi_applications[i]), 0, sizeof(hycapi_appl));
+	}
+	return(0);
+}
+
+/**************************************************************
+hycapi_cleanup(void)
+
+detach the capi-driver to the kernel-capi. Actually this should
+free some more ressources. Do that later.
+**************************************************************/
+
+void 
+hycapi_cleanup(void)
+{
+}
+
+/********************************************************************
+hycapi_capi_create(hysdn_card *card)
+
+Attach the card with its capi-ctrl.
+*********************************************************************/
+
+static void hycapi_fill_profile(hysdn_card *card)
+{
+	hycapictrl_info *cinfo = NULL;
+	struct capi_ctr *ctrl = NULL;
+	cinfo = card->hyctrlinfo;
+	if(!cinfo) return;
+	ctrl = &cinfo->capi_ctrl;
+	strcpy(ctrl->manu, "Hypercope");	
+	ctrl->version.majorversion = 2;
+	ctrl->version.minorversion = 0;
+	ctrl->version.majormanuversion = 3;
+	ctrl->version.minormanuversion = 2;
+	ctrl->profile.ncontroller = card->myid;
+	ctrl->profile.nbchannel = card->bchans;
+	ctrl->profile.goptions = GLOBAL_OPTION_INTERNAL_CONTROLLER |
+		GLOBAL_OPTION_B_CHANNEL_OPERATION;
+	ctrl->profile.support1 =  B1_PROT_64KBIT_HDLC |
+		(card->faxchans ? B1_PROT_T30 : 0) |
+		B1_PROT_64KBIT_TRANSPARENT;
+	ctrl->profile.support2 = B2_PROT_ISO7776 |
+		(card->faxchans ? B2_PROT_T30 : 0) |
+		B2_PROT_TRANSPARENT;
+	ctrl->profile.support3 = B3_PROT_TRANSPARENT |
+		B3_PROT_T90NL |
+		(card->faxchans ? B3_PROT_T30 : 0) |
+		(card->faxchans ? B3_PROT_T30EXT : 0) |
+		B3_PROT_ISO8208;
+}	
+
+int 
+hycapi_capi_create(hysdn_card *card)
+{
+	hycapictrl_info *cinfo = NULL;
+	struct capi_ctr *ctrl = NULL;
+	int retval;
+#ifdef HYCAPI_PRINTFNAMES
+	printk(KERN_NOTICE "hycapi_capi_create\n");        
+#endif
+	if((hycapi_enable & (1 << card->myid)) == 0) {
+		return 1;
+	}
+	if (!card->hyctrlinfo) {
+		cinfo = (hycapictrl_info *) kmalloc(sizeof(hycapictrl_info), GFP_ATOMIC);
+		if (!cinfo) {
+			printk(KERN_WARNING "HYSDN: no memory for capi-ctrl.\n");
+			return -ENOMEM;
+		}
+		memset(cinfo, 0, sizeof(hycapictrl_info));
+		card->hyctrlinfo = cinfo;
+		cinfo->card = card;
+		spin_lock_init(&cinfo->lock);
+		INIT_LIST_HEAD(&cinfo->ncci_head);
+
+		switch (card->brdtype) {
+			case BD_PCCARD:  strcpy(cinfo->cardname,"HYSDN Hycard"); break;
+			case BD_ERGO: strcpy(cinfo->cardname,"HYSDN Ergo2"); break;
+			case BD_METRO: strcpy(cinfo->cardname,"HYSDN Metro4"); break;
+			case BD_CHAMP2: strcpy(cinfo->cardname,"HYSDN Champ2"); break;
+			case BD_PLEXUS: strcpy(cinfo->cardname,"HYSDN Plexus30"); break;
+			default: strcpy(cinfo->cardname,"HYSDN ???"); break;
+		}
+
+		ctrl = &cinfo->capi_ctrl;
+		ctrl->driver_name   = "hycapi";
+		ctrl->driverdata    = cinfo;
+		ctrl->register_appl = hycapi_register_appl;
+		ctrl->release_appl  = hycapi_release_appl;
+		ctrl->send_message  = hycapi_send_message;
+		ctrl->load_firmware = hycapi_load_firmware;
+		ctrl->reset_ctr     = hycapi_reset_ctr;
+		ctrl->procinfo      = hycapi_procinfo;
+		ctrl->ctr_read_proc = hycapi_read_proc;
+		strcpy(ctrl->name, cinfo->cardname);
+		ctrl->owner = THIS_MODULE;
+
+		retval = attach_capi_ctr(ctrl);
+		if (retval) {
+			printk(KERN_ERR "hycapi: attach controller failed.\n");
+			return -EBUSY;
+		}
+		/* fill in the blanks: */
+		hycapi_fill_profile(card);
+		capi_ctr_ready(ctrl);
+	} else {
+		/* resume output on stopped ctrl */
+		ctrl = &card->hyctrlinfo->capi_ctrl;
+		hycapi_fill_profile(card);
+		capi_ctr_ready(ctrl);
+		hycapi_restart_internal(ctrl); 
+/*		ctrl->resume_output(ctrl); */
+	}
+	return 0;
+}
diff --git a/drivers/isdn/hysdn/hysdn_boot.c b/drivers/isdn/hysdn/hysdn_boot.c
new file mode 100644
index 0000000..6c04281
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_boot.c
@@ -0,0 +1,399 @@
+/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * specific routines for booting and pof handling
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+
+#include "hysdn_defs.h"
+#include "hysdn_pof.h"
+
+/********************************/
+/* defines for pof read handler */
+/********************************/
+#define POF_READ_FILE_HEAD  0
+#define POF_READ_TAG_HEAD   1
+#define POF_READ_TAG_DATA   2
+
+/************************************************************/
+/* definition of boot specific data area. This data is only */
+/* needed during boot and so allocated dynamically.         */
+/************************************************************/
+struct boot_data {
+	word Cryptor;		/* for use with Decrypt function */
+	word Nrecs;		/* records remaining in file */
+	uchar pof_state;	/* actual state of read handler */
+	uchar is_crypted;	/* card data is crypted */
+	int BufSize;		/* actual number of bytes bufferd */
+	int last_error;		/* last occurred error */
+	word pof_recid;		/* actual pof recid */
+	ulong pof_reclen;	/* total length of pof record data */
+	ulong pof_recoffset;	/* actual offset inside pof record */
+	union {
+		uchar BootBuf[BOOT_BUF_SIZE];	/* buffer as byte count */
+		tPofRecHdr PofRecHdr;	/* header for actual record/chunk */
+		tPofFileHdr PofFileHdr;		/* header from POF file */
+		tPofTimeStamp PofTime;	/* time information */
+	} buf;
+};
+
+/*****************************************************/
+/*  start decryption of successive POF file chuncks.  */
+/*                                                   */
+/*  to be called at start of POF file reading,       */
+/*  before starting any decryption on any POF record. */
+/*****************************************************/
+void
+StartDecryption(struct boot_data *boot)
+{
+	boot->Cryptor = CRYPT_STARTTERM;
+}				/* StartDecryption */
+
+
+/***************************************************************/
+/* decrypt complete BootBuf                                    */
+/* NOTE: decryption must be applied to all or none boot tags - */
+/*       to HI and LO boot loader and (all) seq tags, because  */
+/*       global Cryptor is started for whole POF.              */
+/***************************************************************/
+void
+DecryptBuf(struct boot_data *boot, int cnt)
+{
+	uchar *bufp = boot->buf.BootBuf;
+
+	while (cnt--) {
+		boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
+		*bufp++ ^= (uchar) boot->Cryptor;
+	}
+}				/* DecryptBuf */
+
+/********************************************************************************/
+/* pof_handle_data executes the required actions dependent on the active record */
+/* id. If successful 0 is returned, a negative value shows an error.           */
+/********************************************************************************/
+static int
+pof_handle_data(hysdn_card * card, int datlen)
+{
+	struct boot_data *boot = card->boot;	/* pointer to boot specific data */
+	long l;
+	uchar *imgp;
+	int img_len;
+
+	/* handle the different record types */
+	switch (boot->pof_recid) {
+
+		case TAG_TIMESTMP:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
+			break;
+
+		case TAG_CBOOTDTA:
+			DecryptBuf(boot, datlen);	/* we need to encrypt the buffer */
+		case TAG_BOOTDTA:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+					     (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
+					     datlen, boot->pof_recoffset);
+
+			if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
+				boot->last_error = EPOF_BAD_IMG_SIZE;	/* invalid length */
+				return (boot->last_error);
+			}
+			imgp = boot->buf.BootBuf;	/* start of buffer */
+			img_len = datlen;	/* maximum length to transfer */
+
+			l = POF_BOOT_LOADER_OFF_IN_PAGE -
+			    (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
+			if (l > 0) {
+				/* buffer needs to be truncated */
+				imgp += l;	/* advance pointer */
+				img_len -= l;	/* adjust len */
+			}
+			/* at this point no special handling for data wrapping over buffer */
+			/* is necessary, because the boot image always will be adjusted to */
+			/* match a page boundary inside the buffer.                        */
+			/* The buffer for the boot image on the card is filled in 2 cycles */
+			/* first the 1024 hi-words are put in the buffer, then the low 1024 */
+			/* word are handled in the same way with different offset.         */
+
+			if (img_len > 0) {
+				/* data available for copy */
+				if ((boot->last_error =
+				     card->writebootimg(card, imgp,
+							(boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
+					return (boot->last_error);
+			}
+			break;	/* end of case boot image hi/lo */
+
+		case TAG_CABSDATA:
+			DecryptBuf(boot, datlen);	/* we need to encrypt the buffer */
+		case TAG_ABSDATA:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+					     (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
+					     datlen, boot->pof_recoffset);
+
+			if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0))
+				return (boot->last_error);	/* error writing data */
+
+			if (boot->pof_recoffset + datlen >= boot->pof_reclen)
+				return (card->waitpofready(card));	/* data completely spooled, wait for ready */
+
+			break;	/* end of case boot seq data */
+
+		default:
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
+					     datlen, boot->pof_recoffset);
+
+			break;	/* simply skip record */
+	}			/* switch boot->pof_recid */
+
+	return (0);
+}				/* pof_handle_data */
+
+
+/******************************************************************************/
+/* pof_write_buffer is called when the buffer has been filled with the needed */
+/* number of data bytes. The number delivered is additionally supplied for    */
+/* verification. The functions handles the data and returns the needed number */
+/* of bytes for the next action. If the returned value is 0 or less an error  */
+/* occurred and booting must be aborted.                                       */
+/******************************************************************************/
+int
+pof_write_buffer(hysdn_card * card, int datlen)
+{
+	struct boot_data *boot = card->boot;	/* pointer to boot specific data */
+
+	if (!boot)
+		return (-EFAULT);	/* invalid call */
+	if (boot->last_error < 0)
+		return (boot->last_error);	/* repeated error */
+
+	if (card->debug_flags & LOG_POF_WRITE)
+		hysdn_addlog(card, "POF write: got %d bytes ", datlen);
+
+	switch (boot->pof_state) {
+		case POF_READ_FILE_HEAD:
+			if (card->debug_flags & LOG_POF_WRITE)
+				hysdn_addlog(card, "POF write: checking file header");
+
+			if (datlen != sizeof(tPofFileHdr)) {
+				boot->last_error = -EPOF_INTERNAL;
+				break;
+			}
+			if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
+				boot->last_error = -EPOF_BAD_MAGIC;
+				break;
+			}
+			/* Setup the new state and vars */
+			boot->Nrecs = (word) (boot->buf.PofFileHdr.N_PofRecs);	/* limited to 65535 */
+			boot->pof_state = POF_READ_TAG_HEAD;	/* now start with single tags */
+			boot->last_error = sizeof(tPofRecHdr);	/* new length */
+			break;
+
+		case POF_READ_TAG_HEAD:
+			if (card->debug_flags & LOG_POF_WRITE)
+				hysdn_addlog(card, "POF write: checking tag header");
+
+			if (datlen != sizeof(tPofRecHdr)) {
+				boot->last_error = -EPOF_INTERNAL;
+				break;
+			}
+			boot->pof_recid = boot->buf.PofRecHdr.PofRecId;		/* actual pof recid */
+			boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen;	/* total length */
+			boot->pof_recoffset = 0;	/* no starting offset */
+
+			if (card->debug_flags & LOG_POF_RECORD)
+				hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
+				      boot->pof_recid, boot->pof_reclen);
+
+			boot->pof_state = POF_READ_TAG_DATA;	/* now start with tag data */
+			if (boot->pof_reclen < BOOT_BUF_SIZE)
+				boot->last_error = boot->pof_reclen;	/* limit size */
+			else
+				boot->last_error = BOOT_BUF_SIZE;	/* maximum */
+
+			if (!boot->last_error) {	/* no data inside record */
+				boot->pof_state = POF_READ_TAG_HEAD;	/* now start with single tags */
+				boot->last_error = sizeof(tPofRecHdr);	/* new length */
+			}
+			break;
+
+		case POF_READ_TAG_DATA:
+			if (card->debug_flags & LOG_POF_WRITE)
+				hysdn_addlog(card, "POF write: getting tag data");
+
+			if (datlen != boot->last_error) {
+				boot->last_error = -EPOF_INTERNAL;
+				break;
+			}
+			if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
+				return (boot->last_error);	/* an error occurred */
+			boot->pof_recoffset += datlen;
+			if (boot->pof_recoffset >= boot->pof_reclen) {
+				boot->pof_state = POF_READ_TAG_HEAD;	/* now start with single tags */
+				boot->last_error = sizeof(tPofRecHdr);	/* new length */
+			} else {
+				if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
+					boot->last_error = boot->pof_reclen - boot->pof_recoffset;	/* limit size */
+				else
+					boot->last_error = BOOT_BUF_SIZE;	/* maximum */
+			}
+			break;
+
+		default:
+			boot->last_error = -EPOF_INTERNAL;	/* unknown state */
+			break;
+	}			/* switch (boot->pof_state) */
+
+	return (boot->last_error);
+}				/* pof_write_buffer */
+
+
+/*******************************************************************************/
+/* pof_write_open is called when an open for boot on the cardlog device occurs. */
+/* The function returns the needed number of bytes for the next operation. If  */
+/* the returned number is less or equal 0 an error specified by this code      */
+/* occurred. Additionally the pointer to the buffer data area is set on success */
+/*******************************************************************************/
+int
+pof_write_open(hysdn_card * card, uchar ** bufp)
+{
+	struct boot_data *boot;	/* pointer to boot specific data */
+
+	if (card->boot) {
+		if (card->debug_flags & LOG_POF_OPEN)
+			hysdn_addlog(card, "POF open: already opened for boot");
+		return (-ERR_ALREADY_BOOT);	/* boot already active */
+	}
+	/* error no mem available */
+	if (!(boot = kmalloc(sizeof(struct boot_data), GFP_KERNEL))) {
+		if (card->debug_flags & LOG_MEM_ERR)
+			hysdn_addlog(card, "POF open: unable to allocate mem");
+		return (-EFAULT);
+	}
+	card->boot = boot;
+	card->state = CARD_STATE_BOOTING;
+	memset(boot, 0, sizeof(struct boot_data));
+
+	card->stopcard(card);	/* first stop the card */
+	if (card->testram(card)) {
+		if (card->debug_flags & LOG_POF_OPEN)
+			hysdn_addlog(card, "POF open: DPRAM test failure");
+		boot->last_error = -ERR_BOARD_DPRAM;
+		card->state = CARD_STATE_BOOTERR;	/* show boot error */
+		return (boot->last_error);
+	}
+	boot->BufSize = 0;	/* Buffer is empty */
+	boot->pof_state = POF_READ_FILE_HEAD;	/* read file header */
+	StartDecryption(boot);	/* if POF File should be encrypted */
+
+	if (card->debug_flags & LOG_POF_OPEN)
+		hysdn_addlog(card, "POF open: success");
+
+	*bufp = boot->buf.BootBuf;	/* point to buffer */
+	return (sizeof(tPofFileHdr));
+}				/* pof_write_open */
+
+/********************************************************************************/
+/* pof_write_close is called when an close of boot on the cardlog device occurs. */
+/* The return value must be 0 if everything has happened as desired.            */
+/********************************************************************************/
+int
+pof_write_close(hysdn_card * card)
+{
+	struct boot_data *boot = card->boot;	/* pointer to boot specific data */
+
+	if (!boot)
+		return (-EFAULT);	/* invalid call */
+
+	card->boot = NULL;	/* no boot active */
+	kfree(boot);
+
+	if (card->state == CARD_STATE_RUN)
+		card->set_errlog_state(card, 1);	/* activate error log */
+
+	if (card->debug_flags & LOG_POF_OPEN)
+		hysdn_addlog(card, "POF close: success");
+
+	return (0);
+}				/* pof_write_close */
+
+/*********************************************************************************/
+/* EvalSysrTokData checks additional records delivered with the Sysready Message */
+/* when POF has been booted. A return value of 0 is used if no error occurred.    */
+/*********************************************************************************/
+int
+EvalSysrTokData(hysdn_card * card, uchar * cp, int len)
+{
+	u_char *p;
+	u_char crc;
+
+	if (card->debug_flags & LOG_POF_RECORD)
+		hysdn_addlog(card, "SysReady Token data length %d", len);
+
+	if (len < 2) {
+		hysdn_addlog(card, "SysReady Token Data to short");
+		return (1);
+	}
+	for (p = cp, crc = 0; p < (cp + len - 2); p++)
+		if ((crc & 0x80))
+			crc = (((u_char) (crc << 1)) + 1) + *p;
+		else
+			crc = ((u_char) (crc << 1)) + *p;
+	crc = ~crc;
+	if (crc != *(cp + len - 1)) {
+		hysdn_addlog(card, "SysReady Token Data invalid CRC");
+		return (1);
+	}
+	len--;			/* don't check CRC byte */
+	while (len > 0) {
+
+		if (*cp == SYSR_TOK_END)
+			return (0);	/* End of Token stream */
+
+		if (len < (*(cp + 1) + 2)) {
+			hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
+			return (1);
+		}
+		switch (*cp) {
+			case SYSR_TOK_B_CHAN:	/* 1 */
+				if (*(cp + 1) != 1)
+					return (1);	/* length invalid */
+				card->bchans = *(cp + 2);
+				break;
+
+			case SYSR_TOK_FAX_CHAN:	/* 2 */
+				if (*(cp + 1) != 1)
+					return (1);	/* length invalid */
+				card->faxchans = *(cp + 2);
+				break;
+
+			case SYSR_TOK_MAC_ADDR:	/* 3 */
+				if (*(cp + 1) != 6)
+					return (1);	/* length invalid */
+				memcpy(card->mac_addr, cp + 2, 6);
+				break;
+
+			default:
+				hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
+				break;
+		}
+		len -= (*(cp + 1) + 2);		/* adjust len */
+		cp += (*(cp + 1) + 2);	/* and pointer */
+	}
+
+	hysdn_addlog(card, "no end token found");
+	return (1);
+}				/* EvalSysrTokData */
diff --git a/drivers/isdn/hysdn/hysdn_defs.h b/drivers/isdn/hysdn/hysdn_defs.h
new file mode 100644
index 0000000..4cee26e
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_defs.h
@@ -0,0 +1,298 @@
+/* $Id: hysdn_defs.h,v 1.5.6.3 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * global definitions and exported vars and functions.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef HYSDN_DEFS_H
+#define HYSDN_DEFS_H
+
+#include <linux/config.h>
+#include <linux/hysdn_if.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+
+/****************************/
+/* storage type definitions */
+/****************************/
+#define uchar unsigned char
+#define uint unsigned int
+#define ulong unsigned long
+#define word unsigned short
+
+#include "ince1pc.h"
+
+#ifdef CONFIG_HYSDN_CAPI
+#include <linux/capi.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+
+/***************************/
+/*   CAPI-Profile values.  */
+/***************************/
+
+#define GLOBAL_OPTION_INTERNAL_CONTROLLER 0x0001
+#define GLOBAL_OPTION_EXTERNAL_CONTROLLER 0x0002
+#define GLOBAL_OPTION_HANDSET             0x0004
+#define GLOBAL_OPTION_DTMF                0x0008
+#define GLOBAL_OPTION_SUPPL_SERVICES      0x0010
+#define GLOBAL_OPTION_CHANNEL_ALLOCATION  0x0020
+#define GLOBAL_OPTION_B_CHANNEL_OPERATION 0x0040
+
+#define B1_PROT_64KBIT_HDLC        0x0001
+#define B1_PROT_64KBIT_TRANSPARENT 0x0002
+#define B1_PROT_V110_ASYNCH        0x0004 
+#define B1_PROT_V110_SYNCH         0x0008
+#define B1_PROT_T30                0x0010
+#define B1_PROT_64KBIT_INV_HDLC    0x0020
+#define B1_PROT_56KBIT_TRANSPARENT 0x0040
+
+#define B2_PROT_ISO7776            0x0001
+#define B2_PROT_TRANSPARENT        0x0002
+#define B2_PROT_SDLC               0x0004
+#define B2_PROT_LAPD               0x0008
+#define B2_PROT_T30                0x0010
+#define B2_PROT_PPP                0x0020
+#define B2_PROT_TRANSPARENT_IGNORE_B1_FRAMING_ERRORS 0x0040
+
+#define B3_PROT_TRANSPARENT        0x0001
+#define B3_PROT_T90NL              0x0002
+#define B3_PROT_ISO8208            0x0004
+#define B3_PROT_X25_DCE            0x0008
+#define B3_PROT_T30                0x0010
+#define B3_PROT_T30EXT             0x0020
+
+#define HYSDN_MAXVERSION		8
+
+/* Number of sendbuffers in CAPI-queue */
+#define HYSDN_MAX_CAPI_SKB             20
+
+#endif /* CONFIG_HYSDN_CAPI*/
+
+/************************************************/
+/* constants and bits for debugging/log outputs */
+/************************************************/
+#define LOG_MAX_LINELEN 120
+#define DEB_OUT_SYSLOG  0x80000000	/* output to syslog instead of proc fs */
+#define LOG_MEM_ERR     0x00000001	/* log memory errors like kmalloc failure */
+#define LOG_POF_OPEN    0x00000010	/* log pof open and close activities */
+#define LOG_POF_RECORD  0x00000020	/* log pof record parser */
+#define LOG_POF_WRITE   0x00000040	/* log detailed pof write operation */
+#define LOG_POF_CARD    0x00000080	/* log pof related card functions */
+#define LOG_CNF_LINE    0x00000100	/* all conf lines are put to procfs */
+#define LOG_CNF_DATA    0x00000200	/* non comment conf lines are shown with channel */
+#define LOG_CNF_MISC    0x00000400	/* additional conf line debug outputs */
+#define LOG_SCHED_ASYN  0x00001000	/* debug schedulers async tx routines */
+#define LOG_PROC_OPEN   0x00100000	/* open and close from procfs are logged */
+#define LOG_PROC_ALL    0x00200000	/* all actions from procfs are logged */
+#define LOG_NET_INIT    0x00010000	/* network init and deinit logging */
+
+#define DEF_DEB_FLAGS   0x7fff000f	/* everything is logged to procfs */
+
+/**********************************/
+/* proc filesystem name constants */
+/**********************************/
+#define PROC_SUBDIR_NAME "hysdn"
+#define PROC_CONF_BASENAME "cardconf"
+#define PROC_LOG_BASENAME "cardlog"
+
+/***********************************/
+/* PCI 32 bit parms for IO and MEM */
+/***********************************/
+#define PCI_REG_PLX_MEM_BASE    0
+#define PCI_REG_PLX_IO_BASE     1
+#define PCI_REG_MEMORY_BASE     3
+
+/**************/
+/* card types */
+/**************/
+#define BD_NONE         0U
+#define BD_PERFORMANCE  1U
+#define BD_VALUE        2U
+#define BD_PCCARD       3U
+#define BD_ERGO         4U
+#define BD_METRO        5U
+#define BD_CHAMP2       6U
+#define BD_PLEXUS       7U
+
+/******************************************************/
+/* defined states for cards shown by reading cardconf */
+/******************************************************/
+#define CARD_STATE_UNUSED   0	/* never been used or booted */
+#define CARD_STATE_BOOTING  1	/* booting is in progress */
+#define CARD_STATE_BOOTERR  2	/* a previous boot was aborted */
+#define CARD_STATE_RUN      3	/* card is active */
+
+/*******************************/
+/* defines for error_log_state */
+/*******************************/
+#define ERRLOG_STATE_OFF   0	/* error log is switched off, nothing to do */
+#define ERRLOG_STATE_ON    1	/* error log is switched on, wait for data */
+#define ERRLOG_STATE_START 2	/* start error logging */
+#define ERRLOG_STATE_STOP  3	/* stop error logging */
+
+/*******************************/
+/* data structure for one card */
+/*******************************/
+typedef struct HYSDN_CARD {
+
+	/* general variables for the cards */
+	int myid;		/* own driver card id */
+	uchar bus;		/* pci bus the card is connected to */
+	uchar devfn;		/* slot+function bit encoded */
+	word subsysid;		/* PCI subsystem id */
+	uchar brdtype;		/* type of card */
+	uint bchans;		/* number of available B-channels */
+	uint faxchans;		/* number of available fax-channels */
+	uchar mac_addr[6];	/* MAC Address read from card */
+	uint irq;		/* interrupt number */
+	uint iobase;		/* IO-port base address */
+	ulong plxbase;		/* PLX memory base */
+	ulong membase;		/* DPRAM memory base */
+	ulong memend;		/* DPRAM memory end */
+	void *dpram;		/* mapped dpram */
+	int state;		/* actual state of card -> CARD_STATE_** */
+	struct HYSDN_CARD *next;	/* pointer to next card */
+
+	/* data areas for the /proc file system */
+	void *proclog;		/* pointer to proclog filesystem specific data */
+	void *procconf;		/* pointer to procconf filesystem specific data */
+
+	/* debugging and logging */
+	uchar err_log_state;	/* actual error log state of the card */
+	ulong debug_flags;	/* tells what should be debugged and where */
+	void (*set_errlog_state) (struct HYSDN_CARD *, int);
+
+	/* interrupt handler + interrupt synchronisation */
+	struct work_struct irq_queue;	/* interrupt task queue */
+	uchar volatile irq_enabled;	/* interrupt enabled if != 0 */
+	uchar volatile hw_lock;	/* hardware is currently locked -> no access */
+
+	/* boot process */
+	void *boot;		/* pointer to boot private data */
+	int (*writebootimg) (struct HYSDN_CARD *, uchar *, ulong);
+	int (*writebootseq) (struct HYSDN_CARD *, uchar *, int);
+	int (*waitpofready) (struct HYSDN_CARD *);
+	int (*testram) (struct HYSDN_CARD *);
+
+	/* scheduler for data transfer (only async parts) */
+	uchar async_data[256];	/* async data to be sent (normally for config) */
+	word volatile async_len;	/* length of data to sent */
+	word volatile async_channel;	/* channel number for async transfer */
+	int volatile async_busy;	/* flag != 0 sending in progress */
+	int volatile net_tx_busy;	/* a network packet tx is in progress */
+
+	/* network interface */
+	void *netif;		/* pointer to network structure */
+
+	/* init and deinit stopcard for booting, too */
+	void (*stopcard) (struct HYSDN_CARD *);
+	void (*releasehardware) (struct HYSDN_CARD *);
+#ifdef CONFIG_HYSDN_CAPI
+	struct hycapictrl_info {
+		char cardname[32];
+		spinlock_t lock;
+		int versionlen;
+		char versionbuf[1024];
+		char *version[HYSDN_MAXVERSION];
+
+		char infobuf[128];	/* for function procinfo */
+		
+		struct HYSDN_CARD  *card;
+		struct capi_ctr capi_ctrl;
+		struct sk_buff *skbs[HYSDN_MAX_CAPI_SKB];
+		int in_idx, out_idx;	/* indexes to buffer ring */
+		int sk_count;		/* number of buffers currently in ring */
+		struct sk_buff *tx_skb;	/* buffer for tx operation */
+	  
+		struct list_head ncci_head;
+	} *hyctrlinfo;
+#endif /* CONFIG_HYSDN_CAPI */
+} hysdn_card;
+
+#ifdef CONFIG_HYSDN_CAPI
+typedef struct hycapictrl_info hycapictrl_info;
+#endif /* CONFIG_HYSDN_CAPI */
+
+
+/*****************/
+/* exported vars */
+/*****************/
+extern int cardmax;		/* number of found cards */
+extern hysdn_card *card_root;	/* pointer to first card */
+
+
+
+/*************************/
+/* im/exported functions */
+/*************************/
+extern char *hysdn_getrev(const char *);
+
+/* hysdn_procconf.c */
+extern int hysdn_procconf_init(void);	/* init proc config filesys */
+extern void hysdn_procconf_release(void);	/* deinit proc config filesys */
+
+/* hysdn_proclog.c */
+extern int hysdn_proclog_init(hysdn_card *);	/* init proc log entry */
+extern void hysdn_proclog_release(hysdn_card *);	/* deinit proc log entry */
+extern void put_log_buffer(hysdn_card *, char *);	/* output log data */
+extern void hysdn_addlog(hysdn_card *, char *,...);	/* output data to log */
+extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int);	/* output card log */
+
+/* boardergo.c */
+extern int ergo_inithardware(hysdn_card * card);	/* get hardware -> module init */
+
+/* hysdn_boot.c */
+extern int pof_write_close(hysdn_card *);	/* close proc file after writing pof */
+extern int pof_write_open(hysdn_card *, uchar **);	/* open proc file for writing pof */
+extern int pof_write_buffer(hysdn_card *, int);		/* write boot data to card */
+extern int EvalSysrTokData(hysdn_card *, uchar *, int);		/* Check Sysready Token Data */
+
+/* hysdn_sched.c */
+extern int hysdn_sched_tx(hysdn_card *, uchar *, word volatile *, word volatile *,
+			  word);
+extern int hysdn_sched_rx(hysdn_card *, uchar *, word, word);
+extern int hysdn_tx_cfgline(hysdn_card *, uchar *, word);	/* send one cfg line */
+
+/* hysdn_net.c */
+extern unsigned int hynet_enable; 
+extern char *hysdn_net_revision;
+extern int hysdn_net_create(hysdn_card *);	/* create a new net device */
+extern int hysdn_net_release(hysdn_card *);	/* delete the device */
+extern char *hysdn_net_getname(hysdn_card *);	/* get name of net interface */
+extern void hysdn_tx_netack(hysdn_card *);	/* acknowledge a packet tx */
+extern struct sk_buff *hysdn_tx_netget(hysdn_card *);	/* get next network packet */
+extern void hysdn_rx_netpkt(hysdn_card *, uchar *, word);	/* rxed packet from network */
+
+#ifdef CONFIG_HYSDN_CAPI
+extern unsigned int hycapi_enable; 
+extern int hycapi_capi_create(hysdn_card *);	/* create a new capi device */
+extern int hycapi_capi_release(hysdn_card *);	/* delete the device */
+extern int hycapi_capi_stop(hysdn_card *card);   /* suspend */
+extern int hycapi_load_firmware(struct capi_ctr *, capiloaddata *);
+extern void hycapi_reset_ctr(struct capi_ctr *);
+extern void hycapi_remove_ctr(struct capi_ctr *);
+extern void hycapi_register_appl(struct capi_ctr *, __u16 appl,
+				 capi_register_params *);
+extern void hycapi_release_appl(struct capi_ctr *, __u16 appl);
+extern u16  hycapi_send_message(struct capi_ctr *, struct sk_buff *skb);
+extern char *hycapi_procinfo(struct capi_ctr *);
+extern int hycapi_read_proc(char *page, char **start, off_t off,
+			    int count, int *eof, struct capi_ctr *card);
+extern void hycapi_rx_capipkt(hysdn_card * card, uchar * buf, word len);
+extern void hycapi_tx_capiack(hysdn_card * card);
+extern struct sk_buff *hycapi_tx_capiget(hysdn_card *card);
+extern int hycapi_init(void);
+extern void hycapi_cleanup(void);
+#endif /* CONFIG_HYSDN_CAPI */
+
+#endif /* HYSDN_DEFS_H */
diff --git a/drivers/isdn/hysdn/hysdn_init.c b/drivers/isdn/hysdn/hysdn_init.c
new file mode 100644
index 0000000..5cac2bf
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_init.c
@@ -0,0 +1,254 @@
+/* $Id: hysdn_init.c,v 1.6.6.6 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, init functions.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static struct pci_device_id hysdn_pci_tbl[] = {
+	{PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO},
+	{PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2},
+	{PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO},
+	{PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX, PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO},
+	{ }				/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, hysdn_pci_tbl);
+MODULE_DESCRIPTION("ISDN4Linux: Driver for HYSDN cards");
+MODULE_AUTHOR("Werner Cornelius");
+MODULE_LICENSE("GPL");
+
+static char *hysdn_init_revision = "$Revision: 1.6.6.6 $";
+int cardmax;			/* number of found cards */
+hysdn_card *card_root = NULL;	/* pointer to first card */
+
+/**********************************************/
+/* table assigning PCI-sub ids to board types */
+/* the last entry contains all 0              */
+/**********************************************/
+static struct {
+	word subid;		/* PCI sub id */
+	uchar cardtyp;		/* card type assigned */
+} pci_subid_map[] = {
+
+	{
+		PCI_SUBDEVICE_ID_HYPERCOPE_METRO, BD_METRO
+	},
+	{
+		PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, BD_CHAMP2
+	},
+	{
+		PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, BD_ERGO
+	},
+	{
+		PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, BD_ERGO
+	},
+	{
+		0, 0
+	}			/* terminating entry */
+};
+
+
+/*********************************************************************/
+/* search_cards searches for available cards in the pci config data. */
+/* If a card is found, the card structure is allocated and the cards */
+/* ressources are reserved. cardmax is incremented.                  */
+/*********************************************************************/
+static void
+search_cards(void)
+{
+	struct pci_dev *akt_pcidev = NULL;
+	hysdn_card *card, *card_last;
+	int i;
+
+	card_root = NULL;
+	card_last = NULL;
+	while ((akt_pcidev = pci_find_device(PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+					     akt_pcidev)) != NULL) {
+		if (pci_enable_device(akt_pcidev))
+			continue;
+
+		if (!(card = kmalloc(sizeof(hysdn_card), GFP_KERNEL))) {
+			printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
+			return;
+		}
+		memset(card, 0, sizeof(hysdn_card));
+		card->myid = cardmax;	/* set own id */
+		card->bus = akt_pcidev->bus->number;
+		card->devfn = akt_pcidev->devfn;	/* slot + function */
+		card->subsysid = akt_pcidev->subsystem_device;
+		card->irq = akt_pcidev->irq;
+		card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE);
+		card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE);
+		card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE);
+		card->brdtype = BD_NONE;	/* unknown */
+		card->debug_flags = DEF_DEB_FLAGS;	/* set default debug */
+		card->faxchans = 0;	/* default no fax channels */
+		card->bchans = 2;	/* and 2 b-channels */
+		for (i = 0; pci_subid_map[i].subid; i++)
+			if (pci_subid_map[i].subid == card->subsysid) {
+				card->brdtype = pci_subid_map[i].cardtyp;
+				break;
+			}
+		if (card->brdtype != BD_NONE) {
+			if (ergo_inithardware(card)) {
+				printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
+				kfree(card);
+				continue;
+			}
+		} else {
+			printk(KERN_WARNING "HYSDN: unknown card id 0x%04x\n", card->subsysid);
+			kfree(card);	/* release mem */
+			continue;
+		}
+		cardmax++;
+		card->next = NULL;	/*end of chain */
+		if (card_last)
+			card_last->next = card;		/* pointer to next card */
+		else
+			card_root = card;
+		card_last = card;	/* new chain end */
+	}			/* device found */
+}				/* search_cards */
+
+/************************************************************************************/
+/* free_resources frees the acquired PCI resources and returns the allocated memory */
+/************************************************************************************/
+static void
+free_resources(void)
+{
+	hysdn_card *card;
+
+	while (card_root) {
+		card = card_root;
+		if (card->releasehardware)
+			card->releasehardware(card);	/* free all hardware resources */
+		card_root = card_root->next;	/* remove card from chain */
+		kfree(card);	/* return mem */
+
+	}			/* while card_root */
+}				/* free_resources */
+
+/**************************************************************************/
+/* stop_cards disables (hardware resets) all cards and disables interrupt */
+/**************************************************************************/
+static void
+stop_cards(void)
+{
+	hysdn_card *card;
+
+	card = card_root;	/* first in chain */
+	while (card) {
+		if (card->stopcard)
+			card->stopcard(card);
+		card = card->next;	/* remove card from chain */
+	}			/* while card */
+}				/* stop_cards */
+
+
+/****************************************************************************/
+/* The module startup and shutdown code. Only compiled when used as module. */
+/* Using the driver as module is always advisable, because the booting      */
+/* image becomes smaller and the driver code is only loaded when needed.    */
+/* Additionally newer versions may be activated without rebooting.          */
+/****************************************************************************/
+
+/******************************************************/
+/* extract revision number from string for log output */
+/******************************************************/
+char *
+hysdn_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+
+/****************************************************************************/
+/* init_module is called once when the module is loaded to do all necessary */
+/* things like autodetect...                                                */
+/* If the return value of this function is 0 the init has been successful   */
+/* and the module is added to the list in /proc/modules, otherwise an error */
+/* is assumed and the module will not be kept in memory.                    */
+/****************************************************************************/
+static int __init
+hysdn_init(void)
+{
+	char tmp[50];
+
+	strcpy(tmp, hysdn_init_revision);
+	printk(KERN_NOTICE "HYSDN: module Rev: %s loaded\n", hysdn_getrev(tmp));
+	strcpy(tmp, hysdn_net_revision);
+	printk(KERN_NOTICE "HYSDN: network interface Rev: %s \n", hysdn_getrev(tmp));
+	search_cards();
+	printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
+
+	if (hysdn_procconf_init()) {
+		free_resources();	/* proc file_sys not created */
+		return (-1);
+	}
+#ifdef CONFIG_HYSDN_CAPI
+	if(cardmax > 0) {
+		if(hycapi_init()) {
+			printk(KERN_ERR "HYCAPI: init failed\n");
+			return(-1);
+		}
+	}
+#endif /* CONFIG_HYSDN_CAPI */
+	return (0);		/* no error */
+}				/* init_module */
+
+
+/***********************************************************************/
+/* cleanup_module is called when the module is released by the kernel. */
+/* The routine is only called if init_module has been successful and   */
+/* the module counter has a value of 0. Otherwise this function will   */
+/* not be called. This function must release all resources still allo- */
+/* cated as after the return from this function the module code will   */
+/* be removed from memory.                                             */
+/***********************************************************************/
+static void __exit
+hysdn_exit(void)
+{
+#ifdef CONFIG_HYSDN_CAPI
+	hysdn_card *card;
+#endif /* CONFIG_HYSDN_CAPI */
+	stop_cards();
+#ifdef CONFIG_HYSDN_CAPI
+	card = card_root;	/* first in chain */
+	while (card) {
+		hycapi_capi_release(card);
+		card = card->next;	/* remove card from chain */
+	}			/* while card */
+	hycapi_cleanup();
+#endif /* CONFIG_HYSDN_CAPI */
+	hysdn_procconf_release();
+	free_resources();
+	printk(KERN_NOTICE "HYSDN: module unloaded\n");
+}				/* cleanup_module */
+
+module_init(hysdn_init);
+module_exit(hysdn_exit);
diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
new file mode 100644
index 0000000..babec81
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_net.c
@@ -0,0 +1,348 @@
+/* $Id: hysdn_net.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, net (ethernet type) handling routines.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This net module has been inspired by the skeleton driver from
+ * Donald Becker (becker@CESDIS.gsfc.nasa.gov)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inetdevice.h>
+
+#include "hysdn_defs.h"
+
+unsigned int hynet_enable = 0xffffffff; 
+MODULE_PARM(hynet_enable, "i");
+
+/* store the actual version for log reporting */
+char *hysdn_net_revision = "$Revision: 1.8.6.4 $";
+
+#define MAX_SKB_BUFFERS 20	/* number of buffers for keeping TX-data */
+
+/****************************************************************************/
+/* structure containing the complete network data. The structure is aligned */
+/* in a way that both, the device and statistics are kept inside it.        */
+/* for proper access, the device structure MUST be the first var/struct     */
+/* inside the definition.                                                   */
+/****************************************************************************/
+struct net_local {
+	struct net_device netdev;	/* the network device */
+	struct net_device_stats stats;
+	/* additional vars may be added here */
+	char dev_name[9];	/* our own device name */
+
+	/* Tx control lock.  This protects the transmit buffer ring
+	 * state along with the "tx full" state of the driver.  This
+	 * means all netif_queue flow control actions are protected
+	 * by this lock as well.
+	 */
+	spinlock_t lock;
+	struct sk_buff *skbs[MAX_SKB_BUFFERS];	/* pointers to tx-skbs */
+	int in_idx, out_idx;	/* indexes to buffer ring */
+	int sk_count;		/* number of buffers currently in ring */
+};				/* net_local */
+
+
+/*****************************************************/
+/* Get the current statistics for this card.         */
+/* This may be called with the card open or closed ! */
+/*****************************************************/
+static struct net_device_stats *
+net_get_stats(struct net_device *dev)
+{
+	return (&((struct net_local *) dev)->stats);
+}				/* net_device_stats */
+
+/*********************************************************************/
+/* Open/initialize the board. This is called (in the current kernel) */
+/* sometime after booting when the 'ifconfig' program is run.        */
+/* This routine should set everything up anew at each open, even     */
+/* registers that "should" only need to be set once at boot, so that */
+/* there is non-reboot way to recover if something goes wrong.       */
+/*********************************************************************/
+static int
+net_open(struct net_device *dev)
+{
+	struct in_device *in_dev;
+	hysdn_card *card = dev->priv;
+	int i;
+
+	netif_start_queue(dev);	/* start tx-queueing */
+
+	/* Fill in the MAC-level header (if not already set) */
+	if (!card->mac_addr[0]) {
+		for (i = 0; i < ETH_ALEN - sizeof(ulong); i++)
+			dev->dev_addr[i] = 0xfc;
+		if ((in_dev = dev->ip_ptr) != NULL) {
+			struct in_ifaddr *ifa = in_dev->ifa_list;
+			if (ifa != NULL)
+				memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ulong)), &ifa->ifa_local, sizeof(ulong));
+		}
+	} else
+		memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
+
+	return (0);
+}				/* net_open */
+
+/*******************************************/
+/* flush the currently occupied tx-buffers */
+/* must only be called when device closed  */
+/*******************************************/
+static void
+flush_tx_buffers(struct net_local *nl)
+{
+
+	while (nl->sk_count) {
+		dev_kfree_skb(nl->skbs[nl->out_idx++]);		/* free skb */
+		if (nl->out_idx >= MAX_SKB_BUFFERS)
+			nl->out_idx = 0;	/* wrap around */
+		nl->sk_count--;
+	}
+}				/* flush_tx_buffers */
+
+
+/*********************************************************************/
+/* close/decativate the device. The device is not removed, but only  */
+/* deactivated.                                                      */
+/*********************************************************************/
+static int
+net_close(struct net_device *dev)
+{
+
+	netif_stop_queue(dev);	/* disable queueing */
+
+	flush_tx_buffers((struct net_local *) dev);
+
+	return (0);		/* success */
+}				/* net_close */
+
+/************************************/
+/* send a packet on this interface. */
+/* new style for kernel >= 2.3.33   */
+/************************************/
+static int
+net_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *) dev;
+
+	spin_lock_irq(&lp->lock);
+
+	lp->skbs[lp->in_idx++] = skb;	/* add to buffer list */
+	if (lp->in_idx >= MAX_SKB_BUFFERS)
+		lp->in_idx = 0;	/* wrap around */
+	lp->sk_count++;		/* adjust counter */
+	dev->trans_start = jiffies;
+
+	/* If we just used up the very last entry in the
+	 * TX ring on this device, tell the queueing
+	 * layer to send no more.
+	 */
+	if (lp->sk_count >= MAX_SKB_BUFFERS)
+		netif_stop_queue(dev);
+
+	/* When the TX completion hw interrupt arrives, this
+	 * is when the transmit statistics are updated.
+	 */
+
+	spin_unlock_irq(&lp->lock);
+
+	if (lp->sk_count <= 3) {
+		schedule_work(&((hysdn_card *) dev->priv)->irq_queue);
+	}
+	return (0);		/* success */
+}				/* net_send_packet */
+
+
+
+/***********************************************************************/
+/* acknowlegde a packet send. The network layer will be informed about */
+/* completion                                                          */
+/***********************************************************************/
+void
+hysdn_tx_netack(hysdn_card * card)
+{
+	struct net_local *lp = card->netif;
+
+	if (!lp)
+		return;		/* non existing device */
+
+
+	if (!lp->sk_count)
+		return;		/* error condition */
+
+	lp->stats.tx_packets++;
+	lp->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
+
+	dev_kfree_skb(lp->skbs[lp->out_idx++]);		/* free skb */
+	if (lp->out_idx >= MAX_SKB_BUFFERS)
+		lp->out_idx = 0;	/* wrap around */
+
+	if (lp->sk_count-- == MAX_SKB_BUFFERS)	/* dec usage count */
+		netif_start_queue((struct net_device *) lp);
+}				/* hysdn_tx_netack */
+
+/*****************************************************/
+/* we got a packet from the network, go and queue it */
+/*****************************************************/
+void
+hysdn_rx_netpkt(hysdn_card * card, uchar * buf, word len)
+{
+	struct net_local *lp = card->netif;
+	struct sk_buff *skb;
+
+	if (!lp)
+		return;		/* non existing device */
+
+	lp->stats.rx_bytes += len;
+
+	skb = dev_alloc_skb(len);
+	if (skb == NULL) {
+		printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+		       lp->netdev.name);
+		lp->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = &lp->netdev;
+
+	/* copy the data */
+	memcpy(skb_put(skb, len), buf, len);
+
+	/* determine the used protocol */
+	skb->protocol = eth_type_trans(skb, &lp->netdev);
+
+	netif_rx(skb);
+	lp->stats.rx_packets++;	/* adjust packet count */
+
+}				/* hysdn_rx_netpkt */
+
+/*****************************************************/
+/* return the pointer to a network packet to be send */
+/*****************************************************/
+struct sk_buff *
+hysdn_tx_netget(hysdn_card * card)
+{
+	struct net_local *lp = card->netif;
+
+	if (!lp)
+		return (NULL);	/* non existing device */
+
+	if (!lp->sk_count)
+		return (NULL);	/* nothing available */
+
+	return (lp->skbs[lp->out_idx]);		/* next packet to send */
+}				/* hysdn_tx_netget */
+
+
+/*******************************************/
+/* init function called by register device */
+/*******************************************/
+static int
+net_init(struct net_device *dev)
+{
+	/* setup the function table */
+	dev->open = net_open;
+	dev->stop = net_close;
+	dev->hard_start_xmit = net_send_packet;
+	dev->get_stats = net_get_stats;
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	return (0);		/* success */
+}				/* net_init */
+
+/*****************************************************************************/
+/* hysdn_net_create creates a new net device for the given card. If a device */
+/* already exists, it will be deleted and created a new one. The return value */
+/* 0 announces success, else a negative error code will be returned.         */
+/*****************************************************************************/
+int
+hysdn_net_create(hysdn_card * card)
+{
+	struct net_device *dev;
+	int i;
+	if(!card) {
+		printk(KERN_WARNING "No card-pt in hysdn_net_create!\n");
+		return (-ENOMEM);
+	}
+	hysdn_net_release(card);	/* release an existing net device */
+	if ((dev = kmalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) {
+		printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
+		return (-ENOMEM);
+	}
+	memset(dev, 0, sizeof(struct net_local));	/* clean the structure */
+
+	spin_lock_init(&((struct net_local *) dev)->lock);
+
+	/* initialise necessary or informing fields */
+	dev->base_addr = card->iobase;	/* IO address */
+	dev->irq = card->irq;	/* irq */
+	dev->init = net_init;	/* the init function of the device */
+	if(dev->name) {
+		strcpy(dev->name, ((struct net_local *) dev)->dev_name);
+	} 
+	if ((i = register_netdev(dev))) {
+		printk(KERN_WARNING "HYSDN: unable to create network device\n");
+		kfree(dev);
+		return (i);
+	}
+	dev->priv = card;	/* remember pointer to own data structure */
+	card->netif = dev;	/* setup the local pointer */
+
+	if (card->debug_flags & LOG_NET_INIT)
+		hysdn_addlog(card, "network device created");
+	return (0);		/* and return success */
+}				/* hysdn_net_create */
+
+/***************************************************************************/
+/* hysdn_net_release deletes the net device for the given card. The return */
+/* value 0 announces success, else a negative error code will be returned. */
+/***************************************************************************/
+int
+hysdn_net_release(hysdn_card * card)
+{
+	struct net_device *dev = card->netif;
+
+	if (!dev)
+		return (0);	/* non existing */
+
+	card->netif = NULL;	/* clear out pointer */
+	dev->stop(dev);		/* close the device */
+
+	flush_tx_buffers((struct net_local *) dev);	/* empty buffers */
+
+	unregister_netdev(dev);	/* release the device */
+	free_netdev(dev);	/* release the memory allocated */
+	if (card->debug_flags & LOG_NET_INIT)
+		hysdn_addlog(card, "network device deleted");
+
+	return (0);		/* always successful */
+}				/* hysdn_net_release */
+
+/*****************************************************************************/
+/* hysdn_net_getname returns a pointer to the name of the network interface. */
+/* if the interface is not existing, a "-" is returned.                      */
+/*****************************************************************************/
+char *
+hysdn_net_getname(hysdn_card * card)
+{
+	struct net_device *dev = card->netif;
+
+	if (!dev)
+		return ("-");	/* non existing */
+
+	return (dev->name);
+}				/* hysdn_net_getname */
diff --git a/drivers/isdn/hysdn/hysdn_pof.h b/drivers/isdn/hysdn/hysdn_pof.h
new file mode 100644
index 0000000..6cd81b9
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_pof.h
@@ -0,0 +1,78 @@
+/* $Id: hysdn_pof.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, definitions used for handling pof-files.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/************************/
+/* POF specific defines */
+/************************/
+#define BOOT_BUF_SIZE   0x1000	/* =4096, maybe moved to other h file */
+#define CRYPT_FEEDTERM  0x8142
+#define CRYPT_STARTTERM 0x81a5
+				    /*  max. timeout time in seconds
+				     *  from end of booting to POF is ready
+				     */
+#define POF_READY_TIME_OUT_SEC  10
+
+/**********************************/
+/* defines for 1.stage boot image */
+/**********************************/
+
+/*  the POF file record containing the boot loader image
+ *  has 2 pages a 16KB:
+ *  1. page contains the high 16-bit part of the 32-bit E1 words
+ *  2. page contains the low  16-bit part of the 32-bit E1 words
+ *
+ *  In each 16KB page we assume the start of the boot loader code
+ *  in the highest 2KB part (at offset 0x3800);
+ *  the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
+ */
+
+#define POF_BOOT_LOADER_PAGE_SIZE   0x4000	/* =16384U */
+#define POF_BOOT_LOADER_TOTAL_SIZE  (2U*POF_BOOT_LOADER_PAGE_SIZE)
+
+#define POF_BOOT_LOADER_CODE_SIZE   0x0800	/* =2KB =2048U */
+
+		    /* offset in boot page, where loader code may start */
+					    /* =0x3800= 14336U */
+#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
+
+
+/*--------------------------------------POF file record structs------------*/
+typedef struct PofFileHdr_tag {	/* Pof file header */
+/*00 */ ulong Magic __attribute__((packed));
+/*04 */ ulong N_PofRecs __attribute__((packed));
+/*08 */
+} tPofFileHdr;
+
+typedef struct PofRecHdr_tag {	/* Pof record header */
+/*00 */ word PofRecId __attribute__((packed));
+/*02 */ ulong PofRecDataLen __attribute__((packed));
+/*06 */
+} tPofRecHdr;
+
+typedef struct PofTimeStamp_tag {
+/*00 */ ulong UnixTime __attribute__((packed));
+	/*04 */ uchar DateTimeText[0x28] __attribute__((packed));
+	/* =40 */
+/*2C */
+} tPofTimeStamp;
+
+				    /* tPofFileHdr.Magic value: */
+#define TAGFILEMAGIC 0x464F501AUL
+				    /* tPofRecHdr.PofRecId values: */
+#define TAG_ABSDATA  0x1000	/* abs. data */
+#define TAG_BOOTDTA  0x1001	/* boot data */
+#define TAG_COMMENT  0x0020
+#define TAG_SYSCALL  0x0021
+#define TAG_FLOWCTRL 0x0022
+#define TAG_TIMESTMP 0x0010	/* date/time stamp of version */
+#define TAG_CABSDATA 0x1100	/* crypted abs. data */
+#define TAG_CBOOTDTA 0x1101	/* crypted boot data */
diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c
new file mode 100644
index 0000000..5da507e
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_procconf.c
@@ -0,0 +1,443 @@
+/* $Id: hysdn_procconf.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999  by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_procconf_revision = "$Revision: 1.8.6.4 $";
+
+#define INFO_OUT_LEN 80		/* length of info line including lf */
+
+/********************************************************/
+/* defines and data structure for conf write operations */
+/********************************************************/
+#define CONF_STATE_DETECT 0	/* waiting for detect */
+#define CONF_STATE_CONF   1	/* writing config data */
+#define CONF_STATE_POF    2	/* writing pof data */
+#define CONF_LINE_LEN   255	/* 255 chars max */
+
+struct conf_writedata {
+	hysdn_card *card;	/* card the device is connected to */
+	int buf_size;		/* actual number of bytes in the buffer */
+	int needed_size;	/* needed size when reading pof */
+	int state;		/* actual interface states from above constants */
+	uchar conf_line[CONF_LINE_LEN];		/* buffered conf line */
+	word channel;		/* active channel number */
+	uchar *pof_buffer;	/* buffer when writing pof */
+};
+
+/***********************************************************************/
+/* process_line parses one config line and transfers it to the card if */
+/* necessary.                                                          */
+/* if the return value is negative an error occurred.                   */
+/***********************************************************************/
+static int
+process_line(struct conf_writedata *cnf)
+{
+	uchar *cp = cnf->conf_line;
+	int i;
+
+	if (cnf->card->debug_flags & LOG_CNF_LINE)
+		hysdn_addlog(cnf->card, "conf line: %s", cp);
+
+	if (*cp == '-') {	/* option */
+		cp++;		/* point to option char */
+
+		if (*cp++ != 'c')
+			return (0);	/* option unknown or used */
+		i = 0;		/* start value for channel */
+		while ((*cp <= '9') && (*cp >= '0'))
+			i = i * 10 + *cp++ - '0';	/* get decimal number */
+		if (i > 65535) {
+			if (cnf->card->debug_flags & LOG_CNF_MISC)
+				hysdn_addlog(cnf->card, "conf channel invalid  %d", i);
+			return (-ERR_INV_CHAN);		/* invalid channel */
+		}
+		cnf->channel = i & 0xFFFF;	/* set new channel number */
+		return (0);	/* success */
+	}			/* option */
+	if (*cp == '*') {	/* line to send */
+		if (cnf->card->debug_flags & LOG_CNF_DATA)
+			hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
+		return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
+					 cnf->channel));	/* send the line without * */
+	}			/* line to send */
+	return (0);
+}				/* process_line */
+
+/***********************************/
+/* conf file operations and tables */
+/***********************************/
+
+/****************************************************/
+/* write conf file -> boot or send cfg line to card */
+/****************************************************/
+static ssize_t
+hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+	struct conf_writedata *cnf;
+	int i;
+	uchar ch, *cp;
+
+	if (!count)
+		return (0);	/* nothing to handle */
+
+	if (!(cnf = file->private_data))
+		return (-EFAULT);	/* should never happen */
+
+	if (cnf->state == CONF_STATE_DETECT) {	/* auto detect cnf or pof data */
+		if (copy_from_user(&ch, buf, 1))	/* get first char for detect */
+			return (-EFAULT);
+
+		if (ch == 0x1A) {
+			/* we detected a pof file */
+			if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
+				return (cnf->needed_size);	/* an error occurred -> exit */
+			cnf->buf_size = 0;	/* buffer is empty */
+			cnf->state = CONF_STATE_POF;	/* new state */
+		} else {
+			/* conf data has been detected */
+			cnf->buf_size = 0;	/* buffer is empty */
+			cnf->state = CONF_STATE_CONF;	/* requested conf data write */
+			if (cnf->card->state != CARD_STATE_RUN)
+				return (-ERR_NOT_BOOTED);
+			cnf->conf_line[CONF_LINE_LEN - 1] = 0;	/* limit string length */
+			cnf->channel = 4098;	/* default channel for output */
+		}
+	}			/* state was auto detect */
+	if (cnf->state == CONF_STATE_POF) {	/* pof write active */
+		i = cnf->needed_size - cnf->buf_size;	/* bytes still missing for write */
+		if (i <= 0)
+			return (-EINVAL);	/* size error handling pof */
+
+		if (i < count)
+			count = i;	/* limit requested number of bytes */
+		if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
+			return (-EFAULT);	/* error while copying */
+		cnf->buf_size += count;
+
+		if (cnf->needed_size == cnf->buf_size) {
+			cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size);	/* write data */
+			if (cnf->needed_size <= 0) {
+				cnf->card->state = CARD_STATE_BOOTERR;	/* show boot error */
+				return (cnf->needed_size);	/* an error occurred */
+			}
+			cnf->buf_size = 0;	/* buffer is empty again */
+		}
+	}
+	/* pof write active */
+	else {			/* conf write active */
+
+		if (cnf->card->state != CARD_STATE_RUN) {
+			if (cnf->card->debug_flags & LOG_CNF_MISC)
+				hysdn_addlog(cnf->card, "cnf write denied -> not booted");
+			return (-ERR_NOT_BOOTED);
+		}
+		i = (CONF_LINE_LEN - 1) - cnf->buf_size;	/* bytes available in buffer */
+		if (i > 0) {
+			/* copy remaining bytes into buffer */
+
+			if (count > i)
+				count = i;	/* limit transfer */
+			if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
+				return (-EFAULT);	/* error while copying */
+
+			i = count;	/* number of chars in buffer */
+			cp = cnf->conf_line + cnf->buf_size;
+			while (i) {
+				/* search for end of line */
+				if ((*cp < ' ') && (*cp != 9))
+					break;	/* end of line found */
+				cp++;
+				i--;
+			}	/* search for end of line */
+
+			if (i) {
+				/* delimiter found */
+				*cp++ = 0;	/* string termination */
+				count -= (i - 1);	/* subtract remaining bytes from count */
+				while ((i) && (*cp < ' ') && (*cp != 9)) {
+					i--;	/* discard next char */
+					count++;	/* mark as read */
+					cp++;	/* next char */
+				}
+				cnf->buf_size = 0;	/* buffer is empty after transfer */
+				if ((i = process_line(cnf)) < 0)	/* handle the line */
+					count = i;	/* return the error */
+			}
+			/* delimiter found */
+			else {
+				cnf->buf_size += count;		/* add chars to string */
+				if (cnf->buf_size >= CONF_LINE_LEN - 1) {
+					if (cnf->card->debug_flags & LOG_CNF_MISC)
+						hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
+					return (-ERR_CONF_LONG);
+				}
+			}	/* not delimited */
+
+		}
+		/* copy remaining bytes into buffer */
+		else {
+			if (cnf->card->debug_flags & LOG_CNF_MISC)
+				hysdn_addlog(cnf->card, "cnf line too long");
+			return (-ERR_CONF_LONG);
+		}
+	}			/* conf write active */
+
+	return (count);
+}				/* hysdn_conf_write */
+
+/*******************************************/
+/* read conf file -> output card info data */
+/*******************************************/
+static ssize_t
+hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+	char *cp;
+	int i;
+
+	if (file->f_mode & FMODE_READ) {
+		if (!(cp = file->private_data))
+			return (-EFAULT);	/* should never happen */
+		i = strlen(cp);	/* get total string length */
+		if (*off < i) {
+			/* still bytes to transfer */
+			cp += *off;	/* point to desired data offset */
+			i -= *off;	/* remaining length */
+			if (i > count)
+				i = count;	/* limit length to transfer */
+			if (copy_to_user(buf, cp, i))
+				return (-EFAULT);	/* copy error */
+			*off += i;	/* adjust offset */
+		} else
+			return (0);
+	} else
+		return (-EPERM);	/* no permission to read */
+
+	return (i);
+}				/* hysdn_conf_read */
+
+/******************/
+/* open conf file */
+/******************/
+static int
+hysdn_conf_open(struct inode *ino, struct file *filep)
+{
+	hysdn_card *card;
+	struct proc_dir_entry *pd;
+	struct conf_writedata *cnf;
+	char *cp, *tmp;
+
+	/* now search the addressed card */
+	lock_kernel();
+	card = card_root;
+	while (card) {
+		pd = card->procconf;
+		if (pd == PDE(ino))
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card) {
+		unlock_kernel();
+		return (-ENODEV);	/* device is unknown/invalid */
+	}
+	if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+		hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
+			     filep->f_uid, filep->f_gid, filep->f_mode);
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write boot file or conf line */
+
+		if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
+			unlock_kernel();
+			return (-EFAULT);
+		}
+		cnf->card = card;
+		cnf->buf_size = 0;	/* nothing buffered */
+		cnf->state = CONF_STATE_DETECT;		/* start auto detect */
+		filep->private_data = cnf;
+
+	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+		/* read access -> output card info data */
+
+		if (!(tmp = (char *) kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
+			unlock_kernel();
+			return (-EFAULT);	/* out of memory */
+		}
+		filep->private_data = tmp;	/* start of string */
+
+		/* first output a headline */
+		sprintf(tmp, "id bus slot type irq iobase dp-mem     b-chans fax-chans state device");
+		cp = tmp;	/* start of string */
+		while (*cp)
+			cp++;
+		while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+			*cp++ = ' ';
+		*cp++ = '\n';
+
+		/* and now the data */
+		sprintf(cp, "%d  %3d %4d %4d %3d 0x%04x 0x%08lx %7d %9d %3d   %s",
+			card->myid,
+			card->bus,
+			PCI_SLOT(card->devfn),
+			card->brdtype,
+			card->irq,
+			card->iobase,
+			card->membase,
+			card->bchans,
+			card->faxchans,
+			card->state,
+			hysdn_net_getname(card));
+		while (*cp)
+			cp++;
+		while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+			*cp++ = ' ';
+		*cp++ = '\n';
+		*cp = 0;	/* end of string */
+	} else {		/* simultaneous read/write access forbidden ! */
+		unlock_kernel();
+		return (-EPERM);	/* no permission this time */
+	}
+	unlock_kernel();
+	return nonseekable_open(ino, filep);
+}				/* hysdn_conf_open */
+
+/***************************/
+/* close a config file.    */
+/***************************/
+static int
+hysdn_conf_close(struct inode *ino, struct file *filep)
+{
+	hysdn_card *card;
+	struct conf_writedata *cnf;
+	int retval = 0;
+	struct proc_dir_entry *pd;
+
+	lock_kernel();
+	/* search the addressed card */
+	card = card_root;
+	while (card) {
+		pd = card->procconf;
+		if (pd == PDE(ino))
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card) {
+		unlock_kernel();
+		return (-ENODEV);	/* device is unknown/invalid */
+	}
+	if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+		hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
+			     filep->f_uid, filep->f_gid, filep->f_mode);
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write boot file or conf line */
+		if (filep->private_data) {
+			cnf = filep->private_data;
+
+			if (cnf->state == CONF_STATE_POF)
+				retval = pof_write_close(cnf->card);	/* close the pof write */
+			kfree(filep->private_data);	/* free allocated memory for buffer */
+
+		}		/* handle write private data */
+	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+		/* read access -> output card info data */
+
+		if (filep->private_data)
+			kfree(filep->private_data);	/* release memory */
+	}
+	unlock_kernel();
+	return (retval);
+}				/* hysdn_conf_close */
+
+/******************************************************/
+/* table for conf filesystem functions defined above. */
+/******************************************************/
+static struct file_operations conf_fops =
+{
+	.llseek         = no_llseek,
+	.read           = hysdn_conf_read,
+	.write          = hysdn_conf_write,
+	.open           = hysdn_conf_open,
+	.release        = hysdn_conf_close,                                       
+};
+
+/*****************************/
+/* hysdn subdir in /proc/net */
+/*****************************/
+struct proc_dir_entry *hysdn_proc_entry = NULL;
+
+/*******************************************************************************/
+/* hysdn_procconf_init is called when the module is loaded and after the cards */
+/* have been detected. The needed proc dir and card config files are created.  */
+/* The log init is called at last.                                             */
+/*******************************************************************************/
+int
+hysdn_procconf_init(void)
+{
+	hysdn_card *card;
+	uchar conf_name[20];
+
+	hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
+	if (!hysdn_proc_entry) {
+		printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
+		return (-1);
+	}
+	card = card_root;	/* point to first card */
+	while (card) {
+
+		sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+		if ((card->procconf = (void *) create_proc_entry(conf_name,
+					     S_IFREG | S_IRUGO | S_IWUSR,
+					    hysdn_proc_entry)) != NULL) {
+			((struct proc_dir_entry *) card->procconf)->proc_fops = &conf_fops;
+			((struct proc_dir_entry *) card->procconf)->owner = THIS_MODULE;
+			hysdn_proclog_init(card);	/* init the log file entry */
+		}
+		card = card->next;	/* next entry */
+	}
+
+	printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procconf_revision));
+	return (0);
+}				/* hysdn_procconf_init */
+
+/*************************************************************************************/
+/* hysdn_procconf_release is called when the module is unloaded and before the cards */
+/* resources are released. The module counter is assumed to be 0 !                   */
+/*************************************************************************************/
+void
+hysdn_procconf_release(void)
+{
+	hysdn_card *card;
+	uchar conf_name[20];
+
+	card = card_root;	/* start with first card */
+	while (card) {
+
+		sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+		if (card->procconf)
+			remove_proc_entry(conf_name, hysdn_proc_entry);
+
+		hysdn_proclog_release(card);	/* init the log file entry */
+
+		card = card->next;	/* point to next card */
+	}
+
+	remove_proc_entry(PROC_SUBDIR_NAME, proc_net);
+}
diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c
new file mode 100644
index 0000000..8ef2b7c
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_proclog.c
@@ -0,0 +1,441 @@
+/* $Id: hysdn_proclog.c,v 1.9.6.3 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, /proc/net filesystem log functions.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+
+#include "hysdn_defs.h"
+
+/* the proc subdir for the interface is defined in the procconf module */
+extern struct proc_dir_entry *hysdn_proc_entry;
+
+/*************************************************/
+/* structure keeping ascii log for device output */
+/*************************************************/
+struct log_data {
+	struct log_data *next;
+	ulong usage_cnt;	/* number of files still to work */
+	void *proc_ctrl;	/* pointer to own control procdata structure */
+	char log_start[2];	/* log string start (final len aligned by size) */
+};
+
+/**********************************************/
+/* structure holding proc entrys for one card */
+/**********************************************/
+struct procdata {
+	struct proc_dir_entry *log;	/* log entry */
+	char log_name[15];	/* log filename */
+	struct log_data *log_head, *log_tail;	/* head and tail for queue */
+	int if_used;		/* open count for interface */
+	int volatile del_lock;	/* lock for delete operations */
+	uchar logtmp[LOG_MAX_LINELEN];
+	wait_queue_head_t rd_queue;
+};
+
+
+/**********************************************/
+/* log function for cards error log interface */
+/**********************************************/
+void
+hysdn_card_errlog(hysdn_card * card, tErrLogEntry * logp, int maxsize)
+{
+	char buf[ERRLOG_TEXT_SIZE + 40];
+
+	sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
+	put_log_buffer(card, buf);	/* output the string */
+}				/* hysdn_card_errlog */
+
+/***************************************************/
+/* Log function using format specifiers for output */
+/***************************************************/
+void
+hysdn_addlog(hysdn_card * card, char *fmt,...)
+{
+	struct procdata *pd = card->proclog;
+	char *cp;
+	va_list args;
+
+	if (!pd)
+		return;		/* log structure non existent */
+
+	cp = pd->logtmp;
+	cp += sprintf(cp, "HYSDN: card %d ", card->myid);
+
+	va_start(args, fmt);
+	cp += vsprintf(cp, fmt, args);
+	va_end(args);
+	*cp++ = '\n';
+	*cp = 0;
+
+	if (card->debug_flags & DEB_OUT_SYSLOG)
+		printk(KERN_INFO "%s", pd->logtmp);
+	else
+		put_log_buffer(card, pd->logtmp);
+
+}				/* hysdn_addlog */
+
+/********************************************/
+/* put an log buffer into the log queue.    */
+/* This buffer will be kept until all files */
+/* opened for read got the contents.        */
+/* Flushes buffers not longer in use.       */
+/********************************************/
+void
+put_log_buffer(hysdn_card * card, char *cp)
+{
+	struct log_data *ib;
+	struct procdata *pd = card->proclog;
+	int i;
+	unsigned long flags;
+
+	if (!pd)
+		return;
+	if (!cp)
+		return;
+	if (!*cp)
+		return;
+	if (pd->if_used <= 0)
+		return;		/* no open file for read */
+
+	if (!(ib = (struct log_data *) kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+		 return;	/* no memory */
+	strcpy(ib->log_start, cp);	/* set output string */
+	ib->next = NULL;
+	ib->proc_ctrl = pd;	/* point to own control structure */
+	save_flags(flags);
+	cli();
+	ib->usage_cnt = pd->if_used;
+	if (!pd->log_head)
+		pd->log_head = ib;	/* new head */
+	else
+		pd->log_tail->next = ib;	/* follows existing messages */
+	pd->log_tail = ib;	/* new tail */
+	i = pd->del_lock++;	/* get lock state */
+	restore_flags(flags);
+
+	/* delete old entrys */
+	if (!i)
+		while (pd->log_head->next) {
+			if ((pd->log_head->usage_cnt <= 0) &&
+			    (pd->log_head->next->usage_cnt <= 0)) {
+				ib = pd->log_head;
+				pd->log_head = pd->log_head->next;
+				kfree(ib);
+			} else
+				break;
+		}		/* pd->log_head->next */
+	pd->del_lock--;		/* release lock level */
+	wake_up_interruptible(&(pd->rd_queue));		/* announce new entry */
+}				/* put_log_buffer */
+
+
+/******************************/
+/* file operations and tables */
+/******************************/
+
+/****************************************/
+/* write log file -> set log level bits */
+/****************************************/
+static ssize_t
+hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+	ulong u = 0;
+	int found = 0;
+	uchar *cp, valbuf[128];
+	long base = 10;
+	hysdn_card *card = (hysdn_card *) file->private_data;
+
+	if (count > (sizeof(valbuf) - 1))
+		count = sizeof(valbuf) - 1;	/* limit length */
+	if (copy_from_user(valbuf, buf, count))
+		return (-EFAULT);	/* copy failed */
+
+	valbuf[count] = 0;	/* terminating 0 */
+	cp = valbuf;
+	if ((count > 2) && (valbuf[0] == '0') && (valbuf[1] == 'x')) {
+		cp += 2;	/* pointer after hex modifier */
+		base = 16;
+	}
+	/* scan the input for debug flags */
+	while (*cp) {
+		if ((*cp >= '0') && (*cp <= '9')) {
+			found = 1;
+			u *= base;	/* adjust to next digit */
+			u += *cp++ - '0';
+			continue;
+		}
+		if (base != 16)
+			break;	/* end of number */
+
+		if ((*cp >= 'a') && (*cp <= 'f')) {
+			found = 1;
+			u *= base;	/* adjust to next digit */
+			u += *cp++ - 'a' + 10;
+			continue;
+		}
+		break;		/* terminated */
+	}
+
+	if (found) {
+		card->debug_flags = u;	/* remember debug flags */
+		hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
+	}
+	return (count);
+}				/* hysdn_log_write */
+
+/******************/
+/* read log file */
+/******************/
+static ssize_t
+hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+	struct log_data *inf;
+	int len;
+	struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+	struct procdata *pd = NULL;
+	hysdn_card *card;
+
+	if (!*((struct log_data **) file->private_data)) {
+		if (file->f_flags & O_NONBLOCK)
+			return (-EAGAIN);
+
+		/* sorry, but we need to search the card */
+		card = card_root;
+		while (card) {
+			pd = card->proclog;
+			if (pd->log == pde)
+				break;
+			card = card->next;	/* search next entry */
+		}
+		if (card)
+			interruptible_sleep_on(&(pd->rd_queue));
+		else
+			return (-EAGAIN);
+
+	}
+	if (!(inf = *((struct log_data **) file->private_data)))
+		return (0);
+
+	inf->usage_cnt--;	/* new usage count */
+	file->private_data = &inf->next;	/* next structure */
+	if ((len = strlen(inf->log_start)) <= count) {
+		if (copy_to_user(buf, inf->log_start, len))
+			return -EFAULT;
+		*off += len;
+		return (len);
+	}
+	return (0);
+}				/* hysdn_log_read */
+
+/******************/
+/* open log file */
+/******************/
+static int
+hysdn_log_open(struct inode *ino, struct file *filep)
+{
+	hysdn_card *card;
+	struct procdata *pd = NULL;
+	ulong flags;
+
+	lock_kernel();
+	card = card_root;
+	while (card) {
+		pd = card->proclog;
+		if (pd->log == PDE(ino))
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card) {
+		unlock_kernel();
+		return (-ENODEV);	/* device is unknown/invalid */
+	}
+	filep->private_data = card;	/* remember our own card */
+
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write log level only */
+	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+
+		/* read access -> log/debug read */
+		save_flags(flags);
+		cli();
+		pd->if_used++;
+		if (pd->log_head)
+			filep->private_data = &pd->log_tail->next;
+		else
+			filep->private_data = &pd->log_head;
+		restore_flags(flags);
+	} else {		/* simultaneous read/write access forbidden ! */
+		unlock_kernel();
+		return (-EPERM);	/* no permission this time */
+	}
+	unlock_kernel();
+	return nonseekable_open(ino, filep);
+}				/* hysdn_log_open */
+
+/*******************************************************************************/
+/* close a cardlog file. If the file has been opened for exclusive write it is */
+/* assumed as pof data input and the pof loader is noticed about.              */
+/* Otherwise file is handled as log output. In this case the interface usage   */
+/* count is decremented and all buffers are noticed of closing. If this file   */
+/* was the last one to be closed, all buffers are freed.                       */
+/*******************************************************************************/
+static int
+hysdn_log_close(struct inode *ino, struct file *filep)
+{
+	struct log_data *inf;
+	struct procdata *pd;
+	hysdn_card *card;
+	int retval = 0;
+	unsigned long flags;
+
+
+	lock_kernel();
+	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+		/* write only access -> write debug level written */
+		retval = 0;	/* success */
+	} else {
+		/* read access -> log/debug read, mark one further file as closed */
+
+		pd = NULL;
+		save_flags(flags);
+		cli();
+		inf = *((struct log_data **) filep->private_data);	/* get first log entry */
+		if (inf)
+			pd = (struct procdata *) inf->proc_ctrl;	/* still entries there */
+		else {
+			/* no info available -> search card */
+			card = card_root;
+			while (card) {
+				pd = card->proclog;
+				if (pd->log == PDE(ino))
+					break;
+				card = card->next;	/* search next entry */
+			}
+			if (card)
+				pd = card->proclog;	/* pointer to procfs log */
+		}
+		if (pd)
+			pd->if_used--;	/* decrement interface usage count by one */
+
+		while (inf) {
+			inf->usage_cnt--;	/* decrement usage count for buffers */
+			inf = inf->next;
+		}
+		restore_flags(flags);
+
+		if (pd)
+			if (pd->if_used <= 0)	/* delete buffers if last file closed */
+				while (pd->log_head) {
+					inf = pd->log_head;
+					pd->log_head = pd->log_head->next;
+					kfree(inf);
+				}
+	}			/* read access */
+	unlock_kernel();
+
+	return (retval);
+}				/* hysdn_log_close */
+
+/*************************************************/
+/* select/poll routine to be able using select() */
+/*************************************************/
+static unsigned int
+hysdn_log_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+	struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+	hysdn_card *card;
+	struct procdata *pd = NULL;
+
+	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
+		return (mask);	/* no polling for write supported */
+
+	/* we need to search the card */
+	card = card_root;
+	while (card) {
+		pd = card->proclog;
+		if (pd->log == pde)
+			break;
+		card = card->next;	/* search next entry */
+	}
+	if (!card)
+		return (mask);	/* card not found */
+
+	poll_wait(file, &(pd->rd_queue), wait);
+
+	if (*((struct log_data **) file->private_data))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}				/* hysdn_log_poll */
+
+/**************************************************/
+/* table for log filesystem functions defined above. */
+/**************************************************/
+static struct file_operations log_fops =
+{
+	.llseek         = no_llseek,
+	.read           = hysdn_log_read,
+	.write          = hysdn_log_write,
+	.poll           = hysdn_log_poll,
+	.open           = hysdn_log_open,
+	.release        = hysdn_log_close,                                        
+};
+
+
+/***********************************************************************************/
+/* hysdn_proclog_init is called when the module is loaded after creating the cards */
+/* conf files.                                                                     */
+/***********************************************************************************/
+int
+hysdn_proclog_init(hysdn_card * card)
+{
+	struct procdata *pd;
+
+	/* create a cardlog proc entry */
+
+	if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
+		memset(pd, 0, sizeof(struct procdata));
+		sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
+		if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL) {
+		        pd->log->proc_fops = &log_fops; 
+		        pd->log->owner = THIS_MODULE;
+		}
+
+		init_waitqueue_head(&(pd->rd_queue));
+
+		card->proclog = (void *) pd;	/* remember procfs structure */
+	}
+	return (0);
+}				/* hysdn_proclog_init */
+
+/************************************************************************************/
+/* hysdn_proclog_release is called when the module is unloaded and before the cards */
+/* conf file is released                                                            */
+/* The module counter is assumed to be 0 !                                          */
+/************************************************************************************/
+void
+hysdn_proclog_release(hysdn_card * card)
+{
+	struct procdata *pd;
+
+	if ((pd = (struct procdata *) card->proclog) != NULL) {
+		if (pd->log)
+			remove_proc_entry(pd->log_name, hysdn_proc_entry);
+		kfree(pd);	/* release memory */
+		card->proclog = NULL;
+	}
+}				/* hysdn_proclog_release */
diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
new file mode 100644
index 0000000..4fa3b01
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_sched.c
@@ -0,0 +1,207 @@
+/* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * scheduler routines for handling exchange card <-> pc.
+ *
+ * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "hysdn_defs.h"
+
+/*****************************************************************************/
+/* hysdn_sched_rx is called from the cards handler to announce new data is   */
+/* available from the card. The routine has to handle the data and return    */
+/* with a nonzero code if the data could be worked (or even thrown away), if */
+/* no room to buffer the data is available a zero return tells the card      */
+/* to keep the data until later.                                             */
+/*****************************************************************************/
+int
+hysdn_sched_rx(hysdn_card * card, uchar * buf, word len, word chan)
+{
+
+	switch (chan) {
+		case CHAN_NDIS_DATA:
+			if (hynet_enable & (1 << card->myid)) {
+                          /* give packet to network handler */
+				hysdn_rx_netpkt(card, buf, len);
+			}
+			break;
+
+		case CHAN_ERRLOG:
+			hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
+			if (card->err_log_state == ERRLOG_STATE_ON)
+				card->err_log_state = ERRLOG_STATE_START;	/* start new fetch */
+			break;
+#ifdef CONFIG_HYSDN_CAPI
+         	case CHAN_CAPI:
+/* give packet to CAPI handler */
+			if (hycapi_enable & (1 << card->myid)) {
+				hycapi_rx_capipkt(card, buf, len);
+			}
+			break;
+#endif /* CONFIG_HYSDN_CAPI */
+		default:
+			printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
+			break;
+
+	}			/* switch rx channel */
+
+	return (1);		/* always handled */
+}				/* hysdn_sched_rx */
+
+/*****************************************************************************/
+/* hysdn_sched_tx is called from the cards handler to announce that there is */
+/* room in the tx-buffer to the card and data may be sent if needed.         */
+/* If the routine wants to send data it must fill buf, len and chan with the */
+/* appropriate data and return a nonzero value. With a zero return no new    */
+/* data to send is assumed. maxlen specifies the buffer size available for   */
+/* sending.                                                                  */
+/*****************************************************************************/
+int
+hysdn_sched_tx(hysdn_card * card, uchar * buf, word volatile *len, word volatile *chan, word maxlen)
+{
+	struct sk_buff *skb;
+
+	if (card->net_tx_busy) {
+		card->net_tx_busy = 0;	/* reset flag */
+		hysdn_tx_netack(card);	/* acknowledge packet send */
+	}			/* a network packet has completely been transferred */
+	/* first of all async requests are handled */
+	if (card->async_busy) {
+		if (card->async_len <= maxlen) {
+			memcpy(buf, card->async_data, card->async_len);
+			*len = card->async_len;
+			*chan = card->async_channel;
+			card->async_busy = 0;	/* reset request */
+			return (1);
+		}
+		card->async_busy = 0;	/* in case of length error */
+	}			/* async request */
+	if ((card->err_log_state == ERRLOG_STATE_START) &&
+	    (maxlen >= ERRLOG_CMD_REQ_SIZE)) {
+		strcpy(buf, ERRLOG_CMD_REQ);	/* copy the command */
+		*len = ERRLOG_CMD_REQ_SIZE;	/* buffer length */
+		*chan = CHAN_ERRLOG;	/* and channel */
+		card->err_log_state = ERRLOG_STATE_ON;	/* new state is on */
+		return (1);	/* tell that data should be send */
+	}			/* error log start and able to send */
+	if ((card->err_log_state == ERRLOG_STATE_STOP) &&
+	    (maxlen >= ERRLOG_CMD_STOP_SIZE)) {
+		strcpy(buf, ERRLOG_CMD_STOP);	/* copy the command */
+		*len = ERRLOG_CMD_STOP_SIZE;	/* buffer length */
+		*chan = CHAN_ERRLOG;	/* and channel */
+		card->err_log_state = ERRLOG_STATE_OFF;		/* new state is off */
+		return (1);	/* tell that data should be send */
+	}			/* error log start and able to send */
+	/* now handle network interface packets */
+	if ((hynet_enable & (1 << card->myid)) && 
+	    (skb = hysdn_tx_netget(card)) != NULL) 
+	{
+		if (skb->len <= maxlen) {
+			memcpy(buf, skb->data, skb->len);	/* copy the packet to the buffer */
+			*len = skb->len;
+			*chan = CHAN_NDIS_DATA;
+			card->net_tx_busy = 1;	/* we are busy sending network data */
+			return (1);	/* go and send the data */
+		} else
+			hysdn_tx_netack(card);	/* aknowledge packet -> throw away */
+	}			/* send a network packet if available */
+#ifdef CONFIG_HYSDN_CAPI
+	if( ((hycapi_enable & (1 << card->myid))) && 
+	    ((skb = hycapi_tx_capiget(card)) != NULL) )
+	{
+		if (skb->len <= maxlen) {
+			memcpy(buf, skb->data, skb->len);
+			*len = skb->len;
+			*chan = CHAN_CAPI;
+			hycapi_tx_capiack(card);
+			return (1);	/* go and send the data */
+		}
+	}
+#endif /* CONFIG_HYSDN_CAPI */
+	return (0);		/* nothing to send */
+}				/* hysdn_sched_tx */
+
+
+/*****************************************************************************/
+/* send one config line to the card and return 0 if successful, otherwise a */
+/* negative error code.                                                      */
+/* The function works with timeouts perhaps not giving the greatest speed    */
+/* sending the line, but this should be meaningless beacuse only some lines  */
+/* are to be sent and this happens very seldom.                              */
+/*****************************************************************************/
+int
+hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan)
+{
+	int cnt = 50;		/* timeout intervalls */
+	ulong flags;
+
+	if (card->debug_flags & LOG_SCHED_ASYN)
+		hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
+
+	save_flags(flags);
+	cli();
+	while (card->async_busy) {
+		sti();
+
+		if (card->debug_flags & LOG_SCHED_ASYN)
+			hysdn_addlog(card, "async tx-cfg delayed");
+
+		msleep_interruptible(20);		/* Timeout 20ms */
+		if (!--cnt) {
+			restore_flags(flags);
+			return (-ERR_ASYNC_TIME);	/* timed out */
+		}
+		cli();
+	}			/* wait for buffer to become free */
+
+	strcpy(card->async_data, line);
+	card->async_len = strlen(line) + 1;
+	card->async_channel = chan;
+	card->async_busy = 1;	/* request transfer */
+
+	/* now queue the task */
+	schedule_work(&card->irq_queue);
+	sti();
+
+	if (card->debug_flags & LOG_SCHED_ASYN)
+		hysdn_addlog(card, "async tx-cfg data queued");
+
+	cnt++;			/* short delay */
+	cli();
+
+	while (card->async_busy) {
+		sti();
+
+		if (card->debug_flags & LOG_SCHED_ASYN)
+			hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
+
+		msleep_interruptible(20);		/* Timeout 20ms */
+		if (!--cnt) {
+			restore_flags(flags);
+			return (-ERR_ASYNC_TIME);	/* timed out */
+		}
+		cli();
+	}			/* wait for buffer to become free again */
+
+	restore_flags(flags);
+
+	if (card->debug_flags & LOG_SCHED_ASYN)
+		hysdn_addlog(card, "async tx-cfg data send");
+
+	return (0);		/* line send correctly */
+}				/* hysdn_tx_cfgline */
diff --git a/drivers/isdn/hysdn/ince1pc.h b/drivers/isdn/hysdn/ince1pc.h
new file mode 100644
index 0000000..4a115a8
--- /dev/null
+++ b/drivers/isdn/hysdn/ince1pc.h
@@ -0,0 +1,134 @@
+/*
+ * Linux driver for HYSDN cards
+ * common definitions for both sides of the bus:
+ * - conventions both spoolers must know
+ * - channel numbers agreed upon
+ *
+ * Author    M. Steinkopf
+ * Copyright 1999 by M. Steinkopf
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __INCE1PC_H__
+#define __INCE1PC_H__
+
+/*  basic scalar definitions have same meanning,
+ *  but their declaration location depends on environment
+ */ 
+
+/*--------------------------------------channel numbers---------------------*/ 
+#define CHAN_SYSTEM     0x0001      /* system channel (spooler to spooler) */
+#define CHAN_ERRLOG     0x0005      /* error logger */
+#define CHAN_CAPI       0x0064      /* CAPI interface */
+#define CHAN_NDIS_DATA  0x1001      /* NDIS data transfer */
+
+/*--------------------------------------POF ready msg-----------------------*/ 
+	    /* NOTE: after booting POF sends system ready message to PC: */ 
+#define RDY_MAGIC       0x52535953UL    /* 'SYSR' reversed */
+#define RDY_MAGIC_SIZE  4               /* size in bytes */
+
+#define MAX_N_TOK_BYTES 255
+
+#define MIN_RDY_MSG_SIZE    RDY_MAGIC_SIZE
+#define MAX_RDY_MSG_SIZE    (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+
+#define SYSR_TOK_END            0
+#define SYSR_TOK_B_CHAN         1   /* nr. of B-Channels;   DataLen=1; def: 2 */
+#define SYSR_TOK_FAX_CHAN       2   /* nr. of FAX Channels; DataLen=1; def: 0 */
+#define SYSR_TOK_MAC_ADDR       3   /* MAC-Address; DataLen=6; def: auto */
+#define SYSR_TOK_ESC            255 /* undefined data size yet */
+			    /* default values, if not corrected by token: */ 
+#define SYSR_TOK_B_CHAN_DEF     2   /* assume 2 B-Channels */
+#define SYSR_TOK_FAX_CHAN_DEF   1   /* assume 1 FAX Channel */
+
+/*  syntax of new SYSR token stream:
+ *  channel: CHAN_SYSTEM
+ *  msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
+ *           RDY_MAGIC_SIZE   <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+ *  msg    : 0 1 2 3 {4 5 6 ..}
+ *           S Y S R  MAX_N_TOK_BYTES bytes of TokenStream
+ *
+ *  TokenStream     :=   empty
+ *                     | {NonEndTokenChunk} EndToken RotlCRC
+ *  NonEndTokenChunk:= NonEndTokenId DataLen [Data]
+ *  NonEndTokenId   := 0x01 .. 0xFE                 1 BYTE
+ *  DataLen         := 0x00 .. 0xFF                 1 BYTE
+ *  Data            := DataLen bytes
+ *  EndToken        := 0x00
+ *  RotlCRC         := special 1 byte CRC over all NonEndTokenChunk bytes
+ *                     s. RotlCRC algorithm
+ *
+ *  RotlCRC algorithm:
+ *      ucSum= 0                        1 uchar
+ *      for all NonEndTokenChunk bytes:
+ *          ROTL(ucSum,1)               rotate left by 1
+ *          ucSum += Char;              add current byte with swap around
+ *      RotlCRC= ~ucSum;                invert all bits for result
+ *
+ *  note:
+ *  - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
+ */ 
+
+/*--------------------------------------error logger------------------------*/ 
+					    /* note: pof needs final 0 ! */ 
+#define ERRLOG_CMD_REQ          "ERRLOG ON"
+#define ERRLOG_CMD_REQ_SIZE     10              /* with final 0 byte ! */
+#define ERRLOG_CMD_STOP         "ERRLOG OFF"
+#define ERRLOG_CMD_STOP_SIZE    11              /* with final 0 byte ! */
+
+#define ERRLOG_ENTRY_SIZE       64      /* sizeof(tErrLogEntry) */
+					/* remaining text size = 55 */ 
+#define ERRLOG_TEXT_SIZE    (ERRLOG_ENTRY_SIZE-2*4-1)
+
+typedef struct ErrLogEntry_tag {
+	
+/*00 */ ulong ulErrType;
+	
+/*04 */ ulong ulErrSubtype;
+	
+/*08 */ uchar ucTextSize;
+	
+	/*09 */ uchar ucText[ERRLOG_TEXT_SIZE];
+	/* ASCIIZ of len ucTextSize-1 */
+	
+/*40 */ 
+} tErrLogEntry;
+
+
+#if defined(__TURBOC__)
+#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
+#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
+#endif				/*  */
+#endif				/*  */
+
+/*--------------------------------------DPRAM boot spooler------------------*/ 
+				/*  this is the struture used between pc and
+				 *  hyperstone to exchange boot data
+				 */ 
+#define DPRAM_SPOOLER_DATA_SIZE 0x20
+typedef struct DpramBootSpooler_tag {
+	
+/*00 */ uchar Len;
+	
+/*01 */ volatile uchar RdPtr;
+	
+/*02 */ uchar WrPtr;
+	
+/*03 */ uchar Data[DPRAM_SPOOLER_DATA_SIZE];
+	
+/*23 */ 
+} tDpramBootSpooler;
+
+
+#define DPRAM_SPOOLER_MIN_SIZE  5       /* Len+RdPtr+Wrptr+2*data */
+#define DPRAM_SPOOLER_DEF_SIZE  0x23    /* current default size   */
+
+/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/ 
+				    /* at DPRAM offset 0x1C00: */ 
+#define SIZE_RSV_SOFT_UART  0x1B0   /* 432 bytes reserved for SoftUart */
+
+
+#endif	/* __INCE1PC_H__ */
diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig
new file mode 100644
index 0000000..1789b60
--- /dev/null
+++ b/drivers/isdn/i4l/Kconfig
@@ -0,0 +1,141 @@
+#
+# Old ISDN4Linux config
+#
+
+config ISDN_PPP
+	bool "Support synchronous PPP"
+	depends on INET
+	help
+	  Over digital connections such as ISDN, there is no need to
+	  synchronize sender and recipient's clocks with start and stop bits
+	  as is done over analog telephone lines. Instead, one can use
+	  "synchronous PPP". Saying Y here will include this protocol. This
+	  protocol is used by Cisco and Sun for example. So you want to say Y
+	  here if the other end of your ISDN connection supports it. You will
+	  need a special version of pppd (called ipppd) for using this
+	  feature. See <file:Documentation/isdn/README.syncppp> and
+	  <file:Documentation/isdn/syncPPP.FAQ> for more information.
+
+config ISDN_PPP_VJ
+	bool "Use VJ-compression with synchronous PPP"
+	depends on ISDN_PPP
+	help
+	  This enables Van Jacobson header compression for synchronous PPP.
+	  Say Y if the other end of the connection supports it.
+
+config ISDN_MPP
+	bool "Support generic MP (RFC 1717)"
+	depends on ISDN_PPP
+	help
+	  With synchronous PPP enabled, it is possible to increase throughput
+	  by bundling several ISDN-connections, using this protocol. See
+	  <file:Documentation/isdn/README.syncppp> for more information.
+
+config IPPP_FILTER
+	bool "Filtering for synchronous PPP"
+	depends on ISDN_PPP
+	help
+	  Say Y here if you want to be able to filter the packets passing over
+	  IPPP interfaces.  This allows you to control which packets count as
+	  activity (i.e. which packets will reset the idle timer or bring up
+	  a demand-dialled link) and which packets are to be dropped entirely.
+	  You need to say Y here if you wish to use the pass-filter and
+	  active-filter options to ipppd.
+
+config ISDN_PPP_BSDCOMP
+	tristate "Support BSD compression"
+	depends on ISDN_PPP
+	help
+	  Support for the BSD-Compress compression method for PPP, which uses
+	  the LZW compression method to compress each PPP packet before it is
+	  sent over the wire. The machine at the other end of the PPP link
+	  (usually your ISP) has to support the BSD-Compress compression
+	  method as well for this to be useful. Even if they don't support it,
+	  it is safe to say Y here.
+
+config ISDN_AUDIO
+	bool "Support audio via ISDN"
+	help
+	  If you say Y here, the modem-emulator will support a subset of the
+	  EIA Class 8 Voice commands. Using a getty with voice-support
+	  (mgetty+sendfax by <gert@greenie.muc.de> with an extension, available
+	  with the ISDN utility package for example), you will be able to use
+	  your Linux box as an ISDN-answering machine. Of course, this must be
+	  supported by the lowlevel driver also. Currently, the HiSax driver
+	  is the only voice-supporting driver. See
+	  <file:Documentation/isdn/README.audio> for more information.
+
+config ISDN_TTY_FAX
+	bool "Support AT-Fax Class 1 and 2 commands"
+	depends on ISDN_AUDIO
+	help
+	  If you say Y here, the modem-emulator will support a subset of the
+	  Fax Class 1 and 2 commands. Using a getty with fax-support
+	  (mgetty+sendfax, hylafax), you will be able to use your Linux box as
+	  an ISDN-fax-machine. This must be supported by the lowlevel driver
+	  also. See <file:Documentation/isdn/README.fax> for more information.
+
+config ISDN_X25
+	bool "X.25 PLP on top of ISDN"
+	depends on X25
+	help
+	  This feature provides the X.25 protocol over ISDN connections.
+	  See <file:Documentation/isdn/README.x25> for more information
+	  if you are thinking about using this.
+
+
+menu "ISDN feature submodules"
+	depends on ISDN
+
+config ISDN_DRV_LOOP
+	tristate "isdnloop support"
+	depends on BROKEN_ON_SMP
+	help
+	  This driver provides a virtual ISDN card. Its primary purpose is
+	  testing of linklevel features or configuration without getting
+	  charged by your service-provider for lots of phone calls.
+	  You need will need the loopctrl utility from the latest isdn4k-utils
+	  package to set up this driver.
+
+config ISDN_DIVERSION
+	tristate "Support isdn diversion services"
+	depends on ISDN && ISDN_I4L
+	help
+	  This option allows you to use some supplementary diversion
+	  services in conjunction with the HiSax driver on an EURO/DSS1
+	  line.
+
+	  Supported options are CD (call deflection), CFU (Call forward
+	  unconditional), CFB (Call forward when busy) and CFNR (call forward
+	  not reachable). Additionally the actual CFU, CFB and CFNR state may
+	  be interrogated.
+
+	  The use of CFU, CFB, CFNR and interrogation may be limited to some
+	  countries. The keypad protocol is still not implemented. CD should
+	  work in all countries if the service has been subscribed to.
+
+	  Please read the file <file:Documentation/isdn/README.diversion>.
+
+endmenu
+
+comment "ISDN4Linux hardware drivers"
+	depends on NET && ISDN && ISDN_I4L
+
+source "drivers/isdn/hisax/Kconfig"
+
+
+menu "Active cards"
+	depends on NET && ISDN && ISDN_I4L!=n
+
+source "drivers/isdn/icn/Kconfig"
+
+source "drivers/isdn/pcbit/Kconfig"
+
+source "drivers/isdn/sc/Kconfig"
+
+source "drivers/isdn/act2000/Kconfig"
+
+source "drivers/isdn/hysdn/Kconfig"
+
+endmenu
+
diff --git a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile
new file mode 100644
index 0000000..49a06c0
--- /dev/null
+++ b/drivers/isdn/i4l/Makefile
@@ -0,0 +1,18 @@
+# Makefile for the kernel ISDN subsystem and device drivers.
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_I4L)		+= isdn.o
+obj-$(CONFIG_ISDN_PPP_BSDCOMP)	+= isdn_bsdcomp.o
+
+# Multipart objects.
+
+isdn-y				:= isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o
+
+# Optional parts of multipart objects.
+
+isdn-$(CONFIG_ISDN_PPP)		+= isdn_ppp.o
+isdn-$(CONFIG_ISDN_X25)		+= isdn_concap.o isdn_x25iface.o
+isdn-$(CONFIG_ISDN_AUDIO)		+= isdn_audio.o
+isdn-$(CONFIG_ISDN_TTY_FAX)	+= isdn_ttyfax.o
+
diff --git a/drivers/isdn/i4l/isdn_audio.c b/drivers/isdn/i4l/isdn_audio.c
new file mode 100644
index 0000000..5350836
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_audio.c
@@ -0,0 +1,720 @@
+/* $Id: isdn_audio.c,v 1.1.2.2 2004/01/12 22:37:18 keil Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
+ * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/isdn.h>
+#include "isdn_audio.h"
+#include "isdn_common.h"
+
+char *isdn_audio_revision = "$Revision: 1.1.2.2 $";
+
+/*
+ * Misc. lookup-tables.
+ */
+
+/* ulaw -> signed 16-bit */
+static short isdn_audio_ulaw_to_s16[] =
+{
+	0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
+	0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
+	0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
+	0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
+	0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
+	0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
+	0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
+	0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
+	0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
+	0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
+	0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
+	0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
+	0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
+	0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
+	0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
+	0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
+	0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
+	0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
+	0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
+	0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
+	0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
+	0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
+	0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
+	0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
+	0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
+	0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
+	0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
+	0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
+	0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
+	0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
+	0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
+	0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+/* alaw -> signed 16-bit */
+static short isdn_audio_alaw_to_s16[] =
+{
+	0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
+	0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
+	0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
+	0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
+	0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
+	0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
+	0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
+	0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
+	0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
+	0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
+	0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
+	0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
+	0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
+	0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
+	0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
+	0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
+	0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
+	0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
+	0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
+	0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
+	0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
+	0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
+	0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
+	0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
+	0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
+	0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
+	0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
+	0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
+	0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
+	0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
+	0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
+	0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
+};
+
+/* alaw -> ulaw */
+static char isdn_audio_alaw_to_ulaw[] =
+{
+	0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+	0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+	0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+	0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+	0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+	0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+	0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+	0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+	0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+	0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+	0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+	0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+	0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+	0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+	0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+	0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+	0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+	0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+	0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+	0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+	0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+	0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+	0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+	0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+	0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+	0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+	0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+	0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+	0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+	0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+	0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+	0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+};
+
+/* ulaw -> alaw */
+static char isdn_audio_ulaw_to_alaw[] =
+{
+	0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+	0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+	0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+	0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+	0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+	0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+	0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+	0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+	0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+	0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+	0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+	0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+	0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+	0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+	0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+	0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+	0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+	0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+	0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+	0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+	0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+	0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+	0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+	0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+	0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+	0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+	0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+	0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+	0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+	0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+	0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+	0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+};
+
+#define NCOEFF            8     /* number of frequencies to be analyzed       */
+#define DTMF_TRESH     4000     /* above this is dtmf                         */
+#define SILENCE_TRESH   200     /* below this is silence                      */
+#define AMP_BITS          9     /* bits per sample, reduced to avoid overflow */
+#define LOGRP             0
+#define HIGRP             1
+
+/* For DTMF recognition:
+ * 2 * cos(2 * PI * k / N) precalculated for all k
+ */
+static int cos2pik[NCOEFF] =
+{
+	55813, 53604, 51193, 48591, 38114, 33057, 25889, 18332
+};
+
+static char dtmf_matrix[4][4] =
+{
+	{'1', '2', '3', 'A'},
+	{'4', '5', '6', 'B'},
+	{'7', '8', '9', 'C'},
+	{'*', '0', '#', 'D'}
+};
+
+static inline void
+isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n)
+{
+#ifdef __i386__
+	unsigned long d0, d1, d2, d3;
+	__asm__ __volatile__(
+		"cld\n"
+		"1:\tlodsb\n\t"
+		"xlatb\n\t"
+		"stosb\n\t"
+		"loop 1b\n\t"
+	:	"=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3)
+	:	"0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff)
+	:	"memory", "ax");
+#else
+	while (n--)
+		*buff = table[*(unsigned char *)buff], buff++;
+#endif
+}
+
+void
+isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
+{
+	isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
+}
+
+void
+isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len)
+{
+	isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
+}
+
+/*
+ * linear <-> adpcm conversion stuff
+ * Most parts from the mgetty-package.
+ * (C) by Gert Doering and Klaus Weidner
+ * Used by permission of Gert Doering
+ */
+
+
+#define ZEROTRAP                /* turn on the trap as per the MIL-STD */
+#undef ZEROTRAP
+#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+static unsigned char
+isdn_audio_linear2ulaw(int sample)
+{
+	static int exp_lut[256] =
+	{
+		0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+	};
+	int sign,
+	 exponent,
+	 mantissa;
+	unsigned char ulawbyte;
+
+	/* Get the sample into sign-magnitude. */
+	sign = (sample >> 8) & 0x80;	/* set aside the sign  */
+	if (sign != 0)
+		sample = -sample;	/* get magnitude       */
+	if (sample > CLIP)
+		sample = CLIP;  /* clip the magnitude  */
+
+	/* Convert from 16 bit linear to ulaw. */
+	sample = sample + BIAS;
+	exponent = exp_lut[(sample >> 7) & 0xFF];
+	mantissa = (sample >> (exponent + 3)) & 0x0F;
+	ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+	/* optional CCITT trap */
+	if (ulawbyte == 0)
+		ulawbyte = 0x02;
+#endif
+	return (ulawbyte);
+}
+
+
+static int Mx[3][8] =
+{
+	{0x3800, 0x5600, 0, 0, 0, 0, 0, 0},
+	{0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0},
+	{0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607},
+};
+
+static int bitmask[9] =
+{
+	0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
+};
+
+static int
+isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len)
+{
+	while (s->nleft < s->nbits) {
+		int d = *((*in)++);
+		(*len)--;
+		s->word = (s->word << 8) | d;
+		s->nleft += 8;
+	}
+	s->nleft -= s->nbits;
+	return (s->word >> s->nleft) & bitmask[s->nbits];
+}
+
+static void
+isdn_audio_put_bits(int data, int nbits, adpcm_state * s,
+		    unsigned char **out, int *len)
+{
+	s->word = (s->word << nbits) | (data & bitmask[nbits]);
+	s->nleft += nbits;
+	while (s->nleft >= 8) {
+		int d = (s->word >> (s->nleft - 8));
+		*(out[0]++) = d & 255;
+		(*len)++;
+		s->nleft -= 8;
+	}
+}
+
+adpcm_state *
+isdn_audio_adpcm_init(adpcm_state * s, int nbits)
+{
+	if (!s)
+		s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
+	if (s) {
+		s->a = 0;
+		s->d = 5;
+		s->word = 0;
+		s->nleft = 0;
+		s->nbits = nbits;
+	}
+	return s;
+}
+
+dtmf_state *
+isdn_audio_dtmf_init(dtmf_state * s)
+{
+	if (!s)
+		s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
+	if (s) {
+		s->idx = 0;
+		s->last = ' ';
+	}
+	return s;
+}
+
+/*
+ * Decompression of adpcm data to a/u-law
+ *
+ */
+
+int
+isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
+{
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len) {
+		int e = isdn_audio_get_bits(s, &in, &len);
+		int sign;
+
+		if (nbits == 4 && e == 0)
+			d = 4;
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		if (fmt)
+			*out++ = isdn_audio_ulaw_to_alaw[
+					 isdn_audio_linear2ulaw(a << 2)];
+		else
+			*out++ = isdn_audio_linear2ulaw(a << 2);
+		olen++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
+	s->a = a;
+	s->d = d;
+	return olen;
+}
+
+int
+isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out)
+{
+	int olen = 0;
+
+	if (s->nleft)
+		isdn_audio_put_bits(0, 8 - s->nleft, s, &out, &olen);
+	return olen;
+}
+
+int
+isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in,
+		      unsigned char *out, int len)
+{
+	int a = s->a;
+	int d = s->d;
+	int nbits = s->nbits;
+	int olen = 0;
+
+	while (len--) {
+		int e = 0,
+		 nmax = 1 << (nbits - 1);
+		int sign,
+		 delta;
+
+		if (fmt)
+			delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
+		else
+			delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
+		if (delta < 0) {
+			e = nmax;
+			delta = -delta;
+		}
+		while (--nmax && delta > d) {
+			delta -= d;
+			e++;
+		}
+		if (nbits == 4 && ((e & 0x0f) == 0))
+			e = 8;
+		isdn_audio_put_bits(e, nbits, s, &out, &olen);
+		sign = (e >> (nbits - 1)) ? -1 : 1;
+		e &= bitmask[nbits - 1];
+
+		a += sign * ((e << 1) + 1) * d >> 1;
+		if (d & 1)
+			a++;
+		d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+		if (d < 5)
+			d = 5;
+	}
+	s->a = a;
+	s->d = d;
+	return olen;
+}
+
+/*
+ * Goertzel algorithm.
+ * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/
+ * for more info.
+ * Result is stored into an sk_buff and queued up for later
+ * evaluation.
+ */
+static void
+isdn_audio_goertzel(int *sample, modem_info * info)
+{
+	int sk,
+	 sk1,
+	 sk2;
+	int k,
+	 n;
+	struct sk_buff *skb;
+	int *result;
+
+	skb = dev_alloc_skb(sizeof(int) * NCOEFF);
+	if (!skb) {
+		printk(KERN_WARNING
+		  "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
+		       info->line);
+		return;
+	}
+	result = (int *) skb_put(skb, sizeof(int) * NCOEFF);
+	for (k = 0; k < NCOEFF; k++) {
+		sk = sk1 = sk2 = 0;
+		for (n = 0; n < DTMF_NPOINTS; n++) {
+			sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
+			sk2 = sk1;
+			sk1 = sk;
+		}
+		/* Avoid overflows */
+		sk >>= 1;
+		sk2 >>= 1;
+		/* compute |X(k)|**2 */
+		/* report overflows. This should not happen. */
+		/* Comment this out if desired */
+		if (sk < -32768 || sk > 32767)
+			printk(KERN_DEBUG
+			       "isdn_audio: dtmf goertzel overflow, sk=%d\n", sk);
+		if (sk2 < -32768 || sk2 > 32767)
+			printk(KERN_DEBUG
+			       "isdn_audio: dtmf goertzel overflow, sk2=%d\n", sk2);
+		result[k] =
+		    ((sk * sk) >> AMP_BITS) -
+		    ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
+		    ((sk2 * sk2) >> AMP_BITS);
+	}
+	skb_queue_tail(&info->dtmf_queue, skb);
+	isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+}
+
+void
+isdn_audio_eval_dtmf(modem_info * info)
+{
+	struct sk_buff *skb;
+	int *result;
+	dtmf_state *s;
+	int silence;
+	int i;
+	int di;
+	int ch;
+	int grp[2];
+	char what;
+	char *p;
+	int thresh;
+
+	while ((skb = skb_dequeue(&info->dtmf_queue))) {
+		result = (int *) skb->data;
+		s = info->dtmf_state;
+		grp[LOGRP] = grp[HIGRP] = -1;
+		silence = 0;
+		thresh = 0;
+		for (i = 0; i < NCOEFF; i++) {
+			if (result[i] > DTMF_TRESH) {
+				if (result[i] > thresh)
+					thresh = result[i];
+			}
+			else if (result[i] < SILENCE_TRESH)
+				silence++;
+		}
+		if (silence == NCOEFF)
+			what = ' ';
+		else {
+			if (thresh > 0)	{
+				thresh = thresh >> 4;  /* touchtones must match within 12 dB */
+				for (i = 0; i < NCOEFF; i++) {
+					if (result[i] < thresh)
+						continue;  /* ignore */
+					/* good level found. This is allowed only one time per group */
+					if (i < NCOEFF / 2) {
+						/* lowgroup*/
+						if (grp[LOGRP] >= 0) {
+							// Bad. Another tone found. */
+							grp[LOGRP] = -1;
+							break;
+						}
+						else
+							grp[LOGRP] = i;
+					}
+					else { /* higroup */
+						if (grp[HIGRP] >= 0) { // Bad. Another tone found. */
+							grp[HIGRP] = -1;
+							break;
+						}
+						else
+							grp[HIGRP] = i - NCOEFF/2;
+					}
+				}
+				if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
+					what = dtmf_matrix[grp[LOGRP]][grp[HIGRP]];
+					if (s->last != ' ' && s->last != '.')
+						s->last = what;	/* min. 1 non-DTMF between DTMF */
+				} else
+					what = '.';
+			}
+			else
+				what = '.';
+		}
+		if ((what != s->last) && (what != ' ') && (what != '.')) {
+			printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
+			p = skb->data;
+			*p++ = 0x10;
+			*p = what;
+			skb_trim(skb, 2);
+			ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+			di = info->isdn_driver;
+			ch = info->isdn_channel;
+			__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+			dev->drv[di]->rcvcount[ch] += 2;
+			/* Schedule dequeuing */
+			if ((dev->modempoll) && (info->rcvsched))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+			wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+		} else
+			kfree_skb(skb);
+		s->last = what;
+	}
+}
+
+/*
+ * Decode DTMF tones, queue result in separate sk_buf for
+ * later examination.
+ * Parameters:
+ *   s    = pointer to state-struct.
+ *   buf  = input audio data
+ *   len  = size of audio data.
+ *   fmt  = audio data format (0 = ulaw, 1 = alaw)
+ */
+void
+isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+	dtmf_state *s = info->dtmf_state;
+	int i;
+	int c;
+
+	while (len) {
+		c = DTMF_NPOINTS - s->idx;
+		if (c > len)
+			c = len;
+		if (c <= 0)
+			break;
+		for (i = 0; i < c; i++) {
+			if (fmt)
+				s->buf[s->idx++] =
+				    isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
+			else
+				s->buf[s->idx++] =
+				    isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
+		}
+		if (s->idx == DTMF_NPOINTS) {
+			isdn_audio_goertzel(s->buf, info);
+			s->idx = 0;
+		}
+		len -= c;
+	}
+}
+
+silence_state *
+isdn_audio_silence_init(silence_state * s)
+{
+	if (!s)
+		s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC);
+	if (s) {
+		s->idx = 0;
+		s->state = 0;
+	}
+	return s;
+}
+
+void
+isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+	silence_state *s = info->silence_state;
+	int i;
+	signed char c;
+
+	if (!info->emu.vpar[1]) return;
+
+	for (i = 0; i < len; i++) {
+		if (fmt)
+		    c = isdn_audio_alaw_to_ulaw[*buf++];
+			else
+		    c = *buf++;
+
+		if (c > 0) c -= 128;
+		c = abs(c);
+
+		if (c > (info->emu.vpar[1] * 4)) { 
+			s->idx = 0;
+			s->state = 1; 
+		} else {
+			if (s->idx < 210000) s->idx++; 
+		}
+	}
+}
+
+void
+isdn_audio_put_dle_code(modem_info * info, u_char code)
+{
+	struct sk_buff *skb;
+	int di;
+	int ch;
+	char *p;
+
+	skb = dev_alloc_skb(2);
+	if (!skb) {
+		printk(KERN_WARNING
+		  "isdn_audio: Could not alloc skb for ttyI%d\n",
+		       info->line);
+		return;
+	}
+	p = (char *) skb_put(skb, 2);
+	p[0] = 0x10;
+	p[1] = code;
+	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+	ISDN_AUDIO_SKB_LOCK(skb) = 0;
+	di = info->isdn_driver;
+	ch = info->isdn_channel;
+	__skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+	dev->drv[di]->rcvcount[ch] += 2;
+	/* Schedule dequeuing */
+	if ((dev->modempoll) && (info->rcvsched))
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+	wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+}
+
+void
+isdn_audio_eval_silence(modem_info * info)
+{
+	silence_state *s = info->silence_state;
+	char what;
+
+	what = ' ';
+
+	if (s->idx > (info->emu.vpar[2] * 800)) { 
+		s->idx = 0;
+		if (!s->state) {	/* silence from beginning of rec */ 
+			what = 's';
+		} else {
+			what = 'q';
+		}
+	}
+		if ((what == 's') || (what == 'q')) {
+			printk(KERN_DEBUG "ttyI%d: %s\n", info->line,
+				(what=='s') ? "silence":"quiet");
+			isdn_audio_put_dle_code(info, what);
+		} 
+}
diff --git a/drivers/isdn/i4l/isdn_audio.h b/drivers/isdn/i4l/isdn_audio.h
new file mode 100644
index 0000000..5a977b2
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_audio.h
@@ -0,0 +1,45 @@
+/* $Id: isdn_audio.h,v 1.1.2.2 2004/01/12 22:37:18 keil Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DTMF_NPOINTS 205        /* Number of samples for DTMF recognition */
+typedef struct adpcm_state {
+	int a;
+	int d;
+	int word;
+	int nleft;
+	int nbits;
+} adpcm_state;
+
+typedef struct dtmf_state {
+	char last;
+	char llast;
+	int idx;
+	int buf[DTMF_NPOINTS];
+} dtmf_state;
+
+typedef struct silence_state {
+	int state;
+	unsigned int idx;
+} silence_state;
+
+extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
+extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
+extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
+extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out);
+extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_dtmf(modem_info *);
+dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
+extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_silence(modem_info *);
+silence_state *isdn_audio_silence_init(silence_state *);
+extern void isdn_audio_put_dle_code(modem_info *, u_char);
diff --git a/drivers/isdn/i4l/isdn_bsdcomp.c b/drivers/isdn/i4l/isdn_bsdcomp.c
new file mode 100644
index 0000000..baf4bcad
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_bsdcomp.c
@@ -0,0 +1,937 @@
+/*
+ * BSD compression module
+ *
+ * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
+ * The whole module is now SKB based.
+ *
+ */
+
+/*
+ * Update: The Berkeley copyright was changed, and the change 
+ * is retroactive to all "true" BSD software (ie everything
+ * from UCB as opposed to other peoples code that just carried
+ * the same license). The new copyright doesn't clash with the
+ * GPL, so the module-only restriction has been removed..
+ */
+
+/*
+ * Original copyright notice:
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>	/* used in new tty drivers */
+#include <linux/signal.h>	/* used in new tty drivers */
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+
+#include <linux/if.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ppp_defs.h>
+
+#include <linux/isdn.h>
+#include <linux/isdn_ppp.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <linux/ppp-comp.h>
+
+#include "isdn_ppp.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: BSD Compression for PPP over ISDN");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define BSD_VERSION(x)	((x) >> 5)
+#define BSD_NBITS(x)	((x) & 0x1F)
+
+#define BSD_CURRENT_VERSION	1
+
+#define DEBUG 1
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+
+struct bsd_dict {
+	u32 fcode;
+	u16 codem1;		/* output of hash table -1 */
+	u16 cptr;		/* map code to hash table entry */
+};
+
+struct bsd_db {
+	int            totlen;		/* length of this structure */
+	unsigned int   hsize;		/* size of the hash table */
+	unsigned char  hshift;		/* used in hash function */
+	unsigned char  n_bits;		/* current bits/code */
+	unsigned char  maxbits;		/* maximum bits/code */
+	unsigned char  debug;		/* non-zero if debug desired */
+	unsigned char  unit;		/* ppp unit number */
+	u16 seqno;          		/* sequence # of next packet */
+	unsigned int   mru;		/* size of receive (decompress) bufr */
+	unsigned int   maxmaxcode;	/* largest valid code */
+	unsigned int   max_ent;		/* largest code in use */
+	unsigned int   in_count;	/* uncompressed bytes, aged */
+	unsigned int   bytes_out;	/* compressed bytes, aged */
+	unsigned int   ratio;		/* recent compression ratio */
+	unsigned int   checkpoint;	/* when to next check the ratio */
+	unsigned int   clear_count;	/* times dictionary cleared */
+	unsigned int   incomp_count;	/* incompressible packets */
+	unsigned int   incomp_bytes;	/* incompressible bytes */
+	unsigned int   uncomp_count;	/* uncompressed packets */
+	unsigned int   uncomp_bytes;	/* uncompressed bytes */
+	unsigned int   comp_count;	/* compressed packets */
+	unsigned int   comp_bytes;	/* compressed bytes */
+	unsigned short  *lens;		/* array of lengths of codes */
+	struct bsd_dict *dict;		/* dictionary */
+	int xmit;
+};
+
+#define BSD_OVHD	2		/* BSD compress overhead/packet */
+#define MIN_BSD_BITS	9
+#define BSD_INIT_BITS	MIN_BSD_BITS
+#define MAX_BSD_BITS	15
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR	256			/* table clear output code */
+#define FIRST	257			/* first free entry */
+#define LAST	255
+
+#define MAXCODE(b)	((1 << (b)) - 1)
+#define BADCODEM1	MAXCODE(MAX_BSD_BITS);
+
+#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
+					 ^ (unsigned long)(prefix))
+#define BSD_KEY(prefix,suffix)		((((unsigned long)(suffix)) << 16) \
+					 + (unsigned long)(prefix))
+
+#define CHECK_GAP	10000		/* Ratio check interval */
+
+#define RATIO_SCALE_LOG	8
+#define RATIO_SCALE	(1<<RATIO_SCALE_LOG)
+#define RATIO_MAX	(0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+
+static void bsd_clear(struct bsd_db *db)
+{
+	db->clear_count++;
+	db->max_ent      = FIRST-1;
+	db->n_bits       = BSD_INIT_BITS;
+	db->bytes_out    = 0;
+	db->in_count     = 0;
+	db->incomp_count = 0;
+	db->ratio	     = 0;
+	db->checkpoint   = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int bsd_check (struct bsd_db *db)	/* 1=output CLEAR */
+{
+    unsigned int new_ratio;
+
+    if (db->in_count >= db->checkpoint)
+      {
+	/* age the ratio by limiting the size of the counts */
+	if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
+	  {
+	    db->in_count  -= (db->in_count  >> 2);
+	    db->bytes_out -= (db->bytes_out >> 2);
+	  }
+	
+	db->checkpoint = db->in_count + CHECK_GAP;
+	
+	if (db->max_ent >= db->maxmaxcode)
+	  {
+	    /* Reset the dictionary only if the ratio is worse,
+	     * or if it looks as if it has been poisoned
+	     * by incompressible data.
+	     *
+	     * This does not overflow, because
+	     *	db->in_count <= RATIO_MAX.
+	     */
+
+	    new_ratio = db->in_count << RATIO_SCALE_LOG;
+	    if (db->bytes_out != 0)
+	      {
+		new_ratio /= db->bytes_out;
+	      }
+	    
+	    if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
+	      {
+		bsd_clear (db);
+		return 1;
+	      }
+	    db->ratio = new_ratio;
+	  }
+      }
+    return 0;
+}
+
+/*
+ * Return statistics.
+ */
+
+static void bsd_stats (void *state, struct compstat *stats)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+    
+	stats->unc_bytes    = db->uncomp_bytes;
+	stats->unc_packets  = db->uncomp_count;
+	stats->comp_bytes   = db->comp_bytes;
+	stats->comp_packets = db->comp_count;
+	stats->inc_bytes    = db->incomp_bytes;
+	stats->inc_packets  = db->incomp_count;
+	stats->in_count     = db->in_count;
+	stats->bytes_out    = db->bytes_out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void bsd_reset (void *state,unsigned char code, unsigned char id,
+			unsigned char *data, unsigned len,
+			struct isdn_ppp_resetparams *rsparm)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+
+	bsd_clear(db);
+	db->seqno       = 0;
+	db->clear_count = 0;
+}
+
+/*
+ * Release the compression structure
+ */
+static void bsd_free (void *state)
+{
+	struct bsd_db *db = (struct bsd_db *) state;
+
+	if (db) {
+		/*
+		 * Release the dictionary
+		 */
+		if (db->dict) {
+			vfree (db->dict);
+			db->dict = NULL;
+		}
+
+		/*
+		 * Release the string buffer
+		 */
+		if (db->lens) {
+			vfree (db->lens);
+			db->lens = NULL;
+		}
+
+		/*
+		 * Finally release the structure itself.
+		 */
+		kfree (db);
+	}
+}
+
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *bsd_alloc (struct isdn_ppp_comp_data *data)
+{
+	int bits;
+	unsigned int hsize, hshift, maxmaxcode;
+	struct bsd_db *db;
+	int decomp;
+
+	static unsigned int htab[][2] = {
+		{ 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , 
+		{ 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 } 
+	};
+		
+	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+		|| BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+		return NULL;
+
+	bits = BSD_NBITS(data->options[0]);
+
+	if(bits < 9 || bits > 15)
+		return NULL;
+
+	hsize = htab[bits-9][0];
+	hshift = htab[bits-9][1];
+	
+	/*
+	 * Allocate the main control structure for this instance.
+	 */
+	maxmaxcode = MAXCODE(bits);
+	db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),GFP_KERNEL);
+	if (!db)
+		return NULL;
+
+	memset (db, 0, sizeof(struct bsd_db));
+
+	db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
+	decomp = db->xmit ? 0 : 1;
+
+	/*
+	 * Allocate space for the dictionary. This may be more than one page in
+	 * length.
+	 */
+	db->dict = (struct bsd_dict *) vmalloc (hsize * sizeof (struct bsd_dict));
+	if (!db->dict) {
+		bsd_free (db);
+		return NULL;
+	}
+
+	/*
+	 * If this is the compression buffer then there is no length data.
+	 * For decompression, the length information is needed as well.
+	 */
+	if (!decomp)
+		db->lens = NULL;
+	else {
+		db->lens = (unsigned short *) vmalloc ((maxmaxcode + 1) *
+			sizeof (db->lens[0]));
+		if (!db->lens) {
+			bsd_free (db);
+			return (NULL);
+		}
+	}
+
+	/*
+	 * Initialize the data information for the compression code
+	 */
+	db->totlen     = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
+	db->hsize      = hsize;
+	db->hshift     = hshift;
+	db->maxmaxcode = maxmaxcode;
+	db->maxbits    = bits;
+
+	return (void *) db;
+}
+
+/*
+ * Initialize the database.
+ */
+static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
+{
+	struct bsd_db *db = state;
+	int indx;
+	int decomp;
+
+	if(!state || !data) {
+		printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
+		return 0;
+	}
+
+	decomp = db->xmit ? 0 : 1;
+    
+	if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+		|| (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+		|| (BSD_NBITS(data->options[0]) != db->maxbits)
+		|| (decomp && db->lens == NULL)) {
+		printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
+		return 0;
+	}
+
+	if (decomp)
+		for(indx=LAST;indx>=0;indx--)
+			db->lens[indx] = 1;
+
+	indx = db->hsize;
+	while (indx-- != 0) {
+		db->dict[indx].codem1 = BADCODEM1;
+		db->dict[indx].cptr   = 0;
+	}
+
+	db->unit = unit;
+	db->mru  = 0;
+
+	db->debug = 1;
+    
+	bsd_reset(db,0,0,NULL,0,NULL);
+    
+	return 1;
+}
+
+/*
+ * Obtain pointers to the various structures in the compression tables
+ */
+
+#define dict_ptrx(p,idx) &(p->dict[idx])
+#define lens_ptrx(p,idx) &(p->lens[idx])
+
+#ifdef DEBUG
+static unsigned short *lens_ptr(struct bsd_db *db, int idx)
+{
+	if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
+		printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
+		idx = 0;
+	}
+	return lens_ptrx (db, idx);
+}
+
+static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
+{
+	if ((unsigned int) idx >= (unsigned int) db->hsize) {
+		printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
+		idx = 0;
+	}
+	return dict_ptrx (db, idx);
+}
+
+#else
+#define lens_ptr(db,idx) lens_ptrx(db,idx)
+#define dict_ptr(db,idx) dict_ptrx(db,idx)
+#endif
+
+/*
+ * compress a packet
+ */
+static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
+{
+	struct bsd_db *db;
+	int hshift;
+	unsigned int max_ent;
+	unsigned int n_bits;
+	unsigned int bitno;
+	unsigned long accm;
+	int ent;
+	unsigned long fcode;
+	struct bsd_dict *dictp;
+	unsigned char c;
+	int hval,disp,ilen,mxcode;
+	unsigned char *rptr = skb_in->data;
+	int isize = skb_in->len;
+
+#define OUTPUT(ent)			\
+  {					\
+    bitno -= n_bits;			\
+    accm |= ((ent) << bitno);		\
+    do	{				\
+        if(skb_out && skb_tailroom(skb_out) > 0) 	\
+      		*(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
+	accm <<= 8;			\
+	bitno += 8;			\
+    } while (bitno <= 24);		\
+  }
+
+	/*
+	 * If the protocol is not in the range we're interested in,
+	 * just return without compressing the packet.  If it is,
+	 * the protocol becomes the first byte to compress.
+	 */
+	printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
+	
+	ent = proto;
+	if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
+		return 0;
+
+	db      = (struct bsd_db *) state;
+	hshift  = db->hshift;
+	max_ent = db->max_ent;
+	n_bits  = db->n_bits;
+	bitno   = 32;
+	accm    = 0;
+	mxcode  = MAXCODE (n_bits);
+	
+	/* This is the PPP header information */
+	if(skb_out && skb_tailroom(skb_out) >= 2) {
+		char *v = skb_put(skb_out,2);
+		/* we only push our own data on the header,
+		  AC,PC and protos is pushed by caller  */
+		v[0] = db->seqno >> 8;
+		v[1] = db->seqno;
+	}
+
+	ilen   = ++isize; /* This is off by one, but that is what is in draft! */
+
+	while (--ilen > 0) {
+		c     = *rptr++;
+		fcode = BSD_KEY  (ent, c);
+		hval  = BSD_HASH (ent, c, hshift);
+		dictp = dict_ptr (db, hval);
+	
+		/* Validate and then check the entry. */
+		if (dictp->codem1 >= max_ent)
+			goto nomatch;
+
+		if (dictp->fcode == fcode) {
+			ent = dictp->codem1 + 1;
+			continue;	/* found (prefix,suffix) */
+		}
+	
+		/* continue probing until a match or invalid entry */
+		disp = (hval == 0) ? 1 : hval;
+
+		do {
+			hval += disp;
+			if (hval >= db->hsize)
+				hval -= db->hsize;
+			dictp = dict_ptr (db, hval);
+			if (dictp->codem1 >= max_ent)
+				goto nomatch;
+		} while (dictp->fcode != fcode);
+
+		ent = dictp->codem1 + 1;	/* finally found (prefix,suffix) */
+		continue;
+	
+nomatch:
+		OUTPUT(ent);		/* output the prefix */
+	
+		/* code -> hashtable */
+		if (max_ent < db->maxmaxcode) {
+			struct bsd_dict *dictp2;
+			struct bsd_dict *dictp3;
+			int indx;
+
+			/* expand code size if needed */
+			if (max_ent >= mxcode) {
+				db->n_bits = ++n_bits;
+				mxcode = MAXCODE (n_bits);
+			}
+	    
+			/* 
+			 * Invalidate old hash table entry using
+			 * this code, and then take it over.
+			 */
+			dictp2 = dict_ptr (db, max_ent + 1);
+			indx   = dictp2->cptr;
+			dictp3 = dict_ptr (db, indx);
+
+			if (dictp3->codem1 == max_ent)
+				dictp3->codem1 = BADCODEM1;
+
+			dictp2->cptr   = hval;
+			dictp->codem1  = max_ent;
+			dictp->fcode = fcode;
+			db->max_ent    = ++max_ent;
+
+			if (db->lens) {
+				unsigned short *len1 = lens_ptr (db, max_ent);
+				unsigned short *len2 = lens_ptr (db, ent);
+				*len1 = *len2 + 1;
+			}
+		}
+		ent = c;
+	}
+    
+	OUTPUT(ent);		/* output the last code */
+
+	if(skb_out)
+		db->bytes_out    += skb_out->len; /* Do not count bytes from here */
+	db->uncomp_bytes += isize;
+	db->in_count     += isize;
+	++db->uncomp_count;
+	++db->seqno;
+
+	if (bitno < 32)
+		++db->bytes_out; /* must be set before calling bsd_check */
+
+	/*
+	 * Generate the clear command if needed
+	 */
+
+	if (bsd_check(db))
+		OUTPUT (CLEAR);
+
+	/*
+	 * Pad dribble bits of last code with ones.
+	 * Do not emit a completely useless byte of ones.
+	 */
+	if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0) 
+		*(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
+    
+	/*
+	 * Increase code size if we would have without the packet
+	 * boundary because the decompressor will do so.
+	 */
+	if (max_ent >= mxcode && max_ent < db->maxmaxcode)
+		db->n_bits++;
+
+	/* If output length is too large then this is an incompressible frame. */
+	if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
+		++db->incomp_count;
+		db->incomp_bytes += isize;
+		return 0;
+	}
+
+	/* Count the number of compressed frames */
+	++db->comp_count;
+	db->comp_bytes += skb_out->len;
+	return skb_out->len;
+
+#undef OUTPUT
+}
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
+{
+	bsd_compress (state, skb_in, NULL, proto);
+}
+
+/*
+ * Decompress "BSD Compress".
+ */
+static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
+			   struct isdn_ppp_resetparams *rsparm)
+{
+	struct bsd_db *db;
+	unsigned int max_ent;
+	unsigned long accm;
+	unsigned int bitno;		/* 1st valid bit in accm */
+	unsigned int n_bits;
+	unsigned int tgtbitno;	/* bitno when we have a code */
+	struct bsd_dict *dictp;
+	int seq;
+	unsigned int incode;
+	unsigned int oldcode;
+	unsigned int finchar;
+	unsigned char *p,*ibuf;
+	int ilen;
+	int codelen;
+	int extra;
+
+	db       = (struct bsd_db *) state;
+	max_ent  = db->max_ent;
+	accm     = 0;
+	bitno    = 32;		/* 1st valid bit in accm */
+	n_bits   = db->n_bits;
+	tgtbitno = 32 - n_bits;	/* bitno when we have a code */
+
+	printk(KERN_DEBUG "bsd_decompress called\n");
+
+	if(!skb_in || !skb_out) {
+		printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
+		return DECOMP_ERROR;
+	}
+    
+	/*
+	 * Get the sequence number.
+	 */
+	if( (p = skb_pull(skb_in,2)) == NULL) {
+		return DECOMP_ERROR;
+	}
+	p-=2;
+	seq   = (p[0] << 8) + p[1];
+	ilen  = skb_in->len;
+	ibuf = skb_in->data;
+
+	/*
+	 * Check the sequence number and give up if it differs from
+	 * the value we're expecting.
+	 */
+	if (seq != db->seqno) {
+		if (db->debug) {
+			printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
+				db->unit, seq, db->seqno - 1);
+		}
+		return DECOMP_ERROR;
+	}
+
+	++db->seqno;
+	db->bytes_out += ilen;
+
+	if(skb_tailroom(skb_out) > 0)
+		*(skb_put(skb_out,1)) = 0;
+	else
+		return DECOMP_ERR_NOMEM;
+    
+	oldcode = CLEAR;
+
+	/*
+	 * Keep the checkpoint correctly so that incompressible packets
+	 * clear the dictionary at the proper times.
+	 */
+
+	for (;;) {
+		if (ilen-- <= 0) {
+			db->in_count += (skb_out->len - 1); /* don't count the header */
+			break;
+		}
+
+		/*
+		 * Accumulate bytes until we have a complete code.
+		 * Then get the next code, relying on the 32-bit,
+		 * unsigned accm to mask the result.
+		 */
+
+		bitno -= 8;
+		accm  |= *ibuf++ << bitno;
+		if (tgtbitno < bitno)
+			continue;
+
+		incode = accm >> tgtbitno;
+		accm <<= n_bits;
+		bitno += n_bits;
+
+		/*
+		 * The dictionary must only be cleared at the end of a packet.
+		 */
+	
+		if (incode == CLEAR) {
+			if (ilen > 0) {
+				if (db->debug)
+					printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
+				return DECOMP_FATALERROR;	/* probably a bug */
+			}
+			bsd_clear(db);
+			break;
+		}
+
+		if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
+			|| (incode > max_ent && oldcode == CLEAR)) {
+			if (db->debug) {
+				printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+					db->unit, incode, oldcode);
+				printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
+					max_ent, skb_out->len, db->seqno);
+			}
+			return DECOMP_FATALERROR;	/* probably a bug */
+		}
+	
+		/* Special case for KwKwK string. */
+		if (incode > max_ent) {
+			finchar = oldcode;
+			extra   = 1;
+		} else {
+			finchar = incode;
+			extra   = 0;
+		}
+
+		codelen = *(lens_ptr (db, finchar));
+		if( skb_tailroom(skb_out) < codelen + extra) {
+			if (db->debug) {
+				printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
+#ifdef DEBUG
+				printk(KERN_DEBUG "  len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
+					ilen, finchar, codelen, skb_out->len);
+#endif
+			}
+			return DECOMP_FATALERROR;
+		}
+
+		/*
+		 * Decode this code and install it in the decompressed buffer.
+		 */
+
+		p     = skb_put(skb_out,codelen);
+		p += codelen;
+		while (finchar > LAST) {
+			struct bsd_dict *dictp2 = dict_ptr (db, finchar);
+	    
+			dictp = dict_ptr (db, dictp2->cptr);
+
+#ifdef DEBUG
+			if (--codelen <= 0 || dictp->codem1 != finchar-1) {
+				if (codelen <= 0) {
+					printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
+					printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
+				} else {
+					if (dictp->codem1 != finchar-1) {
+						printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
+						printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
+					}
+				}
+				return DECOMP_FATALERROR;
+			}
+#endif
+
+			{
+				u32 fcode = dictp->fcode;
+				*--p    = (fcode >> 16) & 0xff;
+				finchar = fcode & 0xffff;
+			}
+		}
+		*--p = finchar;
+	
+#ifdef DEBUG
+		if (--codelen != 0)
+			printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
+#endif
+	
+		if (extra)		/* the KwKwK case again */
+			*(skb_put(skb_out,1)) = finchar;
+	
+		/*
+		 * If not first code in a packet, and
+		 * if not out of code space, then allocate a new code.
+		 *
+		 * Keep the hash table correct so it can be used
+		 * with uncompressed packets.
+		 */
+		if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+			struct bsd_dict *dictp2, *dictp3;
+			u16  *lens1,  *lens2;
+			unsigned long fcode;
+			int hval, disp, indx;
+	    
+			fcode = BSD_KEY(oldcode,finchar);
+			hval  = BSD_HASH(oldcode,finchar,db->hshift);
+			dictp = dict_ptr (db, hval);
+	    
+			/* look for a free hash table entry */
+			if (dictp->codem1 < max_ent) {
+				disp = (hval == 0) ? 1 : hval;
+				do {
+					hval += disp;
+					if (hval >= db->hsize)
+						hval -= db->hsize;
+					dictp = dict_ptr (db, hval);
+				} while (dictp->codem1 < max_ent);
+			}
+	    
+			/*
+			 * Invalidate previous hash table entry
+			 * assigned this code, and then take it over
+			 */
+
+			dictp2 = dict_ptr (db, max_ent + 1);
+			indx   = dictp2->cptr;
+			dictp3 = dict_ptr (db, indx);
+
+			if (dictp3->codem1 == max_ent)
+				dictp3->codem1 = BADCODEM1;
+
+			dictp2->cptr   = hval;
+			dictp->codem1  = max_ent;
+			dictp->fcode = fcode;
+			db->max_ent    = ++max_ent;
+
+			/* Update the length of this string. */
+			lens1  = lens_ptr (db, max_ent);
+			lens2  = lens_ptr (db, oldcode);
+			*lens1 = *lens2 + 1;
+	    
+			/* Expand code size if needed. */
+			if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+				db->n_bits = ++n_bits;
+				tgtbitno   = 32-n_bits;
+			}
+		}
+		oldcode = incode;
+	}
+
+	++db->comp_count;
+	++db->uncomp_count;
+	db->comp_bytes   += skb_in->len - BSD_OVHD;
+	db->uncomp_bytes += skb_out->len;
+
+	if (bsd_check(db)) {
+		if (db->debug)
+			printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
+				db->unit, db->seqno - 1);
+	}
+	return skb_out->len;
+}
+
+/*************************************************************
+ * Table of addresses for the BSD compression module
+ *************************************************************/
+
+static struct isdn_ppp_compressor ippp_bsd_compress = {
+	.owner          = THIS_MODULE,
+	.num            = CI_BSD_COMPRESS,
+	.alloc          = bsd_alloc,
+	.free           = bsd_free,
+	.init           = bsd_init,
+	.reset          = bsd_reset,
+	.compress       = bsd_compress,
+	.decompress     = bsd_decompress,
+	.incomp         = bsd_incomp,
+	.stat           = bsd_stats,
+};
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+static int __init isdn_bsdcomp_init(void)
+{
+	int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
+	if (answer == 0)
+		printk (KERN_INFO "PPP BSD Compression module registered\n");
+	return answer;
+}
+
+static void __exit isdn_bsdcomp_exit(void)
+{
+	isdn_ppp_unregister_compressor (&ippp_bsd_compress);
+}
+
+module_init(isdn_bsdcomp_init);
+module_exit(isdn_bsdcomp_exit);
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
new file mode 100644
index 0000000..c406df6
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -0,0 +1,2253 @@
+/* $Id: isdn_common.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $
+ *
+ * Linux ISDN subsystem, common used functions (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/isdn.h>
+#include <linux/smp_lock.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#endif
+#ifdef CONFIG_ISDN_DIVERSION_MODULE
+#define CONFIG_ISDN_DIVERSION
+#endif
+#ifdef CONFIG_ISDN_DIVERSION
+#include <linux/isdn_divertif.h>
+#endif /* CONFIG_ISDN_DIVERSION */
+#include "isdn_v110.h"
+
+/* Debugflags */
+#undef ISDN_DEBUG_STATCALLB
+
+MODULE_DESCRIPTION("ISDN4Linux: link layer");
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_LICENSE("GPL");
+
+isdn_dev *dev;
+
+static char *isdn_revision = "$Revision: 1.1.2.3 $";
+
+extern char *isdn_net_revision;
+extern char *isdn_tty_revision;
+#ifdef CONFIG_ISDN_PPP
+extern char *isdn_ppp_revision;
+#else
+static char *isdn_ppp_revision = ": none $";
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+extern char *isdn_audio_revision;
+#else
+static char *isdn_audio_revision = ": none $";
+#endif
+extern char *isdn_v110_revision;
+
+#ifdef CONFIG_ISDN_DIVERSION
+static isdn_divert_if *divert_if; /* = NULL */
+#endif /* CONFIG_ISDN_DIVERSION */
+
+
+static int isdn_writebuf_stub(int, int, const u_char __user *, int);
+static void set_global_features(void);
+static int isdn_wildmat(char *s, char *p);
+
+
+static inline void
+isdn_lock_driver(isdn_driver_t *drv)
+{
+	try_module_get(drv->interface->owner);
+	drv->locks++;
+}
+
+void
+isdn_lock_drivers(void)
+{
+	int i;
+
+	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+		if (!dev->drv[i])
+			continue;
+		isdn_lock_driver(dev->drv[i]);
+	}
+}
+
+static inline void
+isdn_unlock_driver(isdn_driver_t *drv)
+{
+	if (drv->locks > 0) {
+		drv->locks--;
+		module_put(drv->interface->owner);
+	}
+}
+
+void
+isdn_unlock_drivers(void)
+{
+	int i;
+
+	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+		if (!dev->drv[i])
+			continue;
+		isdn_unlock_driver(dev->drv[i]);
+	}
+}
+
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+void
+isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+{
+	int dumpc;
+
+	printk(KERN_DEBUG "%s(%d) ", s, len);
+	for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++)
+		printk(" %02x", *p++);
+	printk("\n");
+}
+#endif
+
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+static int
+isdn_star(char *s, char *p)
+{
+	while (isdn_wildmat(s, p)) {
+		if (*++s == '\0')
+			return (2);
+	}
+	return (0);
+}
+
+/*
+ * Shell-type Pattern-matching for incoming caller-Ids
+ * This function gets a string in s and checks, if it matches the pattern
+ * given in p.
+ *
+ * Return:
+ *   0 = match.
+ *   1 = no match.
+ *   2 = no match. Would eventually match, if s would be longer.
+ *
+ * Possible Patterns:
+ *
+ * '?'     matches one character
+ * '*'     matches zero or more characters
+ * [xyz]   matches the set of characters in brackets.
+ * [^xyz]  matches any single character not in the set of characters
+ */
+
+static int
+isdn_wildmat(char *s, char *p)
+{
+	register int last;
+	register int matched;
+	register int reverse;
+	register int nostar = 1;
+
+	if (!(*s) && !(*p))
+		return(1);
+	for (; *p; s++, p++)
+		switch (*p) {
+			case '\\':
+				/*
+				 * Literal match with following character,
+				 * fall through.
+				 */
+				p++;
+			default:
+				if (*s != *p)
+					return (*s == '\0')?2:1;
+				continue;
+			case '?':
+				/* Match anything. */
+				if (*s == '\0')
+					return (2);
+				continue;
+			case '*':
+				nostar = 0;	
+				/* Trailing star matches everything. */
+				return (*++p ? isdn_star(s, p) : 0);
+			case '[':
+				/* [^....] means inverse character class. */
+				if ((reverse = (p[1] == '^')))
+					p++;
+				for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+					/* This next line requires a good C compiler. */
+					if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+						matched = 1;
+				if (matched == reverse)
+					return (1);
+				continue;
+		}
+	return (*s == '\0')?0:nostar;
+}
+
+int isdn_msncmp( const char * msn1, const char * msn2 )
+{
+	char TmpMsn1[ ISDN_MSNLEN ];
+	char TmpMsn2[ ISDN_MSNLEN ];
+	char *p;
+
+	for ( p = TmpMsn1; *msn1 && *msn1 != ':'; )  // Strip off a SPID
+		*p++ = *msn1++;
+	*p = '\0';
+
+	for ( p = TmpMsn2; *msn2 && *msn2 != ':'; )  // Strip off a SPID
+		*p++ = *msn2++;
+	*p = '\0';
+
+	return isdn_wildmat( TmpMsn1, TmpMsn2 );
+}
+
+int
+isdn_dc2minor(int di, int ch)
+{
+	int i;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (dev->chanmap[i] == ch && dev->drvmap[i] == di)
+			return i;
+	return -1;
+}
+
+static int isdn_timer_cnt1 = 0;
+static int isdn_timer_cnt2 = 0;
+static int isdn_timer_cnt3 = 0;
+
+static void
+isdn_timer_funct(ulong dummy)
+{
+	int tf = dev->tflags;
+	if (tf & ISDN_TIMER_FAST) {
+		if (tf & ISDN_TIMER_MODEMREAD)
+			isdn_tty_readmodem();
+		if (tf & ISDN_TIMER_MODEMPLUS)
+			isdn_tty_modem_escape();
+		if (tf & ISDN_TIMER_MODEMXMIT)
+			isdn_tty_modem_xmit();
+	}
+	if (tf & ISDN_TIMER_SLOW) {
+		if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) {
+			isdn_timer_cnt1 = 0;
+			if (tf & ISDN_TIMER_NETDIAL)
+				isdn_net_dial();
+		}
+		if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
+			isdn_timer_cnt2 = 0;
+			if (tf & ISDN_TIMER_NETHANGUP)
+				isdn_net_autohup();
+			if (++isdn_timer_cnt3 >= ISDN_TIMER_RINGING) {
+				isdn_timer_cnt3 = 0;
+				if (tf & ISDN_TIMER_MODEMRING)
+					isdn_tty_modem_ring();
+			}
+			if (tf & ISDN_TIMER_CARRIER)
+				isdn_tty_carrier_timeout();
+		}
+	}
+	if (tf) 
+		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
+}
+
+void
+isdn_timer_ctrl(int tf, int onoff)
+{
+	unsigned long flags;
+	int old_tflags;
+
+	spin_lock_irqsave(&dev->timerlock, flags);
+	if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) {
+		/* If the slow-timer wasn't activated until now */
+		isdn_timer_cnt1 = 0;
+		isdn_timer_cnt2 = 0;
+	}
+	old_tflags = dev->tflags;
+	if (onoff)
+		dev->tflags |= tf;
+	else
+		dev->tflags &= ~tf;
+	if (dev->tflags && !old_tflags)
+		mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
+	spin_unlock_irqrestore(&dev->timerlock, flags);
+}
+
+/*
+ * Receive a packet from B-Channel. (Called from low-level-module)
+ */
+static void
+isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
+{
+	int i;
+
+	if ((i = isdn_dc2minor(di, channel)) == -1) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	/* Update statistics */
+	dev->ibytes[i] += skb->len;
+	
+	/* First, try to deliver data to network-device */
+	if (isdn_net_rcv_skb(i, skb))
+		return;
+
+	/* V.110 handling
+	 * makes sense for async streams only, so it is
+	 * called after possible net-device delivery.
+	 */
+	if (dev->v110[i]) {
+		atomic_inc(&dev->v110use[i]);
+		skb = isdn_v110_decode(dev->v110[i], skb);
+		atomic_dec(&dev->v110use[i]);
+		if (!skb)
+			return;
+	}
+
+	/* No network-device found, deliver to tty or raw-channel */
+	if (skb->len) {
+		if (isdn_tty_rcv_skb(i, di, channel, skb))
+			return;
+		wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+	} else
+		dev_kfree_skb(skb);
+}
+
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ * If layer 2 protocol is V.110 and this is not supported by current
+ * lowlevel-driver, use driver's transparent mode and handle V.110 in
+ * linklevel instead.
+ */
+int
+isdn_command(isdn_ctrl *cmd)
+{
+	if (cmd->driver == -1) {
+		printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command);
+		return(1);
+	}
+	if (cmd->command == ISDN_CMD_SETL2) {
+		int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
+		unsigned long l2prot = (cmd->arg >> 8) & 255;
+		unsigned long features = (dev->drv[cmd->driver]->interface->features
+						>> ISDN_FEATURE_L2_SHIFT) &
+						ISDN_FEATURE_L2_MASK;
+		unsigned long l2_feature = (1 << l2prot);
+
+		switch (l2prot) {
+			case ISDN_PROTO_L2_V11096:
+			case ISDN_PROTO_L2_V11019:
+			case ISDN_PROTO_L2_V11038:
+			/* If V.110 requested, but not supported by
+			 * HL-driver, set emulator-flag and change
+			 * Layer-2 to transparent
+			 */
+				if (!(features & l2_feature)) {
+					dev->v110emu[idx] = l2prot;
+					cmd->arg = (cmd->arg & 255) |
+						(ISDN_PROTO_L2_TRANS << 8);
+				} else
+					dev->v110emu[idx] = 0;
+		}
+	}
+	return dev->drv[cmd->driver]->interface->command(cmd);
+}
+
+void
+isdn_all_eaz(int di, int ch)
+{
+	isdn_ctrl cmd;
+
+	if (di < 0)
+		return;
+	cmd.driver = di;
+	cmd.arg = ch;
+	cmd.command = ISDN_CMD_SETEAZ;
+	cmd.parm.num[0] = '\0';
+	isdn_command(&cmd);
+}
+
+/*
+ * Begin of a CAPI like LL<->HL interface, currently used only for 
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include <linux/isdn/capicmd.h>
+
+int
+isdn_capi_rec_hl_msg(capi_msg *cm) {
+	
+	int di;
+	int ch;
+	
+	di = (cm->adr.Controller & 0x7f) -1;
+	ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f);
+	switch(cm->Command) {
+		case CAPI_FACILITY:
+			/* in the moment only handled in tty */
+			return(isdn_tty_capi_facility(cm));
+		default:
+			return(-1);
+	}
+}
+
+static int
+isdn_status_callback(isdn_ctrl * c)
+{
+	int di;
+	u_long flags;
+	int i;
+	int r;
+	int retval = 0;
+	isdn_ctrl cmd;
+	isdn_net_dev *p;
+
+	di = c->driver;
+	i = isdn_dc2minor(di, c->arg);
+	switch (c->command) {
+		case ISDN_STAT_BSENT:
+			if (i < 0)
+				return -1;
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c))
+				return 0;
+			if (isdn_v110_stat_callback(i, c))
+				return 0;
+			if (isdn_tty_stat_callback(i, c))
+				return 0;
+			wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+			break;
+		case ISDN_STAT_STAVAIL:
+			dev->drv[di]->stavail += c->arg;
+			wake_up_interruptible(&dev->drv[di]->st_waitq);
+			break;
+		case ISDN_STAT_RUN:
+			dev->drv[di]->flags |= DRV_FLAG_RUNNING;
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di)
+					isdn_all_eaz(di, dev->chanmap[i]);
+			set_global_features();
+			break;
+		case ISDN_STAT_STOP:
+			dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
+			break;
+		case ISDN_STAT_ICALL:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_HANGUP;
+				isdn_command(&cmd);
+				return 0;
+			}
+			/* Try to find a network-interface which will accept incoming call */
+			r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup));
+			switch (r) {
+				case 0:
+					/* No network-device replies.
+					 * Try ttyI's.
+					 * These return 0 on no match, 1 on match and
+					 * 3 on eventually match, if CID is longer.
+					 */
+                                        if (c->command == ISDN_STAT_ICALL)
+					  if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval);
+#ifdef CONFIG_ISDN_DIVERSION 
+                                         if (divert_if)
+                 	                  if ((retval = divert_if->stat_callback(c))) 
+					    return(retval); /* processed */
+#endif /* CONFIG_ISDN_DIVERSION */                       
+					if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) {
+						/* No tty responding */
+						cmd.driver = di;
+						cmd.arg = c->arg;
+						cmd.command = ISDN_CMD_HANGUP;
+						isdn_command(&cmd);
+						retval = 2;
+					}
+					break;
+				case 1:
+					/* Schedule connection-setup */
+					isdn_net_dial();
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_ACCEPTD;
+					for ( p = dev->netdev; p; p = p->next )
+						if ( p->local->isdn_channel == cmd.arg )
+						{
+							strcpy( cmd.parm.setup.eazmsn, p->local->msn );
+							isdn_command(&cmd);
+							retval = 1;
+							break;
+						}
+					break;
+
+				case 2:	/* For calling back, first reject incoming call ... */
+				case 3:	/* Interface found, but down, reject call actively  */
+					retval = 2;
+					printk(KERN_INFO "isdn: Rejecting Call\n");
+					cmd.driver = di;
+					cmd.arg = c->arg;
+					cmd.command = ISDN_CMD_HANGUP;
+					isdn_command(&cmd);
+					if (r == 3)
+						break;
+					/* Fall through */
+				case 4:
+					/* ... then start callback. */
+					isdn_net_dial();
+					break;
+				case 5:
+					/* Number would eventually match, if longer */
+					retval = 3;
+					break;
+			}
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "ICALL: ret=%d\n", retval);
+#endif
+			return retval;
+			break;
+		case ISDN_STAT_CINF:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (strcmp(c->parm.num, "0"))
+				isdn_net_stat_callback(i, c);
+			isdn_tty_stat_callback(i, c);
+			break;
+		case ISDN_STAT_CAUSE:
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num);
+#endif
+			printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n",
+			       dev->drvid[di], c->arg, c->parm.num);
+			isdn_tty_stat_callback(i, c);
+#ifdef CONFIG_ISDN_DIVERSION
+                        if (divert_if)
+                         divert_if->stat_callback(c); 
+#endif /* CONFIG_ISDN_DIVERSION */
+			break;
+		case ISDN_STAT_DISPLAY:
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display);
+#endif
+			isdn_tty_stat_callback(i, c);
+#ifdef CONFIG_ISDN_DIVERSION
+                        if (divert_if)
+                         divert_if->stat_callback(c); 
+#endif /* CONFIG_ISDN_DIVERSION */
+			break;
+		case ISDN_STAT_DCONN:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			/* Find any net-device, waiting for D-channel setup */
+			if (isdn_net_stat_callback(i, c))
+				break;
+			isdn_v110_stat_callback(i, c);
+			/* Find any ttyI, waiting for D-channel setup */
+			if (isdn_tty_stat_callback(i, c)) {
+				cmd.driver = di;
+				cmd.arg = c->arg;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				isdn_command(&cmd);
+				break;
+			}
+			break;
+		case ISDN_STAT_DHUP:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->online &= ~(1 << (c->arg));
+			isdn_info_update();
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c))
+				break;
+			isdn_v110_stat_callback(i, c);
+			if (isdn_tty_stat_callback(i, c))
+				break;
+#ifdef CONFIG_ISDN_DIVERSION
+                        if (divert_if)
+                         divert_if->stat_callback(c); 
+#endif /* CONFIG_ISDN_DIVERSION */
+			break;
+			break;
+		case ISDN_STAT_BCONN:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+#endif
+			/* Signal B-channel-connect to network-devices */
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->online |= (1 << (c->arg));
+			isdn_info_update();
+			if (isdn_net_stat_callback(i, c))
+				break;
+			isdn_v110_stat_callback(i, c);
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_BHUP:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			dev->drv[di]->online &= ~(1 << (c->arg));
+			isdn_info_update();
+#ifdef CONFIG_ISDN_X25
+			/* Signal hangup to network-devices */
+			if (isdn_net_stat_callback(i, c))
+				break;
+#endif
+			isdn_v110_stat_callback(i, c);
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_NODCH:
+			if (i < 0)
+				return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+			printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+#endif
+			if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+				return 0;
+			if (isdn_net_stat_callback(i, c))
+				break;
+			if (isdn_tty_stat_callback(i, c))
+				break;
+			break;
+		case ISDN_STAT_ADDCH:
+			spin_lock_irqsave(&dev->lock, flags);
+			if (isdn_add_channels(dev->drv[di], di, c->arg, 1)) {
+				spin_unlock_irqrestore(&dev->lock, flags);
+				return -1;
+			}
+			spin_unlock_irqrestore(&dev->lock, flags);
+			isdn_info_update();
+			break;
+		case ISDN_STAT_DISCH:
+			spin_lock_irqsave(&dev->lock, flags);
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if ((dev->drvmap[i] == di) &&
+				    (dev->chanmap[i] == c->arg)) {
+				    if (c->parm.num[0])
+				      dev->usage[i] &= ~ISDN_USAGE_DISABLED;
+				    else
+				      if (USG_NONE(dev->usage[i])) {
+					dev->usage[i] |= ISDN_USAGE_DISABLED;
+				      }
+				      else 
+					retval = -1;
+				    break;
+				}
+			spin_unlock_irqrestore(&dev->lock, flags);
+			isdn_info_update();
+			break;
+		case ISDN_STAT_UNLOAD:
+			while (dev->drv[di]->locks > 0) {
+				isdn_unlock_driver(dev->drv[di]);
+			}
+			spin_lock_irqsave(&dev->lock, flags);
+			isdn_tty_stat_callback(i, c);
+			for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+				if (dev->drvmap[i] == di) {
+					dev->drvmap[i] = -1;
+					dev->chanmap[i] = -1;
+					dev->usage[i] &= ~ISDN_USAGE_DISABLED;
+				}
+			dev->drivers--;
+			dev->channels -= dev->drv[di]->channels;
+			kfree(dev->drv[di]->rcverr);
+			kfree(dev->drv[di]->rcvcount);
+			for (i = 0; i < dev->drv[di]->channels; i++)
+				skb_queue_purge(&dev->drv[di]->rpqueue[i]);
+			kfree(dev->drv[di]->rpqueue);
+			kfree(dev->drv[di]->rcv_waitq);
+			kfree(dev->drv[di]);
+			dev->drv[di] = NULL;
+			dev->drvid[di][0] = '\0';
+			isdn_info_update();
+			set_global_features();
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return 0;
+		case ISDN_STAT_L1ERR:
+			break;
+		case CAPI_PUT_MESSAGE:
+			return(isdn_capi_rec_hl_msg(&c->parm.cmsg));
+#ifdef CONFIG_ISDN_TTY_FAX
+		case ISDN_STAT_FAXIND:
+			isdn_tty_stat_callback(i, c);
+			break;
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+		case ISDN_STAT_AUDIO:
+			isdn_tty_stat_callback(i, c);
+			break;
+#endif
+#ifdef CONFIG_ISDN_DIVERSION
+	        case ISDN_STAT_PROT:
+	        case ISDN_STAT_REDIR:
+                        if (divert_if)
+                          return(divert_if->stat_callback(c));
+#endif /* CONFIG_ISDN_DIVERSION */
+		default:
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * Get integer from char-pointer, set pointer to end of number
+ */
+int
+isdn_getnum(char **p)
+{
+	int v = -1;
+
+	while (*p[0] >= '0' && *p[0] <= '9')
+		v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
+	return v;
+}
+
+#define DLE 0x10
+
+/*
+ * isdn_readbchan() tries to get data from the read-queue.
+ * It MUST be called with interrupts off.
+ *
+ * Be aware that this is not an atomic operation when sleep != 0, even though 
+ * interrupts are turned off! Well, like that we are currently only called
+ * on behalf of a read system call on raw device files (which are documented
+ * to be dangerous and for for debugging purpose only). The inode semaphore
+ * takes care that this is not called for the same minor device number while
+ * we are sleeping, but access is not serialized against simultaneous read()
+ * from the corresponding ttyI device. Can other ugly events, like changes
+ * of the mapping (di,ch)<->minor, happen during the sleep? --he 
+ */
+int
+isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep)
+{
+	int count;
+	int count_pull;
+	int count_put;
+	int dflag;
+	struct sk_buff *skb;
+	u_char *cp;
+
+	if (!dev->drv[di])
+		return 0;
+	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) {
+		if (sleep)
+			interruptible_sleep_on(sleep);
+		else
+			return 0;
+	}
+	if (len > dev->drv[di]->rcvcount[channel])
+		len = dev->drv[di]->rcvcount[channel];
+	cp = buf;
+	count = 0;
+	while (len) {
+		if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+			break;
+#ifdef CONFIG_ISDN_AUDIO
+		if (ISDN_AUDIO_SKB_LOCK(skb))
+			break;
+		ISDN_AUDIO_SKB_LOCK(skb) = 1;
+		if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) {
+			char *p = skb->data;
+			unsigned long DLEmask = (1 << channel);
+
+			dflag = 0;
+			count_pull = count_put = 0;
+			while ((count_pull < skb->len) && (len > 0)) {
+				len--;
+				if (dev->drv[di]->DLEflag & DLEmask) {
+					*cp++ = DLE;
+					dev->drv[di]->DLEflag &= ~DLEmask;
+				} else {
+					*cp++ = *p;
+					if (*p == DLE) {
+						dev->drv[di]->DLEflag |= DLEmask;
+						(ISDN_AUDIO_SKB_DLECOUNT(skb))--;
+					}
+					p++;
+					count_pull++;
+				}
+				count_put++;
+			}
+			if (count_pull >= skb->len)
+				dflag = 1;
+		} else {
+#endif
+			/* No DLE's in buff, so simply copy it */
+			dflag = 1;
+			if ((count_pull = skb->len) > len) {
+				count_pull = len;
+				dflag = 0;
+			}
+			count_put = count_pull;
+			memcpy(cp, skb->data, count_put);
+			cp += count_put;
+			len -= count_put;
+#ifdef CONFIG_ISDN_AUDIO
+		}
+#endif
+		count += count_put;
+		if (fp) {
+			memset(fp, 0, count_put);
+			fp += count_put;
+		}
+		if (dflag) {
+			/* We got all the data in this buff.
+			 * Now we can dequeue it.
+			 */
+			if (fp)
+				*(fp - 1) = 0xff;
+#ifdef CONFIG_ISDN_AUDIO
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+			skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+			dev_kfree_skb(skb);
+		} else {
+			/* Not yet emptied this buff, so it
+			 * must stay in the queue, for further calls
+			 * but we pull off the data we got until now.
+			 */
+			skb_pull(skb, count_pull);
+#ifdef CONFIG_ISDN_AUDIO
+			ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+		}
+		dev->drv[di]->rcvcount[channel] -= count_put;
+	}
+	return count;
+}
+
+static __inline int
+isdn_minor2drv(int minor)
+{
+	return (dev->drvmap[minor]);
+}
+
+static __inline int
+isdn_minor2chan(int minor)
+{
+	return (dev->chanmap[minor]);
+}
+
+static char *
+isdn_statstr(void)
+{
+	static char istatbuf[2048];
+	char *p;
+	int i;
+
+	sprintf(istatbuf, "idmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nchmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->chanmap[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\ndrmap:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->drvmap[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nusage:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%d ", dev->usage[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\nflags:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+		if (dev->drv[i]) {
+			sprintf(p, "%ld ", dev->drv[i]->online);
+			p = istatbuf + strlen(istatbuf);
+		} else {
+			sprintf(p, "? ");
+			p = istatbuf + strlen(istatbuf);
+		}
+	}
+	sprintf(p, "\nphone:\t");
+	p = istatbuf + strlen(istatbuf);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		sprintf(p, "%s ", dev->num[i]);
+		p = istatbuf + strlen(istatbuf);
+	}
+	sprintf(p, "\n");
+	return istatbuf;
+}
+
+/* Module interface-code */
+
+void
+isdn_info_update(void)
+{
+	infostruct *p = dev->infochain;
+
+	while (p) {
+		*(p->private) = 1;
+		p = (infostruct *) p->next;
+	}
+	wake_up_interruptible(&(dev->info_waitq));
+}
+
+static ssize_t
+isdn_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+	uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	int len = 0;
+	int drvidx;
+	int chidx;
+	int retval;
+	char *p;
+
+	lock_kernel();
+	if (minor == ISDN_MINOR_STATUS) {
+		if (!file->private_data) {
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				goto out;
+			}
+			interruptible_sleep_on(&(dev->info_waitq));
+		}
+		p = isdn_statstr();
+		file->private_data = NULL;
+		if ((len = strlen(p)) <= count) {
+			if (copy_to_user(buf, p, len)) {
+				retval = -EFAULT;
+				goto out;
+			}
+			*off += len;
+			retval = len;
+			goto out;
+		}
+		retval = 0;
+		goto out;
+	}
+	if (!dev->drivers) {
+		retval = -ENODEV;
+		goto out;
+	}
+	if (minor <= ISDN_MINOR_BMAX) {
+		printk(KERN_WARNING "isdn_read minor %d obsolete!\n", minor);
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) {
+			retval = -ENODEV;
+			goto out;
+		}
+		chidx = isdn_minor2chan(minor);
+		if (!(p = kmalloc(count, GFP_KERNEL))) {
+			retval = -ENOMEM;
+			goto out;
+		}
+		len = isdn_readbchan(drvidx, chidx, p, NULL, count,
+				     &dev->drv[drvidx]->rcv_waitq[chidx]);
+		*off += len;
+		if (copy_to_user(buf,p,len)) 
+			len = -EFAULT;
+		kfree(p);
+		retval = len;
+		goto out;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
+		if (!dev->drv[drvidx]->stavail) {
+			if (file->f_flags & O_NONBLOCK) {
+				retval = -EAGAIN;
+				goto out;
+			}
+			interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
+		}
+		if (dev->drv[drvidx]->interface->readstat) {
+			if (count > dev->drv[drvidx]->stavail)
+				count = dev->drv[drvidx]->stavail;
+			len = dev->drv[drvidx]->interface->
+				readstat(buf, count, drvidx,
+					 isdn_minor2chan(minor));
+		} else {
+			len = 0;
+		}
+		if (len)
+			dev->drv[drvidx]->stavail -= len;
+		else
+			dev->drv[drvidx]->stavail = 0;
+		*off += len;
+		retval = len;
+		goto out;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		retval = isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count);
+		goto out;
+	}
+#endif
+	retval = -ENODEV;
+ out:
+	unlock_kernel();
+	return retval;
+}
+
+static ssize_t
+isdn_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+	uint minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	int drvidx;
+	int chidx;
+	int retval;
+
+	if (minor == ISDN_MINOR_STATUS)
+		return -EPERM;
+	if (!dev->drivers)
+		return -ENODEV;
+
+	lock_kernel();
+	if (minor <= ISDN_MINOR_BMAX) {
+		printk(KERN_WARNING "isdn_write minor %d obsolete!\n", minor);
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) {
+			retval = -ENODEV;
+			goto out;
+		}
+		chidx = isdn_minor2chan(minor);
+		while (isdn_writebuf_stub(drvidx, chidx, buf, count) != count)
+			interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
+		retval = count;
+		goto out;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+		if (drvidx < 0) {
+			retval = -ENODEV;
+			goto out;
+		}
+		/*
+		 * We want to use the isdnctrl device to load the firmware
+		 *
+		 if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
+		 return -ENODEV;
+		 */
+		if (dev->drv[drvidx]->interface->writecmd)
+			retval = dev->drv[drvidx]->interface->
+				writecmd(buf, count, drvidx, isdn_minor2chan(minor));
+		else
+			retval = count;
+		goto out;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		retval = isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count);
+		goto out;
+	}
+#endif
+	retval = -ENODEV;
+ out:
+	unlock_kernel();
+	return retval;
+}
+
+static unsigned int
+isdn_poll(struct file *file, poll_table * wait)
+{
+	unsigned int mask = 0;
+	unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
+	int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+
+	lock_kernel();
+	if (minor == ISDN_MINOR_STATUS) {
+		poll_wait(file, &(dev->info_waitq), wait);
+		/* mask = POLLOUT | POLLWRNORM; */
+		if (file->private_data) {
+			mask |= POLLIN | POLLRDNORM;
+		}
+		goto out;
+	}
+	if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
+		if (drvidx < 0) {
+			/* driver deregistered while file open */
+			mask = POLLHUP;
+			goto out;
+		}
+		poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait);
+		mask = POLLOUT | POLLWRNORM;
+		if (dev->drv[drvidx]->stavail) {
+			mask |= POLLIN | POLLRDNORM;
+		}
+		goto out;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		mask = isdn_ppp_poll(file, wait);
+		goto out;
+	}
+#endif
+	mask = POLLERR;
+ out:
+	unlock_kernel();
+	return mask;
+}
+
+
+static int
+isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+	uint minor = MINOR(inode->i_rdev);
+	isdn_ctrl c;
+	int drvidx;
+	int chidx;
+	int ret;
+	int i;
+	char __user *p;
+	char *s;
+	union iocpar {
+		char name[10];
+		char bname[22];
+		isdn_ioctl_struct iocts;
+		isdn_net_ioctl_phone phone;
+		isdn_net_ioctl_cfg cfg;
+	} iocpar;
+	void __user *argp = (void __user *)arg;
+
+#define name  iocpar.name
+#define bname iocpar.bname
+#define iocts iocpar.iocts
+#define phone iocpar.phone
+#define cfg   iocpar.cfg
+
+	if (minor == ISDN_MINOR_STATUS) {
+		switch (cmd) {
+			case IIOCGETDVR:
+				return (TTY_DV +
+					(NET_DV << 8) +
+					(INF_DV << 16));
+			case IIOCGETCPS:
+				if (arg) {
+					ulong __user *p = argp;
+					int i;
+					if (!access_ok(VERIFY_WRITE, p,
+							sizeof(ulong) * ISDN_MAX_CHANNELS * 2))
+						return -EFAULT;
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						put_user(dev->ibytes[i], p++);
+						put_user(dev->obytes[i], p++);
+					}
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+#ifdef CONFIG_NETDEVICES
+			case IIOCNETGPN:
+				/* Get peer phone number of a connected 
+				 * isdn network interface */
+				if (arg) {
+					if (copy_from_user(&phone, argp, sizeof(phone)))
+						return -EFAULT;
+					return isdn_net_getpeer(&phone, argp);
+				} else
+					return -EINVAL;
+#endif
+			default:
+				return -EINVAL;
+		}
+	}
+	if (!dev->drivers)
+		return -ENODEV;
+	if (minor <= ISDN_MINOR_BMAX) {
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0)
+			return -ENODEV;
+		chidx = isdn_minor2chan(minor);
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
+			return -ENODEV;
+		return 0;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+/*
+ * isdn net devices manage lots of configuration variables as linked lists.
+ * Those lists must only be manipulated from user space. Some of the ioctl's
+ * service routines access user space and are not atomic. Therefor, ioctl's
+ * manipulating the lists and ioctl's sleeping while accessing the lists
+ * are serialized by means of a semaphore.
+ */
+		switch (cmd) {
+			case IIOCNETDWRSET:
+				printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
+				return(-EINVAL);
+			case IIOCNETLCR:
+				printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
+				return -ENODEV;
+#ifdef CONFIG_NETDEVICES
+			case IIOCNETAIF:
+				/* Add a network-interface */
+				if (arg) {
+					if (copy_from_user(name, argp, sizeof(name)))
+						return -EFAULT;
+					s = name;
+				} else {
+					s = NULL;
+				}
+				ret = down_interruptible(&dev->sem);
+				if( ret ) return ret;
+				if ((s = isdn_net_new(s, NULL))) {
+					if (copy_to_user(argp, s, strlen(s) + 1)){
+						ret = -EFAULT;
+					} else {
+						ret = 0;
+					}
+				} else
+					ret = -ENODEV;
+				up(&dev->sem);
+				return ret;
+			case IIOCNETASL:
+				/* Add a slave to a network-interface */
+				if (arg) {
+					if (copy_from_user(bname, argp, sizeof(bname) - 1))
+						return -EFAULT;
+				} else
+					return -EINVAL;
+				ret = down_interruptible(&dev->sem);
+				if( ret ) return ret;
+				if ((s = isdn_net_newslave(bname))) {
+					if (copy_to_user(argp, s, strlen(s) + 1)){
+						ret = -EFAULT;
+					} else {
+						ret = 0;
+					}
+				} else
+					ret = -ENODEV;
+				up(&dev->sem);
+				return ret;
+			case IIOCNETDIF:
+				/* Delete a network-interface */
+				if (arg) {
+					if (copy_from_user(name, argp, sizeof(name)))
+						return -EFAULT;
+					ret = down_interruptible(&dev->sem);
+					if( ret ) return ret;
+					ret = isdn_net_rm(name);
+					up(&dev->sem);
+					return ret;
+				} else
+					return -EINVAL;
+			case IIOCNETSCF:
+				/* Set configurable parameters of a network-interface */
+				if (arg) {
+					if (copy_from_user(&cfg, argp, sizeof(cfg)))
+						return -EFAULT;
+					return isdn_net_setcfg(&cfg);
+				} else
+					return -EINVAL;
+			case IIOCNETGCF:
+				/* Get configurable parameters of a network-interface */
+				if (arg) {
+					if (copy_from_user(&cfg, argp, sizeof(cfg)))
+						return -EFAULT;
+					if (!(ret = isdn_net_getcfg(&cfg))) {
+						if (copy_to_user(argp, &cfg, sizeof(cfg)))
+							return -EFAULT;
+					}
+					return ret;
+				} else
+					return -EINVAL;
+			case IIOCNETANM:
+				/* Add a phone-number to a network-interface */
+				if (arg) {
+					if (copy_from_user(&phone, argp, sizeof(phone)))
+						return -EFAULT;
+					ret = down_interruptible(&dev->sem);
+					if( ret ) return ret;
+					ret = isdn_net_addphone(&phone);
+					up(&dev->sem);
+					return ret;
+				} else
+					return -EINVAL;
+			case IIOCNETGNM:
+				/* Get list of phone-numbers of a network-interface */
+				if (arg) {
+					if (copy_from_user(&phone, argp, sizeof(phone)))
+						return -EFAULT;
+					ret = down_interruptible(&dev->sem);
+					if( ret ) return ret;
+					ret = isdn_net_getphones(&phone, argp);
+					up(&dev->sem);
+					return ret;
+				} else
+					return -EINVAL;
+			case IIOCNETDNM:
+				/* Delete a phone-number of a network-interface */
+				if (arg) {
+					if (copy_from_user(&phone, argp, sizeof(phone)))
+						return -EFAULT;
+					ret = down_interruptible(&dev->sem);
+					if( ret ) return ret;
+					ret = isdn_net_delphone(&phone);
+					up(&dev->sem);
+					return ret;
+				} else
+					return -EINVAL;
+			case IIOCNETDIL:
+				/* Force dialing of a network-interface */
+				if (arg) {
+					if (copy_from_user(name, argp, sizeof(name)))
+						return -EFAULT;
+					return isdn_net_force_dial(name);
+				} else
+					return -EINVAL;
+#ifdef CONFIG_ISDN_PPP
+			case IIOCNETALN:
+				if (!arg)
+					return -EINVAL;
+				if (copy_from_user(name, argp, sizeof(name)))
+					return -EFAULT;
+				return isdn_ppp_dial_slave(name);
+			case IIOCNETDLN:
+				if (!arg)
+					return -EINVAL;
+				if (copy_from_user(name, argp, sizeof(name)))
+					return -EFAULT;
+				return isdn_ppp_hangup_slave(name);
+#endif
+			case IIOCNETHUP:
+				/* Force hangup of a network-interface */
+				if (!arg)
+					return -EINVAL;
+				if (copy_from_user(name, argp, sizeof(name)))
+					return -EFAULT;
+				return isdn_net_force_hangup(name);
+				break;
+#endif                          /* CONFIG_NETDEVICES */
+			case IIOCSETVER:
+				dev->net_verbose = arg;
+				printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+				return 0;
+			case IIOCSETGST:
+				if (arg)
+					dev->global_flags |= ISDN_GLOBAL_STOPPED;
+				else
+					dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+				printk(KERN_INFO "isdn: Global Mode %s\n",
+				       (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+				return 0;
+			case IIOCSETBRJ:
+				drvidx = -1;
+				if (arg) {
+					int i;
+					char *p;
+					if (copy_from_user(&iocts, argp,
+					     sizeof(isdn_ioctl_struct)))
+						return -EFAULT;
+					if (strlen(iocts.drvid)) {
+						if ((p = strchr(iocts.drvid, ',')))
+							*p = 0;
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					}
+				}
+				if (drvidx == -1)
+					return -ENODEV;
+				if (iocts.arg)
+					dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
+				else
+					dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
+				return 0;
+			case IIOCSIGPRF:
+				dev->profd = current;
+				return 0;
+				break;
+			case IIOCGETPRF:
+				/* Get all Modem-Profiles */
+				if (arg) {
+					char __user *p = argp;
+					int i;
+
+					if (!access_ok(VERIFY_WRITE, argp,
+					(ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
+						   * ISDN_MAX_CHANNELS))
+						return -EFAULT;
+
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						if (copy_to_user(p, dev->mdm.info[i].emu.profile,
+						      ISDN_MODEM_NUMREG))
+							return -EFAULT;
+						p += ISDN_MODEM_NUMREG;
+						if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
+							return -EFAULT;
+						p += ISDN_MSNLEN;
+						if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN))
+							return -EFAULT;
+						p += ISDN_LMSNLEN;
+					}
+					return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS;
+				} else
+					return -EINVAL;
+				break;
+			case IIOCSETPRF:
+				/* Set all Modem-Profiles */
+				if (arg) {
+					char __user *p = argp;
+					int i;
+
+					if (!access_ok(VERIFY_READ, argp,
+					(ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
+						   * ISDN_MAX_CHANNELS))
+						return -EFAULT;
+
+					for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+						if (copy_from_user(dev->mdm.info[i].emu.profile, p,
+						     ISDN_MODEM_NUMREG))
+							return -EFAULT;
+						p += ISDN_MODEM_NUMREG;
+						if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN))
+							return -EFAULT;
+						p += ISDN_LMSNLEN;
+						if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))
+							return -EFAULT;
+						p += ISDN_MSNLEN;
+					}
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			case IIOCSETMAP:
+			case IIOCGETMAP:
+				/* Set/Get MSN->EAZ-Mapping for a driver */
+				if (arg) {
+
+					if (copy_from_user(&iocts, argp,
+					     sizeof(isdn_ioctl_struct)))
+						return -EFAULT;
+					if (strlen(iocts.drvid)) {
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					} else
+						drvidx = 0;
+					if (drvidx == -1)
+						return -ENODEV;
+					if (cmd == IIOCSETMAP) {
+						int loop = 1;
+
+						p = (char __user *) iocts.arg;
+						i = 0;
+						while (loop) {
+							int j = 0;
+
+							while (1) {
+								if (!access_ok(VERIFY_READ, p, 1))
+									return -EFAULT;
+								get_user(bname[j], p++);
+								switch (bname[j]) {
+									case '\0':
+										loop = 0;
+										/* Fall through */
+									case ',':
+										bname[j] = '\0';
+										strcpy(dev->drv[drvidx]->msn2eaz[i], bname);
+										j = ISDN_MSNLEN;
+										break;
+									default:
+										j++;
+								}
+								if (j >= ISDN_MSNLEN)
+									break;
+							}
+							if (++i > 9)
+								break;
+						}
+					} else {
+						p = (char __user *) iocts.arg;
+						for (i = 0; i < 10; i++) {
+							sprintf(bname, "%s%s",
+								strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+								dev->drv[drvidx]->msn2eaz[i] : "_",
+								(i < 9) ? "," : "\0");
+							if (copy_to_user(p, bname, strlen(bname) + 1))
+								return -EFAULT;
+							p += strlen(bname);
+						}
+					}
+					return 0;
+				} else
+					return -EINVAL;
+			case IIOCDBGVAR:
+				if (arg) {
+					if (copy_to_user(argp, &dev, sizeof(ulong)))
+						return -EFAULT;
+					return 0;
+				} else
+					return -EINVAL;
+				break;
+			default:
+				if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
+					cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
+				else
+					return -EINVAL;
+				if (arg) {
+					int i;
+					char *p;
+					if (copy_from_user(&iocts, argp, sizeof(isdn_ioctl_struct)))
+						return -EFAULT;
+					if (strlen(iocts.drvid)) {
+						if ((p = strchr(iocts.drvid, ',')))
+							*p = 0;
+						drvidx = -1;
+						for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+							if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+								drvidx = i;
+								break;
+							}
+					} else
+						drvidx = 0;
+					if (drvidx == -1)
+						return -ENODEV;
+					if (!access_ok(VERIFY_WRITE, argp,
+					     sizeof(isdn_ioctl_struct)))
+						return -EFAULT;
+					c.driver = drvidx;
+					c.command = ISDN_CMD_IOCTL;
+					c.arg = cmd;
+					memcpy(c.parm.num, &iocts.arg, sizeof(ulong));
+					ret = isdn_command(&c);
+					memcpy(&iocts.arg, c.parm.num, sizeof(ulong));
+					if (copy_to_user(argp, &iocts, sizeof(isdn_ioctl_struct)))
+						return -EFAULT;
+					return ret;
+				} else
+					return -EINVAL;
+		}
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX)
+		return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg));
+#endif
+	return -ENODEV;
+
+#undef name
+#undef bname
+#undef iocts
+#undef phone
+#undef cfg
+}
+
+/*
+ * Open the device code.
+ */
+static int
+isdn_open(struct inode *ino, struct file *filep)
+{
+	uint minor = MINOR(ino->i_rdev);
+	int drvidx;
+	int chidx;
+	int retval = -ENODEV;
+
+
+	if (minor == ISDN_MINOR_STATUS) {
+		infostruct *p;
+
+		if ((p = kmalloc(sizeof(infostruct), GFP_KERNEL))) {
+			p->next = (char *) dev->infochain;
+			p->private = (char *) &(filep->private_data);
+			dev->infochain = p;
+			/* At opening we allow a single update */
+			filep->private_data = (char *) 1;
+			retval = 0;
+			goto out;
+		} else {
+			retval = -ENOMEM;
+			goto out;
+		}
+	}
+	if (!dev->channels)
+		goto out;
+	if (minor <= ISDN_MINOR_BMAX) {
+		printk(KERN_WARNING "isdn_open minor %d obsolete!\n", minor);
+		drvidx = isdn_minor2drv(minor);
+		if (drvidx < 0)
+			goto out;
+		chidx = isdn_minor2chan(minor);
+		if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
+			goto out;
+		if (!(dev->drv[drvidx]->online & (1 << chidx)))
+			goto out;
+		isdn_lock_drivers();
+		retval = 0;
+		goto out;
+	}
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+		if (drvidx < 0)
+			goto out;
+		isdn_lock_drivers();
+		retval = 0;
+		goto out;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX) {
+		retval = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep);
+		if (retval == 0)
+			isdn_lock_drivers();
+		goto out;
+	}
+#endif
+ out:
+	nonseekable_open(ino, filep);
+	return retval;
+}
+
+static int
+isdn_close(struct inode *ino, struct file *filep)
+{
+	uint minor = MINOR(ino->i_rdev);
+
+	lock_kernel();
+	if (minor == ISDN_MINOR_STATUS) {
+		infostruct *p = dev->infochain;
+		infostruct *q = NULL;
+
+		while (p) {
+			if (p->private == (char *) &(filep->private_data)) {
+				if (q)
+					q->next = p->next;
+				else
+					dev->infochain = (infostruct *) (p->next);
+				kfree(p);
+				goto out;
+			}
+			q = p;
+			p = (infostruct *) (p->next);
+		}
+		printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
+		goto out;
+	}
+	isdn_unlock_drivers();
+	if (minor <= ISDN_MINOR_BMAX)
+		goto out;
+	if (minor <= ISDN_MINOR_CTRLMAX) {
+		if (dev->profd == current)
+			dev->profd = NULL;
+		goto out;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (minor <= ISDN_MINOR_PPPMAX)
+		isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
+#endif
+
+ out:
+	unlock_kernel();
+	return 0;
+}
+
+static struct file_operations isdn_fops =
+{
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= isdn_read,
+	.write		= isdn_write,
+	.poll		= isdn_poll,
+	.ioctl		= isdn_ioctl,
+	.open		= isdn_open,
+	.release	= isdn_close,
+};
+
+char *
+isdn_map_eaz2msn(char *msn, int di)
+{
+	isdn_driver_t *this = dev->drv[di];
+	int i;
+
+	if (strlen(msn) == 1) {
+		i = msn[0] - '0';
+		if ((i >= 0) && (i <= 9))
+			if (strlen(this->msn2eaz[i]))
+				return (this->msn2eaz[i]);
+	}
+	return (msn);
+}
+
+/*
+ * Find an unused ISDN-channel, whose feature-flags match the
+ * given L2- and L3-protocols.
+ */
+#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))
+
+/*
+ * This function must be called with holding the dev->lock.
+ */
+int
+isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+		      ,int pre_chan, char *msn)
+{
+	int i;
+	ulong features;
+	ulong vfeatures;
+
+	features = ((1 << l2_proto) | (0x10000 << l3_proto));
+	vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &
+		     ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));
+	/* If Layer-2 protocol is V.110, accept drivers with
+	 * transparent feature even if these don't support V.110
+	 * because we can emulate this in linklevel.
+	 */
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_NONE(dev->usage[i]) &&
+		    (dev->drvmap[i] != -1)) {
+			int d = dev->drvmap[i];
+			if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
+			((pre_dev != d) || (pre_chan != dev->chanmap[i])))
+				continue;
+			if (!strcmp(isdn_map_eaz2msn(msn, d), "-"))
+				continue;
+			if (dev->usage[i] & ISDN_USAGE_DISABLED)
+			        continue; /* usage not allowed */
+			if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
+				if (((dev->drv[d]->interface->features & features) == features) ||
+				    (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
+				     (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
+					if ((pre_dev < 0) || (pre_chan < 0)) {
+						dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+						dev->usage[i] |= usage;
+						isdn_info_update();
+						return i;
+					} else {
+						if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) {
+							dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+							dev->usage[i] |= usage;
+							isdn_info_update();
+							return i;
+						}
+					}
+				}
+			}
+		}
+	return -1;
+}
+
+/*
+ * Set state of ISDN-channel to 'unused'
+ */
+void
+isdn_free_channel(int di, int ch, int usage)
+{
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) &&
+		    (dev->drvmap[i] == di) &&
+		    (dev->chanmap[i] == ch)) {
+			dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
+			strcpy(dev->num[i], "???");
+			dev->ibytes[i] = 0;
+			dev->obytes[i] = 0;
+// 20.10.99 JIM, try to reinitialize v110 !
+			dev->v110emu[i] = 0;
+			atomic_set(&(dev->v110use[i]), 0);
+			isdn_v110_close(dev->v110[i]);
+			dev->v110[i] = NULL;
+// 20.10.99 JIM, try to reinitialize v110 !
+			isdn_info_update();
+			skb_queue_purge(&dev->drv[di]->rpqueue[ch]);
+		}
+}
+
+/*
+ * Cancel Exclusive-Flag for ISDN-channel
+ */
+void
+isdn_unexclusive_channel(int di, int ch)
+{
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if ((dev->drvmap[i] == di) &&
+		    (dev->chanmap[i] == ch)) {
+			dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE;
+			isdn_info_update();
+			return;
+		}
+}
+
+/*
+ *  writebuf replacement for SKB_ABLE drivers
+ */
+static int
+isdn_writebuf_stub(int drvidx, int chan, const u_char __user * buf, int len)
+{
+	int ret;
+	int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+	struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC);
+
+	if (!skb)
+		return 0;
+	skb_reserve(skb, hl);
+	copy_from_user(skb_put(skb, len), buf, len);
+	ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb);
+	if (ret <= 0)
+		dev_kfree_skb(skb);
+	if (ret > 0)
+		dev->obytes[isdn_dc2minor(drvidx, chan)] += ret;
+	return ret;
+}
+
+/*
+ * Return: length of data on success, -ERRcode on failure.
+ */
+int
+isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb)
+{
+	int ret;
+	struct sk_buff *nskb = NULL;
+	int v110_ret = skb->len;
+	int idx = isdn_dc2minor(drvidx, chan);
+
+	if (dev->v110[idx]) {
+		atomic_inc(&dev->v110use[idx]);
+		nskb = isdn_v110_encode(dev->v110[idx], skb);
+		atomic_dec(&dev->v110use[idx]);
+		if (!nskb)
+			return 0;
+		v110_ret = *((int *)nskb->data);
+		skb_pull(nskb, sizeof(int));
+		if (!nskb->len) {
+			dev_kfree_skb(nskb);
+			return v110_ret;
+		}
+		/* V.110 must always be acknowledged */
+		ack = 1;
+		ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb);
+	} else {
+		int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+
+		if( skb_headroom(skb) < hl ){
+			/* 
+			 * This should only occur when new HL driver with
+			 * increased hl_hdrlen was loaded after netdevice
+			 * was created and connected to the new driver.
+			 *
+			 * The V.110 branch (re-allocates on its own) does
+			 * not need this
+			 */
+			struct sk_buff * skb_tmp;
+
+			skb_tmp = skb_realloc_headroom(skb, hl);
+			printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed");
+			if (!skb_tmp) return -ENOMEM; /* 0 better? */
+			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp);
+			if( ret > 0 ){
+				dev_kfree_skb(skb);
+			} else {
+				dev_kfree_skb(skb_tmp);
+			}
+		} else {
+			ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
+		}
+	}
+	if (ret > 0) {
+		dev->obytes[idx] += ret;
+		if (dev->v110[idx]) {
+			atomic_inc(&dev->v110use[idx]);
+			dev->v110[idx]->skbuser++;
+			atomic_dec(&dev->v110use[idx]);
+			/* For V.110 return unencoded data length */
+			ret = v110_ret;
+			/* if the complete frame was send we free the skb;
+			   if not upper function will requeue the skb */ 
+			if (ret == skb->len)
+				dev_kfree_skb(skb);
+		}
+	} else
+		if (dev->v110[idx])
+			dev_kfree_skb(nskb);
+	return ret;
+}
+
+int
+isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding)
+{
+	int j, k, m;
+
+	init_waitqueue_head(&d->st_waitq);
+	if (d->flags & DRV_FLAG_RUNNING)
+		return -1;
+       	if (n < 1) return 0;
+
+	m = (adding) ? d->channels + n : n;
+
+	if (dev->channels + n > ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+		       ISDN_MAX_CHANNELS);
+		return -1;
+	}
+
+	if ((adding) && (d->rcverr))
+		kfree(d->rcverr);
+	if (!(d->rcverr = kmalloc(sizeof(int) * m, GFP_ATOMIC))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+		return -1;
+	}
+	memset((char *) d->rcverr, 0, sizeof(int) * m);
+
+	if ((adding) && (d->rcvcount))
+		kfree(d->rcvcount);
+	if (!(d->rcvcount = kmalloc(sizeof(int) * m, GFP_ATOMIC))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+		if (!adding) kfree(d->rcverr);
+		return -1;
+	}
+	memset((char *) d->rcvcount, 0, sizeof(int) * m);
+
+	if ((adding) && (d->rpqueue)) {
+		for (j = 0; j < d->channels; j++)
+			skb_queue_purge(&d->rpqueue[j]);
+		kfree(d->rpqueue);
+	}
+	if (!(d->rpqueue = kmalloc(sizeof(struct sk_buff_head) * m, GFP_ATOMIC))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+		if (!adding) {
+			kfree(d->rcvcount);
+			kfree(d->rcverr);
+		}
+		return -1; 
+	}
+	for (j = 0; j < m; j++) {
+		skb_queue_head_init(&d->rpqueue[j]);
+	}
+
+	if ((adding) && (d->rcv_waitq))
+		kfree(d->rcv_waitq);
+	d->rcv_waitq = kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_ATOMIC);
+	if (!d->rcv_waitq) {
+		printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+		if (!adding) {
+			kfree(d->rpqueue);
+			kfree(d->rcvcount);
+			kfree(d->rcverr);
+		}
+		return -1;
+	}
+	d->snd_waitq = d->rcv_waitq + m;
+	for (j = 0; j < m; j++) {
+		init_waitqueue_head(&d->rcv_waitq[j]);
+		init_waitqueue_head(&d->snd_waitq[j]);
+	}
+
+	dev->channels += n;
+	for (j = d->channels; j < m; j++)
+		for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+			if (dev->chanmap[k] < 0) {
+				dev->chanmap[k] = j;
+				dev->drvmap[k] = drvidx;
+				break;
+			}
+	d->channels = m;
+	return 0;
+}
+
+/*
+ * Low-level-driver registration
+ */
+
+static void
+set_global_features(void)
+{
+	int drvidx;
+
+	dev->global_features = 0;
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) {
+		if (!dev->drv[drvidx])
+			continue;
+		if (dev->drv[drvidx]->interface)
+			dev->global_features |= dev->drv[drvidx]->interface->features;
+	}
+}
+
+#ifdef CONFIG_ISDN_DIVERSION
+
+static char *map_drvname(int di)
+{
+  if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) 
+    return(NULL);
+  return(dev->drvid[di]); /* driver name */
+} /* map_drvname */
+
+static int map_namedrv(char *id)
+{  int i;
+
+   for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+    { if (!strcmp(dev->drvid[i],id)) 
+        return(i);
+    }
+   return(-1);
+} /* map_namedrv */
+
+int DIVERT_REG_NAME(isdn_divert_if *i_div)
+{
+  if (i_div->if_magic != DIVERT_IF_MAGIC) 
+    return(DIVERT_VER_ERR);
+  switch (i_div->cmd)
+    {
+      case DIVERT_CMD_REL:
+        if (divert_if != i_div) 
+          return(DIVERT_REL_ERR);
+        divert_if = NULL; /* free interface */
+        return(DIVERT_NO_ERR);
+
+      case DIVERT_CMD_REG:
+        if (divert_if) 
+          return(DIVERT_REG_ERR);
+        i_div->ll_cmd = isdn_command; /* set command function */
+        i_div->drv_to_name = map_drvname; 
+        i_div->name_to_drv = map_namedrv; 
+        divert_if = i_div; /* remember interface */
+        return(DIVERT_NO_ERR);
+
+      default:
+        return(DIVERT_CMD_ERR);   
+    }
+} /* DIVERT_REG_NAME */
+
+EXPORT_SYMBOL(DIVERT_REG_NAME);
+
+#endif /* CONFIG_ISDN_DIVERSION */
+
+
+EXPORT_SYMBOL(register_isdn);
+#ifdef CONFIG_ISDN_PPP
+EXPORT_SYMBOL(isdn_ppp_register_compressor);
+EXPORT_SYMBOL(isdn_ppp_unregister_compressor);
+#endif
+
+int
+register_isdn(isdn_if * i)
+{
+	isdn_driver_t *d;
+	int j;
+	ulong flags;
+	int drvidx;
+
+	if (dev->drivers >= ISDN_MAX_DRIVERS) {
+		printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n",
+		       ISDN_MAX_DRIVERS);
+		return 0;
+	}
+	if (!i->writebuf_skb) {
+		printk(KERN_WARNING "register_isdn: No write routine given.\n");
+		return 0;
+	}
+	if (!(d = kmalloc(sizeof(isdn_driver_t), GFP_KERNEL))) {
+		printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
+		return 0;
+	}
+	memset((char *) d, 0, sizeof(isdn_driver_t));
+
+	d->maxbufsize = i->maxbufsize;
+	d->pktcount = 0;
+	d->stavail = 0;
+	d->flags = DRV_FLAG_LOADED;
+	d->online = 0;
+	d->interface = i;
+	d->channels = 0;
+	spin_lock_irqsave(&dev->lock, flags);
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+		if (!dev->drv[drvidx])
+			break;
+	if (isdn_add_channels(d, drvidx, i->channels, 0)) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		kfree(d);
+		return 0;
+	}
+	i->channels = drvidx;
+	i->rcvcallb_skb = isdn_receive_skb_callback;
+	i->statcallb = isdn_status_callback;
+	if (!strlen(i->id))
+		sprintf(i->id, "line%d", drvidx);
+	for (j = 0; j < drvidx; j++)
+		if (!strcmp(i->id, dev->drvid[j]))
+			sprintf(i->id, "line%d", drvidx);
+	dev->drv[drvidx] = d;
+	strcpy(dev->drvid[drvidx], i->id);
+	isdn_info_update();
+	dev->drivers++;
+	set_global_features();
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return 1;
+}
+
+/*
+ *****************************************************************************
+ * And now the modules code.
+ *****************************************************************************
+ */
+
+static char *
+isdn_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+/*
+ * Allocate and initialize all data, register modem-devices
+ */
+static int __init isdn_init(void)
+{
+	int i;
+	char tmprev[50];
+
+	if (!(dev = (isdn_dev *) vmalloc(sizeof(isdn_dev)))) {
+		printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
+		return -EIO;
+	}
+	memset((char *) dev, 0, sizeof(isdn_dev));
+	init_timer(&dev->timer);
+	dev->timer.function = isdn_timer_funct;
+	spin_lock_init(&dev->lock);
+	spin_lock_init(&dev->timerlock);
+#ifdef MODULE
+	dev->owner = THIS_MODULE;
+#endif
+	init_MUTEX(&dev->sem);
+	init_waitqueue_head(&dev->info_waitq);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		dev->drvmap[i] = -1;
+		dev->chanmap[i] = -1;
+		dev->m_idx[i] = -1;
+		strcpy(dev->num[i], "???");
+		init_waitqueue_head(&dev->mdm.info[i].open_wait);
+		init_waitqueue_head(&dev->mdm.info[i].close_wait);
+	}
+	if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
+		printk(KERN_WARNING "isdn: Could not register control devices\n");
+		vfree(dev);
+		return -EIO;
+	}
+	if ((isdn_tty_modem_init()) < 0) {
+		printk(KERN_WARNING "isdn: Could not register tty devices\n");
+		vfree(dev);
+		unregister_chrdev(ISDN_MAJOR, "isdn");
+		return -EIO;
+	}
+#ifdef CONFIG_ISDN_PPP
+	if (isdn_ppp_init() < 0) {
+		printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n");
+		isdn_tty_exit();
+		unregister_chrdev(ISDN_MAJOR, "isdn");
+		vfree(dev);
+		return -EIO;
+	}
+#endif                          /* CONFIG_ISDN_PPP */
+
+	strcpy(tmprev, isdn_revision);
+	printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_tty_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_net_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_ppp_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_audio_revision);
+	printk("%s/", isdn_getrev(tmprev));
+	strcpy(tmprev, isdn_v110_revision);
+	printk("%s", isdn_getrev(tmprev));
+
+#ifdef MODULE
+	printk(" loaded\n");
+#else
+	printk("\n");
+#endif
+	isdn_info_update();
+	return 0;
+}
+
+/*
+ * Unload module
+ */
+static void __exit isdn_exit(void)
+{
+#ifdef CONFIG_ISDN_PPP
+	isdn_ppp_cleanup();
+#endif
+	if (isdn_net_rmall() < 0) {
+		printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n");
+		return;
+	}
+	isdn_tty_exit();
+	unregister_chrdev(ISDN_MAJOR, "isdn");
+	del_timer(&dev->timer);
+	/* call vfree with interrupts enabled, else it will hang */
+	vfree(dev);
+	printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
+}
+
+module_init(isdn_init);
+module_exit(isdn_exit);
diff --git a/drivers/isdn/i4l/isdn_common.h b/drivers/isdn/i4l/isdn_common.h
new file mode 100644
index 0000000..808135c
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_common.h
@@ -0,0 +1,47 @@
+/* $Id: isdn_common.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem
+ * common used functions and debugging-switches (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#undef  ISDN_DEBUG_MODEM_OPEN
+#undef  ISDN_DEBUG_MODEM_IOCTL
+#undef  ISDN_DEBUG_MODEM_WAITSENT
+#undef  ISDN_DEBUG_MODEM_HUP
+#undef  ISDN_DEBUG_MODEM_ICALL
+#undef  ISDN_DEBUG_MODEM_DUMP
+#undef  ISDN_DEBUG_MODEM_VOICE
+#undef  ISDN_DEBUG_AT
+#undef  ISDN_DEBUG_NET_DUMP
+#undef  ISDN_DEBUG_NET_DIAL
+#undef  ISDN_DEBUG_NET_ICALL
+
+/* Prototypes */
+extern void isdn_lock_drivers(void);
+extern void isdn_unlock_drivers(void);
+extern void isdn_free_channel(int di, int ch, int usage);
+extern void isdn_all_eaz(int di, int ch);
+extern int  isdn_command(isdn_ctrl *);
+extern int  isdn_dc2minor(int di, int ch);
+extern void isdn_info_update(void);
+extern char *isdn_map_eaz2msn(char *msn, int di);
+extern void isdn_timer_ctrl(int tf, int onoff);
+extern void isdn_unexclusive_channel(int di, int ch);
+extern int  isdn_getnum(char **);
+extern int  isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *);
+extern int  isdn_get_free_channel(int, int, int, int, int, char *);
+extern int  isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
+extern int  register_isdn(isdn_if * i);
+extern int  isdn_msncmp( const char *,  const char *);
+extern int  isdn_add_channels(isdn_driver_t *, int, int, int);
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+extern void isdn_dumppkt(char *, u_char *, int, int);
+#endif
diff --git a/drivers/isdn/i4l/isdn_concap.c b/drivers/isdn/i4l/isdn_concap.c
new file mode 100644
index 0000000..83a4f538
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_concap.c
@@ -0,0 +1,108 @@
+/* $Id: isdn_concap.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ * 
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
+ * stuff goes here. Stuff that depends only on the concap protocol goes to
+ * another -- protocol specific -- source file.
+ *
+ */
+
+
+#include <linux/isdn.h>
+#include "isdn_x25iface.h"
+#include "isdn_net.h"
+#include <linux/concap.h>
+#include "isdn_concap.h"
+
+
+/* The following set of device service operations are for encapsulation
+   protocols that require for reliable datalink semantics. That means:
+
+   - before any data is to be submitted the connection must explicitly
+     be set up.
+   - after the successful set up of the connection is signalled the
+     connection is considered to be reliably up.
+
+   Auto-dialing ist not compatible with this requirements. Thus, auto-dialing 
+   is completely bypassed.
+
+   It might be possible to implement a (non standardized) datalink protocol
+   that provides a reliable data link service while using some auto dialing
+   mechanism. Such a protocol would need an auxiliary channel (i.e. user-user-
+   signaling on the D-channel) while the B-channel is down.
+   */
+
+
+int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
+{
+	struct net_device *ndev = concap -> net_dev;
+	isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
+	isdn_net_local *lp = isdn_net_get_locked_lp(nd);
+
+	IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name);
+	if (!lp) {
+		IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 1);
+		return 1;
+	}
+	lp->huptimer = 0;
+	isdn_net_writebuf_skb(lp, skb);
+	spin_unlock_bh(&lp->xmit_lock);
+	IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0);
+	return 0;
+}
+
+
+int isdn_concap_dl_connect_req(struct concap_proto *concap)
+{
+	struct net_device *ndev = concap -> net_dev;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int ret;
+	IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
+
+	/* dial ... */
+	ret = isdn_net_dial_req( lp );
+	if ( ret ) IX25DEBUG("dialing failed\n");
+	return ret;
+}
+
+int isdn_concap_dl_disconn_req(struct concap_proto *concap)
+{
+	IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name);
+
+	isdn_net_hangup( concap -> net_dev );
+	return 0;
+}
+
+struct concap_device_ops isdn_concap_reliable_dl_dops = {
+	&isdn_concap_dl_data_req,
+	&isdn_concap_dl_connect_req,
+	&isdn_concap_dl_disconn_req
+};
+
+struct concap_device_ops isdn_concap_demand_dial_dops = {
+	NULL, /* set this first entry to something like &isdn_net_start_xmit,
+		 but the entry part of the current isdn_net_start_xmit must be
+		 separated first. */
+	/* no connection control for demand dial semantics */
+	NULL,
+	NULL,
+};
+
+/* The following should better go into a dedicated source file such that
+   this sourcefile does not need to include any protocol specific header
+   files. For now:
+   */
+struct concap_proto * isdn_concap_new( int encap )
+{
+	switch ( encap ) {
+	case ISDN_NET_ENCAP_X25IFACE:
+		return isdn_x25iface_proto_new();
+	}
+	return NULL;
+}
diff --git a/drivers/isdn/i4l/isdn_concap.h b/drivers/isdn/i4l/isdn_concap.h
new file mode 100644
index 0000000..306eb18
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_concap.h
@@ -0,0 +1,14 @@
+/* $Id: isdn_concap.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+extern struct concap_device_ops isdn_concap_reliable_dl_dops;
+extern struct concap_device_ops isdn_concap_demand_dial_dops;
+extern struct concap_proto * isdn_concap_new( int );
+
+
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
new file mode 100644
index 0000000..e2b790e
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -0,0 +1,3222 @@
+/* $Id: isdn_net.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, network interfaces and related functions (linklevel).
+ *
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Data Over Voice (DOV) support added - Guy Ellis 23-Mar-02 
+ *                                       guy@traverse.com.au
+ * Outgoing calls - looks for a 'V' in first char of dialed number
+ * Incoming calls - checks first character of eaz as follows:
+ *   Numeric - accept DATA only - original functionality
+ *   'V'     - accept VOICE (DOV) only
+ *   'B'     - accept BOTH DATA and DOV types
+ *
+ * Jan 2001: fix CISCO HDLC      Bjoern A. Zeeb <i4l@zabbadoz.net>
+ *           for info on the protocol, see 
+ *           http://i4l.zabbadoz.net/i4l/cisco-hdlc.txt
+ */
+
+#include <linux/config.h>
+#include <linux/isdn.h>
+#include <net/arp.h>
+#include <net/dst.h>
+#include <net/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
+#include "isdn_ppp.h"
+#endif
+#ifdef CONFIG_ISDN_X25
+#include <linux/concap.h>
+#include "isdn_concap.h"
+#endif
+
+
+/*
+ * Outline of new tbusy handling: 
+ *
+ * Old method, roughly spoken, consisted of setting tbusy when entering
+ * isdn_net_start_xmit() and at several other locations and clearing
+ * it from isdn_net_start_xmit() thread when sending was successful.
+ *
+ * With 2.3.x multithreaded network core, to prevent problems, tbusy should
+ * only be set by the isdn_net_start_xmit() thread and only when a tx-busy
+ * condition is detected. Other threads (in particular isdn_net_stat_callb())
+ * are only allowed to clear tbusy.
+ *
+ * -HE
+ */
+
+/*
+ * About SOFTNET:
+ * Most of the changes were pretty obvious and basically done by HE already.
+ *
+ * One problem of the isdn net device code is that is uses struct net_device
+ * for masters and slaves. However, only master interface are registered to 
+ * the network layer, and therefore, it only makes sense to call netif_* 
+ * functions on them.
+ *
+ * --KG
+ */
+
+/* 
+ * Find out if the netdevice has been ifup-ed yet.
+ * For slaves, look at the corresponding master.
+ */
+static __inline__ int isdn_net_device_started(isdn_net_dev *n)
+{
+	isdn_net_local *lp = n->local;
+	struct net_device *dev;
+	
+	if (lp->master) 
+		dev = lp->master;
+	else
+		dev = &n->dev;
+	return netif_running(dev);
+}
+
+/*
+ * wake up the network -> net_device queue.
+ * For slaves, wake the corresponding master interface.
+ */
+static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp)
+{
+	if (lp->master) 
+		netif_wake_queue(lp->master);
+	else
+		netif_wake_queue(&lp->netdev->dev);
+}
+
+/*
+ * stop the network -> net_device queue.
+ * For slaves, stop the corresponding master interface.
+ */
+static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
+{
+	if (lp->master)
+		netif_stop_queue(lp->master);
+	else
+		netif_stop_queue(&lp->netdev->dev);
+}
+
+/*
+ * find out if the net_device which this lp belongs to (lp can be
+ * master or slave) is busy. It's busy iff all (master and slave) 
+ * queues are busy
+ */
+static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
+{
+	isdn_net_local *nlp;
+	isdn_net_dev *nd;
+	unsigned long flags;
+
+	if (!isdn_net_lp_busy(lp))
+		return 0;
+
+	if (lp->master)
+		nd = ((isdn_net_local *) lp->master->priv)->netdev;
+	else
+		nd = lp->netdev;
+	
+	spin_lock_irqsave(&nd->queue_lock, flags);
+	nlp = lp->next;
+	while (nlp != lp) {
+		if (!isdn_net_lp_busy(nlp)) {
+			spin_unlock_irqrestore(&nd->queue_lock, flags);
+			return 0;
+		}
+		nlp = nlp->next;
+	}
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+	return 1;
+}
+
+static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp)
+{
+	atomic_inc(&lp->frame_cnt);
+	if (isdn_net_device_busy(lp))
+		isdn_net_device_stop_queue(lp);
+}
+
+static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp)
+{
+	atomic_dec(&lp->frame_cnt);
+
+	if (!(isdn_net_device_busy(lp))) {
+		if (!skb_queue_empty(&lp->super_tx_queue)) {
+			schedule_work(&lp->tqueue);
+		} else {
+			isdn_net_device_wake_queue(lp);
+		}
+       }                                                                      
+}
+
+static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp)
+{
+	atomic_set(&lp->frame_cnt, 0);
+}
+
+/* For 2.2.x we leave the transmitter busy timeout at 2 secs, just 
+ * to be safe.
+ * For 2.3.x we push it up to 20 secs, because call establishment
+ * (in particular callback) may take such a long time, and we 
+ * don't want confusing messages in the log. However, there is a slight
+ * possibility that this large timeout will break other things like MPPP,
+ * which might rely on the tx timeout. If so, we'll find out this way...
+ */
+
+#define ISDN_NET_TX_TIMEOUT (20*HZ) 
+
+/* Prototypes */
+
+int isdn_net_force_dial_lp(isdn_net_local *);
+static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
+
+static void isdn_net_ciscohdlck_connected(isdn_net_local *lp);
+static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp);
+
+char *isdn_net_revision = "$Revision: 1.1.2.2 $";
+
+ /*
+  * Code for raw-networking over ISDN
+  */
+
+static void
+isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
+{
+	if(skb) {
+
+		u_short proto = ntohs(skb->protocol);
+
+		printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
+		       dev->name,
+		       (reason != NULL) ? reason : "unknown",
+		       (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
+		
+		dst_link_failure(skb);
+	}
+	else {  /* dial not triggered by rawIP packet */
+		printk(KERN_DEBUG "isdn_net: %s: %s\n",
+			   dev->name,
+			   (reason != NULL) ? reason : "reason unknown");
+	}
+}
+
+static void
+isdn_net_reset(struct net_device *dev)
+{
+#ifdef CONFIG_ISDN_X25
+	struct concap_device_ops * dops =
+		( (isdn_net_local *) dev->priv ) -> dops;
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+#endif
+#ifdef CONFIG_ISDN_X25
+	if( cprot && cprot -> pops && dops )
+		cprot -> pops -> restart ( cprot, dev, dops );
+#endif
+}
+
+/* Open/initialize the board. */
+static int
+isdn_net_open(struct net_device *dev)
+{
+	int i;
+	struct net_device *p;
+	struct in_device *in_dev;
+
+	/* moved here from isdn_net_reset, because only the master has an
+	   interface associated which is supposed to be started. BTW:
+	   we need to call netif_start_queue, not netif_wake_queue here */
+	netif_start_queue(dev);
+
+	isdn_net_reset(dev);
+	/* Fill in the MAC-level header (not needed, but for compatibility... */
+	for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
+		dev->dev_addr[i] = 0xfc;
+	if ((in_dev = dev->ip_ptr) != NULL) {
+		/*
+		 *      Any address will do - we take the first
+		 */
+		struct in_ifaddr *ifa = in_dev->ifa_list;
+		if (ifa != NULL)
+			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
+	}
+
+	/* If this interface has slaves, start them also */
+
+	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+		while (p) {
+			isdn_net_reset(p);
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+	}
+	isdn_lock_drivers();
+	return 0;
+}
+
+/*
+ * Assign an ISDN-channel to a net-interface
+ */
+static void
+isdn_net_bind_channel(isdn_net_local * lp, int idx)
+{
+	lp->flags |= ISDN_NET_CONNECTED;
+	lp->isdn_device = dev->drvmap[idx];
+	lp->isdn_channel = dev->chanmap[idx];
+	dev->rx_netdev[idx] = lp->netdev;
+	dev->st_netdev[idx] = lp->netdev;
+}
+
+/*
+ * unbind a net-interface (resets interface after an error)
+ */
+static void
+isdn_net_unbind_channel(isdn_net_local * lp)
+{
+	skb_queue_purge(&lp->super_tx_queue);
+
+	if (!lp->master) {	/* reset only master device */
+		/* Moral equivalent of dev_purge_queues():
+		   BEWARE! This chunk of code cannot be called from hardware
+		   interrupt handler. I hope it is true. --ANK
+		 */
+		qdisc_reset(lp->netdev->dev.qdisc);
+	}
+	lp->dialstate = 0;
+	dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+	dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+	isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+	lp->flags &= ~ISDN_NET_CONNECTED;
+	lp->isdn_device = -1;
+	lp->isdn_channel = -1;
+}
+
+/*
+ * Perform auto-hangup and cps-calculation for net-interfaces.
+ *
+ * auto-hangup:
+ * Increment idle-counter (this counter is reset on any incoming or
+ * outgoing packet), if counter exceeds configured limit either do a
+ * hangup immediately or - if configured - wait until just before the next
+ * charge-info.
+ *
+ * cps-calculation (needed for dynamic channel-bundling):
+ * Since this function is called every second, simply reset the
+ * byte-counter of the interface after copying it to the cps-variable.
+ */
+unsigned long last_jiffies = -HZ;
+
+void
+isdn_net_autohup(void)
+{
+	isdn_net_dev *p = dev->netdev;
+	int anymore;
+
+	anymore = 0;
+	while (p) {
+		isdn_net_local *l = p->local;
+		if (jiffies == last_jiffies)
+			l->cps = l->transcount;
+		else
+			l->cps = (l->transcount * HZ) / (jiffies - last_jiffies);
+		l->transcount = 0;
+		if (dev->net_verbose > 3)
+			printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps);
+		if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
+			anymore = 1;
+			l->huptimer++;
+			/*
+			 * if there is some dialmode where timeout-hangup
+			 * should _not_ be done, check for that here
+			 */
+			if ((l->onhtime) &&
+			    (l->huptimer > l->onhtime))
+			{
+				if (l->hupflags & ISDN_MANCHARGE &&
+				    l->hupflags & ISDN_CHARGEHUP) {
+					while (time_after(jiffies, l->chargetime + l->chargeint))
+						l->chargetime += l->chargeint;
+					if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ))
+						if (l->outgoing || l->hupflags & ISDN_INHUP)
+							isdn_net_hangup(&p->dev);
+				} else if (l->outgoing) {
+					if (l->hupflags & ISDN_CHARGEHUP) {
+						if (l->hupflags & ISDN_WAITCHARGE) {
+							printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n",
+							       l->name, l->hupflags);
+							isdn_net_hangup(&p->dev);
+						} else if (time_after(jiffies, l->chargetime + l->chargeint)) {
+							printk(KERN_DEBUG
+							       "isdn_net: %s: chtime = %lu, chint = %d\n",
+							       l->name, l->chargetime, l->chargeint);
+							isdn_net_hangup(&p->dev);
+						}
+					} else
+						isdn_net_hangup(&p->dev);
+				} else if (l->hupflags & ISDN_INHUP)
+					isdn_net_hangup(&p->dev);
+			}
+
+			if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) {
+				isdn_net_hangup(&p->dev);
+				break;
+			}
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	last_jiffies = jiffies;
+	isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);
+}
+
+static void isdn_net_lp_disconnected(isdn_net_local *lp)
+{
+	isdn_net_rm_from_bundle(lp);
+}
+
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the low-level driver.
+ * Return: 1 = Event handled, 0 = not for us or unknown Event.
+ */
+int
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
+{
+	isdn_net_dev *p = dev->st_netdev[idx];
+	int cmd = c->command;
+
+	if (p) {
+		isdn_net_local *lp = p->local;
+#ifdef CONFIG_ISDN_X25
+		struct concap_proto *cprot = lp->netdev->cprot;
+		struct concap_proto_ops *pops = cprot ? cprot->pops : NULL;
+#endif
+		switch (cmd) {
+			case ISDN_STAT_BSENT:
+				/* A packet has successfully been sent out */
+				if ((lp->flags & ISDN_NET_CONNECTED) &&
+				    (!lp->dialstate)) {
+					isdn_net_dec_frame_cnt(lp);
+					lp->stats.tx_packets++;
+					lp->stats.tx_bytes += c->parm.length;
+				}
+				return 1;
+			case ISDN_STAT_DCONN:
+				/* D-Channel is up */
+				switch (lp->dialstate) {
+					case 4:
+					case 7:
+					case 8:
+						lp->dialstate++;
+						return 1;
+					case 12:
+						lp->dialstate = 5;
+						return 1;
+				}
+				break;
+			case ISDN_STAT_DHUP:
+				/* Either D-Channel-hangup or error during dialout */
+#ifdef CONFIG_ISDN_X25
+				/* If we are not connencted then dialing had
+				   failed. If there are generic encap protocol
+				   receiver routines signal the closure of
+				   the link*/
+
+				if( !(lp->flags & ISDN_NET_CONNECTED)
+				    && pops && pops -> disconn_ind )
+					pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+				if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
+					if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+						isdn_net_ciscohdlck_disconnected(lp);
+#ifdef CONFIG_ISDN_PPP
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+						isdn_ppp_free(lp);
+#endif
+					isdn_net_lp_disconnected(lp);
+					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+					printk(KERN_INFO "%s: remote hangup\n", lp->name);
+					printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
+					       lp->charge);
+					isdn_net_unbind_channel(lp);
+					return 1;
+				}
+				break;
+#ifdef CONFIG_ISDN_X25
+			case ISDN_STAT_BHUP:
+				/* B-Channel-hangup */
+				/* try if there are generic encap protocol
+				   receiver routines and signal the closure of
+				   the link */
+				if( pops  &&  pops -> disconn_ind ){
+						pops -> disconn_ind(cprot);
+						return 1;
+					}
+				break;
+#endif /* CONFIG_ISDN_X25 */
+			case ISDN_STAT_BCONN:
+				/* B-Channel is up */
+				isdn_net_zero_frame_cnt(lp);
+				switch (lp->dialstate) {
+					case 5:
+					case 6:
+					case 7:
+					case 8:
+					case 9:
+					case 10:
+					case 12:
+						if (lp->dialstate <= 6) {
+							dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+							isdn_info_update();
+						} else
+							dev->rx_netdev[idx] = p;
+						lp->dialstate = 0;
+						isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
+						if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+							isdn_net_ciscohdlck_connected(lp);
+						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
+							if (lp->master) { /* is lp a slave? */
+								isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
+								isdn_net_add_to_bundle(nd, lp);
+							}
+						}
+						printk(KERN_INFO "isdn_net: %s connected\n", lp->name);
+						/* If first Chargeinfo comes before B-Channel connect,
+						 * we correct the timestamp here.
+						 */
+						lp->chargetime = jiffies;
+
+						/* reset dial-timeout */
+						lp->dialstarted = 0;
+						lp->dialwait_timer = 0;
+
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							isdn_ppp_wakeup_daemon(lp);
+#endif
+#ifdef CONFIG_ISDN_X25
+						/* try if there are generic concap receiver routines */
+						if( pops )
+							if( pops->connect_ind)
+								pops->connect_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+						/* ppp needs to do negotiations first */
+						if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+							isdn_net_device_wake_queue(lp);
+						return 1;
+				}
+				break;
+			case ISDN_STAT_NODCH:
+				/* No D-Channel avail. */
+				if (lp->dialstate == 4) {
+					lp->dialstate--;
+					return 1;
+				}
+				break;
+			case ISDN_STAT_CINF:
+				/* Charge-info from TelCo. Calculate interval between
+				 * charge-infos and set timestamp for last info for
+				 * usage by isdn_net_autohup()
+				 */
+				lp->charge++;
+				if (lp->hupflags & ISDN_HAVECHARGE) {
+					lp->hupflags &= ~ISDN_WAITCHARGE;
+					lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
+				}
+				if (lp->hupflags & ISDN_WAITCHARGE)
+					lp->hupflags |= ISDN_HAVECHARGE;
+				lp->chargetime = jiffies;
+				printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %lu\n",
+				       lp->name, lp->chargetime);
+				return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Perform dialout for net-interfaces and timeout-handling for
+ * D-Channel-up and B-Channel-up Messages.
+ * This function is initially called from within isdn_net_start_xmit() or
+ * or isdn_net_find_icall() after initializing the dialstate for an
+ * interface. If further calls are needed, the function schedules itself
+ * for a timer-callback via isdn_timer_function().
+ * The dialstate is also affected by incoming status-messages from
+ * the ISDN-Channel which are handled in isdn_net_stat_callback() above.
+ */
+void
+isdn_net_dial(void)
+{
+	isdn_net_dev *p = dev->netdev;
+	int anymore = 0;
+	int i;
+	isdn_ctrl cmd;
+        u_char *phone_number;
+
+	while (p) {
+		isdn_net_local *lp = p->local;
+
+#ifdef ISDN_DEBUG_NET_DIAL
+		if (lp->dialstate)
+			printk(KERN_DEBUG "%s: dialstate=%d\n", lp->name, lp->dialstate);
+#endif
+		switch (lp->dialstate) {
+			case 0:
+				/* Nothing to do for this interface */
+				break;
+			case 1:
+				/* Initiate dialout. Set phone-number-pointer to first number
+				 * of interface.
+				 */
+				lp->dial = lp->phone[1];
+				if (!lp->dial) {
+					printk(KERN_WARNING "%s: phone number deleted?\n",
+					       lp->name);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
+				anymore = 1;
+
+				if(lp->dialtimeout > 0)
+					if(lp->dialstarted == 0 || time_after(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) {
+						lp->dialstarted = jiffies;
+						lp->dialwait_timer = 0;
+					}
+
+				lp->dialstate++;
+				/* Fall through */
+			case 2:
+				/* Prepare dialing. Clear EAZ, then set EAZ. */
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				cmd.command = ISDN_CMD_CLREAZ;
+				isdn_command(&cmd);
+				sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver));
+				cmd.command = ISDN_CMD_SETEAZ;
+				isdn_command(&cmd);
+				lp->dialretry = 0;
+				anymore = 1;
+				lp->dialstate++;
+				/* Fall through */
+			case 3:
+				/* Setup interface, dial current phone-number, switch to next number.
+				 * If list of phone-numbers is exhausted, increment
+				 * retry-counter.
+				 */
+				if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) {
+					char *s;
+					if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+						s = "dial suppressed: isdn system stopped";
+					else
+						s = "dial suppressed: dialmode `off'";
+					isdn_net_unreachable(&p->dev, NULL, s);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				if (!lp->dial) {
+					printk(KERN_WARNING "%s: phone number deleted?\n",
+					       lp->name);
+					isdn_net_hangup(&p->dev);
+					break;
+				}
+				if (!strncmp(lp->dial->num, "LEASED", strlen("LEASED"))) {
+					lp->dialstate = 4;
+					printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
+				} else {
+					if(lp->dialtimeout > 0)
+						if (time_after(jiffies, lp->dialstarted + lp->dialtimeout)) {
+							lp->dialwait_timer = jiffies + lp->dialwait;
+							lp->dialstarted = 0;
+							isdn_net_unreachable(&p->dev, NULL, "dial: timed out");
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+
+					cmd.driver = lp->isdn_device;
+					cmd.command = ISDN_CMD_DIAL;
+					cmd.parm.setup.si2 = 0;
+
+                                        /* check for DOV */
+                                        phone_number = lp->dial->num;
+                                        if ((*phone_number == 'v') ||
+					    (*phone_number == 'V')) { /* DOV call */
+                                                cmd.parm.setup.si1 = 1;
+                                        } else { /* DATA call */
+                                                cmd.parm.setup.si1 = 7;
+					}
+
+					strcpy(cmd.parm.setup.phone, phone_number);
+					/*
+					 * Switch to next number or back to start if at end of list.
+					 */
+					if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
+						lp->dial = lp->phone[1];
+						lp->dialretry++;
+
+						if (lp->dialretry > lp->dialmax) {
+							if (lp->dialtimeout == 0) {
+								lp->dialwait_timer = jiffies + lp->dialwait;
+								lp->dialstarted = 0;
+								isdn_net_unreachable(&p->dev, NULL, "dial: tried all numbers dialmax times");
+							}
+							isdn_net_hangup(&p->dev);
+							break;
+						}
+					}
+					sprintf(cmd.parm.setup.eazmsn, "%s",
+						isdn_map_eaz2msn(lp->msn, cmd.driver));
+					i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel);
+					if (i >= 0) {
+						strcpy(dev->num[i], cmd.parm.setup.phone);
+						dev->usage[i] |= ISDN_USAGE_OUTGOING;
+						isdn_info_update();
+					}
+					printk(KERN_INFO "%s: dialing %d %s... %s\n", lp->name,
+					       lp->dialretry, cmd.parm.setup.phone,
+					       (cmd.parm.setup.si1 == 1) ? "DOV" : "");
+					lp->dtimer = 0;
+#ifdef ISDN_DEBUG_NET_DIAL
+					printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device,
+					       lp->isdn_channel);
+#endif
+					isdn_command(&cmd);
+				}
+				lp->huptimer = 0;
+				lp->outgoing = 1;
+				if (lp->chargeint) {
+					lp->hupflags |= ISDN_HAVECHARGE;
+					lp->hupflags &= ~ISDN_WAITCHARGE;
+				} else {
+					lp->hupflags |= ISDN_WAITCHARGE;
+					lp->hupflags &= ~ISDN_HAVECHARGE;
+				}
+				anymore = 1;
+				lp->dialstate =
+				    (lp->cbdelay &&
+				     (lp->flags & ISDN_NET_CBOUT)) ? 12 : 4;
+				break;
+			case 4:
+				/* Wait for D-Channel-connect.
+				 * If timeout, switch back to state 3.
+				 * Dialmax-handling moved to state 3.
+				 */
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					lp->dialstate = 3;
+				anymore = 1;
+				break;
+			case 5:
+				/* Got D-Channel-Connect, send B-Channel-request */
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				anymore = 1;
+				lp->dtimer = 0;
+				lp->dialstate++;
+				isdn_command(&cmd);
+				break;
+			case 6:
+				/* Wait for B- or D-Channel-connect. If timeout,
+				 * switch back to state 3.
+				 */
+#ifdef ISDN_DEBUG_NET_DIAL
+				printk(KERN_DEBUG "dialtimer2: %d\n", lp->dtimer);
+#endif
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					lp->dialstate = 3;
+				anymore = 1;
+				break;
+			case 7:
+				/* Got incoming Call, setup L2 and L3 protocols,
+				 * then wait for D-Channel-connect
+				 */
+#ifdef ISDN_DEBUG_NET_DIAL
+				printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
+#endif
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL2;
+				cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
+				isdn_command(&cmd);
+				cmd.driver = lp->isdn_device;
+				cmd.command = ISDN_CMD_SETL3;
+				cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+				isdn_command(&cmd);
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15)
+					isdn_net_hangup(&p->dev);
+				else {
+					anymore = 1;
+					lp->dialstate++;
+				}
+				break;
+			case 9:
+				/* Got incoming D-Channel-Connect, send B-Channel-request */
+				cmd.driver = lp->isdn_device;
+				cmd.arg = lp->isdn_channel;
+				cmd.command = ISDN_CMD_ACCEPTB;
+				isdn_command(&cmd);
+				anymore = 1;
+				lp->dtimer = 0;
+				lp->dialstate++;
+				break;
+			case 8:
+			case 10:
+				/*  Wait for B- or D-channel-connect */
+#ifdef ISDN_DEBUG_NET_DIAL
+				printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
+#endif
+				if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+					isdn_net_hangup(&p->dev);
+				else
+					anymore = 1;
+				break;
+			case 11:
+				/* Callback Delay */
+				if (lp->dtimer++ > lp->cbdelay)
+					lp->dialstate = 1;
+				anymore = 1;
+				break;
+			case 12:
+				/* Remote does callback. Hangup after cbdelay, then wait for incoming
+				 * call (in state 4).
+				 */
+				if (lp->dtimer++ > lp->cbdelay)
+				{
+					printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name);
+					lp->dtimer = 0;
+					lp->dialstate = 4;
+					cmd.driver = lp->isdn_device;
+					cmd.command = ISDN_CMD_HANGUP;
+					cmd.arg = lp->isdn_channel;
+					isdn_command(&cmd);
+					isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+				}
+				anymore = 1;
+				break;
+			default:
+				printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+				       lp->dialstate, lp->name);
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore);
+}
+
+/*
+ * Perform hangup for a net-interface.
+ */
+void
+isdn_net_hangup(struct net_device *d)
+{
+	isdn_net_local *lp = (isdn_net_local *) d->priv;
+	isdn_ctrl cmd;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto *cprot = lp->netdev->cprot;
+	struct concap_proto_ops *pops = cprot ? cprot->pops : NULL;
+#endif
+
+	if (lp->flags & ISDN_NET_CONNECTED) {
+		if (lp->slave != NULL) {
+			isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
+			if (slp->flags & ISDN_NET_CONNECTED) {
+				printk(KERN_INFO
+					"isdn_net: hang up slave %s before %s\n",
+					slp->name, lp->name);
+				isdn_net_hangup(lp->slave);
+			}
+		}
+		printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
+#ifdef CONFIG_ISDN_PPP
+		if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+			isdn_ppp_free(lp);
+#endif
+		isdn_net_lp_disconnected(lp);
+#ifdef CONFIG_ISDN_X25
+		/* try if there are generic encap protocol
+		   receiver routines and signal the closure of
+		   the link */
+		if( pops && pops -> disconn_ind )
+		  pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+
+		cmd.driver = lp->isdn_device;
+		cmd.command = ISDN_CMD_HANGUP;
+		cmd.arg = lp->isdn_channel;
+		isdn_command(&cmd);
+		printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge);
+		isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+	}
+	isdn_net_unbind_channel(lp);
+}
+
+typedef struct {
+	unsigned short source;
+	unsigned short dest;
+} ip_ports;
+
+static void
+isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
+{
+	u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
+	unsigned short proto = ntohs(skb->protocol);
+	int data_ofs;
+	ip_ports *ipp;
+	char addinfo[100];
+
+	addinfo[0] = '\0';
+	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
+	if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
+		/* fall back to old isdn_net_log_packet method() */
+		char * buf = skb->data;
+
+		printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->name);
+		p = buf;
+		proto = ETH_P_IP;
+		switch (lp->p_encap) {
+			case ISDN_NET_ENCAP_IPTYP:
+				proto = ntohs(*(unsigned short *) &buf[0]);
+				p = &buf[2];
+				break;
+			case ISDN_NET_ENCAP_ETHER:
+				proto = ntohs(*(unsigned short *) &buf[12]);
+				p = &buf[14];
+				break;
+			case ISDN_NET_ENCAP_CISCOHDLC:
+				proto = ntohs(*(unsigned short *) &buf[2]);
+				p = &buf[4];
+				break;
+#ifdef CONFIG_ISDN_PPP
+			case ISDN_NET_ENCAP_SYNCPPP:
+				proto = ntohs(skb->protocol);
+				p = &buf[IPPP_MAX_HEADER];
+				break;
+#endif
+		}
+	}
+	data_ofs = ((p[0] & 15) * 4);
+	switch (proto) {
+		case ETH_P_IP:
+			switch (p[9]) {
+				case 1:
+					strcpy(addinfo, " ICMP");
+					break;
+				case 2:
+					strcpy(addinfo, " IGMP");
+					break;
+				case 4:
+					strcpy(addinfo, " IPIP");
+					break;
+				case 6:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 8:
+					strcpy(addinfo, " EGP");
+					break;
+				case 12:
+					strcpy(addinfo, " PUP");
+					break;
+				case 17:
+					ipp = (ip_ports *) (&p[data_ofs]);
+					sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+						ntohs(ipp->dest));
+					break;
+				case 22:
+					strcpy(addinfo, " IDP");
+					break;
+			}
+			printk(KERN_INFO
+				"OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+
+			       p[12], p[13], p[14], p[15],
+			       p[16], p[17], p[18], p[19],
+			       addinfo);
+			break;
+		case ETH_P_ARP:
+			printk(KERN_INFO
+				"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+			       p[14], p[15], p[16], p[17],
+			       p[24], p[25], p[26], p[27]);
+			break;
+	}
+}
+
+/*
+ * this function is used to send supervisory data, i.e. data which was
+ * not received from the network layer, but e.g. frames from ipppd, CCP
+ * reset frames etc.
+ */
+void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb)
+{
+	if (in_irq()) {
+		// we can't grab the lock from irq context, 
+		// so we just queue the packet
+		skb_queue_tail(&lp->super_tx_queue, skb);
+		schedule_work(&lp->tqueue);
+		return;
+	}
+
+	spin_lock_bh(&lp->xmit_lock);
+	if (!isdn_net_lp_busy(lp)) {
+		isdn_net_writebuf_skb(lp, skb);
+	} else {
+		skb_queue_tail(&lp->super_tx_queue, skb);
+	}
+	spin_unlock_bh(&lp->xmit_lock);
+}
+
+/*
+ * called from tq_immediate
+ */
+static void isdn_net_softint(void *private)
+{
+	isdn_net_local *lp = private;
+	struct sk_buff *skb;
+
+	spin_lock_bh(&lp->xmit_lock);
+	while (!isdn_net_lp_busy(lp)) {
+		skb = skb_dequeue(&lp->super_tx_queue);
+		if (!skb)
+			break;
+		isdn_net_writebuf_skb(lp, skb);                                
+	}
+	spin_unlock_bh(&lp->xmit_lock);
+}
+
+/* 
+ * all frames sent from the (net) LL to a HL driver should go via this function
+ * it's serialized by the caller holding the lp->xmit_lock spinlock
+ */
+void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb)
+{
+	int ret;
+	int len = skb->len;     /* save len */
+
+	/* before obtaining the lock the caller should have checked that
+	   the lp isn't busy */
+	if (isdn_net_lp_busy(lp)) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		goto error;
+	}
+
+	if (!(lp->flags & ISDN_NET_CONNECTED)) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		goto error;
+	}
+	ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
+	if (ret != len) {
+		/* we should never get here */
+		printk(KERN_WARNING "%s: HL driver queue full\n", lp->name);
+		goto error;
+	}
+	
+	lp->transcount += len;
+	isdn_net_inc_frame_cnt(lp);
+	return;
+
+ error:
+	dev_kfree_skb(skb);
+	lp->stats.tx_errors++;
+
+}
+
+
+/*
+ *  Helper function for isdn_net_start_xmit.
+ *  When called, the connection is already established.
+ *  Based on cps-calculation, check if device is overloaded.
+ *  If so, and if a slave exists, trigger dialing for it.
+ *  If any slave is online, deliver packets using a simple round robin
+ *  scheme.
+ *
+ *  Return: 0 on success, !0 on failure.
+ */
+
+static int
+isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
+{
+	isdn_net_dev *nd;
+	isdn_net_local *slp;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int retv = 0;
+
+	if (((isdn_net_local *) (ndev->priv))->master) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	/* For the other encaps the header has already been built */
+#ifdef CONFIG_ISDN_PPP
+	if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+		return isdn_ppp_xmit(skb, ndev);
+	}
+#endif
+	nd = ((isdn_net_local *) ndev->priv)->netdev;
+	lp = isdn_net_get_locked_lp(nd);
+	if (!lp) {
+		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
+		return 1;
+	}
+	/* we have our lp locked from now on */
+
+	/* Reset hangup-timeout */
+	lp->huptimer = 0; // FIXME?
+	isdn_net_writebuf_skb(lp, skb);
+	spin_unlock_bh(&lp->xmit_lock);
+
+	/* the following stuff is here for backwards compatibility.
+	 * in future, start-up and hangup of slaves (based on current load)
+	 * should move to userspace and get based on an overall cps
+	 * calculation
+	 */
+	if (lp->cps > lp->triggercps) {
+		if (lp->slave) {
+			if (!lp->sqfull) {
+				/* First time overload: set timestamp only */
+				lp->sqfull = 1;
+				lp->sqfull_stamp = jiffies;
+			} else {
+				/* subsequent overload: if slavedelay exceeded, start dialing */
+				if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) {
+					slp = lp->slave->priv;
+					if (!(slp->flags & ISDN_NET_CONNECTED)) {
+						isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+					}
+				}
+			}
+		}
+	} else {
+		if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) {
+			lp->sqfull = 0;
+		}
+		/* this is a hack to allow auto-hangup for slaves on moderate loads */
+		nd->queue = nd->local;
+	}
+
+	return retv;
+
+}
+
+static void
+isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	if (!skb)
+		return;
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+		if (pullsize > 0) {
+			printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
+			skb_pull(skb, pullsize);
+		}
+	}
+}
+
+
+void isdn_net_tx_timeout(struct net_device * ndev)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+	printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate);
+	if (!lp->dialstate){
+		lp->stats.tx_errors++;
+                /*
+		 * There is a certain probability that this currently
+		 * works at all because if we always wake up the interface,
+		 * then upper layer will try to send the next packet
+		 * immediately. And then, the old clean_up logic in the
+		 * driver will hopefully continue to work as it used to do.
+		 *
+		 * This is rather primitive right know, we better should
+		 * clean internal queues here, in particular for multilink and
+		 * ppp, and reset HL driver's channel, too.   --HE
+		 *
+		 * actually, this may not matter at all, because ISDN hardware
+		 * should not see transmitter hangs at all IMO
+		 * changed KERN_DEBUG to KERN_WARNING to find out if this is 
+		 * ever called   --KG
+		 */
+	}
+	ndev->trans_start = jiffies;
+	netif_wake_queue(ndev);
+}
+
+/*
+ * Try sending a packet.
+ * If this interface isn't connected to a ISDN-Channel, find a free channel,
+ * and start dialing.
+ */
+static int
+isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto * cprot = lp -> netdev -> cprot;
+/* At this point hard_start_xmit() passes control to the encapsulation
+   protocol (if present).
+   For X.25 auto-dialing is completly bypassed because:
+   - It does not conform with the semantics of a reliable datalink
+     service as needed by X.25 PLP.
+   - I don't want that the interface starts dialing when the network layer
+     sends a message which requests to disconnect the lapb link (or if it
+     sends any other message not resulting in data transmission).
+   Instead, dialing will be initiated by the encapsulation protocol entity
+   when a dl_establish request is received from the upper layer.
+*/
+	if (cprot && cprot -> pops) {
+		int ret = cprot -> pops -> encap_and_xmit ( cprot , skb);
+
+		if (ret)
+			netif_stop_queue(ndev);
+		return ret;
+	} else
+#endif
+	/* auto-dialing xmit function */
+	{
+#ifdef ISDN_DEBUG_NET_DUMP
+		u_char *buf;
+#endif
+		isdn_net_adjust_hdr(skb, ndev);
+#ifdef ISDN_DEBUG_NET_DUMP
+		buf = skb->data;
+		isdn_dumppkt("S:", buf, skb->len, 40);
+#endif
+
+		if (!(lp->flags & ISDN_NET_CONNECTED)) {
+			int chi;
+			/* only do autodial if allowed by config */
+			if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
+				isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
+				dev_kfree_skb(skb);
+				return 0;
+			}
+			if (lp->phone[1]) {
+				ulong flags;
+
+				if(lp->dialwait_timer <= 0)
+					if(lp->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait))
+						lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
+
+				if(lp->dialwait_timer > 0) {
+					if(time_before(jiffies, lp->dialwait_timer)) {
+						isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+						dev_kfree_skb(skb);
+						return 0;
+					} else
+						lp->dialwait_timer = 0;
+				}
+				/* Grab a free ISDN-Channel */
+				spin_lock_irqsave(&dev->lock, flags);
+				if (((chi =
+				     isdn_get_free_channel(
+					 		ISDN_USAGE_NET,
+							lp->l2_proto,
+							lp->l3_proto,
+							lp->pre_device,
+						 	lp->pre_channel,
+							lp->msn)
+							) < 0) &&
+					((chi =
+				     isdn_get_free_channel(
+					 		ISDN_USAGE_NET,
+							lp->l2_proto,
+							lp->l3_proto,
+							lp->pre_device,
+							lp->pre_channel^1,
+							lp->msn)
+							) < 0)) {
+					spin_unlock_irqrestore(&dev->lock, flags);
+					isdn_net_unreachable(ndev, skb,
+							   "No channel");
+					dev_kfree_skb(skb);
+					return 0;
+				}
+				/* Log packet, which triggered dialing */
+				if (dev->net_verbose)
+					isdn_net_log_skb(skb, lp);
+				lp->dialstate = 1;
+				/* Connect interface with channel */
+				isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+				if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+					/* no 'first_skb' handling for syncPPP */
+					if (isdn_ppp_bind(lp) < 0) {
+						dev_kfree_skb(skb);
+						isdn_net_unbind_channel(lp);
+						spin_unlock_irqrestore(&dev->lock, flags);
+						return 0;	/* STN (skb to nirvana) ;) */
+					}
+#ifdef CONFIG_IPPP_FILTER
+					if (isdn_ppp_autodial_filter(skb, lp)) {
+						isdn_ppp_free(lp);
+						isdn_net_unbind_channel(lp);
+						spin_unlock_irqrestore(&dev->lock, flags);
+						isdn_net_unreachable(ndev, skb, "dial rejected: packet filtered");
+						dev_kfree_skb(skb);
+						return 0;
+					}
+#endif
+					spin_unlock_irqrestore(&dev->lock, flags);
+					isdn_net_dial();	/* Initiate dialing */
+					netif_stop_queue(ndev);
+					return 1;	/* let upper layer requeue skb packet */
+				}
+#endif
+				/* Initiate dialing */
+				spin_unlock_irqrestore(&dev->lock, flags);
+				isdn_net_dial();
+				isdn_net_device_stop_queue(lp);
+				return 1;
+			} else {
+				isdn_net_unreachable(ndev, skb,
+						     "No phone number");
+				dev_kfree_skb(skb);
+				return 0;
+			}
+		} else {
+			/* Device is connected to an ISDN channel */ 
+			ndev->trans_start = jiffies;
+			if (!lp->dialstate) {
+				/* ISDN connection is established, try sending */
+				int ret;
+				ret = (isdn_net_xmit(ndev, skb));
+				if(ret) netif_stop_queue(ndev);
+				return ret;
+			} else
+				netif_stop_queue(ndev);
+		}
+	}
+	return 1;
+}
+
+/*
+ * Shutdown a net-interface.
+ */
+static int
+isdn_net_close(struct net_device *dev)
+{
+	struct net_device *p;
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto * cprot =
+		( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+	/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
+#endif
+
+#ifdef CONFIG_ISDN_X25
+	if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );
+#endif
+	netif_stop_queue(dev);
+	if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+		/* If this interface has slaves, stop them also */
+		while (p) {
+#ifdef CONFIG_ISDN_X25
+			cprot = ( (isdn_net_local *) p->priv )
+				-> netdev -> cprot;
+			if( cprot && cprot -> pops )
+				cprot -> pops -> close( cprot );
+#endif
+			isdn_net_hangup(p);
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+	}
+	isdn_net_hangup(dev);
+	isdn_unlock_drivers();
+	return 0;
+}
+
+/*
+ * Get statistics
+ */
+static struct net_device_stats *
+isdn_net_get_stats(struct net_device *dev)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	return &lp->stats;
+}
+
+/*      This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
+ *      instead of dev->hard_header_len off. This is done because the
+ *      lowlevel-driver has already pulled off its stuff when we get
+ *      here and this routine only gets called with p_encap == ETHER.
+ *      Determine the packet's protocol ID. The rule here is that we
+ *      assume 802.3 if the type field is short enough to be a length.
+ *      This is normal practice and works for any 'now in use' protocol.
+ */
+
+static unsigned short
+isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ethhdr *eth;
+	unsigned char *rawp;
+
+	skb->mac.raw = skb->data;
+	skb_pull(skb, ETH_HLEN);
+	eth = eth_hdr(skb);
+
+	if (*eth->h_dest & 1) {
+		if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+			skb->pkt_type = PACKET_BROADCAST;
+		else
+			skb->pkt_type = PACKET_MULTICAST;
+	}
+	/*
+	 *      This ALLMULTI check should be redundant by 1.4
+	 *      so don't forget to remove it.
+	 */
+
+	else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) {
+		if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
+			skb->pkt_type = PACKET_OTHERHOST;
+	}
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+
+	rawp = skb->data;
+
+	/*
+	 *      This is a magic hack to spot IPX packets. Older Novell breaks
+	 *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *      won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *) rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+	/*
+	 *      Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
+}
+
+
+/* 
+ * CISCO HDLC keepalive specific stuff
+ */
+static struct sk_buff*
+isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
+{
+	unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+	struct sk_buff *skb;
+
+	skb = alloc_skb(hl + len, GFP_ATOMIC);
+	if (skb)
+		skb_reserve(skb, hl);
+	else 
+		printk("isdn out of mem at %s:%d!\n", __FILE__, __LINE__);
+	return skb;
+}
+
+/* cisco hdlck device private ioctls */
+int
+isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+	unsigned long len = 0;
+	unsigned long expires = 0;
+	int tmp = 0;
+	int period = lp->cisco_keepalive_period;
+	s8 debserint = lp->cisco_debserint;
+	int rc = 0;
+
+	if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
+		return -EINVAL;
+
+	switch (cmd) {
+		/* get/set keepalive period */
+		case SIOCGKEEPPERIOD:
+			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+			if (copy_to_user(ifr->ifr_data,
+				&lp->cisco_keepalive_period, len))
+				rc = -EFAULT;
+			break;
+		case SIOCSKEEPPERIOD:
+			tmp = lp->cisco_keepalive_period;
+			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+			if (copy_from_user(&period, ifr->ifr_data, len))
+				rc = -EFAULT;
+			if ((period > 0) && (period <= 32767))
+				lp->cisco_keepalive_period = period;
+			else
+				rc = -EINVAL;
+			if (!rc && (tmp != lp->cisco_keepalive_period)) {
+				expires = (unsigned long)(jiffies +
+					lp->cisco_keepalive_period * HZ);
+				mod_timer(&lp->cisco_timer, expires);
+				printk(KERN_INFO "%s: Keepalive period set "
+					"to %d seconds.\n",
+					lp->name, lp->cisco_keepalive_period);
+			}
+			break;
+
+		/* get/set debugging */
+		case SIOCGDEBSERINT:
+			len = (unsigned long)sizeof(lp->cisco_debserint);
+			if (copy_to_user(ifr->ifr_data,
+				&lp->cisco_debserint, len))
+				rc = -EFAULT;
+			break;
+		case SIOCSDEBSERINT:
+			len = (unsigned long)sizeof(lp->cisco_debserint);
+			if (copy_from_user(&debserint,
+				ifr->ifr_data, len))
+				rc = -EFAULT;
+			if ((debserint >= 0) && (debserint <= 64))
+				lp->cisco_debserint = debserint;
+			else
+				rc = -EINVAL;
+			break;
+
+		default:
+			rc = -EINVAL;
+			break;
+	}
+	return (rc);
+}
+
+/* called via cisco_timer.function */
+static void
+isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
+{
+	isdn_net_local *lp = (isdn_net_local *) data;
+	struct sk_buff *skb;
+	unsigned char *p;
+	unsigned long last_cisco_myseq = lp->cisco_myseq;
+	int myseq_diff = 0;
+
+	if (!(lp->flags & ISDN_NET_CONNECTED) || lp->dialstate) {
+		printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+		return;
+	}
+	lp->cisco_myseq++;
+
+	myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen);
+	if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) {
+		/* line up -> down */
+		lp->cisco_line_state = 0;
+		printk (KERN_WARNING
+				"UPDOWN: Line protocol on Interface %s,"
+				" changed state to down\n", lp->name);
+		/* should stop routing higher-level data accross */
+	} else if ((!lp->cisco_line_state) &&
+		(myseq_diff >= 0) && (myseq_diff <= 2)) {
+		/* line down -> up */
+		lp->cisco_line_state = 1;
+		printk (KERN_WARNING
+				"UPDOWN: Line protocol on Interface %s,"
+				" changed state to up\n", lp->name);
+		/* restart routing higher-level data accross */
+	}
+
+	if (lp->cisco_debserint)
+		printk (KERN_DEBUG "%s: HDLC "
+			"myseq %lu, mineseen %lu%c, yourseen %lu, %s\n",
+			lp->name, last_cisco_myseq, lp->cisco_mineseen,
+			((last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040),
+			lp->cisco_yourseq,
+			((lp->cisco_line_state) ? "line up" : "line down"));
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp keepalive */
+	p += put_u32(p, CISCO_SLARP_KEEPALIVE);
+	p += put_u32(p, lp->cisco_myseq);
+	p += put_u32(p, lp->cisco_yourseq);
+	p += put_u16(p, 0xffff); // reliablity, always 0xffff
+
+	isdn_net_write_super(lp, skb);
+
+	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+	
+	add_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp request */
+	p += put_u32(p, CISCO_SLARP_REQUEST);
+	p += put_u32(p, 0); // address
+	p += put_u32(p, 0); // netmask
+	p += put_u16(p, 0); // unused
+
+	isdn_net_write_super(lp, skb);
+}
+
+static void 
+isdn_net_ciscohdlck_connected(isdn_net_local *lp)
+{
+	lp->cisco_myseq = 0;
+	lp->cisco_mineseen = 0;
+	lp->cisco_yourseq = 0;
+	lp->cisco_keepalive_period = ISDN_TIMER_KEEPINT;
+	lp->cisco_last_slarp_in = 0;
+	lp->cisco_line_state = 0;
+	lp->cisco_debserint = 0;
+
+	/* send slarp request because interface/seq.no.s reset */
+	isdn_net_ciscohdlck_slarp_send_request(lp);
+
+	init_timer(&lp->cisco_timer);
+	lp->cisco_timer.data = (unsigned long) lp;
+	lp->cisco_timer.function = isdn_net_ciscohdlck_slarp_send_keepalive;
+	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+	add_timer(&lp->cisco_timer);
+}
+
+static void 
+isdn_net_ciscohdlck_disconnected(isdn_net_local *lp)
+{
+	del_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	struct in_device *in_dev = NULL;
+	u32 addr = 0;		/* local ipv4 address */
+	u32 mask = 0;		/* local netmask */
+
+	if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
+		/* take primary(first) address of interface */
+		struct in_ifaddr *ifa = in_dev->ifa_list;
+		if (ifa != NULL) {
+			addr = ifa->ifa_local;
+			mask = ifa->ifa_mask;
+		}
+	}
+
+	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (!skb)
+		return;
+
+	p = skb_put(skb, 4 + 14);
+
+	/* cisco header */
+	p += put_u8 (p, CISCO_ADDR_UNICAST);
+	p += put_u8 (p, CISCO_CTRL);
+	p += put_u16(p, CISCO_TYPE_SLARP);
+
+	/* slarp reply, send own ip/netmask; if values are nonsense remote
+	 * should think we are unable to provide it with an address via SLARP */
+	p += put_u32(p, CISCO_SLARP_REPLY);
+	p += put_u32(p, addr);	// address
+	p += put_u32(p, mask);	// netmask
+	p += put_u16(p, 0);	// unused
+
+	isdn_net_write_super(lp, skb);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
+{
+	unsigned char *p;
+	int period;
+	u32 code;
+	u32 my_seq, addr;
+	u32 your_seq, mask;
+	u32 local;
+	u16 unused;
+
+	if (skb->len < 14)
+		return;
+
+	p = skb->data;
+	p += get_u32(p, &code);
+	
+	switch (code) {
+	case CISCO_SLARP_REQUEST:
+		lp->cisco_yourseq = 0;
+		isdn_net_ciscohdlck_slarp_send_reply(lp);
+		break;
+	case CISCO_SLARP_REPLY:
+		addr = ntohl(*(u32 *)p);
+		mask = ntohl(*(u32 *)(p+4));
+		if (mask != 0xfffffffc)
+			goto slarp_reply_out;
+		if ((addr & 3) == 0 || (addr & 3) == 3)
+			goto slarp_reply_out;
+		local = addr ^ 3;
+		printk(KERN_INFO "%s: got slarp reply: "
+			"remote ip: %d.%d.%d.%d, "
+			"local ip: %d.%d.%d.%d "
+			"mask: %d.%d.%d.%d\n",
+		       lp->name,
+		       HIPQUAD(addr),
+		       HIPQUAD(local),
+		       HIPQUAD(mask));
+		break;
+  slarp_reply_out:
+		 printk(KERN_INFO "%s: got invalid slarp "
+				 "reply (%d.%d.%d.%d/%d.%d.%d.%d) "
+				 "- ignored\n", lp->name,
+				 HIPQUAD(addr), HIPQUAD(mask));
+		break;
+	case CISCO_SLARP_KEEPALIVE:
+		period = (int)((jiffies - lp->cisco_last_slarp_in
+				+ HZ/2 - 1) / HZ);
+		if (lp->cisco_debserint &&
+				(period != lp->cisco_keepalive_period) &&
+				lp->cisco_last_slarp_in) {
+			printk(KERN_DEBUG "%s: Keepalive period mismatch - "
+				"is %d but should be %d.\n",
+				lp->name, period, lp->cisco_keepalive_period);
+		}
+		lp->cisco_last_slarp_in = jiffies;
+		p += get_u32(p, &my_seq);
+		p += get_u32(p, &your_seq);
+		p += get_u16(p, &unused);
+		lp->cisco_yourseq = my_seq;
+		lp->cisco_mineseen = your_seq;
+		break;
+	}
+}
+
+static void
+isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb)
+{
+	unsigned char *p;
+ 	u8 addr;
+ 	u8 ctrl;
+ 	u16 type;
+	
+	if (skb->len < 4)
+		goto out_free;
+
+	p = skb->data;
+	p += get_u8 (p, &addr);
+	p += get_u8 (p, &ctrl);
+	p += get_u16(p, &type);
+	skb_pull(skb, 4);
+	
+	if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) {
+		printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n",
+		       lp->name, addr);
+		goto out_free;
+	}
+	if (ctrl != CISCO_CTRL) {
+		printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n",
+		       lp->name, ctrl);
+		goto out_free;
+	}
+
+	switch (type) {
+	case CISCO_TYPE_SLARP:
+		isdn_net_ciscohdlck_slarp_in(lp, skb);
+		goto out_free;
+	case CISCO_TYPE_CDP:
+		if (lp->cisco_debserint)
+			printk(KERN_DEBUG "%s: Received CDP packet. use "
+				"\"no cdp enable\" on cisco.\n", lp->name);
+		goto out_free;
+	default:
+		/* no special cisco protocol */
+		skb->protocol = htons(type);
+		netif_rx(skb);
+		return;
+	}
+
+ out_free:
+	kfree_skb(skb);
+}
+
+/*
+ * Got a packet from ISDN-Channel.
+ */
+static void
+isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
+{
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	isdn_net_local *olp = lp;	/* original 'lp' */
+#ifdef CONFIG_ISDN_X25
+	struct concap_proto *cprot = lp -> netdev -> cprot;
+#endif
+	lp->transcount += skb->len;
+
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes += skb->len;
+	if (lp->master) {
+		/* Bundling: If device is a slave-device, deliver to master, also
+		 * handle master's statistics and hangup-timeout
+		 */
+		ndev = lp->master;
+		lp = (isdn_net_local *) ndev->priv;
+		lp->stats.rx_packets++;
+		lp->stats.rx_bytes += skb->len;
+	}
+	skb->dev = ndev;
+	skb->input_dev = ndev;
+	skb->pkt_type = PACKET_HOST;
+	skb->mac.raw = skb->data;
+#ifdef ISDN_DEBUG_NET_DUMP
+	isdn_dumppkt("R:", skb->data, skb->len, 40);
+#endif
+	switch (lp->p_encap) {
+		case ISDN_NET_ENCAP_ETHER:
+			/* Ethernet over ISDN */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb->protocol = isdn_net_type_trans(skb, ndev);
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-frame (for ispa with -h1 option) */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_RAWIP:
+			/* RAW-IP without MAC-Header */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			isdn_net_ciscohdlck_receive(lp, skb);
+			return;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+			/* CISCO-HDLC IP with type field and  fake I-frame-header */
+			skb_pull(skb, 2);
+			/* Fall through */
+		case ISDN_NET_ENCAP_IPTYP:
+			/* IP with type field */
+			olp->huptimer = 0;
+			lp->huptimer = 0;
+			skb->protocol = *(unsigned short *) &(skb->data[0]);
+			skb_pull(skb, 2);
+			if (*(unsigned short *) skb->data == 0xFFFF)
+				skb->protocol = htons(ETH_P_802_3);
+			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/* huptimer is done in isdn_ppp_push_higher */
+			isdn_ppp_receive(lp->netdev, olp, skb);
+			return;
+#endif
+
+		default:
+#ifdef CONFIG_ISDN_X25
+		  /* try if there are generic sync_device receiver routines */
+			if(cprot) if(cprot -> pops)
+				if( cprot -> pops -> data_ind){
+					cprot -> pops -> data_ind(cprot,skb);
+					return;
+				};
+#endif /* CONFIG_ISDN_X25 */
+			printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+			       lp->name);
+			kfree_skb(skb);
+			return;
+	}
+
+	netif_rx(skb);
+	return;
+}
+
+/*
+ * A packet arrived via ISDN. Search interface-chain for a corresponding
+ * interface. If found, deliver packet to receiver-function and return 1,
+ * else return 0.
+ */
+int
+isdn_net_rcv_skb(int idx, struct sk_buff *skb)
+{
+	isdn_net_dev *p = dev->rx_netdev[idx];
+
+	if (p) {
+		isdn_net_local *lp = p->local;
+		if ((lp->flags & ISDN_NET_CONNECTED) &&
+		    (!lp->dialstate)) {
+			isdn_net_receive(&p->dev, skb);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+my_eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+	      void *daddr, void *saddr, unsigned len)
+{
+	struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN);
+
+	/*
+	 * Set the protocol type. For a packet of type ETH_P_802_3 we
+	 * put the length here instead. It is up to the 802.2 layer to
+	 * carry protocol information.
+	 */
+
+	if (type != ETH_P_802_3)
+		eth->h_proto = htons(type);
+	else
+		eth->h_proto = htons(len);
+
+	/*
+	 * Set the source hardware address.
+	 */
+	if (saddr)
+		memcpy(eth->h_source, saddr, dev->addr_len);
+	else
+		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+
+	/*
+	 * Anyway, the loopback-device should never use this function...
+	 */
+
+	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+		memset(eth->h_dest, 0, dev->addr_len);
+		return ETH_HLEN /*(dev->hard_header_len)*/;
+	}
+	if (daddr) {
+		memcpy(eth->h_dest, daddr, dev->addr_len);
+		return ETH_HLEN /*dev->hard_header_len*/;
+	}
+	return -ETH_HLEN /*dev->hard_header_len*/;
+}
+
+/*
+ *  build an header
+ *  depends on encaps that is being used.
+ */
+
+static int
+isdn_net_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
+		void *daddr, void *saddr, unsigned plen)
+{
+	isdn_net_local *lp = dev->priv;
+	unsigned char *p;
+	ushort len = 0;
+
+	switch (lp->p_encap) {
+		case ISDN_NET_ENCAP_ETHER:
+			len = my_eth_header(skb, dev, type, daddr, saddr, plen);
+			break;
+#ifdef CONFIG_ISDN_PPP
+		case ISDN_NET_ENCAP_SYNCPPP:
+			/* stick on a fake header to keep fragmentation code happy. */
+			len = IPPP_MAX_HEADER;
+			skb_push(skb,len);
+			break;
+#endif
+		case ISDN_NET_ENCAP_RAWIP:
+			printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+			len = 0;
+			break;
+		case ISDN_NET_ENCAP_IPTYP:
+			/* ethernet type field */
+			*((ushort *) skb_push(skb, 2)) = htons(type);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_UIHDLC:
+			/* HDLC with UI-Frames (for ispa with -h1 option) */
+			*((ushort *) skb_push(skb, 2)) = htons(0x0103);
+			len = 2;
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLC:
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			p = skb_push(skb, 4);
+			p += put_u8 (p, CISCO_ADDR_UNICAST);
+			p += put_u8 (p, CISCO_CTRL);
+			p += put_u16(p, type);
+			len = 4;
+			break;
+#ifdef CONFIG_ISDN_X25
+		default:
+		  /* try if there are generic concap protocol routines */
+			if( lp-> netdev -> cprot ){
+				printk(KERN_WARNING "isdn_net_header called with concap_proto!\n");
+				len = 0;
+				break;
+			}
+			break;
+#endif /* CONFIG_ISDN_X25 */
+	}
+	return len;
+}
+
+/* We don't need to send arp, because we have point-to-point connections. */
+static int
+isdn_net_rebuild_header(struct sk_buff *skb)
+{
+	struct net_device *dev = skb->dev;
+	isdn_net_local *lp = dev->priv;
+	int ret = 0;
+
+	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+		struct ethhdr *eth = (struct ethhdr *) skb->data;
+
+		/*
+		 *      Only ARP/IP is currently supported
+		 */
+
+		if (eth->h_proto != htons(ETH_P_IP)) {
+			printk(KERN_WARNING
+			       "isdn_net: %s don't know how to resolve type %d addresses?\n",
+			       dev->name, (int) eth->h_proto);
+			memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+			return 0;
+		}
+		/*
+		 *      Try to get ARP to resolve the header.
+		 */
+#ifdef CONFIG_INET
+		ret = arp_find(eth->h_dest, skb);
+#endif
+	}
+	return ret;
+}
+
+/*
+ * Interface-setup. (just after registering a new interface)
+ */
+static int
+isdn_net_init(struct net_device *ndev)
+{
+	ushort max_hlhdr_len = 0;
+	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+	int drvidx, i;
+
+	ether_setup(ndev);
+	lp->org_hhc = ndev->hard_header_cache;
+	lp->org_hcu = ndev->header_cache_update;
+
+	/* Setup the generic properties */
+
+	ndev->hard_header = NULL;
+	ndev->hard_header_cache = NULL;
+	ndev->header_cache_update = NULL;
+	ndev->mtu = 1500;
+	ndev->flags = IFF_NOARP|IFF_POINTOPOINT;
+	ndev->type = ARPHRD_ETHER;
+	ndev->addr_len = ETH_ALEN;
+
+	/* for clients with MPPP maybe higher values better */
+	ndev->tx_queue_len = 30;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		ndev->broadcast[i] = 0xff;
+
+	/* The ISDN-specific entries in the device structure. */
+	ndev->open = &isdn_net_open;
+	ndev->hard_start_xmit = &isdn_net_start_xmit;
+
+	/*
+	 *  up till binding we ask the protocol layer to reserve as much
+	 *  as we might need for HL layer
+	 */
+
+	for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+		if (dev->drv[drvidx])
+			if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
+				max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
+
+	ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
+	ndev->stop = &isdn_net_close;
+	ndev->get_stats = &isdn_net_get_stats;
+	ndev->rebuild_header = &isdn_net_rebuild_header;
+	ndev->do_ioctl = NULL;
+	return 0;
+}
+
+static void
+isdn_net_swapbind(int drvidx)
+{
+	isdn_net_dev *p;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx);
+#endif
+	p = dev->netdev;
+	while (p) {
+		if (p->local->pre_device == drvidx)
+			switch (p->local->pre_channel) {
+				case 0:
+					p->local->pre_channel = 1;
+					break;
+				case 1:
+					p->local->pre_channel = 0;
+					break;
+			}
+		p = (isdn_net_dev *) p->next;
+	}
+}
+
+static void
+isdn_net_swap_usage(int i1, int i2)
+{
+	int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE;
+	int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2);
+#endif
+	dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE;
+	dev->usage[i1] |= u2;
+	dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE;
+	dev->usage[i2] |= u1;
+	isdn_info_update();
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the interface-chain for an appropriate interface.
+ * If found, connect the interface to the ISDN-channel and initiate
+ * D- and B-Channel-setup. If secure-flag is set, accept only
+ * configured phone-numbers. If callback-flag is set, initiate
+ * callback-dialing.
+ *
+ * Return-Value: 0 = No appropriate interface for this call.
+ *               1 = Call accepted
+ *               2 = Reject call, wait cbdelay, then call back
+ *               3 = Reject call
+ *               4 = Wait cbdelay, then call back
+ *               5 = No appropriate interface for this call,
+ *                   would eventually match if CID was longer.
+ */
+
+int
+isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
+{
+	char *eaz;
+	int si1;
+	int si2;
+	int ematch;
+	int wret;
+	int swapped;
+	int sidx = 0;
+	u_long flags;
+	isdn_net_dev *p;
+	isdn_net_phone *n;
+	char nr[32];
+	char *my_eaz;
+
+	/* Search name in netdev-chain */
+	if (!setup->phone[0]) {
+		nr[0] = '0';
+		nr[1] = '\0';
+		printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
+	} else
+		strcpy(nr, setup->phone);
+	si1 = (int) setup->si1;
+	si2 = (int) setup->si2;
+	if (!setup->eazmsn[0]) {
+		printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
+		eaz = "0";
+	} else
+		eaz = setup->eazmsn;
+	if (dev->net_verbose > 1)
+		printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
+	/* Accept DATA and VOICE calls at this stage
+	 * local eaz is checked later for allowed call types
+	 */
+	if ((si1 != 7) && (si1 != 1)) {
+		if (dev->net_verbose > 1)
+			printk(KERN_INFO "isdn_net: Service-Indicator not 1 or 7, ignored\n");
+		return 0;
+	}
+	n = (isdn_net_phone *) 0;
+	p = dev->netdev;
+	ematch = wret = swapped = 0;
+#ifdef ISDN_DEBUG_NET_ICALL
+	printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
+		dev->usage[idx]);
+#endif
+	while (p) {
+		int matchret;
+		isdn_net_local *lp = p->local;
+
+		/* If last check has triggered as binding-swap, revert it */
+		switch (swapped) {
+			case 2:
+				isdn_net_swap_usage(idx, sidx);
+				/* fall through */
+			case 1:
+				isdn_net_swapbind(di);
+				break;
+		}
+		swapped = 0;
+                /* check acceptable call types for DOV */
+                my_eaz = isdn_map_eaz2msn(lp->msn, di);
+                if (si1 == 1) { /* it's a DOV call, check if we allow it */
+                        if (*my_eaz == 'v' || *my_eaz == 'V' ||
+			    *my_eaz == 'b' || *my_eaz == 'B')
+                                my_eaz++; /* skip to allow a match */
+                        else
+                                my_eaz = NULL; /* force non match */
+                } else { /* it's a DATA call, check if we allow it */
+                        if (*my_eaz == 'b' || *my_eaz == 'B')
+                                my_eaz++; /* skip to allow a match */
+                }
+                if (my_eaz)
+                        matchret = isdn_msncmp(eaz, my_eaz);
+                else
+                        matchret = 1;
+                if (!matchret)
+                        ematch = 1;
+
+		/* Remember if more numbers eventually can match */
+		if (matchret > wret)
+			wret = matchret;
+#ifdef ISDN_DEBUG_NET_ICALL
+		printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
+		       lp->name, lp->msn, lp->flags, lp->dialstate);
+#endif
+		if ((!matchret) &&                                        /* EAZ is matching   */
+		    (((!(lp->flags & ISDN_NET_CONNECTED)) &&              /* but not connected */
+		      (USG_NONE(dev->usage[idx]))) ||                     /* and ch. unused or */
+		     ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing        */
+		       (!(lp->flags & ISDN_NET_CALLBACK)))                /* but no callback   */
+		     )))
+			 {
+#ifdef ISDN_DEBUG_NET_ICALL
+			printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
+			       lp->pre_device, lp->pre_channel);
+#endif
+			if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
+				if ((lp->pre_channel != ch) ||
+				    (lp->pre_device != di)) {
+					/* Here we got a problem:
+					 * If using an ICN-Card, an incoming call is always signaled on
+					 * on the first channel of the card, if both channels are
+					 * down. However this channel may be bound exclusive. If the
+					 * second channel is free, this call should be accepted.
+					 * The solution is horribly but it runs, so what:
+					 * We exchange the exclusive bindings of the two channels, the
+					 * corresponding variables in the interface-structs.
+					 */
+					if (ch == 0) {
+						sidx = isdn_dc2minor(di, 1);
+#ifdef ISDN_DEBUG_NET_ICALL
+						printk(KERN_DEBUG "n_fi: ch is 0\n");
+#endif
+						if (USG_NONE(dev->usage[sidx])) {
+							/* Second Channel is free, now see if it is bound
+							 * exclusive too. */
+							if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
+#endif
+								/* Yes, swap bindings only, if the original
+								 * binding is bound to channel 1 of this driver */
+								if ((lp->pre_device == di) &&
+								    (lp->pre_channel == 1)) {
+									isdn_net_swapbind(di);
+									swapped = 1;
+								} else {
+									/* ... else iterate next device */
+									p = (isdn_net_dev *) p->next;
+									continue;
+								}
+							} else {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n");
+#endif
+								/* No, swap always and swap excl-usage also */
+								isdn_net_swap_usage(idx, sidx);
+								isdn_net_swapbind(di);
+								swapped = 2;
+							}
+							/* Now check for exclusive binding again */
+#ifdef ISDN_DEBUG_NET_ICALL
+							printk(KERN_DEBUG "n_fi: final check\n");
+#endif
+							if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) &&
+							    ((lp->pre_channel != ch) ||
+							     (lp->pre_device != di))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+								printk(KERN_DEBUG "n_fi: final check failed\n");
+#endif
+								p = (isdn_net_dev *) p->next;
+								continue;
+							}
+						}
+					} else {
+						/* We are already on the second channel, so nothing to do */
+#ifdef ISDN_DEBUG_NET_ICALL
+						printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
+#endif
+					}
+				}
+			}
+#ifdef ISDN_DEBUG_NET_ICALL
+			printk(KERN_DEBUG "n_fi: match2\n");
+#endif
+			n = lp->phone[0];
+			if (lp->flags & ISDN_NET_SECURE) {
+				while (n) {
+					if (!isdn_msncmp(nr, n->num))
+						break;
+					n = (isdn_net_phone *) n->next;
+				}
+			}
+			if (n || (!(lp->flags & ISDN_NET_SECURE))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+				printk(KERN_DEBUG "n_fi: match3\n");
+#endif
+				/* matching interface found */
+
+				/*
+				 * Is the state STOPPED?
+				 * If so, no dialin is allowed,
+				 * so reject actively.
+				 * */
+				if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+					printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/*
+				 * Is the interface up?
+				 * If not, reject the call actively.
+				 */
+				if (!isdn_net_device_started(p)) {
+					printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
+					       lp->name);
+					return 3;
+				}
+				/* Interface is up, now see if it's a slave. If so, see if
+				 * it's master and parent slave is online. If not, reject the call.
+				 */
+				if (lp->master) {
+					isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
+					printk(KERN_DEBUG "ICALLslv: %s\n", lp->name);
+					printk(KERN_DEBUG "master=%s\n", mlp->name);
+					if (mlp->flags & ISDN_NET_CONNECTED) {
+						printk(KERN_DEBUG "master online\n");
+						/* Master is online, find parent-slave (master if first slave) */
+						while (mlp->slave) {
+							if ((isdn_net_local *) mlp->slave->priv == lp)
+								break;
+							mlp = (isdn_net_local *) mlp->slave->priv;
+						}
+					} else
+						printk(KERN_DEBUG "master offline\n");
+					/* Found parent, if it's offline iterate next device */
+					printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
+					if (!(mlp->flags & ISDN_NET_CONNECTED)) {
+						p = (isdn_net_dev *) p->next;
+						continue;
+					}
+				} 
+				if (lp->flags & ISDN_NET_CALLBACK) {
+					int chi;
+					/*
+					 * Is the state MANUAL?
+					 * If so, no callback can be made,
+					 * so reject actively.
+					 * */
+					if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+						printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
+						       lp->name);
+						return 3;
+					}
+					printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
+					       lp->name, nr, eaz);
+					if (lp->phone[1]) {
+						/* Grab a free ISDN-Channel */
+						spin_lock_irqsave(&dev->lock, flags);
+						if ((chi = 
+							isdn_get_free_channel(
+								ISDN_USAGE_NET,
+								lp->l2_proto,
+								lp->l3_proto,
+							  	lp->pre_device,
+						 		lp->pre_channel,
+						 		lp->msn)
+								) < 0) {
+
+							printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name);
+							spin_unlock_irqrestore(&dev->lock, flags);
+							return 0;
+						}
+						/* Setup dialstate. */
+						lp->dtimer = 0;
+						lp->dialstate = 11;
+						/* Connect interface with channel */
+						isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							if (isdn_ppp_bind(lp) < 0) {
+								spin_unlock_irqrestore(&dev->lock, flags);
+								isdn_net_unbind_channel(lp);
+								return 0;
+							}
+#endif
+						spin_unlock_irqrestore(&dev->lock, flags);
+						/* Initiate dialing by returning 2 or 4 */
+						return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
+					} else
+						printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name);
+					return 0;
+				} else {
+					printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", lp->name, nr,
+					       eaz);
+					/* if this interface is dialing, it does it probably on a different
+					   device, so free this device */
+					if ((lp->dialstate == 4) || (lp->dialstate == 12)) {
+#ifdef CONFIG_ISDN_PPP
+						if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+							isdn_ppp_free(lp);
+#endif
+						isdn_net_lp_disconnected(lp);
+						isdn_free_channel(lp->isdn_device, lp->isdn_channel,
+							 ISDN_USAGE_NET);
+					}
+					spin_lock_irqsave(&dev->lock, flags);
+					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+					dev->usage[idx] |= ISDN_USAGE_NET;
+					strcpy(dev->num[idx], nr);
+					isdn_info_update();
+					dev->st_netdev[idx] = lp->netdev;
+					lp->isdn_device = di;
+					lp->isdn_channel = ch;
+					lp->ppp_slot = -1;
+					lp->flags |= ISDN_NET_CONNECTED;
+					lp->dialstate = 7;
+					lp->dtimer = 0;
+					lp->outgoing = 0;
+					lp->huptimer = 0;
+					lp->hupflags |= ISDN_WAITCHARGE;
+					lp->hupflags &= ~ISDN_HAVECHARGE;
+#ifdef CONFIG_ISDN_PPP
+					if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+						if (isdn_ppp_bind(lp) < 0) {
+							isdn_net_unbind_channel(lp);
+							spin_unlock_irqrestore(&dev->lock, flags);
+							return 0;
+						}
+					}
+#endif
+					spin_unlock_irqrestore(&dev->lock, flags);
+					return 1;
+				}
+			}
+		}
+		p = (isdn_net_dev *) p->next;
+	}
+	/* If none of configured EAZ/MSN matched and not verbose, be silent */
+	if (!ematch || dev->net_verbose)
+		printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
+	return (wret == 2)?5:0;
+}
+
+/*
+ * Search list of net-interfaces for an interface with given name.
+ */
+isdn_net_dev *
+isdn_net_findif(char *name)
+{
+	isdn_net_dev *p = dev->netdev;
+
+	while (p) {
+		if (!strcmp(p->local->name, name))
+			return p;
+		p = (isdn_net_dev *) p->next;
+	}
+	return (isdn_net_dev *) NULL;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is called from the userlevel-routine below or
+ * from isdn_net_start_xmit().
+ */
+int
+isdn_net_force_dial_lp(isdn_net_local * lp)
+{
+	if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
+		int chi;
+		if (lp->phone[1]) {
+			ulong flags;
+
+			/* Grab a free ISDN-Channel */
+			spin_lock_irqsave(&dev->lock, flags);
+			if ((chi = isdn_get_free_channel(
+					ISDN_USAGE_NET,
+					lp->l2_proto,
+					lp->l3_proto,
+					lp->pre_device,
+					lp->pre_channel,
+					lp->msn)) < 0) {
+				printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name);
+				spin_unlock_irqrestore(&dev->lock, flags);
+				return -EAGAIN;
+			}
+			lp->dialstate = 1;
+			/* Connect interface with channel */
+			isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+			if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+				if (isdn_ppp_bind(lp) < 0) {
+					isdn_net_unbind_channel(lp);
+					spin_unlock_irqrestore(&dev->lock, flags);
+					return -EAGAIN;
+				}
+#endif
+			/* Initiate dialing */
+			spin_unlock_irqrestore(&dev->lock, flags);
+			isdn_net_dial();
+			return 0;
+		} else
+			return -EINVAL;
+	} else
+		return -EBUSY;
+}
+
+/*
+ * This is called from certain upper protocol layers (multilink ppp
+ * and x25iface encapsulation module) that want to initiate dialing
+ * themselves.
+ */
+int
+isdn_net_dial_req(isdn_net_local * lp)
+{
+	/* is there a better error code? */
+	if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
+
+	return isdn_net_force_dial_lp(lp);
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
+ */
+int
+isdn_net_force_dial(char *name)
+{
+	isdn_net_dev *p = isdn_net_findif(name);
+
+	if (!p)
+		return -ENODEV;
+	return (isdn_net_force_dial_lp(p->local));
+}
+
+/*
+ * Allocate a new network-interface and initialize its data structures.
+ */
+char *
+isdn_net_new(char *name, struct net_device *master)
+{
+	isdn_net_dev *netdev;
+
+	/* Avoid creating an existing interface */
+	if (isdn_net_findif(name)) {
+		printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
+		return NULL;
+	}
+	if (!(netdev = (isdn_net_dev *) kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
+		return NULL;
+	}
+	memset(netdev, 0, sizeof(isdn_net_dev));
+	if (!(netdev->local = (isdn_net_local *) kmalloc(sizeof(isdn_net_local), GFP_KERNEL))) {
+		printk(KERN_WARNING "isdn_net: Could not allocate device locals\n");
+		kfree(netdev);
+		return NULL;
+	}
+	memset(netdev->local, 0, sizeof(isdn_net_local));
+	if (name == NULL)
+		strcpy(netdev->local->name, "         ");
+	else
+		strcpy(netdev->local->name, name);
+	strcpy(netdev->dev.name, netdev->local->name);
+	netdev->dev.priv = netdev->local;
+	netdev->dev.init = isdn_net_init;
+	netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP;
+	if (master) {
+		/* Device shall be a slave */
+		struct net_device *p = (((isdn_net_local *) master->priv)->slave);
+		struct net_device *q = master;
+
+		netdev->local->master = master;
+		/* Put device at end of slave-chain */
+		while (p) {
+			q = p;
+			p = (((isdn_net_local *) p->priv)->slave);
+		}
+		((isdn_net_local *) q->priv)->slave = &(netdev->dev);
+	} else {
+		/* Device shall be a master */
+		/*
+		 * Watchdog timer (currently) for master only.
+		 */
+		netdev->dev.tx_timeout = isdn_net_tx_timeout;
+		netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT;
+		if (register_netdev(&netdev->dev) != 0) {
+			printk(KERN_WARNING "isdn_net: Could not register net-device\n");
+			kfree(netdev->local);
+			kfree(netdev);
+			return NULL;
+		}
+	}
+	netdev->local->magic = ISDN_NET_MAGIC;
+
+	netdev->queue = netdev->local;
+	spin_lock_init(&netdev->queue_lock);
+
+	netdev->local->last = netdev->local;
+	netdev->local->netdev = netdev;
+	netdev->local->next = netdev->local;
+
+	INIT_WORK(&netdev->local->tqueue, (void *)(void *) isdn_net_softint, netdev->local);
+	spin_lock_init(&netdev->local->xmit_lock);
+
+	netdev->local->isdn_device = -1;
+	netdev->local->isdn_channel = -1;
+	netdev->local->pre_device = -1;
+	netdev->local->pre_channel = -1;
+	netdev->local->exclusive = -1;
+	netdev->local->ppp_slot = -1;
+	netdev->local->pppbind = -1;
+	skb_queue_head_init(&netdev->local->super_tx_queue);
+	netdev->local->l2_proto = ISDN_PROTO_L2_X75I;
+	netdev->local->l3_proto = ISDN_PROTO_L3_TRANS;
+	netdev->local->triggercps = 6000;
+	netdev->local->slavedelay = 10 * HZ;
+	netdev->local->hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
+	netdev->local->onhtime = 10;	/* Default hangup-time for saving costs
+	   of those who forget configuring this */
+	netdev->local->dialmax = 1;
+	netdev->local->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;	/* Hangup before Callback, manual dial */
+	netdev->local->cbdelay = 25;	/* Wait 5 secs before Callback */
+	netdev->local->dialtimeout = -1;  /* Infinite Dial-Timeout */
+	netdev->local->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
+	netdev->local->dialstarted = 0;   /* Jiffies of last dial-start */
+	netdev->local->dialwait_timer = 0;  /* Jiffies of earliest next dial-start */
+
+	/* Put into to netdev-chain */
+	netdev->next = (void *) dev->netdev;
+	dev->netdev = netdev;
+	return netdev->dev.name;
+}
+
+char *
+isdn_net_newslave(char *parm)
+{
+	char *p = strchr(parm, ',');
+	isdn_net_dev *n;
+	char newname[10];
+
+	if (p) {
+		/* Slave-Name MUST not be empty */
+		if (!strlen(p + 1))
+			return NULL;
+		strcpy(newname, p + 1);
+		*p = 0;
+		/* Master must already exist */
+		if (!(n = isdn_net_findif(parm)))
+			return NULL;
+		/* Master must be a real interface, not a slave */
+		if (n->local->master)
+			return NULL;
+		/* Master must not be started yet */
+		if (isdn_net_device_started(n)) 
+			return NULL;
+		return (isdn_net_new(newname, &(n->dev)));
+	}
+	return NULL;
+}
+
+/*
+ * Set interface-parameters.
+ * Always set all parameters, so the user-level application is responsible
+ * for not overwriting existing setups. It has to get the current
+ * setup first, if only selected parameters are to be changed.
+ */
+int
+isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+{
+	isdn_net_dev *p = isdn_net_findif(cfg->name);
+	ulong features;
+	int i;
+	int drvidx;
+	int chidx;
+	char drvid[25];
+
+	if (p) {
+		isdn_net_local *lp = p->local;
+
+		/* See if any registered driver supports the features we want */
+		features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
+			((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
+		for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+			if (dev->drv[i])
+				if ((dev->drv[i]->interface->features & features) == features)
+					break;
+		if (i == ISDN_MAX_DRIVERS) {
+			printk(KERN_WARNING "isdn_net: No driver with selected features\n");
+			return -ENODEV;
+		}
+		if (lp->p_encap != cfg->p_encap){
+#ifdef CONFIG_ISDN_X25
+			struct concap_proto * cprot = p -> cprot;
+#endif
+			if (isdn_net_device_started(p)) {
+				printk(KERN_WARNING "%s: cannot change encap when if is up\n",
+				       lp->name);
+				return -EBUSY;
+			}
+#ifdef CONFIG_ISDN_X25
+			if( cprot && cprot -> pops )
+				cprot -> pops -> proto_del ( cprot );
+			p -> cprot = NULL;
+			lp -> dops = NULL;
+			/* ... ,  prepare for configuration of new one ... */
+			switch ( cfg -> p_encap ){
+			case ISDN_NET_ENCAP_X25IFACE:
+				lp -> dops = &isdn_concap_reliable_dl_dops;
+			}
+			/* ... and allocate new one ... */
+			p -> cprot = isdn_concap_new( cfg -> p_encap );
+			/* p -> cprot == NULL now if p_encap is not supported
+			   by means of the concap_proto mechanism */
+			/* the protocol is not configured yet; this will
+			   happen later when isdn_net_reset() is called */
+#endif
+		}
+		switch ( cfg->p_encap ) {
+		case ISDN_NET_ENCAP_SYNCPPP:
+#ifndef CONFIG_ISDN_PPP
+			printk(KERN_WARNING "%s: SyncPPP support not configured\n",
+			       lp->name);
+			return -EINVAL;
+#else
+			p->dev.type = ARPHRD_PPP;	/* change ARP type */
+			p->dev.addr_len = 0;
+			p->dev.do_ioctl = isdn_ppp_dev_ioctl;
+#endif
+			break;
+		case ISDN_NET_ENCAP_X25IFACE:
+#ifndef CONFIG_ISDN_X25
+			printk(KERN_WARNING "%s: isdn-x25 support not configured\n",
+			       p->local->name);
+			return -EINVAL;
+#else
+			p->dev.type = ARPHRD_X25;	/* change ARP type */
+			p->dev.addr_len = 0;
+#endif
+			break;
+		case ISDN_NET_ENCAP_CISCOHDLCK:
+			p->dev.do_ioctl = isdn_ciscohdlck_dev_ioctl;
+			break;
+		default:
+			if( cfg->p_encap >= 0 &&
+			    cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
+				break;
+			printk(KERN_WARNING
+			       "%s: encapsulation protocol %d not supported\n",
+			       p->local->name, cfg->p_encap);
+			return -EINVAL;
+		}
+		if (strlen(cfg->drvid)) {
+			/* A bind has been requested ... */
+			char *c,
+			*e;
+
+			drvidx = -1;
+			chidx = -1;
+			strcpy(drvid, cfg->drvid);
+			if ((c = strchr(drvid, ','))) {
+				/* The channel-number is appended to the driver-Id with a comma */
+				chidx = (int) simple_strtoul(c + 1, &e, 10);
+				if (e == c)
+					chidx = -1;
+				*c = '\0';
+			}
+			for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+				/* Lookup driver-Id in array */
+				if (!(strcmp(dev->drvid[i], drvid))) {
+					drvidx = i;
+					break;
+				}
+			if ((drvidx == -1) || (chidx == -1))
+				/* Either driver-Id or channel-number invalid */
+				return -ENODEV;
+		} else {
+			/* Parameters are valid, so get them */
+			drvidx = lp->pre_device;
+			chidx = lp->pre_channel;
+		}
+		if (cfg->exclusive > 0) {
+			unsigned long flags;
+
+			/* If binding is exclusive, try to grab the channel */
+			spin_lock_irqsave(&dev->lock, flags);
+			if ((i = isdn_get_free_channel(ISDN_USAGE_NET,
+				lp->l2_proto, lp->l3_proto, drvidx,
+				chidx, lp->msn)) < 0) {
+				/* Grab failed, because desired channel is in use */
+				lp->exclusive = -1;
+				spin_unlock_irqrestore(&dev->lock, flags);
+				return -EBUSY;
+			}
+			/* All went ok, so update isdninfo */
+			dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
+			isdn_info_update();
+			spin_unlock_irqrestore(&dev->lock, flags);
+			lp->exclusive = i;
+		} else {
+			/* Non-exclusive binding or unbind. */
+			lp->exclusive = -1;
+			if ((lp->pre_device != -1) && (cfg->exclusive == -1)) {
+				isdn_unexclusive_channel(lp->pre_device, lp->pre_channel);
+				isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET);
+				drvidx = -1;
+				chidx = -1;
+			}
+		}
+		strcpy(lp->msn, cfg->eaz);
+		lp->pre_device = drvidx;
+		lp->pre_channel = chidx;
+		lp->onhtime = cfg->onhtime;
+		lp->charge = cfg->charge;
+		lp->l2_proto = cfg->l2_proto;
+		lp->l3_proto = cfg->l3_proto;
+		lp->cbdelay = cfg->cbdelay;
+		lp->dialmax = cfg->dialmax;
+		lp->triggercps = cfg->triggercps;
+		lp->slavedelay = cfg->slavedelay * HZ;
+		lp->pppbind = cfg->pppbind;
+		lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+		lp->dialwait = cfg->dialwait * HZ;
+		if (cfg->secure)
+			lp->flags |= ISDN_NET_SECURE;
+		else
+			lp->flags &= ~ISDN_NET_SECURE;
+		if (cfg->cbhup)
+			lp->flags |= ISDN_NET_CBHUP;
+		else
+			lp->flags &= ~ISDN_NET_CBHUP;
+		switch (cfg->callback) {
+			case 0:
+				lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
+				break;
+			case 1:
+				lp->flags |= ISDN_NET_CALLBACK;
+				lp->flags &= ~ISDN_NET_CBOUT;
+				break;
+			case 2:
+				lp->flags |= ISDN_NET_CBOUT;
+				lp->flags &= ~ISDN_NET_CALLBACK;
+				break;
+		}
+		lp->flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
+		if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+			/* old isdnctrl version, where only 0 or 1 is given */
+			printk(KERN_WARNING
+			     "Old isdnctrl version detected! Please update.\n");
+			lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */
+		}
+		else {
+			lp->flags |= cfg->dialmode;  /* turn on selected bits */
+		}
+		if (cfg->chargehup)
+			lp->hupflags |= ISDN_CHARGEHUP;
+		else
+			lp->hupflags &= ~ISDN_CHARGEHUP;
+		if (cfg->ihup)
+			lp->hupflags |= ISDN_INHUP;
+		else
+			lp->hupflags &= ~ISDN_INHUP;
+		if (cfg->chargeint > 10) {
+			lp->hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE;
+			lp->chargeint = cfg->chargeint * HZ;
+		}
+		if (cfg->p_encap != lp->p_encap) {
+			if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+				p->dev.hard_header = NULL;
+				p->dev.hard_header_cache = NULL;
+				p->dev.header_cache_update = NULL;
+				p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
+			} else {
+				p->dev.hard_header = isdn_net_header;
+				if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) {
+					p->dev.hard_header_cache = lp->org_hhc;
+					p->dev.header_cache_update = lp->org_hcu;
+					p->dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+				} else {
+					p->dev.hard_header_cache = NULL;
+					p->dev.header_cache_update = NULL;
+					p->dev.flags = IFF_NOARP|IFF_POINTOPOINT;
+				}
+			}
+		}
+		lp->p_encap = cfg->p_encap;
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Perform get-interface-parameters.ioctl
+ */
+int
+isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+{
+	isdn_net_dev *p = isdn_net_findif(cfg->name);
+
+	if (p) {
+		isdn_net_local *lp = p->local;
+
+		strcpy(cfg->eaz, lp->msn);
+		cfg->exclusive = lp->exclusive;
+		if (lp->pre_device >= 0) {
+			sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device],
+				lp->pre_channel);
+		} else
+			cfg->drvid[0] = '\0';
+		cfg->onhtime = lp->onhtime;
+		cfg->charge = lp->charge;
+		cfg->l2_proto = lp->l2_proto;
+		cfg->l3_proto = lp->l3_proto;
+		cfg->p_encap = lp->p_encap;
+		cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
+		cfg->callback = 0;
+		if (lp->flags & ISDN_NET_CALLBACK)
+			cfg->callback = 1;
+		if (lp->flags & ISDN_NET_CBOUT)
+			cfg->callback = 2;
+		cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+		cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
+		cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
+		cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
+		cfg->cbdelay = lp->cbdelay;
+		cfg->dialmax = lp->dialmax;
+		cfg->triggercps = lp->triggercps;
+		cfg->slavedelay = lp->slavedelay / HZ;
+		cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
+		    (lp->chargeint / HZ) : 0;
+		cfg->pppbind = lp->pppbind;
+		cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
+		cfg->dialwait = lp->dialwait / HZ;
+		if (lp->slave)
+			strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name);
+		else
+			cfg->slave[0] = '\0';
+		if (lp->master)
+			strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->name);
+		else
+			cfg->master[0] = '\0';
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Add a phone-number to an interface.
+ */
+int
+isdn_net_addphone(isdn_net_ioctl_phone * phone)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	isdn_net_phone *n;
+
+	if (p) {
+		if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
+			return -ENOMEM;
+		strcpy(n->num, phone->phone);
+		n->next = p->local->phone[phone->outgoing & 1];
+		p->local->phone[phone->outgoing & 1] = n;
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Copy a string of all phone-numbers of an interface to user space.
+ * This might sleep and must be called with the isdn semaphore down.
+ */
+int
+isdn_net_getphones(isdn_net_ioctl_phone * phone, char __user *phones)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int inout = phone->outgoing & 1;
+	int more = 0;
+	int count = 0;
+	isdn_net_phone *n;
+
+	if (!p)
+		return -ENODEV;
+	inout &= 1;
+	for (n = p->local->phone[inout]; n; n = n->next) {
+		if (more) {
+			put_user(' ', phones++);
+			count++;
+		}
+		if (copy_to_user(phones, n->num, strlen(n->num) + 1)) {
+			return -EFAULT;
+		}
+		phones += strlen(n->num);
+		count += strlen(n->num);
+		more = 1;
+	}
+	put_user(0, phones);
+	count++;
+	return count;
+}
+
+/*
+ * Copy a string containing the peer's phone number of a connected interface
+ * to user space.
+ */
+int
+isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone __user *peer)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int ch, dv, idx;
+
+	if (!p) return -ENODEV;
+	/*
+	 * Theoretical race: while this executes, the remote number might
+	 * become invalid (hang up) or change (new connection), resulting
+         * in (partially) wrong number copied to user. This race
+	 * currently ignored.
+	 */
+	ch = p->local->isdn_channel;
+	dv = p->local->isdn_device;
+	if(ch<0 && dv<0) return -ENOTCONN;
+	idx = isdn_dc2minor(dv, ch);
+	if (idx<0) return -ENODEV;
+	/* for pre-bound channels, we need this extra check */
+	if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN;
+	strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN);
+	phone->outgoing=USG_OUTGOING(dev->usage[idx]);
+	if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
+	return 0;
+}
+/*
+ * Delete a phone-number from an interface.
+ */
+int
+isdn_net_delphone(isdn_net_ioctl_phone * phone)
+{
+	isdn_net_dev *p = isdn_net_findif(phone->name);
+	int inout = phone->outgoing & 1;
+	isdn_net_phone *n;
+	isdn_net_phone *m;
+
+	if (p) {
+		n = p->local->phone[inout];
+		m = NULL;
+		while (n) {
+			if (!strcmp(n->num, phone->phone)) {
+				if (p->local->dial == n)
+					p->local->dial = n->next;
+				if (m)
+					m->next = n->next;
+				else
+					p->local->phone[inout] = n->next;
+				kfree(n);
+				return 0;
+			}
+			m = n;
+			n = (isdn_net_phone *) n->next;
+		}
+		return -EINVAL;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Delete all phone-numbers of an interface.
+ */
+static int
+isdn_net_rmallphone(isdn_net_dev * p)
+{
+	isdn_net_phone *n;
+	isdn_net_phone *m;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		n = p->local->phone[i];
+		while (n) {
+			m = n->next;
+			kfree(n);
+			n = m;
+		}
+		p->local->phone[i] = NULL;
+	}
+	p->local->dial = NULL;
+	return 0;
+}
+
+/*
+ * Force a hangup of a network-interface.
+ */
+int
+isdn_net_force_hangup(char *name)
+{
+	isdn_net_dev *p = isdn_net_findif(name);
+	struct net_device *q;
+
+	if (p) {
+		if (p->local->isdn_device < 0)
+			return 1;
+		q = p->local->slave;
+		/* If this interface has slaves, do a hangup for them also. */
+		while (q) {
+			isdn_net_hangup(q);
+			q = (((isdn_net_local *) q->priv)->slave);
+		}
+		isdn_net_hangup(&p->dev);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+/*
+ * Helper-function for isdn_net_rm: Do the real work.
+ */
+static int
+isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+{
+	u_long flags;
+
+	if (isdn_net_device_started(p)) {
+		return -EBUSY;
+	}
+#ifdef CONFIG_ISDN_X25
+	if( p -> cprot && p -> cprot -> pops )
+		p -> cprot -> pops -> proto_del ( p -> cprot );
+#endif
+	/* Free all phone-entries */
+	isdn_net_rmallphone(p);
+	/* If interface is bound exclusive, free channel-usage */
+	if (p->local->exclusive != -1)
+		isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel);
+	if (p->local->master) {
+		/* It's a slave-device, so update master's slave-pointer if necessary */
+		if (((isdn_net_local *) (p->local->master->priv))->slave == &p->dev)
+			((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave;
+	} else {
+		/* Unregister only if it's a master-device */
+		p->dev.hard_header_cache = p->local->org_hhc;
+		p->dev.header_cache_update = p->local->org_hcu;
+		unregister_netdev(&p->dev);
+	}
+	/* Unlink device from chain */
+	spin_lock_irqsave(&dev->lock, flags);
+	if (q)
+		q->next = p->next;
+	else
+		dev->netdev = p->next;
+	if (p->local->slave) {
+		/* If this interface has a slave, remove it also */
+		char *slavename = ((isdn_net_local *) (p->local->slave->priv))->name;
+		isdn_net_dev *n = dev->netdev;
+		q = NULL;
+		while (n) {
+			if (!strcmp(n->local->name, slavename)) {
+				spin_unlock_irqrestore(&dev->lock, flags);
+				isdn_net_realrm(n, q);
+				spin_lock_irqsave(&dev->lock, flags);
+				break;
+			}
+			q = n;
+			n = (isdn_net_dev *) n->next;
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+	/* If no more net-devices remain, disable auto-hangup timer */
+	if (dev->netdev == NULL)
+		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+	kfree(p->local);
+	kfree(p);
+
+	return 0;
+}
+
+/*
+ * Remove a single network-interface.
+ */
+int
+isdn_net_rm(char *name)
+{
+	u_long flags;
+	isdn_net_dev *p;
+	isdn_net_dev *q;
+
+	/* Search name in netdev-chain */
+	spin_lock_irqsave(&dev->lock, flags);
+	p = dev->netdev;
+	q = NULL;
+	while (p) {
+		if (!strcmp(p->local->name, name)) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return (isdn_net_realrm(p, q));
+		}
+		q = p;
+		p = (isdn_net_dev *) p->next;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+	/* If no more net-devices remain, disable auto-hangup timer */
+	if (dev->netdev == NULL)
+		isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+	return -ENODEV;
+}
+
+/*
+ * Remove all network-interfaces
+ */
+int
+isdn_net_rmall(void)
+{
+	u_long flags;
+	int ret;
+
+	/* Walk through netdev-chain */
+	spin_lock_irqsave(&dev->lock, flags);
+	while (dev->netdev) {
+		if (!dev->netdev->local->master) {
+			/* Remove master-devices only, slaves get removed with their master */
+			spin_unlock_irqrestore(&dev->lock, flags);
+			if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
+				return ret;
+			}
+			spin_lock_irqsave(&dev->lock, flags);
+		}
+	}
+	dev->netdev = NULL;
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return 0;
+}
diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h
new file mode 100644
index 0000000..bc2f0dd
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_net.h
@@ -0,0 +1,190 @@
+/* $Id: isdn_net.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, network related functions (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+			      /* Definitions for hupflags:                */
+#define ISDN_WAITCHARGE  1      /* did not get a charge info yet            */
+#define ISDN_HAVECHARGE  2      /* We know a charge info                    */
+#define ISDN_CHARGEHUP   4      /* We want to use the charge mechanism      */
+#define ISDN_INHUP       8      /* Even if incoming, close after huptimeout */
+#define ISDN_MANCHARGE  16      /* Charge Interval manually set             */
+
+/*
+ * Definitions for Cisco-HDLC header.
+ */
+
+#define CISCO_ADDR_UNICAST    0x0f
+#define CISCO_ADDR_BROADCAST  0x8f
+#define CISCO_CTRL            0x00
+#define CISCO_TYPE_CDP        0x2000
+#define CISCO_TYPE_SLARP      0x8035
+#define CISCO_SLARP_REQUEST   0
+#define CISCO_SLARP_REPLY     1
+#define CISCO_SLARP_KEEPALIVE 2
+
+extern char *isdn_net_new(char *, struct net_device *);
+extern char *isdn_net_newslave(char *);
+extern int isdn_net_rm(char *);
+extern int isdn_net_rmall(void);
+extern int isdn_net_stat_callback(int, isdn_ctrl *);
+extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int isdn_net_getphones(isdn_net_ioctl_phone *, char __user *);
+extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone __user *);
+extern int isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int isdn_net_find_icall(int, int, int, setup_parm *);
+extern void isdn_net_hangup(struct net_device *);
+extern void isdn_net_dial(void);
+extern void isdn_net_autohup(void);
+extern int isdn_net_force_hangup(char *);
+extern int isdn_net_force_dial(char *);
+extern isdn_net_dev *isdn_net_findif(char *);
+extern int isdn_net_rcv_skb(int, struct sk_buff *);
+extern int isdn_net_dial_req(isdn_net_local *);
+extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb);
+extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
+
+#define ISDN_NET_MAX_QUEUE_LENGTH 2
+
+/*
+ * is this particular channel busy?
+ */
+static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
+{
+	if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
+		return 0;
+	else 
+		return 1;
+}
+
+/*
+ * For the given net device, this will get a non-busy channel out of the
+ * corresponding bundle. The returned channel is locked.
+ */
+static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd)
+{
+	unsigned long flags;
+	isdn_net_local *lp;
+
+	spin_lock_irqsave(&nd->queue_lock, flags);
+	lp = nd->queue;         /* get lp on top of queue */
+	spin_lock(&nd->queue->xmit_lock);
+	while (isdn_net_lp_busy(nd->queue)) {
+		spin_unlock(&nd->queue->xmit_lock);
+		nd->queue = nd->queue->next;
+		if (nd->queue == lp) { /* not found -- should never happen */
+			lp = NULL;
+			goto errout;
+		}
+		spin_lock(&nd->queue->xmit_lock);
+	}
+	lp = nd->queue;
+	nd->queue = nd->queue->next;
+	local_bh_disable();
+errout:
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+	return lp;
+}
+
+/*
+ * add a channel to a bundle
+ */
+static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp)
+{
+	isdn_net_local *lp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&nd->queue_lock, flags);
+
+	lp = nd->queue;
+//	printk(KERN_DEBUG "%s: lp:%s(%p) nlp:%s(%p) last(%p)\n",
+//		__FUNCTION__, lp->name, lp, nlp->name, nlp, lp->last);
+	nlp->last = lp->last;
+	lp->last->next = nlp;
+	lp->last = nlp;
+	nlp->next = lp;
+	nd->queue = nlp;
+
+	spin_unlock_irqrestore(&nd->queue_lock, flags);
+}
+/*
+ * remove a channel from the bundle it belongs to
+ */
+static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
+{
+	isdn_net_local *master_lp = lp;
+	unsigned long flags;
+
+	if (lp->master)
+		master_lp = (isdn_net_local *) lp->master->priv;
+
+//	printk(KERN_DEBUG "%s: lp:%s(%p) mlp:%s(%p) last(%p) next(%p) mndq(%p)\n",
+//		__FUNCTION__, lp->name, lp, master_lp->name, master_lp, lp->last, lp->next, master_lp->netdev->queue);
+	spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
+	lp->last->next = lp->next;
+	lp->next->last = lp->last;
+	if (master_lp->netdev->queue == lp) {
+		master_lp->netdev->queue = lp->next;
+		if (lp->next == lp) { /* last in queue */
+			master_lp->netdev->queue = master_lp->netdev->local;
+		}
+	}
+	lp->next = lp->last = lp;	/* (re)set own pointers */
+//	printk(KERN_DEBUG "%s: mndq(%p)\n",
+//		__FUNCTION__, master_lp->netdev->queue);
+	spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
+}
+
+static inline int
+put_u8(unsigned char *p, u8 x)
+{
+	*p = x;
+	return 1;
+}
+
+static inline int
+put_u16(unsigned char *p, u16 x)
+{
+	*((u16 *)p) = htons(x);
+	return 2;
+}
+
+static inline int
+put_u32(unsigned char *p, u32 x)
+{
+	*((u32 *)p) = htonl(x);
+	return 4;
+}
+
+static inline int
+get_u8(unsigned char *p, u8 *x)
+{
+	*x = *p;
+	return 1;
+}
+
+static inline int
+get_u16(unsigned char *p, u16 *x)
+{
+	*x = ntohs(*((u16 *)p));
+	return 2;
+}
+
+static inline int
+get_u32(unsigned char *p, u32 *x)
+{
+	*x = ntohl(*((u32 *)p));
+	return 4;
+}
+
+
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
new file mode 100644
index 0000000..3c09211
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -0,0 +1,3020 @@
+/* $Id: isdn_ppp.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $
+ *
+ * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/isdn.h>
+#include <linux/poll.h>
+#include <linux/ppp-comp.h>
+#ifdef CONFIG_IPPP_FILTER
+#include <linux/filter.h>
+#endif
+
+#include "isdn_common.h"
+#include "isdn_ppp.h"
+#include "isdn_net.h"
+
+#ifndef PPP_IPX
+#define PPP_IPX 0x002b
+#endif
+
+/* Prototypes */
+static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
+static int isdn_ppp_closewait(int slot);
+static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
+				 struct sk_buff *skb, int proto);
+static int isdn_ppp_if_get_unit(char *namebuf);
+static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
+				struct ippp_struct *,struct ippp_struct *,int *proto);
+static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff *skb,int proto);
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type);
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb);
+
+/* New CCP stuff */
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is);
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len);
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id);
+static void isdn_ppp_ccp_timer_callback(unsigned long closure);
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id);
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp);
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id);
+
+
+
+#ifdef CONFIG_ISDN_MPP
+static ippp_bundle * isdn_ppp_bundle_arr = NULL;
+ 
+static int isdn_ppp_mp_bundle_array_init(void);
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
+							struct sk_buff *skb);
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
+
+static int isdn_ppp_bundle(struct ippp_struct *, int unit);
+#endif	/* CONFIG_ISDN_MPP */
+  
+char *isdn_ppp_revision = "$Revision: 1.1.2.3 $";
+
+static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+
+static struct isdn_ppp_compressor *ipc_head = NULL;
+
+/*
+ * frame log (debug)
+ */
+static void
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
+{
+	int cnt,
+	 j,
+	 i;
+	char buf[80];
+
+	if (len < maxlen)
+		maxlen = len;
+
+	for (i = 0, cnt = 0; cnt < maxlen; i++) {
+		for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
+			sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
+		printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
+	}
+}
+
+/*
+ * unbind isdn_net_local <=> ippp-device
+ * note: it can happen, that we hangup/free the master before the slaves
+ *       in this case we bind another lp to the master device
+ */
+int
+isdn_ppp_free(isdn_net_local * lp)
+{
+	struct ippp_struct *is;
+
+	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
+		return 0;
+	}
+
+#ifdef CONFIG_ISDN_MPP
+	spin_lock(&lp->netdev->pb->lock);
+#endif
+	isdn_net_rm_from_bundle(lp);
+#ifdef CONFIG_ISDN_MPP
+	if (lp->netdev->pb->ref_ct == 1)	/* last link in queue? */
+		isdn_ppp_mp_cleanup(lp);
+
+	lp->netdev->pb->ref_ct--;
+	spin_unlock(&lp->netdev->pb->lock);
+#endif /* CONFIG_ISDN_MPP */
+	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: ppp_slot(%d) now invalid\n",
+			__FUNCTION__, lp->ppp_slot);
+		return 0;
+	}
+	is = ippp_table[lp->ppp_slot];
+	if ((is->state & IPPP_CONNECT))
+		isdn_ppp_closewait(lp->ppp_slot);	/* force wakeup on ippp device */
+	else if (is->state & IPPP_ASSIGNED)
+		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGNED' state */
+
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
+
+	is->lp = NULL;          /* link is down .. set lp to NULL */
+	lp->ppp_slot = -1;      /* is this OK ?? */
+
+	return 0;
+}
+
+/*
+ * bind isdn_net_local <=> ippp-device
+ *
+ * This function is allways called with holding dev->lock so
+ * no additional lock is needed
+ */
+int
+isdn_ppp_bind(isdn_net_local * lp)
+{
+	int i;
+	int unit = 0;
+	struct ippp_struct *is;
+	int retval;
+
+	if (lp->pppbind < 0) {  /* device bounded to ippp device ? */
+		isdn_net_dev *net_dev = dev->netdev;
+		char exclusive[ISDN_MAX_CHANNELS];	/* exclusive flags */
+		memset(exclusive, 0, ISDN_MAX_CHANNELS);
+		while (net_dev) {	/* step through net devices to find exclusive minors */
+			isdn_net_local *lp = net_dev->local;
+			if (lp->pppbind >= 0)
+				exclusive[lp->pppbind] = 1;
+			net_dev = net_dev->next;
+		}
+		/*
+		 * search a free device / slot
+		 */
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+			if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) {	/* OPEN, but not connected! */
+				break;
+			}
+		}
+	} else {
+		for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+			if (ippp_table[i]->minor == lp->pppbind &&
+			    (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
+				break;
+		}
+	}
+
+	if (i >= ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
+		retval = -1;
+		goto out;
+	}
+	unit = isdn_ppp_if_get_unit(lp->name);	/* get unit number from interface name .. ugly! */
+	if (unit < 0) {
+		printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name);
+		retval = -1;
+		goto out;
+	}
+	
+	lp->ppp_slot = i;
+	is = ippp_table[i];
+	is->lp = lp;
+	is->unit = unit;
+	is->state = IPPP_OPEN | IPPP_ASSIGNED;	/* assigned to a netdevice but not connected */
+#ifdef CONFIG_ISDN_MPP
+	retval = isdn_ppp_mp_init(lp, NULL);
+	if (retval < 0)
+		goto out;
+#endif /* CONFIG_ISDN_MPP */
+
+	retval = lp->ppp_slot;
+
+ out:
+	return retval;
+}
+
+/*
+ * kick the ipppd on the device
+ * (wakes up daemon after B-channel connect)
+ */
+
+void
+isdn_ppp_wakeup_daemon(isdn_net_local * lp)
+{
+	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
+		return;
+	}
+	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+	wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
+}
+
+/*
+ * there was a hangup on the netdevice
+ * force wakeup of the ippp device
+ * go into 'device waits for release' state
+ */
+static int
+isdn_ppp_closewait(int slot)
+{
+	struct ippp_struct *is;
+
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: slot(%d) out of range\n",
+			__FUNCTION__, slot);
+		return 0;
+	}
+	is = ippp_table[slot];
+	if (is->state)
+		wake_up_interruptible(&is->wq);
+	is->state = IPPP_CLOSEWAIT;
+	return 1;
+}
+
+/*
+ * isdn_ppp_find_slot / isdn_ppp_free_slot
+ */
+
+static int
+isdn_ppp_get_slot(void)
+{
+	int i;
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if (!ippp_table[i]->state)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * isdn_ppp_open
+ */
+
+int
+isdn_ppp_open(int min, struct file *file)
+{
+	int slot;
+	struct ippp_struct *is;
+
+	if (min < 0 || min > ISDN_MAX_CHANNELS)
+		return -ENODEV;
+
+	slot = isdn_ppp_get_slot();
+	if (slot < 0) {
+		return -EBUSY;
+	}
+	is = file->private_data = ippp_table[slot];
+	
+	printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",
+	       slot, min, is->state);
+
+	/* compression stuff */
+	is->link_compressor   = is->compressor = NULL;
+	is->link_decompressor = is->decompressor = NULL;
+	is->link_comp_stat    = is->comp_stat = NULL;
+	is->link_decomp_stat  = is->decomp_stat = NULL;
+	is->compflags = 0;
+
+	is->reset = isdn_ppp_ccp_reset_alloc(is);
+
+	is->lp = NULL;
+	is->mp_seqno = 0;       /* MP sequence number */
+	is->pppcfg = 0;         /* ppp configuration */
+	is->mpppcfg = 0;        /* mppp configuration */
+	is->last_link_seqno = -1;	/* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
+	is->unit = -1;          /* set, when we have our interface */
+	is->mru = 1524;         /* MRU, default 1524 */
+	is->maxcid = 16;        /* VJ: maxcid */
+	is->tk = current;
+	init_waitqueue_head(&is->wq);
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
+	is->last = is->rq;
+	is->minor = min;
+#ifdef CONFIG_ISDN_PPP_VJ
+	/*
+	 * VJ header compression init
+	 */
+	is->slcomp = slhc_init(16, 16);	/* not necessary for 2. link in bundle */
+#endif
+#ifdef CONFIG_IPPP_FILTER
+	is->pass_filter = NULL;
+	is->active_filter = NULL;
+#endif
+	is->state = IPPP_OPEN;
+
+	return 0;
+}
+
+/*
+ * release ippp device
+ */
+void
+isdn_ppp_release(int min, struct file *file)
+{
+	int i;
+	struct ippp_struct *is;
+
+	if (min < 0 || min >= ISDN_MAX_CHANNELS)
+		return;
+	is = file->private_data;
+
+	if (!is) {
+		printk(KERN_ERR "%s: no file->private_data\n", __FUNCTION__);
+		return;
+	}
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp);
+
+	if (is->lp) {           /* a lp address says: this link is still up */
+		isdn_net_dev *p = is->lp->netdev;
+
+		if (!p) {
+			printk(KERN_ERR "%s: no lp->netdev\n", __FUNCTION__);
+			return;
+		}
+		is->state &= ~IPPP_CONNECT;	/* -> effect: no call of wakeup */
+		/*
+		 * isdn_net_hangup() calls isdn_ppp_free()
+		 * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1
+		 * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon()
+		 */
+		isdn_net_hangup(&p->dev);
+	}
+	for (i = 0; i < NUM_RCV_BUFFS; i++) {
+		if (is->rq[i].buf) {
+			kfree(is->rq[i].buf);
+			is->rq[i].buf = NULL;
+		}
+	}
+	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
+	is->last = is->rq;
+
+#ifdef CONFIG_ISDN_PPP_VJ
+/* TODO: if this was the previous master: link the slcomp to the new master */
+	slhc_free(is->slcomp);
+	is->slcomp = NULL;
+#endif
+#ifdef CONFIG_IPPP_FILTER
+	if (is->pass_filter) {
+		kfree(is->pass_filter);
+		is->pass_filter = NULL;
+	}
+	if (is->active_filter) {
+		kfree(is->active_filter);
+		is->active_filter = NULL;
+	}
+#endif
+
+/* TODO: if this was the previous master: link the stuff to the new master */
+	if(is->comp_stat)
+		is->compressor->free(is->comp_stat);
+	if(is->link_comp_stat)
+		is->link_compressor->free(is->link_comp_stat);
+	if(is->link_decomp_stat)
+		is->link_decompressor->free(is->link_decomp_stat);
+	if(is->decomp_stat)
+		is->decompressor->free(is->decomp_stat);
+        is->compressor   = is->link_compressor   = NULL;
+        is->decompressor = is->link_decompressor = NULL;
+	is->comp_stat    = is->link_comp_stat    = NULL;
+        is->decomp_stat  = is->link_decomp_stat  = NULL;
+
+	/* Clean up if necessary */
+	if(is->reset)
+		isdn_ppp_ccp_reset_free(is);
+
+	/* this slot is ready for new connections */
+	is->state = 0;
+}
+
+/*
+ * get_arg .. ioctl helper
+ */
+static int
+get_arg(void __user *b, void *val, int len)
+{
+	if (len <= 0)
+		len = sizeof(void *);
+	if (copy_from_user(val, b, len))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * set arg .. ioctl helper
+ */
+static int
+set_arg(void __user *b, void *val,int len)
+{
+	if(len <= 0)
+		len = sizeof(void *);
+	if (copy_to_user(b, val, len))
+		return -EFAULT;
+	return 0;
+}
+
+static int get_filter(void __user *arg, struct sock_filter **p)
+{
+	struct sock_fprog uprog;
+	struct sock_filter *code = NULL;
+	int len, err;
+
+	if (copy_from_user(&uprog, arg, sizeof(uprog)))
+		return -EFAULT;
+
+	if (!uprog.len) {
+		*p = NULL;
+		return 0;
+	}
+
+	/* uprog.len is unsigned short, so no overflow here */
+	len = uprog.len * sizeof(struct sock_filter);
+	code = kmalloc(len, GFP_KERNEL);
+	if (code == NULL)
+		return -ENOMEM;
+
+	if (copy_from_user(code, uprog.filter, len)) {
+		kfree(code);
+		return -EFAULT;
+	}
+
+	err = sk_chk_filter(code, uprog.len);
+	if (err) {
+		kfree(code);
+		return err;
+	}
+
+	*p = code;
+	return uprog.len;
+}
+
+/*
+ * ippp device ioctl
+ */
+int
+isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	unsigned long val;
+	int r,i,j;
+	struct ippp_struct *is;
+	isdn_net_local *lp;
+	struct isdn_ppp_comp_data data;
+	void __user *argp = (void __user *)arg;
+
+	is = (struct ippp_struct *) file->private_data;
+	lp = is->lp;
+
+	if (is->debug & 0x1)
+		printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state);
+
+	if (!(is->state & IPPP_OPEN))
+		return -EINVAL;
+
+	switch (cmd) {
+		case PPPIOCBUNDLE:
+#ifdef CONFIG_ISDN_MPP
+			if (!(is->state & IPPP_CONNECT))
+				return -EINVAL;
+			if ((r = get_arg(argp, &val, sizeof(val) )))
+				return r;
+			printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+			       (int) min, (int) is->unit, (int) val);
+			return isdn_ppp_bundle(is, val);
+#else
+			return -1;
+#endif
+			break;
+		case PPPIOCGUNIT:	/* get ppp/isdn unit number */
+			if ((r = set_arg(argp, &is->unit, sizeof(is->unit) )))
+				return r;
+			break;
+		case PPPIOCGIFNAME:
+			if(!lp)
+				return -EINVAL;
+			if ((r = set_arg(argp, lp->name, strlen(lp->name))))
+				return r;
+			break;
+		case PPPIOCGMPFLAGS:	/* get configuration flags */
+			if ((r = set_arg(argp, &is->mpppcfg, sizeof(is->mpppcfg) )))
+				return r;
+			break;
+		case PPPIOCSMPFLAGS:	/* set configuration flags */
+			if ((r = get_arg(argp, &val, sizeof(val) )))
+				return r;
+			is->mpppcfg = val;
+			break;
+		case PPPIOCGFLAGS:	/* get configuration flags */
+			if ((r = set_arg(argp, &is->pppcfg,sizeof(is->pppcfg) )))
+				return r;
+			break;
+		case PPPIOCSFLAGS:	/* set configuration flags */
+			if ((r = get_arg(argp, &val, sizeof(val) ))) {
+				return r;
+			}
+			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
+				if (lp) {
+					/* OK .. we are ready to send buffers */
+					is->pppcfg = val; /* isdn_ppp_xmit test for SC_ENABLE_IP !!! */
+					netif_wake_queue(&lp->netdev->dev);
+					break;
+				}
+			}
+			is->pppcfg = val;
+			break;
+		case PPPIOCGIDLE:	/* get idle time information */
+			if (lp) {
+				struct ppp_idle pidle;
+				pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
+				if ((r = set_arg(argp, &pidle,sizeof(struct ppp_idle))))
+					 return r;
+			}
+			break;
+		case PPPIOCSMRU:	/* set receive unit size for PPP */
+			if ((r = get_arg(argp, &val, sizeof(val) )))
+				return r;
+			is->mru = val;
+			break;
+		case PPPIOCSMPMRU:
+			break;
+		case PPPIOCSMPMTU:
+			break;
+		case PPPIOCSMAXCID:	/* set the maximum compression slot id */
+			if ((r = get_arg(argp, &val, sizeof(val) )))
+				return r;
+			val++;
+			if (is->maxcid != val) {
+#ifdef CONFIG_ISDN_PPP_VJ
+				struct slcompress *sltmp;
+#endif
+				if (is->debug & 0x1)
+					printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val);
+				is->maxcid = val;
+#ifdef CONFIG_ISDN_PPP_VJ
+				sltmp = slhc_init(16, val);
+				if (!sltmp) {
+					printk(KERN_ERR "ippp, can't realloc slhc struct\n");
+					return -ENOMEM;
+				}
+				if (is->slcomp)
+					slhc_free(is->slcomp);
+				is->slcomp = sltmp;
+#endif
+			}
+			break;
+		case PPPIOCGDEBUG:
+			if ((r = set_arg(argp, &is->debug, sizeof(is->debug) )))
+				return r;
+			break;
+		case PPPIOCSDEBUG:
+			if ((r = get_arg(argp, &val, sizeof(val) )))
+				return r;
+			is->debug = val;
+			break;
+		case PPPIOCGCOMPRESSORS:
+			{
+				unsigned long protos[8] = {0,};
+				struct isdn_ppp_compressor *ipc = ipc_head;
+				while(ipc) {
+					j = ipc->num / (sizeof(long)*8);
+					i = ipc->num % (sizeof(long)*8);
+					if(j < 8)
+						protos[j] |= (0x1<<i);
+					ipc = ipc->next;
+				}
+				if ((r = set_arg(argp,protos,8*sizeof(long) )))
+					return r;
+			}
+			break;
+		case PPPIOCSCOMPRESSOR:
+			if ((r = get_arg(argp, &data, sizeof(struct isdn_ppp_comp_data))))
+				return r;
+			return isdn_ppp_set_compressor(is, &data);
+		case PPPIOCGCALLINFO:
+			{
+				struct pppcallinfo pci;
+				memset((char *) &pci,0,sizeof(struct pppcallinfo));
+				if(lp)
+				{
+					strncpy(pci.local_num,lp->msn,63);
+					if(lp->dial) {
+						strncpy(pci.remote_num,lp->dial->num,63);
+					}
+					pci.charge_units = lp->charge;
+					if(lp->outgoing)
+						pci.calltype = CALLTYPE_OUTGOING;
+					else
+						pci.calltype = CALLTYPE_INCOMING;
+					if(lp->flags & ISDN_NET_CALLBACK)
+						pci.calltype |= CALLTYPE_CALLBACK;
+				}
+				return set_arg(argp,&pci,sizeof(struct pppcallinfo));
+			}
+#ifdef CONFIG_IPPP_FILTER
+		case PPPIOCSPASS:
+			{
+				struct sock_filter *code;
+				int len = get_filter(argp, &code);
+				if (len < 0)
+					return len;
+				kfree(is->pass_filter);
+				is->pass_filter = code;
+				is->pass_len = len;
+				break;
+			}
+		case PPPIOCSACTIVE:
+			{
+				struct sock_filter *code;
+				int len = get_filter(argp, &code);
+				if (len < 0)
+					return len;
+				kfree(is->active_filter);
+				is->active_filter = code;
+				is->active_len = len;
+				break;
+			}
+#endif /* CONFIG_IPPP_FILTER */
+		default:
+			break;
+	}
+	return 0;
+}
+
+unsigned int
+isdn_ppp_poll(struct file *file, poll_table * wait)
+{
+	u_int mask;
+	struct ippp_buf_queue *bf, *bl;
+	u_long flags;
+	struct ippp_struct *is;
+
+	is = file->private_data;
+
+	if (is->debug & 0x2)
+		printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
+				MINOR(file->f_dentry->d_inode->i_rdev));
+
+	/* just registers wait_queue hook. This doesn't really wait. */
+	poll_wait(file, &is->wq, wait);
+
+	if (!(is->state & IPPP_OPEN)) {
+		if(is->state == IPPP_CLOSEWAIT)
+			return POLLHUP;
+		printk(KERN_DEBUG "isdn_ppp: device not open\n");
+		return POLLERR;
+	}
+	/* we're always ready to send .. */
+	mask = POLLOUT | POLLWRNORM;
+
+	spin_lock_irqsave(&is->buflock, flags);
+	bl = is->last;
+	bf = is->first;
+	/*
+	 * if IPPP_NOBLOCK is set we return even if we have nothing to read
+	 */
+	if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
+		is->state &= ~IPPP_NOBLOCK;
+		mask |= POLLIN | POLLRDNORM;
+	}
+	spin_unlock_irqrestore(&is->buflock, flags);
+	return mask;
+}
+
+/*
+ *  fill up isdn_ppp_read() queue ..
+ */
+
+static int
+isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
+{
+	struct ippp_buf_queue *bf, *bl;
+	u_long flags;
+	u_char *nbuf;
+	struct ippp_struct *is;
+
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_WARNING "ippp: illegal slot(%d).\n", slot);
+		return 0;
+	}
+	is = ippp_table[slot];
+
+	if (!(is->state & IPPP_CONNECT)) {
+		printk(KERN_DEBUG "ippp: device not activated.\n");
+		return 0;
+	}
+	nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
+	if (!nbuf) {
+		printk(KERN_WARNING "ippp: Can't alloc buf\n");
+		return 0;
+	}
+	nbuf[0] = PPP_ALLSTATIONS;
+	nbuf[1] = PPP_UI;
+	nbuf[2] = proto >> 8;
+	nbuf[3] = proto & 0xff;
+	memcpy(nbuf + 4, buf, len);
+
+	spin_lock_irqsave(&is->buflock, flags);
+	bf = is->first;
+	bl = is->last;
+
+	if (bf == bl) {
+		printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
+		bf = bf->next;
+		kfree(bf->buf);
+		is->first = bf;
+	}
+	bl->buf = (char *) nbuf;
+	bl->len = len + 4;
+
+	is->last = bl->next;
+	spin_unlock_irqrestore(&is->buflock, flags);
+	wake_up_interruptible(&is->wq);
+	return len;
+}
+
+/*
+ * read() .. non-blocking: ipppd calls it only after select()
+ *           reports, that there is data
+ */
+
+int
+isdn_ppp_read(int min, struct file *file, char __user *buf, int count)
+{
+	struct ippp_struct *is;
+	struct ippp_buf_queue *b;
+	u_long flags;
+	u_char *save_buf;
+
+	is = file->private_data;
+
+	if (!(is->state & IPPP_OPEN))
+		return 0;
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	spin_lock_irqsave(&is->buflock, flags);
+	b = is->first->next;
+	save_buf = b->buf;
+	if (!save_buf) {
+		spin_unlock_irqrestore(&is->buflock, flags);
+		return -EAGAIN;
+	}
+	if (b->len < count)
+		count = b->len;
+	b->buf = NULL;
+	is->first = b;
+
+	spin_unlock_irqrestore(&is->buflock, flags);
+	copy_to_user(buf, save_buf, count);
+	kfree(save_buf);
+
+	return count;
+}
+
+/*
+ * ipppd wanna write a packet to the card .. non-blocking
+ */
+
+int
+isdn_ppp_write(int min, struct file *file, const char __user *buf, int count)
+{
+	isdn_net_local *lp;
+	struct ippp_struct *is;
+	int proto;
+	unsigned char protobuf[4];
+
+	is = file->private_data;
+
+	if (!(is->state & IPPP_CONNECT))
+		return 0;
+
+	lp = is->lp;
+
+	/* -> push it directly to the lowlevel interface */
+
+	if (!lp)
+		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
+	else {
+		/*
+		 * Don't reset huptimer for
+		 * LCP packets. (Echo requests).
+		 */
+		if (copy_from_user(protobuf, buf, 4))
+			return -EFAULT;
+		proto = PPP_PROTOCOL(protobuf);
+		if (proto != PPP_LCP)
+			lp->huptimer = 0;
+
+		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
+			return 0;
+
+		if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
+			lp->dialstate == 0 &&
+		    (lp->flags & ISDN_NET_CONNECTED)) {
+			unsigned short hl;
+			struct sk_buff *skb;
+			/*
+			 * we need to reserve enought space in front of
+			 * sk_buff. old call to dev_alloc_skb only reserved
+			 * 16 bytes, now we are looking what the driver want
+			 */
+			hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+			skb = alloc_skb(hl+count, GFP_ATOMIC);
+			if (!skb) {
+				printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
+				return count;
+			}
+			skb_reserve(skb, hl);
+			if (copy_from_user(skb_put(skb, count), buf, count))
+			{
+				kfree_skb(skb);
+				return -EFAULT;
+			}
+			if (is->debug & 0x40) {
+				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
+				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+			}
+
+			isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
+
+			isdn_net_write_super(lp, skb);
+		}
+	}
+	return count;
+}
+
+/*
+ * init memory, structures etc.
+ */
+
+int
+isdn_ppp_init(void)
+{
+	int i,
+	 j;
+	 
+#ifdef CONFIG_ISDN_MPP
+	if( isdn_ppp_mp_bundle_array_init() < 0 )
+		return -ENOMEM;
+#endif /* CONFIG_ISDN_MPP */
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if (!(ippp_table[i] = (struct ippp_struct *)
+		      kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
+			printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+			for (j = 0; j < i; j++)
+				kfree(ippp_table[j]);
+			return -1;
+		}
+		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
+		spin_lock_init(&ippp_table[i]->buflock);
+		ippp_table[i]->state = 0;
+		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
+		ippp_table[i]->last = ippp_table[i]->rq;
+
+		for (j = 0; j < NUM_RCV_BUFFS; j++) {
+			ippp_table[i]->rq[j].buf = NULL;
+			ippp_table[i]->rq[j].last = ippp_table[i]->rq +
+			    (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
+			ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
+		}
+	}
+	return 0;
+}
+
+void
+isdn_ppp_cleanup(void)
+{
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		kfree(ippp_table[i]);
+
+#ifdef CONFIG_ISDN_MPP
+	if (isdn_ppp_bundle_arr)
+		kfree(isdn_ppp_bundle_arr);
+#endif /* CONFIG_ISDN_MPP */
+
+}
+
+/*
+ * check for address/control field and skip if allowed
+ * retval != 0 -> discard packet silently
+ */
+static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb) 
+{
+	if (skb->len < 1)
+		return -1;
+
+	if (skb->data[0] == 0xff) {
+		if (skb->len < 2)
+			return -1;
+
+		if (skb->data[1] != 0x03)
+			return -1;
+
+		// skip address/control (AC) field
+		skb_pull(skb, 2);
+	} else { 
+		if (is->pppcfg & SC_REJ_COMP_AC)
+			// if AC compression was not negotiated, but used, discard packet
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * get the PPP protocol header and pull skb
+ * retval < 0 -> discard packet silently
+ */
+static int isdn_ppp_strip_proto(struct sk_buff *skb) 
+{
+	int proto;
+	
+	if (skb->len < 1)
+		return -1;
+
+	if (skb->data[0] & 0x1) {
+		// protocol field is compressed
+		proto = skb->data[0];
+		skb_pull(skb, 1);
+	} else {
+		if (skb->len < 2)
+			return -1;
+		proto = ((int) skb->data[0] << 8) + skb->data[1];
+		skb_pull(skb, 2);
+	}
+	return proto;
+}
+
+
+/*
+ * handler for incoming packets on a syncPPP interface
+ */
+void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
+{
+	struct ippp_struct *is;
+	int slot;
+	int proto;
+
+	if (net_dev->local->master)
+		BUG(); // we're called with the master device always
+
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot(%d)\n",
+			lp->ppp_slot);
+		kfree_skb(skb);
+		return;
+	}
+	is = ippp_table[slot];
+
+	if (is->debug & 0x4) {
+		printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
+		       (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len);
+		isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+	}
+
+ 	if (isdn_ppp_skip_ac(is, skb) < 0) {
+ 		kfree_skb(skb);
+ 		return;
+ 	}
+  	proto = isdn_ppp_strip_proto(skb);
+ 	if (proto < 0) {
+ 		kfree_skb(skb);
+ 		return;
+ 	}
+  
+#ifdef CONFIG_ISDN_MPP
+ 	if (is->compflags & SC_LINK_DECOMP_ON) {
+ 		skb = isdn_ppp_decompress(skb, is, NULL, &proto);
+ 		if (!skb) // decompression error
+ 			return;
+ 	}
+	
+ 	if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP
+  		if (proto == PPP_MP) {
+  			isdn_ppp_mp_receive(net_dev, lp, skb);
+ 			return;
+ 		}
+ 	} 
+#endif
+ 	isdn_ppp_push_higher(net_dev, lp, skb, proto);
+}
+
+/*
+ * we receive a reassembled frame, MPPP has been taken care of before.
+ * address/control and protocol have been stripped from the skb
+ * note: net_dev has to be master net_dev
+ */
+static void
+isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
+{
+	struct net_device *dev = &net_dev->dev;
+ 	struct ippp_struct *is, *mis;
+	isdn_net_local *mlp = NULL;
+	int slot;
+
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot(%d)\n",
+			lp->ppp_slot);
+		goto drop_packet;
+	}
+	is = ippp_table[slot];
+ 	
+ 	if (lp->master) { // FIXME?
+		mlp = (isdn_net_local *) lp->master->priv;
+ 		slot = mlp->ppp_slot;
+ 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+ 			printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n",
+ 				lp->ppp_slot);
+			goto drop_packet;
+ 		}
+ 	}
+ 	mis = ippp_table[slot];
+
+	if (is->debug & 0x10) {
+		printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
+		isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+	}
+	if (mis->compflags & SC_DECOMP_ON) {
+		skb = isdn_ppp_decompress(skb, is, mis, &proto);
+		if (!skb) // decompression error
+  			return;
+  	}
+	switch (proto) {
+		case PPP_IPX:  /* untested */
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IPX\n");
+			skb->protocol = htons(ETH_P_IPX);
+			break;
+		case PPP_IP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: IP\n");
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case PPP_COMP:
+		case PPP_COMPFRAG:
+			printk(KERN_INFO "isdn_ppp: unexpected compressed frame dropped\n");
+			goto drop_packet;
+#ifdef CONFIG_ISDN_PPP_VJ
+		case PPP_VJC_UNCOMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
+			if (net_dev->local->ppp_slot < 0) {
+				printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
+					__FUNCTION__, net_dev->local->ppp_slot);
+				goto drop_packet;
+			}
+			if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+				printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
+				goto drop_packet;
+			}
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case PPP_VJC_COMP:
+			if (is->debug & 0x20)
+				printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+			{
+				struct sk_buff *skb_old = skb;
+				int pkt_len;
+				skb = dev_alloc_skb(skb_old->len + 128);
+
+				if (!skb) {
+					printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+					skb = skb_old;
+					goto drop_packet;
+				}
+				skb_put(skb, skb_old->len + 128);
+				memcpy(skb->data, skb_old->data, skb_old->len);
+				if (net_dev->local->ppp_slot < 0) {
+					printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
+						__FUNCTION__, net_dev->local->ppp_slot);
+					goto drop_packet;
+				}
+				pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
+						skb->data, skb_old->len);
+				kfree_skb(skb_old);
+				if (pkt_len < 0)
+					goto drop_packet;
+
+				skb_trim(skb, pkt_len);
+				skb->protocol = htons(ETH_P_IP);
+			}
+			break;
+#endif
+		case PPP_CCP:
+		case PPP_CCPFRAG:
+			isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
+			/* Dont pop up ResetReq/Ack stuff to the daemon any
+			   longer - the job is done already */
+			if(skb->data[0] == CCP_RESETREQ ||
+			   skb->data[0] == CCP_RESETACK)
+				break;
+			/* fall through */
+		default:
+			isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot);	/* push data to pppd device */
+			kfree_skb(skb);
+			return;
+	}
+
+#ifdef CONFIG_IPPP_FILTER
+	/* check if the packet passes the pass and active filters
+	 * the filter instructions are constructed assuming
+	 * a four-byte PPP header on each packet (which is still present) */
+	skb_push(skb, 4);
+
+	{
+		u_int16_t *p = (u_int16_t *) skb->data;
+
+		*p = 0;	/* indicate inbound in DLT_LINUX_SLL */
+	}
+
+	if (is->pass_filter
+	    && sk_run_filter(skb, is->pass_filter, is->pass_len) == 0) {
+		if (is->debug & 0x2)
+			printk(KERN_DEBUG "IPPP: inbound frame filtered.\n");
+		kfree_skb(skb);
+		return;
+	}
+	if (!(is->active_filter
+	      && sk_run_filter(skb, is->active_filter,
+	                       is->active_len) == 0)) {
+		if (is->debug & 0x2)
+			printk(KERN_DEBUG "IPPP: link-active filter: reseting huptimer.\n");
+		lp->huptimer = 0;
+		if (mlp)
+			mlp->huptimer = 0;
+	}
+	skb_pull(skb, 4);
+#else /* CONFIG_IPPP_FILTER */
+	lp->huptimer = 0;
+	if (mlp)
+		mlp->huptimer = 0;
+#endif /* CONFIG_IPPP_FILTER */
+	skb->dev = dev;
+	skb->input_dev = dev;
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	/* net_dev->local->stats.rx_packets++; done in isdn_net.c */
+	return;
+
+ drop_packet:
+	net_dev->local->stats.rx_dropped++;
+	kfree_skb(skb);
+}
+
+/*
+ * isdn_ppp_skb_push ..
+ * checks whether we have enough space at the beginning of the skb
+ * and allocs a new SKB if necessary
+ */
+static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
+{
+	struct sk_buff *skb = *skb_p;
+
+	if(skb_headroom(skb) < len) {
+		struct sk_buff *nskb = skb_realloc_headroom(skb, len);
+
+		if (!nskb) {
+			printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n");
+			dev_kfree_skb(skb);
+			return NULL;
+		}
+		printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len);
+		dev_kfree_skb(skb);
+		*skb_p = nskb;
+		return skb_push(nskb, len);
+	}
+	return skb_push(skb,len);
+}
+
+/*
+ * send ppp frame .. we expect a PIDCOMPressable proto --
+ *  (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
+ *
+ * VJ compression may change skb pointer!!! .. requeue with old
+ * skb isn't allowed!!
+ */
+
+int
+isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	isdn_net_local *lp,*mlp;
+	isdn_net_dev *nd;
+	unsigned int proto = PPP_IP;     /* 0x21 */
+	struct ippp_struct *ipt,*ipts;
+	int slot, retval = 0;
+
+	mlp = (isdn_net_local *) (netdev->priv);
+	nd = mlp->netdev;       /* get master lp */
+
+	slot = mlp->ppp_slot;
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
+			mlp->ppp_slot);
+		kfree_skb(skb);
+		goto out;
+	}
+	ipts = ippp_table[slot];
+
+	if (!(ipts->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
+		if (ipts->debug & 0x1)
+			printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name);
+		retval = 1;
+		goto out;
+	}
+
+	switch (ntohs(skb->protocol)) {
+		case ETH_P_IP:
+			proto = PPP_IP;
+			break;
+		case ETH_P_IPX:
+			proto = PPP_IPX;	/* untested */
+			break;
+		default:
+			printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n", 
+			       skb->protocol);
+			dev_kfree_skb(skb);
+			goto out;
+	}
+
+	lp = isdn_net_get_locked_lp(nd);
+	if (!lp) {
+		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
+		retval = 1;
+		goto out;
+	}
+	/* we have our lp locked from now on */
+
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
+			lp->ppp_slot);
+		kfree_skb(skb);
+		goto unlock;
+	}
+	ipt = ippp_table[slot];
+
+	/*
+	 * after this line .. requeueing in the device queue is no longer allowed!!!
+	 */
+
+	/* Pull off the fake header we stuck on earlier to keep
+	 * the fragmentation code happy.
+	 */
+	skb_pull(skb,IPPP_MAX_HEADER);
+
+#ifdef CONFIG_IPPP_FILTER
+	/* check if we should pass this packet
+	 * the filter instructions are constructed assuming
+	 * a four-byte PPP header on each packet */
+	skb_push(skb, 4);
+
+	{
+		u_int16_t *p = (u_int16_t *) skb->data;
+
+		*p++ = htons(4); /* indicate outbound in DLT_LINUX_SLL */
+		*p   = htons(proto);
+	}
+
+	if (ipt->pass_filter
+	    && sk_run_filter(skb, ipt->pass_filter, ipt->pass_len) == 0) {
+		if (ipt->debug & 0x4)
+			printk(KERN_DEBUG "IPPP: outbound frame filtered.\n");
+		kfree_skb(skb);
+		goto unlock;
+	}
+	if (!(ipt->active_filter
+	      && sk_run_filter(skb, ipt->active_filter,
+		               ipt->active_len) == 0)) {
+		if (ipt->debug & 0x4)
+			printk(KERN_DEBUG "IPPP: link-active filter: reseting huptimer.\n");
+		lp->huptimer = 0;
+	}
+	skb_pull(skb, 4);
+#else /* CONFIG_IPPP_FILTER */
+	lp->huptimer = 0;
+#endif /* CONFIG_IPPP_FILTER */
+
+	if (ipt->debug & 0x4)
+		printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
+        if (ipts->debug & 0x40)
+                isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot);
+
+#ifdef CONFIG_ISDN_PPP_VJ
+	if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) {	/* ipts here? probably yes, but check this again */
+		struct sk_buff *new_skb;
+	        unsigned short hl;
+		/*
+		 * we need to reserve enought space in front of
+		 * sk_buff. old call to dev_alloc_skb only reserved
+		 * 16 bytes, now we are looking what the driver want.
+		 */
+		hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER;
+		/* 
+		 * Note: hl might still be insufficient because the method
+		 * above does not account for a possibible MPPP slave channel
+		 * which had larger HL header space requirements than the
+		 * master.
+		 */
+		new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC);
+		if (new_skb) {
+			u_char *buf;
+			int pktlen;
+
+			skb_reserve(new_skb, hl);
+			new_skb->dev = skb->dev;
+			skb_put(new_skb, skb->len);
+			buf = skb->data;
+
+			pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
+				 &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+
+			if (buf != skb->data) {	
+				if (new_skb->data != buf)
+					printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
+				dev_kfree_skb(skb);
+				skb = new_skb;
+			} else {
+				dev_kfree_skb(new_skb);
+			}
+
+			skb_trim(skb, pktlen);
+			if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) {	/* cslip? style -> PPP */
+				proto = PPP_VJC_COMP;
+				skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
+			} else {
+				if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
+					proto = PPP_VJC_UNCOMP;
+				skb->data[0] = (skb->data[0] & 0x0f) | 0x40;
+			}
+		}
+	}
+#endif
+
+	/*
+	 * normal (single link) or bundle compression
+	 */
+	if(ipts->compflags & SC_COMP_ON) {
+		/* We send compressed only if both down- und upstream
+		   compression is negotiated, that means, CCP is up */
+		if(ipts->compflags & SC_DECOMP_ON) {
+			skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+		} else {
+			printk(KERN_DEBUG "isdn_ppp: CCP not yet up - sending as-is\n");
+		}
+	}
+
+	if (ipt->debug & 0x24)
+		printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
+
+#ifdef CONFIG_ISDN_MPP
+	if (ipt->mpppcfg & SC_MP_PROT) {
+		/* we get mp_seqno from static isdn_net_local */
+		long mp_seqno = ipts->mp_seqno;
+		ipts->mp_seqno++;
+		if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
+			unsigned char *data = isdn_ppp_skb_push(&skb, 3);
+			if(!data)
+				goto unlock;
+			mp_seqno &= 0xfff;
+			data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf);	/* (B)egin & (E)ndbit .. */
+			data[1] = mp_seqno & 0xff;
+			data[2] = proto;	/* PID compression */
+		} else {
+			unsigned char *data = isdn_ppp_skb_push(&skb, 5);
+			if(!data)
+				goto unlock;
+			data[0] = MP_BEGIN_FRAG | MP_END_FRAG;	/* (B)egin & (E)ndbit .. */
+			data[1] = (mp_seqno >> 16) & 0xff;	/* sequence number: 24bit */
+			data[2] = (mp_seqno >> 8) & 0xff;
+			data[3] = (mp_seqno >> 0) & 0xff;
+			data[4] = proto;	/* PID compression */
+		}
+		proto = PPP_MP; /* MP Protocol, 0x003d */
+	}
+#endif
+
+	/*
+	 * 'link in bundle' compression  ...
+	 */
+	if(ipt->compflags & SC_LINK_COMP_ON)
+		skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+
+	if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
+		unsigned char *data = isdn_ppp_skb_push(&skb,1);
+		if(!data)
+			goto unlock;
+		data[0] = proto & 0xff;
+	}
+	else {
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			goto unlock;
+		data[0] = (proto >> 8) & 0xff;
+		data[1] = proto & 0xff;
+	}
+	if(!(ipt->pppcfg & SC_COMP_AC)) {
+		unsigned char *data = isdn_ppp_skb_push(&skb,2);
+		if(!data)
+			goto unlock;
+		data[0] = 0xff;    /* All Stations */
+		data[1] = 0x03;    /* Unnumbered information */
+	}
+
+	/* tx-stats are now updated via BSENT-callback */
+
+	if (ipts->debug & 0x40) {
+		printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
+		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
+	}
+	
+	isdn_net_writebuf_skb(lp, skb);
+
+ unlock:
+	spin_unlock_bh(&lp->xmit_lock);
+ out:
+	return retval;
+}
+
+#ifdef CONFIG_IPPP_FILTER
+/*
+ * check if this packet may trigger auto-dial.
+ */
+
+int isdn_ppp_autodial_filter(struct sk_buff *skb, isdn_net_local *lp)
+{
+	struct ippp_struct *is = ippp_table[lp->ppp_slot];
+	u_int16_t proto;
+	int drop = 0;
+
+	switch (ntohs(skb->protocol)) {
+	case ETH_P_IP:
+		proto = PPP_IP;
+		break;
+	case ETH_P_IPX:
+		proto = PPP_IPX;
+		break;
+	default:
+		printk(KERN_ERR "isdn_ppp_autodial_filter: unsupported protocol 0x%x.\n",
+		       skb->protocol);
+		return 1;
+	}
+
+	/* the filter instructions are constructed assuming
+	 * a four-byte PPP header on each packet. we have to
+	 * temporarily remove part of the fake header stuck on
+	 * earlier.
+	 */
+	skb_pull(skb, IPPP_MAX_HEADER - 4);
+
+	{
+		u_int16_t *p = (u_int16_t *) skb->data;
+
+		*p++ = htons(4);	/* indicate outbound in DLT_LINUX_SLL */
+		*p   = htons(proto);
+	}
+	
+	drop |= is->pass_filter
+	        && sk_run_filter(skb, is->pass_filter, is->pass_len) == 0;
+	drop |= is->active_filter
+	        && sk_run_filter(skb, is->active_filter, is->active_len) == 0;
+	
+	skb_push(skb, IPPP_MAX_HEADER - 4);
+	return drop;
+}
+#endif
+#ifdef CONFIG_ISDN_MPP
+
+/* this is _not_ rfc1990 header, but something we convert both short and long
+ * headers to for convinience's sake:
+ * 	byte 0 is flags as in rfc1990
+ *	bytes 1...4 is 24-bit seqence number converted to host byte order 
+ */
+#define MP_HEADER_LEN	5
+
+#define MP_LONGSEQ_MASK		0x00ffffff
+#define MP_SHORTSEQ_MASK	0x00000fff
+#define MP_LONGSEQ_MAX		MP_LONGSEQ_MASK
+#define MP_SHORTSEQ_MAX		MP_SHORTSEQ_MASK
+#define MP_LONGSEQ_MAXBIT	((MP_LONGSEQ_MASK+1)>>1)
+#define MP_SHORTSEQ_MAXBIT	((MP_SHORTSEQ_MASK+1)>>1)
+
+/* sequence-wrap safe comparisions (for long sequence)*/ 
+#define MP_LT(a,b)	((a-b)&MP_LONGSEQ_MAXBIT)
+#define MP_LE(a,b) 	!((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GT(a,b) 	((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GE(a,b)	!((a-b)&MP_LONGSEQ_MAXBIT)
+
+#define MP_SEQ(f)	((*(u32*)(f->data+1)))
+#define MP_FLAGS(f)	(f->data[0])
+
+static int isdn_ppp_mp_bundle_array_init(void)
+{
+	int i;
+	int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
+	if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz, 
+							GFP_KERNEL)) == NULL )
+		return -ENOMEM;
+	memset(isdn_ppp_bundle_arr, 0, sz);
+	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
+		spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
+	return 0;
+}
+
+static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
+{
+	int i;
+	for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
+		if (isdn_ppp_bundle_arr[i].ref_ct <= 0)
+			return (isdn_ppp_bundle_arr + i);
+	return NULL;
+}
+
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
+{
+	struct ippp_struct * is;
+
+	if (lp->ppp_slot < 0) {
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
+		return(-EINVAL);
+	}
+
+	is = ippp_table[lp->ppp_slot];
+	if (add_to) {
+		if( lp->netdev->pb )
+			lp->netdev->pb->ref_ct--;
+		lp->netdev->pb = add_to;
+	} else {		/* first link in a bundle */
+		is->mp_seqno = 0;
+		if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
+			return -ENOMEM;
+		lp->next = lp->last = lp;	/* nobody else in a queue */
+		lp->netdev->pb->frags = NULL;
+		lp->netdev->pb->frames = 0;
+		lp->netdev->pb->seq = UINT_MAX;
+	}
+	lp->netdev->pb->ref_ct++;
+	
+	is->last_link_seqno = 0;
+	return 0;
+}
+
+static u32 isdn_ppp_mp_get_seq( int short_seq, 
+					struct sk_buff * skb, u32 last_seq );
+static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+			struct sk_buff * from, struct sk_buff * to );
+static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff * from, struct sk_buff * to );
+static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );
+static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );
+
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
+							struct sk_buff *skb)
+{
+	struct ippp_struct *is;
+	isdn_net_local * lpq;
+	ippp_bundle * mp;
+	isdn_mppp_stats * stats;
+	struct sk_buff * newfrag, * frag, * start, *nextf;
+	u32 newseq, minseq, thisseq;
+	unsigned long flags;
+	int slot;
+
+	spin_lock_irqsave(&net_dev->pb->lock, flags);
+    	mp = net_dev->pb;
+        stats = &mp->stats;
+	slot = lp->ppp_slot;
+	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: lp->ppp_slot(%d)\n",
+			__FUNCTION__, lp->ppp_slot);
+		stats->frame_drops++;
+		dev_kfree_skb(skb);
+		spin_unlock_irqrestore(&mp->lock, flags);
+		return;
+	}
+	is = ippp_table[slot];
+    	if( ++mp->frames > stats->max_queue_len )
+		stats->max_queue_len = mp->frames;
+	
+	if (is->debug & 0x8)
+		isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
+
+	newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, 
+						skb, is->last_link_seqno);
+
+
+	/* if this packet seq # is less than last already processed one,
+	 * toss it right away, but check for sequence start case first 
+	 */
+	if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) {
+		mp->seq = newseq;	/* the first packet: required for
+					 * rfc1990 non-compliant clients --
+					 * prevents constant packet toss */
+	} else if( MP_LT(newseq, mp->seq) ) {
+		stats->frame_drops++;
+		isdn_ppp_mp_free_skb(mp, skb);
+		spin_unlock_irqrestore(&mp->lock, flags);
+		return;
+	}
+	
+	/* find the minimum received sequence number over all links */
+	is->last_link_seqno = minseq = newseq;
+	for (lpq = net_dev->queue;;) {
+		slot = lpq->ppp_slot;
+		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+			printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",
+				__FUNCTION__, lpq->ppp_slot);
+		} else {
+			u32 lls = ippp_table[slot]->last_link_seqno;
+			if (MP_LT(lls, minseq))
+				minseq = lls;
+		}
+		if ((lpq = lpq->next) == net_dev->queue)
+			break;
+	}
+	if (MP_LT(minseq, mp->seq))
+		minseq = mp->seq;	/* can't go beyond already processed
+					 * packets */
+	newfrag = skb;
+
+  	/* if this new fragment is before the first one, then enqueue it now. */
+  	if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) {
+		newfrag->next = frag;
+    		mp->frags = frag = newfrag;
+    		newfrag = NULL;
+  	}
+
+  	start = MP_FLAGS(frag) & MP_BEGIN_FRAG &&
+				MP_SEQ(frag) == mp->seq ? frag : NULL;
+
+	/* 
+	 * main fragment traversing loop
+	 *
+	 * try to accomplish several tasks:
+	 * - insert new fragment into the proper sequence slot (once that's done
+	 *   newfrag will be set to NULL)
+	 * - reassemble any complete fragment sequence (non-null 'start'
+	 *   indicates there is a continguous sequence present)
+	 * - discard any incomplete sequences that are below minseq -- due
+	 *   to the fact that sender always increment sequence number, if there
+	 *   is an incomplete sequence below minseq, no new fragments would
+	 *   come to complete such sequence and it should be discarded
+	 *
+	 * loop completes when we accomplished the following tasks:
+	 * - new fragment is inserted in the proper sequence ('newfrag' is 
+	 *   set to NULL)
+	 * - we hit a gap in the sequence, so no reassembly/processing is 
+	 *   possible ('start' would be set to NULL)
+	 *
+	 * algorightm for this code is derived from code in the book
+	 * 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
+	 */
+  	while (start != NULL || newfrag != NULL) {
+
+    		thisseq = MP_SEQ(frag);
+    		nextf = frag->next;
+
+    		/* drop any duplicate fragments */
+    		if (newfrag != NULL && thisseq == newseq) {
+      			isdn_ppp_mp_free_skb(mp, newfrag);
+      			newfrag = NULL;
+    		}
+
+    		/* insert new fragment before next element if possible. */
+    		if (newfrag != NULL && (nextf == NULL || 
+						MP_LT(newseq, MP_SEQ(nextf)))) {
+      			newfrag->next = nextf;
+      			frag->next = nextf = newfrag;
+      			newfrag = NULL;
+    		}
+
+    		if (start != NULL) {
+	    		/* check for misplaced start */
+      			if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {
+				printk(KERN_WARNING"isdn_mppp(seq %d): new "
+				      "BEGIN flag with no prior END", thisseq);
+				stats->seqerrs++;
+				stats->frame_drops++;
+				start = isdn_ppp_mp_discard(mp, start,frag);
+				nextf = frag->next;
+      			}
+    		} else if (MP_LE(thisseq, minseq)) {		
+      			if (MP_FLAGS(frag) & MP_BEGIN_FRAG)
+				start = frag;
+      			else {
+				if (MP_FLAGS(frag) & MP_END_FRAG)
+	  				stats->frame_drops++;
+				if( mp->frags == frag )
+					mp->frags = nextf;	
+				isdn_ppp_mp_free_skb(mp, frag);
+				frag = nextf;
+				continue;
+      			}
+		}
+		
+		/* if start is non-null and we have end fragment, then
+		 * we have full reassembly sequence -- reassemble 
+		 * and process packet now
+		 */
+    		if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) {
+      			minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
+      			/* Reassemble the packet then dispatch it */
+			isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);
+      
+      			start = NULL;
+      			frag = NULL;
+
+      			mp->frags = nextf;
+    		}
+
+		/* check if need to update start pointer: if we just
+		 * reassembled the packet and sequence is contiguous
+		 * then next fragment should be the start of new reassembly
+		 * if sequence is contiguous, but we haven't reassembled yet,
+		 * keep going.
+		 * if sequence is not contiguous, either clear everyting
+		 * below low watermark and set start to the next frag or
+		 * clear start ptr.
+		 */ 
+    		if (nextf != NULL && 
+		    ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
+      			/* if we just reassembled and the next one is here, 
+			 * then start another reassembly. */
+
+      			if (frag == NULL) {
+				if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
+	  				start = nextf;
+				else
+				{
+	  				printk(KERN_WARNING"isdn_mppp(seq %d):"
+						" END flag with no following "
+						"BEGIN", thisseq);
+					stats->seqerrs++;
+				}
+			}
+
+    		} else {
+			if ( nextf != NULL && frag != NULL &&
+						MP_LT(thisseq, minseq)) {
+				/* we've got a break in the sequence
+				 * and we not at the end yet
+				 * and we did not just reassembled
+				 *(if we did, there wouldn't be anything before)
+				 * and we below the low watermark 
+			 	 * discard all the frames below low watermark 
+				 * and start over */
+				stats->frame_drops++;
+				mp->frags = isdn_ppp_mp_discard(mp,start,nextf);
+			}
+			/* break in the sequence, no reassembly */
+      			start = NULL;
+    		}
+	  			
+    		frag = nextf;
+  	}	/* while -- main loop */
+	
+  	if (mp->frags == NULL)
+    		mp->frags = frag;
+		
+	/* rather straighforward way to deal with (not very) possible 
+	 * queue overflow */
+	if (mp->frames > MP_MAX_QUEUE_LEN) {
+		stats->overflows++;
+		while (mp->frames > MP_MAX_QUEUE_LEN) {
+			frag = mp->frags->next;
+			isdn_ppp_mp_free_skb(mp, mp->frags);
+			mp->frags = frag;
+		}
+	}
+	spin_unlock_irqrestore(&mp->lock, flags);
+}
+
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp )
+{
+	struct sk_buff * frag = lp->netdev->pb->frags;
+	struct sk_buff * nextfrag;
+    	while( frag ) {
+		nextfrag = frag->next;
+		isdn_ppp_mp_free_skb(lp->netdev->pb, frag);
+		frag = nextfrag;
+	}
+	lp->netdev->pb->frags = NULL;
+}
+
+static u32 isdn_ppp_mp_get_seq( int short_seq, 
+					struct sk_buff * skb, u32 last_seq )
+{
+	u32 seq;
+	int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);
+   
+   	if( !short_seq )
+	{
+		seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK;
+		skb_push(skb,1);
+	}
+	else
+	{
+		/* convert 12-bit short seq number to 24-bit long one 
+	 	*/
+		seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK;
+	
+		/* check for seqence wrap */
+		if( !(seq &  MP_SHORTSEQ_MAXBIT) && 
+		     (last_seq &  MP_SHORTSEQ_MAXBIT) && 
+		     (unsigned long)last_seq <= MP_LONGSEQ_MAX )
+			seq |= (last_seq + MP_SHORTSEQ_MAX+1) & 
+					(~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+		else
+			seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+		
+		skb_push(skb, 3);	/* put converted seqence back in skb */
+	}
+	*(u32*)(skb->data+1) = seq; 	/* put seqence back in _host_ byte
+					 * order */
+	skb->data[0] = flags;	        /* restore flags */
+	return seq;
+}
+
+struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp,
+			struct sk_buff * from, struct sk_buff * to )
+{
+	if( from )
+		while (from != to) {
+	  		struct sk_buff * next = from->next;
+			isdn_ppp_mp_free_skb(mp, from);
+	  		from = next;
+		}
+	return from;
+}
+
+void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp,
+				struct sk_buff * from, struct sk_buff * to )
+{
+	ippp_bundle * mp = net_dev->pb;
+	int proto;
+	struct sk_buff * skb;
+	unsigned int tot_len;
+
+	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
+		return;
+	}
+	if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
+		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
+			printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, "
+					"len %d\n", MP_SEQ(from), from->len );
+		skb = from;
+		skb_pull(skb, MP_HEADER_LEN);
+		mp->frames--;	
+	} else {
+		struct sk_buff * frag;
+		int n;
+
+		for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++)
+			tot_len += frag->len - MP_HEADER_LEN;
+
+		if( ippp_table[lp->ppp_slot]->debug & 0x40 )
+			printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
+				"to %d, len %d\n", MP_SEQ(from), 
+				(MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len );
+		if( (skb = dev_alloc_skb(tot_len)) == NULL ) {
+			printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
+					"of size %d\n", tot_len);
+			isdn_ppp_mp_discard(mp, from, to);
+			return;
+		}
+
+		while( from != to ) {
+			unsigned int len = from->len - MP_HEADER_LEN;
+
+			memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len);
+			frag = from->next;
+			isdn_ppp_mp_free_skb(mp, from);
+			from = frag; 
+		}
+	}
+   	proto = isdn_ppp_strip_proto(skb);
+	isdn_ppp_push_higher(net_dev, lp, skb, proto);
+}
+
+static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
+{
+	dev_kfree_skb(skb);
+	mp->frames--;
+}
+
+static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb )
+{
+	printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n", 
+		slot, (int) skb->len, 
+		(int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
+		(int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+}
+
+static int
+isdn_ppp_bundle(struct ippp_struct *is, int unit)
+{
+	char ifn[IFNAMSIZ + 1];
+	isdn_net_dev *p;
+	isdn_net_local *lp, *nlp;
+	int rc;
+	unsigned long flags;
+
+	sprintf(ifn, "ippp%d", unit);
+	p = isdn_net_findif(ifn);
+	if (!p) {
+		printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);
+		return -EINVAL;
+	}
+
+    	spin_lock_irqsave(&p->pb->lock, flags);
+
+	nlp = is->lp;
+	lp = p->queue;
+	if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ||
+		lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) {
+		printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
+			nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? 
+			nlp->ppp_slot : lp->ppp_slot );
+		rc = -EINVAL;
+		goto out;
+ 	}
+
+	isdn_net_add_to_bundle(p, nlp);
+
+	ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
+
+	/* maybe also SC_CCP stuff */
+	ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
+		(SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
+	ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
+		(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+	rc = isdn_ppp_mp_init(nlp, p->pb);
+out:
+	spin_unlock_irqrestore(&p->pb->lock, flags);
+	return rc;
+}
+  
+#endif /* CONFIG_ISDN_MPP */
+  
+/*
+ * network device ioctl handlers
+ */
+
+static int
+isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev)
+{
+	struct ppp_stats __user *res = ifr->ifr_data;
+	struct ppp_stats t;
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+	if (!access_ok(VERIFY_WRITE, res, sizeof(struct ppp_stats)))
+		return -EFAULT;
+
+	/* build a temporary stat struct and copy it to user space */
+
+	memset(&t, 0, sizeof(struct ppp_stats));
+	if (dev->flags & IFF_UP) {
+		t.p.ppp_ipackets = lp->stats.rx_packets;
+		t.p.ppp_ibytes = lp->stats.rx_bytes;
+		t.p.ppp_ierrors = lp->stats.rx_errors;
+		t.p.ppp_opackets = lp->stats.tx_packets;
+		t.p.ppp_obytes = lp->stats.tx_bytes;
+		t.p.ppp_oerrors = lp->stats.tx_errors;
+#ifdef CONFIG_ISDN_PPP_VJ
+		if (slot >= 0 && ippp_table[slot]->slcomp) {
+			struct slcompress *slcomp = ippp_table[slot]->slcomp;
+			t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed;
+			t.vj.vjs_compressed = slcomp->sls_o_compressed;
+			t.vj.vjs_searches = slcomp->sls_o_searches;
+			t.vj.vjs_misses = slcomp->sls_o_misses;
+			t.vj.vjs_errorin = slcomp->sls_i_error;
+			t.vj.vjs_tossed = slcomp->sls_i_tossed;
+			t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
+			t.vj.vjs_compressedin = slcomp->sls_i_compressed;
+		}
+#endif
+	}
+	if (copy_to_user(res, &t, sizeof(struct ppp_stats)))
+		return -EFAULT;
+	return 0;
+}
+
+int
+isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	int error=0;
+	int len;
+	isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+
+	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+		return -EINVAL;
+
+	switch (cmd) {
+#define PPP_VERSION "2.3.7"
+		case SIOCGPPPVER:
+			len = strlen(PPP_VERSION) + 1;
+			if (copy_to_user(ifr->ifr_data, PPP_VERSION, len))
+				error = -EFAULT;
+			break;
+
+		case SIOCGPPPSTATS:
+			error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev);
+			break;
+		default:
+			error = -EINVAL;
+			break;
+	}
+	return error;
+}
+
+static int
+isdn_ppp_if_get_unit(char *name)
+{
+	int len,
+	 i,
+	 unit = 0,
+	 deci;
+
+	len = strlen(name);
+
+	if (strncmp("ippp", name, 4) || len > 8)
+		return -1;
+
+	for (i = 0, deci = 1; i < len; i++, deci *= 10) {
+		char a = name[len - i - 1];
+		if (a >= '0' && a <= '9')
+			unit += (a - '0') * deci;
+		else
+			break;
+	}
+	if (!i || len - i != 4)
+		unit = -1;
+
+	return unit;
+}
+
+
+int
+isdn_ppp_dial_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+	isdn_net_dev *ndev;
+	isdn_net_local *lp;
+	struct net_device *sdev;
+
+	if (!(ndev = isdn_net_findif(name)))
+		return 1;
+	lp = ndev->local;
+	if (!(lp->flags & ISDN_NET_CONNECTED))
+		return 5;
+
+	sdev = lp->slave;
+	while (sdev) {
+		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+		if (!(mlp->flags & ISDN_NET_CONNECTED))
+			break;
+		sdev = mlp->slave;
+	}
+	if (!sdev)
+		return 2;
+
+	isdn_net_dial_req((isdn_net_local *) sdev->priv);
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+int
+isdn_ppp_hangup_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+	isdn_net_dev *ndev;
+	isdn_net_local *lp;
+	struct net_device *sdev;
+
+	if (!(ndev = isdn_net_findif(name)))
+		return 1;
+	lp = ndev->local;
+	if (!(lp->flags & ISDN_NET_CONNECTED))
+		return 5;
+
+	sdev = lp->slave;
+	while (sdev) {
+		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+
+		if (mlp->slave) { /* find last connected link in chain */
+			isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv;
+
+			if (!(nlp->flags & ISDN_NET_CONNECTED))
+				break;
+		} else if (mlp->flags & ISDN_NET_CONNECTED)
+			break;
+		
+		sdev = mlp->slave;
+	}
+	if (!sdev)
+		return 2;
+
+	isdn_net_hangup(sdev);
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+/*
+ * PPP compression stuff
+ */
+
+
+/* Push an empty CCP Data Frame up to the daemon to wake it up and let it
+   generate a CCP Reset-Request or tear down CCP altogether */
+
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is)
+{
+	isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);
+}
+
+/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary,
+   but absolutely nontrivial. The most abstruse problem we are facing is
+   that the generation, reception and all the handling of timeouts and
+   resends including proper request id management should be entirely left
+   to the (de)compressor, but indeed is not covered by the current API to
+   the (de)compressor. The API is a prototype version from PPP where only
+   some (de)compressors have yet been implemented and all of them are
+   rather simple in their reset handling. Especially, their is only one
+   outstanding ResetAck at a time with all of them and ResetReq/-Acks do
+   not have parameters. For this very special case it was sufficient to
+   just return an error code from the decompressor and have a single
+   reset() entry to communicate all the necessary information between
+   the framework and the (de)compressor. Bad enough, LZS is different
+   (and any other compressor may be different, too). It has multiple
+   histories (eventually) and needs to Reset each of them independently
+   and thus uses multiple outstanding Acks and history numbers as an
+   additional parameter to Reqs/Acks.
+   All that makes it harder to port the reset state engine into the
+   kernel because it is not just the same simple one as in (i)pppd but
+   it must be able to pass additional parameters and have multiple out-
+   standing Acks. We are trying to achieve the impossible by handling
+   reset transactions independent by their id. The id MUST change when
+   the data portion changes, thus any (de)compressor who uses more than
+   one resettable state must provide and recognize individual ids for
+   each individual reset transaction. The framework itself does _only_
+   differentiate them by id, because it has no other semantics like the
+   (de)compressor might.
+   This looks like a major redesign of the interface would be nice,
+   but I don't have an idea how to do it better. */
+
+/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is
+   getting that lengthy because there is no simple "send-this-frame-out"
+   function above but every wrapper does a bit different. Hope I guess
+   correct in this hack... */
+
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+				    unsigned char code, unsigned char id,
+				    unsigned char *data, int len)
+{
+	struct sk_buff *skb;
+	unsigned char *p;
+	int hl;
+	int cnt = 0;
+	isdn_net_local *lp = is->lp;
+
+	/* Alloc large enough skb */
+	hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+	skb = alloc_skb(len + hl + 16,GFP_ATOMIC);
+	if(!skb) {
+		printk(KERN_WARNING
+		       "ippp: CCP cannot send reset - out of memory\n");
+		return;
+	}
+	skb_reserve(skb, hl);
+
+	/* We may need to stuff an address and control field first */
+	if(!(is->pppcfg & SC_COMP_AC)) {
+		p = skb_put(skb, 2);
+		*p++ = 0xff;
+		*p++ = 0x03;
+	}
+
+	/* Stuff proto, code, id and length */
+	p = skb_put(skb, 6);
+	*p++ = (proto >> 8);
+	*p++ = (proto & 0xff);
+	*p++ = code;
+	*p++ = id;
+	cnt = 4 + len;
+	*p++ = (cnt >> 8);
+	*p++ = (cnt & 0xff);
+
+	/* Now stuff remaining bytes */
+	if(len) {
+		p = skb_put(skb, len);
+		memcpy(p, data, len);
+	}
+
+	/* skb is now ready for xmit */
+	printk(KERN_DEBUG "Sending CCP Frame:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	isdn_net_write_super(lp, skb);
+}
+
+/* Allocate the reset state vector */
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
+{
+	struct ippp_ccp_reset *r;
+	r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
+	if(!r) {
+		printk(KERN_ERR "ippp_ccp: failed to allocate reset data"
+		       " structure - no mem\n");
+		return NULL;
+	}
+	memset(r, 0, sizeof(struct ippp_ccp_reset));
+	printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r);
+	is->reset = r;
+	return r;
+}
+
+/* Destroy the reset state vector. Kill all pending timers first. */
+static void isdn_ppp_ccp_reset_free(struct ippp_struct *is)
+{
+	unsigned int id;
+
+	printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n",
+	       is->reset);
+	for(id = 0; id < 256; id++) {
+		if(is->reset->rs[id]) {
+			isdn_ppp_ccp_reset_free_state(is, (unsigned char)id);
+		}
+	}
+	kfree(is->reset);
+	is->reset = NULL;
+}
+
+/* Free a given state and clear everything up for later reallocation */
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+					  unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(is->reset->rs[id]) {
+		printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id);
+		rs = is->reset->rs[id];
+		/* Make sure the kernel will not call back later */
+		if(rs->ta)
+			del_timer(&rs->timer);
+		is->reset->rs[id] = NULL;
+		kfree(rs);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id);
+	}
+}
+
+/* The timer callback function which is called when a ResetReq has timed out,
+   aka has never been answered by a ResetAck */
+static void isdn_ppp_ccp_timer_callback(unsigned long closure)
+{
+	struct ippp_ccp_reset_state *rs =
+		(struct ippp_ccp_reset_state *)closure;
+
+	if(!rs) {
+		printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
+		return;
+	}
+	if(rs->ta && rs->state == CCPResetSentReq) {
+		/* We are correct here */
+		if(!rs->expra) {
+			/* Hmm, there is no Ack really expected. We can clean
+			   up the state now, it will be reallocated if the
+			   decompressor insists on another reset */
+			rs->ta = 0;
+			isdn_ppp_ccp_reset_free_state(rs->is, rs->id);
+			return;
+		}
+		printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
+		       rs->id);
+		/* Push it again */
+		isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id,
+					rs->data, rs->dlen);
+		/* Restart timer */
+		rs->timer.expires = jiffies + HZ*5;
+		add_timer(&rs->timer);
+	} else {
+		printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
+		       rs->state);
+	}
+}
+
+/* Allocate a new reset transaction state */
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+						      unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs;
+	if(is->reset->rs[id]) {
+		printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
+		       id);
+		return NULL;
+	} else {
+		rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+		if(!rs)
+			return NULL;
+		memset(rs, 0, sizeof(struct ippp_ccp_reset_state));
+		rs->state = CCPResetIdle;
+		rs->is = is;
+		rs->id = id;
+		rs->timer.data = (unsigned long)rs;
+		rs->timer.function = isdn_ppp_ccp_timer_callback;
+		is->reset->rs[id] = rs;
+	}
+	return rs;
+}
+
+
+/* A decompressor wants a reset with a set of parameters - do what is
+   necessary to fulfill it */
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+				     struct isdn_ppp_resetparams *rp)
+{
+	struct ippp_ccp_reset_state *rs;
+
+	if(rp->valid) {
+		/* The decompressor defines parameters by itself */
+		if(rp->rsend) {
+			/* And he wants us to send a request */
+			if(!(rp->idval)) {
+				printk(KERN_ERR "ippp_ccp: decompressor must"
+				       " specify reset id\n");
+				return;
+			}
+			if(is->reset->rs[rp->id]) {
+				/* There is already a transaction in existence
+				   for this id. May be still waiting for a
+				   Ack or may be wrong. */
+				rs = is->reset->rs[rp->id];
+				if(rs->state == CCPResetSentReq && rs->ta) {
+					printk(KERN_DEBUG "ippp_ccp: reset"
+					       " trans still in progress"
+					       " for id %d\n", rp->id);
+				} else {
+					printk(KERN_WARNING "ippp_ccp: reset"
+					       " trans in wrong state %d for"
+					       " id %d\n", rs->state, rp->id);
+				}
+			} else {
+				/* Ok, this is a new transaction */
+				printk(KERN_DEBUG "ippp_ccp: new trans for id"
+				       " %d to be started\n", rp->id);
+				rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id);
+				if(!rs) {
+					printk(KERN_ERR "ippp_ccp: out of mem"
+					       " allocing ccp trans\n");
+					return;
+				}
+				rs->state = CCPResetSentReq;
+				rs->expra = rp->expra;
+				if(rp->dtval) {
+					rs->dlen = rp->dlen;
+					memcpy(rs->data, rp->data, rp->dlen);
+				}
+				/* HACK TODO - add link comp here */
+				isdn_ppp_ccp_xmit_reset(is, PPP_CCP,
+							CCP_RESETREQ, rs->id,
+							rs->data, rs->dlen);
+				/* Start the timer */
+				rs->timer.expires = jiffies + 5*HZ;
+				add_timer(&rs->timer);
+				rs->ta = 1;
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: no reset sent\n");
+		}
+	} else {
+		/* The reset params are invalid. The decompressor does not
+		   care about them, so we just send the minimal requests
+		   and increase ids only when an Ack is received for a
+		   given id */
+		if(is->reset->rs[is->reset->lastid]) {
+			/* There is already a transaction in existence
+			   for this id. May be still waiting for a
+			   Ack or may be wrong. */
+			rs = is->reset->rs[is->reset->lastid];
+			if(rs->state == CCPResetSentReq && rs->ta) {
+				printk(KERN_DEBUG "ippp_ccp: reset"
+				       " trans still in progress"
+				       " for id %d\n", rp->id);
+			} else {
+				printk(KERN_WARNING "ippp_ccp: reset"
+				       " trans in wrong state %d for"
+				       " id %d\n", rs->state, rp->id);
+			}
+		} else {
+			printk(KERN_DEBUG "ippp_ccp: new trans for id"
+			       " %d to be started\n", is->reset->lastid);
+			rs = isdn_ppp_ccp_reset_alloc_state(is,
+							    is->reset->lastid);
+			if(!rs) {
+				printk(KERN_ERR "ippp_ccp: out of mem"
+				       " allocing ccp trans\n");
+				return;
+			}
+			rs->state = CCPResetSentReq;
+			/* We always expect an Ack if the decompressor doesn't
+			   know	better */
+			rs->expra = 1;
+			rs->dlen = 0;
+			/* HACK TODO - add link comp here */
+			isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ,
+						rs->id, NULL, 0);
+			/* Start the timer */
+			rs->timer.expires = jiffies + 5*HZ;
+			add_timer(&rs->timer);
+			rs->ta = 1;
+		}
+	}
+}
+
+/* An Ack was received for this id. This means we stop the timer and clean
+   up the state prior to calling the decompressors reset routine. */
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+					unsigned char id)
+{
+	struct ippp_ccp_reset_state *rs = is->reset->rs[id];
+
+	if(rs) {
+		if(rs->ta && rs->state == CCPResetSentReq) {
+			/* Great, we are correct */
+			if(!rs->expra)
+				printk(KERN_DEBUG "ippp_ccp: ResetAck received"
+				       " for id %d but not expected\n", id);
+		} else {
+			printk(KERN_INFO "ippp_ccp: ResetAck received out of"
+			       "sync for id %d\n", id);
+		}
+		if(rs->ta) {
+			rs->ta = 0;
+			del_timer(&rs->timer);
+		}
+		isdn_ppp_ccp_reset_free_state(is, id);
+	} else {
+		printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
+		       " %d\n", id);
+	}
+	/* Make sure the simple reset stuff uses a new id next time */
+	is->reset->lastid++;
+}
+
+/* 
+ * decompress packet
+ *
+ * if master = 0, we're trying to uncompress an per-link compressed packet,
+ * as opposed to an compressed reconstructed-from-MPPP packet.
+ * proto is updated to protocol field of uncompressed packet.
+ *
+ * retval: decompressed packet,
+ *         same packet if uncompressed,
+ *	   NULL if decompression error
+ */
+
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
+	int *proto)
+{
+	void *stat = NULL;
+	struct isdn_ppp_compressor *ipc = NULL;
+	struct sk_buff *skb_out;
+	int len;
+	struct ippp_struct *ri;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
+	if(!master) {
+		// per-link decompression 
+		stat = is->link_decomp_stat;
+		ipc = is->link_decompressor;
+		ri = is;
+	} else {
+		stat = master->decomp_stat;
+		ipc = master->decompressor;
+		ri = master;
+	}
+
+	if (!ipc) {
+		// no decompressor -> we can't decompress.
+		printk(KERN_DEBUG "ippp: no decompressor defined!\n");
+		return skb;
+	}
+	if (!stat) // if we have a compressor, stat has been set as well
+		BUG();
+
+	if((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG) ) {
+		// compressed packets are compressed by their protocol type
+
+		// Set up reset params for the decompressor
+  		memset(&rsparm, 0, sizeof(rsparm));
+  		rsparm.data = rsdata;
+  		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+  
+  		skb_out = dev_alloc_skb(is->mru + PPP_HDRLEN);
+		len = ipc->decompress(stat, skb, skb_out, &rsparm);
+		kfree_skb(skb);
+		if (len <= 0) {
+			switch(len) {
+			case DECOMP_ERROR:
+				printk(KERN_INFO "ippp: decomp wants reset %s params\n",
+				       rsparm.valid ? "with" : "without");
+				
+				isdn_ppp_ccp_reset_trans(ri, &rsparm);
+				break;
+			case DECOMP_FATALERROR:
+				ri->pppcfg |= SC_DC_FERROR;
+				/* Kick ipppd to recognize the error */
+				isdn_ppp_ccp_kickup(ri);
+				break;
+			}
+			kfree_skb(skb_out);
+			return NULL;
+		}
+		*proto = isdn_ppp_strip_proto(skb_out);
+		if (*proto < 0) {
+			kfree_skb(skb_out);
+			return NULL;
+		}
+		return skb_out;
+	} else { 
+		// uncompressed packets are fed through the decompressor to
+		// update the decompressor state
+		ipc->incomp(stat, skb, *proto);
+		return skb;
+	}
+}
+
+/*
+ * compress a frame 
+ *   type=0: normal/bundle compression
+ *       =1: link compression
+ * returns original skb if we haven't compressed the frame
+ * and a new skb pointer if we've done it
+ */
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+	struct ippp_struct *is,struct ippp_struct *master,int type)
+{
+    int ret;
+    int new_proto;
+    struct isdn_ppp_compressor *compressor;
+    void *stat;
+    struct sk_buff *skb_out;
+
+	/* we do not compress control protocols */
+    if(*proto < 0 || *proto > 0x3fff) {
+	    return skb_in;
+    }
+
+	if(type) { /* type=1 => Link compression */
+		return skb_in;
+	}
+	else {
+		if(!master) {
+			compressor = is->compressor;
+			stat = is->comp_stat;
+		}
+		else {
+			compressor = master->compressor;
+			stat = master->comp_stat;
+		}
+		new_proto = PPP_COMP;
+	}
+
+	if(!compressor) {
+		printk(KERN_ERR "isdn_ppp: No compressor set!\n");
+		return skb_in;
+	}
+	if(!stat) {
+		printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
+		return skb_in;
+	}
+
+	/* Allow for at least 150 % expansion (for now) */
+	skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 +
+		skb_headroom(skb_in), GFP_ATOMIC);
+	if(!skb_out)
+		return skb_in;
+	skb_reserve(skb_out, skb_headroom(skb_in));
+
+	ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
+	if(!ret) {
+		dev_kfree_skb(skb_out);
+		return skb_in;
+	}
+	
+	dev_kfree_skb(skb_in);
+	*proto = new_proto;
+	return skb_out;
+}
+
+/*
+ * we received a CCP frame .. 
+ * not a clean solution, but we MUST handle a few cases in the kernel
+ */
+static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+	 struct sk_buff *skb,int proto)
+{
+	struct ippp_struct *is;
+	struct ippp_struct *mis;
+	int len;
+	struct isdn_ppp_resetparams rsparm;
+	unsigned char rsdata[IPPP_RESET_MAXDATABYTES];	
+
+	printk(KERN_DEBUG "Received CCP frame from peer slot(%d)\n",
+		lp->ppp_slot);
+	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
+		return;
+	}
+	is = ippp_table[lp->ppp_slot];
+	isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	if(lp->master) {
+		int slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
+		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+			printk(KERN_ERR "%s: slot(%d) out of range\n",
+				__FUNCTION__, slot);
+			return;
+		}	
+		mis = ippp_table[slot];
+	} else
+		mis = is;
+
+	switch(skb->data[0]) {
+	case CCP_CONFREQ:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable compression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags &= ~SC_COMP_ON;		
+		else
+			is->compflags &= ~SC_LINK_COMP_ON;		
+		break;
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);		
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);		
+		break;
+	case CCP_CONFACK:
+		/* if we RECEIVE an ackowledge we enable the decompressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable decompression here!\n");
+		if(proto == PPP_CCP) {
+			if (!mis->decompressor)
+				break;
+			mis->compflags |= SC_DECOMP_ON;
+		} else {
+			if (!is->decompressor)
+				break;
+			is->compflags |= SC_LINK_DECOMP_ON;
+		}
+		break;
+
+	case CCP_RESETACK:
+		printk(KERN_DEBUG "Received ResetAck from peer\n");
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+
+		if(proto == PPP_CCP) {
+			/* If a reset Ack was outstanding for this id, then
+			   clean up the state engine */
+			isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]);
+			if(mis->decompressor && mis->decomp_stat)
+				mis->decompressor->
+					reset(mis->decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: This is not easy to decide here */
+			mis->compflags &= ~SC_DECOMP_DISCARD;
+		}
+		else {
+			isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]);
+			if(is->link_decompressor && is->link_decomp_stat)
+				is->link_decompressor->
+					reset(is->link_decomp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, NULL);
+			/* TODO: neither here */
+			is->compflags &= ~SC_LINK_DECOMP_DISCARD;
+		}
+		break;
+
+	case CCP_RESETREQ:
+		printk(KERN_DEBUG "Received ResetReq from peer\n");
+		/* Receiving a ResetReq means we must reset our compressor */
+		/* Set up reset params for the reset entry */
+		memset(&rsparm, 0, sizeof(rsparm));
+		rsparm.data = rsdata;
+		rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; 
+		/* Isolate data length */
+		len = (skb->data[2] << 8) | skb->data[3];
+		len -= 4;
+		if(proto == PPP_CCP) {
+			if(mis->compressor && mis->comp_stat)
+				mis->compressor->
+					reset(mis->comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->
+					reset(is->link_comp_stat,
+					      skb->data[0],
+					      skb->data[1],
+					      len ? &skb->data[4] : NULL,
+					      len, &rsparm);
+		}
+		/* Ack the Req as specified by rsparm */
+		if(rsparm.valid) {
+			/* Compressor reset handler decided how to answer */
+			if(rsparm.rsend) {
+				/* We should send a Frame */
+				isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+							rsparm.idval ? rsparm.id
+							: skb->data[1],
+							rsparm.dtval ?
+							rsparm.data : NULL,
+							rsparm.dtval ?
+							rsparm.dlen : 0);
+			} else {
+				printk(KERN_DEBUG "ResetAck suppressed\n");
+			}
+		} else {
+			/* We answer with a straight reflected Ack */
+			isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+						skb->data[1],
+						len ? &skb->data[4] : NULL,
+						len);
+		}
+		break;
+	}
+}
+
+
+/*
+ * Daemon sends a CCP frame ...
+ */
+
+/* TODO: Clean this up with new Reset semantics */
+
+/* I believe the CCP handling as-is is done wrong. Compressed frames
+ * should only be sent/received after CCP reaches UP state, which means
+ * both sides have sent CONF_ACK. Currently, we handle both directions
+ * independently, which means we may accept compressed frames too early
+ * (supposedly not a problem), but may also mean we send compressed frames
+ * too early, which may turn out to be a problem.
+ * This part of state machine should actually be handled by (i)pppd, but
+ * that's too big of a change now. --kai
+ */
+
+/* Actually, we might turn this into an advantage: deal with the RFC in
+ * the old tradition of beeing generous on what we accept, but beeing
+ * strict on what we send. Thus we should just
+ * - accept compressed frames as soon as decompression is negotiated
+ * - send compressed frames only when decomp *and* comp are negotiated
+ * - drop rx compressed frames if we cannot decomp (instead of pushing them
+ *   up to ipppd)
+ * and I tried to modify this file according to that. --abp
+ */
+
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
+{
+	struct ippp_struct *mis,*is;
+	int proto, slot = lp->ppp_slot;
+	unsigned char *data;
+
+	if(!skb || skb->len < 3)
+		return;
+	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, slot);
+		return;
+	}	
+	is = ippp_table[slot];
+	/* Daemon may send with or without address and control field comp */
+	data = skb->data;
+	if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
+		data += 2;
+		if(skb->len < 5)
+			return;
+	}
+
+	proto = ((int)data[0]<<8)+data[1];
+	if(proto != PPP_CCP && proto != PPP_CCPFRAG)
+		return;
+
+	printk(KERN_DEBUG "Received CCP frame from daemon:\n");
+	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+	if (lp->master) {
+		slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
+		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
+			printk(KERN_ERR "%s: slot(%d) out of range\n",
+				__FUNCTION__, slot);
+			return;
+		}	
+		mis = ippp_table[slot];
+	} else
+		mis = is;
+	if (mis != is)
+		printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
+	
+        switch(data[2]) {
+	case CCP_CONFREQ:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable decompression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags &= ~SC_DECOMP_ON;
+		else
+			is->compflags &= ~SC_LINK_DECOMP_ON;
+		break;
+	case CCP_TERMREQ:
+	case CCP_TERMACK:
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Disable (de)compression here!\n");
+		if(proto == PPP_CCP)
+			is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+		else
+			is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+		break;
+	case CCP_CONFACK:
+		/* if we SEND an ackowledge we can/must enable the compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Enable compression here!\n");
+		if(proto == PPP_CCP) {
+			if (!is->compressor)
+				break;
+			is->compflags |= SC_COMP_ON;
+		} else {
+			if (!is->compressor)
+				break;
+			is->compflags |= SC_LINK_COMP_ON;
+		}
+		break;
+	case CCP_RESETACK:
+		/* If we send a ACK we should reset our compressor */
+		if(is->debug & 0x10)
+			printk(KERN_DEBUG "Reset decompression state here!\n");
+		printk(KERN_DEBUG "ResetAck from daemon passed by\n");
+		if(proto == PPP_CCP) {
+			/* link to master? */
+			if(is->compressor && is->comp_stat)
+				is->compressor->reset(is->comp_stat, 0, 0,
+						      NULL, 0, NULL);
+			is->compflags &= ~SC_COMP_DISCARD;	
+		}
+		else {
+			if(is->link_compressor && is->link_comp_stat)
+				is->link_compressor->reset(is->link_comp_stat,
+							   0, 0, NULL, 0, NULL);
+			is->compflags &= ~SC_LINK_COMP_DISCARD;	
+		}
+		break;
+	case CCP_RESETREQ:
+		/* Just let it pass by */
+		printk(KERN_DEBUG "ResetReq from daemon passed by\n");
+		break;
+	}
+}
+
+int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
+{
+	ipc->next = ipc_head;
+	ipc->prev = NULL;
+	if(ipc_head) {
+		ipc_head->prev = ipc;
+	}
+	ipc_head = ipc;
+	return 0;
+}
+
+int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc)
+{
+	if(ipc->prev)
+		ipc->prev->next = ipc->next;
+	else
+		ipc_head = ipc->next;
+	if(ipc->next)
+		ipc->next->prev = ipc->prev;
+	ipc->prev = ipc->next = NULL;
+	return 0;
+}
+
+static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data)
+{
+	struct isdn_ppp_compressor *ipc = ipc_head;
+	int ret;
+	void *stat;
+	int num = data->num;
+
+	if(is->debug & 0x10)
+		printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
+			(data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
+
+	/* If is has no valid reset state vector, we cannot allocate a
+	   decompressor. The decompressor would cause reset transactions
+	   sooner or later, and they need that vector. */
+
+	if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) {
+		printk(KERN_ERR "ippp_ccp: no reset data structure - can't"
+		       " allow decompression.\n");
+		return -ENOMEM;
+	}
+
+	while(ipc) {
+		if(ipc->num == num) {
+			stat = ipc->alloc(data);
+			if(stat) {
+				ret = ipc->init(stat,data,is->unit,0);
+				if(!ret) {
+					printk(KERN_ERR "Can't init (de)compression!\n");
+					ipc->free(stat);
+					stat = NULL;
+					break;
+				}
+			}
+			else {
+				printk(KERN_ERR "Can't alloc (de)compression!\n");
+				break;
+			}
+
+                        if(data->flags & IPPP_COMP_FLAG_XMIT) {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_comp_stat)
+						is->link_compressor->free(is->link_comp_stat);
+					is->link_comp_stat = stat;
+                                	is->link_compressor = ipc;
+				}
+				else {
+					if(is->comp_stat)
+						is->compressor->free(is->comp_stat);
+					is->comp_stat = stat;
+                                	is->compressor = ipc;
+				}
+			}
+                        else {
+				if(data->flags & IPPP_COMP_FLAG_LINK) {
+					if(is->link_decomp_stat)
+						is->link_decompressor->free(is->link_decomp_stat);
+					is->link_decomp_stat = stat;
+        	                        is->link_decompressor = ipc;
+				}
+				else {
+					if(is->decomp_stat)
+						is->decompressor->free(is->decomp_stat);
+					is->decomp_stat = stat;
+        	                        is->decompressor = ipc;
+				}
+			}
+			return 0;
+		}
+		ipc = ipc->next;
+	}
+	return -EINVAL;
+}
diff --git a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h
new file mode 100644
index 0000000..8cc05c7c
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ppp.h
@@ -0,0 +1,43 @@
+/* $Id: isdn_ppp.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/ppp_defs.h>     /* for PPP_PROTOCOL */
+#include <linux/isdn_ppp.h>	/* for isdn_ppp info */
+
+extern int isdn_ppp_read(int, struct file *, char __user *, int);
+extern int isdn_ppp_write(int, struct file *, const char __user *, int);
+extern int isdn_ppp_open(int, struct file *);
+extern int isdn_ppp_init(void);
+extern void isdn_ppp_cleanup(void);
+extern int isdn_ppp_free(isdn_net_local *);
+extern int isdn_ppp_bind(isdn_net_local *);
+extern int isdn_ppp_autodial_filter(struct sk_buff *, isdn_net_local *);
+extern int isdn_ppp_xmit(struct sk_buff *, struct net_device *);
+extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
+extern int isdn_ppp_dev_ioctl(struct net_device *, struct ifreq *, int);
+extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *);
+extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
+extern void isdn_ppp_release(int, struct file *);
+extern int isdn_ppp_dial_slave(char *);
+extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
+
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc);
+
+#define IPPP_OPEN	0x01
+#define IPPP_CONNECT	0x02
+#define IPPP_CLOSEWAIT	0x04
+#define IPPP_NOBLOCK	0x08
+#define IPPP_ASSIGNED	0x10
+
+#define IPPP_MAX_HEADER 10
+
+
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
new file mode 100644
index 0000000..e21007e
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_tty.c
@@ -0,0 +1,3911 @@
+/* $Id: isdn_tty.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $
+ *
+ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#undef ISDN_TTY_STAT_DEBUG
+
+#include <linux/config.h>
+#include <linux/isdn.h>
+#include <linux/delay.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#define VBUF 0x3e0
+#define VBUFX (VBUF/16)
+#endif
+
+#define FIX_FILE_TRANSFER
+#define	DUMMY_HAYES_AT
+
+/* Prototypes */
+
+static int isdn_tty_edit_at(const char *, int, modem_info *);
+static void isdn_tty_check_esc(const u_char *, u_char, int, int *, u_long *);
+static void isdn_tty_modem_reset_regs(modem_info *, int);
+static void isdn_tty_cmd_ATA(modem_info *);
+static void isdn_tty_flush_buffer(struct tty_struct *);
+static void isdn_tty_modem_result(int, modem_info *);
+#ifdef CONFIG_ISDN_AUDIO
+static int isdn_tty_countDLE(unsigned char *, int);
+#endif
+
+/* Leave this unchanged unless you know what you do! */
+#define MODEM_PARANOIA_CHECK
+#define MODEM_DO_RESTART
+
+static int bit2si[8] =
+{1, 5, 7, 7, 7, 7, 7, 7};
+static int si2bit[8] =
+{4, 1, 4, 4, 4, 4, 4, 4};
+
+char *isdn_tty_revision = "$Revision: 1.1.2.3 $";
+
+
+/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
+ * to stuff incoming data directly into a tty's flip-buffer. This
+ * is done to speed up tty-receiving if the receive-queue is empty.
+ * This routine MUST be called with interrupts off.
+ * Return:
+ *  1 = Success
+ *  0 = Failure, data has to be buffered and later processed by
+ *      isdn_tty_readmodem().
+ */
+static int
+isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
+{
+	int c;
+	int len;
+	struct tty_struct *tty;
+
+	if (info->online) {
+		if ((tty = info->tty)) {
+			if (info->mcr & UART_MCR_RTS) {
+				c = TTY_FLIPBUF_SIZE - tty->flip.count;
+				len = skb->len
+#ifdef CONFIG_ISDN_AUDIO
+					+ ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+					;
+				if (c >= len) {
+#ifdef CONFIG_ISDN_AUDIO
+					if (ISDN_AUDIO_SKB_DLECOUNT(skb))
+						while (skb->len--) {
+							if (*skb->data == DLE)
+								tty_insert_flip_char(tty, DLE, 0);
+							tty_insert_flip_char(tty, *skb->data++, 0);
+					} else {
+#endif
+						memcpy(tty->flip.char_buf_ptr,
+						       skb->data, len);
+						tty->flip.count += len;
+						tty->flip.char_buf_ptr += len;
+						memset(tty->flip.flag_buf_ptr, 0, len);
+						tty->flip.flag_buf_ptr += len;
+#ifdef CONFIG_ISDN_AUDIO
+					}
+#endif
+					if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)
+						tty->flip.flag_buf_ptr[len - 1] = 0xff;
+					schedule_delayed_work(&tty->flip.work, 1);
+					kfree_skb(skb);
+					return 1;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
+ * It tries getting received data from the receive queue an stuff it into
+ * the tty's flip-buffer.
+ */
+void
+isdn_tty_readmodem(void)
+{
+	int resched = 0;
+	int midx;
+	int i;
+	int c;
+	int r;
+	struct tty_struct *tty;
+	modem_info *info;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		if ((midx = dev->m_idx[i]) >= 0) {
+			info = &dev->mdm.info[midx];
+			if (info->online) {
+				r = 0;
+#ifdef CONFIG_ISDN_AUDIO
+				isdn_audio_eval_dtmf(info);
+				if ((info->vonline & 1) && (info->emu.vpar[1]))
+					isdn_audio_eval_silence(info);
+#endif
+				if ((tty = info->tty)) {
+					if (info->mcr & UART_MCR_RTS) {
+						c = TTY_FLIPBUF_SIZE - tty->flip.count;
+						if (c > 0) {
+							r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
+									   tty->flip.char_buf_ptr,
+									   tty->flip.flag_buf_ptr, c, NULL);
+							/* CISCO AsyncPPP Hack */
+							if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP))
+								memset(tty->flip.flag_buf_ptr, 0, r);
+							tty->flip.count += r;
+							tty->flip.flag_buf_ptr += r;
+							tty->flip.char_buf_ptr += r;
+							if (r)
+								schedule_delayed_work(&tty->flip.work, 1);
+						}
+					} else
+						r = 1;
+				} else
+					r = 1;
+				if (r) {
+					info->rcvsched = 0;
+					resched = 1;
+				} else
+					info->rcvsched = 1;
+			}
+		}
+	}
+	if (!resched)
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
+}
+
+int
+isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
+{
+	ulong flags;
+	int midx;
+#ifdef CONFIG_ISDN_AUDIO
+	int ifmt;
+#endif
+	modem_info *info;
+
+	if ((midx = dev->m_idx[i]) < 0) {
+		/* if midx is invalid, packet is not for tty */
+		return 0;
+	}
+	info = &dev->mdm.info[midx];
+#ifdef CONFIG_ISDN_AUDIO
+	ifmt = 1;
+	
+	if ((info->vonline) && (!info->emu.vpar[4]))
+		isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+	if ((info->vonline & 1) && (info->emu.vpar[1]))
+		isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
+#endif
+	if ((info->online < 2)
+#ifdef CONFIG_ISDN_AUDIO
+	    && (!(info->vonline & 1))
+#endif
+		) {
+		/* If Modem not listening, drop data */
+		kfree_skb(skb);
+		return 1;
+	}
+	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
+		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) {
+			/* T.70 decoding: throw away the T.70 header (2 or 4 bytes)   */
+			if (skb->data[0] == 3) /* pure data packet -> 4 byte headers  */
+				skb_pull(skb, 4);
+			else
+				if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr  */
+					skb_pull(skb, 2);
+		} else
+			/* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+			if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
+				skb_pull(skb, 4);
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+	ISDN_AUDIO_SKB_LOCK(skb) = 0;
+	if (info->vonline & 1) {
+		/* voice conversion/compression */
+		switch (info->emu.vpar[3]) {
+			case 2:
+			case 3:
+			case 4:
+				/* adpcm
+				 * Since compressed data takes less
+				 * space, we can overwrite the buffer.
+				 */
+				skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
+								    ifmt,
+								    skb->data,
+								    skb->data,
+								    skb->len));
+				break;
+			case 5:
+				/* a-law */
+				if (!ifmt)
+					isdn_audio_ulaw2alaw(skb->data, skb->len);
+				break;
+			case 6:
+				/* u-law */
+				if (ifmt)
+					isdn_audio_alaw2ulaw(skb->data, skb->len);
+				break;
+		}
+		ISDN_AUDIO_SKB_DLECOUNT(skb) =
+			isdn_tty_countDLE(skb->data, skb->len);
+	}
+#ifdef CONFIG_ISDN_TTY_FAX
+	else {
+		if (info->faxonline & 2) {
+			isdn_tty_fax_bitorder(info, skb);
+			ISDN_AUDIO_SKB_DLECOUNT(skb) =
+				isdn_tty_countDLE(skb->data, skb->len);
+		}
+	}
+#endif
+#endif
+	/* Try to deliver directly via tty-flip-buf if queue is empty */
+	spin_lock_irqsave(&info->readlock, flags);
+	if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+		if (isdn_tty_try_read(info, skb)) {
+			spin_unlock_irqrestore(&info->readlock, flags);
+			return 1;
+		}
+	/* Direct deliver failed or queue wasn't empty.
+	 * Queue up for later dequeueing via timer-irq.
+	 */
+	__skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
+	dev->drv[di]->rcvcount[channel] +=
+		(skb->len
+#ifdef CONFIG_ISDN_AUDIO
+		 + ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+			);
+	spin_unlock_irqrestore(&info->readlock, flags);
+	/* Schedule dequeuing */
+	if ((dev->modempoll) && (info->rcvsched))
+		isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+	return 1;
+}
+
+void
+isdn_tty_cleanup_xmit(modem_info * info)
+{
+	skb_queue_purge(&info->xmit_queue);
+#ifdef CONFIG_ISDN_AUDIO
+	skb_queue_purge(&info->dtmf_queue);
+#endif
+}
+
+static void
+isdn_tty_tint(modem_info * info)
+{
+	struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
+	int len, slen;
+
+	if (!skb)
+		return;
+	len = skb->len;
+	if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
+					   info->isdn_channel, 1, skb)) == len) {
+		struct tty_struct *tty = info->tty;
+		info->send_outstanding++;
+		info->msr &= ~UART_MSR_CTS;
+		info->lsr &= ~UART_LSR_TEMT;
+		tty_wakeup(tty);
+		return;
+	}
+	if (slen < 0) {
+		/* Error: no channel, already shutdown, or wrong parameter */
+		dev_kfree_skb(skb);
+		return;
+	}
+	skb_queue_head(&info->xmit_queue, skb);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+static int
+isdn_tty_countDLE(unsigned char *buf, int len)
+{
+	int count = 0;
+
+	while (len--)
+		if (*buf++ == DLE)
+			count++;
+	return count;
+}
+
+/* This routine is called from within isdn_tty_write() to perform
+ * DLE-decoding when sending audio-data.
+ */
+static int
+isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len)
+{
+	unsigned char *p = &info->xmit_buf[info->xmit_count];
+	int count = 0;
+
+	while (len > 0) {
+		if (m->lastDLE) {
+			m->lastDLE = 0;
+			switch (*p) {
+				case DLE:
+					/* Escape code */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					count++;
+					break;
+				case ETX:
+					/* End of data */
+					info->vonline |= 4;
+					return count;
+				case DC4:
+					/* Abort RX */
+					info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+					printk(KERN_DEBUG
+					       "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",
+					       info->line);
+#endif
+					isdn_tty_at_cout("\020\003", info);
+					if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+						printk(KERN_DEBUG
+						       "DLEdown: send VCON on ttyI%d\n",
+						       info->line);
+#endif
+						isdn_tty_at_cout("\r\nVCON\r\n", info);
+					}
+					/* Fall through */
+				case 'q':
+				case 's':
+					/* Silence */
+					if (len > 1)
+						memmove(p, p + 1, len - 1);
+					p--;
+					break;
+			}
+		} else {
+			if (*p == DLE)
+				m->lastDLE = 1;
+			else
+				count++;
+		}
+		p++;
+		len--;
+	}
+	if (len < 0) {
+		printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
+		return 0;
+	}
+	return count;
+}
+
+/* This routine is called from within isdn_tty_write() when receiving
+ * audio-data. It interrupts receiving, if an character other than
+ * ^S or ^Q is sent.
+ */
+static int
+isdn_tty_end_vrx(const char *buf, int c)
+{
+	char ch;
+
+	while (c--) {
+		ch = *buf;
+		if ((ch != 0x11) && (ch != 0x13))
+			return 1;
+		buf++;
+	}
+	return 0;
+}
+
+static int voice_cf[7] =
+{0, 0, 4, 3, 2, 0, 0};
+
+#endif                          /* CONFIG_ISDN_AUDIO */
+
+/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
+ * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
+ * outgoing data from the tty's xmit-buffer, handles voice-decompression or
+ * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
+ */
+static void
+isdn_tty_senddown(modem_info * info)
+{
+	int buflen;
+	int skb_res;
+#ifdef CONFIG_ISDN_AUDIO
+	int audio_len;
+#endif
+	struct sk_buff *skb;
+
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 4) {
+		info->vonline &= ~6;
+		if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+			printk(KERN_DEBUG
+			       "senddown: send VCON on ttyI%d\n",
+			       info->line);
+#endif
+			isdn_tty_at_cout("\r\nVCON\r\n", info);
+		}
+	}
+#endif
+	if (!(buflen = info->xmit_count))
+		return;
+ 	if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
+		info->msr &= ~UART_MSR_CTS;
+	info->lsr &= ~UART_LSR_TEMT;	
+	/* info->xmit_count is modified here and in isdn_tty_write().
+	 * So we return here if isdn_tty_write() is in the
+	 * critical section.
+	 */
+	atomic_inc(&info->xmit_lock);
+	if (!(atomic_dec_and_test(&info->xmit_lock)))
+		return;
+	if (info->isdn_driver < 0) {
+		info->xmit_count = 0;
+		return;
+	}
+	skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 2)
+		audio_len = buflen * voice_cf[info->emu.vpar[3]];
+	else
+		audio_len = 0;
+	skb = dev_alloc_skb(skb_res + buflen + audio_len);
+#else
+	skb = dev_alloc_skb(skb_res + buflen);
+#endif
+	if (!skb) {
+		printk(KERN_WARNING
+		       "isdn_tty: Out of memory in ttyI%d senddown\n",
+		       info->line);
+		return;
+	}
+	skb_reserve(skb, skb_res);
+	memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
+	info->xmit_count = 0;
+#ifdef CONFIG_ISDN_AUDIO
+	if (info->vonline & 2) {
+		/* For now, ifmt is fixed to 1 (alaw), since this
+		 * is used with ISDN everywhere in the world, except
+		 * US, Canada and Japan.
+		 * Later, when US-ISDN protocols are implemented,
+		 * this setting will depend on the D-channel protocol.
+		 */
+		int ifmt = 1;
+
+		/* voice conversion/decompression */
+		switch (info->emu.vpar[3]) {
+			case 2:
+			case 3:
+			case 4:
+				/* adpcm, compatible to ZyXel 1496 modem
+				 * with ROM revision 6.01
+				 */
+				audio_len = isdn_audio_adpcm2xlaw(info->adpcms,
+								  ifmt,
+								  skb->data,
+						    skb_put(skb, audio_len),
+								  buflen);
+				skb_pull(skb, buflen);
+				skb_trim(skb, audio_len);
+				break;
+			case 5:
+				/* a-law */
+				if (!ifmt)
+					isdn_audio_alaw2ulaw(skb->data,
+							     buflen);
+				break;
+			case 6:
+				/* u-law */
+				if (ifmt)
+					isdn_audio_ulaw2alaw(skb->data,
+							     buflen);
+				break;
+		}
+	}
+#endif                          /* CONFIG_ISDN_AUDIO */
+	if (info->emu.mdmreg[REG_T70] & BIT_T70) {
+		/* Add T.70 simplified header */
+		if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT)
+			memcpy(skb_push(skb, 2), "\1\0", 2);
+		else
+			memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+	}
+	skb_queue_tail(&info->xmit_queue, skb);
+}
+
+/************************************************************
+ *
+ * Modem-functions
+ *
+ * mostly "stolen" from original Linux-serial.c and friends.
+ *
+ ************************************************************/
+
+/* The next routine is called once from within timer-interrupt
+ * triggered within isdn_tty_modem_ncarrier(). It calls
+ * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
+ * into the tty's flip-buffer.
+ */
+static void
+isdn_tty_modem_do_ncarrier(unsigned long data)
+{
+	modem_info *info = (modem_info *) data;
+	isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+}
+
+/* Next routine is called, whenever the DTR-signal is raised.
+ * It checks the ncarrier-flag, and triggers the above routine
+ * when necessary. The ncarrier-flag is set, whenever DTR goes
+ * low.
+ */
+static void
+isdn_tty_modem_ncarrier(modem_info * info)
+{
+	if (info->ncarrier) {
+		info->nc_timer.expires = jiffies + HZ;
+		add_timer(&info->nc_timer);
+	}
+}
+
+/*
+ * return the usage calculated by si and layer 2 protocol
+ */
+int
+isdn_calc_usage(int si, int l2)
+{
+	int usg = ISDN_USAGE_MODEM;
+
+#ifdef CONFIG_ISDN_AUDIO
+	if (si == 1) {
+		switch(l2) {
+			case ISDN_PROTO_L2_MODEM: 
+				usg = ISDN_USAGE_MODEM;
+				break;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_PROTO_L2_FAX: 
+				usg = ISDN_USAGE_FAX;
+				break;
+#endif
+			case ISDN_PROTO_L2_TRANS: 
+			default:
+				usg = ISDN_USAGE_VOICE;
+				break;
+		}
+	}
+#endif
+	return(usg);
+}
+
+/* isdn_tty_dial() performs dialing of a tty an the necessary
+ * setup of the lower levels before that.
+ */
+static void
+isdn_tty_dial(char *n, modem_info * info, atemu * m)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	u_long flags;
+	isdn_ctrl cmd;
+	int i;
+	int j;
+
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+	usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	spin_lock_irqsave(&dev->lock, flags);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+	if (i < 0) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		strcpy(info->last_num, n);
+		isdn_info_update();
+		spin_unlock_irqrestore(&dev->lock, flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_OUT;
+		}
+#endif
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		sprintf(cmd.parm.setup.phone, "%s", n);
+		sprintf(cmd.parm.setup.eazmsn, "%s",
+			isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.parm.setup.si1 = si;
+		cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
+		cmd.command = ISDN_CMD_DIAL;
+		info->dialing = 1;
+		info->emu.carrierwait = 0;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+	}
+}
+
+/* isdn_tty_hangup() disassociates a tty from the real
+ * ISDN-line (hangup). The usage-status is cleared
+ * and some cleanup is done also.
+ */
+void
+isdn_tty_modem_hup(modem_info * info, int local)
+{
+	isdn_ctrl cmd;
+	int di, ch;
+
+	if (!info)
+		return;
+
+	di = info->isdn_driver;
+	ch = info->isdn_channel;
+	if (di < 0 || ch < 0)
+		return;
+
+	info->isdn_driver = -1;
+	info->isdn_channel = -1;
+
+#ifdef ISDN_DEBUG_MODEM_HUP
+	printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
+#endif
+	info->rcvsched = 0;
+	isdn_tty_flush_buffer(info->tty);
+	if (info->online) {
+		info->last_lhup = local;
+		info->online = 0;
+		isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	info->vonline = 0;
+#ifdef CONFIG_ISDN_TTY_FAX
+	info->faxonline = 0;
+	info->fax->phase = ISDN_FAX_PHASE_IDLE;
+#endif
+	info->emu.vpar[4] = 0;
+	info->emu.vpar[5] = 8;
+	if (info->dtmf_state) {
+		kfree(info->dtmf_state);
+		info->dtmf_state = NULL;
+	}
+	if (info->silence_state) {
+		kfree(info->silence_state);
+		info->silence_state = NULL;
+	}
+	if (info->adpcms) {
+		kfree(info->adpcms);
+		info->adpcms = NULL;
+	}
+	if (info->adpcmr) {
+		kfree(info->adpcmr);
+		info->adpcmr = NULL;
+	}
+#endif
+	if ((info->msr & UART_MSR_RI) &&
+		(info->emu.mdmreg[REG_RUNG] & BIT_RUNG))
+		isdn_tty_modem_result(RESULT_RUNG, info);
+	info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+	info->lsr |= UART_LSR_TEMT;
+
+	if (local) {
+		cmd.driver = di;
+		cmd.command = ISDN_CMD_HANGUP;
+		cmd.arg = ch;
+		isdn_command(&cmd);
+	}
+
+	isdn_all_eaz(di, ch);
+	info->emu.mdmreg[REG_RINGCNT] = 0;
+	isdn_free_channel(di, ch, 0);
+
+	if (info->drv_index >= 0) {
+		dev->m_idx[info->drv_index] = -1;
+		info->drv_index = -1;
+	}
+}
+
+/*
+ * Begin of a CAPI like interface, currently used only for 
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include <linux/isdn/capicmd.h>
+
+int
+isdn_tty_capi_facility(capi_msg *cm) {
+	return(-1); /* dummy */
+}
+
+/* isdn_tty_suspend() tries to suspend the current tty connection
+ */
+static void
+isdn_tty_suspend(char *id, modem_info * info, atemu * m)
+{
+	isdn_ctrl cmd;
+	
+	int l;
+
+	if (!info)
+		return;
+
+#ifdef ISDN_DEBUG_MODEM_SERVICES
+	printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
+#endif
+	l = strlen(id);
+	if ((info->isdn_driver >= 0)) {
+		cmd.parm.cmsg.Length = l+18;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l + 3;
+		cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command = CAPI_PUT_MESSAGE;
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		isdn_command(&cmd);
+	}
+}
+
+/* isdn_tty_resume() tries to resume a suspended call
+ * setup of the lower levels before that. unfortunatly here is no
+ * checking for compatibility of used protocols implemented by Q931
+ * It does the same things like isdn_tty_dial, the last command
+ * is different, may be we can merge it.
+ */
+
+static void
+isdn_tty_resume(char *id, modem_info * info, atemu * m)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(id);
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+	usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	spin_lock_irqsave(&dev->lock, flags);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+	if (i < 0) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+//		strcpy(info->last_num, n);
+		isdn_info_update();
+		spin_unlock_irqrestore(&dev->lock, flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+18;
+		cmd.parm.cmsg.Command = CAPI_FACILITY;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+		cmd.parm.cmsg.para[1] = 0;
+		cmd.parm.cmsg.para[2] = l+3;
+		cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
+		cmd.parm.cmsg.para[4] = 0;
+		cmd.parm.cmsg.para[5] = l;
+		strncpy(&cmd.parm.cmsg.para[6], id, l);
+		cmd.command =CAPI_PUT_MESSAGE;
+		info->dialing = 1;
+//		strcpy(dev->num[i], n);
+		isdn_info_update();
+		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+	}
+}
+
+/* isdn_tty_send_msg() sends a message to a HL driver
+ * This is used for hybrid modem cards to send AT commands to it
+ */
+
+static void
+isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
+{
+	int usg = ISDN_USAGE_MODEM;
+	int si = 7;
+	int l2 = m->mdmreg[REG_L2PROT];
+	isdn_ctrl cmd;
+	ulong flags;
+	int i;
+	int j;
+	int l;
+
+	l = strlen(msg);
+	if (!l) {
+		isdn_tty_modem_result(RESULT_ERROR, info);
+		return;
+	}
+	for (j = 7; j >= 0; j--)
+		if (m->mdmreg[REG_SI1] & (1 << j)) {
+			si = bit2si[j];
+			break;
+		}
+	usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+	if ((si == 1) && 
+		(l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+		&& (l2 != ISDN_PROTO_L2_FAX)
+#endif
+		) {
+		l2 = ISDN_PROTO_L2_TRANS;
+		usg = ISDN_USAGE_VOICE;
+	}
+#endif
+	m->mdmreg[REG_SI1I] = si2bit[si];
+	spin_lock_irqsave(&dev->lock, flags);
+	i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+	if (i < 0) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+	} else {
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		dev->usage[i] |= ISDN_USAGE_OUTGOING;
+		info->last_dir = 1;
+		isdn_info_update();
+		spin_unlock_irqrestore(&dev->lock, flags);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_CLREAZ;
+		isdn_command(&cmd);
+		strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETEAZ;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		info->last_l2 = l2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.parm.cmsg.Length = l+14;
+		cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
+		cmd.parm.cmsg.Subcommand = CAPI_REQ;
+		cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+		cmd.parm.cmsg.para[0] = l+1;
+		strncpy(&cmd.parm.cmsg.para[1], msg, l);
+		cmd.parm.cmsg.para[l+1] = 0xd;
+		cmd.command =CAPI_PUT_MESSAGE;
+/*		info->dialing = 1;
+		strcpy(dev->num[i], n);
+		isdn_info_update();
+*/
+		isdn_command(&cmd);
+	}
+}
+
+static inline int
+isdn_tty_paranoia_check(modem_info *info, char *name, const char *routine)
+{
+#ifdef MODEM_PARANOIA_CHECK
+	if (!info) {
+		printk(KERN_WARNING "isdn_tty: null info_struct for %s in %s\n",
+			name, routine);
+		return 1;
+	}
+	if (info->magic != ISDN_ASYNC_MAGIC) {
+		printk(KERN_WARNING "isdn_tty: bad magic for modem struct %s in %s\n",
+		       name, routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void
+isdn_tty_change_speed(modem_info * info)
+{
+	uint cflag,
+	 cval,
+	 fcr,
+	 quot;
+	int i;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+
+	quot = i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 2)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (quot) {
+		info->mcr |= UART_MCR_DTR;
+		isdn_tty_modem_ncarrier(info);
+	} else {
+		info->mcr &= ~UART_MCR_DTR;
+		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "Mhup in changespeed\n");
+#endif
+			if (info->online)
+				info->ncarrier = 1;
+			isdn_tty_modem_reset_regs(info, 0);
+			isdn_tty_modem_hup(info, 1);
+		}
+		return;
+	}
+	/* byte size and parity */
+	cval = cflag & (CSIZE | CSTOPB);
+	cval >>= 4;
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+	fcr = 0;
+
+	/* CTS flow control flag and modem status interrupts */
+	if (cflag & CRTSCTS) {
+		info->flags |= ISDN_ASYNC_CTS_FLOW;
+	} else
+		info->flags &= ~ISDN_ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ISDN_ASYNC_CHECK_CD;
+	else {
+		info->flags |= ISDN_ASYNC_CHECK_CD;
+	}
+}
+
+static int
+isdn_tty_startup(modem_info * info)
+{
+	if (info->flags & ISDN_ASYNC_INITIALIZED)
+		return 0;
+	isdn_lock_drivers();
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
+#endif
+	/*
+	 * Now, initialize the UART
+	 */
+	info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	/*
+	 * and set the speed of the serial port
+	 */
+	isdn_tty_change_speed(info);
+
+	info->flags |= ISDN_ASYNC_INITIALIZED;
+	info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
+	info->send_outstanding = 0;
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+isdn_tty_shutdown(modem_info * info)
+{
+	if (!(info->flags & ISDN_ASYNC_INITIALIZED))
+		return;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
+#endif
+	isdn_unlock_drivers();
+	info->msr &= ~UART_MSR_RI;
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
+		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+			isdn_tty_modem_reset_regs(info, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+#endif
+			isdn_tty_modem_hup(info, 1);
+		}
+	}
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+	info->flags &= ~ISDN_ASYNC_INITIALIZED;
+}
+
+/* isdn_tty_write() is the main send-routine. It is called from the upper
+ * levels within the kernel to perform sending data. Depending on the
+ * online-flag it either directs output to the at-command-interpreter or
+ * to the lower level. Additional tasks done here:
+ *  - If online, check for escape-sequence (+++)
+ *  - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
+ *  - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
+ *  - If dialing, abort dial.
+ */
+static int
+isdn_tty_write(struct tty_struct *tty, const u_char * buf, int count)
+{
+	int c;
+	int total = 0;
+	modem_info *info = (modem_info *) tty->driver_data;
+	atemu *m = &info->emu;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write"))
+		return 0;
+	/* See isdn_tty_senddown() */
+	atomic_inc(&info->xmit_lock);
+	while (1) {
+		c = count;
+		if (c > info->xmit_size - info->xmit_count)
+			c = info->xmit_size - info->xmit_count;
+		if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize)
+			c = dev->drv[info->isdn_driver]->maxbufsize;
+		if (c <= 0)
+			break;
+		if ((info->online > 1)
+#ifdef CONFIG_ISDN_AUDIO
+		    || (info->vonline & 3)
+#endif
+			) {
+#ifdef CONFIG_ISDN_AUDIO
+			if (!info->vonline)
+#endif
+				isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c,
+						   &(m->pluscount),
+						   &(m->lastplus));
+			memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+#ifdef CONFIG_ISDN_AUDIO
+			if (info->vonline) {
+				int cc = isdn_tty_handleDLEdown(info, m, c);
+				if (info->vonline & 2) {
+					if (!cc) {
+						/* If DLE decoding results in zero-transmit, but
+						 * c originally was non-zero, do a wakeup.
+						 */
+						tty_wakeup(tty);
+						info->msr |= UART_MSR_CTS;
+						info->lsr |= UART_LSR_TEMT;
+					}
+					info->xmit_count += cc;
+				}
+				if ((info->vonline & 3) == 1) {
+					/* Do NOT handle Ctrl-Q or Ctrl-S
+					 * when in full-duplex audio mode.
+					 */
+					if (isdn_tty_end_vrx(buf, c)) {
+						info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+						printk(KERN_DEBUG
+						       "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n",
+						       info->line);
+#endif
+						isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
+					}
+				}
+			} else
+			if (TTY_IS_FCLASS1(info)) {
+				int cc = isdn_tty_handleDLEdown(info, m, c);
+				
+				if (info->vonline & 4) { /* ETX seen */
+					isdn_ctrl c;
+
+					c.command = ISDN_CMD_FAXCMD;
+					c.driver = info->isdn_driver;
+					c.arg = info->isdn_channel;
+					c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL;
+					c.parm.aux.subcmd = ETX;
+					isdn_command(&c);
+				}
+				info->vonline = 0;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c);
+#endif
+				info->xmit_count += cc;
+			} else
+#endif
+				info->xmit_count += c;
+		} else {
+			info->msr |= UART_MSR_CTS;
+			info->lsr |= UART_LSR_TEMT;
+			if (info->dialing) {
+				info->dialing = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+				printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+#endif
+				isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+				isdn_tty_modem_hup(info, 1);
+			} else
+				c = isdn_tty_edit_at(buf, c, info);
+		}
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	atomic_dec(&info->xmit_lock);
+	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) {
+		if (m->mdmreg[REG_DXMT] & BIT_DXMT) {
+			isdn_tty_senddown(info);
+			isdn_tty_tint(info);
+		}
+		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+	}
+	return total;
+}
+
+static int
+isdn_tty_write_room(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	int ret;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write_room"))
+		return 0;
+	if (!info->online)
+		return info->xmit_size;
+	ret = info->xmit_size - info->xmit_count;
+	return (ret < 0) ? 0 : ret;
+}
+
+static int
+isdn_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_chars_in_buffer"))
+		return 0;
+	if (!info->online)
+		return 0;
+	return (info->xmit_count);
+}
+
+static void
+isdn_tty_flush_buffer(struct tty_struct *tty)
+{
+	modem_info *info;
+
+	if (!tty) {
+		return;
+	}
+	info = (modem_info *) tty->driver_data;
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_buffer")) {
+		return;
+	}
+	isdn_tty_cleanup_xmit(info);
+	info->xmit_count = 0;
+	wake_up_interruptible(&tty->write_wait);
+	tty_wakeup(tty);
+}
+
+static void
+isdn_tty_flush_chars(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_chars"))
+		return;
+	if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
+		isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void
+isdn_tty_throttle(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_throttle"))
+		return;
+	if (I_IXOFF(tty))
+		info->x_char = STOP_CHAR(tty);
+	info->mcr &= ~UART_MCR_RTS;
+}
+
+static void
+isdn_tty_unthrottle(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_unthrottle"))
+		return;
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			info->x_char = START_CHAR(tty);
+	}
+	info->mcr |= UART_MCR_RTS;
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * isdn_tty_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *          is emptied.  On bus types like RS485, the transmitter must
+ *          release the bus after transmitting. This must be done when
+ *          the transmit shift register is empty, not be done when the
+ *          transmit holding register is empty.  This functionality
+ *          allows RS485 driver to be written in user space.
+ */
+static int
+isdn_tty_get_lsr_info(modem_info * info, uint __user * value)
+{
+	u_char status;
+	uint result;
+
+	status = info->lsr;
+	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+	return put_user(result, value);
+}
+
+
+static int
+isdn_tty_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	u_char control, status;
+
+	if (isdn_tty_paranoia_check(info, tty->name, __FUNCTION__))
+		return -ENODEV;
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+	printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
+#endif
+
+	control = info->mcr;
+	status = info->msr;
+	return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+	    | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+	    | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
+	    | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
+	    | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
+	    | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+}
+
+static int
+isdn_tty_tiocmset(struct tty_struct *tty, struct file *file,
+		unsigned int set, unsigned int clear)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->name, __FUNCTION__))
+		return -ENODEV;
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+	printk(KERN_DEBUG "ttyI%d ioctl TIOCMxxx: %x %x\n", info->line, set, clear);
+#endif
+
+	if (set & TIOCM_RTS)
+		info->mcr |= UART_MCR_RTS;
+	if (set & TIOCM_DTR) {
+		info->mcr |= UART_MCR_DTR;
+		isdn_tty_modem_ncarrier(info);
+	}
+
+	if (clear & TIOCM_RTS)
+		info->mcr &= ~UART_MCR_RTS;
+	if (clear & TIOCM_DTR) {
+		info->mcr &= ~UART_MCR_DTR;
+		if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+			isdn_tty_modem_reset_regs(info, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+#endif
+			if (info->online)
+				info->ncarrier = 1;
+			isdn_tty_modem_hup(info, 1);
+		}
+	}
+	return 0;
+}
+
+static int
+isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+	       uint cmd, ulong arg)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	int retval;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_ioctl"))
+		return -ENODEV;
+	if (tty->flags & (1 << TTY_IO_ERROR))
+		return -EIO;
+	switch (cmd) {
+		case TCSBRK:   /* SVID version: non-zero arg --> no break */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
+#endif
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TCSBRKP:  /* support for POSIX tcsendbreak() */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
+#endif
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			return 0;
+		case TIOCGSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
+#endif
+			return put_user(C_CLOCAL(tty) ? 1 : 0, (ulong __user *) arg);
+		case TIOCSSOFTCAR:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
+#endif
+			if (get_user(arg, (ulong __user *) arg))
+				return -EFAULT;
+			tty->termios->c_cflag =
+			    ((tty->termios->c_cflag & ~CLOCAL) |
+			     (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCSERGETLSR:	/* Get line status register */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
+#endif
+			return isdn_tty_get_lsr_info(info, (uint __user *) arg);
+		default:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+			printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+#endif
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static void
+isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (!old_termios)
+		isdn_tty_change_speed(info);
+	else {
+		if (tty->termios->c_cflag == old_termios->c_cflag)
+			return;
+		isdn_tty_change_speed(info);
+		if ((old_termios->c_cflag & CRTSCTS) &&
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			tty->hw_stopped = 0;
+		}
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_open() and friends
+ * ------------------------------------------------------------
+ */
+static int
+isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+{
+	DECLARE_WAITQUEUE(wait, NULL);
+	int do_clocal = 0;
+	int retval;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ISDN_ASYNC_CLOSING)) {
+		if (info->flags & ISDN_ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef MODEM_DO_RESTART
+		if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+	/*
+	 * If non-blocking mode is set, then make the check up front
+	 * and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			do_clocal = 1;
+	}
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * isdn_tty_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (!(tty_hung_up_p(filp)))
+		info->count--;
+	info->blocked_open++;
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ISDN_ASYNC_INITIALIZED)) {
+#ifdef MODEM_DO_RESTART
+			if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		    !(info->flags & ISDN_ASYNC_CLOSING) &&
+		    (do_clocal || (info->msr & UART_MSR_DCD))) {
+			break;
+		}
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int
+isdn_tty_open(struct tty_struct *tty, struct file *filp)
+{
+	modem_info *info;
+	int retval, line;
+
+	line = tty->index;
+	if (line < 0 || line > ISDN_MAX_CHANNELS)
+		return -ENODEV;
+	info = &dev->mdm.info[line];
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open"))
+		return -ENODEV;
+	if (!try_module_get(info->owner)) {
+		printk(KERN_WARNING "%s: cannot reserve module\n", __FUNCTION__);
+		return -ENODEV;
+	}
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name, 
+	       info->count);
+#endif
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+	/*
+	 * Start up serial port
+	 */
+	retval = isdn_tty_startup(info);
+	if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_open return after startup\n");
+#endif
+		module_put(info->owner);
+		return retval;
+	}
+	retval = isdn_tty_block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
+#endif
+		module_put(info->owner);
+		return retval;
+	}
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
+#endif
+	dev->modempoll++;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_open normal exit\n");
+#endif
+	return 0;
+}
+
+static void
+isdn_tty_close(struct tty_struct *tty, struct file *filp)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+	ulong timeout;
+
+	if (!info || isdn_tty_paranoia_check(info, tty->name, "isdn_tty_close"))
+		return;
+	if (tty_hung_up_p(filp)) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
+#endif
+		return;
+	}
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+		printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
+#endif
+		return;
+	}
+	info->flags |= ISDN_ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+		info->callout_termios = *tty->termios;
+
+	tty->closing = 1;
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	if (info->flags & ISDN_ASYNC_INITIALIZED) {
+		tty_wait_until_sent(tty, 3000);	/* 30 seconds timeout */
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies + HZ;
+		while (!(info->lsr & UART_LSR_TEMT)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(20);
+			if (time_after(jiffies,timeout))
+				break;
+		}
+	}
+	dev->modempoll--;
+	isdn_tty_shutdown(info);
+	
+	if (tty->driver->flush_buffer)
+		tty->driver->flush_buffer(tty);
+	tty_ldisc_flush(tty);
+	info->tty = NULL;
+	info->ncarrier = 0;
+	tty->closing = 0;
+	module_put(info->owner);
+	if (info->blocked_open) {
+		msleep_interruptible(500);
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+	printk(KERN_DEBUG "isdn_tty_close normal exit\n");
+#endif
+}
+
+/*
+ * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void
+isdn_tty_hangup(struct tty_struct *tty)
+{
+	modem_info *info = (modem_info *) tty->driver_data;
+
+	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup"))
+		return;
+	isdn_tty_shutdown(info);
+	info->count = 0;
+	info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
+	info->tty = NULL;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/* This routine initializes all emulator-data.
+ */
+static void
+isdn_tty_reset_profile(atemu * m)
+{
+	m->profile[0] = 0;
+	m->profile[1] = 0;
+	m->profile[2] = 43;
+	m->profile[3] = 13;
+	m->profile[4] = 10;
+	m->profile[5] = 8;
+	m->profile[6] = 3;
+	m->profile[7] = 60;
+	m->profile[8] = 2;
+	m->profile[9] = 6;
+	m->profile[10] = 7;
+	m->profile[11] = 70;
+	m->profile[12] = 0x45;
+	m->profile[13] = 4;
+	m->profile[14] = ISDN_PROTO_L2_X75I;
+	m->profile[15] = ISDN_PROTO_L3_TRANS;
+	m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
+	m->profile[17] = ISDN_MODEM_WINSIZE;
+	m->profile[18] = 4;
+	m->profile[19] = 0;
+	m->profile[20] = 0;
+	m->profile[23] = 0;
+	m->pmsn[0] = '\0';
+	m->plmsn[0] = '\0';
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+static void
+isdn_tty_modem_reset_vpar(atemu * m)
+{
+	m->vpar[0] = 2;         /* Voice-device            (2 = phone line) */
+	m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
+	m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
+	m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
+	m->vpar[4] = 0;         /* DTMF detection level    (0 = softcode  ) */
+	m->vpar[5] = 8;         /* DTMF interval           (8 * 5 ms.     ) */
+}
+#endif
+
+#ifdef CONFIG_ISDN_TTY_FAX
+static void
+isdn_tty_modem_reset_faxpar(modem_info * info)
+{
+	T30_s *f = info->fax;
+
+	f->code = 0;
+	f->phase = ISDN_FAX_PHASE_IDLE;
+	f->direction = 0;
+	f->resolution = 1;	/* fine */
+	f->rate = 5;		/* 14400 bit/s */
+	f->width = 0;
+	f->length = 0;
+	f->compression = 0;
+	f->ecm = 0;
+	f->binary = 0;
+	f->scantime = 0;
+	memset(&f->id[0], 32, FAXIDLEN - 1);
+	f->id[FAXIDLEN - 1] = 0;
+	f->badlin = 0;
+	f->badmul = 0;
+	f->bor = 0;
+	f->nbc = 0;
+	f->cq = 0;
+	f->cr = 0;
+	f->ctcrty = 0;
+	f->minsp = 0;
+	f->phcto = 30;
+	f->rel = 0;
+	memset(&f->pollid[0], 32, FAXIDLEN - 1);
+	f->pollid[FAXIDLEN - 1] = 0;
+}
+#endif
+
+static void
+isdn_tty_modem_reset_regs(modem_info * info, int force)
+{
+	atemu *m = &info->emu;
+	if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
+		memcpy(m->mdmreg, m->profile, ISDN_MODEM_NUMREG);
+		memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+		memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
+		info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	isdn_tty_modem_reset_vpar(m);
+#endif
+#ifdef CONFIG_ISDN_TTY_FAX
+	isdn_tty_modem_reset_faxpar(info);
+#endif
+	m->mdmcmdl = 0;
+}
+
+static void
+modem_write_profile(atemu * m)
+{
+	memcpy(m->profile, m->mdmreg, ISDN_MODEM_NUMREG);
+	memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+	memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
+	if (dev->profd)
+		send_sig(SIGIO, dev->profd, 1);
+}
+
+static struct tty_operations modem_ops = {
+        .open = isdn_tty_open,
+	.close = isdn_tty_close,
+	.write = isdn_tty_write,
+	.flush_chars = isdn_tty_flush_chars,
+	.write_room = isdn_tty_write_room,
+	.chars_in_buffer = isdn_tty_chars_in_buffer,
+	.flush_buffer = isdn_tty_flush_buffer,
+	.ioctl = isdn_tty_ioctl,
+	.throttle = isdn_tty_throttle,
+	.unthrottle = isdn_tty_unthrottle,
+	.set_termios = isdn_tty_set_termios,
+	.hangup = isdn_tty_hangup,
+	.tiocmget = isdn_tty_tiocmget,
+	.tiocmset = isdn_tty_tiocmset,
+};
+
+int
+isdn_tty_modem_init(void)
+{
+	isdn_modem_t	*m;
+	int		i, retval;
+	modem_info	*info;
+
+	m = &dev->mdm;
+	m->tty_modem = alloc_tty_driver(ISDN_MAX_CHANNELS);
+	if (!m->tty_modem)
+		return -ENOMEM;
+	m->tty_modem->name = "ttyI";
+	m->tty_modem->devfs_name = "isdn/ttyI";
+	m->tty_modem->major = ISDN_TTY_MAJOR;
+	m->tty_modem->minor_start = 0;
+	m->tty_modem->type = TTY_DRIVER_TYPE_SERIAL;
+	m->tty_modem->subtype = SERIAL_TYPE_NORMAL;
+	m->tty_modem->init_termios = tty_std_termios;
+	m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	m->tty_modem->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+	m->tty_modem->driver_name = "isdn_tty";
+	tty_set_operations(m->tty_modem, &modem_ops);
+	retval = tty_register_driver(m->tty_modem);
+	if (retval) {
+		printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n");
+		goto err;
+	}
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		info = &m->info[i];
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate fax t30-buffer\n");
+			retval = -ENOMEM;
+			goto err_unregister;
+		}
+#endif
+#ifdef MODULE
+		info->owner = THIS_MODULE;
+#endif
+		spin_lock_init(&info->readlock);
+		init_MUTEX(&info->write_sem);
+		sprintf(info->last_cause, "0000");
+		sprintf(info->last_num, "none");
+		info->last_dir = 0;
+		info->last_lhup = 1;
+		info->last_l2 = -1;
+		info->last_si = 0;
+		isdn_tty_reset_profile(&info->emu);
+		isdn_tty_modem_reset_regs(info, 1);
+		info->magic = ISDN_ASYNC_MAGIC;
+		info->line = i;
+		info->tty = NULL;
+		info->x_char = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		info->isdn_driver = -1;
+		info->isdn_channel = -1;
+		info->drv_index = -1;
+		info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
+		init_timer(&info->nc_timer);
+		info->nc_timer.function = isdn_tty_modem_do_ncarrier;
+		info->nc_timer.data = (unsigned long) info;
+		skb_queue_head_init(&info->xmit_queue);
+#ifdef CONFIG_ISDN_AUDIO
+		skb_queue_head_init(&info->dtmf_queue);
+#endif
+		if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
+			printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+			retval = -ENOMEM;
+			goto err_unregister;
+		}
+		/* Make room for T.70 header */
+		info->xmit_buf += 4;
+	}
+	return 0;
+err_unregister:
+	for (i--; i >= 0; i--) {
+		info = &m->info[i];
+#ifdef CONFIG_ISDN_TTY_FAX
+		kfree(info->fax);
+#endif
+		kfree(info->xmit_buf - 4);
+	}
+	tty_unregister_driver(m->tty_modem);
+ err:
+	put_tty_driver(m->tty_modem);
+	m->tty_modem = NULL;
+	return retval;
+}
+
+void
+isdn_tty_exit(void)
+{
+	modem_info *info;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		info = &dev->mdm.info[i];
+		isdn_tty_cleanup_xmit(info);
+#ifdef CONFIG_ISDN_TTY_FAX
+		kfree(info->fax);
+#endif
+		kfree(info->xmit_buf - 4);
+	}
+	tty_unregister_driver(dev->mdm.tty_modem);
+	put_tty_driver(dev->mdm.tty_modem);
+	dev->mdm.tty_modem = NULL;
+}
+
+
+/*
+ * isdn_tty_match_icall(char *MSN, atemu *tty_emulator, int dev_idx)
+ *      match the MSN against the MSNs (glob patterns) defined for tty_emulator,
+ *      and return 0 for match, 1 for no match, 2 if MSN could match if longer.
+ */
+
+static int
+isdn_tty_match_icall(char *cid, atemu *emu, int di)
+{
+#ifdef ISDN_DEBUG_MODEM_ICALL
+	printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
+	       emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
+	       emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
+#endif
+	if (strlen(emu->lmsn)) {
+		char *p = emu->lmsn;
+		char *q;
+		int  tmp;
+		int  ret = 0;
+
+		while (1) {
+			if ((q = strchr(p, ';')))
+				*q = '\0';
+			if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret)
+				ret = tmp;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
+			       p, isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+			if (q) {
+				*q = ';';
+				p = q;
+				p++;
+			}
+			if (!tmp)
+				return 0;
+			if (!q)
+				break;
+		}
+		return ret;
+	} else {
+		int tmp;
+		tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di));
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
+			       isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+		return tmp;
+	}
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the tty-devices for an appropriate device and bind
+ * it to the ISDN-Channel.
+ * Return:
+ *
+ *  0 = No matching device found.
+ *  1 = A matching device found.
+ *  3 = No match found, but eventually would match, if
+ *      CID is longer.
+ */
+int
+isdn_tty_find_icall(int di, int ch, setup_parm *setup)
+{
+	char *eaz;
+	int i;
+	int wret;
+	int idx;
+	int si1;
+	int si2;
+	char *nr;
+	ulong flags;
+
+	if (!setup->phone[0]) {
+		nr = "0";
+		printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
+	} else
+		nr = setup->phone;
+	si1 = (int) setup->si1;
+	si2 = (int) setup->si2;
+	if (!setup->eazmsn[0]) {
+		printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
+		eaz = "0";
+	} else
+		eaz = setup->eazmsn;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+	printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
+#endif
+	wret = 0;
+	spin_lock_irqsave(&dev->lock, flags);
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+
+                if (info->count == 0)
+                    continue;
+		if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
+		    (info->emu.mdmreg[REG_SI2] == si2))	{         /* SI2 is matching */
+			idx = isdn_dc2minor(di, ch);
+#ifdef ISDN_DEBUG_MODEM_ICALL
+			printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret);
+			printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
+			       info->flags, info->isdn_driver, info->isdn_channel,
+			       dev->usage[idx]);
+#endif
+			if (
+#ifndef FIX_FILE_TRANSFER
+				(info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+#endif
+				(info->isdn_driver == -1) &&
+				(info->isdn_channel == -1) &&
+				(USG_NONE(dev->usage[idx]))) {
+				int matchret;
+
+				if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
+					wret = matchret;
+				if (!matchret) {                  /* EAZ is matching */
+					info->isdn_driver = di;
+					info->isdn_channel = ch;
+					info->drv_index = idx;
+					dev->m_idx[idx] = info->line;
+					dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+					dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); 
+					strcpy(dev->num[idx], nr);
+					strcpy(info->emu.cpn, eaz);
+					info->emu.mdmreg[REG_SI1I] = si2bit[si1];
+					info->emu.mdmreg[REG_PLAN] = setup->plan;
+					info->emu.mdmreg[REG_SCREEN] = setup->screen;
+					isdn_info_update();
+					spin_unlock_irqrestore(&dev->lock, flags);
+					printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+					       info->line);
+					info->msr |= UART_MSR_RI;
+					isdn_tty_modem_result(RESULT_RING, info);
+					isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+					return 1;
+				}
+			}
+		}
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+	printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+	       ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored");
+	return (wret == 2)?3:0;
+}
+
+#define TTY_IS_ACTIVE(info) \
+	(info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE))
+
+int
+isdn_tty_stat_callback(int i, isdn_ctrl *c)
+{
+	int mi;
+	modem_info *info;
+	char *e;
+
+	if (i < 0)
+		return 0;
+	if ((mi = dev->m_idx[i]) >= 0) {
+		info = &dev->mdm.info[mi];
+		switch (c->command) {
+                        case ISDN_STAT_CINF:
+                                printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
+                                info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
+                                if (e == (char *)c->parm.num)
+					info->emu.charge = 0;
+				
+                                break;			
+			case ISDN_STAT_BSENT:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
+#endif
+				if ((info->isdn_driver == c->driver) &&
+				    (info->isdn_channel == c->arg)) {
+					info->msr |= UART_MSR_CTS;
+					if (info->send_outstanding)
+						if (!(--info->send_outstanding))
+							info->lsr |= UART_LSR_TEMT;
+					isdn_tty_tint(info);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_CAUSE:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line);
+#endif
+				/* Signal cause to tty-device */
+				strncpy(info->last_cause, c->parm.num, 5);
+				return 1;
+			case ISDN_STAT_DISPLAY:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line);
+#endif
+				/* Signal display to tty-device */
+				if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && 
+					!(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) {
+				  isdn_tty_at_cout("\r\n", info);
+				  isdn_tty_at_cout("DISPLAY: ", info);
+				  isdn_tty_at_cout(c->parm.display, info);
+				  isdn_tty_at_cout("\r\n", info);
+				}
+				return 1;
+			case ISDN_STAT_DCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing == 1) {
+						info->dialing = 2;
+						return 1;
+					}
+				}
+				break;
+			case ISDN_STAT_DHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing == 1) 
+						isdn_tty_modem_result(RESULT_BUSY, info);
+					if (info->dialing > 1) 
+						isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+					info->dialing = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+#endif
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_BCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
+#endif
+				/* Wake up any processes waiting
+				 * for incoming call of this device when
+				 * DCD follow the state of incoming carrier
+				 */
+				if (info->blocked_open &&
+				   (info->emu.mdmreg[REG_DCD] & BIT_DCD)) {
+					wake_up_interruptible(&info->open_wait);
+				}
+
+				/* Schedule CONNECT-Message to any tty
+				 * waiting for it and
+				 * set DCD-bit of its modem-status.
+				 */
+				if (TTY_IS_ACTIVE(info) ||
+				    (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) {
+					info->msr |= UART_MSR_DCD;
+					info->emu.charge = 0;
+					if (info->dialing & 0xf)
+						info->last_dir = 1;
+					else
+						info->last_dir = 0;
+					info->dialing = 0;
+					info->rcvsched = 1;
+					if (USG_MODEM(dev->usage[i])) {
+						if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
+							strcpy(info->emu.connmsg, c->parm.num);
+							isdn_tty_modem_result(RESULT_CONNECT, info);
+						} else
+							isdn_tty_modem_result(RESULT_CONNECT64000, info);
+					}
+					if (USG_VOICE(dev->usage[i]))
+						isdn_tty_modem_result(RESULT_VCON, info);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_BHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+					printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+#endif
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_NODCH:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line);
+#endif
+				if (TTY_IS_ACTIVE(info)) {
+					if (info->dialing) {
+						info->dialing = 0;
+						info->last_l2 = -1;
+						info->last_si = 0;
+						sprintf(info->last_cause, "0000");
+						isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+					}
+					isdn_tty_modem_hup(info, 0);
+					return 1;
+				}
+				break;
+			case ISDN_STAT_UNLOAD:
+#ifdef ISDN_TTY_STAT_DEBUG
+				printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line);
+#endif
+				for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+					info = &dev->mdm.info[i];
+					if (info->isdn_driver == c->driver) {
+						if (info->online)
+							isdn_tty_modem_hup(info, 1);
+					}
+				}
+				return 1;
+#ifdef CONFIG_ISDN_TTY_FAX
+			case ISDN_STAT_FAXIND:
+				if (TTY_IS_ACTIVE(info)) {
+					isdn_tty_fax_command(info, c); 
+				}
+				break;
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+			case ISDN_STAT_AUDIO:
+				if (TTY_IS_ACTIVE(info)) {
+					switch(c->parm.num[0]) {
+						case ISDN_AUDIO_DTMF:
+							if (info->vonline) {
+								isdn_audio_put_dle_code(info,
+									c->parm.num[1]);
+							}
+							break;
+					}
+				}
+				break;
+#endif
+		}
+	}
+	return 0;
+}
+
+/*********************************************************************
+ Modem-Emulator-Routines
+ *********************************************************************/
+
+#define cmdchar(c) ((c>=' ')&&(c<=0x7f))
+
+/*
+ * Put a message from the AT-emulator into receive-buffer of tty,
+ * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
+ */
+void
+isdn_tty_at_cout(char *msg, modem_info * info)
+{
+	struct tty_struct *tty;
+	atemu *m = &info->emu;
+	char *p;
+	char c;
+	u_long flags;
+	struct sk_buff *skb = NULL;
+	char *sp = NULL;
+
+	if (!msg) {
+		printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
+		return;
+	}
+	spin_lock_irqsave(&info->readlock, flags);
+	tty = info->tty;
+	if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+		spin_unlock_irqrestore(&info->readlock, flags);
+		return;
+	}
+
+	/* use queue instead of direct flip, if online and */
+	/* data is in queue or flip buffer is full */
+	if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) ||
+	    (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) {
+		skb = alloc_skb(strlen(msg), GFP_ATOMIC);
+		if (!skb) {
+			spin_unlock_irqrestore(&info->readlock, flags);
+			return;
+		}
+		sp = skb_put(skb, strlen(msg));
+#ifdef CONFIG_ISDN_AUDIO
+		ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+		ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+	}
+
+	for (p = msg; *p; p++) {
+		switch (*p) {
+			case '\r':
+				c = m->mdmreg[REG_CR];
+				break;
+			case '\n':
+				c = m->mdmreg[REG_LF];
+				break;
+			case '\b':
+				c = m->mdmreg[REG_BS];
+				break;
+			default:
+				c = *p;
+		}
+		if (skb) {
+			*sp++ = c;
+		} else {
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				break;
+			tty_insert_flip_char(tty, c, 0);
+		}
+	}
+	if (skb) {
+		__skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb);
+		dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len;
+		spin_unlock_irqrestore(&info->readlock, flags);
+		/* Schedule dequeuing */
+		if ((dev->modempoll) && (info->rcvsched))
+			isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+
+	} else {
+		spin_unlock_irqrestore(&info->readlock, flags);
+		schedule_delayed_work(&tty->flip.work, 1);
+	}
+}
+
+/*
+ * Perform ATH Hangup
+ */
+static void
+isdn_tty_on_hook(modem_info * info)
+{
+	if (info->isdn_channel >= 0) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+		printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
+#endif
+		isdn_tty_modem_hup(info, 1);
+	}
+}
+
+static void
+isdn_tty_off_hook(void)
+{
+	printk(KERN_DEBUG "isdn_tty_off_hook\n");
+}
+
+#define PLUSWAIT1 (HZ/2)        /* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2)      /* 1.5 sec */
+
+/*
+ * Check Buffer for Modem-escape-sequence, activate timer-callback to
+ * isdn_tty_modem_escape() if sequence found.
+ *
+ * Parameters:
+ *   p          pointer to databuffer
+ *   plus       escape-character
+ *   count      length of buffer
+ *   pluscount  count of valid escape-characters so far
+ *   lastplus   timestamp of last character
+ */
+static void
+isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+		   u_long *lastplus)
+{
+	if (plus > 127)
+		return;
+	if (count > 3) {
+		p += count - 3;
+		count = 3;
+		*pluscount = 0;
+	}
+	while (count > 0) {
+		if (*(p++) == plus) {
+			if ((*pluscount)++) {
+				/* Time since last '+' > 0.5 sec. ? */
+				if (time_after(jiffies, *lastplus + PLUSWAIT1))
+					*pluscount = 1;
+			} else {
+				/* Time since last non-'+' < 1.5 sec. ? */
+				if (time_before(jiffies, *lastplus + PLUSWAIT2))
+					*pluscount = 0;
+			}
+			if ((*pluscount == 3) && (count == 1))
+				isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
+			if (*pluscount > 3)
+				*pluscount = 1;
+		} else
+			*pluscount = 0;
+		*lastplus = jiffies;
+		count--;
+	}
+}
+
+/*
+ * Return result of AT-emulator to tty-receive-buffer, depending on
+ * modem-register 12, bit 0 and 1.
+ * For CONNECT-messages also switch to online-mode.
+ * For RING-message handle auto-ATA if register 0 != 0
+ */
+
+static void
+isdn_tty_modem_result(int code, modem_info * info)
+{
+	atemu *m = &info->emu;
+	static char *msg[] =
+	{"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
+	 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
+	 "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
+	char s[ISDN_MSNLEN+10];
+
+	switch (code) {
+		case RESULT_RING:
+			m->mdmreg[REG_RINGCNT]++;
+			if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA])
+				/* Automatically accept incoming call */
+				isdn_tty_cmd_ATA(info);
+			break;
+		case RESULT_NO_CARRIER:
+#ifdef ISDN_DEBUG_MODEM_HUP
+			printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
+			       (info->flags & ISDN_ASYNC_CLOSING),
+			       (!info->tty));
+#endif
+			m->mdmreg[REG_RINGCNT] = 0;
+			del_timer(&info->nc_timer);
+			info->ncarrier = 0;
+			if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+				return;
+			}
+#ifdef CONFIG_ISDN_AUDIO
+			if (info->vonline & 1) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n",
+				       info->line);
+#endif
+				/* voice-recording, add DLE-ETX */
+				isdn_tty_at_cout("\020\003", info);
+			}
+			if (info->vonline & 2) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+				printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n",
+				       info->line);
+#endif
+				/* voice-playing, add DLE-DC4 */
+				isdn_tty_at_cout("\020\024", info);
+			}
+#endif
+			break;
+		case RESULT_CONNECT:
+		case RESULT_CONNECT64000:
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 2;
+			break;
+		case RESULT_VCON:
+#ifdef ISDN_DEBUG_MODEM_VOICE
+			printk(KERN_DEBUG "res3: send VCON on ttyI%d\n",
+			       info->line);
+#endif
+			sprintf(info->last_cause, "0000");
+			if (!info->online)
+				info->online = 1;
+			break;
+	} /* switch(code) */
+
+	if (m->mdmreg[REG_RESP] & BIT_RESP) {
+		/* Show results */
+		if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) {
+			/* Show numeric results only */
+			sprintf(s, "\r\n%d\r\n", code);
+			isdn_tty_at_cout(s, info);
+		} else {
+			if (code == RESULT_RING) {
+			    /* return if "show RUNG" and ringcounter>1 */
+			    if ((m->mdmreg[REG_RUNG] & BIT_RUNG) &&
+				    (m->mdmreg[REG_RINGCNT] > 1))
+						return;
+			    /* print CID, _before_ _every_ ring */
+			    if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) {
+				    isdn_tty_at_cout("\r\nCALLER NUMBER: ", info);
+				    isdn_tty_at_cout(dev->num[info->drv_index], info);
+				    if (m->mdmreg[REG_CDN] & BIT_CDN) {
+					    isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
+					    isdn_tty_at_cout(info->emu.cpn, info);
+				    }
+			    }
+			}
+			isdn_tty_at_cout("\r\n", info);
+			isdn_tty_at_cout(msg[code], info);
+			switch (code) {
+				case RESULT_CONNECT:
+					switch (m->mdmreg[REG_L2PROT]) {
+						case ISDN_PROTO_L2_MODEM:
+							isdn_tty_at_cout(" ", info);
+							isdn_tty_at_cout(m->connmsg, info);
+							break;
+					}
+					break;
+				case RESULT_RING:
+					/* Append CPN, if enabled */
+					if ((m->mdmreg[REG_CPN] & BIT_CPN)) {
+						sprintf(s, "/%s", m->cpn);
+						isdn_tty_at_cout(s, info);
+					}
+					/* Print CID only once, _after_ 1st RING */
+					if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
+					    (m->mdmreg[REG_RINGCNT] == 1)) {
+						isdn_tty_at_cout("\r\n", info);
+						isdn_tty_at_cout("CALLER NUMBER: ", info);
+						isdn_tty_at_cout(dev->num[info->drv_index], info);
+						if (m->mdmreg[REG_CDN] & BIT_CDN) {
+							isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
+							isdn_tty_at_cout(info->emu.cpn, info);
+						}
+					}
+					break;
+				case RESULT_NO_CARRIER:
+				case RESULT_NO_DIALTONE:
+				case RESULT_BUSY:
+				case RESULT_NO_ANSWER:
+					m->mdmreg[REG_RINGCNT] = 0;
+					/* Append Cause-Message if enabled */
+					if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) {
+						sprintf(s, "/%s", info->last_cause);
+						isdn_tty_at_cout(s, info);
+					}
+					break;
+				case RESULT_CONNECT64000:
+					/* Append Protocol to CONNECT message */
+					switch (m->mdmreg[REG_L2PROT]) {
+						case ISDN_PROTO_L2_X75I:
+						case ISDN_PROTO_L2_X75UI:
+						case ISDN_PROTO_L2_X75BUI:
+							isdn_tty_at_cout("/X.75", info);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							isdn_tty_at_cout("/HDLC", info);
+							break;
+						case ISDN_PROTO_L2_V11096:
+							isdn_tty_at_cout("/V110/9600", info);
+							break;
+						case ISDN_PROTO_L2_V11019:
+							isdn_tty_at_cout("/V110/19200", info);
+							break;
+						case ISDN_PROTO_L2_V11038:
+							isdn_tty_at_cout("/V110/38400", info);
+							break;
+					}
+					if (m->mdmreg[REG_T70] & BIT_T70) {
+						isdn_tty_at_cout("/T.70", info);
+						if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+							isdn_tty_at_cout("+", info);
+					}
+					break;
+			}
+			isdn_tty_at_cout("\r\n", info);
+		}
+	}
+	if (code == RESULT_NO_CARRIER) {
+		if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+			return;
+		}
+		tty_ldisc_flush(info->tty);
+		if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+		    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+		       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
+			tty_hangup(info->tty);
+		}
+	}
+}
+
+
+/*
+ * Display a modem-register-value.
+ */
+static void
+isdn_tty_show_profile(int ridx, modem_info * info)
+{
+	char v[6];
+
+	sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
+	isdn_tty_at_cout(v, info);
+}
+
+/*
+ * Get MSN-string from char-pointer, set pointer to end of number
+ */
+static void
+isdn_tty_get_msnstr(char *n, char **p)
+{
+	int limit = ISDN_MSNLEN - 1;
+
+	while (((*p[0] >= '0' && *p[0] <= '9') ||
+		/* Why a comma ??? */
+		(*p[0] == ',') || (*p[0] == ':')) &&
+		(limit--))
+		*n++ = *p[0]++;
+	*n = '\0';
+}
+
+/*
+ * Get phone-number from modem-commandbuffer
+ */
+static void
+isdn_tty_getdial(char *p, char *q,int cnt)
+{
+	int first = 1;
+	int limit = ISDN_MSNLEN - 1;	/* MUST match the size of interface var to avoid
+					buffer overflow */
+
+	while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) {
+		if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) ||
+		    (*p == '*') || (*p == '#')) {
+			*q++ = *p;
+			limit--;
+		}
+		if(!limit)
+			break;
+		p++;
+		first = 0;
+	}
+	*q = 0;
+}
+
+#define PARSE_ERROR { isdn_tty_modem_result(RESULT_ERROR, info); return; }
+#define PARSE_ERROR1 { isdn_tty_modem_result(RESULT_ERROR, info); return 1; }
+
+static void
+isdn_tty_report(modem_info * info)
+{
+	atemu *m = &info->emu;
+	char s[80];
+
+	isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
+	sprintf(s, "    Remote Number:    %s\r\n", info->last_num);
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Direction:        %s\r\n", info->last_dir ? "outgoing" : "incoming");
+	isdn_tty_at_cout(s, info);
+	isdn_tty_at_cout("    Layer-2 Protocol: ", info);
+	switch (info->last_l2) {
+		case ISDN_PROTO_L2_X75I:
+			isdn_tty_at_cout("X.75i", info);
+			break;
+		case ISDN_PROTO_L2_X75UI:
+			isdn_tty_at_cout("X.75ui", info);
+			break;
+		case ISDN_PROTO_L2_X75BUI:
+			isdn_tty_at_cout("X.75bui", info);
+			break;
+		case ISDN_PROTO_L2_HDLC:
+			isdn_tty_at_cout("HDLC", info);
+			break;
+		case ISDN_PROTO_L2_V11096:
+			isdn_tty_at_cout("V.110 9600 Baud", info);
+			break;
+		case ISDN_PROTO_L2_V11019:
+			isdn_tty_at_cout("V.110 19200 Baud", info);
+			break;
+		case ISDN_PROTO_L2_V11038:
+			isdn_tty_at_cout("V.110 38400 Baud", info);
+			break;
+		case ISDN_PROTO_L2_TRANS:
+			isdn_tty_at_cout("transparent", info);
+			break;
+		case ISDN_PROTO_L2_MODEM:
+			isdn_tty_at_cout("modem", info);
+			break;
+		case ISDN_PROTO_L2_FAX:
+			isdn_tty_at_cout("fax", info);
+			break;
+		default:
+			isdn_tty_at_cout("unknown", info);
+			break;
+	}
+	if (m->mdmreg[REG_T70] & BIT_T70) {
+		isdn_tty_at_cout("/T.70", info);
+		if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+			isdn_tty_at_cout("+", info);
+	}
+	isdn_tty_at_cout("\r\n", info);
+	isdn_tty_at_cout("    Service:          ", info);
+	switch (info->last_si) {
+		case 1:
+			isdn_tty_at_cout("audio\r\n", info);
+			break;
+		case 5:
+			isdn_tty_at_cout("btx\r\n", info);
+			break;
+		case 7:
+			isdn_tty_at_cout("data\r\n", info);
+			break;
+		default:
+			sprintf(s, "%d\r\n", info->last_si);
+			isdn_tty_at_cout(s, info);
+			break;
+	}
+	sprintf(s, "    Hangup location:  %s\r\n", info->last_lhup ? "local" : "remote");
+	isdn_tty_at_cout(s, info);
+	sprintf(s, "    Last cause:       %s\r\n", info->last_cause);
+	isdn_tty_at_cout(s, info);
+}
+
+/*
+ * Parse AT&.. commands.
+ */
+static int
+isdn_tty_cmd_ATand(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	int i;
+	char rb[100];
+
+#define MAXRB (sizeof(rb) - 1)
+
+	switch (*p[0]) {
+		case 'B':
+			/* &B - Set Buffersize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
+				PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+			if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF))
+				PARSE_ERROR1;
+#endif
+			m->mdmreg[REG_PSIZE] = i / 16;
+			info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+			switch (m->mdmreg[REG_L2PROT]) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+					info->xmit_size /= 10;		
+			}
+			break;
+		case 'C':
+			/* &C - DCD Status */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[REG_DCD] &= ~BIT_DCD;
+					break;
+				case 1:
+					m->mdmreg[REG_DCD] |= BIT_DCD;
+					break;
+				default:
+					PARSE_ERROR1
+			}
+			break;
+		case 'D':
+			/* &D - Set DTR-Low-behavior */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
+					break;
+				case 2:
+					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
+					break;
+				case 3:
+					m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+					m->mdmreg[REG_DTRR] |= BIT_DTRR;
+					break;
+				default:
+					PARSE_ERROR1
+			}
+			break;
+		case 'E':
+			/* &E -Set EAZ/MSN */
+			p[0]++;
+			isdn_tty_get_msnstr(m->msn, p);
+			break;
+		case 'F':
+			/* &F -Set Factory-Defaults */
+			p[0]++;
+			if (info->msr & UART_MSR_DCD)
+				PARSE_ERROR1;
+			isdn_tty_reset_profile(m);
+			isdn_tty_modem_reset_regs(info, 1);
+			break;
+#ifdef DUMMY_HAYES_AT
+		case 'K':
+			/* only for be compilant with common scripts */
+			/* &K Flowcontrol - no function */
+			p[0]++;
+			isdn_getnum(p);
+			break;
+#endif
+		case 'L':
+			/* &L -Set Numbers to listen on */
+			p[0]++;
+			i = 0;
+			while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) &&
+			       (i < ISDN_LMSNLEN))
+				m->lmsn[i++] = *p[0]++;
+			m->lmsn[i] = '\0';
+			break;
+		case 'R':
+			/* &R - Set V.110 bitrate adaption */
+			p[0]++;
+			i = isdn_getnum(p);
+			switch (i) {
+				case 0:
+					/* Switch off V.110, back to X.75 */
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					m->mdmreg[REG_SI2] = 0;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+					break;
+				case 9600:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096;
+					m->mdmreg[REG_SI2] = 197;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				case 19200:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019;
+					m->mdmreg[REG_SI2] = 199;
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				case 38400:
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038;
+					m->mdmreg[REG_SI2] = 198; /* no existing standard for this */
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			/* Switch off T.70 */
+			m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+			/* Set Service 7 */
+			m->mdmreg[REG_SI1] |= 4;
+			break;
+		case 'S':
+			/* &S - Set Windowsize */
+			p[0]++;
+			i = isdn_getnum(p);
+			if ((i > 0) && (i < 9))
+				m->mdmreg[REG_WSIZE] = i;
+			else
+				PARSE_ERROR1;
+			break;
+		case 'V':
+			/* &V - Show registers */
+			p[0]++;
+			isdn_tty_at_cout("\r\n", info);
+			for (i = 0; i < ISDN_MODEM_NUMREG; i++) {
+				sprintf(rb, "S%02d=%03d%s", i,
+					m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
+				isdn_tty_at_cout(rb, info);
+			}
+			sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
+				strlen(m->msn) ? m->msn : "None");
+			isdn_tty_at_cout(rb, info);
+			if (strlen(m->lmsn)) {
+				isdn_tty_at_cout("\r\nListen: ", info);
+				isdn_tty_at_cout(m->lmsn, info);
+				isdn_tty_at_cout("\r\n", info);
+			}
+			break;
+		case 'W':
+			/* &W - Write Profile */
+			p[0]++;
+			switch (*p[0]) {
+				case '0':
+					p[0]++;
+					modem_write_profile(m);
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 'X':
+			/* &X - Switch to BTX-Mode and T.70 */
+			p[0]++;
+			switch (isdn_getnum(p)) {
+				case 0:
+					m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+					info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+					break;
+				case 1:
+					m->mdmreg[REG_T70] |= BIT_T70;
+					m->mdmreg[REG_T70] &= ~BIT_T70_EXT;
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					info->xmit_size = 112;
+					m->mdmreg[REG_SI1] = 4;
+					m->mdmreg[REG_SI2] = 0;
+					break;
+				case 2:
+					m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT);
+					m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+					info->xmit_size = 112;
+					m->mdmreg[REG_SI1] = 4;
+					m->mdmreg[REG_SI2] = 0;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
+}
+
+static int
+isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m)
+{
+	/* Some plausibility checks */
+	switch (mreg) {
+		case REG_L2PROT:
+			if (mval > ISDN_PROTO_L2_MAX)
+				return 1;
+			break;
+		case REG_PSIZE:
+			if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
+				return 1;
+#ifdef CONFIG_ISDN_AUDIO
+			if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX))
+				return 1;
+#endif
+			info->xmit_size = mval * 16;
+			switch (m->mdmreg[REG_L2PROT]) {
+				case ISDN_PROTO_L2_V11096:
+				case ISDN_PROTO_L2_V11019:
+				case ISDN_PROTO_L2_V11038:
+					info->xmit_size /= 10;		
+			}
+			break;
+		case REG_SI1I:
+		case REG_PLAN:
+		case REG_SCREEN:
+			/* readonly registers */
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Perform ATS command
+ */
+static int
+isdn_tty_cmd_ATS(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	int bitpos;
+	int mreg;
+	int mval;
+	int bval;
+
+	mreg = isdn_getnum(p);
+	if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG)
+		PARSE_ERROR1;
+	switch (*p[0]) {
+		case '=':
+			p[0]++;
+			mval = isdn_getnum(p);
+			if (mval < 0 || mval > 255)
+				PARSE_ERROR1;
+			if (isdn_tty_check_ats(mreg, mval, info, m))
+				PARSE_ERROR1;
+			m->mdmreg[mreg] = mval;
+			break;
+		case '.':
+			/* Set/Clear a single bit */
+			p[0]++;
+			bitpos = isdn_getnum(p);
+			if ((bitpos < 0) || (bitpos > 7))
+				PARSE_ERROR1;
+			switch (*p[0]) {
+				case '=':
+					p[0]++;
+					bval = isdn_getnum(p);
+					if (bval < 0 || bval > 1)
+						PARSE_ERROR1;
+					if (bval)
+						mval = m->mdmreg[mreg] | (1 << bitpos);
+					else
+						mval = m->mdmreg[mreg] & ~(1 << bitpos);
+					if (isdn_tty_check_ats(mreg, mval, info, m))
+						PARSE_ERROR1;
+					m->mdmreg[mreg] = mval;
+					break;
+				case '?':
+					p[0]++;
+					isdn_tty_at_cout("\r\n", info);
+					isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0",
+							 info);
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case '?':
+			p[0]++;
+			isdn_tty_show_profile(mreg, info);
+			break;
+		default:
+			PARSE_ERROR1;
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Perform ATA command
+ */
+static void
+isdn_tty_cmd_ATA(modem_info * info)
+{
+	atemu *m = &info->emu;
+	isdn_ctrl cmd;
+	int l2;
+
+	if (info->msr & UART_MSR_RI) {
+		/* Accept incoming call */
+		info->last_dir = 0;
+		strcpy(info->last_num, dev->num[info->drv_index]);
+		m->mdmreg[REG_RINGCNT] = 0;
+		info->msr &= ~UART_MSR_RI;
+		l2 = m->mdmreg[REG_L2PROT];
+#ifdef CONFIG_ISDN_AUDIO
+		/* If more than one bit set in reg18, autoselect Layer2 */
+		if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
+			if (m->mdmreg[REG_SI1I] == 1) {
+				if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX))
+					l2 = ISDN_PROTO_L2_TRANS;
+			} else
+				l2 = ISDN_PROTO_L2_X75I;
+		}
+#endif
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL2;
+		cmd.arg = info->isdn_channel + (l2 << 8);
+		info->last_l2 = l2;
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.command = ISDN_CMD_SETL3;
+		cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+		if (l2 == ISDN_PROTO_L2_FAX) {
+			cmd.parm.fax = info->fax;
+			info->fax->direction = ISDN_TTY_FAX_CONN_IN;
+		}
+#endif
+		isdn_command(&cmd);
+		cmd.driver = info->isdn_driver;
+		cmd.arg = info->isdn_channel;
+		cmd.command = ISDN_CMD_ACCEPTD;
+		info->dialing = 16;
+		info->emu.carrierwait = 0;
+		isdn_command(&cmd);
+		isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+	} else
+		isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+/*
+ * Parse AT+F.. commands
+ */
+static int
+isdn_tty_cmd_PLUSF(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	char rs[20];
+
+	if (!strncmp(p[0], "CLASS", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d",
+					(m->mdmreg[REG_SI1] & 1) ? 8 : 0);
+#ifdef CONFIG_ISDN_TTY_FAX
+				if (TTY_IS_FCLASS2(info))
+						sprintf(rs, "\r\n2");
+				else if (TTY_IS_FCLASS1(info))
+						sprintf(rs, "\r\n1");
+#endif
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				switch (*p[0]) {
+					case '0':
+						p[0]++;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
+						m->mdmreg[REG_SI1] = 4;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
+#ifdef CONFIG_ISDN_TTY_FAX
+					case '1':
+						p[0]++;
+						if (!(dev->global_features &
+							ISDN_FEATURE_L3_FCLASS1))
+							PARSE_ERROR1;
+						m->mdmreg[REG_SI1] = 1;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
+					case '2':
+						p[0]++;
+						if (!(dev->global_features &
+							ISDN_FEATURE_L3_FCLASS2))
+							PARSE_ERROR1;
+						m->mdmreg[REG_SI1] = 1;
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2;
+						info->xmit_size =
+						    m->mdmreg[REG_PSIZE] * 16;
+						break;
+#endif
+					case '8':
+						p[0]++;
+						/* L2 will change on dialout with si=1 */
+						m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+						m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
+						m->mdmreg[REG_SI1] = 5;
+						info->xmit_size = VBUF;
+						break;
+					case '?':
+						p[0]++;
+						strcpy(rs, "\r\n0,");
+#ifdef CONFIG_ISDN_TTY_FAX
+						if (dev->global_features &
+							ISDN_FEATURE_L3_FCLASS1)
+							strcat(rs, "1,");
+						if (dev->global_features &
+							ISDN_FEATURE_L3_FCLASS2)
+							strcat(rs, "2,");
+#endif
+						strcat(rs, "8");
+						isdn_tty_at_cout(rs, info);
+						break;
+					default:
+						PARSE_ERROR1;
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+#ifdef CONFIG_ISDN_TTY_FAX
+	return (isdn_tty_cmd_PLUSF_FAX(p, info));
+#else
+	PARSE_ERROR1;
+#endif
+}
+
+/*
+ * Parse AT+V.. commands
+ */
+static int
+isdn_tty_cmd_PLUSV(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	isdn_ctrl cmd;
+	static char *vcmd[] =
+	{"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL};
+	int i;
+	int par1;
+	int par2;
+	char rs[20];
+
+	i = 0;
+	while (vcmd[i]) {
+		if (!strncmp(vcmd[i], p[0], 2)) {
+			p[0] += 2;
+			break;
+		}
+		i++;
+	}
+	switch (i) {
+		case 0:
+			/* AT+VNH - Auto hangup feature */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					isdn_tty_at_cout("\r\n1", info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '1':
+							p[0]++;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n1", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 1:
+			/* AT+VIP - Reset all voice parameters */
+			isdn_tty_modem_reset_vpar(m);
+			break;
+		case 2:
+			/* AT+VLS - Select device, accept incoming call */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n%d", m->vpar[0]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '0':
+							p[0]++;
+							m->vpar[0] = 0;
+							break;
+						case '2':
+							p[0]++;
+							m->vpar[0] = 2;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n0,2", info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 3:
+			/* AT+VRX - Start recording */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			info->silence_state = isdn_audio_silence_init(info->silence_state);
+			if (!info->silence_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
+				if (!info->adpcmr) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VRX\n");
+#endif
+			info->vonline |= 1;
+			isdn_tty_modem_result(RESULT_CONNECT, info);
+			return 0;
+			break;
+		case 4:
+			/* AT+VSD - Silence detection */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d>",
+						m->vpar[1],
+						m->vpar[2]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					if ((*p[0]>='0') && (*p[0]<='9')) {
+						par1 = isdn_getnum(p);
+						if ((par1 < 0) || (par1 > 31))
+							PARSE_ERROR1;
+						if (*p[0] != ',')
+							PARSE_ERROR1;
+						p[0]++;
+						par2 = isdn_getnum(p);
+						if ((par2 < 0) || (par2 > 255))
+							PARSE_ERROR1;
+						m->vpar[1] = par1;
+						m->vpar[2] = par2;
+						break;
+					} else 
+					if (*p[0] == '?') {
+						p[0]++;
+						isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+							   info);
+						break;
+					} else
+					PARSE_ERROR1;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 5:
+			/* AT+VSM - Select compression */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d><8000>",
+						m->vpar[3],
+						m->vpar[1]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					switch (*p[0]) {
+						case '2':
+						case '3':
+						case '4':
+						case '5':
+						case '6':
+							par1 = isdn_getnum(p);
+							if ((par1 < 2) || (par1 > 6))
+								PARSE_ERROR1;
+							m->vpar[3] = par1;
+							break;
+						case '?':
+							p[0]++;
+							isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n",
+								   info);
+							isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n",
+								   info);
+							break;
+						default:
+							PARSE_ERROR1;
+					}
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		case 6:
+			/* AT+VTX - Start sending */
+			if (!m->vpar[0])
+				PARSE_ERROR1;
+			if (info->online != 1) {
+				isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+				return 1;
+			}
+			info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+			if (!info->dtmf_state) {
+				printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+				PARSE_ERROR1;
+			}
+			if (m->vpar[3] < 5) {
+				info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
+				if (!info->adpcms) {
+					printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+					PARSE_ERROR1;
+				}
+			}
+#ifdef ISDN_DEBUG_AT
+			printk(KERN_DEBUG "AT: +VTX\n");
+#endif
+			m->lastDLE = 0;
+			info->vonline |= 2;
+			isdn_tty_modem_result(RESULT_CONNECT, info);
+			return 0;
+			break;
+		case 7:
+			/* AT+VDD - DTMF detection */
+			switch (*p[0]) {
+				case '?':
+					p[0]++;
+					sprintf(rs, "\r\n<%d>,<%d>",
+						m->vpar[4],
+						m->vpar[5]);
+					isdn_tty_at_cout(rs, info);
+					break;
+				case '=':
+					p[0]++;
+					if ((*p[0]>='0') && (*p[0]<='9')) {
+						if (info->online != 1)
+							PARSE_ERROR1;
+						par1 = isdn_getnum(p);
+						if ((par1 < 0) || (par1 > 15))
+							PARSE_ERROR1;
+						if (*p[0] != ',')
+							PARSE_ERROR1;
+						p[0]++;
+						par2 = isdn_getnum(p);
+						if ((par2 < 0) || (par2 > 255))
+							PARSE_ERROR1;
+						m->vpar[4] = par1;
+						m->vpar[5] = par2;
+						cmd.driver = info->isdn_driver;
+						cmd.command = ISDN_CMD_AUDIO;
+						cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8);
+						cmd.parm.num[0] = par1;
+						cmd.parm.num[1] = par2;
+						isdn_command(&cmd);
+						break;
+					} else
+					if (*p[0] == '?') {
+						p[0]++;
+						isdn_tty_at_cout("\r\n<0-15>,<0-255>",
+							info);
+						break;
+					} else
+					PARSE_ERROR1;
+					break;
+				default:
+					PARSE_ERROR1;
+			}
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	return 0;
+}
+#endif                          /* CONFIG_ISDN_AUDIO */
+
+/*
+ * Parse and perform an AT-command-line.
+ */
+static void
+isdn_tty_parse_at(modem_info * info)
+{
+	atemu *m = &info->emu;
+	char *p;
+	char ds[40];
+
+#ifdef ISDN_DEBUG_AT
+	printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+#endif
+	for (p = &m->mdmcmd[2]; *p;) {
+		switch (*p) {
+			case ' ':
+				p++;
+				break;
+			case 'A':
+				/* A - Accept incoming call */
+				p++;
+				isdn_tty_cmd_ATA(info);
+				return;
+				break;
+			case 'D':
+				/* D - Dial */
+				if (info->msr & UART_MSR_DCD)
+					PARSE_ERROR;
+				if (info->msr & UART_MSR_RI) {
+					isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+					return;
+				}
+				isdn_tty_getdial(++p, ds, sizeof ds);
+				p += strlen(p);
+				if (!strlen(m->msn))
+					isdn_tty_modem_result(RESULT_NO_MSN_EAZ, info);
+				else if (strlen(ds))
+					isdn_tty_dial(ds, info, m);
+				else
+					PARSE_ERROR;
+				return;
+			case 'E':
+				/* E - Turn Echo on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[REG_ECHO] &= ~BIT_ECHO;
+						break;
+					case 1:
+						m->mdmreg[REG_ECHO] |= BIT_ECHO;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'H':
+				/* H - On/Off-hook */
+				p++;
+				switch (*p) {
+					case '0':
+						p++;
+						isdn_tty_on_hook(info);
+						break;
+					case '1':
+						p++;
+						isdn_tty_off_hook();
+						break;
+					default:
+						isdn_tty_on_hook(info);
+						break;
+				}
+				break;
+			case 'I':
+				/* I - Information */
+				p++;
+				isdn_tty_at_cout("\r\nLinux ISDN", info);
+				switch (*p) {
+					case '0':
+					case '1':
+						p++;
+						break;
+					case '2':
+						p++;
+						isdn_tty_report(info);
+						break;
+					case '3':
+                                                p++;
+                                                sprintf(ds, "\r\n%d", info->emu.charge);
+                                                isdn_tty_at_cout(ds, info);
+                                                break;
+					default:;
+				}
+				break;
+#ifdef DUMMY_HAYES_AT
+			case 'L':
+			case 'M':
+				/* only for be compilant with common scripts */
+				/* no function */
+				p++;
+				isdn_getnum(&p);
+				break;
+#endif
+			case 'O':
+				/* O - Go online */
+				p++;
+				if (info->msr & UART_MSR_DCD)
+					/* if B-Channel is up */
+					isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT:RESULT_CONNECT64000, info);
+				else
+					isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+				return;
+			case 'Q':
+				/* Q - Turn Emulator messages on/off */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[REG_RESP] |= BIT_RESP;
+						break;
+					case 1:
+						m->mdmreg[REG_RESP] &= ~BIT_RESP;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'S':
+				/* S - Set/Get Register */
+				p++;
+				if (isdn_tty_cmd_ATS(&p, info))
+					return;
+				break;
+			case 'V':
+				/* V - Numeric or ASCII Emulator-messages */
+				p++;
+				switch (isdn_getnum(&p)) {
+					case 0:
+						m->mdmreg[REG_RESP] |= BIT_RESPNUM;
+						break;
+					case 1:
+						m->mdmreg[REG_RESP] &= ~BIT_RESPNUM;
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case 'Z':
+				/* Z - Load Registers from Profile */
+				p++;
+				if (info->msr & UART_MSR_DCD) {
+					info->online = 0;
+					isdn_tty_on_hook(info);
+				}
+				isdn_tty_modem_reset_regs(info, 1);
+				break;
+			case '+':
+				p++;
+				switch (*p) {
+#ifdef CONFIG_ISDN_AUDIO
+					case 'F':
+						p++;
+						if (isdn_tty_cmd_PLUSF(&p, info))
+							return;
+						break;
+					case 'V':
+						if ((!(m->mdmreg[REG_SI1] & 1)) ||
+							(m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
+							PARSE_ERROR;
+						p++;
+						if (isdn_tty_cmd_PLUSV(&p, info))
+							return;
+						break;
+#endif                          /* CONFIG_ISDN_AUDIO */
+					case 'S':	/* SUSPEND */
+						p++;
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_suspend(ds, info, m);
+						break;
+					case 'R':	/* RESUME */
+						p++;
+						isdn_tty_get_msnstr(ds, &p);
+						isdn_tty_resume(ds, info, m);
+						break;
+					case 'M':	/* MESSAGE */
+						p++;
+						isdn_tty_send_msg(info, m, p);
+						break;
+					default:
+						PARSE_ERROR;
+				}
+				break;
+			case '&':
+				p++;
+				if (isdn_tty_cmd_ATand(&p, info))
+					return;
+				break;
+			default:
+				PARSE_ERROR;
+		}
+	}
+#ifdef CONFIG_ISDN_AUDIO
+	if (!info->vonline)
+#endif
+		isdn_tty_modem_result(RESULT_OK, info);
+}
+
+/* Need own toupper() because standard-toupper is not available
+ * within modules.
+ */
+#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
+
+/*
+ * Perform line-editing of AT-commands
+ *
+ * Parameters:
+ *   p        inputbuffer
+ *   count    length of buffer
+ *   channel  index to line (minor-device)
+ */
+static int
+isdn_tty_edit_at(const char *p, int count, modem_info * info)
+{
+	atemu *m = &info->emu;
+	int total = 0;
+	u_char c;
+	char eb[2];
+	int cnt;
+
+	for (cnt = count; cnt > 0; p++, cnt--) {
+		c = *p;
+		total++;
+		if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) {
+			/* Separator (CR or LF) */
+			m->mdmcmd[m->mdmcmdl] = 0;
+			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
+				eb[0] = c;
+				eb[1] = 0;
+				isdn_tty_at_cout(eb, info);
+			}
+			if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2))))
+				isdn_tty_parse_at(info);
+			m->mdmcmdl = 0;
+			continue;
+		}
+		if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) {
+			/* Backspace-Function */
+			if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
+				if (m->mdmcmdl)
+					m->mdmcmdl--;
+				if (m->mdmreg[REG_ECHO] & BIT_ECHO)
+					isdn_tty_at_cout("\b", info);
+			}
+			continue;
+		}
+		if (cmdchar(c)) {
+			if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
+				eb[0] = c;
+				eb[1] = 0;
+				isdn_tty_at_cout(eb, info);
+			}
+			if (m->mdmcmdl < 255) {
+				c = my_toupper(c);
+				switch (m->mdmcmdl) {
+					case 1:
+						if (c == 'T') {
+							m->mdmcmd[m->mdmcmdl] = c;
+							m->mdmcmd[++m->mdmcmdl] = 0;
+							break;
+						} else
+							m->mdmcmdl = 0;
+						/* Fall through, check for 'A' */
+					case 0:
+						if (c == 'A') {
+							m->mdmcmd[m->mdmcmdl] = c;
+							m->mdmcmd[++m->mdmcmdl] = 0;
+						}
+						break;
+					default:
+						m->mdmcmd[m->mdmcmdl] = c;
+						m->mdmcmd[++m->mdmcmdl] = 0;
+				}
+			}
+		}
+	}
+	return total;
+}
+
+/*
+ * Switch all modem-channels who are online and got a valid
+ * escape-sequence 1.5 seconds ago, to command-mode.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void
+isdn_tty_modem_escape(void)
+{
+	int ton = 0;
+	int i;
+	int midx;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+		if (USG_MODEM(dev->usage[i]))
+			if ((midx = dev->m_idx[i]) >= 0) {
+				modem_info *info = &dev->mdm.info[midx];
+				if (info->online) {
+					ton = 1;
+					if ((info->emu.pluscount == 3) &&
+					    time_after(jiffies , info->emu.lastplus + PLUSWAIT2)) {
+						info->emu.pluscount = 0;
+						info->online = 0;
+						isdn_tty_modem_result(RESULT_OK, info);
+					}
+				}
+			}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
+}
+
+/*
+ * Put a RING-message to all modem-channels who have the RI-bit set.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void
+isdn_tty_modem_ring(void)
+{
+	int ton = 0;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->msr & UART_MSR_RI) {
+			ton = 1;
+			isdn_tty_modem_result(RESULT_RING, info);
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
+}
+
+/*
+ * For all online tty's, try sending data to
+ * the lower levels.
+ */
+void
+isdn_tty_modem_xmit(void)
+{
+	int ton = 1;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->online) {
+			ton = 1;
+			isdn_tty_senddown(info);
+			isdn_tty_tint(info);
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+/*
+ * Check all channels if we have a 'no carrier' timeout.
+ * Timeout value is set by Register S7.
+ */
+void
+isdn_tty_carrier_timeout(void)
+{
+	int ton = 0;
+	int i;
+
+	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+		modem_info *info = &dev->mdm.info[i];
+		if (info->dialing) {
+			if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
+				info->dialing = 0;
+				isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+				isdn_tty_modem_hup(info, 1);
+			}
+			else
+				ton = 1;
+		}
+	}
+	isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
+}
diff --git a/drivers/isdn/i4l/isdn_tty.h b/drivers/isdn/i4l/isdn_tty.h
new file mode 100644
index 0000000..2423a7f
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_tty.h
@@ -0,0 +1,122 @@
+/* $Id: isdn_tty.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, tty related functions (linklevel).
+ *
+ * Copyright 1994-1999  by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+
+#define DLE 0x10
+#define ETX 0x03
+#define DC4 0x14
+
+
+/*
+ * Definition of some special Registers of AT-Emulator
+ */
+#define REG_RINGATA   0
+#define REG_RINGCNT   1  /* ring counter register */
+#define REG_ESC       2
+#define REG_CR        3
+#define REG_LF        4
+#define REG_BS        5
+
+#define REG_WAITC     7
+
+#define REG_RESP     12  /* show response messages register */
+#define BIT_RESP      1  /* show response messages bit      */
+#define REG_RESPNUM  12  /* show numeric responses register */
+#define BIT_RESPNUM   2  /* show numeric responses bit      */
+#define REG_ECHO     12
+#define BIT_ECHO      4
+#define REG_DCD      12
+#define BIT_DCD       8
+#define REG_CTS      12
+#define BIT_CTS      16
+#define REG_DTRR     12
+#define BIT_DTRR     32
+#define REG_DSR      12
+#define BIT_DSR      64
+#define REG_CPPP     12
+#define BIT_CPPP    128
+
+#define REG_DXMT     13
+#define BIT_DXMT      1
+#define REG_T70      13
+#define BIT_T70       2
+#define BIT_T70_EXT  32
+#define REG_DTRHUP   13
+#define BIT_DTRHUP    4
+#define REG_RESPXT   13
+#define BIT_RESPXT    8
+#define REG_CIDONCE  13
+#define BIT_CIDONCE  16
+#define REG_RUNG     13  /* show RUNG message register      */
+#define BIT_RUNG     64  /* show RUNG message bit           */
+#define REG_DISPLAY  13
+#define BIT_DISPLAY 128
+
+#define REG_L2PROT   14
+#define REG_L3PROT   15
+#define REG_PSIZE    16
+#define REG_WSIZE    17
+#define REG_SI1      18
+#define REG_SI2      19
+#define REG_SI1I     20
+#define REG_PLAN     21
+#define REG_SCREEN   22
+
+#define REG_CPN      23
+#define BIT_CPN       1
+#define REG_CPNFCON  23
+#define BIT_CPNFCON   2
+#define REG_CDN      23
+#define BIT_CDN       4
+
+/* defines for result codes */
+#define RESULT_OK		0
+#define RESULT_CONNECT		1
+#define RESULT_RING		2
+#define RESULT_NO_CARRIER	3
+#define RESULT_ERROR		4
+#define RESULT_CONNECT64000	5
+#define RESULT_NO_DIALTONE	6
+#define RESULT_BUSY		7
+#define RESULT_NO_ANSWER	8
+#define RESULT_RINGING		9
+#define RESULT_NO_MSN_EAZ	10
+#define RESULT_VCON		11
+#define RESULT_RUNG		12
+
+#define TTY_IS_FCLASS1(info) \
+	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1))
+#define TTY_IS_FCLASS2(info) \
+	((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+	 (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2))
+
+extern void isdn_tty_modem_escape(void);
+extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_carrier_timeout(void);
+extern void isdn_tty_modem_xmit(void);
+extern int  isdn_tty_modem_init(void);
+extern void isdn_tty_exit(void);
+extern void isdn_tty_readmodem(void);
+extern int  isdn_tty_find_icall(int, int, setup_parm *);
+extern void isdn_tty_cleanup_xmit(modem_info *);
+extern int  isdn_tty_stat_callback(int, isdn_ctrl *);
+extern int  isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
+extern int  isdn_tty_capi_facility(capi_msg *cm); 
+extern void isdn_tty_at_cout(char *, modem_info *);
+extern void isdn_tty_modem_hup(modem_info *, int);
+#ifdef CONFIG_ISDN_TTY_FAX
+extern int  isdn_tty_cmd_PLUSF_FAX(char **, modem_info *);
+extern int  isdn_tty_fax_command(modem_info *, isdn_ctrl *);
+extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *);
+#endif
diff --git a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c
new file mode 100644
index 0000000..ce2c3ef
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ttyfax.c
@@ -0,0 +1,1122 @@
+/* $Id: isdn_ttyfax.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel).
+ *
+ * Copyright 1999    by Armin Schindler (mac@melware.de)
+ * Copyright 1999    by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999    by Cytronics & Melware
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#undef ISDN_TTY_FAX_STAT_DEBUG
+#undef ISDN_TTY_FAX_CMD_DEBUG
+
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_ttyfax.h"
+
+
+static char *isdn_tty_fax_revision = "$Revision: 1.1.2.2 $";
+
+#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; }
+
+static char *
+isdn_getrev(const char *revision)
+{
+	char *rev;
+	char *p;
+
+	if ((p = strchr(revision, ':'))) {
+		rev = p + 2;
+		p = strchr(rev, '$');
+		*--p = 0;
+	} else
+		rev = "???";
+	return rev;
+}
+
+/*
+ * Fax Class 2 Modem results
+ *
+ */
+
+static void
+isdn_tty_fax_modem_result(int code, modem_info * info)
+{
+	atemu *m = &info->emu;
+	T30_s *f = info->fax;
+	char rs[50];
+	char rss[50];
+	char *rp;
+	int i;
+	static char *msg[] =
+	{"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:",
+	 "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:",
+	 "+FCFR", "+FPTS:", "+FET:"};
+
+
+	isdn_tty_at_cout("\r\n", info);
+	isdn_tty_at_cout(msg[code], info);
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n",
+		msg[code], info->line);
+#endif
+	switch (code) {
+		case 0: /* OK */
+			break;
+		case 1: /* ERROR */
+			break;
+		case 2:	/* +FCON */
+			/* Append CPN, if enabled */
+			if ((m->mdmreg[REG_CPNFCON] & BIT_CPNFCON) &&
+				(!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) {
+				sprintf(rs, "/%s", m->cpn);
+				isdn_tty_at_cout(rs, info);
+			}
+			info->online = 1;
+			f->fet = 0;
+			if (f->phase == ISDN_FAX_PHASE_A)
+				f->phase = ISDN_FAX_PHASE_B;
+			break;
+		case 3:	/* +FCSI */
+		case 8:	/* +FTSI */
+			sprintf(rs, "\"%s\"", f->r_id);
+			isdn_tty_at_cout(rs, info);
+			break;
+		case 4:	/* +FDIS */
+			rs[0] = 0;
+			rp = &f->r_resolution;
+			for (i = 0; i < 8; i++) {
+				sprintf(rss, "%c%s", rp[i] + 48,
+					(i < 7) ? "," : "");
+				strcat(rs, rss);
+			}
+			isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n",
+			       rs, info->line);
+#endif
+			break;
+		case 5:	/* +FHNG */
+			sprintf(rs, "%d", f->code);
+			isdn_tty_at_cout(rs, info);
+			info->faxonline = 0;
+			break;
+		case 6:	/* +FDCS */
+			rs[0] = 0;
+			rp = &f->r_resolution;
+			for (i = 0; i < 8; i++) {
+				sprintf(rss, "%c%s", rp[i] + 48,
+					(i < 7) ? "," : "");
+				strcat(rs, rss);
+			}
+			isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n",
+			       rs, info->line);
+#endif
+			break;
+		case 7:	/* CONNECT */
+			info->faxonline |= 2;
+			break;
+		case 9:	/* FCFR */
+			break;
+		case 10:	/* FPTS */
+			isdn_tty_at_cout("1", info);
+			break;
+		case 11:	/* FET */
+			sprintf(rs, "%d", f->fet);
+			isdn_tty_at_cout(rs, info);
+			break;
+	}
+
+	isdn_tty_at_cout("\r\n", info);
+
+	switch (code) {
+		case 7:	/* CONNECT */
+			info->online = 2;
+			if (info->faxonline & 1) {
+				sprintf(rs, "%c", XON);
+				isdn_tty_at_cout(rs, info);
+			}
+			break;
+	}
+}
+
+int
+isdn_tty_fax_command1(modem_info * info, isdn_ctrl * c)
+{
+	static char *msg[] =
+	{"OK", "CONNECT", "NO CARRIER", "ERROR", "FCERROR"};
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: FCLASS1 cmd(%d)\n", c->parm.aux.cmd);
+#endif
+	if (c->parm.aux.cmd < ISDN_FAX_CLASS1_QUERY) {
+		if (info->online)
+			info->online = 1;
+		isdn_tty_at_cout("\r\n", info);
+		isdn_tty_at_cout(msg[c->parm.aux.cmd], info);
+		isdn_tty_at_cout("\r\n", info);
+	}
+	switch (c->parm.aux.cmd) {
+		case ISDN_FAX_CLASS1_CONNECT:
+			info->online = 2;
+			break;
+		case ISDN_FAX_CLASS1_OK:
+		case ISDN_FAX_CLASS1_FCERROR:
+		case ISDN_FAX_CLASS1_ERROR:
+		case ISDN_FAX_CLASS1_NOCARR:
+			break;
+		case ISDN_FAX_CLASS1_QUERY:
+			isdn_tty_at_cout("\r\n", info);
+			if (!c->parm.aux.para[0]) {
+				isdn_tty_at_cout(msg[ISDN_FAX_CLASS1_ERROR], info);
+				isdn_tty_at_cout("\r\n", info);
+			} else {
+				isdn_tty_at_cout(c->parm.aux.para, info);
+				isdn_tty_at_cout("\r\nOK\r\n", info);
+			}
+			break;
+	}
+	return (0);
+}
+
+int
+isdn_tty_fax_command(modem_info * info, isdn_ctrl * c)
+{
+	T30_s *f = info->fax;
+	char rs[10];
+
+	if (TTY_IS_FCLASS1(info))
+		return (isdn_tty_fax_command1(info, c));
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n",
+	       f->r_code, info->line);
+#endif
+	switch (f->r_code) {
+		case ISDN_TTY_FAX_FCON:
+			info->faxonline = 1;
+			isdn_tty_fax_modem_result(2, info);	/* +FCON */
+			return (0);
+		case ISDN_TTY_FAX_FCON_I:
+			info->faxonline = 16;
+			isdn_tty_fax_modem_result(2, info);	/* +FCON */
+			return (0);
+		case ISDN_TTY_FAX_RID:
+			if (info->faxonline & 1)
+				isdn_tty_fax_modem_result(3, info);	/* +FCSI */
+			if (info->faxonline & 16)
+				isdn_tty_fax_modem_result(8, info);	/* +FTSI */
+			return (0);
+		case ISDN_TTY_FAX_DIS:
+			isdn_tty_fax_modem_result(4, info);	/* +FDIS */
+			return (0);
+		case ISDN_TTY_FAX_HNG:
+			if (f->phase == ISDN_FAX_PHASE_C) {
+				if (f->direction == ISDN_TTY_FAX_CONN_IN) {
+					sprintf(rs, "%c%c", DLE, ETX);
+					isdn_tty_at_cout(rs, info);
+				} else {
+					sprintf(rs, "%c", 0x18);
+					isdn_tty_at_cout(rs, info);
+				}
+				info->faxonline &= ~2;	/* leave data mode */
+				info->online = 1;
+			}
+			f->phase = ISDN_FAX_PHASE_E;
+			isdn_tty_fax_modem_result(5, info);	/* +FHNG */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
+		case ISDN_TTY_FAX_DCS:
+			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
+			isdn_tty_fax_modem_result(7, info);	/* CONNECT */
+			f->phase = ISDN_FAX_PHASE_C;
+			return (0);
+		case ISDN_TTY_FAX_TRAIN_OK:
+			isdn_tty_fax_modem_result(6, info);	/* +FDCS */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
+		case ISDN_TTY_FAX_SENT:
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			return (0);
+		case ISDN_TTY_FAX_CFR:
+			isdn_tty_fax_modem_result(9, info);	/* +FCFR */
+			return (0);
+		case ISDN_TTY_FAX_ET:
+			sprintf(rs, "%c%c", DLE, ETX);
+			isdn_tty_at_cout(rs, info);
+			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
+			isdn_tty_fax_modem_result(11, info);	/* +FET */
+			isdn_tty_fax_modem_result(0, info);	/* OK */
+			info->faxonline &= ~2;	/* leave data mode */
+			info->online = 1;
+			f->phase = ISDN_FAX_PHASE_D;
+			return (0);
+		case ISDN_TTY_FAX_PTS:
+			isdn_tty_fax_modem_result(10, info);	/* +FPTS */
+			if (f->direction == ISDN_TTY_FAX_CONN_OUT) {
+				if (f->fet == 1)
+					f->phase = ISDN_FAX_PHASE_B;
+				if (f->fet == 0)
+					isdn_tty_fax_modem_result(0, info);	/* OK */
+			}
+			return (0);
+		case ISDN_TTY_FAX_EOP:
+			info->faxonline &= ~2;	/* leave data mode */
+			info->online = 1;
+			f->phase = ISDN_FAX_PHASE_D;
+			return (0);
+
+	}
+	return (-1);
+}
+
+
+void
+isdn_tty_fax_bitorder(modem_info * info, struct sk_buff *skb)
+{
+	__u8 LeftMask;
+	__u8 RightMask;
+	__u8 fBit;
+	__u8 Data;
+	int i;
+
+	if (!info->fax->bor) {
+		for (i = 0; i < skb->len; i++) {
+			Data = skb->data[i];
+			for (
+				    LeftMask = 0x80, RightMask = 0x01;
+				    LeftMask > RightMask;
+				    LeftMask >>= 1, RightMask <<= 1
+			    ) {
+				fBit = (Data & LeftMask);
+				if (Data & RightMask)
+					Data |= LeftMask;
+				else
+					Data &= ~LeftMask;
+				if (fBit)
+					Data |= RightMask;
+				else
+					Data &= ~RightMask;
+
+			}
+			skb->data[i] = Data;
+		}
+	}
+}
+
+/*
+ * Parse AT+F.. FAX class 1 commands
+ */
+
+int
+isdn_tty_cmd_FCLASS1(char **p, modem_info * info)
+{
+	static char *cmd[] =
+	{"AE", "TS", "RS", "TM", "RM", "TH", "RH"};
+	isdn_ctrl c;
+	int par, i;
+	u_long flags;
+
+	for (c.parm.aux.cmd = 0; c.parm.aux.cmd < 7; c.parm.aux.cmd++)
+		if (!strncmp(p[0], cmd[c.parm.aux.cmd], 2))
+			break;
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 (%s,%d)\n", p[0], c.parm.aux.cmd);
+#endif
+	if (c.parm.aux.cmd == 7)
+		PARSE_ERROR1;
+
+	p[0] += 2;
+	switch (*p[0]) {
+		case '?':
+			p[0]++;
+			c.parm.aux.subcmd = AT_QUERY;
+			break;
+		case '=':
+			p[0]++;
+			if (*p[0] == '?') {
+				p[0]++;
+				c.parm.aux.subcmd = AT_EQ_QUERY;
+			} else {
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				c.parm.aux.subcmd = AT_EQ_VALUE;
+				c.parm.aux.para[0] = par;
+			}
+			break;
+		case 0:
+			c.parm.aux.subcmd = AT_COMMAND;
+			break;
+		default:
+			PARSE_ERROR1;
+	}
+	c.command = ISDN_CMD_FAXCMD;
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+	printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 %d/%d/%d)\n",
+	       c.parm.aux.cmd, c.parm.aux.subcmd, c.parm.aux.para[0]);
+#endif
+	if (info->isdn_driver < 0) {
+		if ((c.parm.aux.subcmd == AT_EQ_VALUE) ||
+		    (c.parm.aux.subcmd == AT_COMMAND)) {
+			PARSE_ERROR1;
+		}
+		spin_lock_irqsave(&dev->lock, flags);
+		/* get a temporary connection to the first free fax driver */
+		i = isdn_get_free_channel(ISDN_USAGE_FAX, ISDN_PROTO_L2_FAX,
+					  ISDN_PROTO_L3_FCLASS1, -1, -1, "00");
+		if (i < 0) {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			PARSE_ERROR1;
+		}
+		info->isdn_driver = dev->drvmap[i];
+		info->isdn_channel = dev->chanmap[i];
+		info->drv_index = i;
+		dev->m_idx[i] = info->line;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		c.driver = info->isdn_driver;
+		c.arg = info->isdn_channel;
+		isdn_command(&c);
+		spin_lock_irqsave(&dev->lock, flags);
+		isdn_free_channel(info->isdn_driver, info->isdn_channel,
+				  ISDN_USAGE_FAX);
+		info->isdn_driver = -1;
+		info->isdn_channel = -1;
+		if (info->drv_index >= 0) {
+			dev->m_idx[info->drv_index] = -1;
+			info->drv_index = -1;
+		}
+		spin_unlock_irqrestore(&dev->lock, flags);
+	} else {
+		c.driver = info->isdn_driver;
+		c.arg = info->isdn_channel;
+		isdn_command(&c);
+	}
+	return 1;
+}
+
+/*
+ * Parse AT+F.. FAX class 2 commands
+ */
+
+int
+isdn_tty_cmd_FCLASS2(char **p, modem_info * info)
+{
+	atemu *m = &info->emu;
+	T30_s *f = info->fax;
+	isdn_ctrl cmd;
+	int par;
+	char rs[50];
+	char rss[50];
+	int maxdccval[] =
+	{1, 5, 2, 2, 3, 2, 0, 7};
+
+	/* FAA still unchanged */
+	if (!strncmp(p[0], "AA", 2)) {	/* TODO */
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", 0);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				par = isdn_getnum(p);
+				if ((par < 0) || (par > 255))
+					PARSE_ERROR1;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */
+	if (!strncmp(p[0], "BADLIN", 6)) {
+		p[0] += 6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->badlin);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->badlin = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
+	if (!strncmp(p[0], "BADMUL", 6)) {
+		p[0] += 6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->badmul);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->badmul = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BOR=n - Phase C bit order, 0=direct, 1=reverse */
+	if (!strncmp(p[0], "BOR", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->bor);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->bor = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* NBC=n - No Best Capabilities */
+	if (!strncmp(p[0], "NBC", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->nbc);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->nbc = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* BUF? - Readonly buffersize readout  */
+	if (!strncmp(p[0], "BUF?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE]));
+#endif
+		p[0]++;
+		sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE]));
+		isdn_tty_at_cout(rs, info);
+		return 0;
+	}
+	/* CIG=string - local fax station id string for polling rx */
+	if (!strncmp(p[0], "CIG", 3)) {
+		int i, r;
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n\"%s\"", f->pollid);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n\"STRING\"");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					if (*p[0] == '"')
+						p[0]++;
+					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+						f->pollid[i] = *p[0]++;
+					}
+					if (*p[0] == '"')
+						p[0]++;
+					for (r = i; r < FAXIDLEN; r++) {
+						f->pollid[r] = 32;
+					}
+					f->pollid[FAXIDLEN - 1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */
+	if (!strncmp(p[0], "CQ", 2)) {
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->cq);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1,2");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 2))
+						PARSE_ERROR1;
+					f->cq = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */
+	if (!strncmp(p[0], "CR", 2)) {
+		p[0] += 2;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->cr);	/* read actual value from struct and print */
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");		/* display online help */
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->cr = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* CTCRTY=value - ECM retry count */
+	if (!strncmp(p[0], "CTCRTY", 6)) {
+		p[0] += 6;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->ctcrty);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->ctcrty = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */
+	if (!strncmp(p[0], "DCC", 3)) {
+		char *rp = &f->resolution;
+		int i;
+
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				strcpy(rs, "\r\n");
+				for (i = 0; i < 8; i++) {
+					sprintf(rss, "%c%s", rp[i] + 48,
+						(i < 7) ? "," : "");
+					strcat(rs, rss);
+				}
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+					p[0]++;
+				} else {
+					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
+						if (*p[0] != ',') {
+							if ((*p[0] - 48) > maxdccval[i]) {
+								PARSE_ERROR1;
+							}
+							rp[i] = *p[0] - 48;
+							p[0]++;
+							if (*p[0] == ',')
+								p[0]++;
+						} else
+							p[0]++;
+					}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n",
+					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */
+	if (!strncmp(p[0], "DIS", 3)) {
+		char *rp = &f->resolution;
+		int i;
+
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				strcpy(rs, "\r\n");
+				for (i = 0; i < 8; i++) {
+					sprintf(rss, "%c%s", rp[i] + 48,
+						(i < 7) ? "," : "");
+					strcat(rs, rss);
+				}
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+					p[0]++;
+				} else {
+					for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
+						if (*p[0] != ',') {
+							if ((*p[0] - 48) > maxdccval[i]) {
+								PARSE_ERROR1;
+							}
+							rp[i] = *p[0] - 48;
+							p[0]++;
+							if (*p[0] == ',')
+								p[0]++;
+						} else
+							p[0]++;
+					}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n",
+					       rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* DR - Receive Phase C data command, initiates document reception */
+	if (!strncmp(p[0], "DR", 2)) {
+		p[0] += 2;
+		if ((info->faxonline & 16) &&	/* incoming connection */
+		    ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) {
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax FDR\n");
+#endif
+			f->code = ISDN_TTY_FAX_DR;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+			if (f->phase == ISDN_FAX_PHASE_B) {
+				f->phase = ISDN_FAX_PHASE_C;
+			} else if (f->phase == ISDN_FAX_PHASE_D) {
+				switch (f->fet) {
+					case 0:	/* next page will be received */
+						f->phase = ISDN_FAX_PHASE_C;
+						isdn_tty_fax_modem_result(7, info);	/* CONNECT */
+						break;
+					case 1:	/* next doc will be received */
+						f->phase = ISDN_FAX_PHASE_B;
+						break;
+					case 2:	/* fax session is terminating */
+						f->phase = ISDN_FAX_PHASE_E;
+						break;
+					default:
+						PARSE_ERROR1;
+				}
+			}
+		} else {
+			PARSE_ERROR1;
+		}
+		return 1;
+	}
+	/* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */
+	if (!strncmp(p[0], "DT", 2)) {
+		int i, val[] =
+		{4, 0, 2, 3};
+		char *rp = &f->resolution;
+
+		p[0] += 2;
+		if (!info->faxonline & 1)	/* not outgoing connection */
+			PARSE_ERROR1;
+
+		for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 4); i++) {
+			if (*p[0] != ',') {
+				if ((*p[0] - 48) > maxdccval[val[i]]) {
+					PARSE_ERROR1;
+				}
+				rp[val[i]] = *p[0] - 48;
+				p[0]++;
+				if (*p[0] == ',')
+					p[0]++;
+			} else
+				p[0]++;
+		}
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n",
+		       rp[4], rp[0], rp[2], rp[3]);
+#endif
+		if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) {
+			f->code = ISDN_TTY_FAX_DT;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+			if (f->phase == ISDN_FAX_PHASE_D) {
+				f->phase = ISDN_FAX_PHASE_C;
+				isdn_tty_fax_modem_result(7, info);	/* CONNECT */
+			}
+		} else {
+			PARSE_ERROR1;
+		}
+		return 1;
+	}
+	/* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */
+	if (!strncmp(p[0], "ECM", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->ecm);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,2");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par != 0) && (par != 2))
+						PARSE_ERROR1;
+					f->ecm = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* ET=n - End of page or document */
+	if (!strncmp(p[0], "ET=", 3)) {
+		p[0] += 3;
+		if (*p[0] == '?') {
+			p[0]++;
+			sprintf(rs, "\r\n0-2");
+			isdn_tty_at_cout(rs, info);
+		} else {
+			if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1))
+				PARSE_ERROR1;
+			par = isdn_getnum(p);
+			if ((par < 0) || (par > 2))
+				PARSE_ERROR1;
+			f->fet = par;
+			f->code = ISDN_TTY_FAX_ET;
+			cmd.driver = info->isdn_driver;
+			cmd.arg = info->isdn_channel;
+			cmd.command = ISDN_CMD_FAXCMD;
+			isdn_command(&cmd);
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+			printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par);
+#endif
+			return 1;
+		}
+		return 0;
+	}
+	/* K - terminate */
+	if (!strncmp(p[0], "K", 1)) {
+		p[0] += 1;
+		if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E))
+			PARSE_ERROR1;
+		isdn_tty_modem_hup(info, 1);
+		return 1;
+	}
+	/* LID=string - local fax ID */
+	if (!strncmp(p[0], "LID", 3)) {
+		int i, r;
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n\"%s\"", f->id);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n\"STRING\"");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					if (*p[0] == '"')
+						p[0]++;
+					for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+						f->id[i] = *p[0]++;
+					}
+					if (*p[0] == '"')
+						p[0]++;
+					for (r = i; r < FAXIDLEN; r++) {
+						f->id[r] = 32;
+					}
+					f->id[FAXIDLEN - 1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+
+	/* MDL? - DCE Model       */
+	if (!strncmp(p[0], "MDL?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: FMDL?\n");
+#endif
+		isdn_tty_at_cout("\r\nisdn4linux", info);
+		return 0;
+	}
+	/* MFR? - DCE Manufacturer */
+	if (!strncmp(p[0], "MFR?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: FMFR?\n");
+#endif
+		isdn_tty_at_cout("\r\nisdn4linux", info);
+		return 0;
+	}
+	/* MINSP=n - Minimum Speed for Phase C */
+	if (!strncmp(p[0], "MINSP", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->minsp);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-5");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 5))
+						PARSE_ERROR1;
+					f->minsp = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* PHCTO=value - DTE phase C timeout */
+	if (!strncmp(p[0], "PHCTO", 5)) {
+		p[0] += 5;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->phcto);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0-255");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 255))
+						PARSE_ERROR1;
+					f->phcto = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+
+	/* REL=n - Phase C received EOL alignment */
+	if (!strncmp(p[0], "REL", 3)) {
+		p[0] += 3;
+		switch (*p[0]) {
+			case '?':
+				p[0]++;
+				sprintf(rs, "\r\n%d", f->rel);
+				isdn_tty_at_cout(rs, info);
+				break;
+			case '=':
+				p[0]++;
+				if (*p[0] == '?') {
+					p[0]++;
+					sprintf(rs, "\r\n0,1");
+					isdn_tty_at_cout(rs, info);
+				} else {
+					par = isdn_getnum(p);
+					if ((par < 0) || (par > 1))
+						PARSE_ERROR1;
+					f->rel = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+					printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par);
+#endif
+				}
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	/* REV? - DCE Revision */
+	if (!strncmp(p[0], "REV?", 4)) {
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: FREV?\n");
+#endif
+		strcpy(rss, isdn_tty_fax_revision);
+		sprintf(rs, "\r\nRev: %s", isdn_getrev(rss));
+		isdn_tty_at_cout(rs, info);
+		return 0;
+	}
+
+	/* Phase C Transmit Data Block Size */
+	if (!strncmp(p[0], "TBC=", 4)) {	/* dummy, not used */
+		p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+		printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
+#endif
+		switch (*p[0]) {
+			case '0':
+				p[0]++;
+				break;
+			default:
+				PARSE_ERROR1;
+		}
+		return 0;
+	}
+	printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
+	PARSE_ERROR1;
+}
+
+int
+isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info)
+{
+	if (TTY_IS_FCLASS2(info))
+		return (isdn_tty_cmd_FCLASS2(p, info));
+	else if (TTY_IS_FCLASS1(info))
+		return (isdn_tty_cmd_FCLASS1(p, info));
+	PARSE_ERROR1;
+}
diff --git a/drivers/isdn/i4l/isdn_ttyfax.h b/drivers/isdn/i4l/isdn_ttyfax.h
new file mode 100644
index 0000000..757a890
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ttyfax.h
@@ -0,0 +1,18 @@
+/* $Id: isdn_ttyfax.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, tty_fax related functions (linklevel).
+ *
+ * Copyright 1999   by Armin Schindler (mac@melware.de)
+ * Copyright 1999   by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999   by Cytronics & Melware
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#define XON	0x11
+#define XOFF	0x13
+#define DC2	0x12
+
diff --git a/drivers/isdn/i4l/isdn_v110.c b/drivers/isdn/i4l/isdn_v110.c
new file mode 100644
index 0000000..f47f2b9
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_v110.c
@@ -0,0 +1,617 @@
+/* $Id: isdn_v110.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <linux/isdn.h>
+#include "isdn_v110.h"
+
+#undef ISDN_V110_DEBUG
+
+char *isdn_v110_revision = "$Revision: 1.1.2.2 $";
+
+#define V110_38400 255
+#define V110_19200  15
+#define V110_9600    3
+
+/* 
+ * The following data are precoded matrices, online and offline matrix 
+ * for 9600, 19200 und 38400, respectively
+ */
+static unsigned char V110_OnMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd};
+
+static unsigned char V110_OffMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7,
+ 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7};
+
+static unsigned char V110_OffMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_38400[] =
+{0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f};
+
+static unsigned char V110_OffMatrix_38400[] =
+{0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff};
+
+/* 
+ * FlipBits reorders sequences of keylen bits in one byte.
+ * E.g. source order 7654321 will be converted to 45670123 when keylen = 4,
+ * and to 67452301 when keylen = 2. This is necessary because ordering on
+ * the isdn line is the other way.
+ */
+static __inline unsigned char
+FlipBits(unsigned char c, int keylen)
+{
+	unsigned char b = c;
+	unsigned char bit = 128;
+	int i;
+	int j;
+	int hunks = (8 / keylen);
+
+	c = 0;
+	for (i = 0; i < hunks; i++) {
+		for (j = 0; j < keylen; j++) {
+			if (b & (bit >> j))
+				c |= bit >> (keylen - j - 1);
+		}
+		bit >>= keylen;
+	}
+	return c;
+}
+
+
+/* isdn_v110_open allocates and initializes private V.110 data
+ * structures and returns a pointer to these.
+ */
+static isdn_v110_stream *
+isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
+{
+	int i;
+	isdn_v110_stream *v;
+
+	if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_ATOMIC)) == NULL)
+		return NULL;
+	memset(v, 0, sizeof(isdn_v110_stream));
+	v->key = key;
+	v->nbits = 0;
+	for (i = 0; key & (1 << i); i++)
+		v->nbits++;
+
+	v->nbytes = 8 / v->nbits;
+	v->decodelen = 0;
+
+	switch (key) {
+		case V110_38400:
+			v->OnlineFrame = V110_OnMatrix_38400;
+			v->OfflineFrame = V110_OffMatrix_38400;
+			break;
+		case V110_19200:
+			v->OnlineFrame = V110_OnMatrix_19200;
+			v->OfflineFrame = V110_OffMatrix_19200;
+			break;
+		default:
+			v->OnlineFrame = V110_OnMatrix_9600;
+			v->OfflineFrame = V110_OffMatrix_9600;
+			break;
+	}
+	v->framelen = v->nbytes * 10;
+	v->SyncInit = 5;
+	v->introducer = 0;
+	v->dbit = 1;
+	v->b = 0;
+	v->skbres = hdrlen;
+	v->maxsize = maxsize - hdrlen;
+	if ((v->encodebuf = kmalloc(maxsize, GFP_ATOMIC)) == NULL) {
+		kfree(v);
+		return NULL;
+	}
+	return v;
+}
+
+/* isdn_v110_close frees private V.110 data structures */
+void
+isdn_v110_close(isdn_v110_stream * v)
+{
+	if (v == NULL)
+		return;
+#ifdef ISDN_V110_DEBUG
+	printk(KERN_DEBUG "v110 close\n");
+#endif
+	kfree(v->encodebuf);
+	kfree(v);
+}
+
+
+/* 
+ * ValidHeaderBytes return the number of valid bytes in v->decodebuf 
+ */
+static int
+ValidHeaderBytes(isdn_v110_stream * v)
+{
+	int i;
+	for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++)
+		if ((v->decodebuf[i] & v->key) != 0)
+			break;
+	return i;
+}
+
+/* 
+ * SyncHeader moves the decodebuf ptr to the next valid header 
+ */
+static void
+SyncHeader(isdn_v110_stream * v)
+{
+	unsigned char *rbuf = v->decodebuf;
+	int len = v->decodelen;
+
+	if (len == 0)
+		return;
+	for (rbuf++, len--; len > 0; len--, rbuf++)	/* such den SyncHeader in buf ! */
+		if ((*rbuf & v->key) == 0)	/* erstes byte gefunden ?       */
+			break;  /* jupp!                        */
+	if (len)
+		memcpy(v->decodebuf, rbuf, len);
+
+	v->decodelen = len;
+#ifdef ISDN_V110_DEBUG
+	printk(KERN_DEBUG "isdn_v110: Header resync\n");
+#endif
+}
+
+/* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where
+   len is the number of matrix-lines. len must be a multiple of 10, i.e.
+   only complete matices must be given.
+   From these, netto data is extracted and returned in buf. The return-value
+   is the bytecount of the decoded data.
+ */
+static int
+DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf)
+{
+	int line = 0;
+	int buflen = 0;
+	int mbit = 64;
+	int introducer = v->introducer;
+	int dbit = v->dbit;
+	unsigned char b = v->b;
+
+	while (line < len) {    /* Are we done with all lines of the matrix? */
+		if ((line % 10) == 0) {	/* the 0. line of the matrix is always 0 ! */
+			if (m[line] != 0x00) {	/* not 0 ? -> error! */
+#ifdef ISDN_V110_DEBUG
+				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n");
+				/* returning now is not the right thing, though :-( */
+#endif
+			} 
+			line++; /* next line of matrix */
+			continue;
+		} else if ((line % 10) == 5) {	/* in line 5 there's only e-bits ! */
+			if ((m[line] & 0x70) != 0x30) {	/* 011 has to be at the beginning! */
+#ifdef ISDN_V110_DEBUG
+				printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n");
+				/* returning now is not the right thing, though :-( */
+#endif
+			}
+			line++; /* next line */
+			continue;
+		} else if (!introducer) {	/* every byte starts with 10 (stopbit, startbit) */
+			introducer = (m[line] & mbit) ? 0 : 1;	/* current bit of the matrix */
+		      next_byte:
+			if (mbit > 2) {	/* was it the last bit in this line ? */
+				mbit >>= 1;	/* no -> take next */
+				continue;
+			}       /* otherwise start with leftmost bit in the next line */
+			mbit = 64;
+			line++;
+			continue;
+		} else {        /* otherwise we need to set a data bit */
+			if (m[line] & mbit)	/* was that bit set in the matrix ? */
+				b |= dbit;	/* yes -> set it in the data byte */
+			else
+				b &= dbit - 1;	/* no -> clear it in the data byte */
+			if (dbit < 128)	/* is that data byte done ? */
+				dbit <<= 1;	/* no, got the next bit */
+			else {  /* data byte is done */
+				buf[buflen++] = b;	/* copy byte into the output buffer */
+				introducer = b = 0;	/* init of the intro sequence and of the data byte */
+				dbit = 1;	/* next we look for the 0th bit */
+			}
+			goto next_byte;	/* look for next bit in the matrix */
+		}
+	}
+	v->introducer = introducer;
+	v->dbit = dbit;
+	v->b = b;
+	return buflen;          /* return number of bytes in the output buffer */
+}
+
+/* 
+ * DecodeStream receives V.110 coded data from the input stream. It recovers the 
+ * original frames.
+ * The input stream doesn't need to be framed
+ */
+struct sk_buff *
+isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+	int i;
+	int j;
+	int len;
+	unsigned char *v110_buf;
+	unsigned char *rbuf;
+
+	if (!skb) {
+		printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n");
+		return NULL;
+	}
+	rbuf = skb->data;
+	len = skb->len;
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	if (v->decodelen == 0)  /* cache empty?               */
+		for (; len > 0; len--, rbuf++)	/* scan for SyncHeader in buf */
+			if ((*rbuf & v->key) == 0)
+				break;	/* found first byte           */
+	if (len == 0) {
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	/* copy new data to decode-buffer */
+	memcpy(&(v->decodebuf[v->decodelen]), rbuf, len);
+	v->decodelen += len;
+      ReSync:
+	if (v->decodelen < v->nbytes) {	/* got a new header ? */
+		dev_kfree_skb(skb);
+		return NULL;    /* no, try later      */
+	}
+	if (ValidHeaderBytes(v) != v->nbytes) {	/* is that a valid header? */
+		SyncHeader(v);  /* no -> look for header */
+		goto ReSync;
+	}
+	len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes;
+	if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) {
+		printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n");
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+	for (i = 0; i < len; i++) {
+		v110_buf[i] = 0;
+		for (j = 0; j < v->nbytes; j++)
+			v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits));
+		v110_buf[i] = FlipBits(v110_buf[i], v->nbits);
+	}
+	v->decodelen = (v->decodelen % (10 * v->nbytes));
+	memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen);
+
+	skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data));
+	kfree(v110_buf);
+	if (skb->len)
+		return skb;
+	else {
+		kfree_skb(skb);
+		return NULL;
+	}
+}
+
+/* EncodeMatrix takes input data in buf, len is the bytecount.
+   Data is encoded into v110 frames in m. Return value is the number of
+   matrix-lines generated.
+ */
+static int
+EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen)
+{
+	int line = 0;
+	int i = 0;
+	int mbit = 128;
+	int dbit = 1;
+	int introducer = 3;
+	int ibit[] = {0, 1, 1};
+
+	while ((i < len) && (line < mlen)) {	/* while we still have input data */
+		switch (line % 10) {	/* in which line of the matrix are we? */
+			case 0:
+				m[line++] = 0x00;	/* line 0 is always 0 */
+				mbit = 128;	/* go on with the 7th bit */
+				break;
+			case 5:
+				m[line++] = 0xbf;	/* line 5 is always 10111111 */
+				mbit = 128;	/* go on with the 7th bit */
+				break;
+		}
+		if (line >= mlen) {
+			printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+			return line;
+		}
+	next_bit:
+		switch (mbit) { /* leftmost or rightmost bit ? */
+			case 1:
+				line++;	/* rightmost -> go to next line */
+				if (line >= mlen) {
+					printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+					return line;
+				}
+			case 128:
+				m[line] = 128;	/* leftmost -> set byte to 1000000 */
+				mbit = 64;	/* current bit in the matrix line */
+				continue;
+		}
+		if (introducer) {	/* set 110 sequence ? */
+			introducer--;	/* set on digit less */
+			m[line] |= ibit[introducer] ? mbit : 0;	/* set corresponding bit */
+			mbit >>= 1;	/* bit of matrix line  >> 1 */
+			goto next_bit;	/* and go on there */
+		}               /* else push data bits into the matrix! */
+		m[line] |= (buf[i] & dbit) ? mbit : 0;	/* set data bit in matrix */
+		if (dbit == 128) {	/* was it the last one? */
+			dbit = 1;	/* then go on with first bit of  */
+			i++;            /* next byte in input buffer */
+			if (i < len)	/* input buffer done ? */
+				introducer = 3;	/* no, write introducer 110 */
+			else {  /* input buffer done ! */
+				m[line] |= (mbit - 1) & 0xfe;	/* set remaining bits in line to 1 */
+				break;
+			}
+		} else          /* not the last data bit */
+			dbit <<= 1;	/* then go to next data bit */
+		mbit >>= 1;     /* go to next bit of matrix */
+		goto next_bit;
+
+	}
+	/* if necessary, generate remaining lines of the matrix... */
+	if ((line) && ((line + 10) < mlen))
+		switch (++line % 10) {
+			case 1:
+				m[line++] = 0xfe;
+			case 2:
+				m[line++] = 0xfe;
+			case 3:
+				m[line++] = 0xfe;
+			case 4:
+				m[line++] = 0xfe;
+			case 5:
+				m[line++] = 0xbf;
+			case 6:
+				m[line++] = 0xfe;
+			case 7:
+				m[line++] = 0xfe;
+			case 8:
+				m[line++] = 0xfe;
+			case 9:
+				m[line++] = 0xfe;
+		}
+	return line;            /* that's how many lines we have */
+}
+
+/*
+ * Build a sync frame.
+ */
+static struct sk_buff *
+isdn_v110_sync(isdn_v110_stream *v)
+{
+	struct sk_buff *skb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+		return NULL;
+	}
+	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+		skb_reserve(skb, v->skbres);
+		memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen);
+	}
+	return skb;
+}
+
+/*
+ * Build an idle frame.
+ */
+static struct sk_buff *
+isdn_v110_idle(isdn_v110_stream *v)
+{
+	struct sk_buff *skb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+		return NULL;
+	}
+	if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+		skb_reserve(skb, v->skbres);
+		memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen);
+	}
+	return skb;
+}
+
+struct sk_buff *
+isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+	int i;
+	int j;
+	int rlen;
+	int mlen;
+	int olen;
+	int size;
+	int sval1;
+	int sval2;
+	int nframes;
+	unsigned char *v110buf;
+	unsigned char *rbuf;
+	struct sk_buff *nskb;
+
+	if (v == NULL) {
+		/* invalid handle, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n");
+		return NULL;
+	}
+	if (!skb) {
+		/* invalid skb, no chance to proceed */
+		printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n");
+		return NULL;
+	}
+	rlen = skb->len;
+	nframes = (rlen + 3) / 4;
+	v110buf = v->encodebuf;
+	if ((nframes * 40) > v->maxsize) {
+		size = v->maxsize;
+		rlen = v->maxsize / 40;
+	} else
+		size = nframes * 40;
+	if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) {
+		printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n");
+		return NULL;
+	}
+	skb_reserve(nskb, v->skbres + sizeof(int));
+	if (skb->len == 0) {
+		memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen);
+		*((int *)skb_push(nskb, sizeof(int))) = 0;
+		return nskb;
+	}
+	mlen = EncodeMatrix(skb->data, rlen, v110buf, size);
+	/* now distribute 2 or 4 bits each to the output stream! */
+	rbuf = skb_put(nskb, size);
+	olen = 0;
+	sval1 = 8 - v->nbits;
+	sval2 = v->key << sval1;
+	for (i = 0; i < mlen; i++) {
+		v110buf[i] = FlipBits(v110buf[i], v->nbits);
+		for (j = 0; j < v->nbytes; j++) {
+			if (size--)
+				*rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1);
+			else {
+				printk(KERN_WARNING "isdn_v110_encode: buffers full!\n");
+				goto buffer_full;
+			}
+			olen++;
+		}
+	}
+buffer_full:
+	skb_trim(nskb, olen);
+	*((int *)skb_push(nskb, sizeof(int))) = rlen;
+	return nskb;
+}
+
+int
+isdn_v110_stat_callback(int idx, isdn_ctrl * c)
+{
+	isdn_v110_stream *v = NULL;
+	int i;
+	int ret;
+
+	if (idx < 0)
+		return 0;
+	switch (c->command) {
+		case ISDN_STAT_BSENT:
+                        /* Keep the send-queue of the driver filled
+			 * with frames:
+			 * If number of outstanding frames < 3,
+			 * send down an Idle-Frame (or an Sync-Frame, if
+			 * v->SyncInit != 0). 
+			 */
+			if (!(v = dev->v110[idx]))
+				return 0;
+			atomic_inc(&dev->v110use[idx]);
+			for (i=0; i * v->framelen < c->parm.length; i++) {
+				if (v->skbidle > 0) {
+					v->skbidle--;
+					ret = 1;
+				} else {
+					if (v->skbuser > 0)
+						v->skbuser--;
+					ret = 0;
+				}
+			}
+			for (i = v->skbuser + v->skbidle; i < 2; i++) {
+				struct sk_buff *skb;
+				if (v->SyncInit > 0)
+					skb = isdn_v110_sync(v);
+				else
+					skb = isdn_v110_idle(v);
+				if (skb) {
+					if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+						dev_kfree_skb(skb);
+						break;
+					} else {
+						if (v->SyncInit)
+							v->SyncInit--;
+						v->skbidle++;
+					}
+				} else
+					break;
+			}
+			atomic_dec(&dev->v110use[idx]);
+			return ret;
+		case ISDN_STAT_DHUP:
+		case ISDN_STAT_BHUP:
+			while (1) {
+				atomic_inc(&dev->v110use[idx]);
+				if (atomic_dec_and_test(&dev->v110use[idx])) {
+					isdn_v110_close(dev->v110[idx]);
+					dev->v110[idx] = NULL;
+					break;
+				}
+				mdelay(1);
+			}
+			break;
+		case ISDN_STAT_BCONN:
+			if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) {
+				int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen;
+				int maxsize = dev->drv[c->driver]->interface->maxbufsize;
+				atomic_inc(&dev->v110use[idx]);
+				switch (dev->v110emu[idx]) {
+					case ISDN_PROTO_L2_V11096:
+						dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize);
+						break;
+					case ISDN_PROTO_L2_V11019:
+						dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize);
+						break;
+					case ISDN_PROTO_L2_V11038:
+						dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize);
+						break;
+					default:;
+				}
+				if ((v = dev->v110[idx])) {
+					while (v->SyncInit) {
+						struct sk_buff *skb = isdn_v110_sync(v);
+						if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+							dev_kfree_skb(skb);
+							/* Unable to send, try later */
+							break;
+						}
+						v->SyncInit--;
+						v->skbidle++;
+					}
+				} else
+					printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx);
+				atomic_dec(&dev->v110use[idx]);
+			}
+			break;
+		default:
+			return 0;
+	}
+	return 0;
+}
diff --git a/drivers/isdn/i4l/isdn_v110.h b/drivers/isdn/i4l/isdn_v110.h
new file mode 100644
index 0000000..08f274b
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_v110.h
@@ -0,0 +1,29 @@
+/* $Id: isdn_v110.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _isdn_v110_h_
+#define _isdn_v110_h_
+
+/* 
+ * isdn_v110_encode will take raw data and encode it using V.110 
+ */
+extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *);
+
+/* 
+ * isdn_v110_decode receives V.110 coded data from the stream and rebuilds
+ * frames from them. The source stream doesn't need to be framed.
+ */
+extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *);
+
+extern int isdn_v110_stat_callback(int, isdn_ctrl *);
+extern void isdn_v110_close(isdn_v110_stream * v);
+
+#endif
diff --git a/drivers/isdn/i4l/isdn_x25iface.c b/drivers/isdn/i4l/isdn_x25iface.c
new file mode 100644
index 0000000..4ab7600
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_x25iface.c
@@ -0,0 +1,328 @@
+/* $Id: isdn_x25iface.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, X.25 related functions
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * stuff needed to support the Linux X.25 PLP code on top of devices that
+ * can provide a lab_b service using the concap_proto mechanism.
+ * This module supports a network interface wich provides lapb_sematics
+ * -- as defined in Documentation/networking/x25-iface.txt -- to
+ * the upper layer and assumes that the lower layer provides a reliable
+ * data link service by means of the concap_device_ops callbacks.
+ *
+ * Only protocol specific stuff goes here. Device specific stuff
+ * goes to another -- device related -- concap_proto support source file.
+ *
+ */
+
+/* #include <linux/isdn.h> */
+#include <linux/netdevice.h>
+#include <linux/concap.h>
+#include <linux/wanrouter.h>
+#include <net/x25device.h>
+#include "isdn_x25iface.h"
+
+/* for debugging messages not to cause an oops when device pointer is NULL*/
+#define MY_DEVNAME(dev)  ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" )
+
+
+typedef struct isdn_x25iface_proto_data {
+	int magic;
+	enum wan_states state;
+	/* Private stuff, not to be accessed via proto_data. We provide the
+	   other storage for the concap_proto instance here as well,
+	   enabling us to allocate both with just one kmalloc(): */ 
+	struct concap_proto priv;
+} ix25_pdata_t;
+
+
+
+/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */
+void isdn_x25iface_proto_del( struct concap_proto * );
+int isdn_x25iface_proto_close( struct concap_proto * );
+int isdn_x25iface_proto_restart( struct concap_proto *,
+				 struct net_device *,
+				 struct concap_device_ops *);
+int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * );
+int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * );
+int isdn_x25iface_connect_ind( struct concap_proto * );
+int isdn_x25iface_disconn_ind( struct concap_proto * );
+
+
+static struct concap_proto_ops ix25_pops = {
+	&isdn_x25iface_proto_new,
+	&isdn_x25iface_proto_del,
+	&isdn_x25iface_proto_restart,
+	&isdn_x25iface_proto_close,
+	&isdn_x25iface_xmit,
+	&isdn_x25iface_receive,
+	&isdn_x25iface_connect_ind,
+	&isdn_x25iface_disconn_ind
+};
+
+/* error message helper function */
+static void illegal_state_warn( unsigned state, unsigned char firstbyte) 
+{
+	printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in"
+		"current state %d\n",firstbyte, state );
+}
+
+/* check protocol data field for consistency */
+static int pdata_is_bad( ix25_pdata_t * pda ){
+
+	if( pda  &&  pda -> magic == ISDN_X25IFACE_MAGIC ) return 0;
+	printk( KERN_WARNING
+		"isdn_x25iface_xxx: illegal pointer to proto data\n" );
+	return 1;
+}
+
+/* create a new x25 interface protocol instance
+ */
+struct concap_proto * isdn_x25iface_proto_new(void)
+{
+	ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL);
+	IX25DEBUG("isdn_x25iface_proto_new\n");
+	if( tmp ){
+		tmp -> magic = ISDN_X25IFACE_MAGIC;
+		tmp -> state = WAN_UNCONFIGURED;
+		/* private data space used to hold the concap_proto data.
+		   Only to be accessed via the returned pointer */
+		spin_lock_init(&tmp->priv.lock);
+		tmp -> priv.dops       = NULL;
+		tmp -> priv.net_dev    = NULL;
+		tmp -> priv.pops       = &ix25_pops;
+		tmp -> priv.flags      = 0;
+		tmp -> priv.proto_data = tmp;
+		return( &(tmp -> priv) );
+	}
+	return NULL;
+};
+
+/* close the x25iface encapsulation protocol 
+ */
+int isdn_x25iface_proto_close(struct concap_proto *cprot){
+
+	ix25_pdata_t *tmp;
+        int ret = 0;
+	ulong flags;
+
+	if( ! cprot ){
+		printk( KERN_ERR "isdn_x25iface_proto_close: "
+			"invalid concap_proto pointer\n" );
+		return -1;
+	}
+	IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) );
+	spin_lock_irqsave(&cprot->lock, flags);
+	cprot -> dops    = NULL;
+	cprot -> net_dev = NULL;
+	tmp = cprot -> proto_data;
+	if( pdata_is_bad( tmp ) ){
+		ret = -1;
+	} else {
+		tmp -> state = WAN_UNCONFIGURED;
+	}
+	spin_unlock_irqrestore(&cprot->lock, flags);
+	return ret;
+}
+
+/* Delete the x25iface encapsulation protocol instance
+ */
+void isdn_x25iface_proto_del(struct concap_proto *cprot){
+
+	ix25_pdata_t * tmp;
+ 
+	IX25DEBUG( "isdn_x25iface_proto_del \n" );
+	if( ! cprot ){
+		printk( KERN_ERR "isdn_x25iface_proto_del: "
+			"concap_proto pointer is NULL\n" );
+		return;
+	}
+	tmp = cprot -> proto_data;
+	if( tmp == NULL ){ 
+		printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent "
+			"proto_data pointer (maybe already deleted?)\n"); 
+		return;
+	}
+	/* close if the protocol is still open */
+	if( cprot -> dops ) isdn_x25iface_proto_close(cprot);
+	/* freeing the storage should be sufficient now. But some additional
+	   settings might help to catch wild pointer bugs */
+	tmp -> magic = 0;
+	cprot -> proto_data = NULL;
+
+	kfree( tmp );
+	return;
+}
+
+/* (re-)initialize the data structures for x25iface encapsulation
+ */
+int isdn_x25iface_proto_restart(struct concap_proto *cprot,
+				struct net_device *ndev, 
+				struct concap_device_ops *dops)
+{
+	ix25_pdata_t * pda = cprot -> proto_data ;
+	ulong flags;
+
+	IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) );
+
+	if ( pdata_is_bad( pda ) ) return -1;
+
+	if( !( dops  && dops -> data_req && dops -> connect_req 
+	       && dops -> disconn_req )  ){
+		printk( KERN_WARNING "isdn_x25iface_restart: required dops"
+			" missing\n" );
+		isdn_x25iface_proto_close(cprot);
+		return -1;
+	}
+	spin_lock_irqsave(&cprot->lock, flags);
+	cprot -> net_dev = ndev;
+	cprot -> pops = &ix25_pops;
+	cprot -> dops = dops;
+	pda -> state = WAN_DISCONNECTED;
+	spin_unlock_irqrestore(&cprot->lock, flags);
+	return 0;
+}
+
+/* deliver a dl_data frame received from i4l HL driver to the network layer 
+ */
+int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb)
+{
+  	IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) );
+	if ( ( (ix25_pdata_t*) (cprot->proto_data) ) 
+	     -> state == WAN_CONNECTED ){
+		if( skb_push(skb, 1)){
+			skb -> data[0]=0x00;
+			skb->protocol = x25_type_trans(skb, cprot->net_dev);
+			netif_rx(skb);
+			return 0;
+		}
+	}
+	printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) );
+	dev_kfree_skb(skb);
+	return -1;
+}
+
+/* a connection set up is indicated by lower layer 
+ */
+int isdn_x25iface_connect_ind(struct concap_proto *cprot)
+{
+	struct sk_buff * skb = dev_alloc_skb(1);
+	enum wan_states *state_p 
+	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+	IX25DEBUG( "isdn_x25iface_connect_ind %s \n"
+		   , MY_DEVNAME(cprot->net_dev) );
+	if( *state_p == WAN_UNCONFIGURED ){ 
+		printk(KERN_WARNING 
+		       "isdn_x25iface_connect_ind while unconfigured %s\n"
+		       , MY_DEVNAME(cprot->net_dev) );
+		return -1;
+	}
+	*state_p = WAN_CONNECTED;
+	if( skb ){
+		*( skb_put(skb, 1) ) = 0x01;
+		skb->protocol = x25_type_trans(skb, cprot->net_dev);
+		netif_rx(skb);
+		return 0;
+	} else {
+		printk(KERN_WARNING "isdn_x25iface_connect_ind: "
+		       " out of memory -- disconnecting\n");
+		cprot -> dops -> disconn_req(cprot);
+		return -1;
+	}
+}
+	
+/* a disconnect is indicated by lower layer 
+ */
+int isdn_x25iface_disconn_ind(struct concap_proto *cprot)
+{
+	struct sk_buff *skb;
+	enum wan_states *state_p 
+	  = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+	IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) );
+	if( *state_p == WAN_UNCONFIGURED ){ 
+		printk(KERN_WARNING 
+		       "isdn_x25iface_disconn_ind while unconfigured\n");
+		return -1;
+	}
+	if(! cprot -> net_dev) return -1;
+	*state_p = WAN_DISCONNECTED;
+	skb = dev_alloc_skb(1);
+	if( skb ){
+		*( skb_put(skb, 1) ) = 0x02;
+		skb->protocol = x25_type_trans(skb, cprot->net_dev);
+		netif_rx(skb);
+		return 0;
+	} else {
+		printk(KERN_WARNING "isdn_x25iface_disconn_ind:"
+		       " out of memory\n");
+		return -1;
+	}
+}
+
+/* process a frame handed over to us from linux network layer. First byte
+   semantics as defined in Documentation/networking/x25-iface.txt
+   */
+int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb)
+{
+	unsigned char firstbyte = skb->data[0];
+	enum wan_states *state = &((ix25_pdata_t*)cprot->proto_data)->state;
+	int ret = 0;
+	IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state );
+	switch ( firstbyte ){
+	case 0x00: /* dl_data request */
+		if( *state == WAN_CONNECTED ){
+			skb_pull(skb, 1);
+			cprot -> net_dev -> trans_start = jiffies;
+			ret = ( cprot -> dops -> data_req(cprot, skb) );
+			/* prepare for future retransmissions */
+			if( ret ) skb_push(skb,1);
+			return ret;
+		}
+		illegal_state_warn( *state, firstbyte ); 
+		break;
+	case 0x01: /* dl_connect request */
+		if( *state == WAN_DISCONNECTED ){
+			*state = WAN_CONNECTING;
+		        ret = cprot -> dops -> connect_req(cprot);
+			if(ret){
+				/* reset state and notify upper layer about
+				 * immidiatly failed attempts */
+				isdn_x25iface_disconn_ind(cprot);
+			}
+		} else {
+			illegal_state_warn( *state, firstbyte );
+		}
+		break;
+	case 0x02: /* dl_disconnect request */
+		switch ( *state ){
+		case WAN_DISCONNECTED: 
+			/* Should not happen. However, give upper layer a
+			   chance to recover from inconstistency  but don't
+			   trust the lower layer sending the disconn_confirm
+			   when already disconnected */
+			printk(KERN_WARNING "isdn_x25iface_xmit: disconnect "
+			       " requested while disconnected\n" );
+			isdn_x25iface_disconn_ind(cprot);
+			break; /* prevent infinite loops */
+		case WAN_CONNECTING:
+		case WAN_CONNECTED:
+			*state = WAN_DISCONNECTED;
+			cprot -> dops -> disconn_req(cprot);
+			break;
+		default:
+			illegal_state_warn( *state, firstbyte );
+		}
+		break;
+	case 0x03: /* changing lapb parameters requested */
+		printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb"
+		       " options not yet supported\n");
+		break;
+	default:
+		printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal"
+		       " first byte %x ignored:\n", firstbyte);
+	}
+	dev_kfree_skb(skb);
+	return 0;
+}
diff --git a/drivers/isdn/i4l/isdn_x25iface.h b/drivers/isdn/i4l/isdn_x25iface.h
new file mode 100644
index 0000000..41a3d49
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_x25iface.h
@@ -0,0 +1,39 @@
+/* $Id: isdn_x25iface.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, x.25 related functions
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _LINUX_ISDN_X25IFACE_H
+#define _LINUX_ISDN_X25IFACE_H
+
+#define ISDN_X25IFACE_MAGIC 0x1e75a2b9
+/* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */
+#ifdef DEBUG_ISDN_X25
+#   define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)
+#else
+#   define IX25DEBUG(fmt,args...)
+#endif
+
+#include <linux/skbuff.h>
+#include <linux/wanrouter.h>
+#include <linux/isdn.h>
+#include <linux/concap.h>
+
+extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt;
+extern struct concap_proto     * isdn_x25iface_proto_new(void);
+
+
+
+#endif
+
+
+
+
+
+
+
+
diff --git a/drivers/isdn/icn/Kconfig b/drivers/isdn/icn/Kconfig
new file mode 100644
index 0000000..fcb99f5
--- /dev/null
+++ b/drivers/isdn/icn/Kconfig
@@ -0,0 +1,16 @@
+#
+# Config.in for ICN ISDN driver
+#
+config ISDN_DRV_ICN
+	tristate "ICN 2B and 4B support"
+	depends on ISDN_I4L && ISA
+	help
+	  This enables support for two kinds of ISDN-cards made by a German
+	  company called ICN.  2B is the standard version for a single ISDN
+	  line with two B-channels, 4B supports two ISDN lines.  For running
+	  this card, additional firmware is necessary, which has to be
+	  downloaded into the card using a utility which is distributed
+	  separately.  See <file:Documentation/isdn/README> and
+	  <file:Documentation/isdn/README.icn> for more
+	  information.
+
diff --git a/drivers/isdn/icn/Makefile b/drivers/isdn/icn/Makefile
new file mode 100644
index 0000000..d9b476f
--- /dev/null
+++ b/drivers/isdn/icn/Makefile
@@ -0,0 +1,5 @@
+# Makefile for the icn ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_ICN)	+= icn.o
diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c
new file mode 100644
index 0000000..9fc0c1e
--- /dev/null
+++ b/drivers/isdn/icn/icn.c
@@ -0,0 +1,1691 @@
+/* $Id: icn.c,v 1.65.6.8 2001/09/23 22:24:55 kai Exp $
+ *
+ * ISDN low-level module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "icn.h"
+#include <linux/module.h>
+#include <linux/init.h>
+
+static int portbase = ICN_BASEADDR;
+static unsigned long membase = ICN_MEMADDR;
+static char *icn_id = "\0";
+static char *icn_id2 = "\0";
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for ICN active ISDN card");
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_LICENSE("GPL");
+module_param(portbase, int, 0);
+MODULE_PARM_DESC(portbase, "Port address of first card");
+module_param(membase, ulong, 0);
+MODULE_PARM_DESC(membase, "Shared memory address of all cards");
+module_param(icn_id, charp, 0);
+MODULE_PARM_DESC(icn_id, "ID-String of first card");
+module_param(icn_id2, charp, 0);
+MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)");
+
+/*
+ * Verbose bootcode- and protocol-downloading.
+ */
+#undef BOOT_DEBUG
+
+/*
+ * Verbose Shmem-Mapping.
+ */
+#undef MAP_DEBUG
+
+static char
+*revision = "$Revision: 1.65.6.8 $";
+
+static int icn_addcard(int, char *, char *);
+
+/*
+ * Free send-queue completely.
+ * Parameter:
+ *   card   = pointer to card struct
+ *   channel = channel number
+ */
+static void
+icn_free_queue(icn_card * card, int channel)
+{
+	struct sk_buff_head *queue = &card->spqueue[channel];
+	struct sk_buff *skb;
+
+	skb_queue_purge(queue);
+	card->xlen[channel] = 0;
+	card->sndcount[channel] = 0;
+	if ((skb = card->xskb[channel])) {
+		card->xskb[channel] = NULL;
+		dev_kfree_skb(skb);
+	}
+}
+
+/* Put a value into a shift-register, highest bit first.
+ * Parameters:
+ *            port     = port for output (bit 0 is significant)
+ *            val      = value to be output
+ *            firstbit = Bit-Number of highest bit
+ *            bitcount = Number of bits to output
+ */
+static inline void
+icn_shiftout(unsigned short port,
+	     unsigned long val,
+	     int firstbit,
+	     int bitcount)
+{
+
+	register u_char s;
+	register u_char c;
+
+	for (s = firstbit, c = bitcount; c > 0; s--, c--)
+		OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
+}
+
+/*
+ * disable a cards shared memory
+ */
+static inline void
+icn_disable_ram(icn_card * card)
+{
+	OUTB_P(0, ICN_MAPRAM);
+}
+
+/*
+ * enable a cards shared memory
+ */
+static inline void
+icn_enable_ram(icn_card * card)
+{
+	OUTB_P(0xff, ICN_MAPRAM);
+}
+
+/*
+ * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12)
+ *
+ * must called with holding the devlock
+ */
+static inline void
+icn_map_channel(icn_card * card, int channel)
+{
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_map_channel %d %d\n", dev.channel, channel);
+#endif
+	if ((channel == dev.channel) && (card == dev.mcard))
+		return;
+	if (dev.mcard)
+		icn_disable_ram(dev.mcard);
+	icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4);	/* Select Bank          */
+	icn_enable_ram(card);
+	dev.mcard = card;
+	dev.channel = channel;
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_map_channel done\n");
+#endif
+}
+
+/*
+ * Lock a cards channel.
+ * Return 0 if requested card/channel is unmapped (failure).
+ * Return 1 on success.
+ *
+ * must called with holding the devlock
+ */
+static inline int
+icn_lock_channel(icn_card * card, int channel)
+{
+	register int retval;
+
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
+#endif
+	if ((dev.channel == channel) && (card == dev.mcard)) {
+		dev.chanlock++;
+		retval = 1;
+#ifdef MAP_DEBUG
+		printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
+#endif
+	} else {
+		retval = 0;
+#ifdef MAP_DEBUG
+		printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev.channel);
+#endif
+	}
+	return retval;
+}
+
+/*
+ * Release current card/channel lock
+ *
+ * must called with holding the devlock
+ */
+static inline void
+__icn_release_channel(void)
+{
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "icn_release_channel l=%d\n", dev.chanlock);
+#endif
+	if (dev.chanlock > 0)
+		dev.chanlock--;
+}
+
+/*
+ * Release current card/channel lock
+ */
+static inline void
+icn_release_channel(void)
+{
+	ulong flags;
+
+	spin_lock_irqsave(&dev.devlock, flags);
+	__icn_release_channel();
+	spin_unlock_irqrestore(&dev.devlock, flags);
+}
+
+/*
+ * Try to map and lock a cards channel.
+ * Return 1 on success, 0 on failure.
+ */
+static inline int
+icn_trymaplock_channel(icn_card * card, int channel)
+{
+	ulong flags;
+
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,
+	       dev.chanlock);
+#endif
+	spin_lock_irqsave(&dev.devlock, flags);
+	if ((!dev.chanlock) ||
+	    ((dev.channel == channel) && (dev.mcard == card))) {
+		dev.chanlock++;
+		icn_map_channel(card, channel);
+		spin_unlock_irqrestore(&dev.devlock, flags);
+#ifdef MAP_DEBUG
+		printk(KERN_DEBUG "trymaplock %d OK\n", channel);
+#endif
+		return 1;
+	}
+	spin_unlock_irqrestore(&dev.devlock, flags);
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
+#endif
+	return 0;
+}
+
+/*
+ * Release current card/channel lock,
+ * then map same or other channel without locking.
+ */
+static inline void
+icn_maprelease_channel(icn_card * card, int channel)
+{
+	ulong flags;
+
+#ifdef MAP_DEBUG
+	printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);
+#endif
+	spin_lock_irqsave(&dev.devlock, flags);
+	if (dev.chanlock > 0)
+		dev.chanlock--;
+	if (!dev.chanlock)
+		icn_map_channel(card, channel);
+	spin_unlock_irqrestore(&dev.devlock, flags);
+}
+
+/* Get Data from the B-Channel, assemble fragmented packets and put them
+ * into receive-queue. Wake up any B-Channel-reading processes.
+ * This routine is called via timer-callback from icn_pollbchan().
+ */
+
+static void
+icn_pollbchan_receive(int channel, icn_card * card)
+{
+	int mch = channel + ((card->secondhalf) ? 2 : 0);
+	int eflag;
+	int cnt;
+	struct sk_buff *skb;
+
+	if (icn_trymaplock_channel(card, mch)) {
+		while (rbavl) {
+			cnt = readb(&rbuf_l);
+			if ((card->rcvidx[channel] + cnt) > 4000) {
+				printk(KERN_WARNING
+				       "icn: (%s) bogus packet on ch%d, dropping.\n",
+				       CID,
+				       channel + 1);
+				card->rcvidx[channel] = 0;
+				eflag = 0;
+			} else {
+				memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],
+					      &rbuf_d, cnt);
+				card->rcvidx[channel] += cnt;
+				eflag = readb(&rbuf_f);
+			}
+			rbnext;
+			icn_maprelease_channel(card, mch & 2);
+			if (!eflag) {
+				if ((cnt = card->rcvidx[channel])) {
+					if (!(skb = dev_alloc_skb(cnt))) {
+						printk(KERN_WARNING "icn: receive out of memory\n");
+						break;
+					}
+					memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt);
+					card->rcvidx[channel] = 0;
+					card->interface.rcvcallb_skb(card->myid, channel, skb);
+				}
+			}
+			if (!icn_trymaplock_channel(card, mch))
+				break;
+		}
+		icn_maprelease_channel(card, mch & 2);
+	}
+}
+
+/* Send data-packet to B-Channel, split it up into fragments of
+ * ICN_FRAGSIZE length. If last fragment is sent out, signal
+ * success to upper layers via statcallb with ISDN_STAT_BSENT argument.
+ * This routine is called via timer-callback from icn_pollbchan() or
+ * directly from icn_sendbuf().
+ */
+
+static void
+icn_pollbchan_send(int channel, icn_card * card)
+{
+	int mch = channel + ((card->secondhalf) ? 2 : 0);
+	int cnt;
+	unsigned long flags;
+	struct sk_buff *skb;
+	isdn_ctrl cmd;
+
+	if (!(card->sndcount[channel] || card->xskb[channel] ||
+	      skb_queue_len(&card->spqueue[channel])))
+		return;
+	if (icn_trymaplock_channel(card, mch)) {
+		while (sbfree && 
+		       (card->sndcount[channel] ||
+			skb_queue_len(&card->spqueue[channel]) ||
+			card->xskb[channel])) {
+			spin_lock_irqsave(&card->lock, flags);
+			if (card->xmit_lock[channel]) {
+				spin_unlock_irqrestore(&card->lock, flags);
+				break;
+			}
+			card->xmit_lock[channel]++;
+			spin_unlock_irqrestore(&card->lock, flags);
+			skb = card->xskb[channel];
+			if (!skb) {
+				skb = skb_dequeue(&card->spqueue[channel]);
+				if (skb) {
+					/* Pop ACK-flag off skb.
+					 * Store length to xlen.
+					 */
+					if (*(skb_pull(skb,1)))
+						card->xlen[channel] = skb->len;
+					else
+						card->xlen[channel] = 0;
+				}
+			}
+			if (!skb)
+				break;
+			if (skb->len > ICN_FRAGSIZE) {
+				writeb(0xff, &sbuf_f);
+				cnt = ICN_FRAGSIZE;
+			} else {
+				writeb(0x0, &sbuf_f);
+				cnt = skb->len;
+			}
+			writeb(cnt, &sbuf_l);
+			memcpy_toio(&sbuf_d, skb->data, cnt);
+			skb_pull(skb, cnt);
+			sbnext; /* switch to next buffer        */
+			icn_maprelease_channel(card, mch & 2);
+			spin_lock_irqsave(&card->lock, flags);
+			card->sndcount[channel] -= cnt;
+			if (!skb->len) {
+				if (card->xskb[channel])
+					card->xskb[channel] = NULL;
+				card->xmit_lock[channel] = 0;
+				spin_unlock_irqrestore(&card->lock, flags);
+				dev_kfree_skb(skb);
+				if (card->xlen[channel]) {
+					cmd.command = ISDN_STAT_BSENT;
+					cmd.driver = card->myid;
+					cmd.arg = channel;
+					cmd.parm.length = card->xlen[channel];
+					card->interface.statcallb(&cmd);
+				}
+			} else {
+				card->xskb[channel] = skb;
+				card->xmit_lock[channel] = 0;
+				spin_unlock_irqrestore(&card->lock, flags);
+			}
+			if (!icn_trymaplock_channel(card, mch))
+				break;
+		}
+		icn_maprelease_channel(card, mch & 2);
+	}
+}
+
+/* Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ */
+
+static void
+icn_pollbchan(unsigned long data)
+{
+	icn_card *card = (icn_card *) data;
+	unsigned long flags;
+
+	if (card->flags & ICN_FLAGS_B1ACTIVE) {
+		icn_pollbchan_receive(0, card);
+		icn_pollbchan_send(0, card);
+	}
+	if (card->flags & ICN_FLAGS_B2ACTIVE) {
+		icn_pollbchan_receive(1, card);
+		icn_pollbchan_send(1, card);
+	}
+	if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+		/* schedule b-channel polling again */
+		spin_lock_irqsave(&card->lock, flags);
+		mod_timer(&card->rb_timer, jiffies+ICN_TIMER_BCREAD);
+		card->flags |= ICN_FLAGS_RBTIMER;
+		spin_unlock_irqrestore(&card->lock, flags);
+	} else
+		card->flags &= ~ICN_FLAGS_RBTIMER;
+}
+
+typedef struct icn_stat {
+	char *statstr;
+	int command;
+	int action;
+} icn_stat;
+/* *INDENT-OFF* */
+static icn_stat icn_stat_table[] =
+{
+	{"BCON_",          ISDN_STAT_BCONN, 1},	/* B-Channel connected        */
+	{"BDIS_",          ISDN_STAT_BHUP,  2},	/* B-Channel disconnected     */
+	/*
+	** add d-channel connect and disconnect support to link-level
+	*/
+	{"DCON_",          ISDN_STAT_DCONN, 10},	/* D-Channel connected        */
+	{"DDIS_",          ISDN_STAT_DHUP,  11},	/* D-Channel disconnected     */
+	{"DCAL_I",         ISDN_STAT_ICALL, 3},	/* Incoming call dialup-line  */
+	{"DSCA_I",         ISDN_STAT_ICALL, 3},	/* Incoming call 1TR6-SPV     */
+	{"FCALL",          ISDN_STAT_ICALL, 4},	/* Leased line connection up  */
+	{"CIF",            ISDN_STAT_CINF,  5},	/* Charge-info, 1TR6-type     */
+	{"AOC",            ISDN_STAT_CINF,  6},	/* Charge-info, DSS1-type     */
+	{"CAU",            ISDN_STAT_CAUSE, 7},	/* Cause code                 */
+	{"TEI OK",         ISDN_STAT_RUN,   0},	/* Card connected to wallplug */
+	{"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8},	/* Layer-1 activation failed  */
+	{"E_L2: DATA LIN", ISDN_STAT_BHUP,  8},	/* Layer-2 data link lost     */
+	{"E_L1: ACTIVATION FAILED",
+					   ISDN_STAT_BHUP,  8},	/* Layer-1 activation failed  */
+	{NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Check Statusqueue-Pointer from isdn-cards.
+ * If there are new status-replies from the interface, check
+ * them against B-Channel-connects/disconnects and set flags accordingly.
+ * Wake-Up any processes, who are reading the status-device.
+ * If there are B-Channels open, initiate a timer-callback to
+ * icn_pollbchan().
+ * This routine is called periodically via timer.
+ */
+
+static void
+icn_parse_status(u_char * status, int channel, icn_card * card)
+{
+	icn_stat *s = icn_stat_table;
+	int action = -1;
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	while (s->statstr) {
+		if (!strncmp(status, s->statstr, strlen(s->statstr))) {
+			cmd.command = s->command;
+			action = s->action;
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return;
+	cmd.driver = card->myid;
+	cmd.arg = channel;
+	switch (action) {
+		case 11:
+			spin_lock_irqsave(&card->lock, flags);
+			icn_free_queue(card,channel);
+			card->rcvidx[channel] = 0;
+
+			if (card->flags & 
+			    ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) {
+				
+				isdn_ctrl ncmd;
+				
+				card->flags &= ~((channel)?
+						 ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
+				
+				memset(&ncmd, 0, sizeof(ncmd));
+				
+				ncmd.driver = card->myid;
+				ncmd.arg = channel;
+				ncmd.command = ISDN_STAT_BHUP;
+				spin_unlock_irqrestore(&card->lock, flags);
+				card->interface.statcallb(&cmd);
+			} else
+				spin_unlock_irqrestore(&card->lock, flags);
+			break;
+		case 1:
+			spin_lock_irqsave(&card->lock, flags);
+			icn_free_queue(card,channel);
+			card->flags |= (channel) ?
+			    ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;
+			spin_unlock_irqrestore(&card->lock, flags);
+			break;
+		case 2:
+			spin_lock_irqsave(&card->lock, flags);
+			card->flags &= ~((channel) ?
+				ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE);
+			icn_free_queue(card, channel);
+			card->rcvidx[channel] = 0;
+			spin_unlock_irqrestore(&card->lock, flags);
+			break;
+		case 3:
+			{
+				char *t = status + 6;
+				char *s = strchr(t, ',');
+
+				*s++ = '\0';
+				strlcpy(cmd.parm.setup.phone, t,
+					sizeof(cmd.parm.setup.phone));
+				s = strchr(t = s, ',');
+				*s++ = '\0';
+				if (!strlen(t))
+					cmd.parm.setup.si1 = 0;
+				else
+					cmd.parm.setup.si1 =
+					    simple_strtoul(t, NULL, 10);
+				s = strchr(t = s, ',');
+				*s++ = '\0';
+				if (!strlen(t))
+					cmd.parm.setup.si2 = 0;
+				else
+					cmd.parm.setup.si2 =
+					    simple_strtoul(t, NULL, 10);
+				strlcpy(cmd.parm.setup.eazmsn, s,
+					sizeof(cmd.parm.setup.eazmsn));
+			}
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 4:
+			sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);
+			sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);
+			cmd.parm.setup.si1 = 7;
+			cmd.parm.setup.si2 = 0;
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 5:
+			strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num));
+			break;
+		case 6:
+			snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d",
+			     (int) simple_strtoul(status + 7, NULL, 16));
+			break;
+		case 7:
+			status += 3;
+			if (strlen(status) == 4)
+				snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%s%c%c",
+				     status + 2, *status, *(status + 1));
+			else
+				strlcpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num));
+			break;
+		case 8:
+			spin_lock_irqsave(&card->lock, flags);
+			card->flags &= ~ICN_FLAGS_B1ACTIVE;
+			icn_free_queue(card, 0);
+			card->rcvidx[0] = 0;
+			spin_unlock_irqrestore(&card->lock, flags);
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_BHUP;
+			spin_lock_irqsave(&card->lock, flags);
+			card->flags &= ~ICN_FLAGS_B2ACTIVE;
+			icn_free_queue(card, 1);
+			card->rcvidx[1] = 0;
+			spin_unlock_irqrestore(&card->lock, flags);
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			break;
+	}
+	card->interface.statcallb(&cmd);
+	return;
+}
+
+static void
+icn_putmsg(icn_card * card, unsigned char c)
+{
+	ulong flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	*card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+	if (card->msg_buf_write == card->msg_buf_read) {
+		if (++card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	if (card->msg_buf_write > card->msg_buf_end)
+		card->msg_buf_write = card->msg_buf;
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void
+icn_polldchan(unsigned long data)
+{
+	icn_card *card = (icn_card *) data;
+	int mch = card->secondhalf ? 2 : 0;
+	int avail = 0;
+	int left;
+	u_char c;
+	int ch;
+	unsigned long flags;
+	int i;
+	u_char *p;
+	isdn_ctrl cmd;
+
+	if (icn_trymaplock_channel(card, mch)) {
+		avail = msg_avail;
+		for (left = avail, i = readb(&msg_o); left > 0; i++, left--) {
+			c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]);
+			icn_putmsg(card, c);
+			if (c == 0xff) {
+				card->imsg[card->iptr] = 0;
+				card->iptr = 0;
+				if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+				    card->imsg[1] <= '2' && card->imsg[2] == ';') {
+					ch = (card->imsg[1] - '0') - 1;
+					p = &card->imsg[3];
+					icn_parse_status(p, ch, card);
+				} else {
+					p = card->imsg;
+					if (!strncmp(p, "DRV1.", 5)) {
+						u_char vstr[10];
+						u_char *q = vstr;
+
+						printk(KERN_INFO "icn: (%s) %s\n", CID, p);
+						if (!strncmp(p + 7, "TC", 2)) {
+							card->ptype = ISDN_PTYPE_1TR6;
+							card->interface.features |= ISDN_FEATURE_P_1TR6;
+							printk(KERN_INFO
+							       "icn: (%s) 1TR6-Protocol loaded and running\n", CID);
+						}
+						if (!strncmp(p + 7, "EC", 2)) {
+							card->ptype = ISDN_PTYPE_EURO;
+							card->interface.features |= ISDN_FEATURE_P_EURO;
+							printk(KERN_INFO
+							       "icn: (%s) Euro-Protocol loaded and running\n", CID);
+						}
+						p = strstr(card->imsg, "BRV") + 3;
+						while (*p) {
+							if (*p >= '0' && *p <= '9')
+								*q++ = *p;
+							p++;
+						}
+						*q = '\0';
+						strcat(vstr, "000");
+						vstr[3] = '\0';
+						card->fw_rev = (int) simple_strtoul(vstr, NULL, 10);
+						continue;
+
+					}
+				}
+			} else {
+				card->imsg[card->iptr] = c;
+				if (card->iptr < 59)
+					card->iptr++;
+			}
+		}
+		writeb((readb(&msg_o) + avail) & 0xff, &msg_o);
+		icn_release_channel();
+	}
+	if (avail) {
+		cmd.command = ISDN_STAT_STAVAIL;
+		cmd.driver = card->myid;
+		cmd.arg = avail;
+		card->interface.statcallb(&cmd);
+	}
+	spin_lock_irqsave(&card->lock, flags);
+	if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
+		if (!(card->flags & ICN_FLAGS_RBTIMER)) {
+			/* schedule b-channel polling */
+			card->flags |= ICN_FLAGS_RBTIMER;
+			del_timer(&card->rb_timer);
+			card->rb_timer.function = icn_pollbchan;
+			card->rb_timer.data = (unsigned long) card;
+			card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+			add_timer(&card->rb_timer);
+		}
+	/* schedule again */
+	mod_timer(&card->st_timer, jiffies+ICN_TIMER_DCREAD);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* Append a packet to the transmit buffer-queue.
+ * Parameters:
+ *   channel = Number of B-channel
+ *   skb     = pointer to sk_buff
+ *   card    = pointer to card-struct
+ * Return:
+ *   Number of bytes transferred, -E??? on error
+ */
+
+static int
+icn_sendbuf(int channel, int ack, struct sk_buff *skb, icn_card * card)
+{
+	int len = skb->len;
+	unsigned long flags;
+	struct sk_buff *nskb;
+
+	if (len > 4000) {
+		printk(KERN_WARNING
+		       "icn: Send packet too large\n");
+		return -EINVAL;
+	}
+	if (len) {
+		if (!(card->flags & (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE))
+			return 0;
+		if (card->sndcount[channel] > ICN_MAX_SQUEUE)
+			return 0;
+		#warning TODO test headroom or use skb->nb to flag ACK
+		nskb = skb_clone(skb, GFP_ATOMIC);
+		if (nskb) {
+			/* Push ACK flag as one
+			 * byte in front of data.
+			 */
+			*(skb_push(nskb, 1)) = ack?1:0;
+			skb_queue_tail(&card->spqueue[channel], nskb);
+			dev_kfree_skb(skb);
+		} else
+			len = 0;
+		spin_lock_irqsave(&card->lock, flags);
+		card->sndcount[channel] += len;
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+	return len;
+}
+
+/*
+ * Check card's status after starting the bootstrap loader.
+ * On entry, the card's shared memory has already to be mapped.
+ * Return:
+ *   0 on success (Boot loader ready)
+ *   -EIO on failure (timeout)
+ */
+static int
+icn_check_loader(int cardnumber)
+{
+	int timer = 0;
+
+	while (1) {
+#ifdef BOOT_DEBUG
+		printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
+#endif
+		if (readb(&dev.shmem->data_control.scns) ||
+		    readb(&dev.shmem->data_control.scnr)) {
+			if (timer++ > 5) {
+				printk(KERN_WARNING
+				       "icn: Boot-Loader %d timed out.\n",
+				       cardnumber);
+				icn_release_channel();
+				return -EIO;
+			}
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
+#endif
+			msleep_interruptible(ICN_BOOT_TIMEOUT1);
+		} else {
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
+#endif
+			icn_release_channel();
+			return 0;
+		}
+	}
+}
+
+/* Load the boot-code into the interface-card's memory and start it.
+ * Always called from user-process.
+ *
+ * Parameters:
+ *            buffer = pointer to packet
+ * Return:
+ *        0 if successfully loaded
+ */
+
+#ifdef BOOT_DEBUG
+#define SLEEP(sec) { \
+int slsec = sec; \
+  printk(KERN_DEBUG "SLEEP(%d)\n",slsec); \
+  while (slsec) { \
+    msleep_interruptible(1000); \
+    slsec--; \
+  } \
+}
+#else
+#define SLEEP(sec)
+#endif
+
+static int
+icn_loadboot(u_char __user * buffer, icn_card * card)
+{
+	int ret;
+	u_char *codebuf;
+	unsigned long flags;
+
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
+#endif
+	if (!(codebuf = kmalloc(ICN_CODE_STAGE1, GFP_KERNEL))) {
+		printk(KERN_WARNING "icn: Could not allocate code buffer\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+	if (copy_from_user(codebuf, buffer, ICN_CODE_STAGE1)) {
+		ret = -EFAULT;
+		goto out_kfree;
+	}
+	if (!card->rvalid) {
+		if (!request_region(card->port, ICN_PORTLEN, card->regname)) {
+			printk(KERN_WARNING
+			       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+			       CID,
+			       card->port,
+			       card->port + ICN_PORTLEN);
+			ret = -EBUSY;
+			goto out_kfree;
+		}
+		card->rvalid = 1;
+		if (card->doubleS0)
+			card->other->rvalid = 1;
+	}
+	if (!dev.mvalid) {
+		if (!request_mem_region(dev.memaddr, 0x4000, "icn-isdn (all cards)")) {
+			printk(KERN_WARNING
+			       "icn: memory at 0x%08lx in use.\n", dev.memaddr);
+			ret = -EBUSY;
+			goto out_kfree;
+		}
+		dev.shmem = ioremap(dev.memaddr, 0x4000);
+		dev.mvalid = 1;
+	}
+	OUTB_P(0, ICN_RUN);     /* Reset Controller */
+	OUTB_P(0, ICN_MAPRAM);  /* Disable RAM      */
+	icn_shiftout(ICN_CFG, 0x0f, 3, 4);	/* Windowsize= 16k  */
+	icn_shiftout(ICN_CFG, dev.memaddr, 23, 10);	/* Set RAM-Addr.    */
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "shmem=%08lx\n", dev.memaddr);
+#endif
+	SLEEP(1);
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+	spin_lock_irqsave(&dev.devlock, flags);
+	icn_map_channel(card, 0);	/* Select Bank 0    */
+	icn_lock_channel(card, 0);	/* Lock Bank 0      */
+	spin_unlock_irqrestore(&dev.devlock, flags);
+	SLEEP(1);
+	memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);	/* Copy code        */
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "Bootloader transferred\n");
+#endif
+	if (card->doubleS0) {
+		SLEEP(1);
+#ifdef BOOT_DEBUG
+		printk(KERN_DEBUG "Map Bank 8\n");
+#endif
+		spin_lock_irqsave(&dev.devlock, flags);
+		__icn_release_channel();
+		icn_map_channel(card, 2);	/* Select Bank 8   */
+		icn_lock_channel(card, 2);	/* Lock Bank 8     */
+		spin_unlock_irqrestore(&dev.devlock, flags);
+		SLEEP(1);
+		memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1);	/* Copy code        */
+#ifdef BOOT_DEBUG
+		printk(KERN_DEBUG "Bootloader transferred\n");
+#endif
+	}
+	SLEEP(1);
+	OUTB_P(0xff, ICN_RUN);  /* Start Boot-Code */
+	if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1))) {
+		goto out_kfree;
+	}
+	if (!card->doubleS0) {
+		ret = 0;
+		goto out_kfree;
+	}
+	/* reached only, if we have a Double-S0-Card */
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+	spin_lock_irqsave(&dev.devlock, flags);
+	icn_map_channel(card, 0);	/* Select Bank 0   */
+	icn_lock_channel(card, 0);	/* Lock Bank 0     */
+	spin_unlock_irqrestore(&dev.devlock, flags);
+	SLEEP(1);
+	ret = (icn_check_loader(1));
+
+ out_kfree:
+	kfree(codebuf);
+ out:
+	return ret;
+}
+
+static int
+icn_loadproto(u_char __user * buffer, icn_card * card)
+{
+	register u_char __user *p = buffer;
+	u_char codebuf[256];
+	uint left = ICN_CODE_STAGE2;
+	uint cnt;
+	int timer;
+	unsigned long flags;
+
+#ifdef BOOT_DEBUG
+	printk(KERN_DEBUG "icn_loadproto called\n");
+#endif
+	if (!access_ok(VERIFY_READ, buffer, ICN_CODE_STAGE2))
+		return -EFAULT;
+	timer = 0;
+	spin_lock_irqsave(&dev.devlock, flags);
+	if (card->secondhalf) {
+		icn_map_channel(card, 2);
+		icn_lock_channel(card, 2);
+	} else {
+		icn_map_channel(card, 0);
+		icn_lock_channel(card, 0);
+	}
+	spin_unlock_irqrestore(&dev.devlock, flags);
+	while (left) {
+		if (sbfree) {   /* If there is a free buffer...  */
+			cnt = left;
+			if (cnt > 256)
+				cnt = 256;
+			if (copy_from_user(codebuf, p, cnt)) {
+				icn_maprelease_channel(card, 0);
+				return -EFAULT;
+			}
+			memcpy_toio(&sbuf_l, codebuf, cnt);	/* copy data                     */
+			sbnext; /* switch to next buffer         */
+			p += cnt;
+			left -= cnt;
+			timer = 0;
+		} else {
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "boot 2 !sbfree\n");
+#endif
+			if (timer++ > 5) {
+				icn_maprelease_channel(card, 0);
+				return -EIO;
+			}
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(10);
+		}
+	}
+	writeb(0x20, &sbuf_n);
+	timer = 0;
+	while (1) {
+		if (readb(&cmd_o) || readb(&cmd_i)) {
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Proto?\n");
+#endif
+			if (timer++ > 5) {
+				printk(KERN_WARNING
+				       "icn: (%s) Protocol timed out.\n",
+				       CID);
+#ifdef BOOT_DEBUG
+				printk(KERN_DEBUG "Proto TO!\n");
+#endif
+				icn_maprelease_channel(card, 0);
+				return -EIO;
+			}
+#ifdef BOOT_DEBUG
+			printk(KERN_DEBUG "Proto TO?\n");
+#endif
+			msleep_interruptible(ICN_BOOT_TIMEOUT1);
+		} else {
+			if ((card->secondhalf) || (!card->doubleS0)) {
+#ifdef BOOT_DEBUG
+				printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
+				       card->secondhalf);
+#endif
+				spin_lock_irqsave(&card->lock, flags);
+				init_timer(&card->st_timer);
+				card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+				card->st_timer.function = icn_polldchan;
+				card->st_timer.data = (unsigned long) card;
+				add_timer(&card->st_timer);
+				card->flags |= ICN_FLAGS_RUNNING;
+				if (card->doubleS0) {
+					init_timer(&card->other->st_timer);
+					card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+					card->other->st_timer.function = icn_polldchan;
+					card->other->st_timer.data = (unsigned long) card->other;
+					add_timer(&card->other->st_timer);
+					card->other->flags |= ICN_FLAGS_RUNNING;
+				}
+				spin_unlock_irqrestore(&card->lock, flags);
+			}
+			icn_maprelease_channel(card, 0);
+			return 0;
+		}
+	}
+}
+
+/* Read the Status-replies from the Interface */
+static int
+icn_readstatus(u_char __user *buf, int len, icn_card * card)
+{
+	int count;
+	u_char __user *p;
+
+	for (p = buf, count = 0; count < len; p++, count++) {
+		if (card->msg_buf_read == card->msg_buf_write)
+			return count;
+		put_user(*card->msg_buf_read++, p);
+		if (card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	return count;
+}
+
+/* Put command-strings into the command-queue of the Interface */
+static int
+icn_writecmd(const u_char * buf, int len, int user, icn_card * card)
+{
+	int mch = card->secondhalf ? 2 : 0;
+	int pp;
+	int i;
+	int count;
+	int xcount;
+	int ocount;
+	int loop;
+	unsigned long flags;
+	int lastmap_channel;
+	struct icn_card *lastmap_card;
+	u_char *p;
+	isdn_ctrl cmd;
+	u_char msg[0x100];
+
+	ocount = 1;
+	xcount = loop = 0;
+	while (len) {
+		count = cmd_free;
+		if (count > len)
+			count = len;
+		if (user) {
+			if (copy_from_user(msg, buf, count))
+				return -EFAULT;
+		} else
+			memcpy(msg, buf, count);
+
+		spin_lock_irqsave(&dev.devlock, flags);
+		lastmap_card = dev.mcard;
+		lastmap_channel = dev.channel;
+		icn_map_channel(card, mch);
+
+		icn_putmsg(card, '>');
+		for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp
+		     ++) {
+			writeb((*p == '\n') ? 0xff : *p,
+			   &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]);
+			len--;
+			xcount++;
+			icn_putmsg(card, *p);
+			if ((*p == '\n') && (i > 1)) {
+				icn_putmsg(card, '>');
+				ocount++;
+			}
+			ocount++;
+		}
+		writeb((readb(&cmd_i) + count) & 0xff, &cmd_i);
+		if (lastmap_card)
+			icn_map_channel(lastmap_card, lastmap_channel);
+		spin_unlock_irqrestore(&dev.devlock, flags);
+		if (len) {
+			mdelay(1);
+			if (loop++ > 20)
+				break;
+		} else
+			break;
+	}
+	if (len && (!user))
+		printk(KERN_WARNING "icn: writemsg incomplete!\n");
+	cmd.command = ISDN_STAT_STAVAIL;
+	cmd.driver = card->myid;
+	cmd.arg = ocount;
+	card->interface.statcallb(&cmd);
+	return xcount;
+}
+
+/*
+ * Delete card's pending timers, send STOP to linklevel
+ */
+static void
+icn_stopcard(icn_card * card)
+{
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	spin_lock_irqsave(&card->lock, flags);
+	if (card->flags & ICN_FLAGS_RUNNING) {
+		card->flags &= ~ICN_FLAGS_RUNNING;
+		del_timer(&card->st_timer);
+		del_timer(&card->rb_timer);
+		spin_unlock_irqrestore(&card->lock, flags);
+		cmd.command = ISDN_STAT_STOP;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		if (card->doubleS0)
+			icn_stopcard(card->other);
+	} else
+		spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void
+icn_stopallcards(void)
+{
+	icn_card *p = cards;
+
+	while (p) {
+		icn_stopcard(p);
+		p = p->next;
+	}
+}
+
+/*
+ * Unmap all cards, because some of them may be mapped accidetly during
+ * autoprobing of some network drivers (SMC-driver?)
+ */
+static void
+icn_disable_cards(void)
+{
+	icn_card *card = cards;
+
+	while (card) {
+		if (!request_region(card->port, ICN_PORTLEN, "icn-isdn")) {
+			printk(KERN_WARNING
+			       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+			       CID,
+			       card->port,
+			       card->port + ICN_PORTLEN);
+		} else {
+			OUTB_P(0, ICN_RUN);	/* Reset Controller     */
+			OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+			release_region(card->port, ICN_PORTLEN);
+		}
+		card = card->next;
+	}
+}
+
+static int
+icn_command(isdn_ctrl * c, icn_card * card)
+{
+	ulong a;
+	ulong flags;
+	int i;
+	char cbuf[60];
+	isdn_ctrl cmd;
+	icn_cdef cdef;
+	char __user *arg;
+
+	switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			arg = (char __user *)a;
+			switch (c->arg) {
+				case ICN_IOCTL_SETMMIO:
+					if (dev.memaddr != (a & 0x0ffc000)) {
+						if (!request_mem_region(a & 0x0ffc000, 0x4000, "icn-isdn (all cards)")) {
+							printk(KERN_WARNING
+							       "icn: memory at 0x%08lx in use.\n",
+							       a & 0x0ffc000);
+							return -EINVAL;
+						}
+						release_mem_region(a & 0x0ffc000, 0x4000);
+						icn_stopallcards();
+						spin_lock_irqsave(&card->lock, flags);
+						if (dev.mvalid) {
+							iounmap(dev.shmem);
+							release_mem_region(dev.memaddr, 0x4000);
+						}
+						dev.mvalid = 0;
+						dev.memaddr = a & 0x0ffc000;
+						spin_unlock_irqrestore(&card->lock, flags);
+						printk(KERN_INFO
+						       "icn: (%s) mmio set to 0x%08lx\n",
+						       CID,
+						       dev.memaddr);
+					}
+					break;
+				case ICN_IOCTL_GETMMIO:
+					return (long) dev.memaddr;
+				case ICN_IOCTL_SETPORT:
+					if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
+					    || a == 0x340 || a == 0x350 || a == 0x360 ||
+					    a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
+					    || a == 0x348 || a == 0x358 || a == 0x368) {
+						if (card->port != (unsigned short) a) {
+							if (!request_region((unsigned short) a, ICN_PORTLEN, "icn-isdn")) {
+								printk(KERN_WARNING
+								       "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+								       CID, (int) a, (int) a + ICN_PORTLEN);
+								return -EINVAL;
+							}
+							release_region((unsigned short) a, ICN_PORTLEN);
+							icn_stopcard(card);
+							spin_lock_irqsave(&card->lock, flags);
+							if (card->rvalid)
+								release_region(card->port, ICN_PORTLEN);
+							card->port = (unsigned short) a;
+							card->rvalid = 0;
+							if (card->doubleS0) {
+								card->other->port = (unsigned short) a;
+								card->other->rvalid = 0;
+							}
+							spin_unlock_irqrestore(&card->lock, flags);
+							printk(KERN_INFO
+							       "icn: (%s) port set to 0x%03x\n",
+							CID, card->port);
+						}
+					} else
+						return -EINVAL;
+					break;
+				case ICN_IOCTL_GETPORT:
+					return (int) card->port;
+				case ICN_IOCTL_GETDOUBLE:
+					return (int) card->doubleS0;
+				case ICN_IOCTL_DEBUGVAR:
+					if (copy_to_user(arg,
+							 &card,
+							 sizeof(ulong)))
+						return -EFAULT;
+					a += sizeof(ulong);
+					{
+						ulong l = (ulong) & dev;
+						if (copy_to_user(arg,
+								 &l,
+								 sizeof(ulong)))
+							return -EFAULT;
+					}
+					return 0;
+				case ICN_IOCTL_LOADBOOT:
+					if (dev.firstload) {
+						icn_disable_cards();
+						dev.firstload = 0;
+					}
+					icn_stopcard(card);
+					return (icn_loadboot(arg, card));
+				case ICN_IOCTL_LOADPROTO:
+					icn_stopcard(card);
+					if ((i = (icn_loadproto(arg, card))))
+						return i;
+					if (card->doubleS0)
+						i = icn_loadproto(arg + ICN_CODE_STAGE2, card->other);
+					return i;
+					break;
+				case ICN_IOCTL_ADDCARD:
+					if (!dev.firstload)
+						return -EBUSY;
+					if (copy_from_user(&cdef,
+							   arg,
+							   sizeof(cdef)))
+						return -EFAULT;
+					return (icn_addcard(cdef.port, cdef.id1, cdef.id2));
+					break;
+				case ICN_IOCTL_LEASEDCFG:
+					if (a) {
+						if (!card->leased) {
+							card->leased = 1;
+							while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+								msleep_interruptible(ICN_BOOT_TIMEOUT1);
+							}
+							msleep_interruptible(ICN_BOOT_TIMEOUT1);
+							sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n",
+								(a & 1)?'1':'C', (a & 2)?'2':'C');
+							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "icn: (%s) Leased-line mode enabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					} else {
+						if (card->leased) {
+							card->leased = 0;
+							sprintf(cbuf, "00;FV2OFF\n");
+							i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "icn: (%s) Leased-line mode disabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					}
+					return 0;
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if ((c->arg & 255) < ICN_BCH) {
+				char *p;
+				char dial[50];
+				char dcode[4];
+
+				a = c->arg;
+				p = c->parm.setup.phone;
+				if (*p == 's' || *p == 'S') {
+					/* Dial for SPV */
+					p++;
+					strcpy(dcode, "SCA");
+				} else
+					/* Normal Dial */
+					strcpy(dcode, "CAL");
+				strcpy(dial, p);
+				sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1),
+					dcode, dial, c->parm.setup.si1,
+				c->parm.setup.si2, c->parm.setup.eazmsn);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->fw_rev >= 300) {
+					switch (card->l2_proto[a - 1]) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BX75\n", (int) a);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BTRA\n", (int) a);
+							break;
+					}
+					i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->fw_rev >= 300)
+					switch (card->l2_proto[a - 1]) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+							break;
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+							break;
+				} else
+					sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_HANGUP:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_SETEAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->ptype == ISDN_PTYPE_EURO) {
+					sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+						c->parm.num[0] ? "N" : "ALL", c->parm.num);
+				} else
+					sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+						c->parm.num[0] ? (char *)(c->parm.num) : "0123456789");
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_CLREAZ:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if (c->arg < ICN_BCH) {
+				a = c->arg + 1;
+				if (card->ptype == ISDN_PTYPE_EURO)
+					sprintf(cbuf, "%02d;MSNC\n", (int) a);
+				else
+					sprintf(cbuf, "%02d;EAZC\n", (int) a);
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_SETL2:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			if ((c->arg & 255) < ICN_BCH) {
+				a = c->arg;
+				switch (a >> 8) {
+					case ISDN_PROTO_L2_X75I:
+						sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+						break;
+					case ISDN_PROTO_L2_HDLC:
+						sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+						break;
+					default:
+						return -EINVAL;
+				}
+				i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+				card->l2_proto[a & 255] = (a >> 8);
+			}
+			break;
+		case ISDN_CMD_SETL3:
+			if (!card->flags & ICN_FLAGS_RUNNING)
+				return -ENODEV;
+			return 0;
+		default:
+			return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline icn_card *
+icn_findcard(int driverid)
+{
+	icn_card *p = cards;
+
+	while (p) {
+		if (p->myid == driverid)
+			return p;
+		p = p->next;
+	}
+	return (icn_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+	icn_card *card = icn_findcard(c->driver);
+
+	if (card)
+		return (icn_command(c, card));
+	printk(KERN_ERR
+	       "icn: if_command %d called with invalid driverId %d!\n",
+	       c->command, c->driver);
+	return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char __user *buf, int len, int id, int channel)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_writecmd(buf, len, 1, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_writecmd called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_readstatus(u_char __user *buf, int len, int id, int channel)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_readstatus(buf, len, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_readstatus called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+	icn_card *card = icn_findcard(id);
+
+	if (card) {
+		if (!card->flags & ICN_FLAGS_RUNNING)
+			return -ENODEV;
+		return (icn_sendbuf(channel, ack, skb, card));
+	}
+	printk(KERN_ERR
+	       "icn: if_sendbuf called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list and register it at linklevel.
+ */
+static icn_card *
+icn_initcard(int port, char *id)
+{
+	icn_card *card;
+	int i;
+
+	if (!(card = (icn_card *) kmalloc(sizeof(icn_card), GFP_KERNEL))) {
+		printk(KERN_WARNING
+		       "icn: (%s) Could not allocate card-struct.\n", id);
+		return (icn_card *) 0;
+	}
+	memset((char *) card, 0, sizeof(icn_card));
+	spin_lock_init(&card->lock);
+	card->port = port;
+	card->interface.owner = THIS_MODULE;
+	card->interface.hl_hdrlen = 1;
+	card->interface.channels = ICN_BCH;
+	card->interface.maxbufsize = 4000;
+	card->interface.command = if_command;
+	card->interface.writebuf_skb = if_sendbuf;
+	card->interface.writecmd = if_writecmd;
+	card->interface.readstat = if_readstatus;
+	card->interface.features = ISDN_FEATURE_L2_X75I |
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L3_TRANS |
+	    ISDN_FEATURE_P_UNKNOWN;
+	card->ptype = ISDN_PTYPE_UNKNOWN;
+	strlcpy(card->interface.id, id, sizeof(card->interface.id));
+	card->msg_buf_write = card->msg_buf;
+	card->msg_buf_read = card->msg_buf;
+	card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+	for (i = 0; i < ICN_BCH; i++) {
+		card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+		skb_queue_head_init(&card->spqueue[i]);
+	}
+	card->next = cards;
+	cards = card;
+	if (!register_isdn(&card->interface)) {
+		cards = cards->next;
+		printk(KERN_WARNING
+		       "icn: Unable to register %s\n", id);
+		kfree(card);
+		return (icn_card *) 0;
+	}
+	card->myid = card->interface.channels;
+	sprintf(card->regname, "icn-isdn (%s)", card->interface.id);
+	return card;
+}
+
+static int
+icn_addcard(int port, char *id1, char *id2)
+{
+	icn_card *card;
+	icn_card *card2;
+
+	if (!(card = icn_initcard(port, id1))) {
+		return -EIO;
+	}
+	if (!strlen(id2)) {
+		printk(KERN_INFO
+		       "icn: (%s) ICN-2B, port 0x%x added\n",
+		       card->interface.id, port);
+		return 0;
+	}
+	if (!(card2 = icn_initcard(port, id2))) {
+		printk(KERN_INFO
+		       "icn: (%s) half ICN-4B, port 0x%x added\n",
+		       card2->interface.id, port);
+		return 0;
+	}
+	card->doubleS0 = 1;
+	card->secondhalf = 0;
+	card->other = card2;
+	card2->doubleS0 = 1;
+	card2->secondhalf = 1;
+	card2->other = card;
+	printk(KERN_INFO
+	       "icn: (%s and %s) ICN-4B, port 0x%x added\n",
+	       card->interface.id, card2->interface.id, port);
+	return 0;
+}
+
+#ifndef MODULE
+static int __init
+icn_setup(char *line)
+{
+	char *p, *str;
+	int	ints[3];
+	static char sid[20];
+	static char sid2[20];
+
+	str = get_options(line, 2, ints);
+	if (ints[0])
+		portbase = ints[1];
+	if (ints[0] > 1)
+		membase = (unsigned long)ints[2];
+	if (str && *str) {
+		strcpy(sid, str);
+		icn_id = sid;
+		if ((p = strchr(sid, ','))) {
+			*p++ = 0;
+			strcpy(sid2, p);
+			icn_id2 = sid2;
+		}
+	}
+	return(1);
+}
+__setup("icn=", icn_setup);
+#endif /* MODULE */
+
+static int __init icn_init(void)
+{
+	char *p;
+	char rev[10];
+
+	memset(&dev, 0, sizeof(icn_dev));
+	dev.memaddr = (membase & 0x0ffc000);
+	dev.channel = -1;
+	dev.mcard = NULL;
+	dev.firstload = 1;
+	spin_lock_init(&dev.devlock);
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+	printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
+	       dev.memaddr);
+	return (icn_addcard(portbase, icn_id, icn_id2));
+}
+
+static void __exit icn_exit(void)
+{
+	isdn_ctrl cmd;
+	icn_card *card = cards;
+	icn_card *last;
+	int i;
+	unsigned long flags;
+
+	icn_stopallcards();
+	while (card) {
+		cmd.command = ISDN_STAT_UNLOAD;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		spin_lock_irqsave(&card->lock, flags);
+		if (card->rvalid) {
+			OUTB_P(0, ICN_RUN);	/* Reset Controller     */
+			OUTB_P(0, ICN_MAPRAM);	/* Disable RAM          */
+			if (card->secondhalf || (!card->doubleS0)) {
+				release_region(card->port, ICN_PORTLEN);
+				card->rvalid = 0;
+			}
+			for (i = 0; i < ICN_BCH; i++)
+				icn_free_queue(card, i);
+		}
+		card = card->next;
+		spin_unlock_irqrestore(&card->lock, flags);
+	}
+	card = cards;
+	cards = NULL;
+	while (card) {
+		last = card;
+		card = card->next;
+		kfree(last);
+	}
+	if (dev.mvalid) {
+		iounmap(dev.shmem);
+		release_mem_region(dev.memaddr, 0x4000);
+	}
+	printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
+}
+
+module_init(icn_init);
+module_exit(icn_exit);
diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h
new file mode 100644
index 0000000..9028cc3
--- /dev/null
+++ b/drivers/isdn/icn/icn.h
@@ -0,0 +1,254 @@
+/* $Id: icn.h,v 1.30.6.5 2001/09/23 22:24:55 kai Exp $
+ *
+ * ISDN lowlevel-module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef icn_h
+#define icn_h
+
+#define ICN_IOCTL_SETMMIO   0
+#define ICN_IOCTL_GETMMIO   1
+#define ICN_IOCTL_SETPORT   2
+#define ICN_IOCTL_GETPORT   3
+#define ICN_IOCTL_LOADBOOT  4
+#define ICN_IOCTL_LOADPROTO 5
+#define ICN_IOCTL_LEASEDCFG 6
+#define ICN_IOCTL_GETDOUBLE 7
+#define ICN_IOCTL_DEBUGVAR  8
+#define ICN_IOCTL_ADDCARD   9
+
+/* Struct for adding new cards */
+typedef struct icn_cdef {
+	int port;
+	char id1[10];
+	char id2[10];
+} icn_cdef;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/isdnif.h>
+
+#endif                          /* __KERNEL__ */
+
+/* some useful macros for debugging */
+#ifdef ICN_DEBUG_PORT
+#define OUTB_P(v,p) {printk(KERN_DEBUG "icn: outb_p(0x%02x,0x%03x)\n",v,p); outb_p(v,p);}
+#else
+#define OUTB_P outb
+#endif
+
+/* Defaults for Port-Address and shared-memory */
+#define ICN_BASEADDR 0x320
+#define ICN_PORTLEN (0x04)
+#define ICN_MEMADDR 0x0d0000
+
+#define ICN_FLAGS_B1ACTIVE 1    /* B-Channel-1 is open                     */
+#define ICN_FLAGS_B2ACTIVE 2    /* B-Channel-2 is open                     */
+#define ICN_FLAGS_RUNNING  4    /* Cards driver activated                  */
+#define ICN_FLAGS_RBTIMER  8    /* cyclic scheduling of B-Channel-poll     */
+
+#define ICN_BOOT_TIMEOUT1  1000 /* Delay for Boot-download (msecs)         */
+
+#define ICN_TIMER_BCREAD (HZ/100)	/* B-Channel poll-cycle                    */
+#define ICN_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle                    */
+
+#define ICN_CODE_STAGE1 4096    /* Size of bootcode                        */
+#define ICN_CODE_STAGE2 65536   /* Size of protocol-code                   */
+
+#define ICN_MAX_SQUEUE 8000     /* Max. outstanding send-data (2* hw-buf.) */
+#define ICN_FRAGSIZE (250)      /* Max. size of send-fragments             */
+#define ICN_BCH 2               /* Number of supported channels per card   */
+
+/* type-definitions for accessing the mmap-io-areas */
+
+#define SHM_DCTL_OFFSET (0)     /* Offset to data-controlstructures in shm */
+#define SHM_CCTL_OFFSET (0x1d2) /* Offset to comm-controlstructures in shm */
+#define SHM_CBUF_OFFSET (0x200) /* Offset to comm-buffers in shm           */
+#define SHM_DBUF_OFFSET (0x2000)	/* Offset to data-buffers in shm           */
+
+/*
+ * Layout of card's data buffers
+ */
+typedef struct {
+	unsigned char length;   /* Bytecount of fragment (max 250)    */
+	unsigned char endflag;  /* 0=last frag., 0xff=frag. continued */
+	unsigned char data[ICN_FRAGSIZE];	/* The data                           */
+	/* Fill to 256 bytes */
+	char unused[0x100 - ICN_FRAGSIZE - 2];
+} frag_buf;
+
+/*
+ * Layout of card's shared memory
+ */
+typedef union {
+	struct {
+		unsigned char scns;	/* Index to free SendFrag.             */
+		unsigned char scnr;	/* Index to active SendFrag   READONLY */
+		unsigned char ecns;	/* Index to free RcvFrag.     READONLY */
+		unsigned char ecnr;	/* Index to valid RcvFrag              */
+		char unused[6];
+		unsigned short fuell1;	/* Internal Buf Bytecount              */
+	} data_control;
+	struct {
+		char unused[SHM_CCTL_OFFSET];
+		unsigned char iopc_i;	/* Read-Ptr Status-Queue      READONLY */
+		unsigned char iopc_o;	/* Write-Ptr Status-Queue              */
+		unsigned char pcio_i;	/* Write-Ptr Command-Queue             */
+		unsigned char pcio_o;	/* Read-Ptr Command Queue     READONLY */
+	} comm_control;
+	struct {
+		char unused[SHM_CBUF_OFFSET];
+		unsigned char pcio_buf[0x100];	/* Ring-Buffer Command-Queue           */
+		unsigned char iopc_buf[0x100];	/* Ring-Buffer Status-Queue            */
+	} comm_buffers;
+	struct {
+		char unused[SHM_DBUF_OFFSET];
+		frag_buf receive_buf[0x10];
+		frag_buf send_buf[0x10];
+	} data_buffers;
+} icn_shmem;
+
+/*
+ * Per card driver data
+ */
+typedef struct icn_card {
+	struct icn_card *next;  /* Pointer to next device struct    */
+	struct icn_card *other; /* Pointer to other card for ICN4B  */
+	unsigned short port;    /* Base-port-address                */
+	int myid;               /* Driver-Nr. assigned by linklevel */
+	int rvalid;             /* IO-portregion has been requested */
+	int leased;             /* Flag: This Adapter is connected  */
+	                        /*       to a leased line           */
+	unsigned short flags;   /* Statusflags                      */
+	int doubleS0;           /* Flag: ICN4B                      */
+	int secondhalf;         /* Flag: Second half of a doubleS0  */
+	int fw_rev;             /* Firmware revision loaded         */
+	int ptype;              /* Protocol type (1TR6 or Euro)     */
+	struct timer_list st_timer;   /* Timer for Status-Polls     */
+	struct timer_list rb_timer;   /* Timer for B-Channel-Polls  */
+	u_char rcvbuf[ICN_BCH][4096]; /* B-Channel-Receive-Buffers  */
+	int rcvidx[ICN_BCH];    /* Index for above buffers          */
+	int l2_proto[ICN_BCH];  /* Current layer-2-protocol         */
+	isdn_if interface;      /* Interface to upper layer         */
+	int iptr;               /* Index to imsg-buffer             */
+	char imsg[60];          /* Internal buf for status-parsing  */
+	char msg_buf[2048];     /* Buffer for status-messages       */
+	char *msg_buf_write;    /* Writepointer for statusbuffer    */
+	char *msg_buf_read;     /* Readpointer for statusbuffer     */
+	char *msg_buf_end;      /* Pointer to end of statusbuffer   */
+	int sndcount[ICN_BCH];  /* Byte-counters for B-Ch.-send     */
+	int xlen[ICN_BCH];      /* Byte-counters/Flags for sent-ACK */
+	struct sk_buff *xskb[ICN_BCH]; /* Current transmitted skb   */
+	struct sk_buff_head spqueue[ICN_BCH];  /* Sendqueue         */
+	char regname[35];       /* Name used for request_region     */
+	u_char xmit_lock[ICN_BCH]; /* Semaphore for pollbchan_send()*/
+	spinlock_t lock;        /* protect critical operations      */
+} icn_card;
+
+/*
+ * Main driver data
+ */
+typedef struct icn_dev {
+	spinlock_t devlock;     /* spinlock to protect this struct  */
+	unsigned long memaddr;	/* Address of memory mapped buffers */
+	icn_shmem __iomem *shmem;       /* Pointer to memory-mapped-buffers */
+	int mvalid;             /* IO-shmem has been requested      */
+	int channel;            /* Currently mapped channel         */
+	struct icn_card *mcard; /* Currently mapped card            */
+	int chanlock;           /* Semaphore for channel-mapping    */
+	int firstload;          /* Flag: firmware never loaded      */
+} icn_dev;
+
+typedef icn_dev *icn_devptr;
+
+#ifdef __KERNEL__
+
+static icn_card *cards = (icn_card *) 0;
+static u_char chan2bank[] =
+{0, 4, 8, 12};                  /* for icn_map_channel() */
+
+static icn_dev dev;
+
+#endif                          /* __KERNEL__ */
+
+/* Utility-Macros */
+
+/* Macros for accessing ports */
+#define ICN_CFG    (card->port)
+#define ICN_MAPRAM (card->port+1)
+#define ICN_RUN    (card->port+2)
+#define ICN_BANK   (card->port+3)
+
+/* Return true, if there is a free transmit-buffer */
+#define sbfree (((readb(&dev.shmem->data_control.scns)+1) & 0xf) != \
+		readb(&dev.shmem->data_control.scnr))
+
+/* Switch to next transmit-buffer */
+#define sbnext (writeb((readb(&dev.shmem->data_control.scns)+1) & 0xf, \
+		       &dev.shmem->data_control.scns))
+
+/* Shortcuts for transmit-buffer-access */
+#define sbuf_n dev.shmem->data_control.scns
+#define sbuf_d dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].data
+#define sbuf_l dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].length
+#define sbuf_f dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].endflag
+
+/* Return true, if there is receive-data is available */
+#define rbavl  (readb(&dev.shmem->data_control.ecnr) != \
+		readb(&dev.shmem->data_control.ecns))
+
+/* Switch to next receive-buffer */
+#define rbnext (writeb((readb(&dev.shmem->data_control.ecnr)+1) & 0xf, \
+		       &dev.shmem->data_control.ecnr))
+
+/* Shortcuts for receive-buffer-access */
+#define rbuf_n dev.shmem->data_control.ecnr
+#define rbuf_d dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].data
+#define rbuf_l dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].length
+#define rbuf_f dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].endflag
+
+/* Shortcuts for command-buffer-access */
+#define cmd_o (dev.shmem->comm_control.pcio_o)
+#define cmd_i (dev.shmem->comm_control.pcio_i)
+
+/* Return free space in command-buffer */
+#define cmd_free ((readb(&cmd_i)>=readb(&cmd_o))? \
+		  0x100-readb(&cmd_i)+readb(&cmd_o): \
+		  readb(&cmd_o)-readb(&cmd_i))
+
+/* Shortcuts for message-buffer-access */
+#define msg_o (dev.shmem->comm_control.iopc_o)
+#define msg_i (dev.shmem->comm_control.iopc_i)
+
+/* Return length of Message, if avail. */
+#define msg_avail ((readb(&msg_o)>readb(&msg_i))? \
+		   0x100-readb(&msg_o)+readb(&msg_i): \
+		   readb(&msg_i)-readb(&msg_o))
+
+#define CID (card->interface.id)
+
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* icn_h */
diff --git a/drivers/isdn/isdnloop/Makefile b/drivers/isdn/isdnloop/Makefile
new file mode 100644
index 0000000..317cd3c
--- /dev/null
+++ b/drivers/isdn/isdnloop/Makefile
@@ -0,0 +1,5 @@
+# Makefile for the isdnloop ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_LOOP)	+= isdnloop.o
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
new file mode 100644
index 0000000..14e1f8f
--- /dev/null
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -0,0 +1,1554 @@
+/* $Id: isdnloop.c,v 1.11.6.7 2001/11/11 19:54:31 kai Exp $
+ *
+ * ISDN low-level module implementing a dummy loop driver.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include "isdnloop.h"
+
+static char *revision = "$Revision: 1.11.6.7 $";
+static char *isdnloop_id = "loop0";
+
+MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card");
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_LICENSE("GPL");
+MODULE_PARM(isdnloop_id, "s");
+MODULE_PARM_DESC(isdnloop_id, "ID-String of first card");
+
+static int isdnloop_addcard(char *);
+
+/*
+ * Free queue completely.
+ *
+ * Parameter:
+ *   card    = pointer to card struct
+ *   channel = channel number
+ */
+static void
+isdnloop_free_queue(isdnloop_card * card, int channel)
+{
+	struct sk_buff_head *queue = &card->bqueue[channel];
+
+	skb_queue_purge(queue);
+	card->sndcount[channel] = 0;
+}
+
+/*
+ * Send B-Channel data to another virtual card.
+ * This routine is called via timer-callback from isdnloop_pollbchan().
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel number (0-based)
+ */
+static void
+isdnloop_bchan_send(isdnloop_card * card, int ch)
+{
+	isdnloop_card *rcard = card->rcard[ch];
+	int rch = card->rch[ch], len, ack;
+	struct sk_buff *skb;
+	isdn_ctrl cmd;
+
+	while (card->sndcount[ch]) {
+		if ((skb = skb_dequeue(&card->bqueue[ch]))) {
+			len = skb->len;
+			card->sndcount[ch] -= len;
+			ack = *(skb->head); /* used as scratch area */
+			cmd.driver = card->myid;
+			cmd.arg = ch;
+			if (rcard){
+				rcard->interface.rcvcallb_skb(rcard->myid, rch, skb);
+			} else {
+				printk(KERN_WARNING "isdnloop: no rcard, skb dropped\n");
+				dev_kfree_skb(skb);
+
+			};
+			cmd.command = ISDN_STAT_BSENT;
+			cmd.parm.length = len;
+			card->interface.statcallb(&cmd);
+		} else
+			card->sndcount[ch] = 0;
+	}
+}
+
+/*
+ * Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ *
+ * Parameter:
+ *   data = pointer to card struct, set by kernel timer.data
+ */
+static void
+isdnloop_pollbchan(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	unsigned long flags;
+
+	if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE)
+		isdnloop_bchan_send(card, 0);
+	if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE)
+		isdnloop_bchan_send(card, 1);
+	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) {
+		/* schedule b-channel polling again */
+		save_flags(flags);
+		cli();
+		card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+		add_timer(&card->rb_timer);
+		card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+		restore_flags(flags);
+	} else
+		card->flags &= ~ISDNLOOP_FLAGS_RBTIMER;
+}
+
+/*
+ * Parse ICN-type setup string and fill fields of setup-struct
+ * with parsed data.
+ *
+ * Parameter:
+ *   setup = setup string, format: [caller-id],si1,si2,[called-id]
+ *   cmd   = pointer to struct to be filled.
+ */
+static void
+isdnloop_parse_setup(char *setup, isdn_ctrl * cmd)
+{
+	char *t = setup;
+	char *s = strchr(t, ',');
+
+	*s++ = '\0';
+	strlcpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone));
+	s = strchr(t = s, ',');
+	*s++ = '\0';
+	if (!strlen(t))
+		cmd->parm.setup.si1 = 0;
+	else
+		cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10);
+	s = strchr(t = s, ',');
+	*s++ = '\0';
+	if (!strlen(t))
+		cmd->parm.setup.si2 = 0;
+	else
+		cmd->parm.setup.si2 =
+		    simple_strtoul(t, NULL, 10);
+	strlcpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn));
+	cmd->parm.setup.plan = 0;
+	cmd->parm.setup.screen = 0;
+}
+
+typedef struct isdnloop_stat {
+	char *statstr;
+	int command;
+	int action;
+} isdnloop_stat;
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_stat_table[] =
+{
+	{"BCON_",          ISDN_STAT_BCONN, 1}, /* B-Channel connected        */
+	{"BDIS_",          ISDN_STAT_BHUP,  2}, /* B-Channel disconnected     */
+	{"DCON_",          ISDN_STAT_DCONN, 0}, /* D-Channel connected        */
+	{"DDIS_",          ISDN_STAT_DHUP,  0}, /* D-Channel disconnected     */
+	{"DCAL_I",         ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line  */
+	{"DSCA_I",         ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV     */
+	{"FCALL",          ISDN_STAT_ICALL, 4}, /* Leased line connection up  */
+	{"CIF",            ISDN_STAT_CINF,  5}, /* Charge-info, 1TR6-type     */
+	{"AOC",            ISDN_STAT_CINF,  6}, /* Charge-info, DSS1-type     */
+	{"CAU",            ISDN_STAT_CAUSE, 7}, /* Cause code                 */
+	{"TEI OK",         ISDN_STAT_RUN,   0}, /* Card connected to wallplug */
+	{"E_L1: ACT FAIL", ISDN_STAT_BHUP,  8}, /* Layer-1 activation failed  */
+	{"E_L2: DATA LIN", ISDN_STAT_BHUP,  8}, /* Layer-2 data link lost     */
+	{"E_L1: ACTIVATION FAILED",
+			   ISDN_STAT_BHUP,  8},         /* Layer-1 activation failed  */
+	{NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Parse Status message-strings from virtual card.
+ * Depending on status, call statcallb for sending messages to upper
+ * levels. Also set/reset B-Channel active-flags.
+ *
+ * Parameter:
+ *   status  = status string to parse.
+ *   channel = channel where message comes from.
+ *   card    = card where message comes from.
+ */
+static void
+isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card)
+{
+	isdnloop_stat *s = isdnloop_stat_table;
+	int action = -1;
+	isdn_ctrl cmd;
+
+	while (s->statstr) {
+		if (!strncmp(status, s->statstr, strlen(s->statstr))) {
+			cmd.command = s->command;
+			action = s->action;
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return;
+	cmd.driver = card->myid;
+	cmd.arg = channel;
+	switch (action) {
+		case 1:
+			/* BCON_x */
+			card->flags |= (channel) ?
+			    ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE;
+			break;
+		case 2:
+			/* BDIS_x */
+			card->flags &= ~((channel) ?
+					 ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE);
+			isdnloop_free_queue(card, channel);
+			break;
+		case 3:
+			/* DCAL_I and DSCA_I */
+			isdnloop_parse_setup(status + 6, &cmd);
+			break;
+		case 4:
+			/* FCALL */
+			sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);
+			sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);
+			cmd.parm.setup.si1 = 7;
+			cmd.parm.setup.si2 = 0;
+			cmd.parm.setup.plan = 0;
+			cmd.parm.setup.screen = 0;
+			break;
+		case 5:
+			/* CIF */
+			strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num));
+			break;
+		case 6:
+			/* AOC */
+			snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d",
+			     (int) simple_strtoul(status + 7, NULL, 16));
+			break;
+		case 7:
+			/* CAU */
+			status += 3;
+			if (strlen(status) == 4)
+				snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%s%c%c",
+				     status + 2, *status, *(status + 1));
+			else
+				strlcpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num));
+			break;
+		case 8:
+			/* Misc Errors on L1 and L2 */
+			card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE;
+			isdnloop_free_queue(card, 0);
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 0;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_BHUP;
+			card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE;
+			isdnloop_free_queue(card, 1);
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			card->interface.statcallb(&cmd);
+			cmd.command = ISDN_STAT_DHUP;
+			cmd.arg = 1;
+			cmd.driver = card->myid;
+			break;
+	}
+	card->interface.statcallb(&cmd);
+}
+
+/*
+ * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   c    = char to store.
+ */
+static void
+isdnloop_putmsg(isdnloop_card * card, unsigned char c)
+{
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+	*card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+	if (card->msg_buf_write == card->msg_buf_read) {
+		if (++card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	if (card->msg_buf_write > card->msg_buf_end)
+		card->msg_buf_write = card->msg_buf;
+	restore_flags(flags);
+}
+
+/*
+ * Poll a virtual cards message queue.
+ * If there are new status-replies from the card, copy them to
+ * ringbuffer for reading on /dev/isdnctrl and call
+ * isdnloop_parse_status() for processing them. Watch for special
+ * Firmware bootmessage and parse it, to get the D-Channel protocol.
+ * If there are B-Channels open, initiate a timer-callback to
+ * isdnloop_pollbchan().
+ * This routine is called periodically via timer interrupt.
+ *
+ * Parameter:
+ *   data = pointer to card struct
+ */
+static void
+isdnloop_polldchan(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	struct sk_buff *skb;
+	int avail;
+	int left;
+	u_char c;
+	int ch;
+	unsigned long flags;
+	u_char *p;
+	isdn_ctrl cmd;
+
+	if ((skb = skb_dequeue(&card->dqueue)))
+		avail = skb->len;
+	else
+		avail = 0;
+	for (left = avail; left > 0; left--) {
+		c = *skb->data;
+		skb_pull(skb, 1);
+		isdnloop_putmsg(card, c);
+		card->imsg[card->iptr] = c;
+		if (card->iptr < 59)
+			card->iptr++;
+		if (!skb->len) {
+			avail++;
+			isdnloop_putmsg(card, '\n');
+			card->imsg[card->iptr] = 0;
+			card->iptr = 0;
+			if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+			  card->imsg[1] <= '2' && card->imsg[2] == ';') {
+				ch = (card->imsg[1] - '0') - 1;
+				p = &card->imsg[3];
+				isdnloop_parse_status(p, ch, card);
+			} else {
+				p = card->imsg;
+				if (!strncmp(p, "DRV1.", 5)) {
+					printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p);
+					if (!strncmp(p + 7, "TC", 2)) {
+						card->ptype = ISDN_PTYPE_1TR6;
+						card->interface.features |= ISDN_FEATURE_P_1TR6;
+						printk(KERN_INFO
+						       "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID);
+					}
+					if (!strncmp(p + 7, "EC", 2)) {
+						card->ptype = ISDN_PTYPE_EURO;
+						card->interface.features |= ISDN_FEATURE_P_EURO;
+						printk(KERN_INFO
+						       "isdnloop: (%s) Euro-Protocol loaded and running\n", CID);
+					}
+					continue;
+
+				}
+			}
+		}
+	}
+	if (avail) {
+		cmd.command = ISDN_STAT_STAVAIL;
+		cmd.driver = card->myid;
+		cmd.arg = avail;
+		card->interface.statcallb(&cmd);
+	}
+	if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE))
+		if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) {
+			/* schedule b-channel polling */
+			card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+			save_flags(flags);
+			cli();
+			del_timer(&card->rb_timer);
+			card->rb_timer.function = isdnloop_pollbchan;
+			card->rb_timer.data = (unsigned long) card;
+			card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+			add_timer(&card->rb_timer);
+			restore_flags(flags);
+		}
+	/* schedule again */
+	save_flags(flags);
+	cli();
+	card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+	add_timer(&card->st_timer);
+	restore_flags(flags);
+}
+
+/*
+ * Append a packet to the transmit buffer-queue.
+ *
+ * Parameter:
+ *   channel = Number of B-channel
+ *   skb     = packet to send.
+ *   card    = pointer to card-struct
+ * Return:
+ *   Number of bytes transferred, -E??? on error
+ */
+static int
+isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card)
+{
+	int len = skb->len;
+	unsigned long flags;
+	struct sk_buff *nskb;
+
+	if (len > 4000) {
+		printk(KERN_WARNING
+		       "isdnloop: Send packet too large\n");
+		return -EINVAL;
+	}
+	if (len) {
+		if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE))
+			return 0;
+		if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE)
+			return 0;
+		save_flags(flags);
+		cli();
+		nskb = dev_alloc_skb(skb->len);
+		if (nskb) {
+			memcpy(skb_put(nskb, len), skb->data, len);
+			skb_queue_tail(&card->bqueue[channel], nskb);
+			dev_kfree_skb(skb);
+		} else
+			len = 0;
+		card->sndcount[channel] += len;
+		restore_flags(flags);
+	}
+	return len;
+}
+
+/*
+ * Read the messages from the card's ringbuffer
+ *
+ * Parameter:
+ *   buf  = pointer to buffer.
+ *   len  = number of bytes to read.
+ *   user = flag, 1: called from userlevel 0: called from kernel.
+ *   card = pointer to card struct.
+ * Return:
+ *   number of bytes actually transferred.
+ */
+static int
+isdnloop_readstatus(u_char __user *buf, int len, isdnloop_card * card)
+{
+	int count;
+	u_char __user *p;
+
+	for (p = buf, count = 0; count < len; p++, count++) {
+		if (card->msg_buf_read == card->msg_buf_write)
+			return count;
+		put_user(*card->msg_buf_read++, p);
+		if (card->msg_buf_read > card->msg_buf_end)
+			card->msg_buf_read = card->msg_buf;
+	}
+	return count;
+}
+
+/*
+ * Simulate a card's response by appending it to the cards
+ * message queue.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   s    = pointer to message-string.
+ *   ch   = channel: 0 = generic messages, 1 and 2 = D-channel messages.
+ * Return:
+ *   0 on success, 1 on memory squeeze.
+ */
+static int
+isdnloop_fake(isdnloop_card * card, char *s, int ch)
+{
+	struct sk_buff *skb;
+	int len = strlen(s) + ((ch >= 0) ? 3 : 0);
+
+	if (!(skb = dev_alloc_skb(len))) {
+		printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n");
+		return 1;
+	}
+	if (ch >= 0)
+		sprintf(skb_put(skb, 3), "%02d;", ch);
+	memcpy(skb_put(skb, strlen(s)), s, strlen(s));
+	skb_queue_tail(&card->dqueue, skb);
+	return 0;
+}
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_cmd_table[] =
+{
+	{"BCON_R",         0,  1},	/* B-Channel connect        */
+	{"BCON_I",         0, 17},	/* B-Channel connect ind    */
+	{"BDIS_R",         0,  2},	/* B-Channel disconnect     */
+	{"DDIS_R",         0,  3},	/* D-Channel disconnect     */
+	{"DCON_R",         0, 16},	/* D-Channel connect        */
+	{"DSCA_R",         0,  4},	/* Dial 1TR6-SPV     */
+	{"DCAL_R",         0,  5},	/* Dial */
+	{"EAZC",           0,  6},	/* Clear EAZ listener */
+	{"EAZ",            0,  7},	/* Set EAZ listener */
+	{"SEEAZ",          0,  8},	/* Get EAZ listener */
+	{"MSN",            0,  9},	/* Set/Clear MSN listener */
+	{"MSALL",          0, 10},	/* Set multi MSN listeners */
+	{"SETSIL",         0, 11},	/* Set SI list     */
+	{"SEESIL",         0, 12},	/* Get SI list     */
+	{"SILC",           0, 13},	/* Clear SI list     */
+	{"LOCK",           0, -1},	/* LOCK channel     */
+	{"UNLOCK",         0, -1},	/* UNLOCK channel     */
+	{"FV2ON",          1, 14},	/* Leased mode on               */
+	{"FV2OFF",         1, 15},	/* Leased mode off              */
+	{NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Simulate an error-response from a card.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ */
+static void
+isdnloop_fake_err(isdnloop_card * card)
+{
+	char buf[60];
+
+	sprintf(buf, "E%s", card->omsg);
+	isdnloop_fake(card, buf, -1);
+	isdnloop_fake(card, "NAK", -1);
+}
+
+static u_char ctable_eu[] =
+{0x00, 0x11, 0x01, 0x12};
+static u_char ctable_1t[] =
+{0x00, 0x3b, 0x01, 0x3a};
+
+/*
+ * Assemble a simplified cause message depending on the
+ * D-channel protocol used.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   loc  = location: 0 = local, 1 = remote.
+ *   cau  = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding.
+ * Return:
+ *   Pointer to buffer containing the assembled message.
+ */
+static char *
+isdnloop_unicause(isdnloop_card * card, int loc, int cau)
+{
+	static char buf[6];
+
+	switch (card->ptype) {
+		case ISDN_PTYPE_EURO:
+			sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]);
+			break;
+		case ISDN_PTYPE_1TR6:
+			sprintf(buf, "%02X44", ctable_1t[cau]);
+			break;
+		default:
+			return ("0000");
+	}
+	return (buf);
+}
+
+/*
+ * Release a virtual connection. Called from timer interrupt, when
+ * called party did not respond.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel (0-based)
+ */
+static void
+isdnloop_atimeout(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+	char buf[60];
+
+	save_flags(flags);
+	cli();
+	if (card->rcard) {
+		isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1);
+		card->rcard[ch]->rcard[card->rch[ch]] = NULL;
+		card->rcard[ch] = NULL;
+	}
+	isdnloop_fake(card, "DDIS_I", ch + 1);
+	/* No user responding */
+	sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3));
+	isdnloop_fake(card, buf, ch + 1);
+	restore_flags(flags);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout0(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	isdnloop_atimeout(card, 0);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout1(unsigned long data)
+{
+	isdnloop_card *card = (isdnloop_card *) data;
+	isdnloop_atimeout(card, 1);
+}
+
+/*
+ * Install a watchdog for a user, not responding.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel to watch for.
+ */
+static void
+isdnloop_start_ctimer(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	init_timer(&card->c_timer[ch]);
+	card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT;
+	if (ch)
+		card->c_timer[ch].function = isdnloop_atimeout1;
+	else
+		card->c_timer[ch].function = isdnloop_atimeout0;
+	card->c_timer[ch].data = (unsigned long) card;
+	add_timer(&card->c_timer[ch]);
+	restore_flags(flags);
+}
+
+/*
+ * Kill a pending channel watchdog.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ *   ch   = channel (0-based).
+ */
+static void
+isdnloop_kill_ctimer(isdnloop_card * card, int ch)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	del_timer(&card->c_timer[ch]);
+	restore_flags(flags);
+}
+
+static u_char si2bit[] =
+{0, 1, 0, 0, 0, 2, 0, 4, 0, 0};
+static u_char bit2si[] =
+{1, 5, 7};
+
+/*
+ * Try finding a listener for an outgoing call.
+ *
+ * Parameter:
+ *   card = pointer to calling card.
+ *   p    = pointer to ICN-type setup-string.
+ *   lch  = channel of calling card.
+ *   cmd  = pointer to struct to be filled when parsing setup.
+ * Return:
+ *   0 = found match, alerting should happen.
+ *   1 = found matching number but it is busy.
+ *   2 = no matching listener.
+ *   3 = found matching number but SI does not match.
+ */
+static int
+isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd)
+{
+	isdnloop_card *cc = cards;
+	unsigned long flags;
+	int ch;
+	int num_match;
+	int i;
+	char *e;
+	char nbuf[32];
+
+	isdnloop_parse_setup(p, cmd);
+	while (cc) {
+		for (ch = 0; ch < 2; ch++) {
+			/* Exclude ourself */
+			if ((cc == card) && (ch == lch))
+				continue;
+			num_match = 0;
+			switch (cc->ptype) {
+				case ISDN_PTYPE_EURO:
+					for (i = 0; i < 3; i++)
+						if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone)))
+							num_match = 1;
+					break;
+				case ISDN_PTYPE_1TR6:
+					e = cc->eazlist[ch];
+					while (*e) {
+						sprintf(nbuf, "%s%c", cc->s0num[0], *e);
+						if (!(strcmp(nbuf, cmd->parm.setup.phone)))
+							num_match = 1;
+						e++;
+					}
+			}
+			if (num_match) {
+				save_flags(flags);
+				cli();
+				/* channel idle? */
+				if (!(cc->rcard[ch])) {
+					/* Check SI */
+					if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) {
+						restore_flags(flags);
+						return 3;
+					}
+					/* ch is idle, si and number matches */
+					cc->rcard[ch] = card;
+					cc->rch[ch] = lch;
+					card->rcard[lch] = cc;
+					card->rch[lch] = ch;
+					restore_flags(flags);
+					return 0;
+				} else {
+					restore_flags(flags);
+					/* num matches, but busy */
+					if (ch == 1)
+						return 1;
+				}
+			}
+		}
+		cc = cc->next;
+	}
+	return 2;
+}
+
+/*
+ * Depending on D-channel protocol and caller/called, modify
+ * phone number.
+ *
+ * Parameter:
+ *   card   = pointer to card struct.
+ *   phone  = pointer phone number.
+ *   caller = flag: 1 = caller, 0 = called.
+ * Return:
+ *   pointer to new phone number.
+ */
+static char *
+isdnloop_vstphone(isdnloop_card * card, char *phone, int caller)
+{
+	int i;
+	static char nphone[30];
+
+	if (!card) {
+		printk("BUG!!!\n");
+		return "";
+	}
+	switch (card->ptype) {
+		case ISDN_PTYPE_EURO:
+			if (caller) {
+				for (i = 0; i < 2; i++)
+					if (!(strcmp(card->s0num[i], phone)))
+						return (phone);
+				return (card->s0num[0]);
+			}
+			return (phone);
+			break;
+		case ISDN_PTYPE_1TR6:
+			if (caller) {
+				sprintf(nphone, "%s%c", card->s0num[0], phone[0]);
+				return (nphone);
+			} else
+				return (&phone[strlen(phone) - 1]);
+			break;
+	}
+	return "";
+}
+
+/*
+ * Parse an ICN-type command string sent to the 'card'.
+ * Perform misc. actions depending on the command.
+ *
+ * Parameter:
+ *   card = pointer to card struct.
+ */
+static void
+isdnloop_parse_cmd(isdnloop_card * card)
+{
+	char *p = card->omsg;
+	isdn_ctrl cmd;
+	char buf[60];
+	isdnloop_stat *s = isdnloop_cmd_table;
+	int action = -1;
+	int i;
+	int ch;
+
+	if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) {
+		isdnloop_fake_err(card);
+		return;
+	}
+	ch = card->omsg[1] - '0';
+	if ((ch < 0) || (ch > 2)) {
+		isdnloop_fake_err(card);
+		return;
+	}
+	p += 3;
+	while (s->statstr) {
+		if (!strncmp(p, s->statstr, strlen(s->statstr))) {
+			action = s->action;
+			if (s->command && (ch != 0)) {
+				isdnloop_fake_err(card);
+				return;
+			}
+			break;
+		}
+		s++;
+	}
+	if (action == -1)
+		return;
+	switch (action) {
+		case 1:
+			/* 0x;BCON_R */
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card->rcard[ch - 1], "BCON_I",
+					      card->rch[ch - 1] + 1);
+				isdnloop_fake(card, "BCON_C", ch);
+			}
+			break;
+		case 17:
+			/* 0x;BCON_I */
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card->rcard[ch - 1], "BCON_C",
+					      card->rch[ch - 1] + 1);
+			}
+			break;
+		case 2:
+			/* 0x;BDIS_R */
+			isdnloop_fake(card, "BDIS_C", ch);
+			if (card->rcard[ch - 1]) {
+				isdnloop_fake(card->rcard[ch - 1], "BDIS_I",
+					      card->rch[ch - 1] + 1);
+			}
+			break;
+		case 16:
+			/* 0x;DCON_R */
+			isdnloop_kill_ctimer(card, ch - 1);
+			if (card->rcard[ch - 1]) {
+				isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+				isdnloop_fake(card->rcard[ch - 1], "DCON_C",
+					      card->rch[ch - 1] + 1);
+				isdnloop_fake(card, "DCON_C", ch);
+			}
+			break;
+		case 3:
+			/* 0x;DDIS_R */
+			isdnloop_kill_ctimer(card, ch - 1);
+			if (card->rcard[ch - 1]) {
+				isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+				isdnloop_fake(card->rcard[ch - 1], "DDIS_I",
+					      card->rch[ch - 1] + 1);
+				card->rcard[ch - 1] = NULL;
+			}
+			isdnloop_fake(card, "DDIS_C", ch);
+			break;
+		case 4:
+			/* 0x;DSCA_Rdd,yy,zz,oo */
+			if (card->ptype != ISDN_PTYPE_1TR6) {
+				isdnloop_fake_err(card);
+				return;
+			}
+			/* Fall through */
+		case 5:
+			/* 0x;DCAL_Rdd,yy,zz,oo */
+			p += 6;
+			switch (isdnloop_try_call(card, p, ch - 1, &cmd)) {
+				case 0:
+					/* Alerting */
+					sprintf(buf, "D%s_I%s,%02d,%02d,%s",
+					   (action == 4) ? "SCA" : "CAL",
+						isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1),
+						cmd.parm.setup.si1,
+						cmd.parm.setup.si2,
+					isdnloop_vstphone(card->rcard[ch - 1],
+					       cmd.parm.setup.phone, 0));
+					isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1);
+					/* Fall through */
+				case 3:
+					/* si1 does not match, don't alert but start timer */
+					isdnloop_start_ctimer(card, ch - 1);
+					break;
+				case 1:
+					/* Remote busy */
+					isdnloop_fake(card, "DDIS_I", ch);
+					sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1));
+					isdnloop_fake(card, buf, ch);
+					break;
+				case 2:
+					/* No such user */
+					isdnloop_fake(card, "DDIS_I", ch);
+					sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2));
+					isdnloop_fake(card, buf, ch);
+					break;
+			}
+			break;
+		case 6:
+			/* 0x;EAZC */
+			card->eazlist[ch - 1][0] = '\0';
+			break;
+		case 7:
+			/* 0x;EAZ */
+			p += 3;
+			strcpy(card->eazlist[ch - 1], p);
+			break;
+		case 8:
+			/* 0x;SEEAZ */
+			sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]);
+			isdnloop_fake(card, buf, ch + 1);
+			break;
+		case 9:
+			/* 0x;MSN */
+			break;
+		case 10:
+			/* 0x;MSNALL */
+			break;
+		case 11:
+			/* 0x;SETSIL */
+			p += 6;
+			i = 0;
+			while (strchr("0157", *p)) {
+				if (i)
+					card->sil[ch - 1] |= si2bit[*p - '0'];
+				i = (*p++ == '0');
+			}
+			if (*p)
+				isdnloop_fake_err(card);
+			break;
+		case 12:
+			/* 0x;SEESIL */
+			sprintf(buf, "SIN-LIST: ");
+			p = buf + 10;
+			for (i = 0; i < 3; i++)
+				if (card->sil[ch - 1] & (1 << i))
+					p += sprintf(p, "%02d", bit2si[i]);
+			isdnloop_fake(card, buf, ch + 1);
+			break;
+		case 13:
+			/* 0x;SILC */
+			card->sil[ch - 1] = 0;
+			break;
+		case 14:
+			/* 00;FV2ON */
+			break;
+		case 15:
+			/* 00;FV2OFF */
+			break;
+	}
+}
+
+/*
+ * Put command-strings into the of the 'card'. In reality, execute them
+ * right in place by calling isdnloop_parse_cmd(). Also copy every
+ * command to the read message ringbuffer, preceeding it with a '>'.
+ * These mesagges can be read at /dev/isdnctrl.
+ *
+ * Parameter:
+ *   buf  = pointer to command buffer.
+ *   len  = length of buffer data.
+ *   user = flag: 1 = called form userlevel, 0 called from kernel.
+ *   card = pointer to card struct.
+ * Return:
+ *   number of bytes transferred (currently always equals len).
+ */
+static int
+isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card)
+{
+	int xcount = 0;
+	int ocount = 1;
+	isdn_ctrl cmd;
+
+	while (len) {
+		int count = len;
+		u_char *p;
+		u_char msg[0x100];
+
+		if (count > 255)
+			count = 255;
+		if (user) {
+			if (copy_from_user(msg, buf, count))
+				return -EFAULT;
+		} else
+			memcpy(msg, buf, count);
+		isdnloop_putmsg(card, '>');
+		for (p = msg; count > 0; count--, p++) {
+			len--;
+			xcount++;
+			isdnloop_putmsg(card, *p);
+			card->omsg[card->optr] = *p;
+			if (*p == '\n') {
+				card->omsg[card->optr] = '\0';
+				card->optr = 0;
+				isdnloop_parse_cmd(card);
+				if (len) {
+					isdnloop_putmsg(card, '>');
+					ocount++;
+				}
+			} else {
+				if (card->optr < 59)
+					card->optr++;
+			}
+			ocount++;
+		}
+	}
+	cmd.command = ISDN_STAT_STAVAIL;
+	cmd.driver = card->myid;
+	cmd.arg = ocount;
+	card->interface.statcallb(&cmd);
+	return xcount;
+}
+
+/*
+ * Delete card's pending timers, send STOP to linklevel
+ */
+static void
+isdnloop_stopcard(isdnloop_card * card)
+{
+	unsigned long flags;
+	isdn_ctrl cmd;
+
+	save_flags(flags);
+	cli();
+	if (card->flags & ISDNLOOP_FLAGS_RUNNING) {
+		card->flags &= ~ISDNLOOP_FLAGS_RUNNING;
+		del_timer(&card->st_timer);
+		del_timer(&card->rb_timer);
+		del_timer(&card->c_timer[0]);
+		del_timer(&card->c_timer[1]);
+		cmd.command = ISDN_STAT_STOP;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+	}
+	restore_flags(flags);
+}
+
+/*
+ * Stop all cards before unload.
+ */
+static void
+isdnloop_stopallcards(void)
+{
+	isdnloop_card *p = cards;
+
+	while (p) {
+		isdnloop_stopcard(p);
+		p = p->next;
+	}
+}
+
+/*
+ * Start a 'card'. Simulate card's boot message and set the phone
+ * number(s) of the virtual 'S0-Interface'. Install D-channel
+ * poll timer.
+ *
+ * Parameter:
+ *   card  = pointer to card struct.
+ *   sdefp = pointer to struct holding ioctl parameters.
+ * Return:
+ *   0 on success, -E??? otherwise.
+ */
+static int
+isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp)
+{
+	unsigned long flags;
+	isdnloop_sdef sdef;
+	int i;
+
+	if (card->flags & ISDNLOOP_FLAGS_RUNNING)
+		return -EBUSY;
+	if (copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef)))
+		return -EFAULT;
+	save_flags(flags);
+	cli();
+	switch (sdef.ptype) {
+		case ISDN_PTYPE_EURO:
+			if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96",
+					  -1)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			card->sil[0] = card->sil[1] = 4;
+			if (isdnloop_fake(card, "TEI OK", 0)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			for (i = 0; i < 3; i++)
+				strcpy(card->s0num[i], sdef.num[i]);
+			break;
+		case ISDN_PTYPE_1TR6:
+			if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95",
+					  -1)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			card->sil[0] = card->sil[1] = 4;
+			if (isdnloop_fake(card, "TEI OK", 0)) {
+				restore_flags(flags);
+				return -ENOMEM;
+			}
+			strcpy(card->s0num[0], sdef.num[0]);
+			card->s0num[1][0] = '\0';
+			card->s0num[2][0] = '\0';
+			break;
+		default:
+			restore_flags(flags);
+			printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n",
+			       sdef.ptype);
+			return -EINVAL;
+	}
+	init_timer(&card->st_timer);
+	card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+	card->st_timer.function = isdnloop_polldchan;
+	card->st_timer.data = (unsigned long) card;
+	add_timer(&card->st_timer);
+	card->flags |= ISDNLOOP_FLAGS_RUNNING;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * Main handler for commands sent by linklevel.
+ */
+static int
+isdnloop_command(isdn_ctrl * c, isdnloop_card * card)
+{
+	ulong a;
+	int i;
+	char cbuf[60];
+	isdn_ctrl cmd;
+	isdnloop_cdef cdef;
+
+	switch (c->command) {
+		case ISDN_CMD_IOCTL:
+			memcpy(&a, c->parm.num, sizeof(ulong));
+			switch (c->arg) {
+				case ISDNLOOP_IOCTL_DEBUGVAR:
+					return (ulong) card;
+				case ISDNLOOP_IOCTL_STARTUP:
+					if (!access_ok(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef)))
+						return -EFAULT;
+					return (isdnloop_start(card, (isdnloop_sdef *) a));
+					break;
+				case ISDNLOOP_IOCTL_ADDCARD:
+					if (copy_from_user((char *)&cdef,
+							   (char *)a,
+							   sizeof(cdef)))
+						return -EFAULT;
+					return (isdnloop_addcard(cdef.id1));
+					break;
+				case ISDNLOOP_IOCTL_LEASEDCFG:
+					if (a) {
+						if (!card->leased) {
+							card->leased = 1;
+							while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+								set_current_state(TASK_INTERRUPTIBLE);
+								schedule_timeout(10);
+							}
+							set_current_state(TASK_INTERRUPTIBLE);
+							schedule_timeout(10);
+							sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n");
+							i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "isdnloop: (%s) Leased-line mode enabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					} else {
+						if (card->leased) {
+							card->leased = 0;
+							sprintf(cbuf, "00;FV2OFF\n");
+							i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+							printk(KERN_INFO
+							       "isdnloop: (%s) Leased-line mode disabled\n",
+							       CID);
+							cmd.command = ISDN_STAT_RUN;
+							cmd.driver = card->myid;
+							cmd.arg = 0;
+							card->interface.statcallb(&cmd);
+						}
+					}
+					return 0;
+				default:
+					return -EINVAL;
+			}
+			break;
+		case ISDN_CMD_DIAL:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (card->leased)
+				break;
+			if ((c->arg & 255) < ISDNLOOP_BCH) {
+				char *p;
+				char dial[50];
+				char dcode[4];
+
+				a = c->arg;
+				p = c->parm.setup.phone;
+				if (*p == 's' || *p == 'S') {
+					/* Dial for SPV */
+					p++;
+					strcpy(dcode, "SCA");
+				} else
+					/* Normal Dial */
+					strcpy(dcode, "CAL");
+				strcpy(dial, p);
+				sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1),
+					dcode, dial, c->parm.setup.si1,
+				c->parm.setup.si2, c->parm.setup.eazmsn);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTD:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ISDNLOOP_BCH) {
+				a = c->arg + 1;
+				cbuf[0] = 0;
+				switch (card->l2_proto[a - 1]) {
+					case ISDN_PROTO_L2_X75I:
+						sprintf(cbuf, "%02d;BX75\n", (int) a);
+						break;
+#ifdef CONFIG_ISDN_X25
+					case ISDN_PROTO_L2_X25DTE:
+						sprintf(cbuf, "%02d;BX2T\n", (int) a);
+						break;
+					case ISDN_PROTO_L2_X25DCE:
+						sprintf(cbuf, "%02d;BX2C\n", (int) a);
+						break;
+#endif
+					case ISDN_PROTO_L2_HDLC:
+						sprintf(cbuf, "%02d;BTRA\n", (int) a);
+						break;
+				}
+				if (strlen(cbuf))
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+			}
+			break;
+		case ISDN_CMD_ACCEPTB:
+			if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+				return -ENODEV;
+			if (c->arg < ISDNLOOP_BCH) {
+				a = c->arg + 1;
+				switch (card->l2_proto[a - 1]) {
+					case ISDN_PROTO_L2_X75I:
+						sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+						break;
+#ifdef CONFIG_ISDN_X25
+					case ISDN_PROTO_L2_X25DTE:
+						sprintf(cbuf, "%02d;BCON_R,BX2T\n", (int) a);
+						break;
+					case ISDN_PROTO_L2_X25DCE:
+						sprintf(cbuf, "%02d;BCON_R,BX2C\n", (int) a);
+						break;
+#endif
+					case ISDN_PROTO_L2_HDLC:
+						sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+						break;
+					default:
+						sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+				}
+				printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf);
+				i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				break;
+		case ISDN_CMD_HANGUP:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (c->arg < ISDNLOOP_BCH) {
+					a = c->arg + 1;
+					sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_SETEAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (card->leased)
+					break;
+				if (c->arg < ISDNLOOP_BCH) {
+					a = c->arg + 1;
+					if (card->ptype == ISDN_PTYPE_EURO) {
+						sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+							c->parm.num[0] ? "N" : "ALL", c->parm.num);
+					} else
+						sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+							c->parm.num[0] ? c->parm.num : (u_char *) "0123456789");
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_CLREAZ:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if (card->leased)
+					break;
+				if (c->arg < ISDNLOOP_BCH) {
+					a = c->arg + 1;
+					if (card->ptype == ISDN_PTYPE_EURO)
+						sprintf(cbuf, "%02d;MSNC\n", (int) a);
+					else
+						sprintf(cbuf, "%02d;EAZC\n", (int) a);
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+				}
+				break;
+		case ISDN_CMD_SETL2:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				if ((c->arg & 255) < ISDNLOOP_BCH) {
+					a = c->arg;
+					switch (a >> 8) {
+						case ISDN_PROTO_L2_X75I:
+							sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+							break;
+#ifdef CONFIG_ISDN_X25
+						case ISDN_PROTO_L2_X25DTE:
+							sprintf(cbuf, "%02d;BX2T\n", (int) (a & 255) + 1);
+							break;
+						case ISDN_PROTO_L2_X25DCE:
+							sprintf(cbuf, "%02d;BX2C\n", (int) (a & 255) + 1);
+							break;
+#endif
+						case ISDN_PROTO_L2_HDLC:
+							sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+							break;
+						case ISDN_PROTO_L2_TRANS:
+							sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+							break;
+						default:
+							return -EINVAL;
+					}
+					i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+					card->l2_proto[a & 255] = (a >> 8);
+				}
+				break;
+		case ISDN_CMD_SETL3:
+				if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+					return -ENODEV;
+				return 0;
+		default:
+				return -EINVAL;
+			}
+	}
+	return 0;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline isdnloop_card *
+isdnloop_findcard(int driverid)
+{
+	isdnloop_card *p = cards;
+
+	while (p) {
+		if (p->myid == driverid)
+			return p;
+		p = p->next;
+	}
+	return (isdnloop_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+	isdnloop_card *card = isdnloop_findcard(c->driver);
+
+	if (card)
+		return (isdnloop_command(c, card));
+	printk(KERN_ERR
+	       "isdnloop: if_command called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char __user *buf, int len, int id, int channel)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		return (isdnloop_writecmd(buf, len, 1, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_writecmd called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_readstatus(u_char __user *buf, int len, int id, int channel)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		return (isdnloop_readstatus(buf, len, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_readstatus called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+	isdnloop_card *card = isdnloop_findcard(id);
+
+	if (card) {
+		if (!card->flags & ISDNLOOP_FLAGS_RUNNING)
+			return -ENODEV;
+		/* ack request stored in skb scratch area */
+		*(skb->head) = ack;
+		return (isdnloop_sendbuf(channel, skb, card));
+	}
+	printk(KERN_ERR
+	       "isdnloop: if_sendbuf called with invalid driverId!\n");
+	return -ENODEV;
+}
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list and register it at linklevel.
+ */
+static isdnloop_card *
+isdnloop_initcard(char *id)
+{
+	isdnloop_card *card;
+	int i;
+
+	if (!(card = (isdnloop_card *) kmalloc(sizeof(isdnloop_card), GFP_KERNEL))) {
+		printk(KERN_WARNING
+		 "isdnloop: (%s) Could not allocate card-struct.\n", id);
+		return (isdnloop_card *) 0;
+	}
+	memset((char *) card, 0, sizeof(isdnloop_card));
+	card->interface.owner = THIS_MODULE;
+	card->interface.channels = ISDNLOOP_BCH;
+	card->interface.hl_hdrlen  = 1; /* scratch area for storing ack flag*/ 
+	card->interface.maxbufsize = 4000;
+	card->interface.command = if_command;
+	card->interface.writebuf_skb = if_sendbuf;
+	card->interface.writecmd = if_writecmd;
+	card->interface.readstat = if_readstatus;
+	card->interface.features = ISDN_FEATURE_L2_X75I |
+#ifdef CONFIG_ISDN_X25
+	    ISDN_FEATURE_L2_X25DTE |
+	    ISDN_FEATURE_L2_X25DCE |
+#endif
+	    ISDN_FEATURE_L2_HDLC |
+	    ISDN_FEATURE_L3_TRANS |
+	    ISDN_FEATURE_P_UNKNOWN;
+	card->ptype = ISDN_PTYPE_UNKNOWN;
+	strlcpy(card->interface.id, id, sizeof(card->interface.id));
+	card->msg_buf_write = card->msg_buf;
+	card->msg_buf_read = card->msg_buf;
+	card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+	for (i = 0; i < ISDNLOOP_BCH; i++) {
+		card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+		skb_queue_head_init(&card->bqueue[i]);
+	}
+	skb_queue_head_init(&card->dqueue);
+	card->next = cards;
+	cards = card;
+	if (!register_isdn(&card->interface)) {
+		cards = cards->next;
+		printk(KERN_WARNING
+		       "isdnloop: Unable to register %s\n", id);
+		kfree(card);
+		return (isdnloop_card *) 0;
+	}
+	card->myid = card->interface.channels;
+	return card;
+}
+
+static int
+isdnloop_addcard(char *id1)
+{
+	isdnloop_card *card;
+
+	if (!(card = isdnloop_initcard(id1))) {
+		return -EIO;
+	}
+	printk(KERN_INFO
+	       "isdnloop: (%s) virtual card added\n",
+	       card->interface.id);
+	return 0;
+}
+
+static int __init
+isdnloop_init(void)
+{
+	char *p;
+	char rev[10];
+
+	if ((p = strchr(revision, ':'))) {
+		strcpy(rev, p + 1);
+		p = strchr(rev, '$');
+		*p = 0;
+	} else
+		strcpy(rev, " ??? ");
+	printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev);
+
+	if (isdnloop_id)
+		return (isdnloop_addcard(isdnloop_id));
+
+	return 0;
+}
+
+static void __exit
+isdnloop_exit(void)
+{
+	isdn_ctrl cmd;
+	isdnloop_card *card = cards;
+	isdnloop_card *last;
+	int i;
+
+	isdnloop_stopallcards();
+	while (card) {
+		cmd.command = ISDN_STAT_UNLOAD;
+		cmd.driver = card->myid;
+		card->interface.statcallb(&cmd);
+		for (i = 0; i < ISDNLOOP_BCH; i++)
+			isdnloop_free_queue(card, i);
+		card = card->next;
+	}
+	card = cards;
+	while (card) {
+		last = card;
+		skb_queue_purge(&card->dqueue);
+		card = card->next;
+		kfree(last);
+	}
+	printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n");
+}
+
+module_init(isdnloop_init);
+module_exit(isdnloop_exit);
diff --git a/drivers/isdn/isdnloop/isdnloop.h b/drivers/isdn/isdnloop/isdnloop.h
new file mode 100644
index 0000000..8fb7bc1
--- /dev/null
+++ b/drivers/isdn/isdnloop/isdnloop.h
@@ -0,0 +1,112 @@
+/* $Id: isdnloop.h,v 1.5.6.3 2001/09/23 22:24:56 kai Exp $
+ *
+ * Loopback lowlevel module for testing of linklevel.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef isdnloop_h
+#define isdnloop_h
+
+#define ISDNLOOP_IOCTL_DEBUGVAR  0
+#define ISDNLOOP_IOCTL_ADDCARD   1
+#define ISDNLOOP_IOCTL_LEASEDCFG 2
+#define ISDNLOOP_IOCTL_STARTUP   3
+
+/* Struct for adding new cards */
+typedef struct isdnloop_cdef {
+	char id1[10];
+} isdnloop_cdef;
+
+/* Struct for configuring cards */
+typedef struct isdnloop_sdef {
+	int ptype;
+	char num[3][20];
+} isdnloop_sdef;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+
+#endif                          /* __KERNEL__ */
+
+#define ISDNLOOP_FLAGS_B1ACTIVE 1	/* B-Channel-1 is open           */
+#define ISDNLOOP_FLAGS_B2ACTIVE 2	/* B-Channel-2 is open           */
+#define ISDNLOOP_FLAGS_RUNNING  4	/* Cards driver activated        */
+#define ISDNLOOP_FLAGS_RBTIMER  8	/* scheduling of B-Channel-poll  */
+#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle          */
+#define ISDNLOOP_TIMER_DCREAD (HZ/2)	/* D-Channel poll-cycle          */
+#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ)	/* Alert timeout                 */
+#define ISDNLOOP_MAX_SQUEUE 65536	/* Max. outstanding send-data    */
+#define ISDNLOOP_BCH 2          /* channels per card             */
+
+/*
+ * Per card driver data
+ */
+typedef struct isdnloop_card {
+	struct isdnloop_card *next;	/* Pointer to next device struct    */
+	struct isdnloop_card
+	*rcard[ISDNLOOP_BCH];   /* Pointer to 'remote' card         */
+	int rch[ISDNLOOP_BCH];  /* 'remote' channel                 */
+	int myid;               /* Driver-Nr. assigned by linklevel */
+	int leased;             /* Flag: This Adapter is connected  */
+	/*       to a leased line           */
+	int sil[ISDNLOOP_BCH];  /* SI's to listen for               */
+	char eazlist[ISDNLOOP_BCH][11];
+	/* EAZ's to listen for              */
+	char s0num[3][20];      /* 1TR6 base-number or MSN's        */
+	unsigned short flags;   /* Statusflags                      */
+	int ptype;              /* Protocol type (1TR6 or Euro)     */
+	struct timer_list st_timer;	/* Timer for Status-Polls           */
+	struct timer_list rb_timer;	/* Timer for B-Channel-Polls        */
+	struct timer_list
+	 c_timer[ISDNLOOP_BCH]; /* Timer for Alerting               */
+	int l2_proto[ISDNLOOP_BCH];	/* Current layer-2-protocol         */
+	isdn_if interface;      /* Interface to upper layer         */
+	int iptr;               /* Index to imsg-buffer             */
+	char imsg[60];          /* Internal buf for status-parsing  */
+	int optr;               /* Index to omsg-buffer             */
+	char omsg[60];          /* Internal buf for cmd-parsing     */
+	char msg_buf[2048];     /* Buffer for status-messages       */
+	char *msg_buf_write;    /* Writepointer for statusbuffer    */
+	char *msg_buf_read;     /* Readpointer for statusbuffer     */
+	char *msg_buf_end;      /* Pointer to end of statusbuffer   */
+	int sndcount[ISDNLOOP_BCH];	/* Byte-counters for B-Ch.-send     */
+	struct sk_buff_head
+	 bqueue[ISDNLOOP_BCH];  /* B-Channel queues                 */
+	struct sk_buff_head dqueue;	/* D-Channel queue                  */
+} isdnloop_card;
+
+/*
+ * Main driver data
+ */
+#ifdef __KERNEL__
+static isdnloop_card *cards = (isdnloop_card *) 0;
+#endif                          /* __KERNEL__ */
+
+/* Utility-Macros */
+
+#define CID (card->interface.id)
+
+#endif                          /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif                          /* isdnloop_h */
diff --git a/drivers/isdn/pcbit/Kconfig b/drivers/isdn/pcbit/Kconfig
new file mode 100644
index 0000000..f06997f
--- /dev/null
+++ b/drivers/isdn/pcbit/Kconfig
@@ -0,0 +1,14 @@
+#
+# Config.in for PCBIT ISDN driver
+#
+config ISDN_DRV_PCBIT
+	tristate "PCBIT-D support"
+	depends on ISDN_I4L && ISA && (BROKEN || !PPC)
+	help
+	  This enables support for the PCBIT ISDN-card.  This card is
+	  manufactured in Portugal by Octal.  For running this card,
+	  additional firmware is necessary, which has to be downloaded into
+	  the card using a utility which is distributed separately.  See
+	  <file:Documentation/isdn/README> and
+	  <file:Documentation/isdn/README.pcbit> for more information.
+
diff --git a/drivers/isdn/pcbit/Makefile b/drivers/isdn/pcbit/Makefile
new file mode 100644
index 0000000..2d026c3
--- /dev/null
+++ b/drivers/isdn/pcbit/Makefile
@@ -0,0 +1,9 @@
+# Makefile for the pcbit ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_PCBIT)	+= pcbit.o
+
+# Multipart objects.
+
+pcbit-y				:= module.o edss1.o drv.o layer2.o capi.o callbacks.o
diff --git a/drivers/isdn/pcbit/callbacks.c b/drivers/isdn/pcbit/callbacks.c
new file mode 100644
index 0000000..692ec72
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.c
@@ -0,0 +1,367 @@
+/*
+ * Callbacks for the FSM
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * Fix: 19981230 - Carlos Morgado <chbm@techie.com>
+ * Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN 
+ * NULL pointer dereference in cb_in_1 (originally fixed in 2.0)
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+#include "callbacks.h"
+#include "capi.h"
+
+ushort last_ref_num = 1;
+
+/*
+ *  send_conn_req
+ *
+ */
+
+void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	      struct callb_data *cbdata) 
+{
+	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+
+#ifdef DEBUG
+        printk(KERN_DEBUG "Called Party Number: %s\n", 
+               cbdata->data.setup.CalledPN);
+#endif
+        /*
+         * hdr - kmalloc in capi_conn_req
+         *     - kfree   when msg has been sent
+         */
+
+        if ((len = capi_conn_req(cbdata->data.setup.CalledPN, &skb, 
+				 chan->proto)) < 0)
+        {
+                printk("capi_conn_req failed\n");
+                return;
+        }
+
+
+        refnum = last_ref_num++ & 0x7fffU;
+
+        chan->callref = 0;
+        chan->layer2link = 0;
+        chan->snum = 0;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_CONN_REQ, refnum, skb, len);
+}
+
+/*
+ *  rcv CONNECT
+ *  will go into ACTIVE state
+ *  send CONN_ACTIVE_RESP
+ *  send Select protocol request 
+ */
+
+void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	      struct callb_data *data) 
+{
+        isdn_ctrl ictl;
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+        if ((len=capi_conn_active_resp(chan, &skb)) < 0)
+        {
+                printk("capi_conn_active_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_CONN_ACTV_RESP, refnum, skb, len);
+
+
+        ictl.command = ISDN_STAT_DCONN;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+
+        /* ACTIVE D-channel */
+
+        /* Select protocol  */
+
+        if ((len=capi_select_proto_req(chan, &skb, 1 /*outgoing*/)) < 0) { 
+                printk("capi_select_proto_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+}
+
+
+/*
+ * Disconnect received (actually RELEASE COMPLETE) 
+ * This means we were not able to establish connection with remote
+ * Inform the big boss above
+ */
+void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+	      struct callb_data *data) 
+{
+        isdn_ctrl ictl;
+
+        ictl.command = ISDN_STAT_DHUP;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+
+/*
+ * Incoming call received
+ * inform user
+ */
+
+void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+	     struct callb_data *cbdata) 
+{
+        isdn_ctrl ictl;
+        unsigned short refnum;
+ 	struct sk_buff *skb;
+	int len;
+
+
+        ictl.command = ISDN_STAT_ICALL;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        
+        /*
+         *  ictl.num >= strlen() + strlen() + 5
+         */
+
+	if (cbdata->data.setup.CallingPN == NULL) {
+		printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n");
+		strcpy(ictl.parm.setup.phone, "0");
+	}
+	else {
+		strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+	}
+	if (cbdata->data.setup.CalledPN == NULL) {
+		printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n");
+		strcpy(ictl.parm.setup.eazmsn, "0");
+	}
+	else {
+		strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
+	}
+	ictl.parm.setup.si1 = 7;
+	ictl.parm.setup.si2 = 0;
+	ictl.parm.setup.plan = 0;
+	ictl.parm.setup.screen = 0;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "statstr: %s\n", ictl.num);
+#endif
+
+        dev->dev_if->statcallb(&ictl);
+
+        
+        if ((len=capi_conn_resp(chan, &skb)) < 0) {
+                printk(KERN_DEBUG "capi_conn_resp failed\n");
+                return;
+	}
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_CONN_RESP, refnum, skb, len);
+}
+
+/*
+ * user has replied
+ * open the channel
+ * send CONNECT message CONNECT_ACTIVE_REQ in CAPI
+ */
+
+void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+	     struct callb_data *data)
+{
+        unsigned short refnum;
+	struct sk_buff *skb;
+        int len;
+        
+        if ((len = capi_conn_active_req(chan, &skb)) < 0) {        
+                printk(KERN_DEBUG "capi_conn_active_req failed\n");
+                return;
+        }
+
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+	printk(KERN_DEBUG "sending MSG_CONN_ACTV_REQ\n");
+        pcbit_l2_write(dev, MSG_CONN_ACTV_REQ, refnum, skb, len);
+}
+
+/*
+ * CONN_ACK arrived
+ * start b-proto selection
+ *
+ */
+
+void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	     struct callb_data *data)
+{
+        unsigned short refnum;
+ 	struct sk_buff *skb;
+	int len;
+        
+        if ((len = capi_select_proto_req(chan, &skb, 0 /*incoming*/)) < 0)
+        {
+                printk("capi_select_proto_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+
+}
+
+
+/*
+ * Received disconnect ind on active state
+ * send disconnect resp
+ * send msg to user
+ */
+void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data)
+{
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+        isdn_ctrl ictl;
+  
+        if ((len = capi_disc_resp(chan, &skb)) < 0) {
+                printk("capi_disc_resp failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_DISC_RESP, refnum, skb, len);    
+
+        ictl.command = ISDN_STAT_BHUP;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+        
+/*
+ *  User HANGUP on active/call proceeding state
+ *  send disc.req
+ */
+void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data)
+{
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+        if ((len = capi_disc_req(chan->callref, &skb, CAUSE_NORMAL)) < 0)
+        {
+                printk("capi_disc_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb, len);  
+}
+
+/*
+ *  Disc confirm received send BHUP
+ *  Problem: when the HL driver sends the disc req itself
+ *           LL receives BHUP
+ */
+void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data)
+{
+        isdn_ctrl ictl;
+
+        ictl.command = ISDN_STAT_BHUP;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		struct callb_data *data)
+{
+}
+
+/*
+ * send activate b-chan protocol
+ */
+void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data) 
+{
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+        if ((len = capi_activate_transp_req(chan, &skb)) < 0)
+        {
+                printk("capi_conn_activate_transp_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_ACT_TRANSP_REQ, refnum, skb, len);
+}
+
+/*
+ *  Inform User that the B-channel is available
+ */
+void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	     struct callb_data *data) 
+{
+        isdn_ctrl ictl;
+
+        ictl.command = ISDN_STAT_BCONN;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+
+
diff --git a/drivers/isdn/pcbit/callbacks.h b/drivers/isdn/pcbit/callbacks.h
new file mode 100644
index 0000000..f510dc5
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.h
@@ -0,0 +1,49 @@
+/*
+ * Callbacks prototypes for FSM
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef CALLBACKS_H
+#define CALLBACKS_H
+
+
+extern void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		     struct callb_data *data);
+
+extern void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		     struct callb_data *data);
+
+extern void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		     struct callb_data *data);
+
+extern void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+extern void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+extern void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+
+extern void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+extern void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+extern void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+
+extern void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		       struct callb_data *data);
+
+extern void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+extern void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+
+#endif
+
+
diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c
new file mode 100644
index 0000000..29eb03a
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.c
@@ -0,0 +1,663 @@
+/*
+ * CAPI encoder/decoder for
+ * Portugal Telecom CAPI 2.0
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ *
+ * Not compatible with the AVM Gmbh. CAPI 2.0
+ *
+ */
+
+/*
+ *        Documentation:
+ *        - "Common ISDN API - Perfil Português - Versão 2.1",
+ *           Telecom Portugal, Fev 1992.
+ *        - "Common ISDN API - Especificação de protocolos para 
+ *           acesso aos canais B", Inesc, Jan 1994.
+ */
+
+/*
+ *        TODO: better decoding of Information Elements
+ *              for debug purposes mainly
+ *              encode our number in CallerPN and ConnectedPN
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/string.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "capi.h"
+
+
+/*
+ *  Encoding of CAPI messages
+ *
+ */
+
+int capi_conn_req(const char * calledPN, struct sk_buff **skb, int proto)
+{
+        ushort len;
+
+        /*
+         * length
+         *   AppInfoMask - 2
+         *   BC0         - 3
+         *   BC1         - 1
+         *   Chan        - 2
+         *   Keypad      - 1
+         *   CPN         - 1
+         *   CPSA        - 1
+         *   CalledPN    - 2 + strlen
+         *   CalledPSA   - 1
+         *   rest...     - 4
+         *   ----------------
+         *   Total        18 + strlen
+         */
+
+        len = 18 + strlen(calledPN);
+
+	if (proto == ISDN_PROTO_L2_TRANS)
+		len++;
+
+	if ((*skb = dev_alloc_skb(len)) == NULL) {
+    
+	        printk(KERN_WARNING "capi_conn_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        /* InfoElmMask */
+        *((ushort*) skb_put(*skb, 2)) = AppInfoMask; 
+
+	if (proto == ISDN_PROTO_L2_TRANS)
+	{
+		/* Bearer Capability - Mandatory*/
+		*(skb_put(*skb, 1)) = 3;        /* BC0.Length		*/
+		*(skb_put(*skb, 1)) = 0x80;     /* Speech		*/
+		*(skb_put(*skb, 1)) = 0x10;     /* Circuit Mode		*/
+		*(skb_put(*skb, 1)) = 0x23;     /* A-law		*/
+	}
+	else
+	{
+		/* Bearer Capability - Mandatory*/
+		*(skb_put(*skb, 1)) = 2;        /* BC0.Length		*/
+		*(skb_put(*skb, 1)) = 0x88;     /* Digital Information	*/
+		*(skb_put(*skb, 1)) = 0x90;     /* BC0.Octect4		*/
+	}
+
+        /* Bearer Capability - Optional*/
+        *(skb_put(*skb, 1)) = 0;        /* BC1.Length = 0                    */
+
+        *(skb_put(*skb, 1)) = 1;        /* ChannelID.Length = 1              */
+        *(skb_put(*skb, 1)) = 0x83;     /* Basic Interface - Any Channel     */
+
+        *(skb_put(*skb, 1)) = 0;        /* Keypad.Length = 0                 */
+                  
+
+        *(skb_put(*skb, 1)) = 0;        /* CallingPN.Length = 0              */
+        *(skb_put(*skb, 1)) = 0;        /* CallingPSA.Length = 0             */
+
+        /* Called Party Number */
+        *(skb_put(*skb, 1)) = strlen(calledPN) + 1;
+        *(skb_put(*skb, 1)) = 0x81;
+        memcpy(skb_put(*skb, strlen(calledPN)), calledPN, strlen(calledPN));
+
+        /* '#' */
+
+        *(skb_put(*skb, 1)) = 0;       /* CalledPSA.Length = 0     */
+
+        /* LLC.Length  = 0; */
+        /* HLC0.Length = 0; */
+        /* HLC1.Length = 0; */ 
+        /* UTUS.Length = 0; */
+        memset(skb_put(*skb, 4), 0, 4);
+
+        return len;
+}
+
+int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+        
+	if ((*skb = dev_alloc_skb(5)) == NULL) {
+    
+		printk(KERN_WARNING "capi_conn_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+        *(skb_put(*skb, 1)) = 0x01;  /* ACCEPT_CALL */
+        *(skb_put(*skb, 1)) = 0;
+        *(skb_put(*skb, 1)) = 0;
+
+        return 5;
+}
+
+int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+        /*
+         * 8 bytes
+         */
+        
+	if ((*skb = dev_alloc_skb(8)) == NULL) {
+    
+		printk(KERN_WARNING "capi_conn_active_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); 
+#endif
+
+        *(skb_put(*skb, 1)) = 0;       /*  BC.Length = 0;          */
+        *(skb_put(*skb, 1)) = 0;       /*  ConnectedPN.Length = 0  */
+        *(skb_put(*skb, 1)) = 0;       /*  PSA.Length              */
+        *(skb_put(*skb, 1)) = 0;       /*  LLC.Length = 0;         */
+        *(skb_put(*skb, 1)) = 0;       /*  HLC.Length = 0;         */
+        *(skb_put(*skb, 1)) = 0;       /*  UTUS.Length = 0;        */
+
+	return 8;
+}
+
+int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+        /*
+         * 2 bytes
+         */
+  
+	if ((*skb = dev_alloc_skb(2)) == NULL) {
+    
+		printk(KERN_WARNING "capi_conn_active_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        return 2;
+}
+
+
+int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, 
+                          int outgoing)
+{
+
+        /*
+         * 18 bytes
+         */
+
+	if ((*skb = dev_alloc_skb(18)) == NULL) {
+    
+		printk(KERN_WARNING "capi_select_proto_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        /* Layer2 protocol */
+
+        switch (chan->proto) {
+        case ISDN_PROTO_L2_X75I: 
+                *(skb_put(*skb, 1)) = 0x05;            /* LAPB */
+                break;
+        case ISDN_PROTO_L2_HDLC:
+                *(skb_put(*skb, 1)) = 0x02;
+                break;
+	case ISDN_PROTO_L2_TRANS:
+		/* 
+		 *	Voice (a-law)
+		 */
+		*(skb_put(*skb, 1)) = 0x06;
+		break;
+        default:
+#ifdef DEBUG 
+                printk(KERN_DEBUG "Transparent\n");
+#endif
+                *(skb_put(*skb, 1)) = 0x03;
+                break;
+        }
+
+        *(skb_put(*skb, 1)) = (outgoing ? 0x02 : 0x42);    /* Don't ask */
+        *(skb_put(*skb, 1)) = 0x00;
+  
+        *((ushort *) skb_put(*skb, 2)) = MRU;
+
+ 
+        *(skb_put(*skb, 1)) = 0x08;           /* Modulo */
+        *(skb_put(*skb, 1)) = 0x07;           /* Max Window */
+  
+        *(skb_put(*skb, 1)) = 0x01;           /* No Layer3 Protocol */
+
+        /*
+         * 2 - layer3 MTU       [10]
+         *   - Modulo           [12]
+         *   - Window           
+         *   - layer1 proto     [14]
+         *   - bitrate
+         *   - sub-channel      [16]
+         *   - layer1dataformat [17]
+         */
+
+        memset(skb_put(*skb, 8), 0, 8);
+
+        return 18;
+}
+
+
+int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+
+	if ((*skb = dev_alloc_skb(7)) == NULL) {
+    
+		printk(KERN_WARNING "capi_activate_transp_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        
+        *(skb_put(*skb, 1)) = chan->layer2link; /* Layer2 id */
+        *(skb_put(*skb, 1)) = 0x00;             /* Transmit by default */
+
+        *((ushort *) skb_put(*skb, 2)) = MRU;
+
+        *(skb_put(*skb, 1)) = 0x01;             /* Enables reception*/
+
+        return 7;
+}
+
+int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb)
+{
+	ushort data_len;
+	
+
+	/*  
+	 * callref      - 2  
+	 * layer2link   - 1
+	 * wBlockLength - 2 
+	 * data         - 4
+	 * sernum       - 1
+	 */
+	
+	data_len = skb->len;
+
+	if(skb_headroom(skb) < 10)
+	{
+		printk(KERN_CRIT "No headspace (%u) on headroom %p for capi header\n", skb_headroom(skb), skb);
+	}
+	else
+	{	
+		skb_push(skb, 10);
+	}
+
+	*((u16 *) (skb->data)) = chan->callref;
+	skb->data[2] = chan->layer2link;
+	*((u16 *) (skb->data + 3)) = data_len;
+
+	chan->s_refnum = (chan->s_refnum + 1) % 8;
+	*((u32 *) (skb->data + 5)) = chan->s_refnum;
+
+	skb->data[9] = 0;                           /* HDLC frame number */
+
+	return 10;
+}
+
+int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb)
+		    
+{
+	if ((*skb = dev_alloc_skb(4)) == NULL) {
+    
+		printk(KERN_WARNING "capi_tdata_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        *(skb_put(*skb, 1)) = chan->layer2link;
+        *(skb_put(*skb, 1)) = chan->r_refnum;
+
+        return (*skb)->len;
+}
+
+int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause)
+{
+
+	if ((*skb = dev_alloc_skb(6)) == NULL) {
+    
+		printk(KERN_WARNING "capi_disc_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = callref;  
+
+        *(skb_put(*skb, 1)) = 2;                  /* Cause.Length = 2; */
+        *(skb_put(*skb, 1)) = 0x80;
+        *(skb_put(*skb, 1)) = 0x80 | cause;           
+
+        /* 
+         * Change it: we should send 'Sic transit gloria Mundi' here ;-) 
+         */
+
+        *(skb_put(*skb, 1)) = 0;                   /* UTUS.Length = 0;  */
+
+        return 6;
+}
+
+int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+	if ((*skb = dev_alloc_skb(2)) == NULL) {
+    
+		printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2)) = chan->callref;  
+
+        return 2;
+}
+
+
+/*
+ *  Decoding of CAPI messages
+ *
+ */
+
+int capi_decode_conn_ind(struct pcbit_chan * chan, 
+                         struct sk_buff *skb,
+                         struct callb_data *info) 
+{
+        int CIlen, len;
+
+        /* Call Reference [CAPI] */
+        chan->callref = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); 
+#endif
+
+        /* Channel Identification */
+
+        /* Expect  
+           Len = 1 
+           Octect 3 = 0100 10CC - [ 7 Basic, 4 , 2-1 chan ]
+           */
+
+        CIlen = skb->data[0];
+#ifdef DEBUG
+        if (CIlen == 1) {
+
+                if ( ((skb->data[1]) & 0xFC) == 0x48 )
+                        printk(KERN_DEBUG "decode_conn_ind: chan ok\n");
+                printk(KERN_DEBUG "phyChan = %d\n", skb->data[1] & 0x03); 
+        }
+	else
+		printk(KERN_DEBUG "conn_ind: CIlen = %d\n", CIlen);
+#endif
+        skb_pull(skb, CIlen + 1);
+
+        /* Calling Party Number */
+        /* An "additional service" as far as Portugal Telecom is concerned */
+
+        len = skb->data[0];
+
+	if (len > 0) {
+		int count = 1;
+		
+#ifdef DEBUG
+		printk(KERN_DEBUG "CPN: Octect 3 %02x\n", skb->data[1]);
+#endif
+		if ((skb->data[1] & 0x80) == 0)
+			count = 2;
+		
+		if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+			return -1;
+       
+		memcpy(info->data.setup.CallingPN, skb->data + count + 1, 
+		       len - count);
+		info->data.setup.CallingPN[len - count] = 0;
+
+	}
+	else {
+		info->data.setup.CallingPN = NULL;
+		printk(KERN_DEBUG "NULL CallingPN\n");
+	}
+
+	skb_pull(skb, len + 1);
+
+        /* Calling Party Subaddress */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* Called Party Number */
+
+        len = skb->data[0];
+
+	if (len > 0) {
+		int count = 1;
+		
+		if ((skb->data[1] & 0x80) == 0)
+			count = 2;
+        
+		if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+			return -1;
+        
+		memcpy(info->data.setup.CalledPN, skb->data + count + 1, 
+		       len - count); 
+		info->data.setup.CalledPN[len - count] = 0;
+
+	}
+	else {
+		info->data.setup.CalledPN = NULL;
+		printk(KERN_DEBUG "NULL CalledPN\n");
+	}
+
+	skb_pull(skb, len + 1);
+
+        /* Called Party Subaddress */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* LLC */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* HLC */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* U2U */
+        skb_pull(skb, skb->data[0] + 1);
+
+        return 0;
+}
+
+/*
+ *  returns errcode
+ */
+
+int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+			  int *complete) 
+{
+        int errcode;
+  
+        chan->callref = *((ushort *) skb->data);     /* Update CallReference */
+        skb_pull(skb, 2);
+
+        errcode = *((ushort *) skb->data);   /* read errcode */
+        skb_pull(skb, 2);
+
+        *complete = *(skb->data);
+        skb_pull(skb, 1);
+
+        /* FIX ME */
+        /* This is actually a firmware bug */
+        if (!*complete)
+        {
+                printk(KERN_DEBUG "complete=%02x\n", *complete);
+                *complete = 1;
+        }
+
+
+        /* Optional Bearer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+        
+        /* Channel Identification */
+        skb_pull(skb, *(skb->data) + 1);
+
+        /* High Layer Compatibility follows */
+        skb_pull(skb, *(skb->data) + 1);
+
+        return errcode;
+}
+
+int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+        ushort len;
+#ifdef DEBUG
+        char str[32];
+#endif
+
+        /* Yet Another Bearer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+  
+
+        /* Connected Party Number */
+        len=*(skb->data);
+
+#ifdef DEBUG
+	if (len > 1 && len < 31) {
+		memcpy(str, skb->data + 2, len - 1);
+		str[len] = 0;
+		printk(KERN_DEBUG "Connected Party Number: %s\n", str);
+	}
+	else
+		printk(KERN_DEBUG "actv_ind CPN len = %d\n", len);
+#endif
+
+        skb_pull(skb, len + 1);
+
+        /* Connected Subaddress */
+        skb_pull(skb, *(skb->data) + 1);
+
+        /* Low Layer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+
+        /* High Layer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+
+        return 0;
+}
+
+int capi_decode_conn_actv_conf(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+        ushort errcode;
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+        
+        /* Channel Identification 
+        skb_pull(skb, skb->data[0] + 1);
+        */
+        return errcode;
+}
+
+
+int capi_decode_sel_proto_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort errcode;
+        
+        chan->layer2link = *(skb->data);
+        skb_pull(skb, 1);
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+        return errcode;
+}
+
+int capi_decode_actv_trans_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort errcode;
+
+        if (chan->layer2link != *(skb->data) )
+                printk("capi_decode_actv_trans_conf: layer2link doesn't match\n");
+
+        skb_pull(skb, 1);
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+        return errcode;        
+}
+
+int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort len;
+#ifdef DEBUG
+        int i;
+#endif
+        /* Cause */
+        
+        len = *(skb->data);
+        skb_pull(skb, 1);
+
+#ifdef DEBUG
+
+        for (i=0; i<len; i++)
+                printk(KERN_DEBUG "Cause Octect %d: %02x\n", i+3, 
+                       *(skb->data + i));
+#endif
+
+        skb_pull(skb, len);
+
+        return 0;
+}
+
+int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort errcode;
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+        return errcode;                
+}
+
+#ifdef DEBUG
+int capi_decode_debug_188(u_char *hdr, ushort hdrlen)
+{
+        char str[64];
+        int len;
+        
+        len = hdr[0];
+
+        if (len < 64 && len == hdrlen - 1) {        
+                memcpy(str, hdr + 1, hdrlen - 1);
+                str[hdrlen - 1] = 0;
+                printk("%s\n", str);
+        }
+        else
+                printk("debug message incorrect\n");
+
+        return 0;
+}
+#endif
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/capi.h b/drivers/isdn/pcbit/capi.h
new file mode 100644
index 0000000..18e6aa3
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.h
@@ -0,0 +1,88 @@
+/*
+ * CAPI encode/decode prototypes and defines
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef CAPI_H
+#define CAPI_H
+
+
+#define REQ_CAUSE         0x01
+#define REQ_DISPLAY       0x04
+#define REQ_USER_TO_USER  0x08 
+
+#define AppInfoMask  REQ_CAUSE|REQ_DISPLAY|REQ_USER_TO_USER 
+
+/* Connection Setup */
+extern int capi_conn_req(const char * calledPN, struct sk_buff **buf,
+			 int proto);
+extern int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+				 int *complete); 
+
+extern int capi_decode_conn_ind(struct pcbit_chan * chan, struct sk_buff *skb,
+				struct callb_data *info);
+extern int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb);
+
+extern int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb);
+extern int capi_decode_conn_actv_conf(struct pcbit_chan * chan, 
+				      struct sk_buff *skb);
+
+extern int capi_decode_conn_actv_ind(struct pcbit_chan * chan, 
+				     struct sk_buff *skb);
+extern int capi_conn_active_resp(struct pcbit_chan* chan, 
+				 struct sk_buff **skb);
+
+/* Data */
+extern int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
+				 int outgoing);
+extern int capi_decode_sel_proto_conf(struct pcbit_chan *chan, 
+				      struct sk_buff *skb);
+
+extern int capi_activate_transp_req(struct pcbit_chan *chan, 
+				    struct sk_buff **skb);
+extern int capi_decode_actv_trans_conf(struct pcbit_chan *chan, 
+				       struct sk_buff *skb);
+
+extern int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb);
+extern int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb);
+
+/* Connection Termination */
+extern int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause);
+extern int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb);
+
+extern int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb);
+extern int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb);
+
+#ifdef DEBUG
+extern int capi_decode_debug_188(u_char *hdr, ushort hdrlen);
+#endif
+
+static inline struct pcbit_chan * 
+capi_channel(struct pcbit_dev *dev, struct sk_buff *skb)
+{
+	ushort callref;
+
+	callref = *((ushort*) skb->data);
+	skb_pull(skb, 2);
+
+	if (dev->b1->callref == callref)
+		return dev->b1;
+	else if (dev->b2->callref == callref)
+		return dev->b2;
+
+	return NULL;
+}
+
+#endif
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c
new file mode 100644
index 0000000..e98f9c4
--- /dev/null
+++ b/drivers/isdn/pcbit/drv.c
@@ -0,0 +1,1088 @@
+/*
+ * PCBIT-D interface with isdn4linux
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ *	Fixes:
+ *
+ *	Nuno Grilo	<l38486@alfa.ist.utl.pt>
+ *      fixed msn_list NULL pointer dereference.
+ *		
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include <asm/string.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "capi.h"
+
+
+extern ushort last_ref_num;
+
+static int pcbit_ioctl(isdn_ctrl* ctl);
+
+static char* pcbit_devname[MAX_PCBIT_CARDS] = {
+	"pcbit0",
+	"pcbit1",
+	"pcbit2",
+	"pcbit3"
+};
+
+/*
+ * prototypes
+ */
+
+int pcbit_command(isdn_ctrl* ctl);
+int pcbit_stat(u_char __user * buf, int len, int, int);
+int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb);
+int pcbit_writecmd(const u_char __user *, int, int, int);
+
+static int set_protocol_running(struct pcbit_dev * dev);
+
+static void pcbit_clear_msn(struct pcbit_dev *dev);
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list);
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn);
+
+
+extern void pcbit_deliver(void * data);
+
+int pcbit_init_dev(int board, int mem_base, int irq)
+{
+	struct pcbit_dev *dev;
+	isdn_if *dev_if;
+
+	if ((dev=kmalloc(sizeof(struct pcbit_dev), GFP_KERNEL)) == NULL)
+	{
+		printk("pcbit_init: couldn't malloc pcbit_dev struct\n");
+		return -ENOMEM;
+	}
+
+	dev_pcbit[board] = dev;
+	memset(dev, 0, sizeof(struct pcbit_dev));
+	init_waitqueue_head(&dev->set_running_wq);
+	spin_lock_init(&dev->lock);
+
+	if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) {
+		dev->ph_mem = mem_base;
+		if (!request_mem_region(dev->ph_mem, 4096, "PCBIT mem")) {
+			printk(KERN_WARNING
+				"PCBIT: memory region %lx-%lx already in use\n",
+				dev->ph_mem, dev->ph_mem + 4096);
+			kfree(dev);
+			dev_pcbit[board] = NULL;
+			return -EACCES;
+		}
+		dev->sh_mem = ioremap(dev->ph_mem, 4096);
+	}
+	else 
+	{
+		printk("memory address invalid");
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EACCES;
+	}
+
+	dev->b1 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+	if (!dev->b1) {
+		printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		return -ENOMEM;
+	}
+    
+	dev->b2 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+	if (!dev->b2) {
+		printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+		kfree(dev->b1);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	memset(dev->b1, 0, sizeof(struct pcbit_chan));
+	memset(dev->b2, 0, sizeof(struct pcbit_chan));
+	dev->b2->id = 1;
+
+	INIT_WORK(&dev->qdelivery, pcbit_deliver, dev);
+
+	/*
+	 *  interrupts
+	 */
+
+	if (request_irq(irq, &pcbit_irq_handler, 0, pcbit_devname[board], dev) != 0) 
+	{
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EIO;
+	}
+
+	dev->irq = irq;
+
+	/* next frame to be received */
+	dev->rcv_seq = 0;
+	dev->send_seq = 0;
+	dev->unack_seq = 0;
+
+	dev->hl_hdrlen = 16;
+
+	dev_if = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+
+	if (!dev_if) {
+		free_irq(irq, dev);
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EIO;
+	}
+
+	dev->dev_if = dev_if;
+
+	dev_if->owner = THIS_MODULE;
+
+	dev_if->channels = 2;
+	
+	dev_if->features = (ISDN_FEATURE_P_EURO  | ISDN_FEATURE_L3_TRANS | 
+			    ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS );
+
+	dev_if->writebuf_skb = pcbit_xmit;
+	dev_if->hl_hdrlen = 16;
+
+	dev_if->maxbufsize = MAXBUFSIZE;
+	dev_if->command  = pcbit_command;
+	
+	dev_if->writecmd = pcbit_writecmd;
+	dev_if->readstat = pcbit_stat;
+
+
+	strcpy(dev_if->id, pcbit_devname[board]);
+
+	if (!register_isdn(dev_if)) {
+		free_irq(irq, dev);
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EIO;
+	}
+
+	dev->id = dev_if->channels;
+
+
+	dev->l2_state = L2_DOWN;
+	dev->free = 511;
+
+	/*
+	 * set_protocol_running(dev);
+	 */
+
+	return 0;
+}
+
+#ifdef MODULE
+void pcbit_terminate(int board)
+{
+	struct pcbit_dev * dev;
+
+	dev = dev_pcbit[board];
+
+	if (dev) {
+	     /* unregister_isdn(dev->dev_if); */
+		free_irq(dev->irq, dev);
+		pcbit_clear_msn(dev);
+		kfree(dev->dev_if);
+		if (dev->b1->fsm_timer.function)
+			del_timer(&dev->b1->fsm_timer);
+		if (dev->b2->fsm_timer.function)
+			del_timer(&dev->b2->fsm_timer);
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+	}
+}
+#endif
+
+int pcbit_command(isdn_ctrl* ctl)
+{
+	struct pcbit_dev  *dev;
+	struct pcbit_chan *chan;
+	struct callb_data info;
+
+	dev = finddev(ctl->driver);
+
+	if (!dev)
+	{
+		printk("pcbit_command: unknown device\n");
+		return -1;
+	}
+
+	chan = (ctl->arg & 0x0F) ? dev->b2 : dev->b1;
+
+
+	switch(ctl->command) {
+	case ISDN_CMD_IOCTL:
+		return pcbit_ioctl(ctl);
+		break;
+	case ISDN_CMD_DIAL:
+		info.type = EV_USR_SETUP_REQ;
+		info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone;
+		pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info);
+		break;
+	case ISDN_CMD_ACCEPTD:
+		pcbit_fsm_event(dev, chan, EV_USR_SETUP_RESP, NULL);
+		break;
+	case ISDN_CMD_ACCEPTB:
+		printk("ISDN_CMD_ACCEPTB - not really needed\n");
+		break;
+	case ISDN_CMD_HANGUP:
+		pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+		break;
+	case ISDN_CMD_SETL2:
+		chan->proto = (ctl->arg >> 8);
+		break;
+	case ISDN_CMD_CLREAZ:
+		pcbit_clear_msn(dev);
+		break;
+	case ISDN_CMD_SETEAZ:
+		pcbit_set_msn(dev, ctl->parm.num);
+		break;
+	case ISDN_CMD_SETL3:
+		if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS)
+			printk(KERN_DEBUG "L3 protocol unknown\n");
+		break;
+	default:
+		printk(KERN_DEBUG "pcbit_command: unknown command\n");
+		break;
+	};
+
+	return 0;
+}
+
+/*
+ * Another Hack :-(
+ * on some conditions the board stops sending TDATA_CONFs
+ * let's see if we can turn around the problem
+ */
+
+#ifdef BLOCK_TIMER
+static void pcbit_block_timer(unsigned long data)
+{
+	struct pcbit_chan *chan;
+	struct pcbit_dev * dev;
+	isdn_ctrl ictl;
+
+	chan = (struct pcbit_chan *) data;
+
+	dev = chan2dev(chan);
+
+	if (dev == NULL) {
+		printk(KERN_DEBUG "pcbit: chan2dev failed\n");
+		return;
+	}
+
+	del_timer(&chan->block_timer);
+	chan->block_timer.function = NULL;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "pcbit_block_timer\n");
+#endif	
+	chan->queued = 0;
+	ictl.driver = dev->id;
+	ictl.command = ISDN_STAT_BSENT;
+	ictl.arg = chan->id;
+	dev->dev_if->statcallb(&ictl);     
+}
+#endif
+
+int pcbit_xmit(int driver, int chnum, int ack, struct sk_buff *skb)
+{
+	ushort hdrlen;
+	int refnum, len;
+	struct pcbit_chan * chan;
+	struct pcbit_dev *dev;
+
+	dev = finddev(driver);
+	if (dev == NULL)
+	{
+		printk("finddev returned NULL");
+		return -1;
+	}
+
+	chan = chnum ? dev->b2 : dev->b1;
+
+
+	if (chan->fsm_state != ST_ACTIVE)
+		return -1;
+
+	if (chan->queued >= MAX_QUEUED )
+	{
+#ifdef DEBUG_QUEUE
+		printk(KERN_DEBUG 
+		       "pcbit: %d packets already in queue - write fails\n",
+		       chan->queued);
+#endif
+		/*
+		 * packet stays on the head of the device queue
+		 * since dev_start_xmit will fail
+		 * see net/core/dev.c
+		 */
+#ifdef BLOCK_TIMER
+		if (chan->block_timer.function == NULL) {
+			init_timer(&chan->block_timer);
+			chan->block_timer.function =  &pcbit_block_timer;
+			chan->block_timer.data = (long) chan;
+			chan->block_timer.expires = jiffies + 1 * HZ;
+			add_timer(&chan->block_timer);
+		}
+#endif		
+		return 0;	                 
+	}
+
+
+	chan->queued++;
+	
+        len = skb->len;
+
+	hdrlen = capi_tdata_req(chan, skb);
+
+	refnum = last_ref_num++ & 0x7fffU;
+	chan->s_refnum = refnum;
+
+	pcbit_l2_write(dev, MSG_TDATA_REQ, refnum, skb, hdrlen);
+
+	return len;
+}
+
+int pcbit_writecmd(const u_char __user *buf, int len, int driver, int channel)
+{
+	struct pcbit_dev * dev;
+	int i, j;
+	const u_char * loadbuf;
+	u_char * ptr = NULL;
+	u_char *cbuf;
+
+	int errstat;
+
+	dev = finddev(driver);
+
+	if (!dev)
+	{
+		printk("pcbit_writecmd: couldn't find device");
+		return -ENODEV;
+	}
+
+	switch(dev->l2_state) {
+	case L2_LWMODE:
+		/* check (size <= rdp_size); write buf into board */
+		if (len < 0 || len > BANK4 + 1 || len > 1024)
+		{
+			printk("pcbit_writecmd: invalid length %d\n", len);
+			return -EINVAL;
+		}
+
+		cbuf = kmalloc(len, GFP_KERNEL);
+		if (!cbuf)
+			return -ENOMEM;
+
+		if (copy_from_user(cbuf, buf, len)) {
+			kfree(cbuf);
+			return -EFAULT;
+		}
+		memcpy_toio(dev->sh_mem, cbuf, len);
+		kfree(cbuf);
+		return len;
+	case L2_FWMODE:
+		/* this is the hard part */
+		/* dumb board */
+		/* get it into kernel space */
+		if ((ptr = kmalloc(len, GFP_KERNEL))==NULL)
+			return -ENOMEM;
+		if (copy_from_user(ptr, buf, len)) {
+			kfree(ptr);
+			return -EFAULT;
+		}
+		loadbuf = ptr;
+    
+		errstat = 0;
+
+		for (i=0; i < len; i++)
+		{
+			for(j=0; j < LOAD_RETRY; j++)
+				if (!(readb(dev->sh_mem + dev->loadptr)))
+					break;
+
+			if (j == LOAD_RETRY)
+			{
+				errstat = -ETIME;
+				printk("TIMEOUT i=%d\n", i);
+				break;
+			}
+			writeb(loadbuf[i], dev->sh_mem + dev->loadptr + 1);
+			writeb(0x01, dev->sh_mem + dev->loadptr);
+
+			dev->loadptr += 2;
+			if (dev->loadptr > LOAD_ZONE_END)
+				dev->loadptr = LOAD_ZONE_START;
+		}
+		kfree(ptr);
+
+		return errstat ? errstat : len;
+	default:
+		return -EBUSY;
+	}
+}
+
+/*
+ *  demultiplexing of messages
+ *
+ */
+
+void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, 
+			     struct sk_buff * skb,
+			     ushort hdr_len, ushort refnum)
+{
+	struct pcbit_chan *chan;
+	struct sk_buff *skb2;
+	unsigned short len;
+	struct callb_data cbdata;
+	int complete, err;
+	isdn_ctrl ictl;
+
+	switch(msg) {
+
+	case MSG_TDATA_IND:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+		chan->r_refnum = skb->data[7];
+		skb_pull(skb, 8);
+
+		dev->dev_if->rcvcallb_skb(dev->id, chan->id, skb);
+
+		if (capi_tdata_resp(chan, &skb2) > 0) 
+			pcbit_l2_write(dev, MSG_TDATA_RESP, refnum, 
+				       skb2, skb2->len);
+		return;
+		break;  
+	case MSG_TDATA_CONF:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+#ifdef DEBUG
+		if ( (*((ushort *) (skb->data + 2) )) != 0) {
+                        printk(KERN_DEBUG "TDATA_CONF error\n");
+		}
+#endif
+#ifdef BLOCK_TIMER
+                if (chan->queued == MAX_QUEUED) {
+                        del_timer(&chan->block_timer);
+			chan->block_timer.function = NULL;
+		}
+                
+#endif		
+		chan->queued--;
+
+		ictl.driver = dev->id;
+		ictl.command = ISDN_STAT_BSENT;
+		ictl.arg = chan->id;
+		dev->dev_if->statcallb(&ictl);
+		break;
+
+	case MSG_CONN_IND:
+		/*
+		 *  channel: 1st not used will do
+		 *           if both are used we're in trouble 
+		 */
+
+		if (!dev->b1->fsm_state)
+			chan = dev->b1;
+		else if (!dev->b2->fsm_state)
+			chan = dev->b2;
+		else {
+			printk(KERN_INFO 
+			       "Incoming connection: no channels available");
+
+			if ((len = capi_disc_req(*(ushort*)(skb->data), &skb2, CAUSE_NOCHAN)) > 0)
+				pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb2, len);
+			break;  
+		}
+
+		cbdata.data.setup.CalledPN = NULL;
+		cbdata.data.setup.CallingPN = NULL;
+
+		capi_decode_conn_ind(chan, skb, &cbdata);
+		cbdata.type = EV_NET_SETUP;
+
+		pcbit_fsm_event(dev, chan, EV_NET_SETUP, NULL);
+
+		if (pcbit_check_msn(dev, cbdata.data.setup.CallingPN)) 
+			pcbit_fsm_event(dev, chan, EV_USR_PROCED_REQ, &cbdata);
+		else
+			pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+
+		if (cbdata.data.setup.CalledPN)
+			kfree(cbdata.data.setup.CalledPN);
+		if (cbdata.data.setup.CallingPN)
+			kfree(cbdata.data.setup.CallingPN);
+		break;
+    
+	case MSG_CONN_CONF:
+		/* 
+		 * We should be able to find the channel by the message
+		 * reference number. The current version of the firmware
+		 * doesn't sent the ref number correctly.
+		 */
+#ifdef DEBUG
+		printk(KERN_DEBUG "refnum=%04x b1=%04x b2=%04x\n", refnum, 
+		       dev->b1->s_refnum, 
+		       dev->b2->s_refnum);
+#endif
+		/* We just try to find a channel in the right state */
+
+		if (dev->b1->fsm_state == ST_CALL_INIT)
+			chan = dev->b1;
+		else { 		   
+			if (dev->b2->s_refnum == ST_CALL_INIT)
+				chan = dev->b2;
+			else {			
+				chan = NULL;
+				printk(KERN_WARNING "Connection Confirm - no channel in Call Init state\n");
+				break;
+			}
+		}
+		if (capi_decode_conn_conf(chan, skb, &complete)) {
+			printk(KERN_DEBUG "conn_conf indicates error\n");
+			pcbit_fsm_event(dev, chan, EV_ERROR, NULL);
+		}
+		else
+			if (complete)
+				pcbit_fsm_event(dev, chan, EV_NET_CALL_PROC, NULL);
+			else
+				pcbit_fsm_event(dev, chan, EV_NET_SETUP_ACK, NULL);
+		break; 
+	case MSG_CONN_ACTV_IND:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+		
+		if (capi_decode_conn_actv_ind(chan, skb)) {
+			printk("error in capi_decode_conn_actv_ind\n");
+		     /* pcbit_fsm_event(dev, chan, EV_ERROR, NULL); */
+			break;
+		}
+		chan->r_refnum = refnum;
+		pcbit_fsm_event(dev, chan, EV_NET_CONN, NULL);
+		break;
+	case MSG_CONN_ACTV_CONF:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (capi_decode_conn_actv_conf(chan, skb) == 0)
+			pcbit_fsm_event(dev, chan, EV_NET_CONN_ACK, NULL);
+		
+		else
+			printk(KERN_DEBUG "decode_conn_actv_conf failed\n");
+		break;
+
+	case  MSG_SELP_CONF:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!(err = capi_decode_sel_proto_conf(chan, skb)))
+			pcbit_fsm_event(dev, chan, EV_NET_SELP_RESP, NULL);
+		else {
+			/* Error */
+			printk("error %d - capi_decode_sel_proto_conf\n", err);
+		}
+		break;
+	case MSG_ACT_TRANSP_CONF:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!capi_decode_actv_trans_conf(chan, skb))
+			pcbit_fsm_event(dev, chan, EV_NET_ACTV_RESP, NULL);
+		break;
+
+	case MSG_DISC_IND:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!capi_decode_disc_ind(chan, skb))
+			pcbit_fsm_event(dev, chan, EV_NET_DISC, NULL);
+		else
+			printk(KERN_WARNING "capi_decode_disc_ind - error\n");
+		break;
+	case MSG_DISC_CONF:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!capi_decode_disc_ind(chan, skb))
+			pcbit_fsm_event(dev, chan, EV_NET_RELEASE, NULL);
+		else
+			printk(KERN_WARNING "capi_decode_disc_conf - error\n");
+		break;
+	case MSG_INFO_IND:
+#ifdef DEBUG
+		printk(KERN_DEBUG "received Info Indication - discarded\n");
+#endif
+		break;
+#ifdef DEBUG
+	case MSG_DEBUG_188:
+		capi_decode_debug_188(skb->data, skb->len);
+		break;
+
+	default:
+		printk(KERN_DEBUG "pcbit_l3_receive: unknown message %08lx\n",
+		       msg);
+		break;
+#endif
+	}
+
+	kfree_skb(skb);
+
+}
+
+/*
+ *   Single statbuf
+ *   should be a statbuf per device
+ */
+
+static char statbuf[STATBUF_LEN];
+static int stat_st = 0;
+static int stat_end = 0;
+
+int pcbit_stat(u_char __user *buf, int len, int driver, int channel)
+{
+	int stat_count;
+	stat_count = stat_end - stat_st;
+
+	if (stat_count < 0)
+		stat_count = STATBUF_LEN - stat_st + stat_end;
+
+	/* FIXME: should we sleep and wait for more cookies ? */
+	if (len > stat_count)            
+		len = stat_count;
+
+	if (stat_st < stat_end)
+	{
+		copy_to_user(buf, statbuf + stat_st, len);
+		stat_st += len;	   
+	}
+	else
+	{
+		if (len > STATBUF_LEN - stat_st)
+		{
+			copy_to_user(buf, statbuf + stat_st, 
+				       STATBUF_LEN - stat_st);
+			copy_to_user(buf, statbuf, 
+				       len - (STATBUF_LEN - stat_st));
+
+			stat_st = len - (STATBUF_LEN - stat_st);
+		}
+		else
+		{
+			copy_to_user(buf, statbuf + stat_st, len);
+
+			stat_st += len;
+			
+			if (stat_st == STATBUF_LEN)
+				stat_st = 0;
+		}
+	}
+
+	if (stat_st == stat_end)
+		stat_st = stat_end = 0;
+
+	return len;
+}
+
+static void pcbit_logstat(struct pcbit_dev *dev, char *str)
+{
+	int i;
+	isdn_ctrl ictl;
+
+	for (i=stat_end; i<strlen(str); i++)
+	{
+		statbuf[i]=str[i];
+		stat_end = (stat_end + 1) % STATBUF_LEN;
+		if (stat_end == stat_st)
+			stat_st = (stat_st + 1) % STATBUF_LEN;
+	}
+
+	ictl.command=ISDN_STAT_STAVAIL;
+	ictl.driver=dev->id;
+	ictl.arg=strlen(str);
+	dev->dev_if->statcallb(&ictl);
+}
+	
+extern char * isdn_state_table[];
+extern char * strisdnevent(unsigned short);
+
+
+void pcbit_state_change(struct pcbit_dev * dev, struct pcbit_chan * chan, 
+			unsigned short i, unsigned short ev, unsigned short f)
+{
+	char buf[256];
+  
+	sprintf(buf, "change on device: %d channel:%d\n%s -> %s -> %s\n",
+		dev->id, chan->id, 
+		isdn_state_table[i], strisdnevent(ev), isdn_state_table[f]
+		);
+
+#ifdef DEBUG
+	printk("%s", buf);
+#endif
+
+	pcbit_logstat(dev, buf);
+}
+
+static void set_running_timeout(unsigned long ptr)
+{
+	struct pcbit_dev * dev;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "set_running_timeout\n");
+#endif
+	dev = (struct pcbit_dev *) ptr;
+
+	wake_up_interruptible(&dev->set_running_wq);
+}
+
+static int set_protocol_running(struct pcbit_dev * dev)
+{
+	isdn_ctrl ctl;
+
+	init_timer(&dev->set_running_timer);
+
+	dev->set_running_timer.function = &set_running_timeout;
+	dev->set_running_timer.data = (ulong) dev;
+	dev->set_running_timer.expires = jiffies + SET_RUN_TIMEOUT;
+
+	/* kick it */
+
+	dev->l2_state = L2_STARTING;
+
+	writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)), 
+	       dev->sh_mem + BANK4);
+
+	add_timer(&dev->set_running_timer);
+
+	interruptible_sleep_on(&dev->set_running_wq);
+
+	del_timer(&dev->set_running_timer);
+
+	if (dev->l2_state == L2_RUNNING)
+	{
+		printk(KERN_DEBUG "pcbit: running\n");
+
+		dev->unack_seq = dev->send_seq;
+
+		dev->writeptr = dev->sh_mem;
+		dev->readptr = dev->sh_mem + BANK2;
+    
+		/* tell the good news to the upper layer */  
+		ctl.driver = dev->id;
+		ctl.command = ISDN_STAT_RUN;
+
+		dev->dev_if->statcallb(&ctl);
+	}
+	else
+	{
+		printk(KERN_DEBUG "pcbit: initialization failed\n");
+		printk(KERN_DEBUG "pcbit: firmware not loaded\n");
+
+		dev->l2_state = L2_DOWN;
+
+#ifdef DEBUG
+		printk(KERN_DEBUG "Bank3 = %02x\n", 
+		       readb(dev->sh_mem + BANK3));
+#endif
+		writeb(0x40, dev->sh_mem + BANK4);
+
+		/* warn the upper layer */
+		ctl.driver = dev->id;
+		ctl.command = ISDN_STAT_STOP;
+
+		dev->dev_if->statcallb(&ctl);
+
+		return -EL2HLT;	/* Level 2 halted */
+	}
+
+	return 0;
+}
+
+static int pcbit_ioctl(isdn_ctrl* ctl)
+{
+	struct pcbit_dev * dev;
+	struct pcbit_ioctl *cmd;
+
+	dev = finddev(ctl->driver);
+  
+	if (!dev)
+	{
+		printk(KERN_DEBUG "pcbit_ioctl: unknown device\n");
+		return -ENODEV;
+	}
+
+	cmd = (struct pcbit_ioctl *) ctl->parm.num;
+
+	switch(ctl->arg) {
+	case PCBIT_IOCTL_GETSTAT:
+		cmd->info.l2_status = dev->l2_state;
+		break;
+
+	case PCBIT_IOCTL_STRLOAD:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+
+		dev->unack_seq = dev->send_seq = dev->rcv_seq = 0;
+
+		dev->writeptr = dev->sh_mem;
+		dev->readptr = dev->sh_mem + BANK2;
+    
+		dev->l2_state = L2_LOADING;
+		break;
+
+	case PCBIT_IOCTL_LWMODE:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+
+		dev->l2_state = L2_LWMODE;
+		break;
+
+	case PCBIT_IOCTL_FWMODE:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+		dev->loadptr = LOAD_ZONE_START;
+		dev->l2_state = L2_FWMODE;
+
+		break; 
+	case PCBIT_IOCTL_ENDLOAD:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+		dev->l2_state = L2_DOWN;
+		break; 
+
+	case PCBIT_IOCTL_SETBYTE: 
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+
+		/* check addr */
+		if (cmd->info.rdp_byte.addr > BANK4)
+			return -EFAULT;
+		
+		writeb(cmd->info.rdp_byte.value, dev->sh_mem + cmd->info.rdp_byte.addr);
+		break;
+	case PCBIT_IOCTL_GETBYTE:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+
+		/* check addr */
+
+		if (cmd->info.rdp_byte.addr > BANK4)
+		{
+			printk("getbyte: invalid addr %04x\n", cmd->info.rdp_byte.addr);
+			return -EFAULT;
+		}
+		
+		cmd->info.rdp_byte.value = readb(dev->sh_mem + cmd->info.rdp_byte.addr); 
+		break;
+	case PCBIT_IOCTL_RUNNING: 
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+		return set_protocol_running(dev);
+		break;
+	case PCBIT_IOCTL_WATCH188:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+		pcbit_l2_write(dev, MSG_WATCH188, 0x0001, NULL, 0);
+		break;
+	case PCBIT_IOCTL_PING188:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+		pcbit_l2_write(dev, MSG_PING188_REQ, 0x0001, NULL, 0);
+		break;
+	case PCBIT_IOCTL_APION:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+		pcbit_l2_write(dev, MSG_API_ON, 0x0001, NULL, 0);
+		break;
+	case PCBIT_IOCTL_STOP:
+		dev->l2_state = L2_DOWN;
+		writeb(0x40, dev->sh_mem + BANK4);
+		dev->rcv_seq = 0;
+		dev->send_seq = 0;
+		dev->unack_seq = 0;
+		break;
+	default:
+		printk("error: unknown ioctl\n");
+		break;
+	};
+	return 0;
+}
+
+/* 
+ *        MSN list handling
+ *
+ *        if null reject all calls
+ *        if first entry has null MSN accept all calls 
+ */
+
+static void pcbit_clear_msn(struct pcbit_dev *dev)
+{
+	struct msn_entry *ptr, *back;
+
+	for (ptr=dev->msn_list; ptr; )
+	{
+		back = ptr->next;
+		kfree(ptr);
+		ptr = back;
+	}
+
+	dev->msn_list = NULL; 
+}
+
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list)
+{
+	struct msn_entry *ptr;
+	struct msn_entry *back = NULL;
+	char *cp, *sp;
+	int len;
+
+	if (strlen(list) == 0) {
+		ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+		if (!ptr) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+
+		ptr->msn = NULL;
+
+		ptr->next = dev->msn_list;
+		dev->msn_list = ptr;
+
+		return;
+	}
+
+	if (dev->msn_list)
+		for (back=dev->msn_list; back->next; back=back->next);
+	
+	sp = list;
+
+	do {
+		cp=strchr(sp, ',');
+		if (cp)
+			len = cp - sp;
+		else
+			len = strlen(sp);
+
+		ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+
+		if (!ptr) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+		ptr->next = NULL;
+		
+		ptr->msn = kmalloc(len, GFP_ATOMIC);
+		if (!ptr->msn) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			kfree(ptr);
+			return;
+		}
+
+		memcpy(ptr->msn, sp, len - 1);
+		ptr->msn[len] = 0;
+
+#ifdef DEBUG
+		printk(KERN_DEBUG "msn: %s\n", ptr->msn);
+#endif
+		if (dev->msn_list == NULL)
+			dev->msn_list = ptr;
+		else
+			back->next = ptr;
+		back = ptr;
+		sp += len;
+	} while(cp);
+}
+
+/*
+ *  check if we do signal or reject an incoming call
+ */
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn)
+{
+	struct msn_entry *ptr;
+	
+	for (ptr=dev->msn_list; ptr; ptr=ptr->next) {
+
+		if (ptr->msn == NULL) 
+			return 1;
+		
+		if (strcmp(ptr->msn, msn) == 0)
+			return 1;
+	}
+
+	return 0;
+}
diff --git a/drivers/isdn/pcbit/edss1.c b/drivers/isdn/pcbit/edss1.c
new file mode 100644
index 0000000..93ca7de
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.c
@@ -0,0 +1,325 @@
+/*
+ * DSS.1 Finite State Machine
+ * base: ITU-T Rec Q.931
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ *        TODO: complete the FSM
+ *              move state/event descriptions to a user space logger
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <linux/timer.h>
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "callbacks.h"
+
+
+extern void pcbit_state_change(struct pcbit_dev *, struct pcbit_chan *, 
+                               unsigned short i, unsigned short ev, 
+                               unsigned short f);
+
+extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+char * isdn_state_table[] = {
+  "Closed",
+  "Call initiated",
+  "Overlap sending",
+  "Outgoing call proceeding",
+  "NOT DEFINED",
+  "Call delivered",
+  "Call present",
+  "Call received",
+  "Connect request",
+  "Incoming call proceeding",
+  "Active",
+  "Disconnect request",
+  "Disconnect indication",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "Suspend request",
+  "NOT DEFINED",
+  "Resume request",
+  "NOT DEFINED",
+  "Release Request",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "Overlap receiving",
+  "Select protocol on B-Channel",
+  "Activate B-channel protocol"
+};
+
+#ifdef DEBUG_ERRS
+static
+struct CauseValue {
+  byte nr;
+  char *descr;
+} cvlist[]={
+  {0x01,"Unallocated (unassigned) number"},
+  {0x02,"No route to specified transit network"},
+  {0x03,"No route to destination"},
+  {0x04,"Send special information tone"},
+  {0x05,"Misdialled trunk prefix"},
+  {0x06,"Channel unacceptable"},
+  {0x07,"Channel awarded and being delivered in an established channel"},
+  {0x08,"Preemption"},
+  {0x09,"Preemption - circuit reserved for reuse"},
+  {0x10,"Normal call clearing"},
+  {0x11,"User busy"},
+  {0x12,"No user responding"},
+  {0x13,"No answer from user (user alerted)"},
+  {0x14,"Subscriber absent"},
+  {0x15,"Call rejected"},
+  {0x16,"Number changed"},
+  {0x1a,"non-selected user clearing"},
+  {0x1b,"Destination out of order"},
+  {0x1c,"Invalid number format (address incomplete)"},
+  {0x1d,"Facility rejected"},
+  {0x1e,"Response to Status enquiry"},
+  {0x1f,"Normal, unspecified"},
+  {0x22,"No circuit/channel available"},
+  {0x26,"Network out of order"},
+  {0x27,"Permanent frame mode connection out-of-service"},
+  {0x28,"Permanent frame mode connection operational"},
+  {0x29,"Temporary failure"},
+  {0x2a,"Switching equipment congestion"},
+  {0x2b,"Access information discarded"},
+  {0x2c,"Requested circuit/channel not available"},
+  {0x2e,"Precedence call blocked"},
+  {0x2f,"Resource unavailable, unspecified"},
+  {0x31,"Quality of service unavailable"},
+  {0x32,"Requested facility not subscribed"},
+  {0x35,"Outgoing calls barred within CUG"},
+  {0x37,"Incoming calls barred within CUG"},
+  {0x39,"Bearer capability not authorized"},
+  {0x3a,"Bearer capability not presently available"},
+  {0x3e,"Inconsistency in designated outgoing access information and subscriber class"},
+  {0x3f,"Service or option not available, unspecified"},
+  {0x41,"Bearer capability not implemented"},
+  {0x42,"Channel type not implemented"},
+  {0x43,"Requested facility not implemented"},
+  {0x44,"Only restricted digital information bearer capability is available"},
+  {0x4f,"Service or option not implemented"},
+  {0x51,"Invalid call reference value"},
+  {0x52,"Identified channel does not exist"},
+  {0x53,"A suspended call exists, but this call identity does not"},
+  {0x54,"Call identity in use"},
+  {0x55,"No call suspended"},
+  {0x56,"Call having the requested call identity has been cleared"},
+  {0x57,"User not member of CUG"},
+  {0x58,"Incompatible destination"},
+  {0x5a,"Non-existent CUG"},
+  {0x5b,"Invalid transit network selection"},
+  {0x5f,"Invalid message, unspecified"},
+  {0x60,"Mandatory information element is missing"},
+  {0x61,"Message type non-existent or not implemented"},
+  {0x62,"Message not compatible with call state or message type non-existent or not implemented"},
+  {0x63,"Information element/parameter non-existent or not implemented"},
+  {0x64,"Invalid information element contents"},
+  {0x65,"Message not compatible with call state"},
+  {0x66,"Recovery on timer expiry"},
+  {0x67,"Parameter non-existent or not implemented - passed on"},
+  {0x6e,"Message with unrecognized parameter discarded"},
+  {0x6f,"Protocol error, unspecified"},
+  {0x7f,"Interworking, unspecified"}
+};
+
+#endif
+
+static struct isdn_event_desc {
+  unsigned short ev;
+  char * desc;
+} isdn_event_table [] = {
+  {EV_USR_SETUP_REQ,     "CC->L3: Setup Request"},
+  {EV_USR_SETUP_RESP,    "CC->L3: Setup Response"},
+  {EV_USR_PROCED_REQ,    "CC->L3: Proceeding Request"},
+  {EV_USR_RELEASE_REQ,   "CC->L3: Release Request"},
+
+  {EV_NET_SETUP,        "NET->TE: setup "},
+  {EV_NET_CALL_PROC,    "NET->TE: call proceeding"},
+  {EV_NET_SETUP_ACK,    "NET->TE: setup acknowledge (more info needed)"},
+  {EV_NET_CONN,         "NET->TE: connect"},
+  {EV_NET_CONN_ACK,     "NET->TE: connect acknowledge"},
+  {EV_NET_DISC,         "NET->TE: disconnect indication"},
+  {EV_NET_RELEASE,      "NET->TE: release"},
+  {EV_NET_RELEASE_COMP, "NET->TE: release complete"},
+  {EV_NET_SELP_RESP,    "Board: Select B-channel protocol ack"},
+  {EV_NET_ACTV_RESP,    "Board: Activate B-channel protocol ack"},
+  {EV_TIMER,            "Timeout"},
+  {0, "NULL"}
+};
+
+char * strisdnevent(ushort ev)
+{
+  struct isdn_event_desc * entry;
+ 
+  for (entry = isdn_event_table; entry->ev; entry++)
+    if (entry->ev == ev)
+      break;
+
+  return entry->desc;
+}
+
+/*
+ * Euro ISDN finite state machine
+ */
+
+static struct fsm_timer_entry fsm_timers[] = {
+  {ST_CALL_PROC, 10},
+  {ST_DISC_REQ, 2},
+  {ST_ACTIVE_SELP, 5},
+  {ST_ACTIVE_ACTV, 5},
+  {ST_INCM_PROC, 10},
+  {ST_CONN_REQ, 2},
+  {0xff, 0}
+};
+
+static struct fsm_entry fsm_table[] = {
+/* Connect Phase */
+  /* Outgoing */
+  {ST_NULL, ST_CALL_INIT, EV_USR_SETUP_REQ, cb_out_1},
+
+  {ST_CALL_INIT, ST_OVER_SEND, EV_NET_SETUP_ACK, cb_notdone},
+  {ST_CALL_INIT, ST_CALL_PROC, EV_NET_CALL_PROC, NULL},
+  {ST_CALL_INIT, ST_NULL, EV_NET_DISC, cb_out_2},
+
+  {ST_CALL_PROC, ST_ACTIVE_SELP, EV_NET_CONN, cb_out_2},
+  {ST_CALL_PROC, ST_NULL, EV_NET_DISC, cb_disc_1},
+  {ST_CALL_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  /* Incoming */
+  {ST_NULL, ST_CALL_PRES, EV_NET_SETUP, NULL},
+
+  {ST_CALL_PRES, ST_INCM_PROC, EV_USR_PROCED_REQ, cb_in_1},
+  {ST_CALL_PRES, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  {ST_INCM_PROC, ST_CONN_REQ, EV_USR_SETUP_RESP, cb_in_2},
+  {ST_INCM_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  {ST_CONN_REQ, ST_ACTIVE_SELP, EV_NET_CONN_ACK, cb_in_3},
+
+  /* Active */
+  {ST_ACTIVE, ST_NULL, EV_NET_DISC, cb_disc_1},
+  {ST_ACTIVE, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+  {ST_ACTIVE, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+  /* Disconnect */
+
+  {ST_DISC_REQ, ST_NULL, EV_NET_DISC, cb_disc_1},
+  {ST_DISC_REQ, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+  /* protocol selection */
+  {ST_ACTIVE_SELP, ST_ACTIVE_ACTV, EV_NET_SELP_RESP, cb_selp_1},
+  {ST_ACTIVE_SELP, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  {ST_ACTIVE_ACTV, ST_ACTIVE, EV_NET_ACTV_RESP, cb_open},
+  {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  /* Timers */
+  {ST_CALL_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+  {ST_DISC_REQ, ST_NULL, EV_TIMER, cb_disc_3},
+  {ST_ACTIVE_SELP, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+  {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_TIMER, cb_disc_2},        
+  {ST_INCM_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+  {ST_CONN_REQ, ST_CONN_REQ, EV_TIMER, cb_in_2},
+        
+  {0xff, 0, 0, NULL}
+};
+
+
+static void pcbit_fsm_timer(unsigned long data)
+{
+        struct pcbit_dev *dev;
+        struct pcbit_chan *chan;
+
+        chan = (struct pcbit_chan *) data;
+
+        del_timer(&chan->fsm_timer);
+        chan->fsm_timer.function = NULL;
+
+        dev = chan2dev(chan);
+
+        if (dev == NULL) {
+                printk(KERN_WARNING "pcbit: timer for unknown device\n");
+                return;
+        }
+
+        pcbit_fsm_event(dev, chan, EV_TIMER, NULL);
+}
+
+
+void pcbit_fsm_event(struct pcbit_dev *dev, struct pcbit_chan *chan,
+		   unsigned short event, struct callb_data *data)
+{
+	struct fsm_entry * action;	
+	struct fsm_timer_entry *tentry;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+        for (action = fsm_table; action->init != 0xff; action++)
+                if (action->init == chan->fsm_state && action->event == event)
+                        break;
+  
+	if (action->init == 0xff) {
+		
+		spin_unlock_irqrestore(&dev->lock, flags);
+		printk(KERN_DEBUG "fsm error: event %x on state %x\n", 
+                       event, chan->fsm_state);
+		return;
+	}
+
+        if (chan->fsm_timer.function) {
+                del_timer(&chan->fsm_timer);
+                chan->fsm_timer.function = NULL;
+        }
+
+	chan->fsm_state = action->final;
+  
+	pcbit_state_change(dev, chan, action->init, event, action->final);
+
+        for (tentry = fsm_timers; tentry->init != 0xff; tentry++)
+                if (tentry->init == chan->fsm_state)
+                        break;
+
+        if (tentry->init != 0xff) {
+                init_timer(&chan->fsm_timer);
+                chan->fsm_timer.function = &pcbit_fsm_timer;
+                chan->fsm_timer.data = (ulong) chan;
+                chan->fsm_timer.expires = jiffies + tentry->timeout * HZ;
+                add_timer(&chan->fsm_timer);
+        }
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (action->callb)
+		action->callb(dev, chan, data);
+
+}
+
+
+
+
diff --git a/drivers/isdn/pcbit/edss1.h b/drivers/isdn/pcbit/edss1.h
new file mode 100644
index 0000000..6bb5870
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.h
@@ -0,0 +1,99 @@
+/*
+ * DSS.1 module definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef EDSS1_H
+#define EDSS1_H
+
+/* ISDN states */
+
+#define ST_NULL      0
+#define ST_CALL_INIT 1    /* Call initiated */
+#define ST_OVER_SEND 2    /* Overlap sending - Requests More Info 4 call */
+#define ST_CALL_PROC 3    /* Call Proceeding */
+#define ST_CALL_DELV 4
+#define ST_CALL_PRES 6    /* Call Present - Received CONN.IND */
+#define ST_CALL_RECV 7    /* Alerting sent */
+#define ST_CONN_REQ  8    /* Answered - waiting 4 CONN.CONF */
+#define ST_INCM_PROC 9
+#define ST_ACTIVE    10
+#define ST_DISC_REQ  11
+#define ST_DISC_IND  12
+#define ST_SUSP_REQ  15
+#define ST_RESM_REQ  17
+#define ST_RELS_REQ  19
+#define ST_OVER_RECV 25
+
+#define ST_ACTIVE_SELP 26 /* Select protocol on B-Channel */
+#define ST_ACTIVE_ACTV 27 /* Activate B-channel protocol  */
+
+#define MAX_STATE ST_ACTIVE_ACTV
+
+#define EV_NULL               0
+#define EV_USR_SETUP_REQ      1
+#define EV_USR_SETUP_RESP     2
+#define EV_USR_PROCED_REQ     3
+#define EV_USR_RELEASE_REQ    4
+#define EV_USR_REJECT_REQ     4
+
+#define EV_NET_SETUP          16
+#define EV_NET_CALL_PROC      17
+#define EV_NET_SETUP_ACK      18
+#define EV_NET_CONN           19
+#define EV_NET_CONN_ACK       20
+
+#define EV_NET_SELP_RESP      21
+#define EV_NET_ACTV_RESP      22
+
+#define EV_NET_DISC           23
+#define EV_NET_RELEASE        24
+#define EV_NET_RELEASE_COMP   25
+
+#define EV_TIMER              26
+#define EV_ERROR              32
+
+/*
+ *  Cause values
+ *  only the ones we use
+ */ 
+
+#define CAUSE_NORMAL          0x10U 
+#define CAUSE_NOCHAN          0x22U
+
+struct callb_data {
+	unsigned short type;
+	union {
+		struct ConnInfo {
+			char *CalledPN;
+			char *CallingPN;
+		} setup;
+		unsigned short cause;
+	} data;
+};
+
+struct fsm_entry {
+	unsigned short init;
+	unsigned short final;
+	unsigned short event;
+	void (*callb)(struct pcbit_dev *, struct pcbit_chan *, struct callb_data*);
+};
+
+struct fsm_timer_entry {
+	unsigned short init;
+	unsigned long timeout;          /* in seconds */
+};
+
+
+extern void pcbit_fsm_event(struct pcbit_dev *, struct pcbit_chan *,
+			    unsigned short event, struct callb_data *);
+#endif
+
+
+
diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c
new file mode 100644
index 0000000..ba76693
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.c
@@ -0,0 +1,732 @@
+/*
+ * PCBIT-D low-layer interface
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * 19991203 - Fernando Carvalho - takion@superbofh.org
+ * Hacked to compile with egcs and run with current version of isdn modules
+*/
+
+/*
+ *        Based on documentation provided by Inesc:
+ *        - "Interface com bus do PC para o PCBIT e PCBIT-D", Inesc, Jan 93
+ */
+
+/*
+ *        TODO: better handling of errors
+ *              re-write/remove debug printks
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+
+#undef DEBUG_FRAG
+
+
+
+/*
+ *  task queue struct
+ */
+
+
+
+/*
+ *  Layer 3 packet demultiplexer
+ *  drv.c
+ */
+
+extern void pcbit_l3_receive(struct pcbit_dev *dev, ulong msg,
+			     struct sk_buff *skb,
+			     ushort hdr_len, ushort refnum);
+
+/*
+ *  Prototypes
+ */
+
+void pcbit_deliver(void *data);
+static void pcbit_transmit(struct pcbit_dev *dev);
+
+static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack);
+
+static void pcbit_l2_error(struct pcbit_dev *dev);
+static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info);
+static void pcbit_l2_err_recover(unsigned long data);
+
+static void pcbit_firmware_bug(struct pcbit_dev *dev);
+
+static __inline__ void
+pcbit_sched_delivery(struct pcbit_dev *dev)
+{
+	schedule_work(&dev->qdelivery);
+}
+
+
+/*
+ *  Called from layer3
+ */
+
+int
+pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
+	       struct sk_buff *skb, unsigned short hdr_len)
+{
+	struct frame_buf *frame,
+	*ptr;
+	unsigned long flags;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev_kfree_skb(skb);
+		return -1;
+	}
+	if ((frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf),
+						  GFP_ATOMIC)) == NULL) {
+		printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
+		dev_kfree_skb(skb);
+		return -1;
+	}
+	frame->msg = msg;
+	frame->refnum = refnum;
+	frame->copied = 0;
+	frame->hdr_len = hdr_len;
+
+	if (skb)
+		frame->dt_len = skb->len - hdr_len;
+	else
+		frame->dt_len = 0;
+
+	frame->skb = skb;
+
+	frame->next = NULL;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->write_queue == NULL) {
+		dev->write_queue = frame;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		pcbit_transmit(dev);
+	} else {
+		for (ptr = dev->write_queue; ptr->next; ptr = ptr->next);
+		ptr->next = frame;
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+	}
+	return 0;
+}
+
+static __inline__ void
+pcbit_tx_update(struct pcbit_dev *dev, ushort len)
+{
+	u_char info;
+
+	dev->send_seq = (dev->send_seq + 1) % 8;
+
+	dev->fsize[dev->send_seq] = len;
+	info = 0;
+	info |= dev->rcv_seq << 3;
+	info |= dev->send_seq;
+
+	writeb(info, dev->sh_mem + BANK4);
+
+}
+
+/*
+ * called by interrupt service routine or by write_2
+ */
+
+static void
+pcbit_transmit(struct pcbit_dev *dev)
+{
+	struct frame_buf *frame = NULL;
+	unsigned char unacked;
+	int flen;               /* fragment frame length including all headers */
+	int free;
+	int count,
+	 cp_len;
+	unsigned long flags;
+	unsigned short tt;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
+
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->free > 16 && dev->write_queue && unacked < 7) {
+
+		if (!dev->w_busy)
+			dev->w_busy = 1;
+		else {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return;
+		}
+
+
+		frame = dev->write_queue;
+		free = dev->free;
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		if (frame->copied == 0) {
+
+			/* Type 0 frame */
+
+			ulong 	msg;
+
+			if (frame->skb)
+				flen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len;
+			else
+				flen = FRAME_HDR_LEN + PREHDR_LEN;
+
+			if (flen > free)
+				flen = free;
+
+			msg = frame->msg;
+
+			/*
+			 *  Board level 2 header
+			 */
+
+			pcbit_writew(dev, flen - FRAME_HDR_LEN);
+
+			pcbit_writeb(dev, GET_MSG_CPU(msg));
+
+			pcbit_writeb(dev, GET_MSG_PROC(msg));
+
+			/* TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+			/* TD */
+			pcbit_writew(dev, frame->dt_len);
+
+
+			/*
+			 *  Board level 3 fixed-header
+			 */
+
+			/* LEN = TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+			/* XX */
+			pcbit_writew(dev, 0);
+
+			/* C + S */
+			pcbit_writeb(dev, GET_MSG_CMD(msg));
+			pcbit_writeb(dev, GET_MSG_SCMD(msg));
+
+			/* NUM */
+			pcbit_writew(dev, frame->refnum);
+
+			count = FRAME_HDR_LEN + PREHDR_LEN;
+		} else {
+			/* Type 1 frame */
+
+			flen = 2 + (frame->skb->len - frame->copied);
+
+			if (flen > free)
+				flen = free;
+
+			/* TT */
+			tt = ((ushort) (flen - 2)) | 0x8000U;	/* Type 1 */
+			pcbit_writew(dev, tt);
+
+			count = 2;
+		}
+
+		if (frame->skb) {
+			cp_len = frame->skb->len - frame->copied;
+			if (cp_len > flen - count)
+				cp_len = flen - count;
+
+			memcpy_topcbit(dev, frame->skb->data + frame->copied,
+				       cp_len);
+			frame->copied += cp_len;
+		}
+		/* bookkeeping */
+		dev->free -= flen;
+		pcbit_tx_update(dev, flen);
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (frame->skb == NULL || frame->copied == frame->skb->len) {
+
+			dev->write_queue = frame->next;
+
+			if (frame->skb != NULL) {
+				/* free frame */
+				dev_kfree_skb(frame->skb);
+			}
+			kfree(frame);
+		}
+		dev->w_busy = 0;
+		spin_unlock_irqrestore(&dev->lock, flags);
+	} else {
+		spin_unlock_irqrestore(&dev->lock, flags);
+#ifdef DEBUG
+		printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
+		     unacked, dev->free, dev->write_queue ? "not empty" :
+		       "empty");
+#endif
+	}
+}
+
+
+/*
+ *  deliver a queued frame to the upper layer
+ */
+
+void
+pcbit_deliver(void *data)
+{
+	struct frame_buf *frame;
+	unsigned long flags, msg;
+	struct pcbit_dev *dev = (struct pcbit_dev *) data;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	while ((frame = dev->read_queue)) {
+		dev->read_queue = frame->next;
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		SET_MSG_CPU(msg, 0);
+		SET_MSG_PROC(msg, 0);
+		SET_MSG_CMD(msg, frame->skb->data[2]);
+		SET_MSG_SCMD(msg, frame->skb->data[3]);
+
+		frame->refnum = *((ushort *) frame->skb->data + 4);
+		frame->msg = *((ulong *) & msg);
+
+		skb_pull(frame->skb, 6);
+
+		pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len,
+				 frame->refnum);
+
+		kfree(frame);
+
+		spin_lock_irqsave(&dev->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Reads BANK 2 & Reassembles
+ */
+
+static void
+pcbit_receive(struct pcbit_dev *dev)
+{
+	unsigned short tt;
+	u_char cpu,
+	 proc;
+	struct frame_buf *frame = NULL;
+	unsigned long flags;
+	u_char type1;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
+
+	tt = pcbit_readw(dev);
+
+	if ((tt & 0x7fffU) > 511) {
+		printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n",
+		       tt);
+		pcbit_l2_error(dev);
+		return;
+	}
+	if (!(tt & 0x8000U)) {  /* Type 0 */
+		type1 = 0;
+
+		if (dev->read_frame) {
+			printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
+			/* discard previous queued frame */
+			if (dev->read_frame->skb)
+				kfree_skb(dev->read_frame->skb);
+			kfree(dev->read_frame);
+			dev->read_frame = NULL;
+		}
+		frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC);
+
+		if (frame == NULL) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+		memset(frame, 0, sizeof(struct frame_buf));
+
+		cpu = pcbit_readb(dev);
+		proc = pcbit_readb(dev);
+
+
+		if (cpu != 0x06 && cpu != 0x02) {
+			printk(KERN_DEBUG "pcbit: invalid cpu value\n");
+			kfree(frame);
+			pcbit_l2_error(dev);
+			return;
+		}
+		/*
+		 * we discard cpu & proc on receiving
+		 * but we read it to update the pointer
+		 */
+
+		frame->hdr_len = pcbit_readw(dev);
+		frame->dt_len = pcbit_readw(dev);
+
+		/*
+		   * 0 sized packet
+		   * I don't know if they are an error or not...
+		   * But they are very frequent
+		   * Not documented
+		 */
+
+		if (frame->hdr_len == 0) {
+			kfree(frame);
+#ifdef DEBUG
+			printk(KERN_DEBUG "0 sized frame\n");
+#endif
+			pcbit_firmware_bug(dev);
+			return;
+		}
+		/* sanity check the length values */
+		if (frame->hdr_len > 1024 || frame->dt_len > 2048) {
+#ifdef DEBUG
+			printk(KERN_DEBUG "length problem: ");
+			printk(KERN_DEBUG "TH=%04x TD=%04x\n",
+			       frame->hdr_len,
+			       frame->dt_len);
+#endif
+			pcbit_l2_error(dev);
+			kfree(frame);
+			return;
+		}
+		/* minimum frame read */
+
+		frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len +
+					   ((frame->hdr_len + 15) & ~15));
+
+		if (!frame->skb) {
+			printk(KERN_DEBUG "pcbit_receive: out of memory\n");
+			kfree(frame);
+			return;
+		}
+		/* 16 byte alignment for IP */
+		if (frame->dt_len)
+			skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
+
+	} else {
+		/* Type 1 */
+		type1 = 1;
+		tt &= 0x7fffU;
+
+		if (!(frame = dev->read_frame)) {
+			printk("Type 1 frame and no frame queued\n");
+			/* usually after an error: toss frame */
+			dev->readptr += tt;
+			if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN)
+				dev->readptr -= BANKLEN;
+			return;
+
+		}
+	}
+
+	memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt);
+
+	frame->copied += tt;
+	spin_lock_irqsave(&dev->lock, flags);
+	if (frame->copied == frame->hdr_len + frame->dt_len) {
+
+		if (type1) {
+			dev->read_frame = NULL;
+		}
+		if (dev->read_queue) {
+			struct frame_buf *ptr;
+			for (ptr = dev->read_queue; ptr->next; ptr = ptr->next);
+			ptr->next = frame;
+		} else
+			dev->read_queue = frame;
+
+	} else {
+		dev->read_frame = frame;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ *  The board sends 0 sized frames
+ *  They are TDATA_CONFs that get messed up somehow
+ *  gotta send a fake acknowledgment to the upper layer somehow
+ */
+
+static __inline__ void
+pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan)
+{
+	isdn_ctrl ictl;
+
+	if (chan->queued) {
+		chan->queued--;
+
+		ictl.driver = dev->id;
+		ictl.command = ISDN_STAT_BSENT;
+		ictl.arg = chan->id;
+		dev->dev_if->statcallb(&ictl);
+	}
+}
+
+static void
+pcbit_firmware_bug(struct pcbit_dev *dev)
+{
+	struct pcbit_chan *chan;
+
+	chan = dev->b1;
+
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
+	chan = dev->b2;
+
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
+}
+
+irqreturn_t
+pcbit_irq_handler(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	struct pcbit_dev *dev;
+	u_char info,
+	 ack_seq,
+	 read_seq;
+
+	dev = (struct pcbit_dev *) devptr;
+
+	if (!dev) {
+		printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
+		return IRQ_NONE;
+	}
+	if (dev->interrupt) {
+		printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
+		return IRQ_HANDLED;
+	}
+	dev->interrupt = 1;
+
+	info = readb(dev->sh_mem + BANK3);
+
+	if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) {
+		pcbit_l2_active_conf(dev, info);
+		dev->interrupt = 0;
+		return IRQ_HANDLED;
+	}
+	if (info & 0x40U) {     /* E bit set */
+#ifdef DEBUG
+		printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n");
+#endif
+		pcbit_l2_error(dev);
+		dev->interrupt = 0;
+		return IRQ_HANDLED;
+	}
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev->interrupt = 0;
+		return IRQ_HANDLED;
+	}
+	ack_seq = (info >> 3) & 0x07U;
+	read_seq = (info & 0x07U);
+
+	dev->interrupt = 0;
+
+	if (read_seq != dev->rcv_seq) {
+		while (read_seq != dev->rcv_seq) {
+			pcbit_receive(dev);
+			dev->rcv_seq = (dev->rcv_seq + 1) % 8;
+		}
+		pcbit_sched_delivery(dev);
+	}
+	if (ack_seq != dev->unack_seq) {
+		pcbit_recv_ack(dev, ack_seq);
+	}
+	info = dev->rcv_seq << 3;
+	info |= dev->send_seq;
+
+	writeb(info, dev->sh_mem + BANK4);
+	return IRQ_HANDLED;
+}
+
+
+static void
+pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
+{
+	u_char state;
+
+	state = dev->l2_state;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "layer2_active_confirm\n");
+#endif
+
+
+	if (info & 0x80U) {
+		dev->rcv_seq = info & 0x07U;
+		dev->l2_state = L2_RUNNING;
+	} else
+		dev->l2_state = L2_DOWN;
+
+	if (state == L2_STARTING)
+		wake_up_interruptible(&dev->set_running_wq);
+
+	if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
+		pcbit_transmit(dev);
+	}
+}
+
+static void
+pcbit_l2_err_recover(unsigned long data)
+{
+
+	struct pcbit_dev *dev;
+	struct frame_buf *frame;
+
+	dev = (struct pcbit_dev *) data;
+
+	del_timer(&dev->error_recover_timer);
+	if (dev->w_busy || dev->r_busy) {
+		init_timer(&dev->error_recover_timer);
+		dev->error_recover_timer.expires = jiffies + ERRTIME;
+		add_timer(&dev->error_recover_timer);
+		return;
+	}
+	dev->w_busy = dev->r_busy = 1;
+
+	if (dev->read_frame) {
+		if (dev->read_frame->skb)
+			kfree_skb(dev->read_frame->skb);
+		kfree(dev->read_frame);
+		dev->read_frame = NULL;
+	}
+	if (dev->write_queue) {
+		frame = dev->write_queue;
+#ifdef FREE_ON_ERROR
+		dev->write_queue = dev->write_queue->next;
+
+		if (frame->skb) {
+			dev_kfree_skb(frame->skb);
+		}
+		kfree(frame);
+#else
+		frame->copied = 0;
+#endif
+	}
+	dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
+	dev->free = 511;
+	dev->l2_state = L2_ERROR;
+
+	/* this is an hack... */
+	pcbit_firmware_bug(dev);
+
+	dev->writeptr = dev->sh_mem;
+	dev->readptr = dev->sh_mem + BANK2;
+
+	writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
+	       dev->sh_mem + BANK4);
+	dev->w_busy = dev->r_busy = 0;
+
+}
+
+static void
+pcbit_l2_error(struct pcbit_dev *dev)
+{
+	if (dev->l2_state == L2_RUNNING) {
+
+		printk(KERN_INFO "pcbit: layer 2 error\n");
+
+#ifdef DEBUG
+		log_state(dev);
+#endif
+
+		dev->l2_state = L2_DOWN;
+
+		init_timer(&dev->error_recover_timer);
+		dev->error_recover_timer.function = &pcbit_l2_err_recover;
+		dev->error_recover_timer.data = (ulong) dev;
+		dev->error_recover_timer.expires = jiffies + ERRTIME;
+		add_timer(&dev->error_recover_timer);
+	}
+}
+
+/*
+ * Description:
+ * if board acks frames
+ *   update dev->free
+ *   call pcbit_transmit to write possible queued frames
+ */
+
+static void
+pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
+{
+	int i,
+	 count;
+	int unacked;
+
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
+
+	/* dev->unack_seq < ack <= dev->send_seq; */
+
+	if (unacked) {
+
+		if (dev->send_seq > dev->unack_seq) {
+			if (ack <= dev->unack_seq || ack > dev->send_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
+				       dev->id);
+
+				pcbit_l2_error(dev);
+			} else if (ack > dev->send_seq && ack <= dev->unack_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
+				       dev->id);
+				pcbit_l2_error(dev);
+			}
+		}
+		/* ack is acceptable */
+
+
+		i = dev->unack_seq;
+
+		do {
+			dev->unack_seq = i = (i + 1) % 8;
+			dev->free += dev->fsize[i];
+		} while (i != ack);
+
+		count = 0;
+		while (count < 7 && dev->write_queue) {
+			u8 lsend_seq = dev->send_seq;
+
+			pcbit_transmit(dev);
+
+			if (dev->send_seq == lsend_seq)
+				break;
+			count++;
+		}
+	} else
+		printk(KERN_DEBUG "recv_ack: unacked = 0\n");
+}
diff --git a/drivers/isdn/pcbit/layer2.h b/drivers/isdn/pcbit/layer2.h
new file mode 100644
index 0000000..0d99da3
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.h
@@ -0,0 +1,288 @@
+/*
+ * PCBIT-D low-layer interface definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * 19991203 - Fernando Carvalho - takion@superbofh.org
+ * Hacked to compile with egcs and run with current version of isdn modules
+*/
+
+#ifndef LAYER2_H
+#define LAYER2_H
+
+#include <linux/interrupt.h>
+
+#include <asm/byteorder.h>
+
+#define BANK1 0x0000U /* PC -> Board */
+#define BANK2 0x01ffU /* Board -> PC */
+#define BANK3 0x03feU /* Att Board */
+#define BANK4 0x03ffU /* Att PC */
+
+#define BANKLEN 0x01FFU
+
+#define LOAD_ZONE_START 0x03f8U
+#define LOAD_ZONE_END   0x03fdU
+
+#define LOAD_RETRY      18000000
+
+
+
+/* TAM - XX - C - S  - NUM */
+#define PREHDR_LEN 8
+/* TT  - M  - I - TH - TD  */      
+#define FRAME_HDR_LEN  8   
+
+#define MSG_CONN_REQ		0x08000100
+#define MSG_CONN_CONF		0x00000101
+#define MSG_CONN_IND		0x00000102
+#define MSG_CONN_RESP		0x08000103
+
+#define MSG_CONN_ACTV_REQ	0x08000300
+#define MSG_CONN_ACTV_CONF	0x00000301
+#define MSG_CONN_ACTV_IND	0x00000302
+#define MSG_CONN_ACTV_RESP	0x08000303
+
+#define MSG_DISC_REQ		0x08000400
+#define MSG_DISC_CONF		0x00000401
+#define MSG_DISC_IND		0x00000402
+#define MSG_DISC_RESP		0x08000403
+
+#define MSG_TDATA_REQ		0x0908E200
+#define MSG_TDATA_CONF		0x0000E201
+#define MSG_TDATA_IND		0x0000E202
+#define MSG_TDATA_RESP		0x0908E203
+
+#define MSG_SELP_REQ		0x09004000
+#define MSG_SELP_CONF		0x00004001
+
+#define MSG_ACT_TRANSP_REQ      0x0908E000
+#define MSG_ACT_TRANSP_CONF     0x0000E001
+
+#define MSG_STPROT_REQ		0x09004100
+#define MSG_STPROT_CONF		0x00004101
+
+#define MSG_PING188_REQ		0x09030500
+#define MSG_PING188_CONF        0x000005bc
+
+#define MSG_WATCH188	        0x09030400
+
+#define MSG_API_ON              0x08020102
+#define MSG_POOL_PCBIT          0x08020400
+#define MSG_POOL_PCBIT_CONF     0x00000401
+
+#define MSG_INFO_IND            0x00002602
+#define MSG_INFO_RESP           0x08002603
+
+#define MSG_DEBUG_188           0x0000ff00
+
+/*
+   
+   long  4 3 2 1
+   Intel 1 2 3 4
+*/
+
+#ifdef __LITTLE_ENDIAN
+#define SET_MSG_SCMD(msg, ch) 	(msg = (msg & 0xffffff00) | (((ch) & 0xff)))
+#define SET_MSG_CMD(msg, ch) 	(msg = (msg & 0xffff00ff) | (((ch) & 0xff) << 8))
+#define SET_MSG_PROC(msg, ch) 	(msg = (msg & 0xff00ffff) | (((ch) & 0xff) << 16))
+#define SET_MSG_CPU(msg, ch) 	(msg = (msg & 0x00ffffff) | (((ch) & 0xff) << 24))
+
+#define GET_MSG_SCMD(msg) 	((msg) & 0xFF)
+#define GET_MSG_CMD(msg) 	((msg) >> 8 & 0xFF)
+#define GET_MSG_PROC(msg) 	((msg) >> 16 & 0xFF)
+#define GET_MSG_CPU(msg) 	((msg) >> 24)
+
+#else
+#error "Non-Intel CPU"
+#endif
+
+#define MAX_QUEUED 7
+
+#define SCHED_READ    0x01
+#define SCHED_WRITE   0x02
+
+#define SET_RUN_TIMEOUT 2*HZ /* 2 seconds */
+     
+struct frame_buf {
+        ulong msg;
+        unsigned int refnum;
+        unsigned int dt_len;
+        unsigned int hdr_len;
+        struct sk_buff *skb;
+	unsigned int copied;
+        struct frame_buf * next;
+};
+
+extern int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum, 
+                          struct sk_buff *skb, unsigned short hdr_len);
+
+extern irqreturn_t pcbit_irq_handler(int interrupt, void *, struct pt_regs *regs);
+
+extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+#ifdef DEBUG
+static __inline__ void log_state(struct pcbit_dev *dev) {
+        printk(KERN_DEBUG "writeptr = %ld\n", 
+	       (ulong) (dev->writeptr - dev->sh_mem));
+        printk(KERN_DEBUG "readptr  = %ld\n", 
+	       (ulong) (dev->readptr - (dev->sh_mem + BANK2)));
+        printk(KERN_DEBUG "{rcv_seq=%01x, send_seq=%01x, unack_seq=%01x}\n", 
+	       dev->rcv_seq, dev->send_seq, dev->unack_seq);
+}
+#endif
+
+static __inline__ struct pcbit_dev * chan2dev(struct pcbit_chan * chan) 
+{
+        struct pcbit_dev * dev;
+        int i;
+
+
+        for (i=0; i<MAX_PCBIT_CARDS; i++)
+                if ((dev=dev_pcbit[i]))
+                        if (dev->b1 == chan || dev->b2 == chan)
+                                return dev;
+        return NULL;
+
+}
+
+static __inline__ struct pcbit_dev * finddev(int id)
+{
+  struct pcbit_dev * dev;
+  int i;
+
+  for (i=0; i<MAX_PCBIT_CARDS; i++)
+    if ((dev=dev_pcbit[i]))
+      if (dev->id == id)
+	return dev;
+  return NULL;
+}
+
+
+/*
+ *  Support routines for reading and writing in the board
+ */
+
+static __inline__ void pcbit_writeb(struct pcbit_dev *dev, unsigned char dt)
+{
+  writeb(dt, dev->writeptr++);
+  if (dev->writeptr == dev->sh_mem + BANKLEN)
+    dev->writeptr = dev->sh_mem;
+}
+
+static __inline__ void pcbit_writew(struct pcbit_dev *dev, unsigned short dt)
+{
+  int dist;
+
+  dist = BANKLEN - (dev->writeptr - dev->sh_mem);
+  switch (dist) {
+  case 2:
+    writew(dt, dev->writeptr);
+    dev->writeptr = dev->sh_mem;
+    break;
+  case 1:
+    writeb((u_char) (dt & 0x00ffU), dev->writeptr);    
+    dev->writeptr = dev->sh_mem;
+    writeb((u_char) (dt >> 8), dev->writeptr++);    
+    break;
+  default:
+    writew(dt, dev->writeptr);
+    dev->writeptr += 2;
+    break;
+  };
+}
+
+static __inline__ void memcpy_topcbit(struct pcbit_dev * dev, u_char * data, 
+				      int len)
+{
+  int diff;
+
+  diff = len - (BANKLEN - (dev->writeptr - dev->sh_mem) );
+
+  if (diff > 0)
+    {
+      memcpy_toio(dev->writeptr, data, len - diff);
+      memcpy_toio(dev->sh_mem, data + (len - diff), diff);
+      dev->writeptr = dev->sh_mem + diff;
+    }
+  else
+    {
+      memcpy_toio(dev->writeptr, data, len);
+
+      dev->writeptr += len;
+      if (diff == 0)
+	dev->writeptr = dev->sh_mem;
+    }
+}
+
+static __inline__ unsigned char pcbit_readb(struct pcbit_dev *dev)
+{
+  unsigned char val;
+
+  val = readb(dev->readptr++);
+  if (dev->readptr == dev->sh_mem + BANK2 + BANKLEN)
+    dev->readptr = dev->sh_mem + BANK2;
+
+  return val;
+}
+
+static __inline__ unsigned short pcbit_readw(struct pcbit_dev *dev)
+{
+  int dist;
+  unsigned short val;
+
+  dist = BANKLEN - ( dev->readptr - (dev->sh_mem + BANK2 ) );
+  switch (dist) {
+  case 2:
+    val = readw(dev->readptr);
+    dev->readptr = dev->sh_mem + BANK2;
+    break;
+  case 1:
+    val = readb(dev->readptr);
+    dev->readptr = dev->sh_mem + BANK2;
+    val = (readb(dev->readptr++) << 8) | val;
+    break;
+  default:
+    val = readw(dev->readptr);
+    dev->readptr += 2;
+    break;
+  };
+  return val;
+}
+
+static __inline__ void memcpy_frompcbit(struct pcbit_dev * dev, u_char * data, int len)
+{
+  int diff;
+
+  diff = len - (BANKLEN - (dev->readptr - (dev->sh_mem + BANK2) ) ); 
+  if (diff > 0)
+    {
+      memcpy_fromio(data, dev->readptr, len - diff);
+      memcpy_fromio(data + (len - diff), dev->sh_mem + BANK2 , diff);
+      dev->readptr = dev->sh_mem + BANK2 + diff;
+    }
+  else
+    {
+      memcpy_fromio(data, dev->readptr, len);
+      dev->readptr += len;
+      if (diff == 0)
+	dev->readptr = dev->sh_mem + BANK2;
+    }
+}
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c
new file mode 100644
index 0000000..282073a
--- /dev/null
+++ b/drivers/isdn/pcbit/module.c
@@ -0,0 +1,130 @@
+/*
+ * PCBIT-D module support
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include "pcbit.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for PCBIT-T card");
+MODULE_AUTHOR("Pedro Roque Marques");
+MODULE_LICENSE("GPL");
+
+static int mem[MAX_PCBIT_CARDS];
+static int irq[MAX_PCBIT_CARDS];
+
+module_param_array(mem, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+
+static int num_boards;
+struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+extern void pcbit_terminate(int board);
+extern int pcbit_init_dev(int board, int mem_base, int irq);
+
+static int __init pcbit_init(void)
+{
+	int board;
+
+	num_boards = 0;
+
+	printk(KERN_NOTICE 
+	       "PCBIT-D device driver v 0.5-fjpc0 19991204 - "
+	       "Copyright (C) 1996 Universidade de Lisboa\n");
+
+	if (mem[0] || irq[0]) 
+	{
+		for (board=0; board < MAX_PCBIT_CARDS && mem[board] && irq[board]; board++)
+		{
+			if (!mem[board])
+				mem[board] = 0xD0000;
+			if (!irq[board])
+				irq[board] = 5;
+			
+			if (pcbit_init_dev(board, mem[board], irq[board]) == 0)
+				num_boards++;
+		
+			else 
+			{
+				printk(KERN_WARNING 
+				       "pcbit_init failed for dev %d", 
+				       board + 1);
+				return -EIO;
+			}
+		}
+	}
+
+	/* Hardcoded default settings detection */
+
+	if (!num_boards)
+	{
+		printk(KERN_INFO 
+		       "Trying to detect board using default settings\n");
+		if (pcbit_init_dev(0, 0xD0000, 5) == 0)
+			num_boards++;
+		else
+			return -EIO;
+	}
+	return 0;
+}
+
+static void __exit pcbit_exit(void)
+{
+#ifdef MODULE
+	int board;
+
+	for (board = 0; board < num_boards; board++)
+		pcbit_terminate(board);
+	printk(KERN_NOTICE 
+	       "PCBIT-D module unloaded\n");
+#endif
+}
+
+#ifndef MODULE
+#define MAX_PARA	(MAX_PCBIT_CARDS * 2)
+static int __init pcbit_setup(char *line)
+{
+	int i, j, argc;
+	char *str;
+	int ints[MAX_PARA+1];
+
+	str = get_options(line, MAX_PARA, ints);
+	argc = ints[0];
+	i = 0;
+	j = 1;
+
+	while (argc && (i<MAX_PCBIT_CARDS)) {
+
+		if (argc) {
+			mem[i]	= ints[j];
+			j++; argc--;
+		}
+		
+		if (argc) {
+			irq[i]	= ints[j];
+			j++; argc--;
+		}
+
+		i++;
+	}
+	return(1);
+}
+__setup("pcbit=", pcbit_setup);
+#endif
+
+module_init(pcbit_init);
+module_exit(pcbit_exit);
+
diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h
new file mode 100644
index 0000000..388bace
--- /dev/null
+++ b/drivers/isdn/pcbit/pcbit.h
@@ -0,0 +1,169 @@
+/*
+ * PCBIT-D device driver definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef PCBIT_H
+#define PCBIT_H
+
+#include <linux/workqueue.h>
+
+#define MAX_PCBIT_CARDS 4
+
+
+#define BLOCK_TIMER
+
+#ifdef __KERNEL__
+
+struct pcbit_chan {
+	unsigned short id;
+	unsigned short callref;                   /* Call Reference */
+	unsigned char  proto;                     /* layer2protocol  */
+	unsigned char  queued;                    /* unacked data messages */
+	unsigned char  layer2link;                /* used in TData */
+	unsigned char  snum;                      /* used in TData */
+	unsigned short s_refnum;
+	unsigned short r_refnum;
+	unsigned short fsm_state;
+	struct timer_list fsm_timer;
+#ifdef  BLOCK_TIMER
+	struct timer_list block_timer;
+#endif
+};
+
+struct msn_entry {
+	char *msn;
+	struct msn_entry * next;
+};
+
+struct pcbit_dev {
+	/* board */
+
+	volatile unsigned char __iomem *sh_mem;		/* RDP address	*/
+	unsigned long ph_mem;
+	unsigned int irq;
+	unsigned int id;
+	unsigned int interrupt;			/* set during interrupt 
+						   processing */
+	spinlock_t lock;
+	/* isdn4linux */
+
+	struct msn_entry * msn_list;		/* ISDN address list */
+	
+	isdn_if * dev_if;
+	
+	ushort ll_hdrlen;
+	ushort hl_hdrlen;
+
+	/* link layer */
+	unsigned char l2_state;
+
+	struct frame_buf *read_queue;
+	struct frame_buf *read_frame;
+	struct frame_buf *write_queue;
+
+	/* Protocol start */
+	wait_queue_head_t set_running_wq;
+	struct timer_list set_running_timer;
+
+	struct timer_list error_recover_timer;
+
+	struct work_struct qdelivery;
+
+	u_char w_busy;
+	u_char r_busy;
+
+	volatile unsigned char __iomem *readptr;
+	volatile unsigned char __iomem *writeptr;
+
+	ushort loadptr;
+
+	unsigned short fsize[8];		/* sent layer2 frames size */
+
+	unsigned char send_seq;
+	unsigned char rcv_seq;
+	unsigned char unack_seq;
+  
+	unsigned short free;
+
+	/* channels */
+
+	struct pcbit_chan *b1;
+	struct pcbit_chan *b2;  
+};
+
+#define STATS_TIMER (10*HZ)
+#define ERRTIME     (HZ/10)
+
+/* MRU */
+#define MAXBUFSIZE  1534
+#define MRU   MAXBUFSIZE
+
+#define STATBUF_LEN 2048
+/*
+ * 
+ */
+
+#endif /* __KERNEL__ */
+
+/* isdn_ctrl only allows a long sized argument */
+
+struct pcbit_ioctl {
+	union {
+		struct byte_op {
+			ushort addr;
+			ushort value;
+		} rdp_byte;
+		unsigned long l2_status;
+	} info;
+};
+
+
+
+#define PCBIT_IOCTL_GETSTAT  0x01    /* layer2 status */
+#define PCBIT_IOCTL_LWMODE   0x02    /* linear write mode */
+#define PCBIT_IOCTL_STRLOAD  0x03    /* start load mode */
+#define PCBIT_IOCTL_ENDLOAD  0x04    /* end load mode */
+#define PCBIT_IOCTL_SETBYTE  0x05    /* set byte */
+#define PCBIT_IOCTL_GETBYTE  0x06    /* get byte */
+#define PCBIT_IOCTL_RUNNING  0x07    /* set protocol running */
+#define PCBIT_IOCTL_WATCH188 0x08    /* set watch 188 */
+#define PCBIT_IOCTL_PING188  0x09    /* ping 188 */
+#define PCBIT_IOCTL_FWMODE   0x0A    /* firmware write mode */
+#define PCBIT_IOCTL_STOP     0x0B    /* stop protocol */
+#define PCBIT_IOCTL_APION    0x0C    /* issue API_ON  */
+
+#ifndef __KERNEL__
+
+#define PCBIT_GETSTAT  (PCBIT_IOCTL_GETSTAT  + IIOCDRVCTL)
+#define PCBIT_LWMODE   (PCBIT_IOCTL_LWMODE   + IIOCDRVCTL)
+#define PCBIT_STRLOAD  (PCBIT_IOCTL_STRLOAD  + IIOCDRVCTL)
+#define PCBIT_ENDLOAD  (PCBIT_IOCTL_ENDLOAD  + IIOCDRVCTL)
+#define PCBIT_SETBYTE  (PCBIT_IOCTL_SETBYTE  + IIOCDRVCTL)
+#define PCBIT_GETBYTE  (PCBIT_IOCTL_GETBYTE  + IIOCDRVCTL)
+#define PCBIT_RUNNING  (PCBIT_IOCTL_RUNNING  + IIOCDRVCTL)
+#define PCBIT_WATCH188 (PCBIT_IOCTL_WATCH188 + IIOCDRVCTL)
+#define PCBIT_PING188  (PCBIT_IOCTL_PING188  + IIOCDRVCTL)
+#define PCBIT_FWMODE   (PCBIT_IOCTL_FWMODE   + IIOCDRVCTL)
+#define PCBIT_STOP     (PCBIT_IOCTL_STOP     + IIOCDRVCTL)
+#define PCBIT_APION    (PCBIT_IOCTL_APION    + IIOCDRVCTL)
+
+#define MAXSUPERLINE 3000
+
+#endif
+
+#define L2_DOWN     0
+#define L2_LOADING  1
+#define L2_LWMODE   2
+#define L2_FWMODE   3
+#define L2_STARTING 4
+#define L2_RUNNING  5
+#define L2_ERROR    6
+
+#endif
diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig
new file mode 100644
index 0000000..5346e33d
--- /dev/null
+++ b/drivers/isdn/sc/Kconfig
@@ -0,0 +1,12 @@
+#
+# Config.in for Spellcaster ISDN driver
+#
+config ISDN_DRV_SC
+	tristate "Spellcaster support"
+	depends on ISDN_I4L && ISA
+	help
+	  This enables support for the Spellcaster BRI ISDN boards.  This
+	  driver currently builds only in a modularized version.
+	  To build it, choose M here: the module will be called sc.
+	  See <file:Documentation/isdn/README.sc> for more information.
+
diff --git a/drivers/isdn/sc/Makefile b/drivers/isdn/sc/Makefile
new file mode 100644
index 0000000..9cc474c
--- /dev/null
+++ b/drivers/isdn/sc/Makefile
@@ -0,0 +1,10 @@
+# Makefile for the sc ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_SC)	+= sc.o
+
+# Multipart objects.
+
+sc-y				:= shmem.o init.o debug.o packet.o command.o event.o \
+		   		   ioctl.o interrupt.o message.o timer.o	
diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h
new file mode 100644
index 0000000..8e44928
--- /dev/null
+++ b/drivers/isdn/sc/card.h
@@ -0,0 +1,101 @@
+/* $Id: card.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Driver parameters for SpellCaster ISA ISDN adapters
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#ifndef CARD_H
+#define CARD_H
+
+/*
+ * We need these if they're not already included
+ */
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/isdnif.h>
+#include "message.h"
+
+/*
+ * Amount of time to wait for a reset to complete
+ */
+#define CHECKRESET_TIME		msecs_to_jiffies(4000)
+
+/*
+ * Amount of time between line status checks
+ */
+#define CHECKSTAT_TIME		msecs_to_jiffies(8000)
+
+/*
+ * The maximum amount of time to wait for a message response
+ * to arrive. Use exclusively by send_and_receive
+ */
+#define SAR_TIMEOUT		msecs_to_jiffies(10000)
+
+/*
+ * Macro to determine is a card id is valid
+ */
+#define IS_VALID_CARD(x)	((x >= 0) && (x <= cinst))
+
+/*
+ * Per channel status and configuration
+ */
+typedef struct {
+	int l2_proto;
+	int l3_proto;
+	char dn[50];
+	unsigned long first_sendbuf;	/* Offset of first send buffer */
+	unsigned int num_sendbufs;	/* Number of send buffers */
+	unsigned int free_sendbufs;	/* Number of free sendbufs */
+	unsigned int next_sendbuf;	/* Next sequential buffer */
+	char eazlist[50];		/* Set with SETEAZ */
+	char sillist[50];		/* Set with SETSIL */
+	int eazclear;			/* Don't accept calls if TRUE */
+} bchan;
+
+/*
+ * Everything you want to know about the adapter ...
+ */
+typedef struct {
+	int model;
+	int driverId;			/* LL Id */
+	char devicename[20];		/* The device name */
+	isdn_if *card;			/* ISDN4Linux structure */
+	bchan *channel;			/* status of the B channels */
+	char nChannels;			/* Number of channels */
+	unsigned int interrupt;		/* Interrupt number */
+	int iobase;			/* I/O Base address */
+	int ioport[MAX_IO_REGS];	/* Index to I/O ports */
+	int shmem_pgport;		/* port for the exp mem page reg. */
+	int shmem_magic;		/* adapter magic number */
+	unsigned int rambase;		/* Shared RAM base address */
+	unsigned int ramsize;		/* Size of shared memory */
+	RspMessage async_msg;		/* Async response message */
+	int want_async_messages;	/* Snoop the Q ? */
+	unsigned char seq_no;		/* Next send seq. number */
+	struct timer_list reset_timer;	/* Check reset timer */
+	struct timer_list stat_timer;	/* Check startproc timer */
+	unsigned char nphystat;		/* Latest PhyStat info */
+	unsigned char phystat;		/* Last PhyStat info */
+	HWConfig_pl hwconfig;		/* Hardware config info */
+	char load_ver[11];		/* CommManage Version string */
+	char proc_ver[11];		/* CommEngine Version */
+	int StartOnReset;		/* Indicates startproc after reset */
+	int EngineUp;			/* Indicates CommEngine Up */
+	int trace_mode;			/* Indicate if tracing is on */
+	spinlock_t lock;		/* local lock */
+} board;
+
+#endif /* CARD_H */
diff --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c
new file mode 100644
index 0000000..b2c4eac
--- /dev/null
+++ b/drivers/isdn/sc/command.c
@@ -0,0 +1,441 @@
+/* $Id: command.c,v 1.4.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include <linux/module.h>
+#include "includes.h"		/* This must be first */
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+int dial(int card, unsigned long channel, setup_parm setup);
+int hangup(int card, unsigned long channel);
+int answer(int card, unsigned long channel);
+int clreaz(int card, unsigned long channel);
+int seteaz(int card, unsigned long channel, char *);
+int setl2(int card, unsigned long arg);
+int setl3(int card, unsigned long arg);
+int acceptb(int card, unsigned long channel);
+
+extern int cinst;
+extern board *sc_adapter[];
+
+extern int sc_ioctl(int, scs_ioctl *);
+extern int setup_buffers(int, int, unsigned int);
+extern int indicate_status(int, int,ulong,char*);
+extern void check_reset(unsigned long);
+extern int send_and_receive(int, unsigned int, unsigned char, unsigned char,
+                unsigned char, unsigned char, unsigned char, unsigned char *,
+                RspMessage *, int);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+                unsigned int, unsigned int, unsigned int, unsigned int *);
+extern inline void pullphone(char *, char *);
+
+#ifdef DEBUG
+/*
+ * Translate command codes to strings
+ */
+static char *commands[] = { "ISDN_CMD_IOCTL",
+			    "ISDN_CMD_DIAL",
+			    "ISDN_CMD_ACCEPTB",
+			    "ISDN_CMD_ACCEPTB",
+			    "ISDN_CMD_HANGUP",
+			    "ISDN_CMD_CLREAZ",
+			    "ISDN_CMD_SETEAZ",
+			    NULL,
+			    NULL,
+			    NULL,
+			    "ISDN_CMD_SETL2",
+			    NULL,
+			    "ISDN_CMD_SETL3",
+			    NULL,
+			    NULL,
+			    NULL,
+			    NULL,
+			    NULL, };
+
+/*
+ * Translates ISDN4Linux protocol codes to strings for debug messages
+ */
+static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" };
+static char *l2protos[] = { "ISDN_PROTO_L2_X75I",
+			    "ISDN_PROTO_L2_X75UI",
+			    "ISDN_PROTO_L2_X75BUI",
+			    "ISDN_PROTO_L2_HDLC",
+			    "ISDN_PROTO_L2_TRANS" };
+#endif
+
+int get_card_from_id(int driver)
+{
+	int i;
+
+	for(i = 0 ; i < cinst ; i++) {
+		if(sc_adapter[i]->driverId == driver)
+			return i;
+	}
+	return -ENODEV;
+}
+
+/* 
+ * command
+ */
+
+int command(isdn_ctrl *cmd)
+{
+	int card;
+
+	card = get_card_from_id(cmd->driver);
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Received %s command from Link Layer\n",
+		sc_adapter[card]->devicename, commands[cmd->command]);
+
+	/*
+	 * Dispatch the command
+	 */
+	switch(cmd->command) {
+	case ISDN_CMD_IOCTL:
+	{
+		unsigned long 	cmdptr;
+		scs_ioctl	ioc;
+
+		memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
+		if (copy_from_user(&ioc, (scs_ioctl __user *)cmdptr,
+				   sizeof(scs_ioctl))) {
+			pr_debug("%s: Failed to verify user space 0x%x\n",
+				sc_adapter[card]->devicename, cmdptr);
+			return -EFAULT;
+		}
+		return sc_ioctl(card, &ioc);
+	}
+	case ISDN_CMD_DIAL:
+		return dial(card, cmd->arg, cmd->parm.setup);
+	case ISDN_CMD_HANGUP:
+		return hangup(card, cmd->arg);
+	case ISDN_CMD_ACCEPTD:
+		return answer(card, cmd->arg);
+	case ISDN_CMD_ACCEPTB:
+		return acceptb(card, cmd->arg);
+	case ISDN_CMD_CLREAZ:
+		return clreaz(card, cmd->arg);
+	case ISDN_CMD_SETEAZ:
+		return seteaz(card, cmd->arg, cmd->parm.num);
+	case ISDN_CMD_SETL2:
+		return setl2(card, cmd->arg);
+	case ISDN_CMD_SETL3:
+		return setl3(card, cmd->arg);
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ * Confirm our ability to communicate with the board.  This test assumes no
+ * other message activity is present
+ */
+int loopback(int card) 
+{
+
+	int status;
+	static char testmsg[] = "Test Message";
+	RspMessage rspmsg;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: Sending loopback message\n",
+		sc_adapter[card]->devicename);
+
+	/*
+	 * Send the loopback message to confirm that memory transfer is
+	 * operational
+	 */
+	status = send_and_receive(card, CMPID, cmReqType1,
+				  cmReqClass0,
+				  cmReqMsgLpbk,
+				  0,
+				  (unsigned char) strlen(testmsg),
+				  (unsigned char *)testmsg,
+				  &rspmsg, SAR_TIMEOUT);
+
+
+	if (!status) {
+		pr_debug("%s: Loopback message successfully sent\n",
+			sc_adapter[card]->devicename);
+		if(strcmp(rspmsg.msg_data.byte_array, testmsg)) {
+			pr_debug("%s: Loopback return != sent\n",
+				sc_adapter[card]->devicename);
+			return -EIO;
+		}
+		return 0;
+	}
+	else {
+		pr_debug("%s: Send loopback message failed\n",
+			sc_adapter[card]->devicename);
+		return -EIO;
+	}
+
+}
+
+/*
+ * start the onboard firmware
+ */
+int startproc(int card) 
+{
+	int status;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*
+	 * send start msg 
+	 */
+       	status = sendmessage(card, CMPID,cmReqType2,
+			  cmReqClass0,
+			  cmReqStartProc,
+			  0,0,NULL);
+	pr_debug("%s: Sent startProc\n", sc_adapter[card]->devicename);
+	
+	return status;
+}
+
+
+int loadproc(int card, char *data) 
+{
+	return -1;
+}
+
+
+/*
+ * Dials the number passed in 
+ */
+int dial(int card, unsigned long channel, setup_parm setup) 
+{
+	int status;
+	char Phone[48];
+  
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*extract ISDN number to dial from eaz/msn string*/ 
+	strcpy(Phone,setup.phone); 
+
+	/*send the connection message*/
+	status = sendmessage(card, CEPID,ceReqTypePhy,
+				ceReqClass1,
+				ceReqPhyConnect,
+				(unsigned char) channel+1, 
+				strlen(Phone),
+				(unsigned int *) Phone);
+
+	pr_debug("%s: Dialing %s on channel %d\n",
+		sc_adapter[card]->devicename, Phone, channel+1);
+	
+	return status;
+}
+
+/*
+ * Answer an incoming call 
+ */
+int answer(int card, unsigned long channel) 
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	if(setup_buffers(card, channel+1, BUFFER_SIZE)) {
+		hangup(card, channel+1);
+		return -ENOBUFS;
+	}
+
+	indicate_status(card, ISDN_STAT_BCONN,channel,NULL);
+	pr_debug("%s: Answered incoming call on channel %s\n",
+		sc_adapter[card]->devicename, channel+1);
+	return 0;
+}
+
+/*
+ * Hangup up the call on specified channel
+ */
+int hangup(int card, unsigned long channel) 
+{
+	int status;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	status = sendmessage(card, CEPID, ceReqTypePhy,
+						 ceReqClass1,
+						 ceReqPhyDisconnect,
+						 (unsigned char) channel+1,
+						 0,
+						 NULL);
+	pr_debug("%s: Sent HANGUP message to channel %d\n",
+		sc_adapter[card]->devicename, channel+1);
+	return status;
+}
+
+/*
+ * Set the layer 2 protocol (X.25, HDLC, Raw)
+ */
+int setl2(int card, unsigned long arg) 
+{
+	int status =0;
+	int protocol,channel;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+	protocol = arg >> 8;
+	channel = arg & 0xff;
+	sc_adapter[card]->channel[channel].l2_proto = protocol;
+	pr_debug("%s: Level 2 protocol for channel %d set to %s from %d\n",
+		sc_adapter[card]->devicename, channel+1,
+		l2protos[sc_adapter[card]->channel[channel].l2_proto],protocol);
+
+	/*
+	 * check that the adapter is also set to the correct protocol
+	 */
+	pr_debug("%s: Sending GetFrameFormat for channel %d\n",
+		sc_adapter[card]->devicename, channel+1);
+	status = sendmessage(card, CEPID, ceReqTypeCall,
+ 				ceReqClass0,
+ 				ceReqCallGetFrameFormat,
+ 				(unsigned char)channel+1,
+ 				1,
+ 				(unsigned int *) protocol);
+	if(status) 
+		return status;
+	return 0;
+}
+
+/*
+ * Set the layer 3 protocol
+ */
+int setl3(int card, unsigned long channel) 
+{
+	int protocol = channel >> 8;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	sc_adapter[card]->channel[channel].l3_proto = protocol;
+	pr_debug("%s: Level 3 protocol for channel %d set to %s\n",
+		sc_adapter[card]->devicename, channel+1, l3protos[protocol]);
+	return 0;
+}
+
+int acceptb(int card, unsigned long channel)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	if(setup_buffers(card, channel+1, BUFFER_SIZE))
+	{
+		hangup(card, channel+1);
+		return -ENOBUFS;
+	}
+
+	pr_debug("%s: B-Channel connection accepted on channel %d\n",
+		sc_adapter[card]->devicename, channel+1);
+	indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
+	return 0;
+}
+
+int clreaz(int card, unsigned long arg)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(sc_adapter[card]->channel[arg].eazlist, "");
+	sc_adapter[card]->channel[arg].eazclear = 1;
+	pr_debug("%s: EAZ List cleared for channel %d\n",
+		sc_adapter[card]->devicename, arg+1);
+	return 0;
+}
+
+int seteaz(int card, unsigned long arg, char *num)
+{
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	strcpy(sc_adapter[card]->channel[arg].eazlist, num);
+	sc_adapter[card]->channel[arg].eazclear = 0;
+	pr_debug("%s: EAZ list for channel %d set to: %s\n",
+		sc_adapter[card]->devicename, arg+1,
+		sc_adapter[card]->channel[arg].eazlist);
+	return 0;
+}
+
+int reset(int card)
+{
+	unsigned long flags;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+
+	if(sc_adapter[card]->EngineUp) {
+		del_timer(&sc_adapter[card]->stat_timer);
+	}
+
+	sc_adapter[card]->EngineUp = 0;
+
+	spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+	init_timer(&sc_adapter[card]->reset_timer);
+	sc_adapter[card]->reset_timer.function = check_reset;
+	sc_adapter[card]->reset_timer.data = card;
+	sc_adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
+	add_timer(&sc_adapter[card]->reset_timer);
+	spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+	outb(0x1,sc_adapter[card]->ioport[SFT_RESET]);
+
+	pr_debug("%s: Adapter Reset\n", sc_adapter[card]->devicename);
+	return 0;
+}
+
+void flushreadfifo (int card)
+{
+	while(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA)
+		inb(sc_adapter[card]->ioport[FIFO_READ]);
+}
diff --git a/drivers/isdn/sc/debug.c b/drivers/isdn/sc/debug.c
new file mode 100644
index 0000000..1a992a7
--- /dev/null
+++ b/drivers/isdn/sc/debug.c
@@ -0,0 +1,46 @@
+/* $Id: debug.c,v 1.5.6.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+int dbg_level = 0;
+static char dbg_funcname[255];
+
+void dbg_endfunc(void)
+{
+	if (dbg_level) {
+		printk("<-- Leaving function %s\n", dbg_funcname);
+		strcpy(dbg_funcname, "");
+	}
+}
+
+void dbg_func(char *func)
+{
+	strcpy(dbg_funcname, func);
+	if(dbg_level)
+		printk("--> Entering function %s\n", dbg_funcname);
+}
+
+inline void pullphone(char *dn, char *str)
+{
+	int i = 0;
+
+	while(dn[i] != ',')
+		str[i] = dn[i], i++;
+	str[i] = 0x0;
+}
diff --git a/drivers/isdn/sc/debug.h b/drivers/isdn/sc/debug.h
new file mode 100644
index 0000000..e9db96e
--- /dev/null
+++ b/drivers/isdn/sc/debug.h
@@ -0,0 +1,19 @@
+/* $Id: debug.h,v 1.2.8.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+#define FREE_IRQ(a,b) free_irq(a,b)
diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c
new file mode 100644
index 0000000..5b8c7c1a
--- /dev/null
+++ b/drivers/isdn/sc/event.c
@@ -0,0 +1,69 @@
+/* $Id: event.c,v 1.4.8.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern int cinst;
+extern board *sc_adapter[];
+
+#ifdef DEBUG
+static char *events[] = { "ISDN_STAT_STAVAIL",
+			  "ISDN_STAT_ICALL",
+			  "ISDN_STAT_RUN",
+			  "ISDN_STAT_STOP",
+			  "ISDN_STAT_DCONN",
+			  "ISDN_STAT_BCONN",
+			  "ISDN_STAT_DHUP",
+			  "ISDN_STAT_BHUP",
+			  "ISDN_STAT_CINF",
+			  "ISDN_STAT_LOAD",
+			  "ISDN_STAT_UNLOAD",
+			  "ISDN_STAT_BSENT",
+			  "ISDN_STAT_NODCH",
+			  "ISDN_STAT_ADDCH",
+			  "ISDN_STAT_CAUSE" };
+#endif
+
+int indicate_status(int card, int event,ulong Channel,char *Data)
+{
+	isdn_ctrl cmd;
+
+	pr_debug("%s: Indicating event %s on Channel %d\n",
+		sc_adapter[card]->devicename, events[event-256], Channel);
+	if (Data != NULL){
+		pr_debug("%s: Event data: %s\n", sc_adapter[card]->devicename,
+			Data);
+		switch (event) {
+			case ISDN_STAT_BSENT:
+				memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length));
+				break;
+			case ISDN_STAT_ICALL:
+				memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
+				break;
+			default:
+				strcpy(cmd.parm.num, Data);
+		}
+	}
+
+	cmd.command = event;
+	cmd.driver = sc_adapter[card]->driverId;
+	cmd.arg = Channel;
+	return sc_adapter[card]->card->statcallb(&cmd);
+}
diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h
new file mode 100644
index 0000000..9e6d530
--- /dev/null
+++ b/drivers/isdn/sc/hardware.h
@@ -0,0 +1,110 @@
+/*
+ * Hardware specific macros, defines and structures
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef HARDWARE_H
+#define HARDWARE_H
+
+#include <asm/param.h>			/* For HZ */
+
+/*
+ * General hardware parameters common to all ISA adapters
+ */
+
+#define MAX_CARDS	4		/* The maximum number of cards to
+					   control or probe for. */
+
+#define SIGNATURE	0x87654321	/* Board reset signature */
+#define SIG_OFFSET	0x1004		/* Where to find signature in shared RAM */
+#define TRACE_OFFSET	0x1008		/* Trace enable word offset in shared RAM */
+#define BUFFER_OFFSET	0x1800		/* Beginning of buffers */
+
+/* I/O Port parameters */
+#define IOBASE_MIN	0x180		/* Lowest I/O port address */
+#define IOBASE_MAX	0x3C0		/* Highest I/O port address */
+#define IOBASE_OFFSET	0x20		/* Inter-board I/O port gap used during
+					   probing */
+#define FIFORD_OFFSET	0x0
+#define FIFOWR_OFFSET	0x400
+#define FIFOSTAT_OFFSET	0x1000
+#define RESET_OFFSET	0x2800
+#define PG0_OFFSET	0x3000		/* Offset from I/O Base for Page 0 register */
+#define PG1_OFFSET	0x3400		/* Offset from I/O Base for Page 1 register */
+#define PG2_OFFSET	0x3800		/* Offset from I/O Base for Page 2 register */
+#define PG3_OFFSET	0x3C00		/* Offset from I/O Base for Page 3 register */
+
+#define FIFO_READ	0		/* FIFO Read register */
+#define FIFO_WRITE	1		/* FIFO Write rgister */
+#define LO_ADDR_PTR	2		/* Extended RAM Low Addr Pointer */
+#define HI_ADDR_PTR	3		/* Extended RAM High Addr Pointer */
+#define NOT_USED_1	4
+#define FIFO_STATUS	5		/* FIFO Status Register */
+#define NOT_USED_2	6
+#define MEM_OFFSET	7
+#define SFT_RESET	10		/* Reset Register */
+#define EXP_BASE	11		/* Shared RAM Base address */
+#define EXP_PAGE0	12		/* Shared RAM Page0 register */
+#define EXP_PAGE1	13		/* Shared RAM Page1 register */
+#define EXP_PAGE2	14		/* Shared RAM Page2 register */
+#define EXP_PAGE3	15		/* Shared RAM Page3 register */
+#define IRQ_SELECT	16		/* IRQ selection register */
+#define MAX_IO_REGS	17		/* Total number of I/O ports */
+
+/* FIFO register values */
+#define RF_HAS_DATA	0x01		/* fifo has data */
+#define RF_QUART_FULL	0x02		/* fifo quarter full */
+#define RF_HALF_FULL	0x04		/* fifo half full */
+#define RF_NOT_FULL	0x08		/* fifo not full */
+#define WF_HAS_DATA	0x10		/* fifo has data */
+#define WF_QUART_FULL	0x20		/* fifo quarter full */
+#define WF_HALF_FULL	0x40		/* fifo half full */
+#define WF_NOT_FULL	0x80		/* fifo not full */
+
+/* Shared RAM parameters */
+#define SRAM_MIN	0xC0000         /* Lowest host shared RAM address */
+#define SRAM_MAX	0xEFFFF         /* Highest host shared RAM address */
+#define SRAM_PAGESIZE	0x4000		/* Size of one RAM page (16K) */
+
+/* Shared RAM buffer parameters */
+#define BUFFER_SIZE	0x800		/* The size of a buffer in bytes */
+#define BUFFER_BASE	BUFFER_OFFSET	/* Offset from start of shared RAM
+					   where buffer start */
+#define BUFFERS_MAX	16		/* Maximum number of send/receive
+					   buffers per channel */
+#define HDLC_PROTO	0x01		/* Frame Format for Layer 2 */
+
+#define BRI_BOARD	0
+#define POTS_BOARD	1
+#define PRI_BOARD	2
+
+/*
+ * Specific hardware parameters for the DataCommute/BRI
+ */
+#define BRI_CHANNELS	2		/* Number of B channels */
+#define BRI_BASEPG_VAL	0x98
+#define BRI_MAGIC	0x60000		/* Magic Number */
+#define BRI_MEMSIZE	0x10000		/* Ammount of RAM (64K) */
+#define BRI_PARTNO	"72-029"
+#define BRI_FEATURES	ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+/*
+ * Specific hardware parameters for the DataCommute/PRI
+ */
+#define PRI_CHANNELS	23		/* Number of B channels */
+#define PRI_BASEPG_VAL	0x88
+#define PRI_MAGIC	0x20000		/* Magic Number */
+#define PRI_MEMSIZE	0x100000	/* Amount of RAM (1M) */
+#define PRI_PARTNO	"72-030"
+#define PRI_FEATURES	ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+
+/*
+ * Some handy macros
+ */
+
+/* Determine if a channel number is valid for the adapter */
+#define IS_VALID_CHANNEL(y,x)	((x>0) && (x <= sc_adapter[y]->channels))
+
+#endif
diff --git a/drivers/isdn/sc/includes.h b/drivers/isdn/sc/includes.h
new file mode 100644
index 0000000..4611da6
--- /dev/null
+++ b/drivers/isdn/sc/includes.h
@@ -0,0 +1,18 @@
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include "debug.h"
diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c
new file mode 100644
index 0000000..efefede
--- /dev/null
+++ b/drivers/isdn/sc/init.c
@@ -0,0 +1,571 @@
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "includes.h"
+#include "hardware.h"
+#include "card.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card");
+MODULE_AUTHOR("Spellcaster Telecommunications Inc.");
+MODULE_LICENSE("GPL");
+
+board *sc_adapter[MAX_CARDS];
+int cinst;
+
+static char devname[] = "scX";
+const char version[] = "2.0b1";
+
+const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
+
+/* insmod set parameters */
+static unsigned int io[] = {0,0,0,0};
+static unsigned char irq[] = {0,0,0,0};
+static unsigned long ram[] = {0,0,0,0};
+static int do_reset = 0;
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(ram, int, NULL, 0);
+module_param(do_reset, bool, 0);
+
+static int sup_irq[] = { 11, 10, 9, 5, 12, 14, 7, 3, 4, 6 };
+#define MAX_IRQS	10
+
+extern irqreturn_t interrupt_handler(int, void *, struct pt_regs *);
+extern int sndpkt(int, int, int, struct sk_buff *);
+extern int command(isdn_ctrl *);
+extern int indicate_status(int, int, ulong, char*);
+extern int reset(int);
+
+int identify_board(unsigned long, unsigned int);
+
+int irq_supported(int irq_x)
+{
+	int i;
+	for(i=0 ; i < MAX_IRQS ; i++) {
+		if(sup_irq[i] == irq_x)
+			return 1;
+	}
+	return 0;
+}
+
+static int __init sc_init(void)
+{
+	int b = -1;
+	int i, j;
+	int status = -ENODEV;
+
+	unsigned long memsize = 0;
+	unsigned long features = 0;
+	isdn_if *interface;
+	unsigned char channels;
+	unsigned char pgport;
+	unsigned long magic;
+	int model;
+	int last_base = IOBASE_MIN;
+	int probe_exhasted = 0;
+
+#ifdef MODULE
+	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
+#else
+	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
+#endif
+	pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
+
+	while(b++ < MAX_CARDS - 1) {
+		pr_debug("Probing for adapter #%d\n", b);
+		/*
+		 * Initialize reusable variables
+		 */
+		model = -1;
+		magic = 0;
+		channels = 0;
+		pgport = 0;
+
+		/* 
+		 * See if we should probe for IO base 
+		 */
+		pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
+			io[b] == 0 ? "will" : "won't");
+		if(io[b]) {
+			/*
+			 * No, I/O Base has been provided
+			 */
+			for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+				if(!request_region(io[b] + i * 0x400, 1, "sc test")) {
+					pr_debug("check_region for 0x%x failed\n", io[b] + i * 0x400);
+					io[b] = 0;
+					break;
+				} else
+					release_region(io[b] + i * 0x400, 1);
+			}
+
+			/*
+			 * Confirm the I/O Address with a test
+			 */
+			if(io[b] == 0) {
+				pr_debug("I/O Address 0x%x is in use.\n");
+				continue;
+			}
+
+			outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+			if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
+				pr_debug("I/O Base 0x%x fails test\n");
+				continue;
+			}
+		}
+		else {
+			/*
+			 * Yes, probe for I/O Base
+			 */
+			if(probe_exhasted) {
+				pr_debug("All probe addresses exhasted, skipping\n");
+				continue;
+			}
+			pr_debug("Probing for I/O...\n");
+			for (i = last_base ; i <= IOBASE_MAX ; i += IOBASE_OFFSET) {
+				int found_io = 1;
+				if (i == IOBASE_MAX) {
+					probe_exhasted = 1; /* No more addresses to probe */
+					pr_debug("End of Probes\n");
+				}
+				last_base = i + IOBASE_OFFSET;
+				pr_debug("  checking 0x%x...", i);
+				for ( j = 0 ; j < MAX_IO_REGS - 1 ; j++) {
+					if(!request_region(i + j * 0x400, 1, "sc test")) {
+						pr_debug("Failed\n");
+						found_io = 0;
+						break;
+					} else
+						release_region(i + j * 0x400, 1);
+				}	
+
+				if(found_io) {
+					io[b] = i;
+					outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+					if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { 
+						pr_debug("Failed by test\n");
+						continue;
+					}
+					pr_debug("Passed\n");
+					break;
+				}
+			}
+			if(probe_exhasted) {
+				continue;
+			}
+		}
+
+		/*
+		 * See if we should probe for shared RAM
+		 */
+		if(do_reset) {
+			pr_debug("Doing a SAFE probe reset\n");
+			outb(0xFF, io[b] + RESET_OFFSET);
+			msleep_interruptible(10000);
+		}
+		pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b],
+			ram[b] == 0 ? "will" : "won't");
+
+		if(ram[b]) {
+			/*
+			 * No, the RAM base has been provided
+			 * Just look for a signature and ID the
+			 * board model
+			 */
+			if(request_region(ram[b], SRAM_PAGESIZE, "sc test")) {
+				pr_debug("request_region for RAM base 0x%x succeeded\n", ram[b]);
+			 	model = identify_board(ram[b], io[b]);
+				release_region(ram[b], SRAM_PAGESIZE);
+			}
+		}
+		else {
+			/*
+			 * Yes, probe for free RAM and look for
+			 * a signature and id the board model
+			 */
+			for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) {
+				pr_debug("Checking RAM address 0x%x...\n", i);
+				if(request_region(i, SRAM_PAGESIZE, "sc test")) {
+					pr_debug("  check_region succeeded\n");
+					model = identify_board(i, io[b]);
+					release_region(i, SRAM_PAGESIZE);
+					if (model >= 0) {
+						pr_debug("  Identified a %s\n",
+							boardname[model]);
+						ram[b] = i;
+						break;
+					}
+					pr_debug("  Unidentifed or inaccessible\n");
+					continue;
+				}
+				pr_debug("  request failed\n");
+			}
+		}
+		/*
+		 * See if we found free RAM and the board model
+		 */
+		if(!ram[b] || model < 0) {
+			/*
+			 * Nope, there was no place in RAM for the
+			 * board, or it couldn't be identified
+			 */
+			 pr_debug("Failed to find an adapter at 0x%x\n", ram[b]);
+			 continue;
+		}
+
+		/*
+		 * Set the board's magic number, memory size and page register
+		 */
+		switch(model) {
+		case PRI_BOARD:
+			channels = 23;
+			magic = 0x20000;
+			memsize = 0x100000;
+			features = PRI_FEATURES;
+			break;
+
+		case BRI_BOARD:
+		case POTS_BOARD:
+			channels = 2;
+			magic = 0x60000;
+			memsize = 0x10000;
+			features = BRI_FEATURES;
+			break;
+		}
+		switch(ram[b] >> 12 & 0x0F) {
+		case 0x0:
+			pr_debug("RAM Page register set to EXP_PAGE0\n");
+			pgport = EXP_PAGE0;
+			break;
+
+		case 0x4:
+			pr_debug("RAM Page register set to EXP_PAGE1\n");
+			pgport = EXP_PAGE1;
+			break;
+
+		case 0x8:
+			pr_debug("RAM Page register set to EXP_PAGE2\n");
+			pgport = EXP_PAGE2;
+			break;
+
+		case 0xC:
+			pr_debug("RAM Page register set to EXP_PAGE3\n");
+			pgport = EXP_PAGE3;
+			break;
+
+		default:
+			pr_debug("RAM base address doesn't fall on 16K boundary\n");
+			continue;
+		}
+
+		pr_debug("current IRQ: %d  b: %d\n",irq[b],b);
+
+		/*
+		 * Make sure we got an IRQ
+		 */
+		if(!irq[b]) {
+			/*
+			 * No interrupt could be used
+			 */
+			pr_debug("Failed to acquire an IRQ line\n");
+			continue;
+		}
+
+		/*
+		 * Horray! We found a board, Make sure we can register
+		 * it with ISDN4Linux
+		 */
+		interface = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+		if (interface == NULL) {
+			/*
+			 * Oops, can't malloc isdn_if
+			 */
+			continue;
+		}
+		memset(interface, 0, sizeof(isdn_if));
+
+		interface->owner = THIS_MODULE;
+		interface->hl_hdrlen = 0;
+		interface->channels = channels;
+		interface->maxbufsize = BUFFER_SIZE;
+		interface->features = features;
+		interface->writebuf_skb = sndpkt;
+		interface->writecmd = NULL;
+		interface->command = command;
+		strcpy(interface->id, devname);
+		interface->id[2] = '0' + cinst;
+
+		/*
+		 * Allocate the board structure
+		 */
+		sc_adapter[cinst] = kmalloc(sizeof(board), GFP_KERNEL);
+		if (sc_adapter[cinst] == NULL) {
+			/*
+			 * Oops, can't alloc memory for the board
+			 */
+			kfree(interface);
+			continue;
+		}
+		memset(sc_adapter[cinst], 0, sizeof(board));
+		spin_lock_init(&sc_adapter[cinst]->lock);
+
+		if(!register_isdn(interface)) {
+			/*
+			 * Oops, couldn't register for some reason
+			 */
+			kfree(interface);
+			kfree(sc_adapter[cinst]);
+			continue;
+		}
+
+		sc_adapter[cinst]->card = interface;
+		sc_adapter[cinst]->driverId = interface->channels;
+		strcpy(sc_adapter[cinst]->devicename, interface->id);
+		sc_adapter[cinst]->nChannels = channels;
+		sc_adapter[cinst]->ramsize = memsize;
+		sc_adapter[cinst]->shmem_magic = magic;
+		sc_adapter[cinst]->shmem_pgport = pgport;
+		sc_adapter[cinst]->StartOnReset = 1;
+
+		/*
+		 * Allocate channels status structures
+		 */
+		sc_adapter[cinst]->channel = kmalloc(sizeof(bchan) * channels, GFP_KERNEL);
+		if (sc_adapter[cinst]->channel == NULL) {
+			/*
+			 * Oops, can't alloc memory for the channels
+			 */
+			indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL);	/* Fix me */
+			kfree(interface);
+			kfree(sc_adapter[cinst]);
+			continue;
+		}
+		memset(sc_adapter[cinst]->channel, 0, sizeof(bchan) * channels);
+
+		/*
+		 * Lock down the hardware resources
+		 */
+		sc_adapter[cinst]->interrupt = irq[b];
+		if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
+				SA_INTERRUPT, interface->id, NULL))
+		{
+			kfree(sc_adapter[cinst]->channel);
+			indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL);	/* Fix me */
+			kfree(interface);
+			kfree(sc_adapter[cinst]);
+			continue;
+			
+		}
+		sc_adapter[cinst]->iobase = io[b];
+		for(i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+			sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400;
+			request_region(sc_adapter[cinst]->ioport[i], 1,
+					interface->id);
+			pr_debug("Requesting I/O Port %#x\n",
+				sc_adapter[cinst]->ioport[i]);
+		}
+		sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
+		request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1,
+				interface->id);
+		pr_debug("Requesting I/O Port %#x\n",
+				sc_adapter[cinst]->ioport[IRQ_SELECT]);
+		sc_adapter[cinst]->rambase = ram[b];
+		request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE,
+				interface->id);
+
+		pr_info("  %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n", 
+			sc_adapter[cinst]->devicename,
+			sc_adapter[cinst]->driverId,
+			boardname[model], channels, irq[b], io[b], ram[b]);
+		
+		/*
+		 * reset the adapter to put things in motion
+		 */
+		reset(cinst);
+
+		cinst++;
+		status = 0;
+	}
+	if (status) 
+		pr_info("Failed to find any adapters, driver unloaded\n");
+	return status;
+}
+
+static void __exit sc_exit(void)
+{
+	int i, j;
+
+	for(i = 0 ; i < cinst ; i++) {
+		pr_debug("Cleaning up after adapter %d\n", i);
+		/*
+		 * kill the timers
+		 */
+		del_timer(&(sc_adapter[i]->reset_timer));
+		del_timer(&(sc_adapter[i]->stat_timer));
+
+		/*
+		 * Tell I4L we're toast
+		 */
+		indicate_status(i, ISDN_STAT_STOP, 0, NULL);
+		indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
+
+		/*
+		 * Release shared RAM
+		 */
+		release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE);
+
+		/*
+		 * Release the IRQ
+		 */
+		FREE_IRQ(sc_adapter[i]->interrupt, NULL);
+
+		/*
+		 * Reset for a clean start
+		 */
+		outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]);
+
+		/*
+		 * Release the I/O Port regions
+		 */
+		for(j = 0 ; j < MAX_IO_REGS - 1; j++) {
+			release_region(sc_adapter[i]->ioport[j], 1);
+			pr_debug("Releasing I/O Port %#x\n",
+				sc_adapter[i]->ioport[j]);
+		}
+		release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1);
+		pr_debug("Releasing I/O Port %#x\n",
+			sc_adapter[i]->ioport[IRQ_SELECT]);
+
+		/*
+		 * Release any memory we alloced
+		 */
+		kfree(sc_adapter[i]->channel);
+		kfree(sc_adapter[i]->card);
+		kfree(sc_adapter[i]);
+	}
+	pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
+}
+
+int identify_board(unsigned long rambase, unsigned int iobase) 
+{
+	unsigned int pgport;
+	unsigned long sig;
+	DualPortMemory *dpm;
+	RspMessage rcvmsg;
+	ReqMessage sndmsg;
+	HWConfig_pl hwci;
+	int x;
+
+	pr_debug("Attempting to identify adapter @ 0x%x io 0x%x\n",
+		rambase, iobase);
+
+	/*
+	 * Enable the base pointer
+	 */
+	outb(rambase >> 12, iobase + 0x2c00);
+
+	switch(rambase >> 12 & 0x0F) {
+	case 0x0:
+		pgport = iobase + PG0_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
+		break;
+		
+	case 0x4:
+		pgport = iobase + PG1_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
+		break;
+
+	case 0x8:
+		pgport = iobase + PG2_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
+		break;
+
+	case 0xC:
+		pgport = iobase + PG3_OFFSET;
+		pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
+		break;
+	default:
+		pr_debug("Invalid rambase 0x%lx\n", rambase);
+		return -1;
+	}
+
+	/*
+	 * Try to identify a PRI card
+	 */
+	outb(PRI_BASEPG_VAL, pgport);
+	msleep_interruptible(1000);
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+	if(sig == SIGNATURE)
+		return PRI_BOARD;
+
+	/*
+	 * Try to identify a PRI card
+	 */
+	outb(BRI_BASEPG_VAL, pgport);
+	msleep_interruptible(1000);
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+	if(sig == SIGNATURE)
+		return BRI_BOARD;
+
+	return -1;
+
+	/*
+	 * Try to spot a card
+	 */
+	sig = readl(rambase + SIG_OFFSET);
+	pr_debug("Looking for a signature, got 0x%x\n", sig);
+	if(sig != SIGNATURE)
+		return -1;
+
+	dpm = (DualPortMemory *) rambase;
+
+	memset(&sndmsg, 0, MSG_LEN);
+	sndmsg.msg_byte_cnt = 3;
+	sndmsg.type = cmReqType1;
+	sndmsg.class = cmReqClass0;
+	sndmsg.code = cmReqHWConfig;
+	memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
+	outb(0, iobase + 0x400);
+	pr_debug("Sent HWConfig message\n");
+	/*
+	 * Wait for the response
+	 */
+	x = 0;
+	while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		x++;
+	}
+	if(x == 100) {
+		pr_debug("Timeout waiting for response\n");
+		return -1;
+	}
+
+	memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
+	pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
+	memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
+	pr_debug("Hardware Config: Interface: %s, RAM Size: %d, Serial: %s\n"
+		 "                 Part: %s, Rev: %s\n",
+		 hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
+		 hwci.serial_no, hwci.part_no, hwci.rev_no);
+
+	if(!strncmp(PRI_PARTNO, hwci.part_no, 6))
+		return PRI_BOARD;
+	if(!strncmp(BRI_PARTNO, hwci.part_no, 6))
+		return BRI_BOARD;
+		
+	return -1;
+}
+
+module_init(sc_init);
+module_exit(sc_exit);
diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c
new file mode 100644
index 0000000..e5e164a
--- /dev/null
+++ b/drivers/isdn/sc/interrupt.c
@@ -0,0 +1,260 @@
+/* $Id: interrupt.c,v 1.4.8.3 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include <linux/interrupt.h>
+
+extern int indicate_status(int, int, ulong, char *);
+extern void check_phystat(unsigned long);
+extern int receivemessage(int, RspMessage *);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+        unsigned int, unsigned int, unsigned int, unsigned int *);
+extern void rcvpkt(int, RspMessage *);
+
+extern int cinst;
+extern board *sc_adapter[];
+
+int get_card_from_irq(int irq)
+{
+	int i;
+
+	for(i = 0 ; i < cinst ; i++) {
+		if(sc_adapter[i]->interrupt == irq)
+			return i;
+	}
+	return -1;
+}
+
+/*
+ * 
+ */
+irqreturn_t interrupt_handler(int interrupt, void *cardptr, struct pt_regs *regs)
+{
+
+	RspMessage rcvmsg;
+	int channel;
+	int card;
+
+	card = get_card_from_irq(interrupt);
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return IRQ_NONE;
+	}
+
+	pr_debug("%s: Entered Interrupt handler\n",
+			sc_adapter[card]->devicename);
+	
+ 	/*
+	 * Pull all of the waiting messages off the response queue
+	 */
+	while (!receivemessage(card, &rcvmsg)) {
+		/*
+		 * Push the message to the adapter structure for
+		 * send_and_receive to snoop
+		 */
+		if(sc_adapter[card]->want_async_messages)
+			memcpy(&(sc_adapter[card]->async_msg),
+					&rcvmsg, sizeof(RspMessage));
+
+		channel = (unsigned int) rcvmsg.phy_link_no;
+		
+		/*
+		 * Trap Invalid request messages
+		 */
+		if(IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) {
+			pr_debug("%s: Invalid request Message, rsp_status = %d\n", 
+				sc_adapter[card]->devicename,
+				rcvmsg.rsp_status);
+			break;	
+		}
+		
+		/*
+		 * Check for a linkRead message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
+		{
+			pr_debug("%s: Received packet 0x%x bytes long at 0x%x\n",
+						sc_adapter[card]->devicename,
+						rcvmsg.msg_data.response.msg_len,
+						rcvmsg.msg_data.response.buff_offset);
+			rcvpkt(card, &rcvmsg);
+			continue;
+
+		}
+
+		/*
+		 * Handle a write acknoledgement
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) {
+			pr_debug("%s: Packet Send ACK on channel %d\n",
+				sc_adapter[card]->devicename,
+				rcvmsg.phy_link_no);
+			sc_adapter[card]->channel[rcvmsg.phy_link_no-1].free_sendbufs++;
+			continue;
+		}
+
+		/*
+		 * Handle a connection message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect)) 
+		{
+			unsigned int callid;
+			setup_parm setup;	
+			pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n",
+						sc_adapter[card]->devicename,
+						rcvmsg.phy_link_no,
+						rcvmsg.rsp_status,
+						rcvmsg.msg_data.byte_array[2]);
+			
+			memcpy(&callid,rcvmsg.msg_data.byte_array,sizeof(int));
+			if(callid>=0x8000 && callid<=0xFFFF)
+			{		
+				pr_debug("%s: Got Dial-Out Rsp\n",
+					sc_adapter[card]->devicename);
+				indicate_status(card, ISDN_STAT_DCONN,
+						(unsigned long)rcvmsg.phy_link_no-1,NULL);
+				
+			}
+			else if(callid>=0x0000 && callid<=0x7FFF)
+			{
+				pr_debug("%s: Got Incoming Call\n",
+						sc_adapter[card]->devicename);
+				strcpy(setup.phone,&(rcvmsg.msg_data.byte_array[4]));
+				strcpy(setup.eazmsn,
+					sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn);
+				setup.si1 = 7;
+				setup.si2 = 0;
+				setup.plan = 0;
+				setup.screen = 0;
+
+				indicate_status(card, ISDN_STAT_ICALL,(unsigned long)rcvmsg.phy_link_no-1,(char *)&setup);
+				indicate_status(card, ISDN_STAT_DCONN,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			}
+			continue;
+		}
+
+		/*
+		 * Handle a disconnection message
+		 */
+		if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect)) 
+		{
+			pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n",
+						sc_adapter[card]->devicename,
+						rcvmsg.phy_link_no,
+						rcvmsg.rsp_status,
+					 	rcvmsg.msg_data.byte_array[2]);
+
+			indicate_status(card, ISDN_STAT_BHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			indicate_status(card, ISDN_STAT_DHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+			continue;
+
+		}
+
+		/*
+		 * Handle a startProc engine up message
+		 */
+		if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) {
+			pr_debug("%s: Received EngineUp message\n",
+				sc_adapter[card]->devicename);
+			sc_adapter[card]->EngineUp = 1;
+			sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,1,0,NULL);
+			sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,2,0,NULL);
+			init_timer(&sc_adapter[card]->stat_timer);
+			sc_adapter[card]->stat_timer.function = check_phystat;
+			sc_adapter[card]->stat_timer.data = card;
+			sc_adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
+			add_timer(&sc_adapter[card]->stat_timer);
+			continue;
+		}
+
+		/*
+		 * Start proc response
+		 */
+		if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) {
+			pr_debug("%s: StartProc Response Status %d\n",
+				sc_adapter[card]->devicename,
+				rcvmsg.rsp_status);
+			continue;
+		}
+
+		/*
+		 * Handle a GetMyNumber Rsp
+		 */
+		if (IS_CE_MESSAGE(rcvmsg,Call,0,GetMyNumber)){
+			strcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn,rcvmsg.msg_data.byte_array);
+			continue;
+		}
+			
+		/*
+		 * PhyStatus response
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) {
+			unsigned int b1stat, b2stat;
+
+			/*
+			 * Covert the message data to the adapter->phystat code
+			 */
+			b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0];
+			b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1];
+
+			sc_adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */
+			pr_debug("%s: PhyStat is 0x%2x\n",
+				sc_adapter[card]->devicename,
+				sc_adapter[card]->nphystat);
+			continue;
+		}
+
+
+		/* 
+		 * Handle a GetFramFormat
+		 */
+		if(IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) {
+			if(rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) {
+				unsigned int proto = HDLC_PROTO;
+				/*
+				 * Set board format to HDLC if it wasn't already
+				 */
+				pr_debug("%s: current frame format: 0x%x, will change to HDLC\n",
+						sc_adapter[card]->devicename,
+					rcvmsg.msg_data.byte_array[0]);
+				sendmessage(card, CEPID, ceReqTypeCall,
+						ceReqClass0,
+						ceReqCallSetFrameFormat,
+						(unsigned char) channel +1,
+						1,&proto);
+				}
+			continue;
+		}
+
+		/*
+		 * Hmm...
+		 */
+		pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n",
+			sc_adapter[card]->devicename,
+			rcvmsg.type, rcvmsg.class, rcvmsg.code,
+			rcvmsg.phy_link_no);
+
+	}	/* while */
+
+	pr_debug("%s: Exiting Interrupt Handler\n",
+			sc_adapter[card]->devicename);
+	return IRQ_HANDLED;
+}
diff --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c
new file mode 100644
index 0000000..1371a99
--- /dev/null
+++ b/drivers/isdn/sc/ioctl.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+extern int indicate_status(int, int, unsigned long, char *);
+extern int startproc(int);
+extern int loadproc(int, char *record);
+extern int reset(int);
+extern int send_and_receive(int, unsigned int, unsigned char,unsigned char,
+		unsigned char,unsigned char, 
+		unsigned char, unsigned char *, RspMessage *, int);
+
+extern board *sc_adapter[];
+
+
+int GetStatus(int card, boardInfo *);
+
+/*
+ * Process private IOCTL messages (typically from scctrl)
+ */
+int sc_ioctl(int card, scs_ioctl *data)
+{
+	int		status;
+	RspMessage	*rcvmsg;
+	char		*spid;
+	char		*dn;
+	char		switchtype;
+	char		speed;
+
+	rcvmsg = kmalloc(sizeof(RspMessage), GFP_KERNEL);
+	if (!rcvmsg)
+		return -ENOMEM;
+
+	switch(data->command) {
+	case SCIOCRESET:	/* Perform a hard reset of the adapter */
+	{
+		pr_debug("%s: SCIOCRESET: ioctl received\n",
+			sc_adapter[card]->devicename);
+		sc_adapter[card]->StartOnReset = 0;
+		return (reset(card));
+	}
+
+	case SCIOCLOAD:
+	{
+		char *srec;
+
+		srec = kmalloc(SCIOC_SRECSIZE, GFP_KERNEL);
+		if (!srec) {
+			kfree(rcvmsg);
+			return -ENOMEM;
+		}
+		pr_debug("%s: SCIOLOAD: ioctl received\n",
+				sc_adapter[card]->devicename);
+		if(sc_adapter[card]->EngineUp) {
+			pr_debug("%s: SCIOCLOAD: command failed, LoadProc while engine running.\n",
+				sc_adapter[card]->devicename);
+			kfree(rcvmsg);
+			kfree(srec);
+			return -1;
+		}
+
+		/*
+		 * Get the SRec from user space
+		 */
+		if (copy_from_user(srec, data->dataptr, sizeof(srec))) {
+			kfree(rcvmsg);
+			kfree(srec);
+			return -EFAULT;
+		}
+
+		status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
+				0, sizeof(srec), srec, rcvmsg, SAR_TIMEOUT);
+		kfree(rcvmsg);
+		kfree(srec);
+
+		if(status) {
+			pr_debug("%s: SCIOCLOAD: command failed, status = %d\n", 
+				sc_adapter[card]->devicename, status);
+			return -1;
+		}
+		else {
+			pr_debug("%s: SCIOCLOAD: command successful\n",
+					sc_adapter[card]->devicename);
+			return 0;
+		}
+	}
+
+	case SCIOCSTART:
+	{
+		pr_debug("%s: SCIOSTART: ioctl received\n",
+				sc_adapter[card]->devicename);
+		if(sc_adapter[card]->EngineUp) {
+			pr_debug("%s: SCIOCSTART: command failed, engine already running.\n",
+				sc_adapter[card]->devicename);
+			return -1;
+		}
+
+		sc_adapter[card]->StartOnReset = 1;
+		startproc(card);
+		return 0;
+	}
+
+	case SCIOCSETSWITCH:
+	{
+		pr_debug("%s: SCIOSETSWITCH: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		/*
+		 * Get the switch type from user space
+		 */
+		if (copy_from_user(&switchtype, data->dataptr, sizeof(char))) {
+			kfree(rcvmsg);
+			return -EFAULT;
+		}
+
+		pr_debug("%s: SCIOCSETSWITCH: setting switch type to %d\n",
+			sc_adapter[card]->devicename,
+			switchtype);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType,
+						0, sizeof(char),&switchtype, rcvmsg, SAR_TIMEOUT);
+		if(!status && !(rcvmsg->rsp_status)) {
+			pr_debug("%s: SCIOCSETSWITCH: command successful\n",
+				sc_adapter[card]->devicename);
+			kfree(rcvmsg);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETSWITCH: command failed (status = %d)\n",
+				sc_adapter[card]->devicename, status);
+			kfree(rcvmsg);
+			return status;
+		}
+	}
+		
+	case SCIOCGETSWITCH:
+	{
+		pr_debug("%s: SCIOGETSWITCH: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		/*
+		 * Get the switch type from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, 
+			ceReqCallGetSwitchType, 0, 0, NULL, rcvmsg, SAR_TIMEOUT);
+		if (!status && !(rcvmsg->rsp_status)) {
+			pr_debug("%s: SCIOCGETSWITCH: command successful\n",
+					sc_adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSWITCH: command failed (status = %d)\n",
+				sc_adapter[card]->devicename, status);
+			kfree(rcvmsg);
+			return status;
+		}
+
+		switchtype = rcvmsg->msg_data.byte_array[0];
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if (copy_to_user(data->dataptr, &switchtype,
+				 sizeof(char))) {
+			kfree(rcvmsg);
+			return -EFAULT;
+		}
+
+		kfree(rcvmsg);
+		return 0;
+	}
+
+	case SCIOCGETSPID:
+	{
+		pr_debug("%s: SCIOGETSPID: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
+		if(!spid) {
+			kfree(rcvmsg);
+			return -ENOMEM;
+		}
+		/*
+		 * Get the spid from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID,
+					data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			pr_debug("%s: SCIOCGETSPID: command successful\n",
+					sc_adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n",
+				sc_adapter[card]->devicename, status);
+			kfree(rcvmsg);
+			return status;
+		}
+		strcpy(spid, rcvmsg->msg_data.byte_array);
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+		if (copy_to_user(data->dataptr, spid, SCIOC_SPIDSIZE)) {
+			kfree(spid);
+			kfree(rcvmsg);
+			return -EFAULT;
+		}
+
+		kfree(spid);
+		kfree(rcvmsg);
+		return 0;
+	}	
+
+	case SCIOCSETSPID:
+	{
+		pr_debug("%s: DCBIOSETSPID: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
+		if(!spid) {
+			kfree(rcvmsg);
+			return -ENOMEM;
+		}
+
+		/*
+		 * Get the spid from user space
+		 */
+		if (copy_from_user(spid, data->dataptr, SCIOC_SPIDSIZE)) {
+			kfree(rcvmsg);
+			return -EFAULT;
+		}
+
+		pr_debug("%s: SCIOCSETSPID: setting channel %d spid to %s\n", 
+			sc_adapter[card]->devicename, data->channel, spid);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, 
+			ceReqClass0, ceReqCallSetSPID, data->channel, 
+			strlen(spid), spid, rcvmsg, SAR_TIMEOUT);
+		if(!status && !(rcvmsg->rsp_status)) {
+			pr_debug("%s: SCIOCSETSPID: command successful\n", 
+				sc_adapter[card]->devicename);
+			kfree(rcvmsg);
+			kfree(spid);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETSPID: command failed (status = %d)\n",
+				sc_adapter[card]->devicename, status);
+			kfree(rcvmsg);
+			kfree(spid);
+			return status;
+		}
+	}
+
+	case SCIOCGETDN:
+	{
+		pr_debug("%s: SCIOGETDN: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		/*
+		 * Get the dn from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber,
+					data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			pr_debug("%s: SCIOCGETDN: command successful\n",
+					sc_adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETDN: command failed (status = %d)\n",
+				sc_adapter[card]->devicename, status);
+			kfree(rcvmsg);
+			return status;
+		}
+
+		dn = kmalloc(SCIOC_DNSIZE, GFP_KERNEL);
+		if (!dn) {
+			kfree(rcvmsg);
+			return -ENOMEM;
+		}
+		strcpy(dn, rcvmsg->msg_data.byte_array);
+		kfree(rcvmsg);
+
+		/*
+		 * Package the dn and send to user space
+		 */
+		if (copy_to_user(data->dataptr, dn, SCIOC_DNSIZE)) {
+			kfree(dn);
+			return -EFAULT;
+		}
+		kfree(dn);
+		return 0;
+	}	
+
+	case SCIOCSETDN:
+	{
+		pr_debug("%s: SCIOSETDN: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		dn = kmalloc(SCIOC_DNSIZE, GFP_KERNEL);
+		if (!dn) {
+			kfree(rcvmsg);
+			return -ENOMEM;
+		}
+		/*
+		 * Get the spid from user space
+		 */
+		if (copy_from_user(dn, data->dataptr, SCIOC_DNSIZE)) {
+			kfree(rcvmsg);
+			kfree(dn);
+			return -EFAULT;
+		}
+
+		pr_debug("%s: SCIOCSETDN: setting channel %d dn to %s\n", 
+			sc_adapter[card]->devicename, data->channel, dn);
+		status = send_and_receive(card, CEPID, ceReqTypeCall, 
+			ceReqClass0, ceReqCallSetMyNumber, data->channel, 
+			strlen(dn),dn,rcvmsg, SAR_TIMEOUT);
+		if(!status && !(rcvmsg->rsp_status)) {
+			pr_debug("%s: SCIOCSETDN: command successful\n", 
+				sc_adapter[card]->devicename);
+			kfree(rcvmsg);
+			kfree(dn);
+			return 0;
+		}
+		else {
+			pr_debug("%s: SCIOCSETDN: command failed (status = %d)\n",
+				sc_adapter[card]->devicename, status);
+			kfree(rcvmsg);
+			kfree(dn);
+			return status;
+		}
+	}
+
+	case SCIOCTRACE:
+
+		pr_debug("%s: SCIOTRACE: ioctl received\n",
+				sc_adapter[card]->devicename);
+/*		sc_adapter[card]->trace = !sc_adapter[card]->trace;
+		pr_debug("%s: SCIOCTRACE: tracing turned %s\n",
+				sc_adapter[card]->devicename,
+			sc_adapter[card]->trace ? "ON" : "OFF"); */
+		break;
+
+	case SCIOCSTAT:
+	{
+		boardInfo *bi;
+
+		pr_debug("%s: SCIOSTAT: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		bi = kmalloc (sizeof(boardInfo), GFP_KERNEL);
+		if (!bi) {
+			kfree(rcvmsg);
+			return -ENOMEM;
+		}
+
+		kfree(rcvmsg);
+		GetStatus(card, bi);
+
+		if (copy_to_user(data->dataptr, bi, sizeof(boardInfo))) {
+			kfree(bi);
+			return -EFAULT;
+		}
+
+		kfree(bi);
+		return 0;
+	}
+
+	case SCIOCGETSPEED:
+	{
+		pr_debug("%s: SCIOGETSPEED: ioctl received\n",
+				sc_adapter[card]->devicename);
+
+		/*
+		 * Get the speed from the board
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, 
+			ceReqCallGetCallType, data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+		if (!status && !(rcvmsg->rsp_status)) {
+			pr_debug("%s: SCIOCGETSPEED: command successful\n",
+				sc_adapter[card]->devicename);
+		}
+		else {
+			pr_debug("%s: SCIOCGETSPEED: command failed (status = %d)\n",
+				sc_adapter[card]->devicename, status);
+			kfree(rcvmsg);
+			return status;
+		}
+
+		speed = rcvmsg->msg_data.byte_array[0];
+
+		kfree(rcvmsg);
+
+		/*
+		 * Package the switch type and send to user space
+		 */
+
+		if (copy_to_user(data->dataptr, &speed, sizeof(char)))
+			return -EFAULT;
+
+		return 0;
+	}
+
+	case SCIOCSETSPEED:
+		pr_debug("%s: SCIOCSETSPEED: ioctl received\n",
+				sc_adapter[card]->devicename);
+		break;
+
+	case SCIOCLOOPTST:
+		pr_debug("%s: SCIOCLOOPTST: ioctl received\n",
+				sc_adapter[card]->devicename);
+		break;
+
+	default:
+		kfree(rcvmsg);
+		return -1;
+	}
+
+	kfree(rcvmsg);
+	return 0;
+}
+
+int GetStatus(int card, boardInfo *bi)
+{
+	RspMessage rcvmsg;
+	int i, status;
+
+	/*
+	 * Fill in some of the basic info about the board
+	 */
+	bi->modelid = sc_adapter[card]->model;
+	strcpy(bi->serial_no, sc_adapter[card]->hwconfig.serial_no);
+	strcpy(bi->part_no, sc_adapter[card]->hwconfig.part_no);
+	bi->iobase = sc_adapter[card]->iobase;
+	bi->rambase = sc_adapter[card]->rambase;
+	bi->irq = sc_adapter[card]->interrupt;
+	bi->ramsize = sc_adapter[card]->hwconfig.ram_size;
+	bi->interface = sc_adapter[card]->hwconfig.st_u_sense;
+	strcpy(bi->load_ver, sc_adapter[card]->load_ver);
+	strcpy(bi->proc_ver, sc_adapter[card]->proc_ver);
+
+	/*
+	 * Get the current PhyStats and LnkStats
+	 */
+	status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2,
+		ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+	if(!status) {
+		if(sc_adapter[card]->model < PRI_BOARD) {
+			bi->l1_status = rcvmsg.msg_data.byte_array[2];
+			for(i = 0 ; i < BRI_CHANNELS ; i++)
+				bi->status.bristats[i].phy_stat =
+					rcvmsg.msg_data.byte_array[i];
+		}
+		else {
+			bi->l1_status = rcvmsg.msg_data.byte_array[0];
+			bi->l2_status = rcvmsg.msg_data.byte_array[1];
+			for(i = 0 ; i < PRI_CHANNELS ; i++)
+				bi->status.pristats[i].phy_stat = 
+					rcvmsg.msg_data.byte_array[i+2];
+		}
+	}
+	
+	/*
+	 * Get the call types for each channel
+	 */
+	for (i = 0 ; i < sc_adapter[card]->nChannels ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			if (sc_adapter[card]->model == PRI_BOARD) {
+				bi->status.pristats[i].call_type = 
+					rcvmsg.msg_data.byte_array[0];
+			}
+			else {
+				bi->status.bristats[i].call_type =
+					rcvmsg.msg_data.byte_array[0];
+			}
+		}
+	}
+	
+	/*
+	 * If PRI, get the call states and service states for each channel
+	 */
+	if (sc_adapter[card]->model == PRI_BOARD) {
+		/*
+		 * Get the call states
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+			ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			for( i = 0 ; i < PRI_CHANNELS ; i++ )
+				bi->status.pristats[i].call_state = 
+					rcvmsg.msg_data.byte_array[i];
+		}
+
+		/*
+		 * Get the service states
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+			ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if(!status) {
+			for( i = 0 ; i < PRI_CHANNELS ; i++ )
+				bi->status.pristats[i].serv_state = 
+					rcvmsg.msg_data.byte_array[i];
+		}
+
+		/*
+		 * Get the link stats for the channels
+		 */
+		for (i = 1 ; i <= PRI_CHANNELS ; i++) {
+			status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+				ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+			if (!status) {
+				bi->status.pristats[i-1].link_stats.tx_good =
+					(unsigned long)rcvmsg.msg_data.byte_array[0];
+				bi->status.pristats[i-1].link_stats.tx_bad =
+					(unsigned long)rcvmsg.msg_data.byte_array[4];
+				bi->status.pristats[i-1].link_stats.rx_good =
+					(unsigned long)rcvmsg.msg_data.byte_array[8];
+				bi->status.pristats[i-1].link_stats.rx_bad =
+					(unsigned long)rcvmsg.msg_data.byte_array[12];
+			}
+		}
+
+		/*
+		 * Link stats for the D channel
+		 */
+		status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+			ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status) {
+			bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+			bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+			bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+			bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+		}
+
+		return 0;
+	}
+
+	/*
+	 * If BRI or POTS, Get SPID, DN and call types for each channel
+	 */
+
+	/*
+	 * Get the link stats for the channels
+	 */
+	status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+		ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+	if (!status) {
+		bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+		bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+		bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+		bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+		bi->status.bristats[0].link_stats.tx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[16];
+		bi->status.bristats[0].link_stats.tx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[20];
+		bi->status.bristats[0].link_stats.rx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[24];
+		bi->status.bristats[0].link_stats.rx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[28];
+		bi->status.bristats[1].link_stats.tx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[32];
+		bi->status.bristats[1].link_stats.tx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[36];
+		bi->status.bristats[1].link_stats.rx_good = 
+			(unsigned long)rcvmsg.msg_data.byte_array[40];
+		bi->status.bristats[1].link_stats.rx_bad = 
+			(unsigned long)rcvmsg.msg_data.byte_array[44];
+	}
+
+	/*
+	 * Get the SPIDs
+	 */
+	for (i = 0 ; i < BRI_CHANNELS ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetSPID, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status)
+			strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array);
+	}
+		
+	/*
+	 * Get the DNs
+	 */
+	for (i = 0 ; i < BRI_CHANNELS ; i++) {
+		status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+			ceReqCallGetMyNumber, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+		if (!status)
+			strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array);
+	}
+		
+	return 0;
+}
diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c
new file mode 100644
index 0000000..ca204da
--- /dev/null
+++ b/drivers/isdn/sc/message.c
@@ -0,0 +1,241 @@
+/* $Id: message.c,v 1.5.8.2 2001/09/23 22:24:59 kai Exp $
+ *
+ * functions for sending and receiving control messages
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *sc_adapter[];
+extern unsigned int cinst;
+
+/*
+ * Obligatory function prototypes
+ */
+extern int indicate_status(int,ulong,char*);
+extern int scm_command(isdn_ctrl *);
+
+
+/*
+ * receive a message from the board
+ */
+int receivemessage(int card, RspMessage *rspmsg) 
+{
+	DualPortMemory *dpm;
+	unsigned long flags;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+	
+	pr_debug("%s: Entered receivemessage\n",
+			sc_adapter[card]->devicename);
+
+	/*
+	 * See if there are messages waiting
+	 */
+	if (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) {
+		/*
+		 * Map in the DPM to the base page and copy the message
+		 */
+		spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+		outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
+			sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+		dpm = (DualPortMemory *) sc_adapter[card]->rambase;
+		memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]), 
+			MSG_LEN);
+		dpm->rsp_tail = (dpm->rsp_tail+1) % MAX_MESSAGES;
+		inb(sc_adapter[card]->ioport[FIFO_READ]);
+		spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+		/*
+		 * Tell the board that the message is received
+		 */
+		pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d "
+				"cnt:%d (type,class,code):(%d,%d,%d) "
+				"link:%d stat:0x%x\n",
+					sc_adapter[card]->devicename,
+					rspmsg->sequence_no,
+					rspmsg->process_id,
+					rspmsg->time_stamp,
+					rspmsg->cmd_sequence_no,
+					rspmsg->msg_byte_cnt,
+					rspmsg->type,
+					rspmsg->class,
+					rspmsg->code,
+					rspmsg->phy_link_no, 
+					rspmsg->rsp_status);
+
+		return 0;
+	}
+	return -ENOMSG;
+}
+	
+/*
+ * send a message to the board
+ */
+int sendmessage(int card,
+		unsigned int procid,
+		unsigned int type, 
+		unsigned int class, 
+		unsigned int code,
+		unsigned int link, 
+		unsigned int data_len, 
+		unsigned int *data) 
+{
+	DualPortMemory *dpm;
+	ReqMessage sndmsg;
+	unsigned long flags;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+
+	/*
+	 * Make sure we only send CEPID messages when the engine is up
+	 * and CMPID messages when it is down
+	 */
+	if(sc_adapter[card]->EngineUp && procid == CMPID) {
+		pr_debug("%s: Attempt to send CM message with engine up\n",
+			sc_adapter[card]->devicename);
+		return -ESRCH;
+	}
+
+	if(!sc_adapter[card]->EngineUp && procid == CEPID) {
+		pr_debug("%s: Attempt to send CE message with engine down\n",
+			sc_adapter[card]->devicename);
+		return -ESRCH;
+	}
+
+	memset(&sndmsg, 0, MSG_LEN);
+	sndmsg.msg_byte_cnt = 4;
+	sndmsg.type = type;
+	sndmsg.class = class;
+	sndmsg.code = code;
+	sndmsg.phy_link_no = link;
+
+	if (data_len > 0) {
+		if (data_len > MSG_DATA_LEN)
+			data_len = MSG_DATA_LEN;
+		memcpy(&(sndmsg.msg_data), data, data_len);
+		sndmsg.msg_byte_cnt = data_len + 8;
+	}
+
+	sndmsg.process_id = procid;
+	sndmsg.sequence_no = sc_adapter[card]->seq_no++ % 256;
+
+	/*
+	 * wait for an empty slot in the queue
+	 */
+	while (!(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
+		udelay(1);
+
+	/*
+	 * Disable interrupts and map in shared memory
+	 */
+	spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+	outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
+		sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+	dpm = (DualPortMemory *) sc_adapter[card]->rambase;	/* Fix me */
+	memcpy_toio(&(dpm->req_queue[dpm->req_head]),&sndmsg,MSG_LEN);
+	dpm->req_head = (dpm->req_head+1) % MAX_MESSAGES;
+	outb(sndmsg.sequence_no, sc_adapter[card]->ioport[FIFO_WRITE]);
+	spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+		
+	pr_debug("%s: Sent Message seq:%d pid:%d time:%d "
+			"cnt:%d (type,class,code):(%d,%d,%d) "
+			"link:%d\n ",
+				sc_adapter[card]->devicename,
+				sndmsg.sequence_no,
+				sndmsg.process_id,
+				sndmsg.time_stamp,
+				sndmsg.msg_byte_cnt,
+				sndmsg.type,
+				sndmsg.class,
+				sndmsg.code,
+				sndmsg.phy_link_no); 
+		
+	return 0;
+}
+
+int send_and_receive(int card,
+		unsigned int procid, 
+		unsigned char type,
+		unsigned char class, 
+		unsigned char code,
+		unsigned char link,
+	 	unsigned char data_len, 
+		unsigned char *data, 
+		RspMessage *mesgdata,
+		int timeout) 
+{
+	int retval;
+	int tries;
+
+	if (!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return -EINVAL;
+	}
+
+	sc_adapter[card]->want_async_messages = 1;
+	retval = sendmessage(card, procid, type, class, code, link, 
+			data_len, (unsigned int *) data);
+  
+	if (retval) {
+		pr_debug("%s: SendMessage failed in SAR\n",
+			sc_adapter[card]->devicename);
+		sc_adapter[card]->want_async_messages = 0;
+		return -EIO;
+	}
+
+	tries = 0;
+	/* wait for the response */
+	while (tries < timeout) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		
+		pr_debug("SAR waiting..\n");
+
+		/*
+		 * See if we got our message back
+		 */
+		if ((sc_adapter[card]->async_msg.type == type) &&
+		    (sc_adapter[card]->async_msg.class == class) &&
+		    (sc_adapter[card]->async_msg.code == code) &&
+		    (sc_adapter[card]->async_msg.phy_link_no == link)) {
+
+			/*
+			 * Got it!
+			 */
+			pr_debug("%s: Got ASYNC message\n",
+				sc_adapter[card]->devicename);
+			memcpy(mesgdata, &(sc_adapter[card]->async_msg),
+				sizeof(RspMessage));
+			sc_adapter[card]->want_async_messages = 0;
+			return 0;
+		}
+
+   		tries++;
+	}
+
+	pr_debug("%s: SAR message timeout\n", sc_adapter[card]->devicename);
+	sc_adapter[card]->want_async_messages = 0;
+	return -ETIME;
+}
diff --git a/drivers/isdn/sc/message.h b/drivers/isdn/sc/message.h
new file mode 100644
index 0000000..8eb15e7
--- /dev/null
+++ b/drivers/isdn/sc/message.h
@@ -0,0 +1,245 @@
+/* $Id: message.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * structures, macros and defines useful for sending
+ * messages to the adapter
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+/*
+ * Board message macros, defines and structures
+ */
+ 
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#define MAX_MESSAGES		32	/* Maximum messages that can be
+					   queued */
+#define MSG_DATA_LEN		48	/* Maximum size of message payload */
+#define MSG_LEN			64	/* Size of a message */
+#define CMPID			0	/* Loader message process ID */
+#define CEPID			64	/* Firmware message process ID */
+
+/*
+ * Macro to determine if a message is a loader message
+ */
+#define IS_CM_MESSAGE(mesg, tx, cx, dx)		\
+		((mesg.type == cmRspType##tx)		\
+		&&(mesg.class == cmRspClass##cx)	\
+		&&(mesg.code == cmRsp##dx))
+
+/*
+ * Macro to determine if a message is a firmware message
+ */
+#define IS_CE_MESSAGE(mesg, tx, cx, dx)		\
+		((mesg.type == ceRspType##tx)		\
+		&&(mesg.class == ceRspClass##cx)	\
+		&&(mesg.code == ceRsp##tx##dx))
+
+/* 
+ * Loader Request and Response Messages
+ */
+
+/* message types */
+#define cmReqType1			1
+#define cmReqType2			2
+#define cmRspType0			0
+#define cmRspType1			1
+#define cmRspType2			2
+#define cmRspType5			5
+
+/* message classes */
+#define cmReqClass0			0
+#define cmRspClass0			0
+
+/* message codes */
+#define cmReqHWConfig		1			/* 1,0,1 */
+#define cmReqMsgLpbk		2			/* 1,0,2 */
+#define cmReqVersion		3			/* 1,0,3 */
+#define cmReqLoadProc		1			/* 2,0,1 */
+#define cmReqStartProc		2			/* 2,0,2 */
+#define cmReqReadMem		6			/* 2,0,6 */
+#define cmRspHWConfig		cmReqHWConfig
+#define	cmRspMsgLpbk		cmReqMsgLpbk
+#define cmRspVersion		cmReqVersion
+#define cmRspLoadProc		cmReqLoadProc
+#define cmRspStartProc		cmReqStartProc
+#define	cmRspReadMem		cmReqReadMem
+#define cmRspMiscEngineUp	1			/* 5,0,1 */
+#define cmRspInvalid		0			/* 0,0,0 */
+
+
+/*
+ * Firmware Request and Response Messages
+ */
+
+/* message types */
+#define ceReqTypePhy		1
+#define ceReqTypeLnk		2
+#define ceReqTypeCall		3
+#define ceReqTypeStat		1
+#define ceRspTypeErr		0
+#define	ceRspTypePhy		ceReqTypePhy
+#define ceRspTypeLnk		ceReqTypeLnk
+#define ceRspTypeCall		ceReqTypeCall
+#define ceRspTypeStat		ceReqTypeStat
+
+/* message classes */
+#define ceReqClass0		0
+#define ceReqClass1		1
+#define ceReqClass2		2
+#define ceReqClass3		3
+#define ceRspClass0		ceReqClass0
+#define ceRspClass1		ceReqClass1
+#define ceRspClass2		ceReqClass2
+#define ceRspClass3		ceReqClass3
+
+/* message codes  (B) = BRI only, (P) = PRI only, (V) = POTS only */
+#define ceReqPhyProcInfo	1			/* 1,0,1 */
+#define ceReqPhyConnect		1			/* 1,1,1 */
+#define ceReqPhyDisconnect	2			/* 1,1,2 */
+#define ceReqPhySetParams	3			/* 1,1,3 (P) */
+#define ceReqPhyGetParams	4			/* 1,1,4 (P) */
+#define ceReqPhyStatus		1			/* 1,2,1 */
+#define ceReqPhyAcfaStatus	2			/* 1,2,2 (P) */
+#define ceReqPhyChCallState	3			/* 1,2,3 (P) */
+#define ceReqPhyChServState	4			/* 1,2,4 (P) */
+#define ceReqPhyRLoopBack	1			/* 1,3,1 */
+#define ceRspPhyProcInfo	ceReqPhyProcInfo
+#define	ceRspPhyConnect		ceReqPhyConnect
+#define ceRspPhyDisconnect	ceReqPhyDisconnect
+#define ceRspPhySetParams	ceReqPhySetParams
+#define ceRspPhyGetParams	ceReqPhyGetParams
+#define ceRspPhyStatus		ceReqPhyStatus
+#define ceRspPhyAcfaStatus	ceReqPhyAcfaStatus
+#define ceRspPhyChCallState	ceReqPhyChCallState
+#define ceRspPhyChServState	ceReqPhyChServState
+#define ceRspPhyRLoopBack	ceReqphyRLoopBack
+#define ceReqLnkSetParam	1			/* 2,0,1 */
+#define ceReqLnkGetParam	2			/* 2,0,2 */
+#define ceReqLnkGetStats	3			/* 2,0,3 */
+#define ceReqLnkWrite		1			/* 2,1,1 */
+#define ceReqLnkRead		2			/* 2,1,2 */
+#define ceReqLnkFlush		3			/* 2,1,3 */
+#define ceReqLnkWrBufTrc	4			/* 2,1,4 */
+#define ceReqLnkRdBufTrc	5			/* 2,1,5 */
+#define ceRspLnkSetParam	ceReqLnkSetParam
+#define ceRspLnkGetParam	ceReqLnkGetParam
+#define ceRspLnkGetStats	ceReqLnkGetStats
+#define ceRspLnkWrite		ceReqLnkWrite
+#define ceRspLnkRead		ceReqLnkRead
+#define ceRspLnkFlush		ceReqLnkFlush
+#define ceRspLnkWrBufTrc	ceReqLnkWrBufTrc
+#define ceRspLnkRdBufTrc	ceReqLnkRdBufTrc
+#define ceReqCallSetSwitchType	1			/* 3,0,1 */
+#define ceReqCallGetSwitchType	2			/* 3,0,2 */
+#define ceReqCallSetFrameFormat	3			/* 3,0,3 */
+#define ceReqCallGetFrameFormat	4			/* 3,0,4 */
+#define ceReqCallSetCallType	5			/* 3,0,5 */
+#define ceReqCallGetCallType	6			/* 3,0,6 */
+#define ceReqCallSetSPID	7			/* 3,0,7 (!P) */
+#define ceReqCallGetSPID	8			/* 3,0,8 (!P) */
+#define ceReqCallSetMyNumber	9			/* 3,0,9 (!P) */
+#define ceReqCallGetMyNumber	10			/* 3,0,10 (!P) */
+#define	ceRspCallSetSwitchType	ceReqCallSetSwitchType
+#define ceRspCallGetSwitchType	ceReqCallSetSwitchType
+#define ceRspCallSetFrameFormat	ceReqCallSetFrameFormat
+#define ceRspCallGetFrameFormat	ceReqCallGetFrameFormat
+#define ceRspCallSetCallType	ceReqCallSetCallType
+#define ceRspCallGetCallType	ceReqCallGetCallType
+#define ceRspCallSetSPID	ceReqCallSetSPID
+#define ceRspCallGetSPID	ceReqCallGetSPID
+#define ceRspCallSetMyNumber	ceReqCallSetMyNumber
+#define ceRspCallGetMyNumber	ceReqCallGetMyNumber
+#define ceRspStatAcfaStatus	2
+#define ceRspStat
+#define ceRspErrError		0			/* 0,0,0 */
+
+/*
+ * Call Types
+ */
+#define CALLTYPE_64K		0
+#define CALLTYPE_56K		1
+#define CALLTYPE_SPEECH		2
+#define CALLTYPE_31KHZ		3
+
+/*
+ * Link Level data contains a pointer to and the length of
+ * a buffer in shared RAM. Used by LnkRead and LnkWrite message
+ * types. Part of RspMsgStruct and ReqMsgStruct.
+ */
+typedef struct {
+	unsigned long buff_offset;
+	unsigned short msg_len;
+} LLData;
+
+
+/* 
+ * Message payload template for an HWConfig message
+ */
+typedef struct {
+	char st_u_sense;
+	char powr_sense;
+	char sply_sense;
+	unsigned char asic_id;
+	long ram_size;
+	char serial_no[13];
+	char part_no[13];
+	char rev_no[2];
+} HWConfig_pl;
+
+/*
+ * A Message
+ */
+struct message {
+	unsigned char sequence_no;
+	unsigned char process_id;
+	unsigned char time_stamp;
+	unsigned char cmd_sequence_no;	/* Rsp messages only */
+	unsigned char reserved1[3];
+	unsigned char msg_byte_cnt;
+	unsigned char type;
+	unsigned char class;
+	unsigned char code;
+	unsigned char phy_link_no;
+	unsigned char rsp_status;	/* Rsp messages only */
+	unsigned char reseved2[3];
+	union {
+		unsigned char byte_array[MSG_DATA_LEN];
+		LLData response;
+		HWConfig_pl HWCresponse;
+	} msg_data;
+};
+
+typedef struct message ReqMessage;	/* Request message */
+typedef struct message RspMessage;	/* Response message */
+
+/*
+ * The first 5010 bytes of shared memory contain the message queues,
+ * indexes and other data. This structure is its template
+ */
+typedef struct {
+	volatile ReqMessage req_queue[MAX_MESSAGES];
+	volatile RspMessage rsp_queue[MAX_MESSAGES];
+	volatile unsigned char req_head;
+	volatile unsigned char req_tail;
+	volatile unsigned char rsp_head;
+	volatile unsigned char rsp_tail;
+	volatile unsigned long signature;
+	volatile unsigned long trace_enable;
+	volatile unsigned char reserved[4];
+} DualPortMemory;
+
+#endif
diff --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c
new file mode 100644
index 0000000..8e3fac3
--- /dev/null
+++ b/drivers/isdn/sc/packet.c
@@ -0,0 +1,231 @@
+/* $Id: packet.c,v 1.5.8.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *sc_adapter[];
+extern unsigned int cinst;
+
+extern int get_card_from_id(int);
+extern int indicate_status(int, int,ulong, char*);
+extern void memcpy_toshmem(int, void *, const void *, size_t);
+extern void memcpy_fromshmem(int, void *, const void *, size_t);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+                unsigned int, unsigned int, unsigned int, unsigned int *);
+
+int sndpkt(int devId, int channel, struct sk_buff *data)
+{
+	LLData	ReqLnkWrite;
+	int status;
+	int card;
+	unsigned long len;
+
+	card = get_card_from_id(devId);
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	pr_debug("%s: sndpkt: frst = 0x%x nxt = %d  f = %d n = %d\n",
+		sc_adapter[card]->devicename,
+		sc_adapter[card]->channel[channel].first_sendbuf,
+		sc_adapter[card]->channel[channel].next_sendbuf,
+		sc_adapter[card]->channel[channel].free_sendbufs,
+		sc_adapter[card]->channel[channel].num_sendbufs);
+
+	if(!sc_adapter[card]->channel[channel].free_sendbufs) {
+		pr_debug("%s: out of TX buffers\n",
+				sc_adapter[card]->devicename);
+		return -EINVAL;
+	}
+
+	if(data->len > BUFFER_SIZE) {
+		pr_debug("%s: data overflows buffer size (data > buffer)\n",
+			sc_adapter[card]->devicename);
+		return -EINVAL;
+	}
+
+	ReqLnkWrite.buff_offset = sc_adapter[card]->channel[channel].next_sendbuf *
+		BUFFER_SIZE + sc_adapter[card]->channel[channel].first_sendbuf;
+	ReqLnkWrite.msg_len = data->len; /* sk_buff size */
+	pr_debug("%s: writing %d bytes to buffer offset 0x%x\n",
+			sc_adapter[card]->devicename,
+			ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset);
+	memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len);
+
+	/*
+	 * sendmessage
+	 */
+	pr_debug("%s: sndpkt size=%d, buf_offset=0x%x buf_indx=%d\n",
+		sc_adapter[card]->devicename,
+		ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset,
+		sc_adapter[card]->channel[channel].next_sendbuf);
+
+	status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
+				channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite);
+	len = data->len;
+	if(status) {
+		pr_debug("%s: failed to send packet, status = %d\n",
+				sc_adapter[card]->devicename, status);
+		return -1;
+	}
+	else {
+		sc_adapter[card]->channel[channel].free_sendbufs--;
+		sc_adapter[card]->channel[channel].next_sendbuf =
+			++sc_adapter[card]->channel[channel].next_sendbuf ==
+			sc_adapter[card]->channel[channel].num_sendbufs ? 0 :
+			sc_adapter[card]->channel[channel].next_sendbuf;
+			pr_debug("%s: packet sent successfully\n", sc_adapter[card]->devicename);
+		dev_kfree_skb(data);
+		indicate_status(card,ISDN_STAT_BSENT,channel, (char *)&len);
+	}
+	return len;
+}
+
+void rcvpkt(int card, RspMessage *rcvmsg)
+{
+	LLData newll;
+	struct sk_buff *skb;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	switch(rcvmsg->rsp_status){
+	case 0x01:
+	case 0x02:
+	case 0x70:
+		pr_debug("%s: error status code: 0x%x\n",
+			sc_adapter[card]->devicename, rcvmsg->rsp_status);
+		return;
+	case 0x00: 
+	    if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) {
+			printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n",
+				sc_adapter[card]->devicename);
+			return;
+		}
+		skb_put(skb, rcvmsg->msg_data.response.msg_len);
+		pr_debug("%s: getting data from offset: 0x%x\n",
+			sc_adapter[card]->devicename,
+			rcvmsg->msg_data.response.buff_offset);
+		memcpy_fromshmem(card,
+			skb_put(skb, rcvmsg->msg_data.response.msg_len),
+		 	(char *)rcvmsg->msg_data.response.buff_offset,
+			rcvmsg->msg_data.response.msg_len);
+		sc_adapter[card]->card->rcvcallb_skb(sc_adapter[card]->driverId,
+			rcvmsg->phy_link_no-1, skb);
+
+	case 0x03:
+		/*
+	 	 * Recycle the buffer
+	 	 */
+		pr_debug("%s: buffer size : %d\n",
+				sc_adapter[card]->devicename, BUFFER_SIZE);
+/*		memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */
+		newll.buff_offset = rcvmsg->msg_data.response.buff_offset;
+		newll.msg_len = BUFFER_SIZE;
+		pr_debug("%s: recycled buffer at offset 0x%x size %d\n",
+			sc_adapter[card]->devicename,
+			newll.buff_offset, newll.msg_len);
+		sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+			rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll);
+	}
+
+}
+
+int setup_buffers(int card, int c)
+{
+	unsigned int nBuffers, i, cBase;
+	unsigned int buffer_size;
+	LLData	RcvBuffOffset;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("invalid param: %d is not a valid card id\n", card);
+		return -ENODEV;
+	}
+
+	/*
+	 * Calculate the buffer offsets (send/recv/send/recv)
+	 */
+	pr_debug("%s: setting up channel buffer space in shared RAM\n",
+			sc_adapter[card]->devicename);
+	buffer_size = BUFFER_SIZE;
+	nBuffers = ((sc_adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2;
+	nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers;
+	pr_debug("%s: calculating buffer space: %d buffers, %d big\n",
+		sc_adapter[card]->devicename,
+		nBuffers, buffer_size);
+	if(nBuffers < 2) {
+		pr_debug("%s: not enough buffer space\n",
+			sc_adapter[card]->devicename);
+		return -1;
+	}
+	cBase = (nBuffers * buffer_size) * (c - 1);
+	pr_debug("%s: channel buffer offset from shared RAM: 0x%x\n",
+			sc_adapter[card]->devicename, cBase);
+	sc_adapter[card]->channel[c-1].first_sendbuf = BUFFER_BASE + cBase;
+	sc_adapter[card]->channel[c-1].num_sendbufs = nBuffers / 2;
+	sc_adapter[card]->channel[c-1].free_sendbufs = nBuffers / 2;
+	sc_adapter[card]->channel[c-1].next_sendbuf = 0;
+	pr_debug("%s: send buffer setup complete: first=0x%x n=%d f=%d, nxt=%d\n",
+				sc_adapter[card]->devicename,
+				sc_adapter[card]->channel[c-1].first_sendbuf,
+				sc_adapter[card]->channel[c-1].num_sendbufs,
+				sc_adapter[card]->channel[c-1].free_sendbufs,
+				sc_adapter[card]->channel[c-1].next_sendbuf);
+
+	/*
+	 * Prep the receive buffers
+	 */
+	pr_debug("%s: adding %d RecvBuffers:\n",
+			sc_adapter[card]->devicename, nBuffers /2);
+	for (i = 0 ; i < nBuffers / 2; i++) {
+		RcvBuffOffset.buff_offset = 
+			((sc_adapter[card]->channel[c-1].first_sendbuf +
+			(nBuffers / 2) * buffer_size) + (buffer_size * i));
+		RcvBuffOffset.msg_len = buffer_size;
+		pr_debug("%s: adding RcvBuffer #%d offset=0x%x sz=%d bufsz:%d\n",
+				sc_adapter[card]->devicename,
+				i + 1, RcvBuffOffset.buff_offset, 
+				RcvBuffOffset.msg_len,buffer_size);
+		sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+				c, sizeof(LLData), (unsigned int *)&RcvBuffOffset);
+	} 
+	return 0;
+}
+
+int print_skb(int card,char *skb_p, int len){
+	int i,data;
+	pr_debug("%s: data at 0x%x len: 0x%x\n", sc_adapter[card]->devicename,
+			skb_p,len);
+	for(i=1;i<=len;i++,skb_p++){
+		data = (int) (0xff & (*skb_p));
+		pr_debug("%s: data =  0x%x", sc_adapter[card]->devicename,data);
+		if(!(i%4))
+			pr_debug(" ");
+		if(!(i%32))
+			pr_debug("\n");
+	}
+	pr_debug("\n");
+	return 0;
+}		
+
diff --git a/drivers/isdn/sc/scioc.h b/drivers/isdn/sc/scioc.h
new file mode 100644
index 0000000..d08e650
--- /dev/null
+++ b/drivers/isdn/sc/scioc.h
@@ -0,0 +1,105 @@
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * IOCTL Command Codes
+ */
+#define SCIOCLOAD	0x01	/* Load a firmware record */
+#define SCIOCRESET	0x02	/* Perform hard reset */
+#define SCIOCDEBUG	0x03	/* Set debug level */
+#define SCIOCREV	0x04	/* Get driver revision(s) */
+#define SCIOCSTART	0x05	/* Start the firmware */
+#define SCIOCGETSWITCH	0x06	/* Get switch type */
+#define SCIOCSETSWITCH	0x07	/* Set switch type */
+#define SCIOCGETSPID	0x08	/* Get channel SPID */
+#define SCIOCSETSPID	0x09 	/* Set channel SPID */
+#define SCIOCGETDN	0x0A	/* Get channel DN */
+#define SCIOCSETDN	0x0B 	/* Set channel DN */
+#define SCIOCTRACE	0x0C	/* Toggle trace mode */
+#define SCIOCSTAT	0x0D	/* Get line status */
+#define SCIOCGETSPEED	0x0E	/* Set channel speed */
+#define SCIOCSETSPEED	0x0F	/* Set channel speed */
+#define SCIOCLOOPTST	0x10	/* Perform loopback test */
+
+typedef struct {
+	int device;
+	int channel;
+	unsigned long command;
+	void __user *dataptr;
+} scs_ioctl;
+
+/* Size of strings */
+#define SCIOC_SPIDSIZE		49
+#define SCIOC_DNSIZE		SCIOC_SPIDSIZE
+#define SCIOC_REVSIZE		SCIOC_SPIDSIZE
+#define SCIOC_SRECSIZE		49
+
+typedef struct {
+	unsigned long tx_good;
+	unsigned long tx_bad;
+	unsigned long rx_good;
+	unsigned long rx_bad;
+} ChLinkStats;
+
+typedef struct {
+	char spid[49];
+	char dn[49];
+	char call_type;
+	char phy_stat;
+	ChLinkStats link_stats;
+} BRIStat;
+
+typedef BRIStat POTStat;
+
+typedef struct {
+	char call_type;
+	char call_state;
+	char serv_state;
+	char phy_stat;
+	ChLinkStats link_stats;
+} PRIStat;
+
+typedef char PRIInfo;
+typedef char BRIInfo;
+typedef char POTInfo;
+
+
+typedef struct {
+	char acfa_nos;
+	char acfa_ais;
+	char acfa_los;
+	char acfa_rra;
+	char acfa_slpp;
+	char acfa_slpn;
+	char acfa_fsrf;
+} ACFAStat;
+
+typedef struct {
+	unsigned char modelid;
+	char serial_no[13];
+	char part_no[13];
+	char load_ver[11];
+	char proc_ver[11];
+	int iobase;
+	long rambase;
+	char irq;
+	long ramsize;
+	char interface;
+	char switch_type;
+	char l1_status;
+	char l2_status;
+	ChLinkStats dch_stats;
+	ACFAStat AcfaStats;
+	union {
+		PRIStat pristats[23];
+		BRIStat bristats[2];
+		POTStat potsstats[2];
+	} status;
+	union {
+		PRIInfo priinfo;
+		BRIInfo briinfo;
+		POTInfo potsinfo;
+	} info;
+} boardInfo;
diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c
new file mode 100644
index 0000000..7bc2dfad0
--- /dev/null
+++ b/drivers/isdn/sc/shmem.c
@@ -0,0 +1,143 @@
+/* $Id: shmem.c,v 1.2.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * Card functions implementing ISDN4Linux functionality
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"		/* This must be first */
+#include "hardware.h"
+#include "card.h"
+
+/*
+ * Main adapter array
+ */
+extern board *sc_adapter[];
+extern int cinst;
+
+/*
+ *
+ */
+void memcpy_toshmem(int card, void *dest, const void *src, size_t n)
+{
+	unsigned long flags;
+	unsigned char ch;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) dest / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename,ch);
+	/*
+	 * Block interrupts and load the page
+	 */
+	spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+	outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+	memcpy_toio(sc_adapter[card]->rambase +
+		((unsigned long) dest % 0x4000), src, n);
+	spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+	pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename,
+		((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	pr_debug("%s: copying %d bytes from %#x to %#x\n",
+		sc_adapter[card]->devicename, n,
+		(unsigned long) src,
+		sc_adapter[card]->rambase + ((unsigned long) dest %0x4000));
+}
+
+/*
+ * Reverse of above
+ */
+void memcpy_fromshmem(int card, void *dest, const void *src, size_t n)
+{
+	unsigned long flags;
+	unsigned char ch;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) src / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename,ch);
+	
+	
+	/*
+	 * Block interrupts and load the page
+	 */
+	spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+	outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+	memcpy_fromio(dest,(void *)(sc_adapter[card]->rambase +
+		((unsigned long) src % 0x4000)), n);
+	spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+	pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename,
+		((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+/*	pr_debug("%s: copying %d bytes from %#x to %#x\n",
+		sc_adapter[card]->devicename, n,
+		sc_adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */
+}
+
+void memset_shmem(int card, void *dest, int c, size_t n)
+{
+	unsigned long flags;
+	unsigned char ch;
+
+	if(!IS_VALID_CARD(card)) {
+		pr_debug("Invalid param: %d is not a valid card id\n", card);
+		return;
+	}
+
+	if(n > SRAM_PAGESIZE) {
+		return;
+	}
+
+	/*
+	 * determine the page to load from the address
+	 */
+	ch = (unsigned long) dest / SRAM_PAGESIZE;
+	pr_debug("%s: loaded page %d\n",sc_adapter[card]->devicename,ch);
+
+	/*
+	 * Block interrupts and load the page
+	 */
+	spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+	outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+		sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+	memset_io(sc_adapter[card]->rambase +
+		((unsigned long) dest % 0x4000), c, n);
+	pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename,
+		((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+	spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+}
diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c
new file mode 100644
index 0000000..710d0f4
--- /dev/null
+++ b/drivers/isdn/sc/timer.c
@@ -0,0 +1,147 @@
+/* $Id: timer.c,v 1.3.6.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996  SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ *     SpellCaster Telecommunications Inc.
+ *     5621 Finch Avenue East, Unit #3
+ *     Scarborough, Ontario  Canada
+ *     M1B 2T9
+ *     +1 (416) 297-8565
+ *     +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *sc_adapter[];
+
+extern void flushreadfifo(int);
+extern int  startproc(int);
+extern int  indicate_status(int, int, unsigned long, char *);
+extern int  sendmessage(int, unsigned int, unsigned int, unsigned int,
+        unsigned int, unsigned int, unsigned int, unsigned int *);
+
+
+/*
+ * Write the proper values into the I/O ports following a reset
+ */
+void setup_ports(int card)
+{
+
+	outb((sc_adapter[card]->rambase >> 12), sc_adapter[card]->ioport[EXP_BASE]);
+
+	/* And the IRQ */
+	outb((sc_adapter[card]->interrupt | 0x80),
+		sc_adapter[card]->ioport[IRQ_SELECT]);
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Setup the ioports for the board that were cleared by the reset.
+ * Then, check to see if the signate has been set. Next, set the
+ * signature to a known value and issue a startproc if needed.
+ */
+void check_reset(unsigned long data)
+{
+	unsigned long flags;
+	unsigned long sig;
+	int card = (unsigned int) data;
+
+	pr_debug("%s: check_timer timer called\n",
+		sc_adapter[card]->devicename);
+
+	/* Setup the io ports */
+	setup_ports(card);
+
+	spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+	outb(sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport],
+		(sc_adapter[card]->shmem_magic>>14) | 0x80);
+	sig = (unsigned long) *((unsigned long *)(sc_adapter[card]->rambase + SIG_OFFSET));
+
+	/* check the signature */
+	if(sig == SIGNATURE) {
+		flushreadfifo(card);
+		spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+		/* See if we need to do a startproc */
+		if (sc_adapter[card]->StartOnReset)
+			startproc(card);
+	} else  {
+		pr_debug("%s: No signature yet, waiting another %d jiffies.\n", 
+			sc_adapter[card]->devicename, CHECKRESET_TIME);
+		mod_timer(&sc_adapter[card]->reset_timer, jiffies+CHECKRESET_TIME);
+		spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+	}
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Send check sc_adapter->phystat to see if the channels are up
+ * If they are, tell ISDN4Linux that the board is up. If not,
+ * tell IADN4Linux that it is up. Always reset the timer to
+ * fire again (endless loop).
+ */
+void check_phystat(unsigned long data)
+{
+	unsigned long flags;
+	int card = (unsigned int) data;
+
+	pr_debug("%s: Checking status...\n", sc_adapter[card]->devicename);
+	/* 
+	 * check the results of the last PhyStat and change only if
+	 * has changed drastically
+	 */
+	if (sc_adapter[card]->nphystat && !sc_adapter[card]->phystat) {   /* All is well */
+		pr_debug("PhyStat transition to RUN\n");
+		pr_info("%s: Switch contacted, transmitter enabled\n", 
+			sc_adapter[card]->devicename);
+		indicate_status(card, ISDN_STAT_RUN, 0, NULL);
+	}
+	else if (!sc_adapter[card]->nphystat && sc_adapter[card]->phystat) {   /* All is not well */
+		pr_debug("PhyStat transition to STOP\n");
+		pr_info("%s: Switch connection lost, transmitter disabled\n", 
+			sc_adapter[card]->devicename);
+
+		indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+	}
+
+	sc_adapter[card]->phystat = sc_adapter[card]->nphystat;
+
+	/* Reinitialize the timer */
+	spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+	mod_timer(&sc_adapter[card]->stat_timer, jiffies+CHECKSTAT_TIME);
+	spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+	/* Send a new cePhyStatus message */
+	sendmessage(card, CEPID,ceReqTypePhy,ceReqClass2,
+		ceReqPhyStatus,0,0,NULL);
+}
+
+/*
+ * When in trace mode, this callback is used to swap the working shared
+ * RAM page to the trace page(s) and process all received messages. It
+ * must be called often enough to get all of the messages out of RAM before
+ * it loops around.
+ * Trace messages are \n terminated strings.
+ * We output the messages in 64 byte chunks through readstat. Each chunk
+ * is scanned for a \n followed by a time stamp. If the timerstamp is older
+ * than the current time, scanning stops and the page and offset are recorded
+ * as the starting point the next time the trace timer is called. The final
+ * step is to restore the working page and reset the timer.
+ */
+void trace_timer(unsigned long data)
+{
+	/* not implemented */
+}