summary refs log tree commit diff
path: root/drivers/isdn/hysdn
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/hysdn
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
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!
Diffstat (limited to 'drivers/isdn/hysdn')
-rw-r--r--drivers/isdn/hysdn/Kconfig18
-rw-r--r--drivers/isdn/hysdn/Makefile11
-rw-r--r--drivers/isdn/hysdn/boardergo.c453
-rw-r--r--drivers/isdn/hysdn/boardergo.h100
-rw-r--r--drivers/isdn/hysdn/hycapi.c797
-rw-r--r--drivers/isdn/hysdn/hysdn_boot.c399
-rw-r--r--drivers/isdn/hysdn/hysdn_defs.h298
-rw-r--r--drivers/isdn/hysdn/hysdn_init.c254
-rw-r--r--drivers/isdn/hysdn/hysdn_net.c348
-rw-r--r--drivers/isdn/hysdn/hysdn_pof.h78
-rw-r--r--drivers/isdn/hysdn/hysdn_procconf.c443
-rw-r--r--drivers/isdn/hysdn/hysdn_proclog.c441
-rw-r--r--drivers/isdn/hysdn/hysdn_sched.c207
-rw-r--r--drivers/isdn/hysdn/ince1pc.h134
14 files changed, 3981 insertions, 0 deletions
diff --git a/drivers/isdn/hysdn/Kconfig b/drivers/isdn/hysdn/Kconfig
new file mode 100644
index 000000000000..c6d8a7042988
--- /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 000000000000..da63b636267d
--- /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 000000000000..e19a01a305a9
--- /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 000000000000..b56ff0889ead
--- /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 000000000000..8ee25b2ccce1
--- /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 000000000000..6c04281e57b8
--- /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 000000000000..4cee26e558ee
--- /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 000000000000..5cac2bf5f4b0
--- /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 000000000000..babec8157ae6
--- /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 000000000000..6cd81b9b08bc
--- /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 000000000000..5da507e532fc
--- /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 000000000000..8ef2b7c952a6
--- /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 000000000000..4fa3b01707cd
--- /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 000000000000..4a115a87c782
--- /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__ */