Merge branch 'tracehook' of git://git.kernel.org/pub/scm/linux/kernel/git/frob/linux-2.6-utrace

* 'tracehook' of git://git.kernel.org/pub/scm/linux/kernel/git/frob/linux-2.6-utrace:
  tracehook: comment fixes
diff --git a/Documentation/isdn/README.mISDN b/Documentation/isdn/README.mISDN
new file mode 100644
index 0000000..cd8bf92
--- /dev/null
+++ b/Documentation/isdn/README.mISDN
@@ -0,0 +1,6 @@
+mISDN is a new modular ISDN driver, in the long term it should replace
+the old I4L driver architecture for passiv ISDN cards.
+It was designed to allow a broad range of applications and interfaces
+but only have the basic function in kernel, the interface to the user
+space is based on sockets with a own address family AF_ISDN.
+
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 32ca1b9..6e94313 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -253,15 +253,15 @@
 }
 
 asmlinkage int
-osf_statfs(char __user *path, struct osf_statfs __user *buffer, unsigned long bufsiz)
+osf_statfs(char __user *pathname, struct osf_statfs __user *buffer, unsigned long bufsiz)
 {
-	struct nameidata nd;
+	struct path path;
 	int retval;
 
-	retval = user_path_walk(path, &nd);
+	retval = user_path(pathname, &path);
 	if (!retval) {
-		retval = do_osf_statfs(nd.path.dentry, buffer, bufsiz);
-		path_put(&nd.path);
+		retval = do_osf_statfs(path.dentry, buffer, bufsiz);
+		path_put(&path);
 	}
 	return retval;
 }
diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c
index be255eb..18072e0 100644
--- a/arch/parisc/hpux/sys_hpux.c
+++ b/arch/parisc/hpux/sys_hpux.c
@@ -210,19 +210,19 @@
 }
 
 /* hpux statfs */
-asmlinkage long hpux_statfs(const char __user *path,
+asmlinkage long hpux_statfs(const char __user *pathname,
 						struct hpux_statfs __user *buf)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (!error) {
 		struct hpux_statfs tmp;
-		error = vfs_statfs_hpux(nd.path.dentry, &tmp);
+		error = vfs_statfs_hpux(path.dentry, &tmp);
 		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
 			error = -EFAULT;
-		path_put(&nd.path);
+		path_put(&path);
 	}
 	return error;
 }
diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c
index 4ba3aec..7b0c352 100644
--- a/drivers/char/ser_a2232.c
+++ b/drivers/char/ser_a2232.c
@@ -192,7 +192,7 @@
 	Maybe one could implement a more efficient version by not only
 	transferring one character at a time.
 */
-	struct tty_struct *tty = port->gs.tty;
+	struct tty_struct *tty = port->gs.port.tty;
 
 #if 0
 	switch(err) {
@@ -226,7 +226,7 @@
 
 	/* Does this here really have to be? */
 	local_irq_save(flags);
-	port->gs.flags &= ~GS_TX_INTEN;
+	port->gs.port.flags &= ~GS_TX_INTEN;
 	local_irq_restore(flags);
 }
 
@@ -242,7 +242,7 @@
 
 	/* Does this here really have to be? */
 	local_irq_save(flags);
-	port->gs.flags |= GS_TX_INTEN;
+	port->gs.port.flags |= GS_TX_INTEN;
 	local_irq_restore(flags);
 }
 
@@ -276,9 +276,9 @@
 
 	local_irq_save(flags);
 
-	port->gs.flags &= ~GS_ACTIVE;
+	port->gs.port.flags &= ~GS_ACTIVE;
 	
-	if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
+	if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) {
 		/* Set DTR and RTS to Low, flush output.
 		   The NetBSD driver "msc.c" does it this way. */
 		stat->Command = (	(stat->Command & ~A2232CMD_CMask) | 
@@ -309,7 +309,7 @@
 	volatile struct a2232status *status;
 	volatile struct a2232memory *mem;
 
-	if (!port->gs.tty || !port->gs.tty->termios) return 0;
+	if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0;
 
 	status = a2232stat(port->which_a2232, port->which_port_on_a2232);
 	mem = a2232mem(port->which_a2232);
@@ -345,7 +345,7 @@
 	}
 	a2232_param |= rate;
 
-	cflag  = port->gs.tty->termios->c_cflag;
+	cflag  = port->gs.port.tty->termios->c_cflag;
 
 	// get character size
 	chsize = cflag & CSIZE;
@@ -382,7 +382,7 @@
 		the conventional way of inserting START/STOP characters
 		by hand in throttle()/unthrottle().
 	*/
-	softflow = !!( port->gs.tty->termios->c_iflag & IXOFF );
+	softflow = !!( port->gs.port.tty->termios->c_iflag & IXOFF );
 
 	// get Parity (Enabled/Disabled? If Enabled, Odd or Even?)
 	parity = cflag & (PARENB | PARODD);
@@ -400,9 +400,9 @@
 	/*	Hmm. Maybe an own a2232_port structure
 		member would be cleaner?	*/
 	if (cflag & CLOCAL)
-		port->gs.flags &= ~ASYNC_CHECK_CD;
+		port->gs.port.flags &= ~ASYNC_CHECK_CD;
 	else
-		port->gs.flags |= ASYNC_CHECK_CD;
+		port->gs.port.flags |= ASYNC_CHECK_CD;
 
 
 	/* Now we have all parameters and can go to set them: */
@@ -482,18 +482,18 @@
 	port = &a2232_ports[line];
 	
 	tty->driver_data = port;
-	port->gs.tty = tty;
-	port->gs.count++;
+	port->gs.port.tty = tty;
+	port->gs.port.count++;
 	retval = gs_init_port(&port->gs);
 	if (retval) {
-		port->gs.count--;
+		port->gs.port.count--;
 		return retval;
 	}
-	port->gs.flags |= GS_ACTIVE;
+	port->gs.port.flags |= GS_ACTIVE;
 	retval = gs_block_til_ready(port, filp);
 
 	if (retval) {
-		port->gs.count--;
+		port->gs.port.count--;
 		return retval;
 	}
 
@@ -522,7 +522,7 @@
 		for (p = 0; p < NUMLINES; p++){	/* for every port on this board */
 			err = 0;
 			port = &a2232_ports[n*NUMLINES+p];
-			if ( port->gs.flags & GS_ACTIVE ){ /* if the port is used */
+			if ( port->gs.port.flags & GS_ACTIVE ){ /* if the port is used */
 
 				status = a2232stat(n,p);
 
@@ -577,8 +577,8 @@
 				obuf = mem->OutBuf[p];
 				bufpos = status->OutHead;
 				while ( (port->gs.xmit_cnt > 0)		&&
-					(!port->gs.tty->stopped)	&&
-					(!port->gs.tty->hw_stopped) ){	/* While there are chars to transmit */
+					(!port->gs.port.tty->stopped)	&&
+					(!port->gs.port.tty->hw_stopped) ){	/* While there are chars to transmit */
 					if (((bufpos+1) & A2232_IOBUFLENMASK) != status->OutTail) { /* If the A2232 buffer is not full */
 						ch = port->gs.xmit_buf[port->gs.xmit_tail];					/* get the next char to transmit */
 						port->gs.xmit_tail = (port->gs.xmit_tail+1) & (SERIAL_XMIT_SIZE-1); /* modulo-addition for the gs.xmit_buf ring-buffer */
@@ -592,8 +592,8 @@
 				status->OutHead = bufpos;
 					
 				/* WakeUp if output buffer runs low */
-				if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) {
-					tty_wakeup(port->gs.tty);
+				if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) {
+					tty_wakeup(port->gs.port.tty);
 				}
 			} // if the port is used
 		} // for every port on the board
@@ -613,16 +613,16 @@
 						struct a2232_port *port = &a2232_ports[n*7+p];
 						port->cd_status = !(ncd & 1); /* ncd&1 <=> CD is now off */
 
-						if (!(port->gs.flags & ASYNC_CHECK_CD))
+						if (!(port->gs.port.flags & ASYNC_CHECK_CD))
 							;	/* Don't report DCD changes */
 						else if (port->cd_status) { // if DCD on: DCD went UP!
 							
 							/* Are we blocking in open?*/
-							wake_up_interruptible(&port->gs.open_wait);
+							wake_up_interruptible(&port->gs.port.open_wait);
 						}
 						else { // if DCD off: DCD went DOWN!
-							if (port->gs.tty)
-								tty_hangup (port->gs.tty);
+							if (port->gs.port.tty)
+								tty_hangup (port->gs.port.tty);
 						}
 						
 					} // if CD changed for this port
@@ -655,8 +655,8 @@
 #ifdef NEW_WRITE_LOCKING
 		mutex_init(&(port->gs.port_write_mutex));
 #endif
-		init_waitqueue_head(&port->gs.open_wait);
-		init_waitqueue_head(&port->gs.close_wait);
+		init_waitqueue_head(&port->gs.port.open_wait);
+		init_waitqueue_head(&port->gs.port.close_wait);
 	}
 }
 
diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c
index 69c5afe..1718b3c 100644
--- a/drivers/char/vme_scc.c
+++ b/drivers/char/vme_scc.c
@@ -183,8 +183,8 @@
 #ifdef NEW_WRITE_LOCKING
 		port->gs.port_write_mutex = MUTEX;
 #endif
-		init_waitqueue_head(&port->gs.open_wait);
-		init_waitqueue_head(&port->gs.close_wait);
+		init_waitqueue_head(&port->gs.port.open_wait);
+		init_waitqueue_head(&port->gs.port.close_wait);
 	}
 }
 
@@ -422,7 +422,7 @@
 {
 	unsigned char	ch;
 	struct scc_port *port = data;
-	struct tty_struct *tty = port->gs.tty;
+	struct tty_struct *tty = port->gs.port.tty;
 	SCC_ACCESS_INIT(port);
 
 	ch = SCCread_NB(RX_DATA_REG);
@@ -453,7 +453,7 @@
 static irqreturn_t scc_spcond_int(int irq, void *data)
 {
 	struct scc_port *port = data;
-	struct tty_struct *tty = port->gs.tty;
+	struct tty_struct *tty = port->gs.port.tty;
 	unsigned char	stat, ch, err;
 	int		int_pending_mask = port->channel == CHANNEL_A ?
 			                   IPR_A_RX : IPR_B_RX;
@@ -500,7 +500,7 @@
 	struct scc_port *port = data;
 	SCC_ACCESS_INIT(port);
 
-	if (!port->gs.tty) {
+	if (!port->gs.port.tty) {
 		printk(KERN_WARNING "scc_tx_int with NULL tty!\n");
 		SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
 		SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);
@@ -512,8 +512,9 @@
 			SCCwrite(TX_DATA_REG, port->x_char);
 			port->x_char = 0;
 		}
-		else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||
-				port->gs.tty->hw_stopped)
+		else if ((port->gs.xmit_cnt <= 0) ||
+			 port->gs.port.tty->stopped ||
+			 port->gs.port.tty->hw_stopped)
 			break;
 		else {
 			SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]);
@@ -522,15 +523,15 @@
 				break;
 		}
 	}
-	if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||
-			port->gs.tty->hw_stopped) {
+	if ((port->gs.xmit_cnt <= 0) || port->gs.port.tty->stopped ||
+	    port->gs.port.tty->hw_stopped) {
 		/* disable tx interrupts */
 		SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
 		SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);   /* disable tx_int on next tx underrun? */
-		port->gs.flags &= ~GS_TX_INTEN;
+		port->gs.port.flags &= ~GS_TX_INTEN;
 	}
-	if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars)
-		tty_wakeup(port->gs.tty);
+	if (port->gs.port.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars)
+		tty_wakeup(port->gs.port.tty);
 
 	SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
 	return IRQ_HANDLED;
@@ -550,14 +551,14 @@
 
 	if (changed & SR_DCD) {
 		port->c_dcd = !!(sr & SR_DCD);
-		if (!(port->gs.flags & ASYNC_CHECK_CD))
+		if (!(port->gs.port.flags & ASYNC_CHECK_CD))
 			;	/* Don't report DCD changes */
 		else if (port->c_dcd) {
-			wake_up_interruptible(&port->gs.open_wait);
+			wake_up_interruptible(&port->gs.port.open_wait);
 		}
 		else {
-			if (port->gs.tty)
-				tty_hangup (port->gs.tty);
+			if (port->gs.port.tty)
+				tty_hangup (port->gs.port.tty);
 		}
 	}
 	SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET);
@@ -578,7 +579,7 @@
 
 	local_irq_save(flags);
 	SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
-	port->gs.flags &= ~GS_TX_INTEN;
+	port->gs.port.flags &= ~GS_TX_INTEN;
 	local_irq_restore(flags);
 }
 
@@ -636,8 +637,8 @@
 {
 	struct scc_port *port = ptr;
 
-	port->gs.flags &= ~ GS_ACTIVE;
-	if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
+	port->gs.port.flags &= ~ GS_ACTIVE;
+	if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL) {
 		scc_setsignals (port, 0, 0);
 	}
 }
@@ -652,14 +653,14 @@
 	struct scc_port *port = ptr;
 	SCC_ACCESS_INIT(port);
 
-	if (!port->gs.tty || !port->gs.tty->termios) return 0;
+	if (!port->gs.port.tty || !port->gs.port.tty->termios) return 0;
 
 	channel = port->channel;
 
 	if (channel == CHANNEL_A)
 		return 0;		/* Settings controlled by boot PROM */
 
-	cflag  = port->gs.tty->termios->c_cflag;
+	cflag  = port->gs.port.tty->termios->c_cflag;
 	baud = port->gs.baud;
 	chsize = (cflag & CSIZE) >> 4;
 
@@ -678,9 +679,9 @@
 	}
 
 	if (cflag & CLOCAL)
-		port->gs.flags &= ~ASYNC_CHECK_CD;
+		port->gs.port.flags &= ~ASYNC_CHECK_CD;
 	else
-		port->gs.flags |= ASYNC_CHECK_CD;
+		port->gs.port.flags |= ASYNC_CHECK_CD;
 
 #ifdef CONFIG_MVME147_SCC
 	if (MACH_IS_MVME147)
@@ -856,7 +857,7 @@
 		{ COMMAND_REG, CR_EXTSTAT_RESET },
 	};
 #endif
-	if (!(port->gs.flags & ASYNC_INITIALIZED)) {
+	if (!(port->gs.port.flags & ASYNC_INITIALIZED)) {
 		local_irq_save(flags);
 #if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC)
 		if (MACH_IS_MVME147 || MACH_IS_MVME16x) {
@@ -880,18 +881,18 @@
 	}
 
 	tty->driver_data = port;
-	port->gs.tty = tty;
-	port->gs.count++;
+	port->gs.port.tty = tty;
+	port->gs.port.count++;
 	retval = gs_init_port(&port->gs);
 	if (retval) {
-		port->gs.count--;
+		port->gs.port.count--;
 		return retval;
 	}
-	port->gs.flags |= GS_ACTIVE;
+	port->gs.port.flags |= GS_ACTIVE;
 	retval = gs_block_til_ready(port, filp);
 
 	if (retval) {
-		port->gs.count--;
+		port->gs.port.count--;
 		return retval;
 	}
 
diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c
index e23399c..001622e 100644
--- a/drivers/firmware/memmap.c
+++ b/drivers/firmware/memmap.c
@@ -153,12 +153,14 @@
 
 static ssize_t start_show(struct firmware_map_entry *entry, char *buf)
 {
-	return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->start);
+	return snprintf(buf, PAGE_SIZE, "0x%llx\n",
+		(unsigned long long)entry->start);
 }
 
 static ssize_t end_show(struct firmware_map_entry *entry, char *buf)
 {
-	return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->end);
+	return snprintf(buf, PAGE_SIZE, "0x%llx\n",
+		(unsigned long long)entry->end);
 }
 
 static ssize_t type_show(struct firmware_map_entry *entry, char *buf)
diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig
index 66f946a..3d113c6 100644
--- a/drivers/isdn/Kconfig
+++ b/drivers/isdn/Kconfig
@@ -3,7 +3,7 @@
 #
 
 menuconfig ISDN
-	tristate "ISDN support"
+	bool "ISDN support"
 	depends on NET
 	depends on !S390
 	---help---
@@ -21,6 +21,8 @@
 
 if ISDN
 
+source "drivers/isdn/mISDN/Kconfig"
+
 menuconfig ISDN_I4L
 	tristate "Old ISDN4Linux (deprecated)"
 	---help---
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile
index 988142c..8380a45 100644
--- a/drivers/isdn/Makefile
+++ b/drivers/isdn/Makefile
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_ISDN_I4L)			+= i4l/
 obj-$(CONFIG_ISDN_CAPI)			+= capi/
+obj-$(CONFIG_MISDN)			+= mISDN/
 obj-$(CONFIG_ISDN_CAPI)			+= hardware/
 obj-$(CONFIG_ISDN_DIVERSION)		+= divert/
 obj-$(CONFIG_ISDN_DRV_HISAX)		+= hisax/
diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile
index 11c8a18..a5d8fce 100644
--- a/drivers/isdn/hardware/Makefile
+++ b/drivers/isdn/hardware/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_CAPI_AVM)		+= avm/
 obj-$(CONFIG_CAPI_EICON)	+= eicon/
+obj-$(CONFIG_MISDN)		+= mISDN/
diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
new file mode 100644
index 0000000..1479348
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/Kconfig
@@ -0,0 +1,25 @@
+#
+# Hardware for mISDN
+#
+comment "mISDN hardware drivers"
+
+config MISDN_HFCPCI
+	tristate "Support for HFC PCI cards"
+	depends on MISDN
+	depends on PCI
+	help
+	  Enable support for cards with Cologne Chip AG's
+          HFC PCI chip.
+
+config MISDN_HFCMULTI
+	tristate "Support for HFC multiport cards (HFC-4S/8S/E1)"
+	depends on PCI
+	depends on MISDN
+	help
+	  Enable support for cards with Cologne Chip AG's HFC multiport
+	  chip. There are three types of chips that are quite similar,
+	  but the interface is different:
+	   * HFC-4S (4 S/T interfaces on one chip)
+	   * HFC-8S (8 S/T interfaces on one chip)
+	   * HFC-E1 (E1 interface for 2Mbit ISDN)
+
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
new file mode 100644
index 0000000..1e7ca53
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the modular ISDN hardware drivers
+#
+#
+
+obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
+obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
new file mode 100644
index 0000000..a33d87a
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfc_multi.h
@@ -0,0 +1,1204 @@
+/*
+ * see notice in hfc_multi.c
+ */
+
+extern void ztdummy_extern_interrupt(void);
+extern void ztdummy_register_interrupt(void);
+extern int ztdummy_unregister_interrupt(void);
+
+#define DEBUG_HFCMULTI_FIFO	0x00010000
+#define	DEBUG_HFCMULTI_CRC	0x00020000
+#define	DEBUG_HFCMULTI_INIT	0x00040000
+#define	DEBUG_HFCMULTI_PLXSD	0x00080000
+#define	DEBUG_HFCMULTI_MODE	0x00100000
+#define	DEBUG_HFCMULTI_MSG	0x00200000
+#define	DEBUG_HFCMULTI_STATE	0x00400000
+#define	DEBUG_HFCMULTI_SYNC	0x01000000
+#define	DEBUG_HFCMULTI_DTMF	0x02000000
+#define	DEBUG_HFCMULTI_LOCK	0x80000000
+
+#define	PCI_ENA_REGIO	0x01
+#define	PCI_ENA_MEMIO	0x02
+
+/*
+ * NOTE: some registers are assigned multiple times due to different modes
+ *       also registers are assigned differen for HFC-4s/8s and HFC-E1
+ */
+
+/*
+#define MAX_FRAME_SIZE	2048
+*/
+
+struct hfc_chan {
+	struct dchannel	*dch;	/* link if channel is a D-channel */
+	struct bchannel	*bch;	/* link if channel is a B-channel */
+	int		port; 	/* the interface port this */
+				/* channel is associated with */
+	int		nt_timer; /* -1 if off, 0 if elapsed, >0 if running */
+	int		los, ais, slip_tx, slip_rx, rdi; /* current alarms */
+	int		jitter;
+	u_long		cfg;	/* port configuration */
+	int		sync;	/* sync state (used by E1) */
+	u_int		protocol; /* current protocol */
+	int		slot_tx; /* current pcm slot */
+	int		bank_tx; /* current pcm bank */
+	int		slot_rx;
+	int		bank_rx;
+	int		conf;	/* conference setting of TX slot */
+	int		txpending;	/* if there is currently data in */
+					/* the FIFO 0=no, 1=yes, 2=splloop */
+	int		rx_off; /* set to turn fifo receive off */
+	int		coeff_count; /* curren coeff block */
+	s32		*coeff; /* memory pointer to 8 coeff blocks */
+};
+
+
+struct hfcm_hw {
+	u_char	r_ctrl;
+	u_char	r_irq_ctrl;
+	u_char	r_cirm;
+	u_char	r_ram_sz;
+	u_char	r_pcm_md0;
+	u_char	r_irqmsk_misc;
+	u_char	r_dtmf;
+	u_char	r_st_sync;
+	u_char	r_sci_msk;
+	u_char	r_tx0, r_tx1;
+	u_char	a_st_ctrl0[8];
+	timer_t	timer;
+};
+
+
+/* for each stack these flags are used (cfg) */
+#define	HFC_CFG_NONCAP_TX	1 /* S/T TX interface has less capacity */
+#define	HFC_CFG_DIS_ECHANNEL	2 /* disable E-channel processing */
+#define	HFC_CFG_REG_ECHANNEL	3 /* register E-channel */
+#define	HFC_CFG_OPTICAL		4 /* the E1 interface is optical */
+#define	HFC_CFG_REPORT_LOS	5 /* the card should report loss of signal */
+#define	HFC_CFG_REPORT_AIS	6 /* the card should report alarm ind. sign. */
+#define	HFC_CFG_REPORT_SLIP	7 /* the card should report bit slips */
+#define	HFC_CFG_REPORT_RDI	8 /* the card should report remote alarm */
+#define	HFC_CFG_DTMF		9 /* enable DTMF-detection */
+#define	HFC_CFG_CRC4		10 /* disable CRC-4 Multiframe mode, */
+					/* use double frame instead. */
+
+#define	HFC_CHIP_EXRAM_128	0 /* external ram 128k */
+#define	HFC_CHIP_EXRAM_512	1 /* external ram 256k */
+#define	HFC_CHIP_REVISION0	2 /* old fifo handling */
+#define	HFC_CHIP_PCM_SLAVE	3 /* PCM is slave */
+#define	HFC_CHIP_PCM_MASTER	4 /* PCM is master */
+#define	HFC_CHIP_RX_SYNC	5 /* disable pll sync for pcm */
+#define	HFC_CHIP_DTMF		6 /* DTMF decoding is enabled */
+#define	HFC_CHIP_ULAW		7 /* ULAW mode */
+#define	HFC_CHIP_CLOCK2		8 /* double clock mode */
+#define	HFC_CHIP_E1CLOCK_GET	9 /* always get clock from E1 interface */
+#define	HFC_CHIP_E1CLOCK_PUT	10 /* always put clock from E1 interface */
+#define	HFC_CHIP_WATCHDOG	11 /* whether we should send signals */
+					/* to the watchdog */
+#define	HFC_CHIP_B410P		12 /* whether we have a b410p with echocan in */
+					/* hw */
+#define	HFC_CHIP_PLXSD		13 /* whether we have a Speech-Design PLX */
+
+#define HFC_IO_MODE_PCIMEM	0x00 /* normal memory mapped IO */
+#define HFC_IO_MODE_REGIO	0x01 /* PCI io access */
+#define HFC_IO_MODE_PLXSD	0x02 /* access HFC via PLX9030 */
+
+/* table entry in the PCI devices list */
+struct hm_map {
+	char *vendor_name;
+	char *card_name;
+	int type;
+	int ports;
+	int clock2;
+	int leds;
+	int opticalsupport;
+	int dip_type;
+	int io_mode;
+};
+
+struct hfc_multi {
+	struct list_head	list;
+	struct hm_map	*mtyp;
+	int		id;
+	int		pcm;	/* id of pcm bus */
+	int		type;
+	int		ports;
+
+	u_int		irq;	/* irq used by card */
+	u_int		irqcnt;
+	struct pci_dev	*pci_dev;
+	int		io_mode; /* selects mode */
+#ifdef HFC_REGISTER_DEBUG
+	void		(*HFC_outb)(struct hfc_multi *hc, u_char reg,
+				u_char val, const char *function, int line);
+	void		(*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+				u_char val, const char *function, int line);
+	u_char		(*HFC_inb)(struct hfc_multi *hc, u_char reg,
+				const char *function, int line);
+	u_char		(*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg,
+				const char *function, int line);
+	u_short		(*HFC_inw)(struct hfc_multi *hc, u_char reg,
+				const char *function, int line);
+	u_short		(*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg,
+				const char *function, int line);
+	void		(*HFC_wait)(struct hfc_multi *hc,
+				const char *function, int line);
+	void		(*HFC_wait_nodebug)(struct hfc_multi *hc,
+				const char *function, int line);
+#else
+	void		(*HFC_outb)(struct hfc_multi *hc, u_char reg,
+				u_char val);
+	void		(*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+				u_char val);
+	u_char		(*HFC_inb)(struct hfc_multi *hc, u_char reg);
+	u_char		(*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg);
+	u_short		(*HFC_inw)(struct hfc_multi *hc, u_char reg);
+	u_short		(*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg);
+	void		(*HFC_wait)(struct hfc_multi *hc);
+	void		(*HFC_wait_nodebug)(struct hfc_multi *hc);
+#endif
+	void		(*read_fifo)(struct hfc_multi *hc, u_char *data,
+				int len);
+	void		(*write_fifo)(struct hfc_multi *hc, u_char *data,
+				int len);
+	u_long		pci_origmembase, plx_origmembase, dsp_origmembase;
+	u_char		*pci_membase; /* PCI memory (MUST BE BYTE POINTER) */
+	u_char		*plx_membase; /* PLX memory */
+	u_char		*dsp_membase; /* DSP on PLX */
+	u_long		pci_iobase; /* PCI IO */
+	struct hfcm_hw	hw;	/* remember data of write-only-registers */
+
+	u_long		chip;	/* chip configuration */
+	int		masterclk; /* port that provides master clock -1=off */
+	int		dtmf;	/* flag that dtmf is currently in process */
+	int		Flen;	/* F-buffer size */
+	int		Zlen;	/* Z-buffer size (must be int for calculation)*/
+	int		max_trans; /* maximum transparent fifo fill */
+	int		Zmin;	/* Z-buffer offset */
+	int		DTMFbase; /* base address of DTMF coefficients */
+
+	u_int		slots;	/* number of PCM slots */
+	u_int		leds;	/* type of leds */
+	u_int		ledcount; /* used to animate leds */
+	u_long		ledstate; /* save last state of leds */
+	int		opticalsupport; /* has the e1 board */
+					/* an optical Interface */
+	int		dslot;	/* channel # of d-channel (E1) default 16 */
+
+	u_long		wdcount; 	/* every 500 ms we need to */
+					/* send the watchdog a signal */
+	u_char		wdbyte; /* watchdog toggle byte */
+	u_int		activity[8]; 	/* if there is any action on this */
+					/* port (will be cleared after */
+					/* showing led-states) */
+	int		e1_state; /* keep track of last state */
+	int		e1_getclock; /* if sync is retrieved from interface */
+	int		syncronized; /* keep track of existing sync interface */
+	int		e1_resync; /* resync jobs */
+
+	spinlock_t	lock;	/* the lock */
+
+	/*
+	 * the channel index is counted from 0, regardless where the channel
+	 * is located on the hfc-channel.
+	 * the bch->channel is equvalent to the hfc-channel
+	 */
+	struct hfc_chan	chan[32];
+	u_char		created[8]; /* what port is created */
+	signed char	slot_owner[256]; /* owner channel of slot */
+};
+
+/* PLX GPIOs */
+#define	PLX_GPIO4_DIR_BIT	13
+#define	PLX_GPIO4_BIT		14
+#define	PLX_GPIO5_DIR_BIT	16
+#define	PLX_GPIO5_BIT		17
+#define	PLX_GPIO6_DIR_BIT	19
+#define	PLX_GPIO6_BIT		20
+#define	PLX_GPIO7_DIR_BIT	22
+#define	PLX_GPIO7_BIT		23
+#define PLX_GPIO8_DIR_BIT	25
+#define PLX_GPIO8_BIT		26
+
+#define	PLX_GPIO4		(1 << PLX_GPIO4_BIT)
+#define	PLX_GPIO5		(1 << PLX_GPIO5_BIT)
+#define	PLX_GPIO6		(1 << PLX_GPIO6_BIT)
+#define	PLX_GPIO7		(1 << PLX_GPIO7_BIT)
+#define PLX_GPIO8		(1 << PLX_GPIO8_BIT)
+
+#define	PLX_GPIO4_DIR		(1 << PLX_GPIO4_DIR_BIT)
+#define	PLX_GPIO5_DIR		(1 << PLX_GPIO5_DIR_BIT)
+#define	PLX_GPIO6_DIR		(1 << PLX_GPIO6_DIR_BIT)
+#define	PLX_GPIO7_DIR		(1 << PLX_GPIO7_DIR_BIT)
+#define PLX_GPIO8_DIR		(1 << PLX_GPIO8_DIR_BIT)
+
+#define	PLX_TERM_ON			PLX_GPIO7
+#define	PLX_SLAVE_EN_N		PLX_GPIO5
+#define	PLX_MASTER_EN		PLX_GPIO6
+#define	PLX_SYNC_O_EN		PLX_GPIO4
+#define PLX_DSP_RES_N		PLX_GPIO8
+/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */
+#define PLX_GPIOC_INIT		(PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \
+			| PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N)
+
+/* PLX Interrupt Control/STATUS */
+#define PLX_INTCSR_LINTI1_ENABLE 0x01
+#define PLX_INTCSR_LINTI1_STATUS 0x04
+#define PLX_INTCSR_LINTI2_ENABLE 0x08
+#define PLX_INTCSR_LINTI2_STATUS 0x20
+#define PLX_INTCSR_PCIINT_ENABLE 0x40
+
+/* PLX Registers */
+#define PLX_INTCSR 0x4c
+#define PLX_CNTRL  0x50
+#define PLX_GPIOC  0x54
+
+
+/*
+ * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1
+ */
+
+/* write only registers */
+#define R_CIRM			0x00
+#define R_CTRL			0x01
+#define R_BRG_PCM_CFG 		0x02
+#define R_RAM_ADDR0		0x08
+#define R_RAM_ADDR1		0x09
+#define R_RAM_ADDR2		0x0A
+#define R_FIRST_FIFO		0x0B
+#define R_RAM_SZ		0x0C
+#define R_FIFO_MD		0x0D
+#define R_INC_RES_FIFO		0x0E
+#define R_FSM_IDX		0x0F
+#define R_FIFO			0x0F
+#define R_SLOT			0x10
+#define R_IRQMSK_MISC		0x11
+#define R_SCI_MSK		0x12
+#define R_IRQ_CTRL		0x13
+#define R_PCM_MD0		0x14
+#define R_PCM_MD1		0x15
+#define R_PCM_MD2		0x15
+#define R_SH0H			0x15
+#define R_SH1H			0x15
+#define R_SH0L			0x15
+#define R_SH1L			0x15
+#define R_SL_SEL0		0x15
+#define R_SL_SEL1		0x15
+#define R_SL_SEL2		0x15
+#define R_SL_SEL3		0x15
+#define R_SL_SEL4		0x15
+#define R_SL_SEL5		0x15
+#define R_SL_SEL6		0x15
+#define R_SL_SEL7		0x15
+#define R_ST_SEL		0x16
+#define R_ST_SYNC		0x17
+#define R_CONF_EN		0x18
+#define R_TI_WD			0x1A
+#define R_BERT_WD_MD		0x1B
+#define R_DTMF			0x1C
+#define R_DTMF_N		0x1D
+#define R_E1_WR_STA		0x20
+#define R_E1_RD_STA		0x20
+#define R_LOS0			0x22
+#define R_LOS1			0x23
+#define R_RX0			0x24
+#define R_RX_FR0		0x25
+#define R_RX_FR1		0x26
+#define R_TX0			0x28
+#define R_TX1			0x29
+#define R_TX_FR0		0x2C
+
+#define R_TX_FR1		0x2D
+#define R_TX_FR2		0x2E
+#define R_JATT_ATT		0x2F /* undocumented */
+#define A_ST_RD_STATE		0x30
+#define A_ST_WR_STATE		0x30
+#define R_RX_OFF		0x30
+#define A_ST_CTRL0		0x31
+#define R_SYNC_OUT		0x31
+#define A_ST_CTRL1		0x32
+#define A_ST_CTRL2		0x33
+#define A_ST_SQ_WR		0x34
+#define R_TX_OFF		0x34
+#define R_SYNC_CTRL		0x35
+#define A_ST_CLK_DLY		0x37
+#define R_PWM0			0x38
+#define R_PWM1			0x39
+#define A_ST_B1_TX		0x3C
+#define A_ST_B2_TX		0x3D
+#define A_ST_D_TX		0x3E
+#define R_GPIO_OUT0		0x40
+#define R_GPIO_OUT1		0x41
+#define R_GPIO_EN0		0x42
+#define R_GPIO_EN1		0x43
+#define R_GPIO_SEL		0x44
+#define R_BRG_CTRL		0x45
+#define R_PWM_MD		0x46
+#define R_BRG_MD		0x47
+#define R_BRG_TIM0		0x48
+#define R_BRG_TIM1		0x49
+#define R_BRG_TIM2		0x4A
+#define R_BRG_TIM3		0x4B
+#define R_BRG_TIM_SEL01		0x4C
+#define R_BRG_TIM_SEL23		0x4D
+#define R_BRG_TIM_SEL45		0x4E
+#define R_BRG_TIM_SEL67		0x4F
+#define A_SL_CFG		0xD0
+#define A_CONF			0xD1
+#define A_CH_MSK		0xF4
+#define A_CON_HDLC		0xFA
+#define A_SUBCH_CFG		0xFB
+#define A_CHANNEL		0xFC
+#define A_FIFO_SEQ		0xFD
+#define A_IRQ_MSK		0xFF
+
+/* read only registers */
+#define A_Z12			0x04
+#define A_Z1L			0x04
+#define A_Z1			0x04
+#define A_Z1H			0x05
+#define A_Z2L			0x06
+#define A_Z2			0x06
+#define A_Z2H			0x07
+#define A_F1			0x0C
+#define A_F12			0x0C
+#define A_F2			0x0D
+#define R_IRQ_OVIEW		0x10
+#define R_IRQ_MISC		0x11
+#define R_IRQ_STATECH		0x12
+#define R_CONF_OFLOW		0x14
+#define R_RAM_USE		0x15
+#define R_CHIP_ID		0x16
+#define R_BERT_STA		0x17
+#define R_F0_CNTL		0x18
+#define R_F0_CNTH		0x19
+#define R_BERT_EC		0x1A
+#define R_BERT_ECL		0x1A
+#define R_BERT_ECH		0x1B
+#define R_STATUS		0x1C
+#define R_CHIP_RV		0x1F
+#define R_STATE			0x20
+#define R_SYNC_STA		0x24
+#define R_RX_SL0_0		0x25
+#define R_RX_SL0_1		0x26
+#define R_RX_SL0_2		0x27
+#define R_JATT_DIR		0x2b /* undocumented */
+#define R_SLIP			0x2c
+#define A_ST_RD_STA		0x30
+#define R_FAS_EC		0x30
+#define R_FAS_ECL		0x30
+#define R_FAS_ECH		0x31
+#define R_VIO_EC		0x32
+#define R_VIO_ECL		0x32
+#define R_VIO_ECH		0x33
+#define A_ST_SQ_RD		0x34
+#define R_CRC_EC		0x34
+#define R_CRC_ECL		0x34
+#define R_CRC_ECH		0x35
+#define R_E_EC			0x36
+#define R_E_ECL			0x36
+#define R_E_ECH			0x37
+#define R_SA6_SA13_EC		0x38
+#define R_SA6_SA13_ECL		0x38
+#define R_SA6_SA13_ECH		0x39
+#define R_SA6_SA23_EC		0x3A
+#define R_SA6_SA23_ECL		0x3A
+#define R_SA6_SA23_ECH		0x3B
+#define A_ST_B1_RX		0x3C
+#define A_ST_B2_RX		0x3D
+#define A_ST_D_RX		0x3E
+#define A_ST_E_RX		0x3F
+#define R_GPIO_IN0		0x40
+#define R_GPIO_IN1		0x41
+#define R_GPI_IN0		0x44
+#define R_GPI_IN1		0x45
+#define R_GPI_IN2		0x46
+#define R_GPI_IN3		0x47
+#define R_INT_DATA		0x88
+#define R_IRQ_FIFO_BL0		0xC8
+#define R_IRQ_FIFO_BL1		0xC9
+#define R_IRQ_FIFO_BL2		0xCA
+#define R_IRQ_FIFO_BL3		0xCB
+#define R_IRQ_FIFO_BL4		0xCC
+#define R_IRQ_FIFO_BL5		0xCD
+#define R_IRQ_FIFO_BL6		0xCE
+#define R_IRQ_FIFO_BL7		0xCF
+
+/* read and write registers */
+#define A_FIFO_DATA0		0x80
+#define A_FIFO_DATA1		0x80
+#define A_FIFO_DATA2		0x80
+#define A_FIFO_DATA0_NOINC	0x84
+#define A_FIFO_DATA1_NOINC	0x84
+#define A_FIFO_DATA2_NOINC	0x84
+#define R_RAM_DATA		0xC0
+
+
+/*
+ * BIT SETTING FOR HFC-4S/8S AND HFC-E1
+ */
+
+/* chapter 2: universal bus interface */
+/* R_CIRM */
+#define V_IRQ_SEL		0x01
+#define V_SRES			0x08
+#define V_HFCRES		0x10
+#define V_PCMRES		0x20
+#define V_STRES			0x40
+#define V_ETRES			0x40
+#define V_RLD_EPR		0x80
+/* R_CTRL */
+#define V_FIFO_LPRIO		0x02
+#define V_SLOW_RD		0x04
+#define V_EXT_RAM		0x08
+#define V_CLK_OFF		0x20
+#define V_ST_CLK		0x40
+/* R_RAM_ADDR0 */
+#define V_RAM_ADDR2		0x01
+#define V_ADDR_RES		0x40
+#define V_ADDR_INC		0x80
+/* R_RAM_SZ */
+#define V_RAM_SZ		0x01
+#define V_PWM0_16KHZ		0x10
+#define V_PWM1_16KHZ		0x20
+#define V_FZ_MD			0x80
+/* R_CHIP_ID */
+#define V_PNP_IRQ		0x01
+#define V_CHIP_ID		0x10
+
+/* chapter 3: data flow */
+/* R_FIRST_FIFO */
+#define V_FIRST_FIRO_DIR	0x01
+#define V_FIRST_FIFO_NUM	0x02
+/* R_FIFO_MD */
+#define V_FIFO_MD		0x01
+#define V_CSM_MD		0x04
+#define V_FSM_MD		0x08
+#define V_FIFO_SZ		0x10
+/* R_FIFO */
+#define V_FIFO_DIR		0x01
+#define V_FIFO_NUM		0x02
+#define V_REV			0x80
+/* R_SLOT */
+#define V_SL_DIR		0x01
+#define V_SL_NUM		0x02
+/* A_SL_CFG */
+#define V_CH_DIR		0x01
+#define V_CH_SEL		0x02
+#define V_ROUTING		0x40
+/* A_CON_HDLC */
+#define V_IFF			0x01
+#define V_HDLC_TRP		0x02
+#define V_TRP_IRQ		0x04
+#define V_DATA_FLOW		0x20
+/* A_SUBCH_CFG */
+#define V_BIT_CNT		0x01
+#define V_START_BIT		0x08
+#define V_LOOP_FIFO		0x40
+#define V_INV_DATA		0x80
+/* A_CHANNEL */
+#define V_CH_DIR0		0x01
+#define V_CH_NUM0		0x02
+/* A_FIFO_SEQ */
+#define V_NEXT_FIFO_DIR		0x01
+#define V_NEXT_FIFO_NUM		0x02
+#define V_SEQ_END		0x40
+
+/* chapter 4: FIFO handling and HDLC controller */
+/* R_INC_RES_FIFO */
+#define V_INC_F			0x01
+#define V_RES_F			0x02
+#define V_RES_LOST		0x04
+
+/* chapter 5: S/T interface */
+/* R_SCI_MSK */
+#define V_SCI_MSK_ST0		0x01
+#define V_SCI_MSK_ST1		0x02
+#define V_SCI_MSK_ST2		0x04
+#define V_SCI_MSK_ST3		0x08
+#define V_SCI_MSK_ST4		0x10
+#define V_SCI_MSK_ST5		0x20
+#define V_SCI_MSK_ST6		0x40
+#define V_SCI_MSK_ST7		0x80
+/* R_ST_SEL */
+#define V_ST_SEL		0x01
+#define V_MULT_ST		0x08
+/* R_ST_SYNC */
+#define V_SYNC_SEL		0x01
+#define V_AUTO_SYNC		0x08
+/* A_ST_WR_STA */
+#define V_ST_SET_STA		0x01
+#define V_ST_LD_STA		0x10
+#define V_ST_ACT		0x20
+#define V_SET_G2_G3		0x80
+/* A_ST_CTRL0 */
+#define V_B1_EN			0x01
+#define V_B2_EN			0x02
+#define V_ST_MD			0x04
+#define V_D_PRIO		0x08
+#define V_SQ_EN			0x10
+#define V_96KHZ			0x20
+#define V_TX_LI			0x40
+#define V_ST_STOP		0x80
+/* A_ST_CTRL1 */
+#define V_G2_G3_EN		0x01
+#define V_D_HI			0x04
+#define V_E_IGNO		0x08
+#define V_E_LO			0x10
+#define V_B12_SWAP		0x80
+/* A_ST_CTRL2 */
+#define V_B1_RX_EN		0x01
+#define V_B2_RX_EN		0x02
+#define V_ST_TRIS		0x40
+/* A_ST_CLK_DLY */
+#define V_ST_CK_DLY		0x01
+#define V_ST_SMPL		0x10
+/* A_ST_D_TX */
+#define V_ST_D_TX		0x40
+/* R_IRQ_STATECH */
+#define V_SCI_ST0		0x01
+#define V_SCI_ST1		0x02
+#define V_SCI_ST2		0x04
+#define V_SCI_ST3		0x08
+#define V_SCI_ST4		0x10
+#define V_SCI_ST5		0x20
+#define V_SCI_ST6		0x40
+#define V_SCI_ST7		0x80
+/* A_ST_RD_STA */
+#define V_ST_STA		0x01
+#define V_FR_SYNC_ST		0x10
+#define V_TI2_EXP		0x20
+#define V_INFO0			0x40
+#define V_G2_G3			0x80
+/* A_ST_SQ_RD */
+#define V_ST_SQ			0x01
+#define V_MF_RX_RDY		0x10
+#define V_MF_TX_RDY		0x80
+/* A_ST_D_RX */
+#define V_ST_D_RX		0x40
+/* A_ST_E_RX */
+#define V_ST_E_RX		0x40
+
+/* chapter 5: E1 interface */
+/* R_E1_WR_STA */
+/* R_E1_RD_STA */
+#define V_E1_SET_STA		0x01
+#define V_E1_LD_STA		0x10
+/* R_RX0 */
+#define V_RX_CODE		0x01
+#define V_RX_FBAUD		0x04
+#define V_RX_CMI		0x08
+#define V_RX_INV_CMI		0x10
+#define V_RX_INV_CLK		0x20
+#define V_RX_INV_DATA		0x40
+#define V_AIS_ITU		0x80
+/* R_RX_FR0 */
+#define V_NO_INSYNC		0x01
+#define V_AUTO_RESYNC		0x02
+#define V_AUTO_RECO		0x04
+#define V_SWORD_COND		0x08
+#define V_SYNC_LOSS		0x10
+#define V_XCRC_SYNC		0x20
+#define V_MF_RESYNC		0x40
+#define V_RESYNC		0x80
+/* R_RX_FR1 */
+#define V_RX_MF			0x01
+#define V_RX_MF_SYNC		0x02
+#define V_RX_SL0_RAM		0x04
+#define V_ERR_SIM		0x20
+#define V_RES_NMF		0x40
+/* R_TX0 */
+#define V_TX_CODE		0x01
+#define V_TX_FBAUD		0x04
+#define V_TX_CMI_CODE		0x08
+#define V_TX_INV_CMI_CODE	0x10
+#define V_TX_INV_CLK		0x20
+#define V_TX_INV_DATA		0x40
+#define V_OUT_EN		0x80
+/* R_TX1 */
+#define V_INV_CLK		0x01
+#define V_EXCHG_DATA_LI		0x02
+#define V_AIS_OUT		0x04
+#define V_ATX			0x20
+#define V_NTRI			0x40
+#define V_AUTO_ERR_RES		0x80
+/* R_TX_FR0 */
+#define V_TRP_FAS		0x01
+#define V_TRP_NFAS		0x02
+#define V_TRP_RAL		0x04
+#define V_TRP_SA		0x08
+/* R_TX_FR1 */
+#define V_TX_FAS		0x01
+#define V_TX_NFAS		0x02
+#define V_TX_RAL		0x04
+#define V_TX_SA			0x08
+/* R_TX_FR2 */
+#define V_TX_MF			0x01
+#define V_TRP_SL0		0x02
+#define V_TX_SL0_RAM		0x04
+#define V_TX_E			0x10
+#define V_NEG_E			0x20
+#define V_XS12_ON		0x40
+#define V_XS15_ON		0x80
+/* R_RX_OFF */
+#define V_RX_SZ			0x01
+#define V_RX_INIT		0x04
+/* R_SYNC_OUT */
+#define V_SYNC_E1_RX		0x01
+#define V_IPATS0		0x20
+#define V_IPATS1		0x40
+#define V_IPATS2		0x80
+/* R_TX_OFF */
+#define V_TX_SZ			0x01
+#define V_TX_INIT		0x04
+/* R_SYNC_CTRL */
+#define V_EXT_CLK_SYNC		0x01
+#define V_SYNC_OFFS		0x02
+#define V_PCM_SYNC		0x04
+#define V_NEG_CLK		0x08
+#define V_HCLK			0x10
+/*
+#define V_JATT_AUTO_DEL		0x20
+#define V_JATT_AUTO		0x40
+*/
+#define V_JATT_OFF		0x80
+/* R_STATE */
+#define V_E1_STA		0x01
+#define V_ALT_FR_RX		0x40
+#define V_ALT_FR_TX		0x80
+/* R_SYNC_STA */
+#define V_RX_STA		0x01
+#define V_FR_SYNC_E1		0x04
+#define V_SIG_LOS		0x08
+#define V_MFA_STA		0x10
+#define V_AIS			0x40
+#define V_NO_MF_SYNC		0x80
+/* R_RX_SL0_0 */
+#define V_SI_FAS		0x01
+#define V_SI_NFAS		0x02
+#define V_A			0x04
+#define V_CRC_OK		0x08
+#define V_TX_E1			0x10
+#define V_TX_E2			0x20
+#define V_RX_E1			0x40
+#define V_RX_E2			0x80
+/* R_SLIP */
+#define V_SLIP_RX		0x01
+#define V_FOSLIP_RX		0x08
+#define V_SLIP_TX		0x10
+#define V_FOSLIP_TX		0x80
+
+/* chapter 6: PCM interface */
+/* R_PCM_MD0 */
+#define V_PCM_MD		0x01
+#define V_C4_POL		0x02
+#define V_F0_NEG		0x04
+#define V_F0_LEN		0x08
+#define V_PCM_ADDR		0x10
+/* R_SL_SEL0 */
+#define V_SL_SEL0		0x01
+#define V_SH_SEL0		0x80
+/* R_SL_SEL1 */
+#define V_SL_SEL1		0x01
+#define V_SH_SEL1		0x80
+/* R_SL_SEL2 */
+#define V_SL_SEL2		0x01
+#define V_SH_SEL2		0x80
+/* R_SL_SEL3 */
+#define V_SL_SEL3		0x01
+#define V_SH_SEL3		0x80
+/* R_SL_SEL4 */
+#define V_SL_SEL4		0x01
+#define V_SH_SEL4		0x80
+/* R_SL_SEL5 */
+#define V_SL_SEL5		0x01
+#define V_SH_SEL5		0x80
+/* R_SL_SEL6 */
+#define V_SL_SEL6		0x01
+#define V_SH_SEL6		0x80
+/* R_SL_SEL7 */
+#define V_SL_SEL7		0x01
+#define V_SH_SEL7		0x80
+/* R_PCM_MD1 */
+#define V_ODEC_CON		0x01
+#define V_PLL_ADJ		0x04
+#define V_PCM_DR		0x10
+#define V_PCM_LOOP		0x40
+/* R_PCM_MD2 */
+#define V_SYNC_PLL		0x02
+#define V_SYNC_SRC		0x04
+#define V_SYNC_OUT		0x08
+#define V_ICR_FR_TIME		0x40
+#define V_EN_PLL		0x80
+
+/* chapter 7: pulse width modulation */
+/* R_PWM_MD */
+#define V_EXT_IRQ_EN		0x08
+#define V_PWM0_MD		0x10
+#define V_PWM1_MD		0x40
+
+/* chapter 8: multiparty audio conferences */
+/* R_CONF_EN */
+#define V_CONF_EN		0x01
+#define V_ULAW			0x80
+/* A_CONF */
+#define V_CONF_NUM		0x01
+#define V_NOISE_SUPPR		0x08
+#define V_ATT_LEV		0x20
+#define V_CONF_SL		0x80
+/* R_CONF_OFLOW */
+#define V_CONF_OFLOW0		0x01
+#define V_CONF_OFLOW1		0x02
+#define V_CONF_OFLOW2		0x04
+#define V_CONF_OFLOW3		0x08
+#define V_CONF_OFLOW4		0x10
+#define V_CONF_OFLOW5		0x20
+#define V_CONF_OFLOW6		0x40
+#define V_CONF_OFLOW7		0x80
+
+/* chapter 9: DTMF contoller */
+/* R_DTMF0 */
+#define V_DTMF_EN		0x01
+#define V_HARM_SEL		0x02
+#define V_DTMF_RX_CH		0x04
+#define V_DTMF_STOP		0x08
+#define V_CHBL_SEL		0x10
+#define V_RST_DTMF		0x40
+#define V_ULAW_SEL		0x80
+
+/* chapter 10: BERT */
+/* R_BERT_WD_MD */
+#define V_PAT_SEQ		0x01
+#define V_BERT_ERR		0x08
+#define V_AUTO_WD_RES		0x20
+#define V_WD_RES		0x80
+/* R_BERT_STA */
+#define V_BERT_SYNC_SRC		0x01
+#define V_BERT_SYNC		0x10
+#define V_BERT_INV_DATA		0x20
+
+/* chapter 11: auxiliary interface */
+/* R_BRG_PCM_CFG */
+#define V_BRG_EN		0x01
+#define V_BRG_MD		0x02
+#define V_PCM_CLK		0x20
+#define V_ADDR_WRDLY		0x40
+/* R_BRG_CTRL */
+#define V_BRG_CS		0x01
+#define V_BRG_ADDR		0x08
+#define V_BRG_CS_SRC		0x80
+/* R_BRG_MD */
+#define V_BRG_MD0		0x01
+#define V_BRG_MD1		0x02
+#define V_BRG_MD2		0x04
+#define V_BRG_MD3		0x08
+#define V_BRG_MD4		0x10
+#define V_BRG_MD5		0x20
+#define V_BRG_MD6		0x40
+#define V_BRG_MD7		0x80
+/* R_BRG_TIM0 */
+#define V_BRG_TIM0_IDLE		0x01
+#define V_BRG_TIM0_CLK		0x10
+/* R_BRG_TIM1 */
+#define V_BRG_TIM1_IDLE		0x01
+#define V_BRG_TIM1_CLK		0x10
+/* R_BRG_TIM2 */
+#define V_BRG_TIM2_IDLE		0x01
+#define V_BRG_TIM2_CLK		0x10
+/* R_BRG_TIM3 */
+#define V_BRG_TIM3_IDLE		0x01
+#define V_BRG_TIM3_CLK		0x10
+/* R_BRG_TIM_SEL01 */
+#define V_BRG_WR_SEL0		0x01
+#define V_BRG_RD_SEL0		0x04
+#define V_BRG_WR_SEL1		0x10
+#define V_BRG_RD_SEL1		0x40
+/* R_BRG_TIM_SEL23 */
+#define V_BRG_WR_SEL2		0x01
+#define V_BRG_RD_SEL2		0x04
+#define V_BRG_WR_SEL3		0x10
+#define V_BRG_RD_SEL3		0x40
+/* R_BRG_TIM_SEL45 */
+#define V_BRG_WR_SEL4		0x01
+#define V_BRG_RD_SEL4		0x04
+#define V_BRG_WR_SEL5		0x10
+#define V_BRG_RD_SEL5		0x40
+/* R_BRG_TIM_SEL67 */
+#define V_BRG_WR_SEL6		0x01
+#define V_BRG_RD_SEL6		0x04
+#define V_BRG_WR_SEL7		0x10
+#define V_BRG_RD_SEL7		0x40
+
+/* chapter 12: clock, reset, interrupt, timer and watchdog */
+/* R_IRQMSK_MISC */
+#define V_STA_IRQMSK		0x01
+#define V_TI_IRQMSK		0x02
+#define V_PROC_IRQMSK		0x04
+#define V_DTMF_IRQMSK		0x08
+#define V_IRQ1S_MSK		0x10
+#define V_SA6_IRQMSK		0x20
+#define V_RX_EOMF_MSK		0x40
+#define V_TX_EOMF_MSK		0x80
+/* R_IRQ_CTRL */
+#define V_FIFO_IRQ		0x01
+#define V_GLOB_IRQ_EN		0x08
+#define V_IRQ_POL		0x10
+/* R_TI_WD */
+#define V_EV_TS			0x01
+#define V_WD_TS			0x10
+/* A_IRQ_MSK */
+#define V_IRQ			0x01
+#define V_BERT_EN		0x02
+#define V_MIX_IRQ		0x04
+/* R_IRQ_OVIEW */
+#define V_IRQ_FIFO_BL0		0x01
+#define V_IRQ_FIFO_BL1		0x02
+#define V_IRQ_FIFO_BL2		0x04
+#define V_IRQ_FIFO_BL3		0x08
+#define V_IRQ_FIFO_BL4		0x10
+#define V_IRQ_FIFO_BL5		0x20
+#define V_IRQ_FIFO_BL6		0x40
+#define V_IRQ_FIFO_BL7		0x80
+/* R_IRQ_MISC */
+#define V_STA_IRQ		0x01
+#define V_TI_IRQ		0x02
+#define V_IRQ_PROC		0x04
+#define V_DTMF_IRQ		0x08
+#define V_IRQ1S			0x10
+#define V_SA6_IRQ		0x20
+#define V_RX_EOMF		0x40
+#define V_TX_EOMF		0x80
+/* R_STATUS */
+#define V_BUSY			0x01
+#define V_PROC			0x02
+#define V_DTMF_STA		0x04
+#define V_LOST_STA		0x08
+#define V_SYNC_IN		0x10
+#define V_EXT_IRQSTA		0x20
+#define V_MISC_IRQSTA		0x40
+#define V_FR_IRQSTA		0x80
+/* R_IRQ_FIFO_BL0 */
+#define V_IRQ_FIFO0_TX		0x01
+#define V_IRQ_FIFO0_RX		0x02
+#define V_IRQ_FIFO1_TX		0x04
+#define V_IRQ_FIFO1_RX		0x08
+#define V_IRQ_FIFO2_TX		0x10
+#define V_IRQ_FIFO2_RX		0x20
+#define V_IRQ_FIFO3_TX		0x40
+#define V_IRQ_FIFO3_RX		0x80
+/* R_IRQ_FIFO_BL1 */
+#define V_IRQ_FIFO4_TX		0x01
+#define V_IRQ_FIFO4_RX		0x02
+#define V_IRQ_FIFO5_TX		0x04
+#define V_IRQ_FIFO5_RX		0x08
+#define V_IRQ_FIFO6_TX		0x10
+#define V_IRQ_FIFO6_RX		0x20
+#define V_IRQ_FIFO7_TX		0x40
+#define V_IRQ_FIFO7_RX		0x80
+/* R_IRQ_FIFO_BL2 */
+#define V_IRQ_FIFO8_TX		0x01
+#define V_IRQ_FIFO8_RX		0x02
+#define V_IRQ_FIFO9_TX		0x04
+#define V_IRQ_FIFO9_RX		0x08
+#define V_IRQ_FIFO10_TX		0x10
+#define V_IRQ_FIFO10_RX		0x20
+#define V_IRQ_FIFO11_TX		0x40
+#define V_IRQ_FIFO11_RX		0x80
+/* R_IRQ_FIFO_BL3 */
+#define V_IRQ_FIFO12_TX		0x01
+#define V_IRQ_FIFO12_RX		0x02
+#define V_IRQ_FIFO13_TX		0x04
+#define V_IRQ_FIFO13_RX		0x08
+#define V_IRQ_FIFO14_TX		0x10
+#define V_IRQ_FIFO14_RX		0x20
+#define V_IRQ_FIFO15_TX		0x40
+#define V_IRQ_FIFO15_RX		0x80
+/* R_IRQ_FIFO_BL4 */
+#define V_IRQ_FIFO16_TX		0x01
+#define V_IRQ_FIFO16_RX		0x02
+#define V_IRQ_FIFO17_TX		0x04
+#define V_IRQ_FIFO17_RX		0x08
+#define V_IRQ_FIFO18_TX		0x10
+#define V_IRQ_FIFO18_RX		0x20
+#define V_IRQ_FIFO19_TX		0x40
+#define V_IRQ_FIFO19_RX		0x80
+/* R_IRQ_FIFO_BL5 */
+#define V_IRQ_FIFO20_TX		0x01
+#define V_IRQ_FIFO20_RX		0x02
+#define V_IRQ_FIFO21_TX		0x04
+#define V_IRQ_FIFO21_RX		0x08
+#define V_IRQ_FIFO22_TX		0x10
+#define V_IRQ_FIFO22_RX		0x20
+#define V_IRQ_FIFO23_TX		0x40
+#define V_IRQ_FIFO23_RX		0x80
+/* R_IRQ_FIFO_BL6 */
+#define V_IRQ_FIFO24_TX		0x01
+#define V_IRQ_FIFO24_RX		0x02
+#define V_IRQ_FIFO25_TX		0x04
+#define V_IRQ_FIFO25_RX		0x08
+#define V_IRQ_FIFO26_TX		0x10
+#define V_IRQ_FIFO26_RX		0x20
+#define V_IRQ_FIFO27_TX		0x40
+#define V_IRQ_FIFO27_RX		0x80
+/* R_IRQ_FIFO_BL7 */
+#define V_IRQ_FIFO28_TX		0x01
+#define V_IRQ_FIFO28_RX		0x02
+#define V_IRQ_FIFO29_TX		0x04
+#define V_IRQ_FIFO29_RX		0x08
+#define V_IRQ_FIFO30_TX		0x10
+#define V_IRQ_FIFO30_RX		0x20
+#define V_IRQ_FIFO31_TX		0x40
+#define V_IRQ_FIFO31_RX		0x80
+
+/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */
+/* R_GPIO_OUT0 */
+#define V_GPIO_OUT0		0x01
+#define V_GPIO_OUT1		0x02
+#define V_GPIO_OUT2		0x04
+#define V_GPIO_OUT3		0x08
+#define V_GPIO_OUT4		0x10
+#define V_GPIO_OUT5		0x20
+#define V_GPIO_OUT6		0x40
+#define V_GPIO_OUT7		0x80
+/* R_GPIO_OUT1 */
+#define V_GPIO_OUT8		0x01
+#define V_GPIO_OUT9		0x02
+#define V_GPIO_OUT10		0x04
+#define V_GPIO_OUT11		0x08
+#define V_GPIO_OUT12		0x10
+#define V_GPIO_OUT13		0x20
+#define V_GPIO_OUT14		0x40
+#define V_GPIO_OUT15		0x80
+/* R_GPIO_EN0 */
+#define V_GPIO_EN0		0x01
+#define V_GPIO_EN1		0x02
+#define V_GPIO_EN2		0x04
+#define V_GPIO_EN3		0x08
+#define V_GPIO_EN4		0x10
+#define V_GPIO_EN5		0x20
+#define V_GPIO_EN6		0x40
+#define V_GPIO_EN7		0x80
+/* R_GPIO_EN1 */
+#define V_GPIO_EN8		0x01
+#define V_GPIO_EN9		0x02
+#define V_GPIO_EN10		0x04
+#define V_GPIO_EN11		0x08
+#define V_GPIO_EN12		0x10
+#define V_GPIO_EN13		0x20
+#define V_GPIO_EN14		0x40
+#define V_GPIO_EN15		0x80
+/* R_GPIO_SEL */
+#define V_GPIO_SEL0		0x01
+#define V_GPIO_SEL1		0x02
+#define V_GPIO_SEL2		0x04
+#define V_GPIO_SEL3		0x08
+#define V_GPIO_SEL4		0x10
+#define V_GPIO_SEL5		0x20
+#define V_GPIO_SEL6		0x40
+#define V_GPIO_SEL7		0x80
+/* R_GPIO_IN0 */
+#define V_GPIO_IN0		0x01
+#define V_GPIO_IN1		0x02
+#define V_GPIO_IN2		0x04
+#define V_GPIO_IN3		0x08
+#define V_GPIO_IN4		0x10
+#define V_GPIO_IN5		0x20
+#define V_GPIO_IN6		0x40
+#define V_GPIO_IN7		0x80
+/* R_GPIO_IN1 */
+#define V_GPIO_IN8		0x01
+#define V_GPIO_IN9		0x02
+#define V_GPIO_IN10		0x04
+#define V_GPIO_IN11		0x08
+#define V_GPIO_IN12		0x10
+#define V_GPIO_IN13		0x20
+#define V_GPIO_IN14		0x40
+#define V_GPIO_IN15		0x80
+/* R_GPI_IN0 */
+#define V_GPI_IN0		0x01
+#define V_GPI_IN1		0x02
+#define V_GPI_IN2		0x04
+#define V_GPI_IN3		0x08
+#define V_GPI_IN4		0x10
+#define V_GPI_IN5		0x20
+#define V_GPI_IN6		0x40
+#define V_GPI_IN7		0x80
+/* R_GPI_IN1 */
+#define V_GPI_IN8		0x01
+#define V_GPI_IN9		0x02
+#define V_GPI_IN10		0x04
+#define V_GPI_IN11		0x08
+#define V_GPI_IN12		0x10
+#define V_GPI_IN13		0x20
+#define V_GPI_IN14		0x40
+#define V_GPI_IN15		0x80
+/* R_GPI_IN2 */
+#define V_GPI_IN16		0x01
+#define V_GPI_IN17		0x02
+#define V_GPI_IN18		0x04
+#define V_GPI_IN19		0x08
+#define V_GPI_IN20		0x10
+#define V_GPI_IN21		0x20
+#define V_GPI_IN22		0x40
+#define V_GPI_IN23		0x80
+/* R_GPI_IN3 */
+#define V_GPI_IN24		0x01
+#define V_GPI_IN25		0x02
+#define V_GPI_IN26		0x04
+#define V_GPI_IN27		0x08
+#define V_GPI_IN28		0x10
+#define V_GPI_IN29		0x20
+#define V_GPI_IN30		0x40
+#define V_GPI_IN31		0x80
+
+/* map of all registers, used for debugging */
+
+#ifdef HFC_REGISTER_DEBUG
+struct hfc_register_names {
+	char *name;
+	u_char reg;
+} hfc_register_names[] = {
+	/* write registers */
+	{"R_CIRM",		0x00},
+	{"R_CTRL",		0x01},
+	{"R_BRG_PCM_CFG ",	0x02},
+	{"R_RAM_ADDR0",		0x08},
+	{"R_RAM_ADDR1",		0x09},
+	{"R_RAM_ADDR2",		0x0A},
+	{"R_FIRST_FIFO",	0x0B},
+	{"R_RAM_SZ",		0x0C},
+	{"R_FIFO_MD",		0x0D},
+	{"R_INC_RES_FIFO",	0x0E},
+	{"R_FIFO / R_FSM_IDX",	0x0F},
+	{"R_SLOT",		0x10},
+	{"R_IRQMSK_MISC",	0x11},
+	{"R_SCI_MSK",		0x12},
+	{"R_IRQ_CTRL",		0x13},
+	{"R_PCM_MD0",		0x14},
+	{"R_0x15",		0x15},
+	{"R_ST_SEL",		0x16},
+	{"R_ST_SYNC",		0x17},
+	{"R_CONF_EN",		0x18},
+	{"R_TI_WD",		0x1A},
+	{"R_BERT_WD_MD",	0x1B},
+	{"R_DTMF",		0x1C},
+	{"R_DTMF_N",		0x1D},
+	{"R_E1_XX_STA",		0x20},
+	{"R_LOS0",		0x22},
+	{"R_LOS1",		0x23},
+	{"R_RX0",		0x24},
+	{"R_RX_FR0",		0x25},
+	{"R_RX_FR1",		0x26},
+	{"R_TX0",		0x28},
+	{"R_TX1",		0x29},
+	{"R_TX_FR0",		0x2C},
+	{"R_TX_FR1",		0x2D},
+	{"R_TX_FR2",		0x2E},
+	{"R_JATT_ATT",		0x2F},
+	{"A_ST_xx_STA/R_RX_OFF", 0x30},
+	{"A_ST_CTRL0/R_SYNC_OUT", 0x31},
+	{"A_ST_CTRL1",		0x32},
+	{"A_ST_CTRL2",		0x33},
+	{"A_ST_SQ_WR",		0x34},
+	{"R_TX_OFF",		0x34},
+	{"R_SYNC_CTRL",		0x35},
+	{"A_ST_CLK_DLY",	0x37},
+	{"R_PWM0",		0x38},
+	{"R_PWM1",		0x39},
+	{"A_ST_B1_TX",		0x3C},
+	{"A_ST_B2_TX",		0x3D},
+	{"A_ST_D_TX",		0x3E},
+	{"R_GPIO_OUT0",		0x40},
+	{"R_GPIO_OUT1",		0x41},
+	{"R_GPIO_EN0",		0x42},
+	{"R_GPIO_EN1",		0x43},
+	{"R_GPIO_SEL",		0x44},
+	{"R_BRG_CTRL",		0x45},
+	{"R_PWM_MD",		0x46},
+	{"R_BRG_MD",		0x47},
+	{"R_BRG_TIM0",		0x48},
+	{"R_BRG_TIM1",		0x49},
+	{"R_BRG_TIM2",		0x4A},
+	{"R_BRG_TIM3",		0x4B},
+	{"R_BRG_TIM_SEL01",	0x4C},
+	{"R_BRG_TIM_SEL23",	0x4D},
+	{"R_BRG_TIM_SEL45",	0x4E},
+	{"R_BRG_TIM_SEL67",	0x4F},
+	{"A_FIFO_DATA0-2",	0x80},
+	{"A_FIFO_DATA0-2_NOINC", 0x84},
+	{"R_RAM_DATA",		0xC0},
+	{"A_SL_CFG",		0xD0},
+	{"A_CONF",		0xD1},
+	{"A_CH_MSK",		0xF4},
+	{"A_CON_HDLC",		0xFA},
+	{"A_SUBCH_CFG",		0xFB},
+	{"A_CHANNEL",		0xFC},
+	{"A_FIFO_SEQ",		0xFD},
+	{"A_IRQ_MSK",		0xFF},
+	{NULL, 0},
+
+	/* read registers */
+	{"A_Z1",		0x04},
+	{"A_Z1H",		0x05},
+	{"A_Z2",		0x06},
+	{"A_Z2H",		0x07},
+	{"A_F1",		0x0C},
+	{"A_F2",		0x0D},
+	{"R_IRQ_OVIEW",		0x10},
+	{"R_IRQ_MISC",		0x11},
+	{"R_IRQ_STATECH",	0x12},
+	{"R_CONF_OFLOW",	0x14},
+	{"R_RAM_USE",		0x15},
+	{"R_CHIP_ID",		0x16},
+	{"R_BERT_STA",		0x17},
+	{"R_F0_CNTL",		0x18},
+	{"R_F0_CNTH",		0x19},
+	{"R_BERT_ECL",		0x1A},
+	{"R_BERT_ECH",		0x1B},
+	{"R_STATUS",		0x1C},
+	{"R_CHIP_RV",		0x1F},
+	{"R_STATE",		0x20},
+	{"R_SYNC_STA",		0x24},
+	{"R_RX_SL0_0",		0x25},
+	{"R_RX_SL0_1",		0x26},
+	{"R_RX_SL0_2",		0x27},
+	{"R_JATT_DIR",		0x2b},
+	{"R_SLIP",		0x2c},
+	{"A_ST_RD_STA",		0x30},
+	{"R_FAS_ECL",		0x30},
+	{"R_FAS_ECH",		0x31},
+	{"R_VIO_ECL",		0x32},
+	{"R_VIO_ECH",		0x33},
+	{"R_CRC_ECL / A_ST_SQ_RD", 0x34},
+	{"R_CRC_ECH",		0x35},
+	{"R_E_ECL",		0x36},
+	{"R_E_ECH",		0x37},
+	{"R_SA6_SA13_ECL",	0x38},
+	{"R_SA6_SA13_ECH",	0x39},
+	{"R_SA6_SA23_ECL",	0x3A},
+	{"R_SA6_SA23_ECH",	0x3B},
+	{"A_ST_B1_RX",		0x3C},
+	{"A_ST_B2_RX",		0x3D},
+	{"A_ST_D_RX",		0x3E},
+	{"A_ST_E_RX",		0x3F},
+	{"R_GPIO_IN0",		0x40},
+	{"R_GPIO_IN1",		0x41},
+	{"R_GPI_IN0",		0x44},
+	{"R_GPI_IN1",		0x45},
+	{"R_GPI_IN2",		0x46},
+	{"R_GPI_IN3",		0x47},
+	{"A_FIFO_DATA0-2",	0x80},
+	{"A_FIFO_DATA0-2_NOINC", 0x84},
+	{"R_INT_DATA",		0x88},
+	{"R_RAM_DATA",		0xC0},
+	{"R_IRQ_FIFO_BL0",	0xC8},
+	{"R_IRQ_FIFO_BL1",	0xC9},
+	{"R_IRQ_FIFO_BL2",	0xCA},
+	{"R_IRQ_FIFO_BL3",	0xCB},
+	{"R_IRQ_FIFO_BL4",	0xCC},
+	{"R_IRQ_FIFO_BL5",	0xCD},
+	{"R_IRQ_FIFO_BL6",	0xCE},
+	{"R_IRQ_FIFO_BL7",	0xCF},
+};
+#endif /* HFC_REGISTER_DEBUG */
+
diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h
new file mode 100644
index 0000000..fd2c9be
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfc_pci.h
@@ -0,0 +1,228 @@
+/*
+ *  specific defines for CCD's HFC 2BDS0 PCI chips
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * thresholds for transparent B-channel mode
+ * change mask and threshold simultaneously
+ */
+#define HFCPCI_BTRANS_THRESHOLD 128
+#define HFCPCI_BTRANS_MAX	256
+#define HFCPCI_BTRANS_THRESMASK 0x00
+
+/* defines for PCI config */
+#define PCI_ENA_MEMIO		0x02
+#define PCI_ENA_MASTER		0x04
+
+/* GCI/IOM bus monitor registers */
+#define HCFPCI_C_I		0x08
+#define HFCPCI_TRxR		0x0C
+#define HFCPCI_MON1_D		0x28
+#define HFCPCI_MON2_D		0x2C
+
+/* GCI/IOM bus timeslot registers */
+#define HFCPCI_B1_SSL		0x80
+#define HFCPCI_B2_SSL		0x84
+#define HFCPCI_AUX1_SSL		0x88
+#define HFCPCI_AUX2_SSL		0x8C
+#define HFCPCI_B1_RSL		0x90
+#define HFCPCI_B2_RSL		0x94
+#define HFCPCI_AUX1_RSL		0x98
+#define HFCPCI_AUX2_RSL		0x9C
+
+/* GCI/IOM bus data registers */
+#define HFCPCI_B1_D		0xA0
+#define HFCPCI_B2_D		0xA4
+#define HFCPCI_AUX1_D		0xA8
+#define HFCPCI_AUX2_D		0xAC
+
+/* GCI/IOM bus configuration registers */
+#define HFCPCI_MST_EMOD		0xB4
+#define HFCPCI_MST_MODE		0xB8
+#define HFCPCI_CONNECT 		0xBC
+
+
+/* Interrupt and status registers */
+#define HFCPCI_FIFO_EN		0x44
+#define HFCPCI_TRM		0x48
+#define HFCPCI_B_MODE		0x4C
+#define HFCPCI_CHIP_ID		0x58
+#define HFCPCI_CIRM		0x60
+#define HFCPCI_CTMT		0x64
+#define HFCPCI_INT_M1		0x68
+#define HFCPCI_INT_M2		0x6C
+#define HFCPCI_INT_S1		0x78
+#define HFCPCI_INT_S2		0x7C
+#define HFCPCI_STATUS		0x70
+
+/* S/T section registers */
+#define HFCPCI_STATES		0xC0
+#define HFCPCI_SCTRL		0xC4
+#define HFCPCI_SCTRL_E		0xC8
+#define HFCPCI_SCTRL_R		0xCC
+#define HFCPCI_SQ		0xD0
+#define HFCPCI_CLKDEL		0xDC
+#define HFCPCI_B1_REC		0xF0
+#define HFCPCI_B1_SEND		0xF0
+#define HFCPCI_B2_REC		0xF4
+#define HFCPCI_B2_SEND		0xF4
+#define HFCPCI_D_REC		0xF8
+#define HFCPCI_D_SEND		0xF8
+#define HFCPCI_E_REC		0xFC
+
+
+/* bits in status register (READ) */
+#define HFCPCI_PCI_PROC		0x02
+#define HFCPCI_NBUSY		0x04
+#define HFCPCI_TIMER_ELAP	0x10
+#define HFCPCI_STATINT		0x20
+#define HFCPCI_FRAMEINT		0x40
+#define HFCPCI_ANYINT		0x80
+
+/* bits in CTMT (Write) */
+#define HFCPCI_CLTIMER		0x80
+#define HFCPCI_TIM3_125		0x04
+#define HFCPCI_TIM25		0x10
+#define HFCPCI_TIM50		0x14
+#define HFCPCI_TIM400		0x18
+#define HFCPCI_TIM800		0x1C
+#define HFCPCI_AUTO_TIMER	0x20
+#define HFCPCI_TRANSB2		0x02
+#define HFCPCI_TRANSB1		0x01
+
+/* bits in CIRM (Write) */
+#define HFCPCI_AUX_MSK		0x07
+#define HFCPCI_RESET		0x08
+#define HFCPCI_B1_REV		0x40
+#define HFCPCI_B2_REV		0x80
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCPCI_INTS_B1TRANS	0x01
+#define HFCPCI_INTS_B2TRANS	0x02
+#define HFCPCI_INTS_DTRANS	0x04
+#define HFCPCI_INTS_B1REC	0x08
+#define HFCPCI_INTS_B2REC	0x10
+#define HFCPCI_INTS_DREC	0x20
+#define HFCPCI_INTS_L1STATE	0x40
+#define HFCPCI_INTS_TIMER	0x80
+
+/* bits in INT_M2 */
+#define HFCPCI_PROC_TRANS	0x01
+#define HFCPCI_GCI_I_CHG	0x02
+#define HFCPCI_GCI_MON_REC	0x04
+#define HFCPCI_IRQ_ENABLE	0x08
+#define HFCPCI_PMESEL		0x80
+
+/* bits in STATES */
+#define HFCPCI_STATE_MSK	0x0F
+#define HFCPCI_LOAD_STATE	0x10
+#define HFCPCI_ACTIVATE		0x20
+#define HFCPCI_DO_ACTION	0x40
+#define HFCPCI_NT_G2_G3		0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCPCI_MASTER		0x01
+#define HFCPCI_SLAVE		0x00
+#define HFCPCI_F0IO_POSITIV	0x02
+#define HFCPCI_F0_NEGATIV	0x04
+#define HFCPCI_F0_2C4		0x08
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define SCTRL_B1_ENA		0x01
+#define SCTRL_B2_ENA		0x02
+#define SCTRL_MODE_TE		0x00
+#define SCTRL_MODE_NT		0x04
+#define SCTRL_LOW_PRIO		0x08
+#define SCTRL_SQ_ENA		0x10
+#define SCTRL_TEST		0x20
+#define SCTRL_NONE_CAP		0x40
+#define SCTRL_PWR_DOWN		0x80
+
+/* bits in SCTRL_E  */
+#define HFCPCI_AUTO_AWAKE	0x01
+#define HFCPCI_DBIT_1		0x04
+#define HFCPCI_IGNORE_COL	0x08
+#define HFCPCI_CHG_B1_B2	0x80
+
+/* bits in FIFO_EN register */
+#define HFCPCI_FIFOEN_B1	0x03
+#define HFCPCI_FIFOEN_B2	0x0C
+#define HFCPCI_FIFOEN_DTX	0x10
+#define HFCPCI_FIFOEN_B1TX	0x01
+#define HFCPCI_FIFOEN_B1RX	0x02
+#define HFCPCI_FIFOEN_B2TX	0x04
+#define HFCPCI_FIFOEN_B2RX	0x08
+
+
+/* definitions of fifo memory area */
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL    0x200
+#define B_FIFO_SIZE  (0x2000 - B_SUB_VAL)
+#define D_FIFO_SIZE  512
+#define D_FREG_MASK  0xF
+
+struct zt {
+	unsigned short z1;  /* Z1 pointer 16 Bit */
+	unsigned short z2;  /* Z2 pointer 16 Bit */
+};
+
+struct dfifo {
+	u_char data[D_FIFO_SIZE]; /* FIFO data space */
+	u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */
+	u_char f1, f2; /* f pointers */
+	u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */
+	/* mask index with D_FREG_MASK for access */
+	struct zt za[MAX_D_FRAMES+1];
+	u_char fill3[0x4000-0x2100]; /* align 16K */
+};
+
+struct bzfifo {
+	struct zt	za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */
+	u_char		f1, f2; /* f pointers */
+	u_char		fill[0x2100-0x2082]; /* alignment */
+};
+
+
+union fifo_area {
+	struct {
+		struct dfifo d_tx; /* D-send channel */
+		struct dfifo d_rx; /* D-receive channel */
+	} d_chan;
+	struct {
+		u_char		fill1[0x200];
+		u_char		txdat_b1[B_FIFO_SIZE];
+		struct bzfifo	txbz_b1;
+		struct bzfifo	txbz_b2;
+		u_char		txdat_b2[B_FIFO_SIZE];
+		u_char		fill2[D_FIFO_SIZE];
+		u_char		rxdat_b1[B_FIFO_SIZE];
+		struct bzfifo	rxbz_b1;
+		struct bzfifo	rxbz_b2;
+		u_char rxdat_b2[B_FIFO_SIZE];
+	} b_chans;
+	u_char fill[32768];
+};
+
+#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io)+b))
+#define Read_hfc(a, b) (readb((a->hw.pci_io)+b))
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
new file mode 100644
index 0000000..2649ea5
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -0,0 +1,5320 @@
+/*
+ * hfcmulti.c  low level driver for hfc-4s/hfc-8s/hfc-e1 based cards
+ *
+ * Author	Andreas Eversberg (jolly@eversberg.eu)
+ * ported to mqueue mechanism:
+ *		Peter Sprenger (sprengermoving-bytes.de)
+ *
+ * inspired by existing hfc-pci driver:
+ * Copyright 1999  by Werner Cornelius (werner@isdn-development.de)
+ * Copyright 2008  by Karsten Keil (kkeil@suse.de)
+ * Copyright 2008  by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Thanks to Cologne Chip AG for this great controller!
+ */
+
+/*
+ * module parameters:
+ * type:
+ *	By default (0), the card is automatically detected.
+ *	Or use the following combinations:
+ *	Bit 0-7   = 0x00001 = HFC-E1 (1 port)
+ * or	Bit 0-7   = 0x00004 = HFC-4S (4 ports)
+ * or	Bit 0-7   = 0x00008 = HFC-8S (8 ports)
+ *	Bit 8     = 0x00100 = uLaw (instead of aLaw)
+ *	Bit 9     = 0x00200 = Disable DTMF detect on all B-channels via hardware
+ *	Bit 10    = spare
+ *	Bit 11    = 0x00800 = Force PCM bus into slave mode. (otherwhise auto)
+ * or   Bit 12    = 0x01000 = Force PCM bus into master mode. (otherwhise auto)
+ *	Bit 13	  = spare
+ *	Bit 14    = 0x04000 = Use external ram (128K)
+ *	Bit 15    = 0x08000 = Use external ram (512K)
+ *	Bit 16    = 0x10000 = Use 64 timeslots instead of 32
+ * or	Bit 17    = 0x20000 = Use 128 timeslots instead of anything else
+ *	Bit 18    = spare
+ *	Bit 19    = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog)
+ * (all other bits are reserved and shall be 0)
+ *	example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM
+ *		 bus (PCM master)
+ *
+ * port: (optional or required for all ports on all installed cards)
+ *	HFC-4S/HFC-8S only bits:
+ *	Bit 0	  = 0x001 = Use master clock for this S/T interface
+ *			    (ony once per chip).
+ *	Bit 1     = 0x002 = transmitter line setup (non capacitive mode)
+ *			    Don't use this unless you know what you are doing!
+ *	Bit 2     = 0x004 = Disable E-channel. (No E-channel processing)
+ *	example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock
+ *		 received from port 1
+ *
+ *	HFC-E1 only bits:
+ *	Bit 0     = 0x0001 = interface: 0=copper, 1=optical
+ *	Bit 1     = 0x0002 = reserved (later for 32 B-channels transparent mode)
+ *	Bit 2     = 0x0004 = Report LOS
+ *	Bit 3     = 0x0008 = Report AIS
+ *	Bit 4     = 0x0010 = Report SLIP
+ *	Bit 5     = 0x0020 = Report RDI
+ *	Bit 8     = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame
+ *			     mode instead.
+ *	Bit 9	  = 0x0200 = Force get clock from interface, even in NT mode.
+ * or	Bit 10	  = 0x0400 = Force put clock to interface, even in TE mode.
+ *	Bit 11    = 0x0800 = Use direct RX clock for PCM sync rather than PLL.
+ *			     (E1 only)
+ *	Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0
+ *			     for default.
+ * (all other bits are reserved and shall be 0)
+ *
+ * debug:
+ *	NOTE: only one debug value must be given for all cards
+ *	enable debugging (see hfc_multi.h for debug options)
+ *
+ * poll:
+ *	NOTE: only one poll value must be given for all cards
+ *	Give the number of samples for each fifo process.
+ *	By default 128 is used. Decrease to reduce delay, increase to
+ *	reduce cpu load. If unsure, don't mess with it!
+ *	Valid is 8, 16, 32, 64, 128, 256.
+ *
+ * pcm:
+ *	NOTE: only one pcm value must be given for every card.
+ *	The PCM bus id tells the mISDNdsp module about the connected PCM bus.
+ *	By default (0), the PCM bus id is 100 for the card that is PCM master.
+ *	If multiple cards are PCM master (because they are not interconnected),
+ *	each card with PCM master will have increasing PCM id.
+ *	All PCM busses with the same ID are expected to be connected and have
+ *	common time slots slots.
+ *	Only one chip of the PCM bus must be master, the others slave.
+ *	-1 means no support of PCM bus not even.
+ *	Omit this value, if all cards are interconnected or none is connected.
+ *	If unsure, don't give this parameter.
+ *
+ * dslot:
+ *	NOTE: only one poll value must be given for every card.
+ *	Also this value must be given for non-E1 cards. If omitted, the E1
+ *	card has D-channel on time slot 16, which is default.
+ *	If 1..15 or 17..31, an alternate time slot is used for D-channel.
+ *	In this case, the application must be able to handle this.
+ *	If -1 is given, the D-channel is disabled and all 31 slots can be used
+ *	for B-channel. (only for specific applications)
+ *	If you don't know how to use it, you don't need it!
+ *
+ * iomode:
+ *	NOTE: only one mode value must be given for every card.
+ *	-> See hfc_multi.h for HFC_IO_MODE_* values
+ *	By default, the IO mode is pci memory IO (MEMIO).
+ *	Some cards requre specific IO mode, so it cannot be changed.
+ *	It may be usefull to set IO mode to register io (REGIO) to solve
+ *	PCI bridge problems.
+ *	If unsure, don't give this parameter.
+ *
+ * clockdelay_nt:
+ *	NOTE: only one clockdelay_nt value must be given once for all cards.
+ *	Give the value of the clock control register (A_ST_CLK_DLY)
+ *	of the S/T interfaces in NT mode.
+ *	This register is needed for the TBR3 certification, so don't change it.
+ *
+ * clockdelay_te:
+ *	NOTE: only one clockdelay_te value must be given once
+ *	Give the value of the clock control register (A_ST_CLK_DLY)
+ *	of the S/T interfaces in TE mode.
+ *	This register is needed for the TBR3 certification, so don't change it.
+ */
+
+/*
+ * debug register access (never use this, it will flood your system log)
+ * #define HFC_REGISTER_DEBUG
+ */
+
+static const char *hfcmulti_revision = "2.00";
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+
+/*
+#define IRQCOUNT_DEBUG
+#define IRQ_DEBUG
+*/
+
+#include "hfc_multi.h"
+#ifdef ECHOPREP
+#include "gaintab.h"
+#endif
+
+#define	MAX_CARDS	8
+#define	MAX_PORTS	(8 * MAX_CARDS)
+
+static LIST_HEAD(HFClist);
+static spinlock_t HFClock; /* global hfc list lock */
+
+static void ph_state_change(struct dchannel *);
+static void (*hfc_interrupt)(void);
+static void (*register_interrupt)(void);
+static int (*unregister_interrupt)(void);
+static int interrupt_registered;
+
+static struct hfc_multi *syncmaster;
+int plxsd_master; /* if we have a master card (yet) */
+static spinlock_t plx_lock; /* may not acquire other lock inside */
+EXPORT_SYMBOL(plx_lock);
+
+#define	TYP_E1		1
+#define	TYP_4S		4
+#define TYP_8S		8
+
+static int poll_timer = 6;	/* default = 128 samples = 16ms */
+/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */
+static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30  };
+#define	CLKDEL_TE	0x0f	/* CLKDEL in TE mode */
+#define	CLKDEL_NT	0x6c	/* CLKDEL in NT mode
+				   (0x60 MUST be included!) */
+static u_char silence =	0xff;	/* silence by LAW */
+
+#define	DIP_4S	0x1		/* DIP Switches for Beronet 1S/2S/4S cards */
+#define	DIP_8S	0x2		/* DIP Switches for Beronet 8S+ cards */
+#define	DIP_E1	0x3		/* DIP Switches for Beronet E1 cards */
+
+/*
+ * module stuff
+ */
+
+static uint	type[MAX_CARDS];
+static uint	pcm[MAX_CARDS];
+static uint	dslot[MAX_CARDS];
+static uint	iomode[MAX_CARDS];
+static uint	port[MAX_PORTS];
+static uint	debug;
+static uint	poll;
+static uint	timer;
+static uint	clockdelay_te = CLKDEL_TE;
+static uint	clockdelay_nt = CLKDEL_NT;
+
+static int	HFC_cnt, Port_cnt, PCM_cnt = 99;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(timer, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR);
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+
+#ifdef HFC_REGISTER_DEBUG
+#define HFC_outb(hc, reg, val) \
+	(hc->HFC_outb(hc, reg, val, __func__, __LINE__))
+#define HFC_outb_nodebug(hc, reg, val) \
+	(hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__))
+#define HFC_inb(hc, reg) \
+	(hc->HFC_inb(hc, reg, __func__, __LINE__))
+#define HFC_inb_nodebug(hc, reg) \
+	(hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_inw(hc, reg) \
+	(hc->HFC_inw(hc, reg, __func__, __LINE__))
+#define HFC_inw_nodebug(hc, reg) \
+	(hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_wait(hc) \
+	(hc->HFC_wait(hc, __func__, __LINE__))
+#define HFC_wait_nodebug(hc) \
+	(hc->HFC_wait_nodebug(hc, __func__, __LINE__))
+#else
+#define HFC_outb(hc, reg, val)		(hc->HFC_outb(hc, reg, val))
+#define HFC_outb_nodebug(hc, reg, val)	(hc->HFC_outb_nodebug(hc, reg, val))
+#define HFC_inb(hc, reg)		(hc->HFC_inb(hc, reg))
+#define HFC_inb_nodebug(hc, reg)	(hc->HFC_inb_nodebug(hc, reg))
+#define HFC_inw(hc, reg)		(hc->HFC_inw(hc, reg))
+#define HFC_inw_nodebug(hc, reg)	(hc->HFC_inw_nodebug(hc, reg))
+#define HFC_wait(hc)			(hc->HFC_wait(hc))
+#define HFC_wait_nodebug(hc)		(hc->HFC_wait_nodebug(hc))
+#endif
+
+/* HFC_IO_MODE_PCIMEM */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val,
+		const char *function, int line)
+#else
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+	writeb(val, (hc->pci_membase)+reg);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+	return readb((hc->pci_membase)+reg);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+	return readw((hc->pci_membase)+reg);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_pcimem(struct hfc_multi *hc)
+#endif
+{
+	while (readb((hc->pci_membase)+R_STATUS) & V_BUSY);
+}
+
+/* HFC_IO_MODE_REGIO */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val,
+	const char *function, int line)
+#else
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+	outb(reg, (hc->pci_iobase)+4);
+	outb(val, hc->pci_iobase);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+	outb(reg, (hc->pci_iobase)+4);
+	return inb(hc->pci_iobase);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+	outb(reg, (hc->pci_iobase)+4);
+	return inw(hc->pci_iobase);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_regio(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_regio(struct hfc_multi *hc)
+#endif
+{
+	outb(R_STATUS, (hc->pci_iobase)+4);
+	while (inb(hc->pci_iobase) & V_BUSY);
+}
+
+#ifdef HFC_REGISTER_DEBUG
+static void
+HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val,
+		const char *function, int line)
+{
+	char regname[256] = "", bits[9] = "xxxxxxxx";
+	int i;
+
+	i = -1;
+	while (hfc_register_names[++i].name) {
+		if (hfc_register_names[i].reg == reg)
+			strcat(regname, hfc_register_names[i].name);
+	}
+	if (regname[0] == '\0')
+		strcpy(regname, "register");
+
+	bits[7] = '0'+(!!(val&1));
+	bits[6] = '0'+(!!(val&2));
+	bits[5] = '0'+(!!(val&4));
+	bits[4] = '0'+(!!(val&8));
+	bits[3] = '0'+(!!(val&16));
+	bits[2] = '0'+(!!(val&32));
+	bits[1] = '0'+(!!(val&64));
+	bits[0] = '0'+(!!(val&128));
+	printk(KERN_DEBUG
+	    "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n",
+	    hc->id, reg, regname, val, bits, function, line);
+	HFC_outb_nodebug(hc, reg, val);
+}
+static u_char
+HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+	char regname[256] = "", bits[9] = "xxxxxxxx";
+	u_char val = HFC_inb_nodebug(hc, reg);
+	int i;
+
+	i = 0;
+	while (hfc_register_names[i++].name)
+		;
+	while (hfc_register_names[++i].name) {
+		if (hfc_register_names[i].reg == reg)
+			strcat(regname, hfc_register_names[i].name);
+	}
+	if (regname[0] == '\0')
+		strcpy(regname, "register");
+
+	bits[7] = '0'+(!!(val&1));
+	bits[6] = '0'+(!!(val&2));
+	bits[5] = '0'+(!!(val&4));
+	bits[4] = '0'+(!!(val&8));
+	bits[3] = '0'+(!!(val&16));
+	bits[2] = '0'+(!!(val&32));
+	bits[1] = '0'+(!!(val&64));
+	bits[0] = '0'+(!!(val&128));
+	printk(KERN_DEBUG
+	    "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n",
+	    hc->id, reg, regname, val, bits, function, line);
+	return val;
+}
+static u_short
+HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+	char regname[256] = "";
+	u_short val = HFC_inw_nodebug(hc, reg);
+	int i;
+
+	i = 0;
+	while (hfc_register_names[i++].name)
+		;
+	while (hfc_register_names[++i].name) {
+		if (hfc_register_names[i].reg == reg)
+			strcat(regname, hfc_register_names[i].name);
+	}
+	if (regname[0] == '\0')
+		strcpy(regname, "register");
+
+	printk(KERN_DEBUG
+	    "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n",
+	    hc->id, reg, regname, val, function, line);
+	return val;
+}
+static void
+HFC_wait_debug(struct hfc_multi *hc, const char *function, int line)
+{
+	printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n",
+	    hc->id, function, line);
+	HFC_wait_nodebug(hc);
+}
+#endif
+
+/* write fifo data (REGIO) */
+void
+write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+	outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+	while (len>>2) {
+		outl(*(u32 *)data, hc->pci_iobase);
+		data += 4;
+		len -= 4;
+	}
+	while (len>>1) {
+		outw(*(u16 *)data, hc->pci_iobase);
+		data += 2;
+		len -= 2;
+	}
+	while (len) {
+		outb(*data, hc->pci_iobase);
+		data++;
+		len--;
+	}
+}
+/* write fifo data (PCIMEM) */
+void
+write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+	while (len>>2) {
+		writel(*(u32 *)data, (hc->pci_membase)+A_FIFO_DATA0);
+		data += 4;
+		len -= 4;
+	}
+	while (len>>1) {
+		writew(*(u16 *)data, (hc->pci_membase)+A_FIFO_DATA0);
+		data += 2;
+		len -= 2;
+	}
+	while (len) {
+		writeb(*data, (hc->pci_membase)+A_FIFO_DATA0);
+		data++;
+		len--;
+	}
+}
+/* read fifo data (REGIO) */
+void
+read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+	outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+	while (len>>2) {
+		*(u32 *)data = inl(hc->pci_iobase);
+		data += 4;
+		len -= 4;
+	}
+	while (len>>1) {
+		*(u16 *)data = inw(hc->pci_iobase);
+		data += 2;
+		len -= 2;
+	}
+	while (len) {
+		*data = inb(hc->pci_iobase);
+		data++;
+		len--;
+	}
+}
+
+/* read fifo data (PCIMEM) */
+void
+read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+	while (len>>2) {
+		*(u32 *)data =
+			readl((hc->pci_membase)+A_FIFO_DATA0);
+		data += 4;
+		len -= 4;
+	}
+	while (len>>1) {
+		*(u16 *)data =
+			readw((hc->pci_membase)+A_FIFO_DATA0);
+		data += 2;
+		len -= 2;
+	}
+	while (len) {
+		*data = readb((hc->pci_membase)+A_FIFO_DATA0);
+		data++;
+		len--;
+	}
+}
+
+
+static void
+enable_hwirq(struct hfc_multi *hc)
+{
+	hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN;
+	HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+static void
+disable_hwirq(struct hfc_multi *hc)
+{
+	hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN);
+	HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+#define	NUM_EC 2
+#define	MAX_TDM_CHAN 32
+
+
+inline void
+enablepcibridge(struct hfc_multi *c)
+{
+	HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */
+}
+
+inline void
+disablepcibridge(struct hfc_multi *c)
+{
+	HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */
+}
+
+inline unsigned char
+readpcibridge(struct hfc_multi *hc, unsigned char address)
+{
+	unsigned short cipv;
+	unsigned char data;
+
+	if (!hc->pci_iobase)
+		return 0;
+
+	/* slow down a PCI read access by 1 PCI clock cycle */
+	HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/
+
+	if (address == 0)
+		cipv = 0x4000;
+	else
+		cipv = 0x5800;
+
+	/* select local bridge port address by writing to CIP port */
+	/* data = HFC_inb(c, cipv); * was _io before */
+	outw(cipv, hc->pci_iobase + 4);
+	data = inb(hc->pci_iobase);
+
+	/* restore R_CTRL for normal PCI read cycle speed */
+	HFC_outb(hc, R_CTRL, 0x0); /* was _io before */
+
+	return data;
+}
+
+inline void
+writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data)
+{
+	unsigned short cipv;
+	unsigned int datav;
+
+	if (!hc->pci_iobase)
+		return;
+
+	if (address == 0)
+		cipv = 0x4000;
+	else
+		cipv = 0x5800;
+
+	/* select local bridge port address by writing to CIP port */
+	outw(cipv, hc->pci_iobase + 4);
+	/* define a 32 bit dword with 4 identical bytes for write sequence */
+	datav = data | ((__u32) data << 8) | ((__u32) data << 16) |
+	    ((__u32) data << 24);
+
+	/*
+	 * write this 32 bit dword to the bridge data port
+	 * this will initiate a write sequence of up to 4 writes to the same
+	 * address on the local bus interface the number of write accesses
+	 * is undefined but >=1 and depends on the next PCI transaction
+	 * during write sequence on the local bus
+	 */
+	outl(datav, hc->pci_iobase);
+}
+
+inline void
+cpld_set_reg(struct hfc_multi *hc, unsigned char reg)
+{
+	/* Do data pin read low byte */
+	HFC_outb(hc, R_GPIO_OUT1, reg);
+}
+
+inline void
+cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val)
+{
+	cpld_set_reg(hc, reg);
+
+	enablepcibridge(hc);
+	writepcibridge(hc, 1, val);
+	disablepcibridge(hc);
+
+	return;
+}
+
+inline unsigned char
+cpld_read_reg(struct hfc_multi *hc, unsigned char reg)
+{
+	unsigned char bytein;
+
+	cpld_set_reg(hc, reg);
+
+	/* Do data pin read low byte */
+	HFC_outb(hc, R_GPIO_OUT1, reg);
+
+	enablepcibridge(hc);
+	bytein = readpcibridge(hc, 1);
+	disablepcibridge(hc);
+
+	return bytein;
+}
+
+inline void
+vpm_write_address(struct hfc_multi *hc, unsigned short addr)
+{
+	cpld_write_reg(hc, 0, 0xff & addr);
+	cpld_write_reg(hc, 1, 0x01 & (addr >> 8));
+}
+
+inline unsigned short
+vpm_read_address(struct hfc_multi *c)
+{
+	unsigned short addr;
+	unsigned short highbit;
+
+	addr = cpld_read_reg(c, 0);
+	highbit = cpld_read_reg(c, 1);
+
+	addr = addr | (highbit << 8);
+
+	return addr & 0x1ff;
+}
+
+inline unsigned char
+vpm_in(struct hfc_multi *c, int which, unsigned short addr)
+{
+	unsigned char res;
+
+	vpm_write_address(c, addr);
+
+	if (!which)
+		cpld_set_reg(c, 2);
+	else
+		cpld_set_reg(c, 3);
+
+	enablepcibridge(c);
+	res = readpcibridge(c, 1);
+	disablepcibridge(c);
+
+	cpld_set_reg(c, 0);
+
+	return res;
+}
+
+inline void
+vpm_out(struct hfc_multi *c, int which, unsigned short addr,
+    unsigned char data)
+{
+	vpm_write_address(c, addr);
+
+	enablepcibridge(c);
+
+	if (!which)
+		cpld_set_reg(c, 2);
+	else
+		cpld_set_reg(c, 3);
+
+	writepcibridge(c, 1, data);
+
+	cpld_set_reg(c, 0);
+
+	disablepcibridge(c);
+
+	{
+	unsigned char regin;
+	regin = vpm_in(c, which, addr);
+	if (regin != data)
+		printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back "
+			"0x%x\n", data, addr, regin);
+	}
+
+}
+
+
+void
+vpm_init(struct hfc_multi *wc)
+{
+	unsigned char reg;
+	unsigned int mask;
+	unsigned int i, x, y;
+	unsigned int ver;
+
+	for (x = 0; x < NUM_EC; x++) {
+		/* Setup GPIO's */
+		if (!x) {
+			ver = vpm_in(wc, x, 0x1a0);
+			printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver);
+		}
+
+		for (y = 0; y < 4; y++) {
+			vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
+			vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
+			vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
+		}
+
+		/* Setup TDM path - sets fsync and tdm_clk as inputs */
+		reg = vpm_in(wc, x, 0x1a3); /* misc_con */
+		vpm_out(wc, x, 0x1a3, reg & ~2);
+
+		/* Setup Echo length (256 taps) */
+		vpm_out(wc, x, 0x022, 1);
+		vpm_out(wc, x, 0x023, 0xff);
+
+		/* Setup timeslots */
+		vpm_out(wc, x, 0x02f, 0x00);
+		mask = 0x02020202 << (x * 4);
+
+		/* Setup the tdm channel masks for all chips */
+		for (i = 0; i < 4; i++)
+			vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff);
+
+		/* Setup convergence rate */
+		printk(KERN_DEBUG "VPM: A-law mode\n");
+		reg = 0x00 | 0x10 | 0x01;
+		vpm_out(wc, x, 0x20, reg);
+		printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg);
+		/*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */
+
+		vpm_out(wc, x, 0x24, 0x02);
+		reg = vpm_in(wc, x, 0x24);
+		printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg);
+
+		/* Initialize echo cans */
+		for (i = 0; i < MAX_TDM_CHAN; i++) {
+			if (mask & (0x00000001 << i))
+				vpm_out(wc, x, i, 0x00);
+		}
+
+		/*
+		 * ARM arch at least disallows a udelay of
+		 * more than 2ms... it gives a fake "__bad_udelay"
+		 * reference at link-time.
+		 * long delays in kernel code are pretty sucky anyway
+		 * for now work around it using 5 x 2ms instead of 1 x 10ms
+		 */
+
+		udelay(2000);
+		udelay(2000);
+		udelay(2000);
+		udelay(2000);
+		udelay(2000);
+
+		/* Put in bypass mode */
+		for (i = 0; i < MAX_TDM_CHAN; i++) {
+			if (mask & (0x00000001 << i))
+				vpm_out(wc, x, i, 0x01);
+		}
+
+		/* Enable bypass */
+		for (i = 0; i < MAX_TDM_CHAN; i++) {
+			if (mask & (0x00000001 << i))
+				vpm_out(wc, x, 0x78 + i, 0x01);
+		}
+
+	}
+}
+
+void
+vpm_check(struct hfc_multi *hctmp)
+{
+	unsigned char gpi2;
+
+	gpi2 = HFC_inb(hctmp, R_GPI_IN2);
+
+	if ((gpi2 & 0x3) != 0x3)
+		printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2);
+}
+
+
+/*
+ * Interface to enable/disable the HW Echocan
+ *
+ * these functions are called within a spin_lock_irqsave on
+ * the channel instance lock, so we are not disturbed by irqs
+ *
+ * we can later easily change the interface to make  other
+ * things configurable, for now we configure the taps
+ *
+ */
+
+void
+vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
+{
+	unsigned int timeslot;
+	unsigned int unit;
+	struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+	int txadj = -4;
+	struct sk_buff *skb;
+#endif
+	if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+		return;
+
+	if (!bch)
+		return;
+
+#ifdef TXADJ
+	skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+		sizeof(int), &txadj, GFP_ATOMIC);
+	if (skb)
+		recv_Bchannel_skb(bch, skb);
+#endif
+
+	timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+	unit = ch % 4;
+
+	printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n",
+	    taps, timeslot);
+
+	vpm_out(hc, unit, timeslot, 0x7e);
+}
+
+void
+vpm_echocan_off(struct hfc_multi *hc, int ch)
+{
+	unsigned int timeslot;
+	unsigned int unit;
+	struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+	int txadj = 0;
+	struct sk_buff *skb;
+#endif
+
+	if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+		return;
+
+	if (!bch)
+		return;
+
+#ifdef TXADJ
+	skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+		sizeof(int), &txadj, GFP_ATOMIC);
+	if (skb)
+		recv_Bchannel_skb(bch, skb);
+#endif
+
+	timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+	unit = ch % 4;
+
+	printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n",
+	    timeslot);
+	/* FILLME */
+	vpm_out(hc, unit, timeslot, 0x01);
+}
+
+
+/*
+ * Speech Design resync feature
+ * NOTE: This is called sometimes outside interrupt handler.
+ * We must lock irqsave, so no other interrupt (other card) will occurr!
+ * Also multiple interrupts may nest, so must lock each access (lists, card)!
+ */
+static inline void
+hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
+{
+	struct hfc_multi *hc, *next, *pcmmaster = 0;
+	u_int *plx_acc_32, pv;
+	u_long flags;
+
+	spin_lock_irqsave(&HFClock, flags);
+	spin_lock(&plx_lock); /* must be locked inside other locks */
+
+	if (debug & DEBUG_HFCMULTI_PLXSD)
+		printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n",
+			__func__, syncmaster);
+
+	/* select new master */
+	if (newmaster) {
+		if (debug & DEBUG_HFCMULTI_PLXSD)
+			printk(KERN_DEBUG "using provided controller\n");
+	} else {
+		list_for_each_entry_safe(hc, next, &HFClist, list) {
+			if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+				if (hc->syncronized) {
+					newmaster = hc;
+					break;
+				}
+			}
+		}
+	}
+
+	/* Disable sync of all cards */
+	list_for_each_entry_safe(hc, next, &HFClist, list) {
+		if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+			plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+			pv = readl(plx_acc_32);
+			pv &= ~PLX_SYNC_O_EN;
+			writel(pv, plx_acc_32);
+			if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+				pcmmaster = hc;
+				if (hc->type == 1) {
+					if (debug & DEBUG_HFCMULTI_PLXSD)
+						printk(KERN_DEBUG
+							"Schedule SYNC_I\n");
+					hc->e1_resync |= 1; /* get SYNC_I */
+				}
+			}
+		}
+	}
+
+	if (newmaster) {
+		hc = newmaster;
+		if (debug & DEBUG_HFCMULTI_PLXSD)
+			printk(KERN_DEBUG "id=%d (0x%p) = syncronized with "
+				"interface.\n", hc->id, hc);
+		/* Enable new sync master */
+		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		pv = readl(plx_acc_32);
+		pv |= PLX_SYNC_O_EN;
+		writel(pv, plx_acc_32);
+		/* switch to jatt PLL, if not disabled by RX_SYNC */
+		if (hc->type == 1 && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) {
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_DEBUG "Schedule jatt PLL\n");
+			hc->e1_resync |= 2; /* switch to jatt */
+		}
+	} else {
+		if (pcmmaster) {
+			hc = pcmmaster;
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_DEBUG
+					"id=%d (0x%p) = PCM master syncronized "
+					"with QUARTZ\n", hc->id, hc);
+			if (hc->type == 1) {
+				/* Use the crystal clock for the PCM
+				   master card */
+				if (debug & DEBUG_HFCMULTI_PLXSD)
+					printk(KERN_DEBUG
+					    "Schedule QUARTZ for HFC-E1\n");
+				hc->e1_resync |= 4; /* switch quartz */
+			} else {
+				if (debug & DEBUG_HFCMULTI_PLXSD)
+					printk(KERN_DEBUG
+					    "QUARTZ is automatically "
+					    "enabled by HFC-%dS\n", hc->type);
+			}
+			plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+			pv = readl(plx_acc_32);
+			pv |= PLX_SYNC_O_EN;
+			writel(pv, plx_acc_32);
+		} else
+			if (!rm)
+				printk(KERN_ERR "%s no pcm master, this MUST "
+					"not happen!\n", __func__);
+	}
+	syncmaster = newmaster;
+
+	spin_unlock(&plx_lock);
+	spin_unlock_irqrestore(&HFClock, flags);
+}
+
+/* This must be called AND hc must be locked irqsave!!! */
+inline void
+plxsd_checksync(struct hfc_multi *hc, int rm)
+{
+	if (hc->syncronized) {
+		if (syncmaster == NULL) {
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_WARNING "%s: GOT sync on card %d"
+					" (id=%d)\n", __func__, hc->id + 1,
+					hc->id);
+			hfcmulti_resync(hc, hc, rm);
+		}
+	} else {
+		if (syncmaster == hc) {
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_WARNING "%s: LOST sync on card %d"
+					" (id=%d)\n", __func__, hc->id + 1,
+					hc->id);
+			hfcmulti_resync(hc, NULL, rm);
+		}
+	}
+}
+
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcmulti(struct hfc_multi *hc)
+{
+	u_int	*plx_acc_32, pv;
+	u_long	plx_flags;
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: entered\n", __func__);
+
+	/* soft reset also masks all interrupts */
+	hc->hw.r_cirm |= V_SRES;
+	HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+	udelay(1000);
+	hc->hw.r_cirm &= ~V_SRES;
+	HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+	udelay(1000); /* instead of 'wait' that may cause locking */
+
+	/* release Speech Design card, if PLX was initialized */
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) {
+		if (debug & DEBUG_HFCMULTI_PLXSD)
+			printk(KERN_DEBUG "%s: release PLXSD card %d\n",
+			    __func__, hc->id + 1);
+		spin_lock_irqsave(&plx_lock, plx_flags);
+		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		writel(PLX_GPIOC_INIT, plx_acc_32);
+		pv = readl(plx_acc_32);
+		/* Termination off */
+		pv &= ~PLX_TERM_ON;
+		/* Disconnect the PCM */
+		pv |= PLX_SLAVE_EN_N;
+		pv &= ~PLX_MASTER_EN;
+		pv &= ~PLX_SYNC_O_EN;
+		/* Put the DSP in Reset */
+		pv &= ~PLX_DSP_RES_N;
+		writel(pv, plx_acc_32);
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_WARNING "%s: PCM off: PLX_GPIO=%x\n",
+				__func__, pv);
+		spin_unlock_irqrestore(&plx_lock, plx_flags);
+	}
+
+	/* disable memory mapped ports / io ports */
+	test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */
+	pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
+	if (hc->pci_membase)
+		iounmap((void *)hc->pci_membase);
+	if (hc->plx_membase)
+		iounmap((void *)hc->plx_membase);
+	if (hc->pci_iobase)
+		release_region(hc->pci_iobase, 8);
+
+	if (hc->pci_dev) {
+		pci_disable_device(hc->pci_dev);
+		pci_set_drvdata(hc->pci_dev, NULL);
+	}
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: done\n", __func__);
+}
+
+/*
+ * function called to reset the HFC chip. A complete software reset of chip
+ * and fifos is done. All configuration of the chip is done.
+ */
+
+static int
+init_chip(struct hfc_multi *hc)
+{
+	u_long			flags, val, val2 = 0, rev;
+	int			i, err = 0;
+	u_char			r_conf_en, rval;
+	u_int			*plx_acc_32, pv;
+	u_long			plx_flags, hfc_flags;
+	int			plx_count;
+	struct hfc_multi	*pos, *next, *plx_last_hc;
+
+	spin_lock_irqsave(&hc->lock, flags);
+	/* reset all registers */
+	memset(&hc->hw, 0, sizeof(struct hfcm_hw));
+
+	/* revision check */
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: entered\n", __func__);
+	val = HFC_inb(hc, R_CHIP_ID)>>4;
+	if (val != 0x8 && val != 0xc && val != 0xe) {
+		printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val);
+		err = -EIO;
+		goto out;
+	}
+	rev = HFC_inb(hc, R_CHIP_RV);
+	printk(KERN_INFO
+	    "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n",
+	    val, rev, (rev == 0) ? " (old FIFO handling)" : "");
+	if (rev == 0) {
+		test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip);
+		printk(KERN_WARNING
+		    "HFC_multi: NOTE: Your chip is revision 0, "
+		    "ask Cologne Chip for update. Newer chips "
+		    "have a better FIFO handling. Old chips "
+		    "still work but may have slightly lower "
+		    "HDLC transmit performance.\n");
+	}
+	if (rev > 1) {
+		printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't "
+		    "consider chip revision = %ld. The chip / "
+		    "bridge may not work.\n", rev);
+	}
+
+	/* set s-ram size */
+	hc->Flen = 0x10;
+	hc->Zmin = 0x80;
+	hc->Zlen = 384;
+	hc->DTMFbase = 0x1000;
+	if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n",
+			    __func__);
+		hc->hw.r_ctrl |= V_EXT_RAM;
+		hc->hw.r_ram_sz = 1;
+		hc->Flen = 0x20;
+		hc->Zmin = 0xc0;
+		hc->Zlen = 1856;
+		hc->DTMFbase = 0x2000;
+	}
+	if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n",
+			    __func__);
+		hc->hw.r_ctrl |= V_EXT_RAM;
+		hc->hw.r_ram_sz = 2;
+		hc->Flen = 0x20;
+		hc->Zmin = 0xc0;
+		hc->Zlen = 8000;
+		hc->DTMFbase = 0x2000;
+	}
+	hc->max_trans = poll << 1;
+	if (hc->max_trans > hc->Zlen)
+		hc->max_trans = hc->Zlen;
+
+	/* Speech Design PLX bridge */
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+		if (debug & DEBUG_HFCMULTI_PLXSD)
+			printk(KERN_DEBUG "%s: initializing PLXSD card %d\n",
+			    __func__, hc->id + 1);
+		spin_lock_irqsave(&plx_lock, plx_flags);
+		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		writel(PLX_GPIOC_INIT, plx_acc_32);
+		pv = readl(plx_acc_32);
+		/* The first and the last cards are terminating the PCM bus */
+		pv |= PLX_TERM_ON; /* hc is currently the last */
+		/* Disconnect the PCM */
+		pv |= PLX_SLAVE_EN_N;
+		pv &= ~PLX_MASTER_EN;
+		pv &= ~PLX_SYNC_O_EN;
+		/* Put the DSP in Reset */
+		pv &= ~PLX_DSP_RES_N;
+		writel(pv, plx_acc_32);
+		spin_unlock_irqrestore(&plx_lock, plx_flags);
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_WARNING "%s: slave/term: PLX_GPIO=%x\n",
+				__func__, pv);
+		/*
+		 * If we are the 3rd PLXSD card or higher, we must turn
+		 * termination of last PLXSD card off.
+		 */
+		spin_lock_irqsave(&HFClock, hfc_flags);
+		plx_count = 0;
+		plx_last_hc = NULL;
+		list_for_each_entry_safe(pos, next, &HFClist, list) {
+			if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) {
+				plx_count++;
+				if (pos != hc)
+					plx_last_hc = pos;
+			}
+		}
+		if (plx_count >= 3) {
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_DEBUG "%s: card %d is between, so "
+					"we disable termination\n",
+				    __func__, plx_last_hc->id + 1);
+			spin_lock_irqsave(&plx_lock, plx_flags);
+			plx_acc_32 = (u_int *)(plx_last_hc->plx_membase
+					+ PLX_GPIOC);
+			pv = readl(plx_acc_32);
+			pv &= ~PLX_TERM_ON;
+			writel(pv, plx_acc_32);
+			spin_unlock_irqrestore(&plx_lock, plx_flags);
+			if (debug & DEBUG_HFCMULTI_INIT)
+			    printk(KERN_WARNING "%s: term off: PLX_GPIO=%x\n",
+					__func__, pv);
+		}
+		spin_unlock_irqrestore(&HFClock, hfc_flags);
+		hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */
+	}
+
+	/* we only want the real Z2 read-pointer for revision > 0 */
+	if (!test_bit(HFC_CHIP_REVISION0, &hc->chip))
+		hc->hw.r_ram_sz |= V_FZ_MD;
+
+	/* select pcm mode */
+	if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: setting PCM into slave mode\n",
+			    __func__);
+	} else
+	if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: setting PCM into master mode\n",
+			    __func__);
+		hc->hw.r_pcm_md0 |= V_PCM_MD;
+	} else {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: performing PCM auto detect\n",
+			    __func__);
+	}
+
+	/* soft reset */
+	HFC_outb(hc, R_CTRL, hc->hw.r_ctrl);
+	HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
+	HFC_outb(hc, R_FIFO_MD, 0);
+	hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR;
+	HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+	udelay(100);
+	hc->hw.r_cirm = 0;
+	HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+	udelay(100);
+	HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
+
+	/* Speech Design PLX bridge pcm and sync mode */
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+		spin_lock_irqsave(&plx_lock, plx_flags);
+		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		pv = readl(plx_acc_32);
+		/* Connect PCM */
+		if (hc->hw.r_pcm_md0 & V_PCM_MD) {
+			pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+			pv |= PLX_SYNC_O_EN;
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_WARNING "%s: master: PLX_GPIO=%x\n",
+					__func__, pv);
+		} else {
+			pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N);
+			pv &= ~PLX_SYNC_O_EN;
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_WARNING "%s: slave: PLX_GPIO=%x\n",
+					__func__, pv);
+		}
+		writel(pv, plx_acc_32);
+		spin_unlock_irqrestore(&plx_lock, plx_flags);
+	}
+
+	/* PCM setup */
+	HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90);
+	if (hc->slots == 32)
+		HFC_outb(hc, R_PCM_MD1, 0x00);
+	if (hc->slots == 64)
+		HFC_outb(hc, R_PCM_MD1, 0x10);
+	if (hc->slots == 128)
+		HFC_outb(hc, R_PCM_MD1, 0x20);
+	HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0);
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+		HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */
+	else
+		HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */
+	HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+	for (i = 0; i < 256; i++) {
+		HFC_outb_nodebug(hc, R_SLOT, i);
+		HFC_outb_nodebug(hc, A_SL_CFG, 0);
+		HFC_outb_nodebug(hc, A_CONF, 0);
+		hc->slot_owner[i] = -1;
+	}
+
+	/* set clock speed */
+	if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: setting double clock\n", __func__);
+		HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+	}
+
+	/* B410P GPIO */
+	if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+		printk(KERN_NOTICE "Setting GPIOs\n");
+		HFC_outb(hc, R_GPIO_SEL, 0x30);
+		HFC_outb(hc, R_GPIO_EN1, 0x3);
+		udelay(1000);
+		printk(KERN_NOTICE "calling vpm_init\n");
+		vpm_init(hc);
+	}
+
+	/* check if R_F0_CNT counts (8 kHz frame count) */
+	val = HFC_inb(hc, R_F0_CNTL);
+	val += HFC_inb(hc, R_F0_CNTH) << 8;
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG
+		    "HFC_multi F0_CNT %ld after reset\n", val);
+	spin_unlock_irqrestore(&hc->lock, flags);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((HZ/100)?:1); /* Timeout minimum 10ms */
+	spin_lock_irqsave(&hc->lock, flags);
+	val2 = HFC_inb(hc, R_F0_CNTL);
+	val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG
+			"HFC_multi F0_CNT %ld after 10 ms (1st try)\n",
+		    val2);
+	if (val2 >= val+8) { /* 1 ms */
+		/* it counts, so we keep the pcm mode */
+		if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+			printk(KERN_INFO "controller is PCM bus MASTER\n");
+		else
+		if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip))
+			printk(KERN_INFO "controller is PCM bus SLAVE\n");
+		else {
+			test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+			printk(KERN_INFO "controller is PCM bus SLAVE "
+				"(auto detected)\n");
+		}
+	} else {
+		/* does not count */
+		if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+controller_fail:
+			printk(KERN_ERR "HFC_multi ERROR, getting no 125us "
+			    "pulse. Seems that controller fails.\n");
+			err = -EIO;
+			goto out;
+		}
+		if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+			printk(KERN_INFO "controller is PCM bus SLAVE "
+				"(ignoring missing PCM clock)\n");
+		} else {
+			/* only one pcm master */
+			if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+				&& plxsd_master) {
+				printk(KERN_ERR "HFC_multi ERROR, no clock "
+				    "on another Speech Design card found. "
+				    "Please be sure to connect PCM cable.\n");
+				err = -EIO;
+				goto out;
+			}
+			/* retry with master clock */
+			if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+				spin_lock_irqsave(&plx_lock, plx_flags);
+				plx_acc_32 = (u_int *)(hc->plx_membase +
+					PLX_GPIOC);
+				pv = readl(plx_acc_32);
+				pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+				pv |= PLX_SYNC_O_EN;
+				writel(pv, plx_acc_32);
+				spin_unlock_irqrestore(&plx_lock, plx_flags);
+				if (debug & DEBUG_HFCMULTI_INIT)
+				    printk(KERN_WARNING "%s: master: PLX_GPIO"
+					"=%x\n", __func__, pv);
+			}
+			hc->hw.r_pcm_md0 |= V_PCM_MD;
+			HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+			spin_unlock_irqrestore(&hc->lock, flags);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout((HZ/100)?:1); /* Timeout min. 10ms */
+			spin_lock_irqsave(&hc->lock, flags);
+			val2 = HFC_inb(hc, R_F0_CNTL);
+			val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG "HFC_multi F0_CNT %ld after "
+					"10 ms (2nd try)\n", val2);
+			if (val2 >= val+8) { /* 1 ms */
+				test_and_set_bit(HFC_CHIP_PCM_MASTER,
+					&hc->chip);
+				printk(KERN_INFO "controller is PCM bus MASTER "
+					"(auto detected)\n");
+			} else
+				goto controller_fail;
+		}
+	}
+
+	/* Release the DSP Reset */
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+		if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+			plxsd_master = 1;
+		spin_lock_irqsave(&plx_lock, plx_flags);
+		plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+		pv = readl(plx_acc_32);
+		pv |=  PLX_DSP_RES_N;
+		writel(pv, plx_acc_32);
+		spin_unlock_irqrestore(&plx_lock, plx_flags);
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_WARNING "%s: reset off: PLX_GPIO=%x\n",
+				__func__, pv);
+	}
+
+	/* pcm id */
+	if (hc->pcm)
+		printk(KERN_INFO "controller has given PCM BUS ID %d\n",
+			hc->pcm);
+	else {
+		if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)
+		 || test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+			PCM_cnt++; /* SD has proprietary bridging */
+		}
+		hc->pcm = PCM_cnt;
+		printk(KERN_INFO "controller has PCM BUS ID %d "
+			"(auto selected)\n", hc->pcm);
+	}
+
+	/* set up timer */
+	HFC_outb(hc, R_TI_WD, poll_timer);
+	hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
+
+	/*
+	 * set up 125us interrupt, only if function pointer is available
+	 * and module parameter timer is set
+	 */
+	if (timer && hfc_interrupt && register_interrupt) {
+		/* only one chip should use this interrupt */
+		timer = 0;
+		interrupt_registered = 1;
+		hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK;
+		/* deactivate other interrupts in ztdummy */
+		register_interrupt();
+	}
+
+	/* set E1 state machine IRQ */
+	if (hc->type == 1)
+		hc->hw.r_irqmsk_misc |= V_STA_IRQMSK;
+
+	/* set DTMF detection */
+	if (test_bit(HFC_CHIP_DTMF, &hc->chip)) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: enabling DTMF detection "
+			    "for all B-channel\n", __func__);
+		hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP;
+		if (test_bit(HFC_CHIP_ULAW, &hc->chip))
+			hc->hw.r_dtmf |= V_ULAW_SEL;
+		HFC_outb(hc, R_DTMF_N, 102 - 1);
+		hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK;
+	}
+
+	/* conference engine */
+	if (test_bit(HFC_CHIP_ULAW, &hc->chip))
+		r_conf_en = V_CONF_EN | V_ULAW;
+	else
+		r_conf_en = V_CONF_EN;
+	HFC_outb(hc, R_CONF_EN, r_conf_en);
+
+	/* setting leds */
+	switch (hc->leds) {
+	case 1: /* HFC-E1 OEM */
+		if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+			HFC_outb(hc, R_GPIO_SEL, 0x32);
+		else
+			HFC_outb(hc, R_GPIO_SEL, 0x30);
+
+		HFC_outb(hc, R_GPIO_EN1, 0x0f);
+		HFC_outb(hc, R_GPIO_OUT1, 0x00);
+
+		HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+		break;
+
+	case 2: /* HFC-4S OEM */
+	case 3:
+		HFC_outb(hc, R_GPIO_SEL, 0xf0);
+		HFC_outb(hc, R_GPIO_EN1, 0xff);
+		HFC_outb(hc, R_GPIO_OUT1, 0x00);
+		break;
+	}
+
+	/* set master clock */
+	if (hc->masterclk >= 0) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: setting ST master clock "
+			    "to port %d (0..%d)\n",
+			    __func__, hc->masterclk, hc->ports-1);
+		hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC;
+		HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync);
+	}
+
+	/* setting misc irq */
+	HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc);
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n",
+		    hc->hw.r_irqmsk_misc);
+
+	/* RAM access test */
+	HFC_outb(hc, R_RAM_ADDR0, 0);
+	HFC_outb(hc, R_RAM_ADDR1, 0);
+	HFC_outb(hc, R_RAM_ADDR2, 0);
+	for (i = 0; i < 256; i++) {
+		HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+		HFC_outb_nodebug(hc, R_RAM_DATA, ((i*3)&0xff));
+	}
+	for (i = 0; i < 256; i++) {
+		HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+		HFC_inb_nodebug(hc, R_RAM_DATA);
+		rval = HFC_inb_nodebug(hc, R_INT_DATA);
+		if (rval != ((i * 3) & 0xff)) {
+			printk(KERN_DEBUG
+			    "addr:%x val:%x should:%x\n", i, rval,
+			    (i * 3) & 0xff);
+			err++;
+		}
+	}
+	if (err) {
+		printk(KERN_DEBUG "aborting - %d RAM access errors\n", err);
+		err = -EIO;
+		goto out;
+	}
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: done\n", __func__);
+out:
+	spin_unlock_irqrestore(&hc->lock, flags);
+	return err;
+}
+
+
+/*
+ * control the watchdog
+ */
+static void
+hfcmulti_watchdog(struct hfc_multi *hc)
+{
+	hc->wdcount++;
+
+	if (hc->wdcount > 10) {
+		hc->wdcount = 0;
+		hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ?
+		    V_GPIO_OUT3 : V_GPIO_OUT2;
+
+	/* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */
+		HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+		HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte);
+	}
+}
+
+
+
+/*
+ * output leds
+ */
+static void
+hfcmulti_leds(struct hfc_multi *hc)
+{
+	unsigned long lled;
+	unsigned long leddw;
+	int i, state, active, leds;
+	struct dchannel *dch;
+	int led[4];
+
+	hc->ledcount += poll;
+	if (hc->ledcount > 4096) {
+		hc->ledcount -= 4096;
+		hc->ledstate = 0xAFFEAFFE;
+	}
+
+	switch (hc->leds) {
+	case 1: /* HFC-E1 OEM */
+		/* 2 red blinking: NT mode deactivate
+		 * 2 red steady:   TE mode deactivate
+		 * left green:     L1 active
+		 * left red:       frame sync, but no L1
+		 * right green:    L2 active
+		 */
+		if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */
+			if (hc->chan[hc->dslot].dch->dev.D.protocol
+				!= ISDN_P_NT_E1) {
+				led[0] = 1;
+				led[1] = 1;
+			} else if (hc->ledcount>>11) {
+				led[0] = 1;
+				led[1] = 1;
+			} else {
+				led[0] = 0;
+				led[1] = 0;
+			}
+			led[2] = 0;
+			led[3] = 0;
+		} else { /* with frame sync */
+			/* TODO make it work */
+			led[0] = 0;
+			led[1] = 0;
+			led[2] = 0;
+			led[3] = 1;
+		}
+		leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF;
+			/* leds are inverted */
+		if (leds != (int)hc->ledstate) {
+			HFC_outb_nodebug(hc, R_GPIO_OUT1, leds);
+			hc->ledstate = leds;
+		}
+		break;
+
+	case 2: /* HFC-4S OEM */
+		/* red blinking = PH_DEACTIVATE NT Mode
+		 * red steady   = PH_DEACTIVATE TE Mode
+		 * green steady = PH_ACTIVATE
+		 */
+		for (i = 0; i < 4; i++) {
+			state = 0;
+			active = -1;
+			dch = hc->chan[(i << 2) | 2].dch;
+			if (dch) {
+				state = dch->state;
+				if (dch->dev.D.protocol == ISDN_P_NT_S0)
+					active = 3;
+				else
+					active = 7;
+			}
+			if (state) {
+				if (state == active) {
+					led[i] = 1; /* led green */
+				} else
+					if (dch->dev.D.protocol == ISDN_P_TE_S0)
+						/* TE mode: led red */
+						led[i] = 2;
+					else
+						if (hc->ledcount>>11)
+							/* led red */
+							led[i] = 2;
+						else
+							/* led off */
+							led[i] = 0;
+			} else
+				led[i] = 0; /* led off */
+		}
+		if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+			leds = 0;
+			for (i = 0; i < 4; i++) {
+				if (led[i] == 1) {
+					/*green*/
+					leds |= (0x2 << (i * 2));
+				} else if (led[i] == 2) {
+					/*red*/
+					leds |= (0x1 << (i * 2));
+				}
+			}
+			if (leds != (int)hc->ledstate) {
+				vpm_out(hc, 0, 0x1a8 + 3, leds);
+				hc->ledstate = leds;
+			}
+		} else {
+			leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) |
+			    ((led[0] > 0) << 2) | ((led[2] > 0) << 3) |
+			    ((led[3] & 1) << 4) | ((led[1] & 1) << 5) |
+			    ((led[0] & 1) << 6) | ((led[2] & 1) << 7);
+			if (leds != (int)hc->ledstate) {
+				HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F);
+				HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4);
+				hc->ledstate = leds;
+			}
+		}
+		break;
+
+	case 3: /* HFC 1S/2S Beronet */
+		/* red blinking = PH_DEACTIVATE NT Mode
+		 * red steady   = PH_DEACTIVATE TE Mode
+		 * green steady = PH_ACTIVATE
+		 */
+		for (i = 0; i < 2; i++) {
+			state = 0;
+			active = -1;
+			dch = hc->chan[(i << 2) | 2].dch;
+			if (dch) {
+				state = dch->state;
+				if (dch->dev.D.protocol == ISDN_P_NT_S0)
+					active = 3;
+				else
+					active = 7;
+			}
+			if (state) {
+				if (state == active) {
+					led[i] = 1; /* led green */
+				} else
+					if (dch->dev.D.protocol == ISDN_P_TE_S0)
+						/* TE mode: led red */
+						led[i] = 2;
+					else
+						if (hc->ledcount >> 11)
+							/* led red */
+							led[i] = 2;
+						else
+							/* led off */
+							led[i] = 0;
+			} else
+				led[i] = 0; /* led off */
+		}
+
+
+		leds = (led[0] > 0) | ((led[1] > 0)<<1) | ((led[0]&1)<<2)
+			| ((led[1]&1)<<3);
+		if (leds != (int)hc->ledstate) {
+			HFC_outb_nodebug(hc, R_GPIO_EN1,
+			    ((led[0] > 0) << 2) | ((led[1] > 0) << 3));
+			HFC_outb_nodebug(hc, R_GPIO_OUT1,
+			    ((led[0] & 1) << 2) | ((led[1] & 1) << 3));
+			hc->ledstate = leds;
+		}
+		break;
+	case 8: /* HFC 8S+ Beronet */
+		lled = 0;
+
+		for (i = 0; i < 8; i++) {
+			state = 0;
+			active = -1;
+			dch = hc->chan[(i << 2) | 2].dch;
+			if (dch) {
+				state = dch->state;
+				if (dch->dev.D.protocol == ISDN_P_NT_S0)
+					active = 3;
+				else
+					active = 7;
+			}
+			if (state) {
+				if (state == active) {
+					lled |= 0 << i;
+				} else
+					if (hc->ledcount >> 11)
+						lled |= 0 << i;
+					else
+						lled |= 1 << i;
+			} else
+				lled |= 1 << i;
+		}
+		leddw = lled << 24 | lled << 16 | lled << 8 | lled;
+		if (leddw != hc->ledstate) {
+			/* HFC_outb(hc, R_BRG_PCM_CFG, 1);
+			HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */
+			/* was _io before */
+			HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+			outw(0x4000, hc->pci_iobase + 4);
+			outl(leddw, hc->pci_iobase);
+			HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+			hc->ledstate = leddw;
+		}
+		break;
+	}
+}
+/*
+ * read dtmf coefficients
+ */
+
+static void
+hfcmulti_dtmf(struct hfc_multi *hc)
+{
+	s32		*coeff;
+	u_int		mantissa;
+	int		co, ch;
+	struct bchannel	*bch = NULL;
+	u8		exponent;
+	int		dtmf = 0;
+	int		addr;
+	u16		w_float;
+	struct sk_buff	*skb;
+	struct mISDNhead *hh;
+
+	if (debug & DEBUG_HFCMULTI_DTMF)
+		printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__);
+	for (ch = 0; ch <= 31; ch++) {
+		/* only process enabled B-channels */
+		bch = hc->chan[ch].bch;
+		if (!bch)
+			continue;
+		if (!hc->created[hc->chan[ch].port])
+			continue;
+		if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+			continue;
+		if (debug & DEBUG_HFCMULTI_DTMF)
+			printk(KERN_DEBUG "%s: dtmf channel %d:",
+				__func__, ch);
+		coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]);
+		dtmf = 1;
+		for (co = 0; co < 8; co++) {
+			/* read W(n-1) coefficient */
+			addr = hc->DTMFbase + ((co<<7) | (ch<<2));
+			HFC_outb_nodebug(hc, R_RAM_ADDR0, addr);
+			HFC_outb_nodebug(hc, R_RAM_ADDR1, addr>>8);
+			HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr>>16)
+				| V_ADDR_INC);
+			w_float = HFC_inb_nodebug(hc, R_RAM_DATA);
+			w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+			if (debug & DEBUG_HFCMULTI_DTMF)
+				printk(" %04x", w_float);
+
+			/* decode float (see chip doc) */
+			mantissa = w_float & 0x0fff;
+			if (w_float & 0x8000)
+				mantissa |= 0xfffff000;
+			exponent = (w_float>>12) & 0x7;
+			if (exponent) {
+				mantissa ^= 0x1000;
+				mantissa <<= (exponent-1);
+			}
+
+			/* store coefficient */
+			coeff[co<<1] = mantissa;
+
+			/* read W(n) coefficient */
+			w_float = HFC_inb_nodebug(hc, R_RAM_DATA);
+			w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+			if (debug & DEBUG_HFCMULTI_DTMF)
+				printk(" %04x", w_float);
+
+			/* decode float (see chip doc) */
+			mantissa = w_float & 0x0fff;
+			if (w_float & 0x8000)
+				mantissa |= 0xfffff000;
+			exponent = (w_float>>12) & 0x7;
+			if (exponent) {
+				mantissa ^= 0x1000;
+				mantissa <<= (exponent-1);
+			}
+
+			/* store coefficient */
+			coeff[(co<<1)|1] = mantissa;
+		}
+		if (debug & DEBUG_HFCMULTI_DTMF)
+			printk("%s: DTMF ready %08x %08x %08x %08x "
+			    "%08x %08x %08x %08x\n", __func__,
+			    coeff[0], coeff[1], coeff[2], coeff[3],
+			    coeff[4], coeff[5], coeff[6], coeff[7]);
+		hc->chan[ch].coeff_count++;
+		if (hc->chan[ch].coeff_count == 8) {
+			hc->chan[ch].coeff_count = 0;
+			skb = mI_alloc_skb(512, GFP_ATOMIC);
+			if (!skb) {
+				printk(KERN_WARNING "%s: No memory for skb\n",
+				    __func__);
+				continue;
+			}
+			hh = mISDN_HEAD_P(skb);
+			hh->prim = PH_CONTROL_IND;
+			hh->id = DTMF_HFC_COEF;
+			memcpy(skb_put(skb, 512), hc->chan[ch].coeff, 512);
+			recv_Bchannel_skb(bch, skb);
+		}
+	}
+
+	/* restart DTMF processing */
+	hc->dtmf = dtmf;
+	if (dtmf)
+		HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
+}
+
+
+/*
+ * fill fifo as much as possible
+ */
+
+static void
+hfcmulti_tx(struct hfc_multi *hc, int ch)
+{
+	int i, ii, temp, len = 0;
+	int Zspace, z1, z2; /* must be int for calculation */
+	int Fspace, f1, f2;
+	u_char *d;
+	int *txpending, slot_tx;
+	struct	bchannel *bch;
+	struct  dchannel *dch;
+	struct  sk_buff **sp = NULL;
+	int *idxp;
+
+	bch = hc->chan[ch].bch;
+	dch = hc->chan[ch].dch;
+	if ((!dch) && (!bch))
+		return;
+
+	txpending = &hc->chan[ch].txpending;
+	slot_tx = hc->chan[ch].slot_tx;
+	if (dch) {
+		if (!test_bit(FLG_ACTIVE, &dch->Flags))
+			return;
+		sp = &dch->tx_skb;
+		idxp = &dch->tx_idx;
+	} else {
+		if (!test_bit(FLG_ACTIVE, &bch->Flags))
+			return;
+		sp = &bch->tx_skb;
+		idxp = &bch->tx_idx;
+	}
+	if (*sp)
+		len = (*sp)->len;
+
+	if ((!len) && *txpending != 1)
+		return; /* no data */
+
+	if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+	    (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+	    (hc->chan[ch].slot_rx < 0) &&
+	    (hc->chan[ch].slot_tx < 0))
+		HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1));
+	else
+		HFC_outb_nodebug(hc, R_FIFO, ch << 1);
+	HFC_wait_nodebug(hc);
+
+	if (*txpending == 2) {
+		/* reset fifo */
+		HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+		HFC_wait_nodebug(hc);
+		HFC_outb(hc, A_SUBCH_CFG, 0);
+		*txpending = 1;
+	}
+next_frame:
+	if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+		f1 = HFC_inb_nodebug(hc, A_F1);
+		f2 = HFC_inb_nodebug(hc, A_F2);
+		while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) {
+			if (debug & DEBUG_HFCMULTI_FIFO)
+				printk(KERN_DEBUG
+				    "%s(card %d): reread f2 because %d!=%d\n",
+				    __func__, hc->id + 1, temp, f2);
+			f2 = temp; /* repeat until F2 is equal */
+		}
+		Fspace = f2 - f1 - 1;
+		if (Fspace < 0)
+			Fspace += hc->Flen;
+		/*
+		 * Old FIFO handling doesn't give us the current Z2 read
+		 * pointer, so we cannot send the next frame before the fifo
+		 * is empty. It makes no difference except for a slightly
+		 * lower performance.
+		 */
+		if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) {
+			if (f1 != f2)
+				Fspace = 0;
+			else
+				Fspace = 1;
+		}
+		/* one frame only for ST D-channels, to allow resending */
+		if (hc->type != 1 && dch) {
+			if (f1 != f2)
+				Fspace = 0;
+		}
+		/* F-counter full condition */
+		if (Fspace == 0)
+			return;
+	}
+	z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin;
+	z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+	while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) {
+		if (debug & DEBUG_HFCMULTI_FIFO)
+			printk(KERN_DEBUG "%s(card %d): reread z2 because "
+				"%d!=%d\n", __func__, hc->id + 1, temp, z2);
+		z2 = temp; /* repeat unti Z2 is equal */
+	}
+	Zspace = z2 - z1;
+	if (Zspace <= 0)
+		Zspace += hc->Zlen;
+	Zspace -= 4; /* keep not too full, so pointers will not overrun */
+	/* fill transparent data only to maxinum transparent load (minus 4) */
+	if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+		Zspace = Zspace - hc->Zlen + hc->max_trans;
+	if (Zspace <= 0) /* no space of 4 bytes */
+		return;
+
+	/* if no data */
+	if (!len) {
+		if (z1 == z2) { /* empty */
+			/* if done with FIFO audio data during PCM connection */
+			if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) &&
+			    *txpending && slot_tx >= 0) {
+				if (debug & DEBUG_HFCMULTI_MODE)
+					printk(KERN_DEBUG
+					    "%s: reconnecting PCM due to no "
+					    "more FIFO data: channel %d "
+					    "slot_tx %d\n",
+					    __func__, ch, slot_tx);
+				/* connect slot */
+				HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+				    V_HDLC_TRP | V_IFF);
+				HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+				HFC_wait_nodebug(hc);
+				HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+				    V_HDLC_TRP | V_IFF);
+				HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+				HFC_wait_nodebug(hc);
+			}
+			*txpending = 0;
+		}
+		return; /* no data */
+	}
+
+	/* if audio data and connected slot */
+	if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending)
+		&& slot_tx >= 0) {
+		if (debug & DEBUG_HFCMULTI_MODE)
+			printk(KERN_DEBUG "%s: disconnecting PCM due to "
+			    "FIFO data: channel %d slot_tx %d\n",
+			    __func__, ch, slot_tx);
+		/* disconnect slot */
+		HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+		HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+		HFC_wait_nodebug(hc);
+		HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+		HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+		HFC_wait_nodebug(hc);
+	}
+	*txpending = 1;
+
+	/* show activity */
+	hc->activity[hc->chan[ch].port] = 1;
+
+	/* fill fifo to what we have left */
+	ii = len;
+	if (dch || test_bit(FLG_HDLC, &bch->Flags))
+		temp = 1;
+	else
+		temp = 0;
+	i = *idxp;
+	d = (*sp)->data + i;
+	if (ii - i > Zspace)
+		ii = Zspace + i;
+	if (debug & DEBUG_HFCMULTI_FIFO)
+		printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space "
+		    "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n",
+			__func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i,
+			temp ? "HDLC":"TRANS");
+
+
+	/* Have to prep the audio data */
+	hc->write_fifo(hc, d, ii - i);
+	*idxp = ii;
+
+	/* if not all data has been written */
+	if (ii != len) {
+		/* NOTE: fifo is started by the calling function */
+		return;
+	}
+
+	/* if all data has been written, terminate frame */
+	if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+		/* increment f-counter */
+		HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+		HFC_wait_nodebug(hc);
+	}
+
+	/* send confirm, since get_net_bframe will not do it with trans */
+	if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+		confirm_Bsend(bch);
+
+	/* check for next frame */
+	dev_kfree_skb(*sp);
+	if (bch && get_next_bframe(bch)) { /* hdlc is confirmed here */
+		len = (*sp)->len;
+		goto next_frame;
+	}
+	if (dch && get_next_dframe(dch)) {
+		len = (*sp)->len;
+		goto next_frame;
+	}
+
+	/*
+	 * now we have no more data, so in case of transparent,
+	 * we set the last byte in fifo to 'silence' in case we will get
+	 * no more data at all. this prevents sending an undefined value.
+	 */
+	if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+		HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+}
+
+
+/* NOTE: only called if E1 card is in active state */
+static void
+hfcmulti_rx(struct hfc_multi *hc, int ch)
+{
+	int temp;
+	int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */
+	int f1 = 0, f2 = 0; /* = 0, to make GCC happy */
+	int again = 0;
+	struct	bchannel *bch;
+	struct  dchannel *dch;
+	struct sk_buff	*skb, **sp = NULL;
+	int	maxlen;
+
+	bch = hc->chan[ch].bch;
+	dch = hc->chan[ch].dch;
+	if ((!dch) && (!bch))
+		return;
+	if (dch) {
+		if (!test_bit(FLG_ACTIVE, &dch->Flags))
+			return;
+		sp = &dch->rx_skb;
+		maxlen = dch->maxlen;
+	} else {
+		if (!test_bit(FLG_ACTIVE, &bch->Flags))
+			return;
+		sp = &bch->rx_skb;
+		maxlen = bch->maxlen;
+	}
+next_frame:
+	/* on first AND before getting next valid frame, R_FIFO must be written
+	   to. */
+	if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+	    (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+	    (hc->chan[ch].slot_rx < 0) &&
+	    (hc->chan[ch].slot_tx < 0))
+		HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch<<1) | 1);
+	else
+		HFC_outb_nodebug(hc, R_FIFO, (ch<<1)|1);
+	HFC_wait_nodebug(hc);
+
+	/* ignore if rx is off BUT change fifo (above) to start pending TX */
+	if (hc->chan[ch].rx_off)
+		return;
+
+	if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+		f1 = HFC_inb_nodebug(hc, A_F1);
+		while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) {
+			if (debug & DEBUG_HFCMULTI_FIFO)
+				printk(KERN_DEBUG
+				    "%s(card %d): reread f1 because %d!=%d\n",
+				    __func__, hc->id + 1, temp, f1);
+			f1 = temp; /* repeat until F1 is equal */
+		}
+		f2 = HFC_inb_nodebug(hc, A_F2);
+	}
+	z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin;
+	while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) {
+		if (debug & DEBUG_HFCMULTI_FIFO)
+			printk(KERN_DEBUG "%s(card %d): reread z2 because "
+				"%d!=%d\n", __func__, hc->id + 1, temp, z2);
+		z1 = temp; /* repeat until Z1 is equal */
+	}
+	z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+	Zsize = z1 - z2;
+	if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2)
+		/* complete hdlc frame */
+		Zsize++;
+	if (Zsize < 0)
+		Zsize += hc->Zlen;
+	/* if buffer is empty */
+	if (Zsize <= 0)
+		return;
+
+	if (*sp == NULL) {
+		*sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC);
+		if (*sp == NULL) {
+			printk(KERN_DEBUG "%s: No mem for rx_skb\n",
+			    __func__);
+			return;
+		}
+	}
+	/* show activity */
+	hc->activity[hc->chan[ch].port] = 1;
+
+	/* empty fifo with what we have */
+	if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+		if (debug & DEBUG_HFCMULTI_FIFO)
+			printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d "
+			    "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) "
+			    "got=%d (again %d)\n", __func__, hc->id + 1, ch,
+			    Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE",
+			    f1, f2, Zsize + (*sp)->len, again);
+		/* HDLC */
+		if ((Zsize + (*sp)->len) > (maxlen + 3)) {
+			if (debug & DEBUG_HFCMULTI_FIFO)
+				printk(KERN_DEBUG
+				    "%s(card %d): hdlc-frame too large.\n",
+				    __func__, hc->id + 1);
+			skb_trim(*sp, 0);
+			HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+			HFC_wait_nodebug(hc);
+			return;
+		}
+
+		hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+
+		if (f1 != f2) {
+			/* increment Z2,F2-counter */
+			HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+			HFC_wait_nodebug(hc);
+			/* check size */
+			if ((*sp)->len < 4) {
+				if (debug & DEBUG_HFCMULTI_FIFO)
+					printk(KERN_DEBUG
+					    "%s(card %d): Frame below minimum "
+					    "size\n", __func__, hc->id + 1);
+				skb_trim(*sp, 0);
+				goto next_frame;
+			}
+			/* there is at least one complete frame, check crc */
+			if ((*sp)->data[(*sp)->len - 1]) {
+				if (debug & DEBUG_HFCMULTI_CRC)
+					printk(KERN_DEBUG
+					    "%s: CRC-error\n", __func__);
+				skb_trim(*sp, 0);
+				goto next_frame;
+			}
+			skb_trim(*sp, (*sp)->len - 3);
+			if ((*sp)->len < MISDN_COPY_SIZE) {
+				skb = *sp;
+				*sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+				if (*sp) {
+					memcpy(skb_put(*sp, skb->len),
+					    skb->data, skb->len);
+					skb_trim(skb, 0);
+				} else {
+					printk(KERN_DEBUG "%s: No mem\n",
+					    __func__);
+					*sp = skb;
+					skb = NULL;
+				}
+			} else {
+				skb = NULL;
+			}
+			if (debug & DEBUG_HFCMULTI_FIFO) {
+				printk(KERN_DEBUG "%s(card %d):",
+					__func__, hc->id + 1);
+				temp = 0;
+				while (temp < (*sp)->len)
+					printk(" %02x", (*sp)->data[temp++]);
+				printk("\n");
+			}
+			if (dch)
+				recv_Dchannel(dch);
+			else
+				recv_Bchannel(bch);
+			*sp = skb;
+			again++;
+			goto next_frame;
+		}
+		/* there is an incomplete frame */
+	} else {
+		/* transparent */
+		if (Zsize > skb_tailroom(*sp))
+			Zsize = skb_tailroom(*sp);
+		hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+		if (((*sp)->len) < MISDN_COPY_SIZE) {
+			skb = *sp;
+			*sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+			if (*sp) {
+				memcpy(skb_put(*sp, skb->len),
+				    skb->data, skb->len);
+				skb_trim(skb, 0);
+			} else {
+				printk(KERN_DEBUG "%s: No mem\n", __func__);
+				*sp = skb;
+				skb = NULL;
+			}
+		} else {
+			skb = NULL;
+		}
+		if (debug & DEBUG_HFCMULTI_FIFO)
+			printk(KERN_DEBUG
+			    "%s(card %d): fifo(%d) reading %d bytes "
+			    "(z1=%04x, z2=%04x) TRANS\n",
+				__func__, hc->id + 1, ch, Zsize, z1, z2);
+		/* only bch is transparent */
+		recv_Bchannel(bch);
+		*sp = skb;
+	}
+}
+
+
+/*
+ * Interrupt handler
+ */
+static void
+signal_state_up(struct dchannel *dch, int info, char *msg)
+{
+	struct sk_buff	*skb;
+	int		id, data = info;
+
+	if (debug & DEBUG_HFCMULTI_STATE)
+		printk(KERN_DEBUG "%s: %s\n", __func__, msg);
+
+	id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */
+
+	skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data,
+		GFP_ATOMIC);
+	if (!skb)
+		return;
+	recv_Dchannel_skb(dch, skb);
+}
+
+static inline void
+handle_timer_irq(struct hfc_multi *hc)
+{
+	int		ch, temp;
+	struct dchannel	*dch;
+	u_long		flags;
+
+	/* process queued resync jobs */
+	if (hc->e1_resync) {
+		/* lock, so e1_resync gets not changed */
+		spin_lock_irqsave(&HFClock, flags);
+		if (hc->e1_resync & 1) {
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_DEBUG "Enable SYNC_I\n");
+			HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC);
+			/* disable JATT, if RX_SYNC is set */
+			if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+				HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+		}
+		if (hc->e1_resync & 2) {
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_DEBUG "Enable jatt PLL\n");
+			HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+		}
+		if (hc->e1_resync & 4) {
+			if (debug & DEBUG_HFCMULTI_PLXSD)
+				printk(KERN_DEBUG
+				    "Enable QUARTZ for HFC-E1\n");
+			/* set jatt to quartz */
+			HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC
+				| V_JATT_OFF);
+			/* switch to JATT, in case it is not already */
+			HFC_outb(hc, R_SYNC_OUT, 0);
+		}
+		hc->e1_resync = 0;
+		spin_unlock_irqrestore(&HFClock, flags);
+	}
+
+	if (hc->type != 1 || hc->e1_state == 1)
+		for (ch = 0; ch <= 31; ch++) {
+			if (hc->created[hc->chan[ch].port]) {
+				hfcmulti_tx(hc, ch);
+				/* fifo is started when switching to rx-fifo */
+				hfcmulti_rx(hc, ch);
+				if (hc->chan[ch].dch &&
+				    hc->chan[ch].nt_timer > -1) {
+					dch = hc->chan[ch].dch;
+					if (!(--hc->chan[ch].nt_timer)) {
+						schedule_event(dch,
+						    FLG_PHCHANGE);
+						if (debug &
+						    DEBUG_HFCMULTI_STATE)
+							printk(KERN_DEBUG
+							    "%s: nt_timer at "
+							    "state %x\n",
+							    __func__,
+							    dch->state);
+					}
+				}
+			}
+		}
+	if (hc->type == 1 && hc->created[0]) {
+		dch = hc->chan[hc->dslot].dch;
+		if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) {
+			/* LOS */
+			temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS;
+			if (!temp && hc->chan[hc->dslot].los)
+				signal_state_up(dch, L1_SIGNAL_LOS_ON,
+				    "LOS detected");
+			if (temp && !hc->chan[hc->dslot].los)
+				signal_state_up(dch, L1_SIGNAL_LOS_OFF,
+				    "LOS gone");
+			hc->chan[hc->dslot].los = temp;
+		}
+		if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) {
+			/* AIS */
+			temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS;
+			if (!temp && hc->chan[hc->dslot].ais)
+				signal_state_up(dch, L1_SIGNAL_AIS_ON,
+				    "AIS detected");
+			if (temp && !hc->chan[hc->dslot].ais)
+				signal_state_up(dch, L1_SIGNAL_AIS_OFF,
+				    "AIS gone");
+			hc->chan[hc->dslot].ais = temp;
+		}
+		if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) {
+			/* SLIP */
+			temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX;
+			if (!temp && hc->chan[hc->dslot].slip_rx)
+				signal_state_up(dch, L1_SIGNAL_SLIP_RX,
+				    " bit SLIP detected RX");
+			hc->chan[hc->dslot].slip_rx = temp;
+			temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX;
+			if (!temp && hc->chan[hc->dslot].slip_tx)
+				signal_state_up(dch, L1_SIGNAL_SLIP_TX,
+				    " bit SLIP detected TX");
+			hc->chan[hc->dslot].slip_tx = temp;
+		}
+		if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) {
+			/* RDI */
+			temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A;
+			if (!temp && hc->chan[hc->dslot].rdi)
+				signal_state_up(dch, L1_SIGNAL_RDI_ON,
+				    "RDI detected");
+			if (temp && !hc->chan[hc->dslot].rdi)
+				signal_state_up(dch, L1_SIGNAL_RDI_OFF,
+				    "RDI gone");
+			hc->chan[hc->dslot].rdi = temp;
+		}
+		temp = HFC_inb_nodebug(hc, R_JATT_DIR);
+		switch (hc->chan[hc->dslot].sync) {
+		case 0:
+			if ((temp & 0x60) == 0x60) {
+				if (debug & DEBUG_HFCMULTI_SYNC)
+					printk(KERN_DEBUG
+					    "%s: (id=%d) E1 now "
+					    "in clock sync\n",
+					    __func__, hc->id);
+				HFC_outb(hc, R_RX_OFF,
+				    hc->chan[hc->dslot].jitter | V_RX_INIT);
+				HFC_outb(hc, R_TX_OFF,
+				    hc->chan[hc->dslot].jitter | V_RX_INIT);
+				hc->chan[hc->dslot].sync = 1;
+				goto check_framesync;
+			}
+			break;
+		case 1:
+			if ((temp & 0x60) != 0x60) {
+				if (debug & DEBUG_HFCMULTI_SYNC)
+					printk(KERN_DEBUG
+					    "%s: (id=%d) E1 "
+					    "lost clock sync\n",
+					    __func__, hc->id);
+				hc->chan[hc->dslot].sync = 0;
+				break;
+			}
+check_framesync:
+			temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+			if (temp == 0x27) {
+				if (debug & DEBUG_HFCMULTI_SYNC)
+					printk(KERN_DEBUG
+					    "%s: (id=%d) E1 "
+					    "now in frame sync\n",
+					    __func__, hc->id);
+				hc->chan[hc->dslot].sync = 2;
+			}
+			break;
+		case 2:
+			if ((temp & 0x60) != 0x60) {
+				if (debug & DEBUG_HFCMULTI_SYNC)
+					printk(KERN_DEBUG
+					    "%s: (id=%d) E1 lost "
+					    "clock & frame sync\n",
+					    __func__, hc->id);
+				hc->chan[hc->dslot].sync = 0;
+				break;
+			}
+			temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+			if (temp != 0x27) {
+				if (debug & DEBUG_HFCMULTI_SYNC)
+					printk(KERN_DEBUG
+					    "%s: (id=%d) E1 "
+					    "lost frame sync\n",
+					    __func__, hc->id);
+				hc->chan[hc->dslot].sync = 1;
+			}
+			break;
+		}
+	}
+
+	if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+		hfcmulti_watchdog(hc);
+
+	if (hc->leds)
+		hfcmulti_leds(hc);
+}
+
+static void
+ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech)
+{
+	struct dchannel	*dch;
+	int		ch;
+	int		active;
+	u_char		st_status, temp;
+
+	/* state machine */
+	for (ch = 0; ch <= 31; ch++) {
+		if (hc->chan[ch].dch) {
+			dch = hc->chan[ch].dch;
+			if (r_irq_statech & 1) {
+				HFC_outb_nodebug(hc, R_ST_SEL,
+					hc->chan[ch].port);
+				/* undocumented: delay after R_ST_SEL */
+				udelay(1);
+				/* undocumented: status changes during read */
+				st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE);
+				while (st_status != (temp =
+					HFC_inb_nodebug(hc, A_ST_RD_STATE))) {
+					if (debug & DEBUG_HFCMULTI_STATE)
+						printk(KERN_DEBUG "%s: reread "
+						    "STATE because %d!=%d\n",
+						    __func__, temp,
+						    st_status);
+					st_status = temp; /* repeat */
+				}
+
+				/* Speech Design TE-sync indication */
+				if (test_bit(HFC_CHIP_PLXSD, &hc->chip) &&
+					dch->dev.D.protocol == ISDN_P_TE_S0) {
+					if (st_status & V_FR_SYNC_ST)
+						hc->syncronized |=
+						    (1 << hc->chan[ch].port);
+					else
+						hc->syncronized &=
+						   ~(1 << hc->chan[ch].port);
+				}
+				dch->state = st_status & 0x0f;
+				if (dch->dev.D.protocol == ISDN_P_NT_S0)
+					active = 3;
+				else
+					active = 7;
+				if (dch->state == active) {
+					HFC_outb_nodebug(hc, R_FIFO,
+						(ch << 1) | 1);
+					HFC_wait_nodebug(hc);
+					HFC_outb_nodebug(hc,
+						R_INC_RES_FIFO, V_RES_F);
+					HFC_wait_nodebug(hc);
+					dch->tx_idx = 0;
+				}
+				schedule_event(dch, FLG_PHCHANGE);
+				if (debug & DEBUG_HFCMULTI_STATE)
+					printk(KERN_DEBUG
+					    "%s: S/T newstate %x port %d\n",
+					    __func__, dch->state,
+					    hc->chan[ch].port);
+			}
+			r_irq_statech >>= 1;
+		}
+	}
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+		plxsd_checksync(hc, 0);
+}
+
+static void
+fifo_irq(struct hfc_multi *hc, int block)
+{
+	int	ch, j;
+	struct dchannel	*dch;
+	struct bchannel	*bch;
+	u_char r_irq_fifo_bl;
+
+	r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block);
+	j = 0;
+	while (j < 8) {
+		ch = (block << 2) + (j >> 1);
+		dch = hc->chan[ch].dch;
+		bch = hc->chan[ch].bch;
+		if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) {
+			j += 2;
+			continue;
+		}
+		if (dch && (r_irq_fifo_bl & (1 << j)) &&
+		    test_bit(FLG_ACTIVE, &dch->Flags)) {
+			hfcmulti_tx(hc, ch);
+			/* start fifo */
+			HFC_outb_nodebug(hc, R_FIFO, 0);
+			HFC_wait_nodebug(hc);
+		}
+		if (bch && (r_irq_fifo_bl & (1 << j)) &&
+		    test_bit(FLG_ACTIVE, &bch->Flags)) {
+			hfcmulti_tx(hc, ch);
+			/* start fifo */
+			HFC_outb_nodebug(hc, R_FIFO, 0);
+			HFC_wait_nodebug(hc);
+		}
+		j++;
+		if (dch && (r_irq_fifo_bl & (1 << j)) &&
+		    test_bit(FLG_ACTIVE, &dch->Flags)) {
+			hfcmulti_rx(hc, ch);
+		}
+		if (bch && (r_irq_fifo_bl & (1 << j)) &&
+		    test_bit(FLG_ACTIVE, &bch->Flags)) {
+			hfcmulti_rx(hc, ch);
+		}
+		j++;
+	}
+}
+
+#ifdef IRQ_DEBUG
+int irqsem;
+#endif
+static irqreturn_t
+hfcmulti_interrupt(int intno, void *dev_id)
+{
+#ifdef IRQCOUNT_DEBUG
+	static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0,
+	    iq5 = 0, iq6 = 0, iqcnt = 0;
+#endif
+	static int		count;
+	struct hfc_multi	*hc = dev_id;
+	struct dchannel		*dch;
+	u_char			r_irq_statech, status, r_irq_misc, r_irq_oview;
+	int			i;
+	u_short			*plx_acc, wval;
+	u_char			e1_syncsta, temp;
+	u_long			flags;
+
+	if (!hc) {
+		printk(KERN_ERR "HFC-multi: Spurious interrupt!\n");
+		return IRQ_NONE;
+	}
+
+	spin_lock(&hc->lock);
+
+#ifdef IRQ_DEBUG
+	if (irqsem)
+		printk(KERN_ERR "irq for card %d during irq from "
+		"card %d, this is no bug.\n", hc->id + 1, irqsem);
+	irqsem = hc->id + 1;
+#endif
+
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+		spin_lock_irqsave(&plx_lock, flags);
+		plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR);
+		wval = readw(plx_acc);
+		spin_unlock_irqrestore(&plx_lock, flags);
+		if (!(wval & PLX_INTCSR_LINTI1_STATUS))
+			goto irq_notforus;
+	}
+
+	status = HFC_inb_nodebug(hc, R_STATUS);
+	r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH);
+#ifdef IRQCOUNT_DEBUG
+	if (r_irq_statech)
+		iq1++;
+	if (status & V_DTMF_STA)
+		iq2++;
+	if (status & V_LOST_STA)
+		iq3++;
+	if (status & V_EXT_IRQSTA)
+		iq4++;
+	if (status & V_MISC_IRQSTA)
+		iq5++;
+	if (status & V_FR_IRQSTA)
+		iq6++;
+	if (iqcnt++ > 5000) {
+		printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n",
+		    iq1, iq2, iq3, iq4, iq5, iq6);
+		iqcnt = 0;
+	}
+#endif
+	if (!r_irq_statech &&
+	    !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA |
+	    V_MISC_IRQSTA | V_FR_IRQSTA))) {
+		/* irq is not for us */
+		goto irq_notforus;
+	}
+	hc->irqcnt++;
+	if (r_irq_statech) {
+		if (hc->type != 1)
+			ph_state_irq(hc, r_irq_statech);
+	}
+	if (status & V_EXT_IRQSTA)
+		; /* external IRQ */
+	if (status & V_LOST_STA) {
+		/* LOST IRQ */
+		HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */
+	}
+	if (status & V_MISC_IRQSTA) {
+		/* misc IRQ */
+		r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC);
+		if (r_irq_misc & V_STA_IRQ) {
+			if (hc->type == 1) {
+				/* state machine */
+				dch = hc->chan[hc->dslot].dch;
+				e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA);
+				if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+				 && hc->e1_getclock) {
+					if (e1_syncsta & V_FR_SYNC_E1)
+						hc->syncronized = 1;
+					else
+						hc->syncronized = 0;
+				}
+				/* undocumented: status changes during read */
+				dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA);
+				while (dch->state != (temp =
+					HFC_inb_nodebug(hc, R_E1_RD_STA))) {
+					if (debug & DEBUG_HFCMULTI_STATE)
+						printk(KERN_DEBUG "%s: reread "
+						    "STATE because %d!=%d\n",
+						    __func__, temp,
+						    dch->state);
+					dch->state = temp; /* repeat */
+				}
+				dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA)
+					& 0x7;
+				schedule_event(dch, FLG_PHCHANGE);
+				if (debug & DEBUG_HFCMULTI_STATE)
+					printk(KERN_DEBUG
+					    "%s: E1 (id=%d) newstate %x\n",
+					    __func__, hc->id, dch->state);
+				if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+					plxsd_checksync(hc, 0);
+			}
+		}
+		if (r_irq_misc & V_TI_IRQ)
+			handle_timer_irq(hc);
+
+		if (r_irq_misc & V_DTMF_IRQ) {
+			/* -> DTMF IRQ */
+			hfcmulti_dtmf(hc);
+		}
+		/* TODO: REPLACE !!!! 125 us Interrupts are not acceptable  */
+		if (r_irq_misc & V_IRQ_PROC) {
+			/* IRQ every 125us */
+			count++;
+			/* generate 1kHz signal */
+			if (count == 8) {
+				if (hfc_interrupt)
+					hfc_interrupt();
+				count = 0;
+			}
+		}
+
+	}
+	if (status & V_FR_IRQSTA) {
+		/* FIFO IRQ */
+		r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW);
+		for (i = 0; i < 8; i++) {
+			if (r_irq_oview & (1 << i))
+				fifo_irq(hc, i);
+		}
+	}
+
+#ifdef IRQ_DEBUG
+	irqsem = 0;
+#endif
+	spin_unlock(&hc->lock);
+	return IRQ_HANDLED;
+
+irq_notforus:
+#ifdef IRQ_DEBUG
+	irqsem = 0;
+#endif
+	spin_unlock(&hc->lock);
+	return IRQ_NONE;
+}
+
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+
+static void
+hfcmulti_dbusy_timer(struct hfc_multi *hc)
+{
+}
+
+
+/*
+ * activate/deactivate hardware for selected channels and mode
+ *
+ * configure B-channel with the given protocol
+ * ch eqals to the HFC-channel (0-31)
+ * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31
+ * for S/T, 1-31 for E1)
+ * the hdlc interrupts will be set/unset
+ */
+static int
+mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx,
+    int bank_tx, int slot_rx, int bank_rx)
+{
+	int flow_tx = 0, flow_rx = 0, routing = 0;
+	int oslot_tx, oslot_rx;
+	int conf;
+
+	if (ch < 0 || ch > 31)
+		return EINVAL;
+	oslot_tx = hc->chan[ch].slot_tx;
+	oslot_rx = hc->chan[ch].slot_rx;
+	conf = hc->chan[ch].conf;
+
+	if (debug & DEBUG_HFCMULTI_MODE)
+		printk(KERN_DEBUG
+		    "%s: card %d channel %d protocol %x slot old=%d new=%d "
+		    "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n",
+		    __func__, hc->id, ch, protocol, oslot_tx, slot_tx,
+		    bank_tx, oslot_rx, slot_rx, bank_rx);
+
+	if (oslot_tx >= 0 && slot_tx != oslot_tx) {
+		/* remove from slot */
+		if (debug & DEBUG_HFCMULTI_MODE)
+			printk(KERN_DEBUG "%s: remove from slot %d (TX)\n",
+			    __func__, oslot_tx);
+		if (hc->slot_owner[oslot_tx<<1] == ch) {
+			HFC_outb(hc, R_SLOT, oslot_tx << 1);
+			HFC_outb(hc, A_SL_CFG, 0);
+			HFC_outb(hc, A_CONF, 0);
+			hc->slot_owner[oslot_tx<<1] = -1;
+		} else {
+			if (debug & DEBUG_HFCMULTI_MODE)
+				printk(KERN_DEBUG
+				    "%s: we are not owner of this tx slot "
+				    "anymore, channel %d is.\n",
+				    __func__, hc->slot_owner[oslot_tx<<1]);
+		}
+	}
+
+	if (oslot_rx >= 0 && slot_rx != oslot_rx) {
+		/* remove from slot */
+		if (debug & DEBUG_HFCMULTI_MODE)
+			printk(KERN_DEBUG
+			    "%s: remove from slot %d (RX)\n",
+			    __func__, oslot_rx);
+		if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) {
+			HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR);
+			HFC_outb(hc, A_SL_CFG, 0);
+			hc->slot_owner[(oslot_rx << 1) | 1] = -1;
+		} else {
+			if (debug & DEBUG_HFCMULTI_MODE)
+				printk(KERN_DEBUG
+				    "%s: we are not owner of this rx slot "
+				    "anymore, channel %d is.\n",
+				    __func__,
+				    hc->slot_owner[(oslot_rx << 1) | 1]);
+		}
+	}
+
+	if (slot_tx < 0) {
+		flow_tx = 0x80; /* FIFO->ST */
+		/* disable pcm slot */
+		hc->chan[ch].slot_tx = -1;
+		hc->chan[ch].bank_tx = 0;
+	} else {
+		/* set pcm slot */
+		if (hc->chan[ch].txpending)
+			flow_tx = 0x80; /* FIFO->ST */
+		else
+			flow_tx = 0xc0; /* PCM->ST */
+		/* put on slot */
+		routing = bank_tx ? 0xc0 : 0x80;
+		if (conf >= 0 || bank_tx > 1)
+			routing = 0x40; /* loop */
+		if (debug & DEBUG_HFCMULTI_MODE)
+			printk(KERN_DEBUG "%s: put channel %d to slot %d bank"
+			    " %d flow %02x routing %02x conf %d (TX)\n",
+			    __func__, ch, slot_tx, bank_tx,
+			    flow_tx, routing, conf);
+		HFC_outb(hc, R_SLOT, slot_tx << 1);
+		HFC_outb(hc, A_SL_CFG, (ch<<1) | routing);
+		HFC_outb(hc, A_CONF, (conf < 0) ? 0 : (conf | V_CONF_SL));
+		hc->slot_owner[slot_tx << 1] = ch;
+		hc->chan[ch].slot_tx = slot_tx;
+		hc->chan[ch].bank_tx = bank_tx;
+	}
+	if (slot_rx < 0) {
+		/* disable pcm slot */
+		flow_rx = 0x80; /* ST->FIFO */
+		hc->chan[ch].slot_rx = -1;
+		hc->chan[ch].bank_rx = 0;
+	} else {
+		/* set pcm slot */
+		if (hc->chan[ch].txpending)
+			flow_rx = 0x80; /* ST->FIFO */
+		else
+			flow_rx = 0xc0; /* ST->(FIFO,PCM) */
+		/* put on slot */
+		routing = bank_rx?0x80:0xc0; /* reversed */
+		if (conf >= 0 || bank_rx > 1)
+			routing = 0x40; /* loop */
+		if (debug & DEBUG_HFCMULTI_MODE)
+			printk(KERN_DEBUG "%s: put channel %d to slot %d bank"
+			    " %d flow %02x routing %02x conf %d (RX)\n",
+			    __func__, ch, slot_rx, bank_rx,
+			    flow_rx, routing, conf);
+		HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR);
+		HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing);
+		hc->slot_owner[(slot_rx<<1)|1] = ch;
+		hc->chan[ch].slot_rx = slot_rx;
+		hc->chan[ch].bank_rx = bank_rx;
+	}
+
+	switch (protocol) {
+	case (ISDN_P_NONE):
+		/* disable TX fifo */
+		HFC_outb(hc, R_FIFO, ch << 1);
+		HFC_wait(hc);
+		HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF);
+		HFC_outb(hc, A_SUBCH_CFG, 0);
+		HFC_outb(hc, A_IRQ_MSK, 0);
+		HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+		HFC_wait(hc);
+		/* disable RX fifo */
+		HFC_outb(hc, R_FIFO, (ch<<1)|1);
+		HFC_wait(hc);
+		HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00);
+		HFC_outb(hc, A_SUBCH_CFG, 0);
+		HFC_outb(hc, A_IRQ_MSK, 0);
+		HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+		HFC_wait(hc);
+		if (hc->chan[ch].bch && hc->type != 1) {
+			hc->hw.a_st_ctrl0[hc->chan[ch].port] &=
+			    ((ch & 0x3) == 0)? ~V_B1_EN: ~V_B2_EN;
+			HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+			/* undocumented: delay after R_ST_SEL */
+			udelay(1);
+			HFC_outb(hc, A_ST_CTRL0,
+			    hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+		}
+		if (hc->chan[ch].bch) {
+			test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+			test_and_clear_bit(FLG_TRANSPARENT,
+			    &hc->chan[ch].bch->Flags);
+		}
+		break;
+	case (ISDN_P_B_RAW): /* B-channel */
+
+		if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+		    (hc->chan[ch].slot_rx < 0) &&
+		    (hc->chan[ch].slot_tx < 0)) {
+
+			printk(KERN_DEBUG
+			    "Setting B-channel %d to echo cancelable "
+			    "state on PCM slot %d\n", ch,
+			    ((ch / 4) * 8) + ((ch % 4) * 4) + 1);
+			printk(KERN_DEBUG
+			    "Enabling pass through for channel\n");
+			vpm_out(hc, ch, ((ch / 4) * 8) +
+			    ((ch % 4) * 4) + 1, 0x01);
+			/* rx path */
+			/* S/T -> PCM */
+			HFC_outb(hc, R_FIFO, (ch << 1));
+			HFC_wait(hc);
+			HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+			HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+			    ((ch % 4) * 4) + 1) << 1);
+			HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1));
+
+			/* PCM -> FIFO */
+			HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1);
+			HFC_wait(hc);
+			HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
+			HFC_outb(hc, A_SUBCH_CFG, 0);
+			HFC_outb(hc, A_IRQ_MSK, 0);
+			HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+			HFC_wait(hc);
+			HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) +
+			    ((ch % 4) * 4) + 1) << 1) | 1);
+			HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1);
+
+			/* tx path */
+			/* PCM -> S/T */
+			HFC_outb(hc, R_FIFO, (ch << 1) | 1);
+			HFC_wait(hc);
+			HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+			HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) +
+			    ((ch % 4) * 4)) << 1) | 1);
+			HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1);
+
+			/* FIFO -> PCM */
+			HFC_outb(hc, R_FIFO, 0x20 | (ch << 1));
+			HFC_wait(hc);
+			HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
+			HFC_outb(hc, A_SUBCH_CFG, 0);
+			HFC_outb(hc, A_IRQ_MSK, 0);
+			HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+			HFC_wait(hc);
+			/* tx silence */
+			HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+			HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+			    ((ch % 4) * 4)) << 1);
+			HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1));
+		} else {
+			/* enable TX fifo */
+			HFC_outb(hc, R_FIFO, ch << 1);
+			HFC_wait(hc);
+			HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 |
+			    V_HDLC_TRP | V_IFF);
+			HFC_outb(hc, A_SUBCH_CFG, 0);
+			HFC_outb(hc, A_IRQ_MSK, 0);
+			HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+			HFC_wait(hc);
+			/* tx silence */
+			HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+			/* enable RX fifo */
+			HFC_outb(hc, R_FIFO, (ch<<1)|1);
+			HFC_wait(hc);
+			HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP);
+			HFC_outb(hc, A_SUBCH_CFG, 0);
+			HFC_outb(hc, A_IRQ_MSK, 0);
+			HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+			HFC_wait(hc);
+		}
+		if (hc->type != 1) {
+			hc->hw.a_st_ctrl0[hc->chan[ch].port] |=
+			    ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN;
+			HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+			/* undocumented: delay after R_ST_SEL */
+			udelay(1);
+			HFC_outb(hc, A_ST_CTRL0,
+			    hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+		}
+		if (hc->chan[ch].bch)
+			test_and_set_bit(FLG_TRANSPARENT,
+			    &hc->chan[ch].bch->Flags);
+		break;
+	case (ISDN_P_B_HDLC): /* B-channel */
+	case (ISDN_P_TE_S0): /* D-channel */
+	case (ISDN_P_NT_S0):
+	case (ISDN_P_TE_E1):
+	case (ISDN_P_NT_E1):
+		/* enable TX fifo */
+		HFC_outb(hc, R_FIFO, ch<<1);
+		HFC_wait(hc);
+		if (hc->type == 1 || hc->chan[ch].bch) {
+			/* E1 or B-channel */
+			HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04);
+			HFC_outb(hc, A_SUBCH_CFG, 0);
+		} else {
+			/* D-Channel without HDLC fill flags */
+			HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF);
+			HFC_outb(hc, A_SUBCH_CFG, 2);
+		}
+		HFC_outb(hc, A_IRQ_MSK, V_IRQ);
+		HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+		HFC_wait(hc);
+		/* enable RX fifo */
+		HFC_outb(hc, R_FIFO, (ch<<1)|1);
+		HFC_wait(hc);
+		HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04);
+		if (hc->type == 1 || hc->chan[ch].bch)
+			HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */
+		else
+			HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */
+		HFC_outb(hc, A_IRQ_MSK, V_IRQ);
+		HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+		HFC_wait(hc);
+		if (hc->chan[ch].bch) {
+			test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+			if (hc->type != 1) {
+				hc->hw.a_st_ctrl0[hc->chan[ch].port] |=
+				  ((ch&0x3) == 0) ? V_B1_EN : V_B2_EN;
+				HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+				/* undocumented: delay after R_ST_SEL */
+				udelay(1);
+				HFC_outb(hc, A_ST_CTRL0,
+				  hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+			}
+		}
+		break;
+	default:
+		printk(KERN_DEBUG "%s: protocol not known %x\n",
+		    __func__, protocol);
+		hc->chan[ch].protocol = ISDN_P_NONE;
+		return -ENOPROTOOPT;
+	}
+	hc->chan[ch].protocol = protocol;
+	return 0;
+}
+
+
+/*
+ * connect/disconnect PCM
+ */
+
+static void
+hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx,
+    int slot_rx, int bank_rx)
+{
+	if (slot_rx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) {
+		/* disable PCM */
+		mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0);
+		return;
+	}
+
+	/* enable pcm */
+	mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx,
+		slot_rx, bank_rx);
+}
+
+/*
+ * set/disable conference
+ */
+
+static void
+hfcmulti_conf(struct hfc_multi *hc, int ch, int num)
+{
+	if (num >= 0 && num <= 7)
+		hc->chan[ch].conf = num;
+	else
+		hc->chan[ch].conf = -1;
+	mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx,
+	    hc->chan[ch].bank_tx, hc->chan[ch].slot_rx,
+	    hc->chan[ch].bank_rx);
+}
+
+
+/*
+ * set/disable sample loop
+ */
+
+/* NOTE: this function is experimental and therefore disabled */
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfcm_l1callback(struct dchannel *dch, u_int cmd)
+{
+	struct hfc_multi	*hc = dch->hw;
+	u_long	flags;
+
+	switch (cmd) {
+	case INFO3_P8:
+	case INFO3_P10:
+		break;
+	case HW_RESET_REQ:
+		/* start activation */
+		spin_lock_irqsave(&hc->lock, flags);
+		if (hc->type == 1) {
+			if (debug & DEBUG_HFCMULTI_MSG)
+				printk(KERN_DEBUG
+				    "%s: HW_RESET_REQ no BRI\n",
+				    __func__);
+		} else {
+			HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+			/* undocumented: delay after R_ST_SEL */
+			udelay(1);
+			HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */
+			udelay(6); /* wait at least 5,21us */
+			HFC_outb(hc, A_ST_WR_STATE, 3);
+			HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3));
+				/* activate */
+		}
+		spin_unlock_irqrestore(&hc->lock, flags);
+		l1_event(dch->l1, HW_POWERUP_IND);
+		break;
+	case HW_DEACT_REQ:
+		/* start deactivation */
+		spin_lock_irqsave(&hc->lock, flags);
+		if (hc->type == 1) {
+			if (debug & DEBUG_HFCMULTI_MSG)
+				printk(KERN_DEBUG
+				    "%s: HW_DEACT_REQ no BRI\n",
+				    __func__);
+		} else {
+			HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+			/* undocumented: delay after R_ST_SEL */
+			udelay(1);
+			HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2);
+				/* deactivate */
+			if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+				hc->syncronized &=
+				   ~(1 << hc->chan[dch->slot].port);
+				plxsd_checksync(hc, 0);
+			}
+		}
+		skb_queue_purge(&dch->squeue);
+		if (dch->tx_skb) {
+			dev_kfree_skb(dch->tx_skb);
+			dch->tx_skb = NULL;
+		}
+		dch->tx_idx = 0;
+		if (dch->rx_skb) {
+			dev_kfree_skb(dch->rx_skb);
+			dch->rx_skb = NULL;
+		}
+		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+			del_timer(&dch->timer);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	case HW_POWERUP_REQ:
+		spin_lock_irqsave(&hc->lock, flags);
+		if (hc->type == 1) {
+			if (debug & DEBUG_HFCMULTI_MSG)
+				printk(KERN_DEBUG
+				    "%s: HW_POWERUP_REQ no BRI\n",
+				    __func__);
+		} else {
+			HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+			/* undocumented: delay after R_ST_SEL */
+			udelay(1);
+			HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */
+			udelay(6); /* wait at least 5,21us */
+			HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */
+		}
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	case PH_ACTIVATE_IND:
+		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	case PH_DEACTIVATE_IND:
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	default:
+		if (dch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: unknown command %x\n",
+			    __func__, cmd);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * Layer2 -> Layer 1 Transfer
+ */
+
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct hfc_multi	*hc = dch->hw;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	int			ret = -EINVAL;
+	unsigned int		id;
+	u_long			flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		if (skb->len < 1)
+			break;
+		spin_lock_irqsave(&hc->lock, flags);
+		ret = dchannel_senddata(dch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			hfcmulti_tx(hc, dch->slot);
+			ret = 0;
+			/* start fifo */
+			HFC_outb(hc, R_FIFO, 0);
+			HFC_wait(hc);
+			spin_unlock_irqrestore(&hc->lock, flags);
+			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(&hc->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+			spin_lock_irqsave(&hc->lock, flags);
+			ret = 0;
+			if (debug & DEBUG_HFCMULTI_MSG)
+				printk(KERN_DEBUG
+				    "%s: PH_ACTIVATE port %d (0..%d)\n",
+				    __func__, hc->chan[dch->slot].port,
+				    hc->ports-1);
+			/* start activation */
+			if (hc->type == 1) {
+				ph_state_change(dch);
+				if (debug & DEBUG_HFCMULTI_STATE)
+					printk(KERN_DEBUG
+					    "%s: E1 report state %x \n",
+					    __func__, dch->state);
+			} else {
+				HFC_outb(hc, R_ST_SEL,
+				    hc->chan[dch->slot].port);
+				/* undocumented: delay after R_ST_SEL */
+				udelay(1);
+				HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1);
+				    /* G1 */
+				udelay(6); /* wait at least 5,21us */
+				HFC_outb(hc, A_ST_WR_STATE, 1);
+				HFC_outb(hc, A_ST_WR_STATE, 1 |
+				    (V_ST_ACT*3)); /* activate */
+				dch->state = 1;
+			}
+			spin_unlock_irqrestore(&hc->lock, flags);
+		} else
+			ret = l1_event(dch->l1, hh->prim);
+		break;
+	case PH_DEACTIVATE_REQ:
+		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+		if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+			spin_lock_irqsave(&hc->lock, flags);
+			if (debug & DEBUG_HFCMULTI_MSG)
+				printk(KERN_DEBUG
+				    "%s: PH_DEACTIVATE port %d (0..%d)\n",
+				    __func__, hc->chan[dch->slot].port,
+				    hc->ports-1);
+			/* start deactivation */
+			if (hc->type == 1) {
+				if (debug & DEBUG_HFCMULTI_MSG)
+					printk(KERN_DEBUG
+					    "%s: PH_DEACTIVATE no BRI\n",
+					    __func__);
+			} else {
+				HFC_outb(hc, R_ST_SEL,
+				    hc->chan[dch->slot].port);
+				/* undocumented: delay after R_ST_SEL */
+				udelay(1);
+				HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2);
+				    /* deactivate */
+				dch->state = 1;
+			}
+			skb_queue_purge(&dch->squeue);
+			if (dch->tx_skb) {
+				dev_kfree_skb(dch->tx_skb);
+				dch->tx_skb = NULL;
+			}
+			dch->tx_idx = 0;
+			if (dch->rx_skb) {
+				dev_kfree_skb(dch->rx_skb);
+				dch->rx_skb = NULL;
+			}
+			test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+			if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+				del_timer(&dch->timer);
+#ifdef FIXME
+			if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+				dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+			ret = 0;
+			spin_unlock_irqrestore(&hc->lock, flags);
+		} else
+			ret = l1_event(dch->l1, hh->prim);
+		break;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+	struct hfc_multi	*hc = bch->hw;
+	u_long			flags;
+
+	spin_lock_irqsave(&hc->lock, flags);
+	if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+		dev_kfree_skb(bch->next_skb);
+		bch->next_skb = NULL;
+	}
+	if (bch->tx_skb) {
+		dev_kfree_skb(bch->tx_skb);
+		bch->tx_skb = NULL;
+	}
+	bch->tx_idx = 0;
+	if (bch->rx_skb) {
+		dev_kfree_skb(bch->rx_skb);
+		bch->rx_skb = NULL;
+	}
+	hc->chan[bch->slot].coeff_count = 0;
+	test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+	test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+	hc->chan[bch->slot].rx_off = 0;
+	hc->chan[bch->slot].conf = -1;
+	mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0);
+	spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel		*bch = container_of(ch, struct bchannel, ch);
+	struct hfc_multi	*hc = bch->hw;
+	int			ret = -EINVAL;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	unsigned int		id;
+	u_long			flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		if (!skb->len)
+			break;
+		spin_lock_irqsave(&hc->lock, flags);
+		ret = bchannel_senddata(bch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			hfcmulti_tx(hc, bch->slot);
+			ret = 0;
+			/* start fifo */
+			HFC_outb_nodebug(hc, R_FIFO, 0);
+			HFC_wait_nodebug(hc);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+				spin_unlock_irqrestore(&hc->lock, flags);
+				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+			} else
+				spin_unlock_irqrestore(&hc->lock, flags);
+		} else
+			spin_unlock_irqrestore(&hc->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n",
+				__func__, bch->slot);
+		spin_lock_irqsave(&hc->lock, flags);
+		/* activate B-channel if not already activated */
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) {
+			hc->chan[bch->slot].txpending = 0;
+			ret = mode_hfcmulti(hc, bch->slot,
+				ch->protocol,
+				hc->chan[bch->slot].slot_tx,
+				hc->chan[bch->slot].bank_tx,
+				hc->chan[bch->slot].slot_rx,
+				hc->chan[bch->slot].bank_rx);
+			if (!ret) {
+				if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf
+					&& test_bit(HFC_CHIP_DTMF, &hc->chip)) {
+					/* start decoder */
+					hc->dtmf = 1;
+					if (debug & DEBUG_HFCMULTI_DTMF)
+						printk(KERN_DEBUG
+						    "%s: start dtmf decoder\n",
+							__func__);
+					HFC_outb(hc, R_DTMF, hc->hw.r_dtmf |
+					    V_RST_DTMF);
+				}
+			}
+		} else
+			ret = 0;
+		spin_unlock_irqrestore(&hc->lock, flags);
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+				GFP_KERNEL);
+		break;
+	case PH_CONTROL_REQ:
+		spin_lock_irqsave(&hc->lock, flags);
+		switch (hh->id) {
+		case HFC_SPL_LOOP_ON: /* set sample loop */
+			if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG
+			    "%s: HFC_SPL_LOOP_ON (len = %d)\n",
+			    __func__, skb->len);
+			ret = 0;
+			break;
+		case HFC_SPL_LOOP_OFF: /* set silence */
+			if (debug & DEBUG_HFCMULTI_MSG)
+				printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n",
+				    __func__);
+			ret = 0;
+			break;
+		default:
+			printk(KERN_ERR
+			     "%s: unknown PH_CONTROL_REQ info %x\n",
+			     __func__, hh->id);
+			ret = -EINVAL;
+		}
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	case PH_DEACTIVATE_REQ:
+		deactivate_bchannel(bch); /* locked there */
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+			GFP_KERNEL);
+		ret = 0;
+		break;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+/*
+ * bchannel control function
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int			ret = 0;
+	struct dsp_features	*features =
+		(struct dsp_features *)(*((u_long *)&cq->p1));
+	struct hfc_multi	*hc = bch->hw;
+	int			slot_tx;
+	int			bank_tx;
+	int			slot_rx;
+	int			bank_rx;
+	int			num;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP
+			| MISDN_CTRL_RX_OFF;
+		break;
+	case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */
+		hc->chan[bch->slot].rx_off = !!cq->p1;
+		if (!hc->chan[bch->slot].rx_off) {
+			/* reset fifo on rx on */
+			HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1);
+			HFC_wait_nodebug(hc);
+			HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+			HFC_wait_nodebug(hc);
+		}
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n",
+			    __func__, bch->nr, hc->chan[bch->slot].rx_off);
+		break;
+	case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+			    __func__);
+		/* create confirm */
+		features->hfc_id = hc->id;
+		if (test_bit(HFC_CHIP_DTMF, &hc->chip))
+			features->hfc_dtmf = 1;
+		features->hfc_loops = 0;
+		if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+			features->hfc_echocanhw = 1;
+		} else {
+			features->pcm_id = hc->pcm;
+			features->pcm_slots = hc->slots;
+			features->pcm_banks = 2;
+		}
+		break;
+	case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */
+		slot_tx = cq->p1 & 0xff;
+		bank_tx = cq->p1 >> 8;
+		slot_rx = cq->p2 & 0xff;
+		bank_rx = cq->p2 >> 8;
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG
+			    "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+			    "slot %d bank %d (RX)\n",
+			    __func__, slot_tx, bank_tx,
+			    slot_rx, bank_rx);
+		if (slot_tx < hc->slots && bank_tx <= 2 &&
+		    slot_rx < hc->slots && bank_rx <= 2)
+			hfcmulti_pcm(hc, bch->slot,
+			    slot_tx, bank_tx, slot_rx, bank_rx);
+		else {
+			printk(KERN_WARNING
+			    "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+			    "slot %d bank %d (RX) out of range\n",
+			    __func__, slot_tx, bank_tx,
+			    slot_rx, bank_rx);
+			ret = -EINVAL;
+		}
+		break;
+	case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: HFC_PCM_DISC\n",
+			    __func__);
+		hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0);
+		break;
+	case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */
+		num = cq->p1 & 0xff;
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n",
+			    __func__, num);
+		if (num <= 7)
+			hfcmulti_conf(hc, bch->slot, num);
+		else {
+			printk(KERN_WARNING
+			    "%s: HW_CONF_JOIN conf %d out of range\n",
+			    __func__, num);
+			ret = -EINVAL;
+		}
+		break;
+	case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__);
+		hfcmulti_conf(hc, bch->slot, -1);
+		break;
+	case MISDN_CTRL_HFC_ECHOCAN_ON:
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__);
+		if (test_bit(HFC_CHIP_B410P, &hc->chip))
+			vpm_echocan_on(hc, bch->slot, cq->p1);
+		else
+			ret = -EINVAL;
+		break;
+
+	case MISDN_CTRL_HFC_ECHOCAN_OFF:
+		if (debug & DEBUG_HFCMULTI_MSG)
+			printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n",
+				__func__);
+		if (test_bit(HFC_CHIP_B410P, &hc->chip))
+			vpm_echocan_off(hc, bch->slot);
+		else
+			ret = -EINVAL;
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown Op %x\n",
+		    __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct bchannel		*bch = container_of(ch, struct bchannel, ch);
+	struct hfc_multi	*hc = bch->hw;
+	int			err = -EINVAL;
+	u_long	flags;
+
+	if (bch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: cmd:%x %p\n",
+		    __func__, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags))
+			deactivate_bchannel(bch); /* locked there */
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(THIS_MODULE);
+		err = 0;
+		break;
+	case CONTROL_CHANNEL:
+		spin_lock_irqsave(&hc->lock, flags);
+		err = channel_bctrl(bch, arg);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown prim(%x)\n",
+			__func__, cmd);
+	}
+	return err;
+}
+
+/*
+ * handle D-channel events
+ *
+ * handle state change event
+ */
+static void
+ph_state_change(struct dchannel *dch)
+{
+	struct hfc_multi *hc = dch->hw;
+	int ch, i;
+
+	if (!dch) {
+		printk(KERN_WARNING "%s: ERROR given dch is NULL\n",
+		    __func__);
+		return;
+	}
+	ch = dch->slot;
+
+	if (hc->type == 1) {
+		if (dch->dev.D.protocol == ISDN_P_TE_E1) {
+			if (debug & DEBUG_HFCMULTI_STATE)
+				printk(KERN_DEBUG
+				    "%s: E1 TE (id=%d) newstate %x\n",
+				    __func__, hc->id, dch->state);
+		} else {
+			if (debug & DEBUG_HFCMULTI_STATE)
+				printk(KERN_DEBUG
+				    "%s: E1 NT (id=%d) newstate %x\n",
+				    __func__, hc->id, dch->state);
+		}
+		switch (dch->state) {
+		case (1):
+			if (hc->e1_state != 1) {
+			    for (i = 1; i <= 31; i++) {
+				/* reset fifos on e1 activation */
+				HFC_outb_nodebug(hc, R_FIFO, (i << 1) | 1);
+				HFC_wait_nodebug(hc);
+				HFC_outb_nodebug(hc,
+					R_INC_RES_FIFO, V_RES_F);
+				HFC_wait_nodebug(hc);
+			    }
+			}
+			test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+			_queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+			    MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+			break;
+
+		default:
+			if (hc->e1_state != 1)
+				return;
+			test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+			_queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+			    MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+		}
+		hc->e1_state = dch->state;
+	} else {
+		if (dch->dev.D.protocol == ISDN_P_TE_S0) {
+			if (debug & DEBUG_HFCMULTI_STATE)
+				printk(KERN_DEBUG
+				    "%s: S/T TE newstate %x\n",
+				    __func__, dch->state);
+			switch (dch->state) {
+			case (0):
+				l1_event(dch->l1, HW_RESET_IND);
+				break;
+			case (3):
+				l1_event(dch->l1, HW_DEACT_IND);
+				break;
+			case (5):
+			case (8):
+				l1_event(dch->l1, ANYSIGNAL);
+				break;
+			case (6):
+				l1_event(dch->l1, INFO2);
+				break;
+			case (7):
+				l1_event(dch->l1, INFO4_P8);
+				break;
+			}
+		} else {
+			if (debug & DEBUG_HFCMULTI_STATE)
+				printk(KERN_DEBUG "%s: S/T NT newstate %x\n",
+				    __func__, dch->state);
+			switch (dch->state) {
+			case (2):
+				if (hc->chan[ch].nt_timer == 0) {
+					hc->chan[ch].nt_timer = -1;
+					HFC_outb(hc, R_ST_SEL,
+					    hc->chan[ch].port);
+					/* undocumented: delay after R_ST_SEL */
+					udelay(1);
+					HFC_outb(hc, A_ST_WR_STATE, 4 |
+					    V_ST_LD_STA); /* G4 */
+					udelay(6); /* wait at least 5,21us */
+					HFC_outb(hc, A_ST_WR_STATE, 4);
+					dch->state = 4;
+				} else {
+					/* one extra count for the next event */
+					hc->chan[ch].nt_timer =
+					    nt_t1_count[poll_timer] + 1;
+					HFC_outb(hc, R_ST_SEL,
+					    hc->chan[ch].port);
+					/* undocumented: delay after R_ST_SEL */
+					udelay(1);
+					/* allow G2 -> G3 transition */
+					HFC_outb(hc, A_ST_WR_STATE, 2 |
+					    V_SET_G2_G3);
+				}
+				break;
+			case (1):
+				hc->chan[ch].nt_timer = -1;
+				test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+				_queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+				    MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+				break;
+			case (4):
+				hc->chan[ch].nt_timer = -1;
+				break;
+			case (3):
+				hc->chan[ch].nt_timer = -1;
+				test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+				_queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+				    MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+				break;
+			}
+		}
+	}
+}
+
+/*
+ * called for card mode init message
+ */
+
+static void
+hfcmulti_initmode(struct dchannel *dch)
+{
+	struct hfc_multi *hc = dch->hw;
+	u_char		a_st_wr_state, r_e1_wr_sta;
+	int		i, pt;
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: entered\n", __func__);
+
+	if (hc->type == 1) {
+		hc->chan[hc->dslot].slot_tx = -1;
+		hc->chan[hc->dslot].slot_rx = -1;
+		hc->chan[hc->dslot].conf = -1;
+		if (hc->dslot) {
+			mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol,
+				-1, 0, -1, 0);
+			dch->timer.function = (void *) hfcmulti_dbusy_timer;
+			dch->timer.data = (long) dch;
+			init_timer(&dch->timer);
+		}
+		for (i = 1; i <= 31; i++) {
+			if (i == hc->dslot)
+				continue;
+			hc->chan[i].slot_tx = -1;
+			hc->chan[i].slot_rx = -1;
+			hc->chan[i].conf = -1;
+			mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0);
+		}
+		/* E1 */
+		if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) {
+			HFC_outb(hc, R_LOS0, 255); /* 2 ms */
+			HFC_outb(hc, R_LOS1, 255); /* 512 ms */
+		}
+		if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) {
+			HFC_outb(hc, R_RX0, 0);
+			hc->hw.r_tx0 = 0 | V_OUT_EN;
+		} else {
+			HFC_outb(hc, R_RX0, 1);
+			hc->hw.r_tx0 = 1 | V_OUT_EN;
+		}
+		hc->hw.r_tx1 = V_ATX | V_NTRI;
+		HFC_outb(hc, R_TX0, hc->hw.r_tx0);
+		HFC_outb(hc, R_TX1, hc->hw.r_tx1);
+		HFC_outb(hc, R_TX_FR0, 0x00);
+		HFC_outb(hc, R_TX_FR1, 0xf8);
+
+		if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+			HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E);
+
+		HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0);
+
+		if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+			HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC);
+
+		if (dch->dev.D.protocol == ISDN_P_NT_E1) {
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG "%s: E1 port is NT-mode\n",
+				    __func__);
+			r_e1_wr_sta = 0; /* G0 */
+			hc->e1_getclock = 0;
+		} else {
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG "%s: E1 port is TE-mode\n",
+				    __func__);
+			r_e1_wr_sta = 0; /* F0 */
+			hc->e1_getclock = 1;
+		}
+		if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+			HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+		else
+			HFC_outb(hc, R_SYNC_OUT, 0);
+		if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip))
+			hc->e1_getclock = 1;
+		if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip))
+			hc->e1_getclock = 0;
+		if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+			/* SLAVE (clock master) */
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG
+				    "%s: E1 port is clock master "
+				    "(clock from PCM)\n", __func__);
+			HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC);
+		} else {
+			if (hc->e1_getclock) {
+				/* MASTER (clock slave) */
+				if (debug & DEBUG_HFCMULTI_INIT)
+					printk(KERN_DEBUG
+					    "%s: E1 port is clock slave "
+					    "(clock to PCM)\n", __func__);
+				HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+			} else {
+				/* MASTER (clock master) */
+				if (debug & DEBUG_HFCMULTI_INIT)
+					printk(KERN_DEBUG "%s: E1 port is "
+					    "clock master "
+					    "(clock from QUARTZ)\n",
+					    __func__);
+				HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC |
+				    V_PCM_SYNC | V_JATT_OFF);
+				HFC_outb(hc, R_SYNC_OUT, 0);
+			}
+		}
+		HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */
+		HFC_outb(hc, R_PWM_MD, V_PWM0_MD);
+		HFC_outb(hc, R_PWM0, 0x50);
+		HFC_outb(hc, R_PWM1, 0xff);
+		/* state machine setup */
+		HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA);
+		udelay(6); /* wait at least 5,21us */
+		HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta);
+		if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+			hc->syncronized = 0;
+			plxsd_checksync(hc, 0);
+		}
+	} else {
+		i = dch->slot;
+		hc->chan[i].slot_tx = -1;
+		hc->chan[i].slot_rx = -1;
+		hc->chan[i].conf = -1;
+		mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0);
+		dch->timer.function = (void *)hfcmulti_dbusy_timer;
+		dch->timer.data = (long) dch;
+		init_timer(&dch->timer);
+		hc->chan[i - 2].slot_tx = -1;
+		hc->chan[i - 2].slot_rx = -1;
+		hc->chan[i - 2].conf = -1;
+		mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0);
+		hc->chan[i - 1].slot_tx = -1;
+		hc->chan[i - 1].slot_rx = -1;
+		hc->chan[i - 1].conf = -1;
+		mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0);
+		/* ST */
+		pt = hc->chan[i].port;
+		/* select interface */
+		HFC_outb(hc, R_ST_SEL, pt);
+		/* undocumented: delay after R_ST_SEL */
+		udelay(1);
+		if (dch->dev.D.protocol == ISDN_P_NT_S0) {
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG
+				    "%s: ST port %d is NT-mode\n",
+				    __func__, pt);
+			/* clock delay */
+			HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt);
+			a_st_wr_state = 1; /* G1 */
+			hc->hw.a_st_ctrl0[pt] = V_ST_MD;
+		} else {
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG
+				    "%s: ST port %d is TE-mode\n",
+				    __func__, pt);
+			/* clock delay */
+			HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te);
+			a_st_wr_state = 2; /* F2 */
+			hc->hw.a_st_ctrl0[pt] = 0;
+		}
+		if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg))
+			hc->hw.a_st_ctrl0[pt] |= V_TX_LI;
+		/* line setup */
+		HFC_outb(hc, A_ST_CTRL0,  hc->hw.a_st_ctrl0[pt]);
+		/* disable E-channel */
+		if ((dch->dev.D.protocol == ISDN_P_NT_S0) ||
+		    test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg))
+			HFC_outb(hc, A_ST_CTRL1, V_E_IGNO);
+		else
+			HFC_outb(hc, A_ST_CTRL1, 0);
+		/* enable B-channel receive */
+		HFC_outb(hc, A_ST_CTRL2,  V_B1_RX_EN | V_B2_RX_EN);
+		/* state machine setup */
+		HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA);
+		udelay(6); /* wait at least 5,21us */
+		HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state);
+		hc->hw.r_sci_msk |= 1 << pt;
+		/* state machine interrupts */
+		HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk);
+		/* unset sync on port */
+		if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+			hc->syncronized &=
+			   ~(1 << hc->chan[dch->slot].port);
+			plxsd_checksync(hc, 0);
+		}
+	}
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk("%s: done\n", __func__);
+}
+
+
+static int
+open_dchannel(struct hfc_multi *hc, struct dchannel *dch,
+    struct channel_req *rq)
+{
+	int	err = 0;
+	u_long	flags;
+
+	if (debug & DEBUG_HW_OPEN)
+		printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+		    dch->dev.id, __builtin_return_address(0));
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+	    (dch->dev.D.protocol != rq->protocol)) {
+	    if (debug & DEBUG_HFCMULTI_MODE)
+		printk(KERN_WARNING "%s: change protocol %x to %x\n",
+		    __func__, dch->dev.D.protocol, rq->protocol);
+	}
+	if ((dch->dev.D.protocol == ISDN_P_TE_S0)
+	 && (rq->protocol != ISDN_P_TE_S0))
+		l1_event(dch->l1, CLOSE_CHANNEL);
+	if (dch->dev.D.protocol != rq->protocol) {
+		if (rq->protocol == ISDN_P_TE_S0) {
+			err = create_l1(dch, hfcm_l1callback);
+			if (err)
+				return err;
+		}
+		dch->dev.D.protocol = rq->protocol;
+		spin_lock_irqsave(&hc->lock, flags);
+		hfcmulti_initmode(dch);
+		spin_unlock_irqrestore(&hc->lock, flags);
+	}
+
+	if (((rq->protocol == ISDN_P_NT_S0) && (dch->state == 3)) ||
+	    ((rq->protocol == ISDN_P_TE_S0) && (dch->state == 7)) ||
+	    ((rq->protocol == ISDN_P_NT_E1) && (dch->state == 1)) ||
+	    ((rq->protocol == ISDN_P_TE_E1) && (dch->state == 1))) {
+		_queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+		    0, NULL, GFP_KERNEL);
+	}
+	rq->ch = &dch->dev.D;
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s:cannot get module\n", __func__);
+	return 0;
+}
+
+static int
+open_bchannel(struct hfc_multi *hc, struct dchannel *dch,
+    struct channel_req *rq)
+{
+	struct bchannel	*bch;
+	int		ch;
+
+	if (!test_bit(rq->adr.channel, &dch->dev.channelmap[0]))
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	if (hc->type == 1)
+		ch = rq->adr.channel;
+	else
+		ch = (rq->adr.channel - 1) + (dch->slot - 2);
+	bch = hc->chan[ch].bch;
+	if (!bch) {
+		printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+		    __func__, ch);
+		return -EINVAL;
+	}
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	bch->ch.protocol = rq->protocol;
+	hc->chan[ch].rx_off = 0;
+	rq->ch = &bch->ch;
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s:cannot get module\n", __func__);
+	return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown Op %x\n",
+		    __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct hfc_multi	*hc = dch->hw;
+	struct channel_req	*rq;
+	int			err = 0;
+	u_long			flags;
+
+	if (dch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: cmd:%x %p\n",
+		    __func__, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		switch (rq->protocol) {
+		case ISDN_P_TE_S0:
+		case ISDN_P_NT_S0:
+			if (hc->type == 1) {
+				err = -EINVAL;
+				break;
+			}
+			err = open_dchannel(hc, dch, rq); /* locked there */
+			break;
+		case ISDN_P_TE_E1:
+		case ISDN_P_NT_E1:
+			if (hc->type != 1) {
+				err = -EINVAL;
+				break;
+			}
+			err = open_dchannel(hc, dch, rq); /* locked there */
+			break;
+		default:
+			spin_lock_irqsave(&hc->lock, flags);
+			err = open_bchannel(hc, dch, rq);
+			spin_unlock_irqrestore(&hc->lock, flags);
+		}
+		break;
+	case CLOSE_CHANNEL:
+		if (debug & DEBUG_HW_OPEN)
+			printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+			    __func__, dch->dev.id,
+			    __builtin_return_address(0));
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		spin_lock_irqsave(&hc->lock, flags);
+		err = channel_dctrl(dch, arg);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	default:
+		if (dch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: unknown command %x\n",
+			    __func__, cmd);
+		err = -EINVAL;
+	}
+	return err;
+}
+
+/*
+ * initialize the card
+ */
+
+/*
+ * start timer irq, wait some time and check if we have interrupts.
+ * if not, reset chip and try again.
+ */
+static int
+init_card(struct hfc_multi *hc)
+{
+	int	err = -EIO;
+	u_long	flags;
+	u_short	*plx_acc;
+	u_long	plx_flags;
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: entered\n", __func__);
+
+	spin_lock_irqsave(&hc->lock, flags);
+	/* set interrupts but leave global interrupt disabled */
+	hc->hw.r_irq_ctrl = V_FIFO_IRQ;
+	disable_hwirq(hc);
+	spin_unlock_irqrestore(&hc->lock, flags);
+
+	if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, IRQF_SHARED,
+	    "HFC-multi", hc)) {
+		printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n",
+		    hc->pci_dev->irq);
+		return -EIO;
+	}
+	hc->irq = hc->pci_dev->irq;
+
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+		spin_lock_irqsave(&plx_lock, plx_flags);
+		plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+		writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE),
+			plx_acc); /* enable PCI & LINT1 irq */
+		spin_unlock_irqrestore(&plx_lock, plx_flags);
+	}
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+		    __func__, hc->irq, hc->irqcnt);
+	err = init_chip(hc);
+	if (err)
+		goto error;
+	/*
+	 * Finally enable IRQ output
+	 * this is only allowed, if an IRQ routine is allready
+	 * established for this HFC, so don't do that earlier
+	 */
+	spin_lock_irqsave(&hc->lock, flags);
+	enable_hwirq(hc);
+	spin_unlock_irqrestore(&hc->lock, flags);
+	/* printk(KERN_DEBUG "no master irq set!!!\n"); */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
+	/* turn IRQ off until chip is completely initialized */
+	spin_lock_irqsave(&hc->lock, flags);
+	disable_hwirq(hc);
+	spin_unlock_irqrestore(&hc->lock, flags);
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+		    __func__, hc->irq, hc->irqcnt);
+	if (hc->irqcnt) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: done\n", __func__);
+
+		return 0;
+	}
+	if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+		printk(KERN_INFO "ignoring missing interrupts\n");
+		return 0;
+	}
+
+	printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n",
+		hc->irq);
+
+	err = -EIO;
+
+error:
+	if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+		spin_lock_irqsave(&plx_lock, plx_flags);
+		plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+		writew(0x00, plx_acc); /*disable IRQs*/
+		spin_unlock_irqrestore(&plx_lock, plx_flags);
+	}
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_WARNING "%s: free irq %d\n", __func__, hc->irq);
+	if (hc->irq) {
+		free_irq(hc->irq, hc);
+		hc->irq = 0;
+	}
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err);
+	return err;
+}
+
+/*
+ * find pci device and set it up
+ */
+
+static int
+setup_pci(struct hfc_multi *hc, struct pci_dev *pdev,
+		const struct pci_device_id *ent)
+{
+	struct hm_map	*m = (struct hm_map *)ent->driver_data;
+
+	printk(KERN_INFO
+	    "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n",
+	    m->vendor_name, m->card_name, m->clock2 ? "double" : "normal");
+
+	hc->pci_dev = pdev;
+	if (m->clock2)
+		test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
+
+	if (ent->device == 0xB410) {
+		test_and_set_bit(HFC_CHIP_B410P, &hc->chip);
+		test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+		test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+		hc->slots = 32;
+	}
+
+	if (hc->pci_dev->irq <= 0) {
+		printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n");
+		return -EIO;
+	}
+	if (pci_enable_device(hc->pci_dev)) {
+		printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n");
+		return -EIO;
+	}
+	hc->leds = m->leds;
+	hc->ledstate = 0xAFFEAFFE;
+	hc->opticalsupport = m->opticalsupport;
+
+	/* set memory access methods */
+	if (m->io_mode) /* use mode from card config */
+		hc->io_mode = m->io_mode;
+	switch (hc->io_mode) {
+	case HFC_IO_MODE_PLXSD:
+		test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip);
+		hc->slots = 128; /* required */
+		/* fall through */
+	case HFC_IO_MODE_PCIMEM:
+		hc->HFC_outb = HFC_outb_pcimem;
+		hc->HFC_inb = HFC_inb_pcimem;
+		hc->HFC_inw = HFC_inw_pcimem;
+		hc->HFC_wait = HFC_wait_pcimem;
+		hc->read_fifo = read_fifo_pcimem;
+		hc->write_fifo = write_fifo_pcimem;
+		break;
+	case HFC_IO_MODE_REGIO:
+		hc->HFC_outb = HFC_outb_regio;
+		hc->HFC_inb = HFC_inb_regio;
+		hc->HFC_inw = HFC_inw_regio;
+		hc->HFC_wait = HFC_wait_regio;
+		hc->read_fifo = read_fifo_regio;
+		hc->write_fifo = write_fifo_regio;
+		break;
+	default:
+		printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+		pci_disable_device(hc->pci_dev);
+		return -EIO;
+	}
+	hc->HFC_outb_nodebug = hc->HFC_outb;
+	hc->HFC_inb_nodebug = hc->HFC_inb;
+	hc->HFC_inw_nodebug = hc->HFC_inw;
+	hc->HFC_wait_nodebug = hc->HFC_wait;
+#ifdef HFC_REGISTER_DEBUG
+	hc->HFC_outb = HFC_outb_debug;
+	hc->HFC_inb = HFC_inb_debug;
+	hc->HFC_inw = HFC_inw_debug;
+	hc->HFC_wait = HFC_wait_debug;
+#endif
+	hc->pci_iobase = 0;
+	hc->pci_membase = NULL;
+	hc->plx_membase = NULL;
+
+	switch (hc->io_mode) {
+	case HFC_IO_MODE_PLXSD:
+		hc->plx_origmembase =  hc->pci_dev->resource[0].start;
+		/* MEMBASE 1 is PLX PCI Bridge */
+
+		if (!hc->plx_origmembase) {
+			printk(KERN_WARNING
+			  "HFC-multi: No IO-Memory for PCI PLX bridge found\n");
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+
+		hc->plx_membase = ioremap(hc->plx_origmembase, 0x80);
+		if (!hc->plx_membase) {
+			printk(KERN_WARNING
+			    "HFC-multi: failed to remap plx address space. "
+			    "(internal error)\n");
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+		printk(KERN_INFO
+		    "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n",
+		    (u_long)hc->plx_membase, hc->plx_origmembase);
+
+		hc->pci_origmembase =  hc->pci_dev->resource[2].start;
+		    /* MEMBASE 1 is PLX PCI Bridge */
+		if (!hc->pci_origmembase) {
+			printk(KERN_WARNING
+			    "HFC-multi: No IO-Memory for PCI card found\n");
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+
+		hc->pci_membase = ioremap(hc->pci_origmembase, 0x400);
+		if (!hc->pci_membase) {
+			printk(KERN_WARNING "HFC-multi: failed to remap io "
+			    "address space. (internal error)\n");
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+
+		printk(KERN_INFO
+		    "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d "
+		    "leds-type %d\n",
+		    hc->id, (u_long)hc->pci_membase, hc->pci_origmembase,
+		    hc->pci_dev->irq, HZ, hc->leds);
+		pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+		break;
+	case HFC_IO_MODE_PCIMEM:
+		hc->pci_origmembase = hc->pci_dev->resource[1].start;
+		if (!hc->pci_origmembase) {
+			printk(KERN_WARNING
+			    "HFC-multi: No IO-Memory for PCI card found\n");
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+
+		hc->pci_membase = ioremap(hc->pci_origmembase, 256);
+		if (!hc->pci_membase) {
+			printk(KERN_WARNING
+			    "HFC-multi: failed to remap io address space. "
+			    "(internal error)\n");
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+		printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d "
+		    "HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase,
+		    hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds);
+		pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+		break;
+	case HFC_IO_MODE_REGIO:
+		hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start;
+		if (!hc->pci_iobase) {
+			printk(KERN_WARNING
+				"HFC-multi: No IO for PCI card found\n");
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+
+		if (!request_region(hc->pci_iobase, 8, "hfcmulti")) {
+			printk(KERN_WARNING "HFC-multi: failed to request "
+			    "address space at 0x%08lx (internal error)\n",
+			    hc->pci_iobase);
+			pci_disable_device(hc->pci_dev);
+			return -EIO;
+		}
+
+		printk(KERN_INFO
+		    "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n",
+		    m->vendor_name, m->card_name, (u_int) hc->pci_iobase,
+		    hc->pci_dev->irq, HZ, hc->leds);
+		pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO);
+		break;
+	default:
+		printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+		pci_disable_device(hc->pci_dev);
+		return -EIO;
+	}
+
+	pci_set_drvdata(hc->pci_dev, hc);
+
+	/* At this point the needed PCI config is done */
+	/* fifos are still not enabled */
+	return 0;
+}
+
+
+/*
+ * remove port
+ */
+
+static void
+release_port(struct hfc_multi *hc, struct dchannel *dch)
+{
+	int	pt, ci, i = 0;
+	u_long	flags;
+	struct bchannel *pb;
+
+	ci = dch->slot;
+	pt = hc->chan[ci].port;
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: entered for port %d\n",
+			__func__, pt + 1);
+
+	if (pt >= hc->ports) {
+		printk(KERN_WARNING "%s: ERROR port out of range (%d).\n",
+		     __func__, pt + 1);
+		return;
+	}
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: releasing port=%d\n",
+		    __func__, pt + 1);
+
+	if (dch->dev.D.protocol == ISDN_P_TE_S0)
+		l1_event(dch->l1, CLOSE_CHANNEL);
+
+	hc->chan[ci].dch = NULL;
+
+	if (hc->created[pt]) {
+		hc->created[pt] = 0;
+		mISDN_unregister_device(&dch->dev);
+	}
+
+	spin_lock_irqsave(&hc->lock, flags);
+
+	if (dch->timer.function) {
+		del_timer(&dch->timer);
+		dch->timer.function = NULL;
+	}
+
+	if (hc->type == 1) { /* E1 */
+		/* remove sync */
+		if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+			hc->syncronized = 0;
+			plxsd_checksync(hc, 1);
+		}
+		/* free channels */
+		for (i = 0; i <= 31; i++) {
+			if (hc->chan[i].bch) {
+				if (debug & DEBUG_HFCMULTI_INIT)
+					printk(KERN_DEBUG
+					    "%s: free port %d channel %d\n",
+					    __func__, hc->chan[i].port+1, i);
+				pb = hc->chan[i].bch;
+				hc->chan[i].bch = NULL;
+				spin_unlock_irqrestore(&hc->lock, flags);
+				mISDN_freebchannel(pb);
+				kfree(pb);
+				kfree(hc->chan[i].coeff);
+				spin_lock_irqsave(&hc->lock, flags);
+			}
+		}
+	} else {
+		/* remove sync */
+		if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+			hc->syncronized &=
+			   ~(1 << hc->chan[ci].port);
+			plxsd_checksync(hc, 1);
+		}
+		/* free channels */
+		if (hc->chan[ci - 2].bch) {
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG
+				    "%s: free port %d channel %d\n",
+				    __func__, hc->chan[ci - 2].port+1,
+				    ci - 2);
+			pb = hc->chan[ci - 2].bch;
+			hc->chan[ci - 2].bch = NULL;
+			spin_unlock_irqrestore(&hc->lock, flags);
+			mISDN_freebchannel(pb);
+			kfree(pb);
+			kfree(hc->chan[ci - 2].coeff);
+			spin_lock_irqsave(&hc->lock, flags);
+		}
+		if (hc->chan[ci - 1].bch) {
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG
+				    "%s: free port %d channel %d\n",
+				    __func__, hc->chan[ci - 1].port+1,
+				    ci - 1);
+			pb = hc->chan[ci - 1].bch;
+			hc->chan[ci - 1].bch = NULL;
+			spin_unlock_irqrestore(&hc->lock, flags);
+			mISDN_freebchannel(pb);
+			kfree(pb);
+			kfree(hc->chan[ci - 1].coeff);
+			spin_lock_irqsave(&hc->lock, flags);
+		}
+	}
+
+	spin_unlock_irqrestore(&hc->lock, flags);
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: free port %d channel D\n", __func__, pt);
+	mISDN_freedchannel(dch);
+	kfree(dch);
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: done!\n", __func__);
+}
+
+static void
+release_card(struct hfc_multi *hc)
+{
+	u_long	flags;
+	int	ch;
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_WARNING "%s: release card (%d) entered\n",
+		    __func__, hc->id);
+
+	spin_lock_irqsave(&hc->lock, flags);
+	disable_hwirq(hc);
+	spin_unlock_irqrestore(&hc->lock, flags);
+
+	udelay(1000);
+
+	/* dimm leds */
+	if (hc->leds)
+		hfcmulti_leds(hc);
+
+	/* disable D-channels & B-channels */
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: disable all channels (d and b)\n",
+		    __func__);
+	for (ch = 0; ch <= 31; ch++) {
+		if (hc->chan[ch].dch)
+			release_port(hc, hc->chan[ch].dch);
+	}
+
+	/* release hardware & irq */
+	if (hc->irq) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_WARNING "%s: free irq %d\n",
+			    __func__, hc->irq);
+		free_irq(hc->irq, hc);
+		hc->irq = 0;
+
+	}
+	release_io_hfcmulti(hc);
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_WARNING "%s: remove instance from list\n",
+		     __func__);
+	list_del(&hc->list);
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_WARNING "%s: delete instance\n", __func__);
+	if (hc == syncmaster)
+		syncmaster = NULL;
+	kfree(hc);
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_WARNING "%s: card successfully removed\n",
+		    __func__);
+}
+
+static int
+init_e1_port(struct hfc_multi *hc, struct hm_map *m)
+{
+	struct dchannel	*dch;
+	struct bchannel	*bch;
+	int		ch, ret = 0;
+	char		name[MISDN_MAX_IDLEN];
+
+	dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+	if (!dch)
+		return -ENOMEM;
+	dch->debug = debug;
+	mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+	dch->hw = hc;
+	dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+	dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+	    (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	dch->dev.D.send = handle_dmsg;
+	dch->dev.D.ctrl = hfcm_dctrl;
+	dch->dev.nrbchan = (hc->dslot)?30:31;
+	dch->slot = hc->dslot;
+	hc->chan[hc->dslot].dch = dch;
+	hc->chan[hc->dslot].port = 0;
+	hc->chan[hc->dslot].nt_timer = -1;
+	for (ch = 1; ch <= 31; ch++) {
+		if (ch == hc->dslot) /* skip dchannel */
+			continue;
+		bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+		if (!bch) {
+			printk(KERN_ERR "%s: no memory for bchannel\n",
+			    __func__);
+			ret = -ENOMEM;
+			goto free_chan;
+		}
+		hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL);
+		if (!hc->chan[ch].coeff) {
+			printk(KERN_ERR "%s: no memory for coeffs\n",
+			    __func__);
+			ret = -ENOMEM;
+			goto free_chan;
+		}
+		bch->nr = ch;
+		bch->slot = ch;
+		bch->debug = debug;
+		mISDN_initbchannel(bch, MAX_DATA_MEM);
+		bch->hw = hc;
+		bch->ch.send = handle_bmsg;
+		bch->ch.ctrl = hfcm_bctrl;
+		bch->ch.nr = ch;
+		list_add(&bch->ch.list, &dch->dev.bchannels);
+		hc->chan[ch].bch = bch;
+		hc->chan[ch].port = 0;
+		test_and_set_bit(bch->nr, &dch->dev.channelmap[0]);
+	}
+	/* set optical line type */
+	if (port[Port_cnt] & 0x001) {
+		if (!m->opticalsupport)  {
+			printk(KERN_INFO
+			    "This board has no optical "
+			    "support\n");
+		} else {
+			if (debug & DEBUG_HFCMULTI_INIT)
+				printk(KERN_DEBUG
+				    "%s: PORT set optical "
+				    "interfacs: card(%d) "
+				    "port(%d)\n",
+				    __func__,
+				    HFC_cnt + 1, 1);
+			test_and_set_bit(HFC_CFG_OPTICAL,
+			    &hc->chan[hc->dslot].cfg);
+		}
+	}
+	/* set LOS report */
+	if (port[Port_cnt] & 0x004) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: PORT set "
+			    "LOS report: card(%d) port(%d)\n",
+			    __func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CFG_REPORT_LOS,
+		    &hc->chan[hc->dslot].cfg);
+	}
+	/* set AIS report */
+	if (port[Port_cnt] & 0x008) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: PORT set "
+			    "AIS report: card(%d) port(%d)\n",
+			    __func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CFG_REPORT_AIS,
+		    &hc->chan[hc->dslot].cfg);
+	}
+	/* set SLIP report */
+	if (port[Port_cnt] & 0x010) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: PORT set SLIP report: "
+			    "card(%d) port(%d)\n",
+			    __func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CFG_REPORT_SLIP,
+		    &hc->chan[hc->dslot].cfg);
+	}
+	/* set RDI report */
+	if (port[Port_cnt] & 0x020) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: PORT set RDI report: "
+			    "card(%d) port(%d)\n",
+			    __func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CFG_REPORT_RDI,
+		    &hc->chan[hc->dslot].cfg);
+	}
+	/* set CRC-4 Mode */
+	if (!(port[Port_cnt] & 0x100)) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: PORT turn on CRC4 report:"
+				" card(%d) port(%d)\n",
+				__func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CFG_CRC4,
+		    &hc->chan[hc->dslot].cfg);
+	} else {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: PORT turn off CRC4"
+				" report: card(%d) port(%d)\n",
+				__func__, HFC_cnt + 1, 1);
+	}
+	/* set forced clock */
+	if (port[Port_cnt] & 0x0200) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: PORT force getting clock from "
+				"E1: card(%d) port(%d)\n",
+				__func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip);
+	} else
+	if (port[Port_cnt] & 0x0400) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: PORT force putting clock to "
+				"E1: card(%d) port(%d)\n",
+				__func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip);
+	}
+	/* set JATT PLL */
+	if (port[Port_cnt] & 0x0800) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG "%s: PORT disable JATT PLL on "
+				"E1: card(%d) port(%d)\n",
+				__func__, HFC_cnt + 1, 1);
+		test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip);
+	}
+	/* set elastic jitter buffer */
+	if (port[Port_cnt] & 0x3000) {
+		hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3;
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: PORT set elastic "
+			    "buffer to %d: card(%d) port(%d)\n",
+			    __func__, hc->chan[hc->dslot].jitter,
+			    HFC_cnt + 1, 1);
+	} else
+		hc->chan[hc->dslot].jitter = 2; /* default */
+	snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1);
+	ret = mISDN_register_device(&dch->dev, name);
+	if (ret)
+		goto free_chan;
+	hc->created[0] = 1;
+	return ret;
+free_chan:
+	release_port(hc, dch);
+	return ret;
+}
+
+static int
+init_multi_port(struct hfc_multi *hc, int pt)
+{
+	struct dchannel	*dch;
+	struct bchannel	*bch;
+	int		ch, i, ret = 0;
+	char		name[MISDN_MAX_IDLEN];
+
+	dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+	if (!dch)
+		return -ENOMEM;
+	dch->debug = debug;
+	mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+	dch->hw = hc;
+	dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+	dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+	    (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	dch->dev.D.send = handle_dmsg;
+	dch->dev.D.ctrl = hfcm_dctrl;
+	dch->dev.nrbchan = 2;
+	i = pt << 2;
+	dch->slot = i + 2;
+	hc->chan[i + 2].dch = dch;
+	hc->chan[i + 2].port = pt;
+	hc->chan[i + 2].nt_timer = -1;
+	for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+		bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+		if (!bch) {
+			printk(KERN_ERR "%s: no memory for bchannel\n",
+			    __func__);
+			ret = -ENOMEM;
+			goto free_chan;
+		}
+		hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL);
+		if (!hc->chan[i + ch].coeff) {
+			printk(KERN_ERR "%s: no memory for coeffs\n",
+			    __func__);
+			ret = -ENOMEM;
+			goto free_chan;
+		}
+		bch->nr = ch + 1;
+		bch->slot = i + ch;
+		bch->debug = debug;
+		mISDN_initbchannel(bch, MAX_DATA_MEM);
+		bch->hw = hc;
+		bch->ch.send = handle_bmsg;
+		bch->ch.ctrl = hfcm_bctrl;
+		bch->ch.nr = ch + 1;
+		list_add(&bch->ch.list, &dch->dev.bchannels);
+		hc->chan[i + ch].bch = bch;
+		hc->chan[i + ch].port = pt;
+		test_and_set_bit(bch->nr, &dch->dev.channelmap[0]);
+	}
+	/* set master clock */
+	if (port[Port_cnt] & 0x001) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: PROTOCOL set master clock: "
+			    "card(%d) port(%d)\n",
+			    __func__, HFC_cnt + 1, pt + 1);
+		if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+			printk(KERN_ERR "Error: Master clock "
+			    "for port(%d) of card(%d) is only"
+			    " possible with TE-mode\n",
+			    pt + 1, HFC_cnt + 1);
+			ret = -EINVAL;
+			goto free_chan;
+		}
+		if (hc->masterclk >= 0) {
+			printk(KERN_ERR "Error: Master clock "
+			    "for port(%d) of card(%d) already "
+			    "defined for port(%d)\n",
+			    pt + 1, HFC_cnt + 1, hc->masterclk+1);
+			ret = -EINVAL;
+			goto free_chan;
+		}
+		hc->masterclk = pt;
+	}
+	/* set transmitter line to non capacitive */
+	if (port[Port_cnt] & 0x002) {
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: PROTOCOL set non capacitive "
+			    "transmitter: card(%d) port(%d)\n",
+			    __func__, HFC_cnt + 1, pt + 1);
+		test_and_set_bit(HFC_CFG_NONCAP_TX,
+		    &hc->chan[i + 2].cfg);
+	}
+	/* disable E-channel */
+	if (port[Port_cnt] & 0x004) {
+	if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: PROTOCOL disable E-channel: "
+			    "card(%d) port(%d)\n",
+			    __func__, HFC_cnt + 1, pt + 1);
+		test_and_set_bit(HFC_CFG_DIS_ECHANNEL,
+		    &hc->chan[i + 2].cfg);
+	}
+	snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d",
+		hc->type, HFC_cnt + 1, pt + 1);
+	ret = mISDN_register_device(&dch->dev, name);
+	if (ret)
+		goto free_chan;
+	hc->created[pt] = 1;
+	return ret;
+free_chan:
+	release_port(hc, dch);
+	return ret;
+}
+
+static int
+hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct hm_map	*m = (struct hm_map *)ent->driver_data;
+	int		ret_err = 0;
+	int		pt;
+	struct hfc_multi	*hc;
+	u_long		flags;
+	u_char		dips = 0, pmj = 0; /* dip settings, port mode Jumpers */
+
+	if (HFC_cnt >= MAX_CARDS) {
+		printk(KERN_ERR "too many cards (max=%d).\n",
+			MAX_CARDS);
+		return -EINVAL;
+	}
+	if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) {
+		printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but "
+		    "type[%d] %d was supplied as module parameter\n",
+		    m->vendor_name, m->card_name, m->type, HFC_cnt,
+		    type[HFC_cnt] & 0xff);
+		printk(KERN_WARNING "HFC-MULTI: Load module without parameters "
+			"first, to see cards and their types.");
+		return -EINVAL;
+	}
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n",
+		    __func__, m->vendor_name, m->card_name, m->type,
+		    type[HFC_cnt]);
+
+	/* allocate card+fifo structure */
+	hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL);
+	if (!hc) {
+		printk(KERN_ERR "No kmem for HFC-Multi card\n");
+		return -ENOMEM;
+	}
+	spin_lock_init(&hc->lock);
+	hc->mtyp = m;
+	hc->type =  m->type;
+	hc->ports = m->ports;
+	hc->id = HFC_cnt;
+	hc->pcm = pcm[HFC_cnt];
+	hc->io_mode = iomode[HFC_cnt];
+	if (dslot[HFC_cnt] < 0) {
+		hc->dslot = 0;
+		printk(KERN_INFO "HFC-E1 card has disabled D-channel, but "
+			"31 B-channels\n");
+	} if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) {
+		hc->dslot = dslot[HFC_cnt];
+		printk(KERN_INFO "HFC-E1 card has alternating D-channel on "
+			"time slot %d\n", dslot[HFC_cnt]);
+	} else
+		hc->dslot = 16;
+
+	/* set chip specific features */
+	hc->masterclk = -1;
+	if (type[HFC_cnt] & 0x100) {
+		test_and_set_bit(HFC_CHIP_ULAW, &hc->chip);
+		silence = 0xff; /* ulaw silence */
+	} else
+		silence = 0x2a; /* alaw silence */
+	if (!(type[HFC_cnt] & 0x200))
+		test_and_set_bit(HFC_CHIP_DTMF, &hc->chip);
+
+	if (type[HFC_cnt] & 0x800)
+		test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+	if (type[HFC_cnt] & 0x1000) {
+		test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+		test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+	}
+	if (type[HFC_cnt] & 0x4000)
+		test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip);
+	if (type[HFC_cnt] & 0x8000)
+		test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip);
+	hc->slots = 32;
+	if (type[HFC_cnt] & 0x10000)
+		hc->slots = 64;
+	if (type[HFC_cnt] & 0x20000)
+		hc->slots = 128;
+	if (type[HFC_cnt] & 0x80000) {
+		test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip);
+		hc->wdcount = 0;
+		hc->wdbyte = V_GPIO_OUT2;
+		printk(KERN_NOTICE "Watchdog enabled\n");
+	}
+
+	/* setup pci, hc->slots may change due to PLXSD */
+	ret_err = setup_pci(hc, pdev, ent);
+	if (ret_err) {
+		if (hc == syncmaster)
+			syncmaster = NULL;
+		kfree(hc);
+		return ret_err;
+	}
+
+	/* crate channels */
+	for (pt = 0; pt < hc->ports; pt++) {
+		if (Port_cnt >= MAX_PORTS) {
+			printk(KERN_ERR "too many ports (max=%d).\n",
+				MAX_PORTS);
+			ret_err = -EINVAL;
+			goto free_card;
+		}
+		if (hc->type == 1)
+			ret_err = init_e1_port(hc, m);
+		else
+			ret_err = init_multi_port(hc, pt);
+		if (debug & DEBUG_HFCMULTI_INIT)
+			printk(KERN_DEBUG
+			    "%s: Registering D-channel, card(%d) port(%d)"
+			    "result %d\n",
+			    __func__, HFC_cnt + 1, pt, ret_err);
+
+		if (ret_err) {
+			while (pt) { /* release already registered ports */
+				pt--;
+				release_port(hc, hc->chan[(pt << 2) + 2].dch);
+			}
+			goto free_card;
+		}
+		Port_cnt++;
+	}
+
+	/* disp switches */
+	switch (m->dip_type) {
+	case DIP_4S:
+		/*
+		 * get DIP Setting for beroNet 1S/2S/4S cards
+		 *  check if Port Jumper config matches
+		 * module param 'protocol'
+		 * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) +
+		 * GPI 19/23 (R_GPI_IN2))
+		 */
+		dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) |
+			((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) |
+			(~HFC_inb(hc, R_GPI_IN2) & 0x08);
+
+		/* Port mode (TE/NT) jumpers */
+		pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4)  & 0xf);
+
+		if (test_bit(HFC_CHIP_B410P, &hc->chip))
+			pmj = ~pmj & 0xf;
+
+		printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n",
+			m->vendor_name, m->card_name, dips, pmj);
+		break;
+	case DIP_8S:
+		/*
+		 * get DIP Setting for beroNet 8S0+ cards
+		 *
+		 * enable PCI auxbridge function
+		 */
+		HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+		/* prepare access to auxport */
+		outw(0x4000, hc->pci_iobase + 4);
+		/*
+		 * some dummy reads are required to
+		 * read valid DIP switch data
+		 */
+		dips = inb(hc->pci_iobase);
+		dips = inb(hc->pci_iobase);
+		dips = inb(hc->pci_iobase);
+		dips = ~inb(hc->pci_iobase) & 0x3F;
+		outw(0x0, hc->pci_iobase + 4);
+		/* disable PCI auxbridge function */
+		HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+		printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+		    m->vendor_name, m->card_name, dips);
+		break;
+	case DIP_E1:
+		/*
+		 * get DIP Setting for beroNet E1 cards
+		 * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0)
+		 */
+		dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0)>>4;
+		printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+		    m->vendor_name, m->card_name, dips);
+		break;
+	}
+
+	/* add to list */
+	spin_lock_irqsave(&HFClock, flags);
+	list_add_tail(&hc->list, &HFClist);
+	spin_unlock_irqrestore(&HFClock, flags);
+
+	/* initialize hardware */
+	ret_err = init_card(hc);
+	if (ret_err) {
+		printk(KERN_ERR "init card returns %d\n", ret_err);
+		release_card(hc);
+		return ret_err;
+	}
+
+	/* start IRQ and return */
+	spin_lock_irqsave(&hc->lock, flags);
+	enable_hwirq(hc);
+	spin_unlock_irqrestore(&hc->lock, flags);
+	return 0;
+
+free_card:
+	release_io_hfcmulti(hc);
+	if (hc == syncmaster)
+		syncmaster = NULL;
+	kfree(hc);
+	return ret_err;
+}
+
+static void __devexit hfc_remove_pci(struct pci_dev *pdev)
+{
+	struct hfc_multi	*card = pci_get_drvdata(pdev);
+	u_long			flags;
+
+	if (debug)
+		printk(KERN_INFO "removing hfc_multi card vendor:%x "
+		    "device:%x subvendor:%x subdevice:%x\n",
+		    pdev->vendor, pdev->device,
+		    pdev->subsystem_vendor, pdev->subsystem_device);
+
+	if (card) {
+		spin_lock_irqsave(&HFClock, flags);
+		release_card(card);
+		spin_unlock_irqrestore(&HFClock, flags);
+	}  else {
+		if (debug)
+			printk(KERN_WARNING "%s: drvdata allready removed\n",
+			    __func__);
+	}
+}
+
+#define	VENDOR_CCD	"Cologne Chip AG"
+#define	VENDOR_BN	"beroNet GmbH"
+#define	VENDOR_DIG	"Digium Inc."
+#define VENDOR_JH	"Junghanns.NET GmbH"
+#define VENDOR_PRIM	"PrimuX"
+
+static const struct hm_map hfcm_map[] = {
+/*0*/	{VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0},
+/*1*/	{VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S},
+/*2*/	{VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0},
+/*3*/	{VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0},
+/*4*/	{VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0},
+/*5*/	{VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0},
+/*6*/	{VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, 0, 0},
+/*7*/	{VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0},
+/*8*/	{VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO},
+/*9*/	{VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0},
+/*10*/	{VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0},
+/*11*/	{VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0},
+
+/*12*/	{VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0},
+/*13*/	{VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S,
+		HFC_IO_MODE_REGIO},
+/*14*/	{VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0},
+/*15*/	{VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0},
+
+/*16*/	{VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0},
+/*17*/	{VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+/*18*/	{VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+
+/*19*/	{VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*20*/	{VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0},
+/*21*/	{VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*22*/	{VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+
+/*23*/	{VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0},
+/*24*/	{VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0},
+/*25*/	{VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0},
+
+/*26*/	{VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0,
+		HFC_IO_MODE_PLXSD},
+/*27*/	{VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0,
+		HFC_IO_MODE_PLXSD},
+/*28*/	{VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0},
+/*29*/	{VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0},
+/*30*/	{VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0},
+};
+
+#undef H
+#define H(x)	((unsigned long)&hfcm_map[x])
+static struct pci_device_id hfmultipci_ids[] __devinitdata = {
+
+	/* Cards with HFC-4S Chip */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */
+	{ PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S,
+		PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)},
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)},
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */
+
+	/* Cards with HFC-8S Chip */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+	PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+	PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+	PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+	PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)},
+	    /* IOB8ST Recording */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST  */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST  */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */
+
+
+	/* Cards with HFC-E1 Chip */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */
+
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+		PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */
+
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */
+	{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+		PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, 0},
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, 0},
+	{ PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_ANY_ID, PCI_ANY_ID,
+		0, 0, 0},
+	{0, }
+};
+#undef H
+
+MODULE_DEVICE_TABLE(pci, hfmultipci_ids);
+
+static int
+hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct hm_map	*m = (struct hm_map *)ent->driver_data;
+	int		ret;
+
+	if (m == NULL) {
+		if (ent->vendor == PCI_VENDOR_ID_CCD)
+			if (ent->device == PCI_DEVICE_ID_CCD_HFC4S ||
+			    ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
+			    ent->device == PCI_DEVICE_ID_CCD_HFCE1)
+				printk(KERN_ERR
+				    "unknown HFC multiport controller "
+				    "(vendor:%x device:%x subvendor:%x "
+				    "subdevice:%x) Please contact the "
+				    "driver maintainer for support.\n",
+				    ent->vendor, ent->device,
+				    ent->subvendor, ent->subdevice);
+		return -ENODEV;
+	}
+	ret = hfcmulti_init(pdev, ent);
+	if (ret)
+		return ret;
+	HFC_cnt++;
+	printk(KERN_INFO "%d devices registered\n", HFC_cnt);
+	return 0;
+}
+
+static struct pci_driver hfcmultipci_driver = {
+	.name		= "hfc_multi",
+	.probe		= hfcmulti_probe,
+	.remove		= __devexit_p(hfc_remove_pci),
+	.id_table	= hfmultipci_ids,
+};
+
+static void __exit
+HFCmulti_cleanup(void)
+{
+	struct hfc_multi *card, *next;
+
+	/* unload interrupt function symbol */
+	if (hfc_interrupt)
+		symbol_put(ztdummy_extern_interrupt);
+	if (register_interrupt)
+		symbol_put(ztdummy_register_interrupt);
+	if (unregister_interrupt) {
+		if (interrupt_registered) {
+			interrupt_registered = 0;
+			unregister_interrupt();
+		}
+		symbol_put(ztdummy_unregister_interrupt);
+	}
+
+	list_for_each_entry_safe(card, next, &HFClist, list)
+		release_card(card);
+	/* get rid of all devices of this driver */
+	pci_unregister_driver(&hfcmultipci_driver);
+}
+
+static int __init
+HFCmulti_init(void)
+{
+	int err;
+
+#ifdef IRQ_DEBUG
+	printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__);
+#endif
+
+	spin_lock_init(&HFClock);
+	spin_lock_init(&plx_lock);
+
+	if (debug & DEBUG_HFCMULTI_INIT)
+		printk(KERN_DEBUG "%s: init entered\n", __func__);
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+	hfc_interrupt = symbol_get(ztdummy_extern_interrupt);
+	register_interrupt = symbol_get(ztdummy_register_interrupt);
+	unregister_interrupt = symbol_get(ztdummy_unregister_interrupt);
+	printk(KERN_INFO "mISDN: HFC-multi driver %s\n",
+	    hfcmulti_revision);
+
+	switch (poll) {
+	case 0:
+		poll_timer = 6;
+		poll = 128;
+		break;
+		/*
+		 * wenn dieses break nochmal verschwindet,
+		 * gibt es heisse ohren :-)
+		 * "without the break you will get hot ears ???"
+		 */
+	case 8:
+		poll_timer = 2;
+		break;
+	case 16:
+		poll_timer = 3;
+		break;
+	case 32:
+		poll_timer = 4;
+		break;
+	case 64:
+		poll_timer = 5;
+		break;
+	case 128:
+		poll_timer = 6;
+		break;
+	case 256:
+		poll_timer = 7;
+		break;
+	default:
+		printk(KERN_ERR
+		    "%s: Wrong poll value (%d).\n", __func__, poll);
+		err = -EINVAL;
+		return err;
+
+	}
+
+	err = pci_register_driver(&hfcmultipci_driver);
+	if (err < 0) {
+		printk(KERN_ERR "error registering pci driver: %x\n", err);
+		if (hfc_interrupt)
+			symbol_put(ztdummy_extern_interrupt);
+		if (register_interrupt)
+			symbol_put(ztdummy_register_interrupt);
+		if (unregister_interrupt) {
+			if (interrupt_registered) {
+				interrupt_registered = 0;
+				unregister_interrupt();
+			}
+			symbol_put(ztdummy_unregister_interrupt);
+		}
+		return err;
+	}
+	return 0;
+}
+
+
+module_init(HFCmulti_init);
+module_exit(HFCmulti_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
new file mode 100644
index 0000000..9179685
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -0,0 +1,2256 @@
+/*
+ *
+ * hfcpci.c     low level driver for CCD's hfc-pci based cards
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)
+ *            based on existing driver for CCD hfc ISA cards
+ *            type approval valid for HFC-S PCI A based card
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn-development.de)
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+
+#include "hfc_pci.h"
+
+static const char *hfcpci_revision = "2.0";
+
+#define MAX_CARDS	8
+static int HFC_cnt;
+static uint debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, 0);
+
+static LIST_HEAD(HFClist);
+DEFINE_RWLOCK(HFClock);
+
+enum {
+	HFC_CCD_2BD0,
+	HFC_CCD_B000,
+	HFC_CCD_B006,
+	HFC_CCD_B007,
+	HFC_CCD_B008,
+	HFC_CCD_B009,
+	HFC_CCD_B00A,
+	HFC_CCD_B00B,
+	HFC_CCD_B00C,
+	HFC_CCD_B100,
+	HFC_CCD_B700,
+	HFC_CCD_B701,
+	HFC_ASUS_0675,
+	HFC_BERKOM_A1T,
+	HFC_BERKOM_TCONCEPT,
+	HFC_ANIGMA_MC145575,
+	HFC_ZOLTRIX_2BD0,
+	HFC_DIGI_DF_M_IOM2_E,
+	HFC_DIGI_DF_M_E,
+	HFC_DIGI_DF_M_IOM2_A,
+	HFC_DIGI_DF_M_A,
+	HFC_ABOCOM_2BD1,
+	HFC_SITECOM_DC105V2,
+};
+
+struct hfcPCI_hw {
+	unsigned char		cirm;
+	unsigned char		ctmt;
+	unsigned char		clkdel;
+	unsigned char		states;
+	unsigned char		conn;
+	unsigned char		mst_m;
+	unsigned char		int_m1;
+	unsigned char		int_m2;
+	unsigned char		sctrl;
+	unsigned char		sctrl_r;
+	unsigned char		sctrl_e;
+	unsigned char		trm;
+	unsigned char		fifo_en;
+	unsigned char		bswapped;
+	unsigned char		protocol;
+	int			nt_timer;
+	unsigned char		*pci_io; /* start of PCI IO memory */
+	dma_addr_t		dmahandle;
+	void			*fifos; /* FIFO memory */
+	int			last_bfifo_cnt[2];
+	    /* marker saving last b-fifo frame count */
+	struct timer_list	timer;
+};
+
+#define	HFC_CFG_MASTER		1
+#define HFC_CFG_SLAVE		2
+#define	HFC_CFG_PCM		3
+#define HFC_CFG_2HFC		4
+#define HFC_CFG_SLAVEHFC	5
+#define HFC_CFG_NEG_F0		6
+#define HFC_CFG_SW_DD_DU	7
+
+#define FLG_HFC_TIMER_T1	16
+#define FLG_HFC_TIMER_T3	17
+
+#define NT_T1_COUNT	1120	/* number of 3.125ms interrupts (3.5s) */
+#define NT_T3_COUNT	31	/* number of 3.125ms interrupts (97 ms) */
+#define CLKDEL_TE	0x0e	/* CLKDEL in TE mode */
+#define CLKDEL_NT	0x6c	/* CLKDEL in NT mode */
+
+
+struct hfc_pci {
+	struct list_head	list;
+	u_char			subtype;
+	u_char			chanlimit;
+	u_char			initdone;
+	u_long			cfg;
+	u_int			irq;
+	u_int			irqcnt;
+	struct pci_dev		*pdev;
+	struct hfcPCI_hw	hw;
+	spinlock_t		lock;	/* card lock */
+	struct dchannel		dch;
+	struct bchannel		bch[2];
+};
+
+/* Interface functions */
+static void
+enable_hwirq(struct hfc_pci *hc)
+{
+	hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE;
+	Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+static void
+disable_hwirq(struct hfc_pci *hc)
+{
+	hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE);
+	Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcpci(struct hfc_pci *hc)
+{
+	/* disable memory mapped ports + busmaster */
+	pci_write_config_word(hc->pdev, PCI_COMMAND, 0);
+	del_timer(&hc->hw.timer);
+	pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos, hc->hw.dmahandle);
+	iounmap((void *)hc->hw.pci_io);
+}
+
+/*
+ * set mode (NT or TE)
+ */
+static void
+hfcpci_setmode(struct hfc_pci *hc)
+{
+	if (hc->hw.protocol == ISDN_P_NT_S0) {
+		hc->hw.clkdel = CLKDEL_NT;	/* ST-Bit delay for NT-Mode */
+		hc->hw.sctrl |= SCTRL_MODE_NT;	/* NT-MODE */
+		hc->hw.states = 1;		/* G1 */
+	} else {
+		hc->hw.clkdel = CLKDEL_TE;	/* ST-Bit delay for TE-Mode */
+		hc->hw.sctrl &= ~SCTRL_MODE_NT;	/* TE-MODE */
+		hc->hw.states = 2;		/* F2 */
+	}
+	Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel);
+	Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states);
+	udelay(10);
+	Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */
+	Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
+}
+
+/*
+ * function called to reset the HFC PCI chip. A complete software reset of chip
+ * and fifos is done.
+ */
+static void
+reset_hfcpci(struct hfc_pci *hc)
+{
+	u_char	val;
+	int	cnt = 0;
+
+	printk(KERN_DEBUG "reset_hfcpci: entered\n");
+	val = Read_hfc(hc, HFCPCI_CHIP_ID);
+	printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val);
+	/* enable memory mapped ports, disable busmaster */
+	pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+	disable_hwirq(hc);
+	/* enable memory ports + busmaster */
+	pci_write_config_word(hc->pdev, PCI_COMMAND,
+	    PCI_ENA_MEMIO + PCI_ENA_MASTER);
+	val = Read_hfc(hc, HFCPCI_STATUS);
+	printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val);
+	hc->hw.cirm = HFCPCI_RESET;	/* Reset On */
+	Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	mdelay(10);			/* Timeout 10ms */
+	hc->hw.cirm = 0;		/* Reset Off */
+	Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+	val = Read_hfc(hc, HFCPCI_STATUS);
+	printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val);
+	while (cnt < 50000) { /* max 50000 us */
+		udelay(5);
+		cnt += 5;
+		val = Read_hfc(hc, HFCPCI_STATUS);
+		if (!(val & 2))
+			break;
+	}
+	printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt);
+
+	hc->hw.fifo_en = 0x30;	/* only D fifos enabled */
+
+	hc->hw.bswapped = 0;	/* no exchange */
+	hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
+	hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */
+	hc->hw.sctrl = 0x40;	/* set tx_lo mode, error in datasheet ! */
+	hc->hw.sctrl_r = 0;
+	hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE;	/* S/T Auto awake */
+	hc->hw.mst_m = 0;
+	if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+		hc->hw.mst_m |= HFCPCI_MASTER;	/* HFC Master Mode */
+	if (test_bit(HFC_CFG_NEG_F0, &hc->cfg))
+		hc->hw.mst_m |= HFCPCI_F0_NEGATIV;
+	Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+	Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+	Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
+	Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+
+	hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+	    HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
+	Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+
+	/* Clear already pending ints */
+	if (Read_hfc(hc, HFCPCI_INT_S1));
+
+	/* set NT/TE mode */
+	hfcpci_setmode(hc);
+
+	Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+	Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+
+	/*
+	 * Init GCI/IOM2 in master mode
+	 * Slots 0 and 1 are set for B-chan 1 and 2
+	 * D- and monitor/CI channel are not enabled
+	 * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC
+	 * STIO2 is used as data input, B1+B2 from IOM->ST
+	 * ST B-channel send disabled -> continous 1s
+	 * The IOM slots are always enabled
+	 */
+	if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
+		/* set data flow directions: connect B1,B2: HFC to/from PCM */
+		hc->hw.conn = 0x09;
+	} else {
+		hc->hw.conn = 0x36;	/* set data flow directions */
+		if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
+			Write_hfc(hc, HFCPCI_B1_SSL, 0xC0);
+			Write_hfc(hc, HFCPCI_B2_SSL, 0xC1);
+			Write_hfc(hc, HFCPCI_B1_RSL, 0xC0);
+			Write_hfc(hc, HFCPCI_B2_RSL, 0xC1);
+		} else {
+			Write_hfc(hc, HFCPCI_B1_SSL, 0x80);
+			Write_hfc(hc, HFCPCI_B2_SSL, 0x81);
+			Write_hfc(hc, HFCPCI_B1_RSL, 0x80);
+			Write_hfc(hc, HFCPCI_B2_RSL, 0x81);
+		}
+	}
+	Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+	val = Read_hfc(hc, HFCPCI_INT_S2);
+}
+
+/*
+ * Timer function called when kernel timer expires
+ */
+static void
+hfcpci_Timer(struct hfc_pci *hc)
+{
+	hc->hw.timer.expires = jiffies + 75;
+	/* WD RESET */
+/*
+ *	WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80);
+ *	add_timer(&hc->hw.timer);
+ */
+}
+
+
+/*
+ * select a b-channel entry matching and active
+ */
+static struct bchannel *
+Sel_BCS(struct hfc_pci *hc, int channel)
+{
+	if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) &&
+		(hc->bch[0].nr & channel))
+		return &hc->bch[0];
+	else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) &&
+		(hc->bch[1].nr & channel))
+		return &hc->bch[1];
+	else
+		return NULL;
+}
+
+/*
+ * clear the desired B-channel rx fifo
+ */
+static void
+hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo)
+{
+	u_char		fifo_state;
+	struct bzfifo	*bzr;
+
+	if (fifo) {
+		bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+		fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX;
+	} else {
+		bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1;
+		fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX;
+	}
+	if (fifo_state)
+		hc->hw.fifo_en ^= fifo_state;
+	Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+	hc->hw.last_bfifo_cnt[fifo] = 0;
+	bzr->f1 = MAX_B_FRAMES;
+	bzr->f2 = bzr->f1;	/* init F pointers to remain constant */
+	bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+	bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16(
+	    le16_to_cpu(bzr->za[MAX_B_FRAMES].z1));
+	if (fifo_state)
+		hc->hw.fifo_en |= fifo_state;
+	Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+}
+
+/*
+ * clear the desired B-channel tx fifo
+ */
+static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo)
+{
+	u_char		fifo_state;
+	struct bzfifo	*bzt;
+
+	if (fifo) {
+		bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+		fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX;
+	} else {
+		bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1;
+		fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX;
+	}
+	if (fifo_state)
+		hc->hw.fifo_en ^= fifo_state;
+	Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+	if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL)
+		printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) "
+		    "z1(%x) z2(%x) state(%x)\n",
+		    fifo, bzt->f1, bzt->f2,
+		    le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+		    le16_to_cpu(bzt->za[MAX_B_FRAMES].z2),
+		    fifo_state);
+	bzt->f2 = MAX_B_FRAMES;
+	bzt->f1 = bzt->f2;	/* init F pointers to remain constant */
+	bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+	bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(
+	    le16_to_cpu(bzt->za[MAX_B_FRAMES].z1 - 1));
+	if (fifo_state)
+		hc->hw.fifo_en |= fifo_state;
+	Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+	if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL)
+		printk(KERN_DEBUG
+		    "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n",
+		    fifo, bzt->f1, bzt->f2,
+		    le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+		    le16_to_cpu(bzt->za[MAX_B_FRAMES].z2));
+}
+
+/*
+ * read a complete B-frame out of the buffer
+ */
+static void
+hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz,
+    u_char *bdata, int count)
+{
+	u_char		*ptr, *ptr1, new_f2;
+	int		total, maxlen, new_z2;
+	struct zt	*zp;
+
+	if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+		printk(KERN_DEBUG "hfcpci_empty_fifo\n");
+	zp = &bz->za[bz->f2];	/* point to Z-Regs */
+	new_z2 = le16_to_cpu(zp->z2) + count;	/* new position in fifo */
+	if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+		new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
+	new_f2 = (bz->f2 + 1) & MAX_B_FRAMES;
+	if ((count > MAX_DATA_SIZE + 3) || (count < 4) ||
+	    (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) {
+		if (bch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet "
+			    "invalid length %d or crc\n", count);
+#ifdef ERROR_STATISTIC
+		bch->err_inv++;
+#endif
+		bz->za[new_f2].z2 = cpu_to_le16(new_z2);
+		bz->f2 = new_f2;	/* next buffer */
+	} else {
+		bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC);
+		if (!bch->rx_skb) {
+			printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+			return;
+		}
+		total = count;
+		count -= 3;
+		ptr = skb_put(bch->rx_skb, count);
+
+		if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL)
+			maxlen = count;		/* complete transfer */
+		else
+			maxlen = B_FIFO_SIZE + B_SUB_VAL -
+			    le16_to_cpu(zp->z2);	/* maximum */
+
+		ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL);
+		    /* start of data */
+		memcpy(ptr, ptr1, maxlen);	/* copy data */
+		count -= maxlen;
+
+		if (count) {	/* rest remaining */
+			ptr += maxlen;
+			ptr1 = bdata;	/* start of buffer */
+			memcpy(ptr, ptr1, count);	/* rest */
+		}
+		bz->za[new_f2].z2 = cpu_to_le16(new_z2);
+		bz->f2 = new_f2;	/* next buffer */
+		recv_Bchannel(bch);
+	}
+}
+
+/*
+ * D-channel receive procedure
+ */
+static int
+receive_dmsg(struct hfc_pci *hc)
+{
+	struct dchannel	*dch = &hc->dch;
+	int		maxlen;
+	int		rcnt, total;
+	int		count = 5;
+	u_char		*ptr, *ptr1;
+	struct dfifo	*df;
+	struct zt	*zp;
+
+	df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx;
+	while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) {
+		zp = &df->za[df->f2 & D_FREG_MASK];
+		rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+		if (rcnt < 0)
+			rcnt += D_FIFO_SIZE;
+		rcnt++;
+		if (dch->debug & DEBUG_HW_DCHANNEL)
+			printk(KERN_DEBUG
+			    "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n",
+				df->f1, df->f2,
+				le16_to_cpu(zp->z1),
+				le16_to_cpu(zp->z2),
+				rcnt);
+
+		if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) ||
+		    (df->data[le16_to_cpu(zp->z1)])) {
+			if (dch->debug & DEBUG_HW)
+				printk(KERN_DEBUG
+				    "empty_fifo hfcpci paket inv. len "
+				    "%d or crc %d\n",
+				    rcnt,
+				    df->data[le16_to_cpu(zp->z1)]);
+#ifdef ERROR_STATISTIC
+			cs->err_rx++;
+#endif
+			df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) |
+			    (MAX_D_FRAMES + 1);	/* next buffer */
+			df->za[df->f2 & D_FREG_MASK].z2 =
+			    cpu_to_le16((zp->z2 + rcnt) & (D_FIFO_SIZE - 1));
+		} else {
+			dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC);
+			if (!dch->rx_skb) {
+				printk(KERN_WARNING
+				    "HFC-PCI: D receive out of memory\n");
+				break;
+			}
+			total = rcnt;
+			rcnt -= 3;
+			ptr = skb_put(dch->rx_skb, rcnt);
+
+			if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE)
+				maxlen = rcnt;	/* complete transfer */
+			else
+				maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2);
+				    /* maximum */
+
+			ptr1 = df->data + le16_to_cpu(zp->z2);
+			    /* start of data */
+			memcpy(ptr, ptr1, maxlen);	/* copy data */
+			rcnt -= maxlen;
+
+			if (rcnt) {	/* rest remaining */
+				ptr += maxlen;
+				ptr1 = df->data;	/* start of buffer */
+				memcpy(ptr, ptr1, rcnt);	/* rest */
+			}
+			df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) |
+			    (MAX_D_FRAMES + 1);	/* next buffer */
+			df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((
+			    le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1));
+			recv_Dchannel(dch);
+		}
+	}
+	return 1;
+}
+
+/*
+ * check for transparent receive data and read max one threshold size if avail
+ */
+int
+hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata)
+{
+	unsigned short	*z1r, *z2r;
+	int		new_z2, fcnt, maxlen;
+	u_char		*ptr, *ptr1;
+
+	z1r = &bz->za[MAX_B_FRAMES].z1;		/* pointer to z reg */
+	z2r = z1r + 1;
+
+	fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r);
+	if (!fcnt)
+		return 0;	/* no data avail */
+
+	if (fcnt <= 0)
+		fcnt += B_FIFO_SIZE;	/* bytes actually buffered */
+	if (fcnt > HFCPCI_BTRANS_THRESHOLD)
+		fcnt = HFCPCI_BTRANS_THRESHOLD;		/* limit size */
+
+	new_z2 = le16_to_cpu(*z2r) + fcnt;	/* new position in fifo */
+	if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+		new_z2 -= B_FIFO_SIZE;	/* buffer wrap */
+
+	bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC);
+	if (bch->rx_skb) {
+		ptr = skb_put(bch->rx_skb, fcnt);
+		if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
+			maxlen = fcnt;	/* complete transfer */
+		else
+			maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r);
+			    /* maximum */
+
+		ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL);
+		    /* start of data */
+		memcpy(ptr, ptr1, maxlen);	/* copy data */
+		fcnt -= maxlen;
+
+		if (fcnt) {	/* rest remaining */
+			ptr += maxlen;
+			ptr1 = bdata;	/* start of buffer */
+			memcpy(ptr, ptr1, fcnt);	/* rest */
+		}
+		recv_Bchannel(bch);
+	} else
+		printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+
+	*z2r = cpu_to_le16(new_z2);		/* new position */
+	return 1;
+}
+
+/*
+ * B-channel main receive routine
+ */
+void
+main_rec_hfcpci(struct bchannel *bch)
+{
+	struct hfc_pci	*hc = bch->hw;
+	int		rcnt, real_fifo;
+	int		receive, count = 5;
+	struct bzfifo	*bz;
+	u_char		*bdata;
+	struct zt	*zp;
+
+
+	if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+		bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+		bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2;
+		real_fifo = 1;
+	} else {
+		bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1;
+		bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1;
+		real_fifo = 0;
+	}
+Begin:
+	count--;
+	if (bz->f1 != bz->f2) {
+		if (bch->debug & DEBUG_HW_BCHANNEL)
+			printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n",
+			    bch->nr, bz->f1, bz->f2);
+		zp = &bz->za[bz->f2];
+
+		rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+		if (rcnt < 0)
+			rcnt += B_FIFO_SIZE;
+		rcnt++;
+		if (bch->debug & DEBUG_HW_BCHANNEL)
+			printk(KERN_DEBUG
+			    "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n",
+			    bch->nr, le16_to_cpu(zp->z1),
+			    le16_to_cpu(zp->z2), rcnt);
+		hfcpci_empty_bfifo(bch, bz, bdata, rcnt);
+		rcnt = bz->f1 - bz->f2;
+		if (rcnt < 0)
+			rcnt += MAX_B_FRAMES + 1;
+		if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) {
+			rcnt = 0;
+			hfcpci_clear_fifo_rx(hc, real_fifo);
+		}
+		hc->hw.last_bfifo_cnt[real_fifo] = rcnt;
+		if (rcnt > 1)
+			receive = 1;
+		else
+			receive = 0;
+	} else if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+		receive = hfcpci_empty_fifo_trans(bch, bz, bdata);
+	else
+		receive = 0;
+	if (count && receive)
+		goto Begin;
+
+}
+
+/*
+ * D-channel send routine
+ */
+static void
+hfcpci_fill_dfifo(struct hfc_pci *hc)
+{
+	struct dchannel	*dch = &hc->dch;
+	int		fcnt;
+	int		count, new_z1, maxlen;
+	struct dfifo	*df;
+	u_char		*src, *dst, new_f1;
+
+	if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO))
+		printk(KERN_DEBUG "%s\n", __func__);
+
+	if (!dch->tx_skb)
+		return;
+	count = dch->tx_skb->len - dch->tx_idx;
+	if (count <= 0)
+		return;
+	df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx;
+
+	if (dch->debug & DEBUG_HW_DFIFO)
+		printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__,
+		    df->f1, df->f2,
+		    le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1));
+	fcnt = df->f1 - df->f2;	/* frame count actually buffered */
+	if (fcnt < 0)
+		fcnt += (MAX_D_FRAMES + 1);	/* if wrap around */
+	if (fcnt > (MAX_D_FRAMES - 1)) {
+		if (dch->debug & DEBUG_HW_DCHANNEL)
+			printk(KERN_DEBUG
+			    "hfcpci_fill_Dfifo more as 14 frames\n");
+#ifdef ERROR_STATISTIC
+		cs->err_tx++;
+#endif
+		return;
+	}
+	/* now determine free bytes in FIFO buffer */
+	maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) -
+	    le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1;
+	if (maxlen <= 0)
+		maxlen += D_FIFO_SIZE;	/* count now contains available bytes */
+
+	if (dch->debug & DEBUG_HW_DCHANNEL)
+		printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n",
+			count, maxlen);
+	if (count > maxlen) {
+		if (dch->debug & DEBUG_HW_DCHANNEL)
+			printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n");
+		return;
+	}
+	new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) &
+	    (D_FIFO_SIZE - 1);
+	new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1);
+	src = dch->tx_skb->data + dch->tx_idx;	/* source pointer */
+	dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
+	maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
+	    /* end fifo */
+	if (maxlen > count)
+		maxlen = count;	/* limit size */
+	memcpy(dst, src, maxlen);	/* first copy */
+
+	count -= maxlen;	/* remaining bytes */
+	if (count) {
+		dst = df->data;	/* start of buffer */
+		src += maxlen;	/* new position */
+		memcpy(dst, src, count);
+	}
+	df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1);
+	    /* for next buffer */
+	df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1);
+	    /* new pos actual buffer */
+	df->f1 = new_f1;	/* next frame */
+	dch->tx_idx = dch->tx_skb->len;
+}
+
+/*
+ * B-channel send routine
+ */
+static void
+hfcpci_fill_fifo(struct bchannel *bch)
+{
+	struct hfc_pci 	*hc = bch->hw;
+	int		maxlen, fcnt;
+	int		count, new_z1;
+	struct bzfifo	*bz;
+	u_char		*bdata;
+	u_char		new_f1, *src, *dst;
+	unsigned short	*z1t, *z2t;
+
+	if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+		printk(KERN_DEBUG "%s\n", __func__);
+	if ((!bch->tx_skb) || bch->tx_skb->len <= 0)
+		return;
+	count = bch->tx_skb->len - bch->tx_idx;
+	if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+		bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+		bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2;
+	} else {
+		bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1;
+		bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1;
+	}
+
+	if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+		z1t = &bz->za[MAX_B_FRAMES].z1;
+		z2t = z1t + 1;
+		if (bch->debug & DEBUG_HW_BCHANNEL)
+			printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) "
+			    "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count,
+			    le16_to_cpu(*z1t), le16_to_cpu(*z2t));
+		fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t);
+		if (fcnt <= 0)
+			fcnt += B_FIFO_SIZE;
+			    /* fcnt contains available bytes in fifo */
+		fcnt = B_FIFO_SIZE - fcnt;
+		    /* remaining bytes to send (bytes in fifo) */
+next_t_frame:
+		count = bch->tx_skb->len - bch->tx_idx;
+		/* maximum fill shall be HFCPCI_BTRANS_MAX */
+		if (count > HFCPCI_BTRANS_MAX - fcnt)
+			count = HFCPCI_BTRANS_MAX - fcnt;
+		if (count <= 0)
+			return;
+		/* data is suitable for fifo */
+		new_z1 = le16_to_cpu(*z1t) + count;
+		    /* new buffer Position */
+		if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+			new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
+		src = bch->tx_skb->data + bch->tx_idx;
+		    /* source pointer */
+		dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL);
+		maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t);
+		    /* end of fifo */
+		if (bch->debug & DEBUG_HW_BFIFO)
+			printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) "
+			    "maxl(%d) nz1(%x) dst(%p)\n",
+			    fcnt, maxlen, new_z1, dst);
+		fcnt += count;
+		bch->tx_idx += count;
+		if (maxlen > count)
+			maxlen = count;		/* limit size */
+		memcpy(dst, src, maxlen);	/* first copy */
+		count -= maxlen;	/* remaining bytes */
+		if (count) {
+			dst = bdata;	/* start of buffer */
+			src += maxlen;	/* new position */
+			memcpy(dst, src, count);
+		}
+		*z1t = cpu_to_le16(new_z1);	/* now send data */
+		if (bch->tx_idx < bch->tx_skb->len)
+			return;
+		/* send confirm, on trans, free on hdlc. */
+		if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+			confirm_Bsend(bch);
+		dev_kfree_skb(bch->tx_skb);
+		if (get_next_bframe(bch))
+			goto next_t_frame;
+		return;
+	}
+	if (bch->debug & DEBUG_HW_BCHANNEL)
+		printk(KERN_DEBUG
+		    "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n",
+		    __func__, bch->nr, bz->f1, bz->f2,
+		    bz->za[bz->f1].z1);
+	fcnt = bz->f1 - bz->f2;	/* frame count actually buffered */
+	if (fcnt < 0)
+		fcnt += (MAX_B_FRAMES + 1);	/* if wrap around */
+	if (fcnt > (MAX_B_FRAMES - 1)) {
+		if (bch->debug & DEBUG_HW_BCHANNEL)
+			printk(KERN_DEBUG
+			    "hfcpci_fill_Bfifo more as 14 frames\n");
+		return;
+	}
+	/* now determine free bytes in FIFO buffer */
+	maxlen = le16_to_cpu(bz->za[bz->f2].z2) -
+	    le16_to_cpu(bz->za[bz->f1].z1) - 1;
+	if (maxlen <= 0)
+		maxlen += B_FIFO_SIZE;	/* count now contains available bytes */
+
+	if (bch->debug & DEBUG_HW_BCHANNEL)
+		printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n",
+			bch->nr, count, maxlen);
+
+	if (maxlen < count) {
+		if (bch->debug & DEBUG_HW_BCHANNEL)
+			printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n");
+		return;
+	}
+	new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count;
+	    /* new buffer Position */
+	if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+		new_z1 -= B_FIFO_SIZE;	/* buffer wrap */
+
+	new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES);
+	src = bch->tx_skb->data + bch->tx_idx;	/* source pointer */
+	dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL);
+	maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1);
+	    /* end fifo */
+	if (maxlen > count)
+		maxlen = count;	/* limit size */
+	memcpy(dst, src, maxlen);	/* first copy */
+
+	count -= maxlen;	/* remaining bytes */
+	if (count) {
+		dst = bdata;	/* start of buffer */
+		src += maxlen;	/* new position */
+		memcpy(dst, src, count);
+	}
+	bz->za[new_f1].z1 = cpu_to_le16(new_z1);	/* for next buffer */
+	bz->f1 = new_f1;	/* next frame */
+	dev_kfree_skb(bch->tx_skb);
+	get_next_bframe(bch);
+}
+
+
+
+/*
+ * handle L1 state changes TE
+ */
+
+static void
+ph_state_te(struct dchannel *dch)
+{
+	if (dch->debug)
+		printk(KERN_DEBUG "%s: TE newstate %x\n",
+			__func__, dch->state);
+	switch (dch->state) {
+	case 0:
+		l1_event(dch->l1, HW_RESET_IND);
+		break;
+	case 3:
+		l1_event(dch->l1, HW_DEACT_IND);
+		break;
+	case 5:
+	case 8:
+		l1_event(dch->l1, ANYSIGNAL);
+		break;
+	case 6:
+		l1_event(dch->l1, INFO2);
+		break;
+	case 7:
+		l1_event(dch->l1, INFO4_P8);
+		break;
+	}
+}
+
+/*
+ * handle L1 state changes NT
+ */
+
+static void
+handle_nt_timer3(struct dchannel *dch) {
+	struct hfc_pci	*hc = dch->hw;
+
+	test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+	hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+	Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+	hc->hw.nt_timer = 0;
+	test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+	if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+		hc->hw.mst_m |= HFCPCI_MASTER;
+	Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+	_queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+	    MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+}
+
+static void
+ph_state_nt(struct dchannel *dch)
+{
+	struct hfc_pci	*hc = dch->hw;
+
+	if (dch->debug)
+		printk(KERN_DEBUG "%s: NT newstate %x\n",
+			__func__, dch->state);
+	switch (dch->state) {
+	case 2:
+		if (hc->hw.nt_timer < 0) {
+			hc->hw.nt_timer = 0;
+			test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+			test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+			hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+			Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+			/* Clear already pending ints */
+			if (Read_hfc(hc, HFCPCI_INT_S1));
+			Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
+			udelay(10);
+			Write_hfc(hc, HFCPCI_STATES, 4);
+			dch->state = 4;
+		} else if (hc->hw.nt_timer == 0) {
+			hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+			Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+			hc->hw.nt_timer = NT_T1_COUNT;
+			hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+			hc->hw.ctmt |= HFCPCI_TIM3_125;
+			Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+				HFCPCI_CLTIMER);
+			test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+			test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+			/* allow G2 -> G3 transition */
+			Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+		} else {
+			Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+		}
+		break;
+	case 1:
+		hc->hw.nt_timer = 0;
+		test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+		test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+		hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+		Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		hc->hw.mst_m &= ~HFCPCI_MASTER;
+		Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+		_queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+		    MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+		break;
+	case 4:
+		hc->hw.nt_timer = 0;
+		test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+		test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+		hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+		Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+		break;
+	case 3:
+		if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) {
+			if (!test_and_clear_bit(FLG_L2_ACTIVATED,
+			    &dch->Flags)) {
+				handle_nt_timer3(dch);
+				break;
+			}
+			test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+			hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+			Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+			hc->hw.nt_timer = NT_T3_COUNT;
+			hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+			hc->hw.ctmt |= HFCPCI_TIM3_125;
+			Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+				HFCPCI_CLTIMER);
+		}
+		break;
+	}
+}
+
+static void
+ph_state(struct dchannel *dch)
+{
+	struct hfc_pci	*hc = dch->hw;
+
+	if (hc->hw.protocol == ISDN_P_NT_S0) {
+		if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) &&
+		    hc->hw.nt_timer < 0)
+			handle_nt_timer3(dch);
+		else
+			ph_state_nt(dch);
+	} else
+		ph_state_te(dch);
+}
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfc_l1callback(struct dchannel *dch, u_int cmd)
+{
+	struct hfc_pci		*hc = dch->hw;
+
+	switch (cmd) {
+	case INFO3_P8:
+	case INFO3_P10:
+		if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+			hc->hw.mst_m |= HFCPCI_MASTER;
+		Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+		break;
+	case HW_RESET_REQ:
+		Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3);
+		/* HFC ST 3 */
+		udelay(6);
+		Write_hfc(hc, HFCPCI_STATES, 3);	/* HFC ST 2 */
+		if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+			hc->hw.mst_m |= HFCPCI_MASTER;
+		Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+		Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE |
+		   HFCPCI_DO_ACTION);
+		l1_event(dch->l1, HW_POWERUP_IND);
+		break;
+	case HW_DEACT_REQ:
+		hc->hw.mst_m &= ~HFCPCI_MASTER;
+		Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+		skb_queue_purge(&dch->squeue);
+		if (dch->tx_skb) {
+			dev_kfree_skb(dch->tx_skb);
+			dch->tx_skb = NULL;
+		}
+		dch->tx_idx = 0;
+		if (dch->rx_skb) {
+			dev_kfree_skb(dch->rx_skb);
+			dch->rx_skb = NULL;
+		}
+		test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+		if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+			del_timer(&dch->timer);
+		break;
+	case HW_POWERUP_REQ:
+		Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION);
+		break;
+	case PH_ACTIVATE_IND:
+		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	case PH_DEACTIVATE_IND:
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+			GFP_ATOMIC);
+		break;
+	default:
+		if (dch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: unknown command %x\n",
+			    __func__, cmd);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * Interrupt handler
+ */
+static inline void
+tx_birq(struct bchannel *bch)
+{
+	if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+		hfcpci_fill_fifo(bch);
+	else {
+		if (bch->tx_skb)
+			dev_kfree_skb(bch->tx_skb);
+		if (get_next_bframe(bch))
+			hfcpci_fill_fifo(bch);
+	}
+}
+
+static inline void
+tx_dirq(struct dchannel *dch)
+{
+	if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len)
+		hfcpci_fill_dfifo(dch->hw);
+	else {
+		if (dch->tx_skb)
+			dev_kfree_skb(dch->tx_skb);
+		if (get_next_dframe(dch))
+			hfcpci_fill_dfifo(dch->hw);
+	}
+}
+
+static irqreturn_t
+hfcpci_int(int intno, void *dev_id)
+{
+	struct hfc_pci	*hc = dev_id;
+	u_char		exval;
+	struct bchannel	*bch;
+	u_char		val, stat;
+
+	spin_lock(&hc->lock);
+	if (!(hc->hw.int_m2 & 0x08)) {
+		spin_unlock(&hc->lock);
+		return IRQ_NONE; /* not initialised */
+	}
+	stat = Read_hfc(hc, HFCPCI_STATUS);
+	if (HFCPCI_ANYINT & stat) {
+		val = Read_hfc(hc, HFCPCI_INT_S1);
+		if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+			printk(KERN_DEBUG
+			    "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val);
+	} else {
+		/* shared */
+		spin_unlock(&hc->lock);
+		return IRQ_NONE;
+	}
+	hc->irqcnt++;
+
+	if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+		printk(KERN_DEBUG "HFC-PCI irq %x\n", val);
+	val &= hc->hw.int_m1;
+	if (val & 0x40) {	/* state machine irq */
+		exval = Read_hfc(hc, HFCPCI_STATES) & 0xf;
+		if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+			printk(KERN_DEBUG "ph_state chg %d->%d\n",
+				hc->dch.state, exval);
+		hc->dch.state = exval;
+		schedule_event(&hc->dch, FLG_PHCHANGE);
+		val &= ~0x40;
+	}
+	if (val & 0x80) {	/* timer irq */
+		if (hc->hw.protocol == ISDN_P_NT_S0) {
+			if ((--hc->hw.nt_timer) < 0)
+				schedule_event(&hc->dch, FLG_PHCHANGE);
+		}
+		val &= ~0x80;
+		Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
+	}
+	if (val & 0x08) {
+		bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+		if (bch)
+			main_rec_hfcpci(bch);
+		else if (hc->dch.debug)
+			printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n");
+	}
+	if (val & 0x10) {
+		bch = Sel_BCS(hc, 2);
+		if (bch)
+			main_rec_hfcpci(bch);
+		else if (hc->dch.debug)
+			printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n");
+	}
+	if (val & 0x01) {
+		bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+		if (bch)
+			tx_birq(bch);
+		else if (hc->dch.debug)
+			printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n");
+	}
+	if (val & 0x02) {
+		bch = Sel_BCS(hc, 2);
+		if (bch)
+			tx_birq(bch);
+		else if (hc->dch.debug)
+			printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n");
+	}
+	if (val & 0x20)
+		receive_dmsg(hc);
+	if (val & 0x04) {	/* dframe transmitted */
+		if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags))
+			del_timer(&hc->dch.timer);
+		tx_dirq(&hc->dch);
+	}
+	spin_unlock(&hc->lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+static void
+hfcpci_dbusy_timer(struct hfc_pci *hc)
+{
+}
+
+/*
+ * activate/deactivate hardware for selected channels and mode
+ */
+static int
+mode_hfcpci(struct bchannel *bch, int bc, int protocol)
+{
+	struct hfc_pci	*hc = bch->hw;
+	int		fifo2;
+	u_char		rx_slot = 0, tx_slot = 0, pcm_mode;
+
+	if (bch->debug & DEBUG_HW_BCHANNEL)
+		printk(KERN_DEBUG
+		    "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n",
+		    bch->state, protocol, bch->nr, bc);
+
+	fifo2 = bc;
+	pcm_mode = (bc>>24) & 0xff;
+	if (pcm_mode) { /* PCM SLOT USE */
+		if (!test_bit(HFC_CFG_PCM, &hc->cfg))
+			printk(KERN_WARNING
+			    "%s: pcm channel id without HFC_CFG_PCM\n",
+			    __func__);
+		rx_slot = (bc>>8) & 0xff;
+		tx_slot = (bc>>16) & 0xff;
+		bc = bc & 0xff;
+	} else if (test_bit(HFC_CFG_PCM, &hc->cfg) &&
+	    (protocol > ISDN_P_NONE))
+		printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n",
+		    __func__);
+	if (hc->chanlimit > 1) {
+		hc->hw.bswapped = 0;	/* B1 and B2 normal mode */
+		hc->hw.sctrl_e &= ~0x80;
+	} else {
+		if (bc & 2) {
+			if (protocol != ISDN_P_NONE) {
+				hc->hw.bswapped = 1; /* B1 and B2 exchanged */
+				hc->hw.sctrl_e |= 0x80;
+			} else {
+				hc->hw.bswapped = 0; /* B1 and B2 normal mode */
+				hc->hw.sctrl_e &= ~0x80;
+			}
+			fifo2 = 1;
+		} else {
+			hc->hw.bswapped = 0;	/* B1 and B2 normal mode */
+			hc->hw.sctrl_e &= ~0x80;
+		}
+	}
+	switch (protocol) {
+	case (-1): /* used for init */
+		bch->state = -1;
+		bch->nr = bc;
+	case (ISDN_P_NONE):
+		if (bch->state == ISDN_P_NONE)
+			return 0;
+		if (bc & 2) {
+			hc->hw.sctrl &= ~SCTRL_B2_ENA;
+			hc->hw.sctrl_r &= ~SCTRL_B2_ENA;
+		} else {
+			hc->hw.sctrl &= ~SCTRL_B1_ENA;
+			hc->hw.sctrl_r &= ~SCTRL_B1_ENA;
+		}
+		if (fifo2 & 2) {
+			hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2;
+			hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS +
+				HFCPCI_INTS_B2REC);
+		} else {
+			hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1;
+			hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS +
+				HFCPCI_INTS_B1REC);
+		}
+#ifdef REVERSE_BITORDER
+		if (bch->nr & 2)
+			hc->hw.cirm &= 0x7f;
+		else
+			hc->hw.cirm &= 0xbf;
+#endif
+		bch->state = ISDN_P_NONE;
+		bch->nr = bc;
+		test_and_clear_bit(FLG_HDLC, &bch->Flags);
+		test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
+		break;
+	case (ISDN_P_B_RAW):
+		bch->state = protocol;
+		bch->nr = bc;
+		hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
+		hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
+		if (bc & 2) {
+			hc->hw.sctrl |= SCTRL_B2_ENA;
+			hc->hw.sctrl_r |= SCTRL_B2_ENA;
+#ifdef REVERSE_BITORDER
+			hc->hw.cirm |= 0x80;
+#endif
+		} else {
+			hc->hw.sctrl |= SCTRL_B1_ENA;
+			hc->hw.sctrl_r |= SCTRL_B1_ENA;
+#ifdef REVERSE_BITORDER
+			hc->hw.cirm |= 0x40;
+#endif
+		}
+		if (fifo2 & 2) {
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
+			hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
+			    HFCPCI_INTS_B2REC);
+			hc->hw.ctmt |= 2;
+			hc->hw.conn &= ~0x18;
+		} else {
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
+			hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
+			    HFCPCI_INTS_B1REC);
+			hc->hw.ctmt |= 1;
+			hc->hw.conn &= ~0x03;
+		}
+		test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
+		break;
+	case (ISDN_P_B_HDLC):
+		bch->state = protocol;
+		bch->nr = bc;
+		hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
+		hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
+		if (bc & 2) {
+			hc->hw.sctrl |= SCTRL_B2_ENA;
+			hc->hw.sctrl_r |= SCTRL_B2_ENA;
+		} else {
+			hc->hw.sctrl |= SCTRL_B1_ENA;
+			hc->hw.sctrl_r |= SCTRL_B1_ENA;
+		}
+		if (fifo2 & 2) {
+			hc->hw.last_bfifo_cnt[1] = 0;
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
+			hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
+			    HFCPCI_INTS_B2REC);
+			hc->hw.ctmt &= ~2;
+			hc->hw.conn &= ~0x18;
+		} else {
+			hc->hw.last_bfifo_cnt[0] = 0;
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
+			hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
+			    HFCPCI_INTS_B1REC);
+			hc->hw.ctmt &= ~1;
+			hc->hw.conn &= ~0x03;
+		}
+		test_and_set_bit(FLG_HDLC, &bch->Flags);
+		break;
+	default:
+		printk(KERN_DEBUG "prot not known %x\n", protocol);
+		return -ENOPROTOOPT;
+	}
+	if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
+		if ((protocol == ISDN_P_NONE) ||
+			(protocol == -1)) {	/* init case */
+			rx_slot = 0;
+			tx_slot = 0;
+		} else {
+			if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
+				rx_slot |= 0xC0;
+				tx_slot |= 0xC0;
+			} else {
+				rx_slot |= 0x80;
+				tx_slot |= 0x80;
+			}
+		}
+		if (bc & 2) {
+			hc->hw.conn &= 0xc7;
+			hc->hw.conn |= 0x08;
+			printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n",
+				__func__, tx_slot);
+			printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n",
+				__func__, rx_slot);
+			Write_hfc(hc, HFCPCI_B2_SSL, tx_slot);
+			Write_hfc(hc, HFCPCI_B2_RSL, rx_slot);
+		} else {
+			hc->hw.conn &= 0xf8;
+			hc->hw.conn |= 0x01;
+			printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n",
+				__func__, tx_slot);
+			printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n",
+				__func__, rx_slot);
+			Write_hfc(hc, HFCPCI_B1_SSL, tx_slot);
+			Write_hfc(hc, HFCPCI_B1_RSL, rx_slot);
+		}
+	}
+	Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
+	Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+	Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+	Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
+	Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+	Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+	Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+#ifdef REVERSE_BITORDER
+	Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+#endif
+	return 0;
+}
+
+static int
+set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan)
+{
+	struct hfc_pci	*hc = bch->hw;
+
+	if (bch->debug & DEBUG_HW_BCHANNEL)
+		printk(KERN_DEBUG
+		    "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n",
+		    bch->state, protocol, bch->nr, chan);
+	if (bch->nr != chan) {
+		printk(KERN_DEBUG
+		    "HFCPCI rxtest wrong channel parameter %x/%x\n",
+		    bch->nr, chan);
+		return -EINVAL;
+	}
+	switch (protocol) {
+	case (ISDN_P_B_RAW):
+		bch->state = protocol;
+		hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0);
+		if (chan & 2) {
+			hc->hw.sctrl_r |= SCTRL_B2_ENA;
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
+			hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
+			hc->hw.ctmt |= 2;
+			hc->hw.conn &= ~0x18;
+#ifdef REVERSE_BITORDER
+			hc->hw.cirm |= 0x80;
+#endif
+		} else {
+			hc->hw.sctrl_r |= SCTRL_B1_ENA;
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
+			hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
+			hc->hw.ctmt |= 1;
+			hc->hw.conn &= ~0x03;
+#ifdef REVERSE_BITORDER
+			hc->hw.cirm |= 0x40;
+#endif
+		}
+		break;
+	case (ISDN_P_B_HDLC):
+		bch->state = protocol;
+		hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0);
+		if (chan & 2) {
+			hc->hw.sctrl_r |= SCTRL_B2_ENA;
+			hc->hw.last_bfifo_cnt[1] = 0;
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
+			hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
+			hc->hw.ctmt &= ~2;
+			hc->hw.conn &= ~0x18;
+		} else {
+			hc->hw.sctrl_r |= SCTRL_B1_ENA;
+			hc->hw.last_bfifo_cnt[0] = 0;
+			hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
+			hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
+			hc->hw.ctmt &= ~1;
+			hc->hw.conn &= ~0x03;
+		}
+		break;
+	default:
+		printk(KERN_DEBUG "prot not known %x\n", protocol);
+		return -ENOPROTOOPT;
+	}
+	Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+	Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+	Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+	Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+	Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+#ifdef REVERSE_BITORDER
+	Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+#endif
+	return 0;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+	struct hfc_pci	*hc = bch->hw;
+	u_long		flags;
+
+	spin_lock_irqsave(&hc->lock, flags);
+	if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+		dev_kfree_skb(bch->next_skb);
+		bch->next_skb = NULL;
+	}
+	if (bch->tx_skb) {
+		dev_kfree_skb(bch->tx_skb);
+		bch->tx_skb = NULL;
+	}
+	bch->tx_idx = 0;
+	if (bch->rx_skb) {
+		dev_kfree_skb(bch->rx_skb);
+		bch->rx_skb = NULL;
+	}
+	mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+	test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+	test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+	spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+/*
+ * Layer 1 B-channel hardware access
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int			ret = 0;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = 0;
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+static int
+hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct bchannel	*bch = container_of(ch, struct bchannel, ch);
+	struct hfc_pci	*hc = bch->hw;
+	int		ret = -EINVAL;
+	u_long		flags;
+
+	if (bch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg);
+	switch (cmd) {
+	case HW_TESTRX_RAW:
+		spin_lock_irqsave(&hc->lock, flags);
+		ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	case HW_TESTRX_HDLC:
+		spin_lock_irqsave(&hc->lock, flags);
+		ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	case HW_TESTRX_OFF:
+		spin_lock_irqsave(&hc->lock, flags);
+		mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		ret = 0;
+		break;
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		if (test_bit(FLG_ACTIVE, &bch->Flags))
+			deactivate_bchannel(bch);
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(THIS_MODULE);
+		ret = 0;
+		break;
+	case CONTROL_CHANNEL:
+		ret = channel_bctrl(bch, arg);
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown prim(%x)\n",
+			__func__, cmd);
+	}
+	return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Dchannel data
+ */
+static int
+hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct hfc_pci		*hc = dch->hw;
+	int			ret = -EINVAL;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	unsigned int		id;
+	u_long			flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(&hc->lock, flags);
+		ret = dchannel_senddata(dch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			hfcpci_fill_dfifo(dch->hw);
+			ret = 0;
+			spin_unlock_irqrestore(&hc->lock, flags);
+			queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(&hc->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		spin_lock_irqsave(&hc->lock, flags);
+		if (hc->hw.protocol == ISDN_P_NT_S0) {
+			ret = 0;
+			if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+				hc->hw.mst_m |= HFCPCI_MASTER;
+			Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+			if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+				spin_unlock_irqrestore(&hc->lock, flags);
+				_queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+				    MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+				break;
+			}
+			test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags);
+			Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE |
+			    HFCPCI_DO_ACTION | 1);
+		} else
+			ret = l1_event(dch->l1, hh->prim);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	case PH_DEACTIVATE_REQ:
+		test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+		spin_lock_irqsave(&hc->lock, flags);
+		if (hc->hw.protocol == ISDN_P_NT_S0) {
+			/* prepare deactivation */
+			Write_hfc(hc, HFCPCI_STATES, 0x40);
+			skb_queue_purge(&dch->squeue);
+			if (dch->tx_skb) {
+				dev_kfree_skb(dch->tx_skb);
+				dch->tx_skb = NULL;
+			}
+			dch->tx_idx = 0;
+			if (dch->rx_skb) {
+				dev_kfree_skb(dch->rx_skb);
+				dch->rx_skb = NULL;
+			}
+			test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+			if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+				del_timer(&dch->timer);
+#ifdef FIXME
+			if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+				dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+			hc->hw.mst_m &= ~HFCPCI_MASTER;
+			Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+			ret = 0;
+		} else {
+			ret = l1_event(dch->l1, hh->prim);
+		}
+		spin_unlock_irqrestore(&hc->lock, flags);
+		break;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Bchannel data
+ */
+static int
+hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel		*bch = container_of(ch, struct bchannel, ch);
+	struct hfc_pci		*hc = bch->hw;
+	int			ret = -EINVAL;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	unsigned int		id;
+	u_long			flags;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		spin_lock_irqsave(&hc->lock, flags);
+		ret = bchannel_senddata(bch, skb);
+		if (ret > 0) { /* direct TX */
+			id = hh->id; /* skb can be freed */
+			hfcpci_fill_fifo(bch);
+			ret = 0;
+			spin_unlock_irqrestore(&hc->lock, flags);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+		} else
+			spin_unlock_irqrestore(&hc->lock, flags);
+		return ret;
+	case PH_ACTIVATE_REQ:
+		spin_lock_irqsave(&hc->lock, flags);
+		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+			ret = mode_hfcpci(bch, bch->nr, ch->protocol);
+		else
+			ret = 0;
+		spin_unlock_irqrestore(&hc->lock, flags);
+		if (!ret)
+			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+				NULL, GFP_KERNEL);
+		break;
+	case PH_DEACTIVATE_REQ:
+		deactivate_bchannel(bch);
+		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_KERNEL);
+		ret = 0;
+		break;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+/*
+ * called for card init message
+ */
+
+void
+inithfcpci(struct hfc_pci *hc)
+{
+	printk(KERN_DEBUG "inithfcpci: entered\n");
+	hc->dch.timer.function = (void *) hfcpci_dbusy_timer;
+	hc->dch.timer.data = (long) &hc->dch;
+	init_timer(&hc->dch.timer);
+	hc->chanlimit = 2;
+	mode_hfcpci(&hc->bch[0], 1, -1);
+	mode_hfcpci(&hc->bch[1], 2, -1);
+}
+
+
+static int
+init_card(struct hfc_pci *hc)
+{
+	int	cnt = 3;
+	u_long	flags;
+
+	printk(KERN_DEBUG "init_card: entered\n");
+
+
+	spin_lock_irqsave(&hc->lock, flags);
+	disable_hwirq(hc);
+	spin_unlock_irqrestore(&hc->lock, flags);
+	if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) {
+		printk(KERN_WARNING
+		    "mISDN: couldn't get interrupt %d\n", hc->irq);
+		return -EIO;
+	}
+	spin_lock_irqsave(&hc->lock, flags);
+	reset_hfcpci(hc);
+	while (cnt) {
+		inithfcpci(hc);
+		/*
+		 * Finally enable IRQ output
+		 * this is only allowed, if an IRQ routine is allready
+		 * established for this HFC, so don't do that earlier
+		 */
+		enable_hwirq(hc);
+		spin_unlock_irqrestore(&hc->lock, flags);
+		/* Timeout 80ms */
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout((80*HZ)/1000);
+		printk(KERN_INFO "HFC PCI: IRQ %d count %d\n",
+			hc->irq, hc->irqcnt);
+		/* now switch timer interrupt off */
+		spin_lock_irqsave(&hc->lock, flags);
+		hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+		Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+		/* reinit mode reg */
+		Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+		if (!hc->irqcnt) {
+			printk(KERN_WARNING
+			    "HFC PCI: IRQ(%d) getting no interrupts "
+			    "during init %d\n", hc->irq, 4 - cnt);
+			if (cnt == 1) {
+				spin_unlock_irqrestore(&hc->lock, flags);
+				return -EIO;
+			} else {
+				reset_hfcpci(hc);
+				cnt--;
+			}
+		} else {
+			spin_unlock_irqrestore(&hc->lock, flags);
+			hc->initdone = 1;
+			return 0;
+		}
+	}
+	disable_hwirq(hc);
+	spin_unlock_irqrestore(&hc->lock, flags);
+	free_irq(hc->irq, hc);
+	return -EIO;
+}
+
+static int
+channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+	u_char	slot;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT |
+		    MISDN_CTRL_DISCONNECT;
+		break;
+	case MISDN_CTRL_LOOP:
+		/* channel 0 disabled loop */
+		if (cq->channel < 0 || cq->channel > 2) {
+			ret = -EINVAL;
+			break;
+		}
+		if (cq->channel & 1) {
+			if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+				slot = 0xC0;
+			else
+				slot = 0x80;
+			printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n",
+			    __func__, slot);
+			Write_hfc(hc, HFCPCI_B1_SSL, slot);
+			Write_hfc(hc, HFCPCI_B1_RSL, slot);
+			hc->hw.conn = (hc->hw.conn & ~7) | 6;
+			Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+		}
+		if (cq->channel & 2) {
+			if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+				slot = 0xC1;
+			else
+				slot = 0x81;
+			printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n",
+			    __func__, slot);
+			Write_hfc(hc, HFCPCI_B2_SSL, slot);
+			Write_hfc(hc, HFCPCI_B2_RSL, slot);
+			hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30;
+			Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+		}
+		if (cq->channel & 3)
+			hc->hw.trm |= 0x80;	/* enable IOM-loop */
+		else {
+			hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+			Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+			hc->hw.trm &= 0x7f;	/* disable IOM-loop */
+		}
+		Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+		break;
+	case MISDN_CTRL_CONNECT:
+		if (cq->channel == cq->p1) {
+			ret = -EINVAL;
+			break;
+		}
+		if (cq->channel < 1 || cq->channel > 2 ||
+		    cq->p1 < 1 || cq->p1 > 2) {
+			ret = -EINVAL;
+			break;
+		}
+		if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+			slot = 0xC0;
+		else
+			slot = 0x80;
+		printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n",
+		    __func__, slot);
+		Write_hfc(hc, HFCPCI_B1_SSL, slot);
+		Write_hfc(hc, HFCPCI_B2_RSL, slot);
+		if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+			slot = 0xC1;
+		else
+			slot = 0x81;
+		printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n",
+		    __func__, slot);
+		Write_hfc(hc, HFCPCI_B2_SSL, slot);
+		Write_hfc(hc, HFCPCI_B1_RSL, slot);
+		hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36;
+		Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+		hc->hw.trm |= 0x80;
+		Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+		break;
+	case MISDN_CTRL_DISCONNECT:
+		hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+		Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+		hc->hw.trm &= 0x7f;	/* disable IOM-loop */
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown Op %x\n",
+		    __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch,
+    struct channel_req *rq)
+{
+	int err = 0;
+
+	if (debug & DEBUG_HW_OPEN)
+		printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+		    hc->dch.dev.id, __builtin_return_address(0));
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	if (!hc->initdone) {
+		if (rq->protocol == ISDN_P_TE_S0) {
+			err = create_l1(&hc->dch, hfc_l1callback);
+			if (err)
+				return err;
+		}
+		hc->hw.protocol = rq->protocol;
+		ch->protocol = rq->protocol;
+		err = init_card(hc);
+		if (err)
+			return err;
+	} else {
+		if (rq->protocol != ch->protocol) {
+			if (hc->hw.protocol == ISDN_P_TE_S0)
+				l1_event(hc->dch.l1, CLOSE_CHANNEL);
+			hc->hw.protocol = rq->protocol;
+			ch->protocol = rq->protocol;
+			hfcpci_setmode(hc);
+		}
+	}
+
+	if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) ||
+	    ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) {
+		_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+		    0, NULL, GFP_KERNEL);
+	}
+	rq->ch = ch;
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s:cannot get module\n", __func__);
+	return 0;
+}
+
+static int
+open_bchannel(struct hfc_pci *hc, struct channel_req *rq)
+{
+	struct bchannel		*bch;
+
+	if (rq->adr.channel > 2)
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	bch = &hc->bch[rq->adr.channel - 1];
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch; /* TODO: E-channel */
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s:cannot get module\n", __func__);
+	return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct hfc_pci		*hc = dch->hw;
+	struct channel_req	*rq;
+	int			err = 0;
+
+	if (dch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: cmd:%x %p\n",
+		    __func__, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		if (rq->adr.channel == 0)
+			err = open_dchannel(hc, ch, rq);
+		else
+			err = open_bchannel(hc, rq);
+		break;
+	case CLOSE_CHANNEL:
+		if (debug & DEBUG_HW_OPEN)
+			printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+			    __func__, hc->dch.dev.id,
+			    __builtin_return_address(0));
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_ctrl(hc, arg);
+		break;
+	default:
+		if (dch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: unknown command %x\n",
+			    __func__, cmd);
+		return -EINVAL;
+	}
+	return err;
+}
+
+static int
+setup_hw(struct hfc_pci *hc)
+{
+	void	*buffer;
+
+	printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision);
+	hc->hw.cirm = 0;
+	hc->dch.state = 0;
+	pci_set_master(hc->pdev);
+	if (!hc->irq) {
+		printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
+		return 1;
+	}
+	hc->hw.pci_io = (char *)(ulong)hc->pdev->resource[1].start;
+
+	if (!hc->hw.pci_io) {
+		printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
+		return 1;
+	}
+	/* Allocate memory for FIFOS */
+	/* the memory needs to be on a 32k boundary within the first 4G */
+	pci_set_dma_mask(hc->pdev, 0xFFFF8000);
+	buffer = pci_alloc_consistent(hc->pdev, 0x8000, &hc->hw.dmahandle);
+	/* We silently assume the address is okay if nonzero */
+	if (!buffer) {
+		printk(KERN_WARNING
+		    "HFC-PCI: Error allocating memory for FIFO!\n");
+		return 1;
+	}
+	hc->hw.fifos = buffer;
+	pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle);
+	hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256);
+	printk(KERN_INFO
+		"HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n",
+		(u_long) hc->hw.pci_io, (u_long) hc->hw.fifos,
+		(u_long) virt_to_bus(hc->hw.fifos),
+		hc->irq, HZ);
+	/* enable memory mapped ports, disable busmaster */
+	pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+	hc->hw.int_m2 = 0;
+	disable_hwirq(hc);
+	hc->hw.int_m1 = 0;
+	Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+	/* At this point the needed PCI config is done */
+	/* fifos are still not enabled */
+	hc->hw.timer.function = (void *) hfcpci_Timer;
+	hc->hw.timer.data = (long) hc;
+	init_timer(&hc->hw.timer);
+	/* default PCM master */
+	test_and_set_bit(HFC_CFG_MASTER, &hc->cfg);
+	return 0;
+}
+
+static void
+release_card(struct hfc_pci *hc) {
+	u_long	flags;
+
+	spin_lock_irqsave(&hc->lock, flags);
+	hc->hw.int_m2 = 0; /* interrupt output off ! */
+	disable_hwirq(hc);
+	mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE);
+	mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE);
+	if (hc->dch.timer.function != NULL) {
+		del_timer(&hc->dch.timer);
+		hc->dch.timer.function = NULL;
+	}
+	spin_unlock_irqrestore(&hc->lock, flags);
+	if (hc->hw.protocol == ISDN_P_TE_S0)
+		l1_event(hc->dch.l1, CLOSE_CHANNEL);
+	if (hc->initdone)
+		free_irq(hc->irq, hc);
+	release_io_hfcpci(hc); /* must release after free_irq! */
+	mISDN_unregister_device(&hc->dch.dev);
+	mISDN_freebchannel(&hc->bch[1]);
+	mISDN_freebchannel(&hc->bch[0]);
+	mISDN_freedchannel(&hc->dch);
+	list_del(&hc->list);
+	pci_set_drvdata(hc->pdev, NULL);
+	kfree(hc);
+}
+
+static int
+setup_card(struct hfc_pci *card)
+{
+	int		err = -EINVAL;
+	u_int		i;
+	u_long		flags;
+	char		name[MISDN_MAX_IDLEN];
+
+	if (HFC_cnt >= MAX_CARDS)
+		return -EINVAL; /* maybe better value */
+
+	card->dch.debug = debug;
+	spin_lock_init(&card->lock);
+	mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state);
+	card->dch.hw = card;
+	card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+	card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+	    (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	card->dch.dev.D.send = hfcpci_l2l1D;
+	card->dch.dev.D.ctrl = hfc_dctrl;
+	card->dch.dev.nrbchan = 2;
+	for (i = 0; i < 2; i++) {
+		card->bch[i].nr = i + 1;
+		test_and_set_bit(i + 1, &card->dch.dev.channelmap[0]);
+		card->bch[i].debug = debug;
+		mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+		card->bch[i].hw = card;
+		card->bch[i].ch.send = hfcpci_l2l1B;
+		card->bch[i].ch.ctrl = hfc_bctrl;
+		card->bch[i].ch.nr = i + 1;
+		list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels);
+	}
+	err = setup_hw(card);
+	if (err)
+		goto error;
+	snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1);
+	err = mISDN_register_device(&card->dch.dev, name);
+	if (err)
+		goto error;
+	HFC_cnt++;
+	write_lock_irqsave(&HFClock, flags);
+	list_add_tail(&card->list, &HFClist);
+	write_unlock_irqrestore(&HFClock, flags);
+	printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt);
+	return 0;
+error:
+	mISDN_freebchannel(&card->bch[1]);
+	mISDN_freebchannel(&card->bch[0]);
+	mISDN_freedchannel(&card->dch);
+	kfree(card);
+	return err;
+}
+
+/* private data in the PCI devices list */
+struct _hfc_map {
+	u_int	subtype;
+	u_int	flag;
+	char	*name;
+};
+
+static const struct _hfc_map hfc_map[] =
+{
+	{HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"},
+	{HFC_CCD_B000, 0, "Billion B000"},
+	{HFC_CCD_B006, 0, "Billion B006"},
+	{HFC_CCD_B007, 0, "Billion B007"},
+	{HFC_CCD_B008, 0, "Billion B008"},
+	{HFC_CCD_B009, 0, "Billion B009"},
+	{HFC_CCD_B00A, 0, "Billion B00A"},
+	{HFC_CCD_B00B, 0, "Billion B00B"},
+	{HFC_CCD_B00C, 0, "Billion B00C"},
+	{HFC_CCD_B100, 0, "Seyeon B100"},
+	{HFC_CCD_B700, 0, "Primux II S0 B700"},
+	{HFC_CCD_B701, 0, "Primux II S0 NT B701"},
+	{HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"},
+	{HFC_ASUS_0675, 0, "Asuscom/Askey 675"},
+	{HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"},
+	{HFC_BERKOM_A1T, 0, "German telekom A1T"},
+	{HFC_ANIGMA_MC145575, 0, "Motorola MC145575"},
+	{HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"},
+	{HFC_DIGI_DF_M_IOM2_E, 0,
+	    "Digi International DataFire Micro V IOM2 (Europe)"},
+	{HFC_DIGI_DF_M_E, 0,
+	    "Digi International DataFire Micro V (Europe)"},
+	{HFC_DIGI_DF_M_IOM2_A, 0,
+	    "Digi International DataFire Micro V IOM2 (North America)"},
+	{HFC_DIGI_DF_M_A, 0,
+	    "Digi International DataFire Micro V (North America)"},
+	{HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"},
+	{},
+};
+
+static struct pci_device_id hfc_ids[] =
+{
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[0]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[1]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[2]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[3]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[4]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[5]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[6]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[7]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[8]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[9]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[10]},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[11]},
+	{PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[12]},
+	{PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[13]},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[14]},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[15]},
+	{PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[16]},
+	{PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[17]},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[18]},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[19]},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[20]},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[21]},
+	{PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[22]},
+	{},
+};
+
+static int __devinit
+hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int		err = -ENOMEM;
+	struct hfc_pci	*card;
+	struct _hfc_map	*m = (struct _hfc_map *)ent->driver_data;
+
+	card = kzalloc(sizeof(struct hfc_pci), GFP_ATOMIC);
+	if (!card) {
+		printk(KERN_ERR "No kmem for HFC card\n");
+		return err;
+	}
+	card->pdev = pdev;
+	card->subtype = m->subtype;
+	err = pci_enable_device(pdev);
+	if (err) {
+		kfree(card);
+		return err;
+	}
+
+	printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n",
+	       m->name, pci_name(pdev));
+
+	card->irq = pdev->irq;
+	pci_set_drvdata(pdev, card);
+	err = setup_card(card);
+	if (err)
+		pci_set_drvdata(pdev, NULL);
+	return err;
+}
+
+static void __devexit
+hfc_remove_pci(struct pci_dev *pdev)
+{
+	struct hfc_pci	*card = pci_get_drvdata(pdev);
+	u_long		flags;
+
+	if (card) {
+		write_lock_irqsave(&HFClock, flags);
+		release_card(card);
+		write_unlock_irqrestore(&HFClock, flags);
+	} else
+		if (debug)
+			printk(KERN_WARNING "%s: drvdata allready removed\n",
+			    __func__);
+}
+
+
+static struct pci_driver hfc_driver = {
+	.name = "hfcpci",
+	.probe = hfc_probe,
+	.remove = __devexit_p(hfc_remove_pci),
+	.id_table = hfc_ids,
+};
+
+static int __init
+HFC_init(void)
+{
+	int		err;
+
+	err = pci_register_driver(&hfc_driver);
+	return err;
+}
+
+static void __exit
+HFC_cleanup(void)
+{
+	struct hfc_pci	*card, *next;
+
+	list_for_each_entry_safe(card, next, &HFClist, list) {
+		release_card(card);
+	}
+	pci_unregister_driver(&hfc_driver);
+}
+
+module_init(HFC_init);
+module_exit(HFC_cleanup);
diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig
new file mode 100644
index 0000000..4938355
--- /dev/null
+++ b/drivers/isdn/mISDN/Kconfig
@@ -0,0 +1,44 @@
+#
+# modularer ISDN driver
+#
+
+menuconfig MISDN
+	tristate "Modular ISDN driver"
+	help
+	  Enable support for the modular ISDN driver.
+
+if MISDN != n
+
+config MISDN_DSP
+	tristate "Digital Audio Processing of transparent data"
+	depends on MISDN
+	help
+	  Enable support for digital audio processing capability.
+	  This module may be used for special applications that require
+	  cross connecting of bchannels, conferencing, dtmf decoding
+	  echo cancelation, tone generation, and Blowfish encryption and
+	  decryption.
+	  It may use hardware features if available.
+	  E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu
+	  and get more informations about this module and it's usage.
+	  If unsure, say 'N'.
+
+config MISDN_L1OIP
+	tristate "ISDN over IP tunnel"
+	depends on MISDN
+	help
+	  Enable support for ISDN over IP tunnel.
+
+	  It features:
+	    - dynamic IP exchange, if one or both peers have dynamic IPs
+	    - BRI (S0) and PRI (S2M) interface
+	    - layer 1 control via network keepalive frames
+	    - direct tunneling of physical interface via IP
+
+	  NOTE: This protocol is called 'Layer 1 over IP' and is not
+	  compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is
+	  provided in the source code.
+
+source "drivers/isdn/hardware/mISDN/Kconfig"
+
+endif #MISDN
diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile
new file mode 100644
index 0000000..1cb5e63
--- /dev/null
+++ b/drivers/isdn/mISDN/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the modular ISDN driver
+#
+
+obj-$(CONFIG_MISDN) += mISDN_core.o
+obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o
+obj-$(CONFIG_MISDN_L1OIP) += l1oip.o
+
+# multi objects
+
+mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o
+mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o
+l1oip-objs := l1oip_core.o l1oip_codec.o
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
new file mode 100644
index 0000000..3306817
--- /dev/null
+++ b/drivers/isdn/mISDN/core.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static u_int debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+static LIST_HEAD(devices);
+DEFINE_RWLOCK(device_lock);
+static u64		device_ids;
+#define MAX_DEVICE_ID	63
+
+static LIST_HEAD(Bprotocols);
+DEFINE_RWLOCK(bp_lock);
+
+struct mISDNdevice
+*get_mdevice(u_int id)
+{
+	struct mISDNdevice	*dev;
+
+	read_lock(&device_lock);
+	list_for_each_entry(dev, &devices, D.list)
+		if (dev->id == id) {
+			read_unlock(&device_lock);
+			return dev;
+		}
+	read_unlock(&device_lock);
+	return NULL;
+}
+
+int
+get_mdevice_count(void)
+{
+	struct mISDNdevice	*dev;
+	int			cnt = 0;
+
+	read_lock(&device_lock);
+	list_for_each_entry(dev, &devices, D.list)
+		cnt++;
+	read_unlock(&device_lock);
+	return cnt;
+}
+
+static int
+get_free_devid(void)
+{
+	u_int	i;
+
+	for (i = 0; i <= MAX_DEVICE_ID; i++)
+		if (!test_and_set_bit(i, (u_long *)&device_ids))
+			return i;
+	return -1;
+}
+
+int
+mISDN_register_device(struct mISDNdevice *dev, char *name)
+{
+	u_long	flags;
+	int	err;
+
+	dev->id = get_free_devid();
+	if (dev->id < 0)
+		return -EBUSY;
+	if (name && name[0])
+		strcpy(dev->name, name);
+	else
+		sprintf(dev->name, "mISDN%d", dev->id);
+	if (debug & DEBUG_CORE)
+		printk(KERN_DEBUG "mISDN_register %s %d\n",
+			dev->name, dev->id);
+	err = create_stack(dev);
+	if (err)
+		return err;
+	write_lock_irqsave(&device_lock, flags);
+	list_add_tail(&dev->D.list, &devices);
+	write_unlock_irqrestore(&device_lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL(mISDN_register_device);
+
+void
+mISDN_unregister_device(struct mISDNdevice *dev) {
+	u_long	flags;
+
+	if (debug & DEBUG_CORE)
+		printk(KERN_DEBUG "mISDN_unregister %s %d\n",
+			dev->name, dev->id);
+	write_lock_irqsave(&device_lock, flags);
+	list_del(&dev->D.list);
+	write_unlock_irqrestore(&device_lock, flags);
+	test_and_clear_bit(dev->id, (u_long *)&device_ids);
+	delete_stack(dev);
+}
+EXPORT_SYMBOL(mISDN_unregister_device);
+
+u_int
+get_all_Bprotocols(void)
+{
+	struct Bprotocol	*bp;
+	u_int	m = 0;
+
+	read_lock(&bp_lock);
+	list_for_each_entry(bp, &Bprotocols, list)
+		m |= bp->Bprotocols;
+	read_unlock(&bp_lock);
+	return m;
+}
+
+struct Bprotocol *
+get_Bprotocol4mask(u_int m)
+{
+	struct Bprotocol	*bp;
+
+	read_lock(&bp_lock);
+	list_for_each_entry(bp, &Bprotocols, list)
+		if (bp->Bprotocols & m) {
+			read_unlock(&bp_lock);
+			return bp;
+		}
+	read_unlock(&bp_lock);
+	return NULL;
+}
+
+struct Bprotocol *
+get_Bprotocol4id(u_int id)
+{
+	u_int	m;
+
+	if (id < ISDN_P_B_START || id > 63) {
+		printk(KERN_WARNING "%s id not in range  %d\n",
+		    __func__, id);
+		return NULL;
+	}
+	m = 1 << (id & ISDN_P_B_MASK);
+	return get_Bprotocol4mask(m);
+}
+
+int
+mISDN_register_Bprotocol(struct Bprotocol *bp)
+{
+	u_long			flags;
+	struct Bprotocol	*old;
+
+	if (debug & DEBUG_CORE)
+		printk(KERN_DEBUG "%s: %s/%x\n", __func__,
+		    bp->name, bp->Bprotocols);
+	old = get_Bprotocol4mask(bp->Bprotocols);
+	if (old) {
+		printk(KERN_WARNING
+		    "register duplicate protocol old %s/%x new %s/%x\n",
+		    old->name, old->Bprotocols, bp->name, bp->Bprotocols);
+		return -EBUSY;
+	}
+	write_lock_irqsave(&bp_lock, flags);
+	list_add_tail(&bp->list, &Bprotocols);
+	write_unlock_irqrestore(&bp_lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL(mISDN_register_Bprotocol);
+
+void
+mISDN_unregister_Bprotocol(struct Bprotocol *bp)
+{
+	u_long	flags;
+
+	if (debug & DEBUG_CORE)
+		printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
+			bp->Bprotocols);
+	write_lock_irqsave(&bp_lock, flags);
+	list_del(&bp->list);
+	write_unlock_irqrestore(&bp_lock, flags);
+}
+EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
+
+int
+mISDNInit(void)
+{
+	int	err;
+
+	printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
+		MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
+	mISDN_initstack(&debug);
+	err = mISDN_inittimer(&debug);
+	if (err)
+		goto error;
+	err = l1_init(&debug);
+	if (err) {
+		mISDN_timer_cleanup();
+		goto error;
+	}
+	err = Isdnl2_Init(&debug);
+	if (err) {
+		mISDN_timer_cleanup();
+		l1_cleanup();
+		goto error;
+	}
+	err = misdn_sock_init(&debug);
+	if (err) {
+		mISDN_timer_cleanup();
+		l1_cleanup();
+		Isdnl2_cleanup();
+	}
+error:
+	return err;
+}
+
+void mISDN_cleanup(void)
+{
+	misdn_sock_cleanup();
+	mISDN_timer_cleanup();
+	l1_cleanup();
+	Isdnl2_cleanup();
+
+	if (!list_empty(&devices))
+		printk(KERN_ERR "%s devices still registered\n", __func__);
+
+	if (!list_empty(&Bprotocols))
+		printk(KERN_ERR "%s Bprotocols still registered\n", __func__);
+	printk(KERN_DEBUG "mISDNcore unloaded\n");
+}
+
+module_init(mISDNInit);
+module_exit(mISDN_cleanup);
+
diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h
new file mode 100644
index 0000000..7da7233
--- /dev/null
+++ b/drivers/isdn/mISDN/core.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef mISDN_CORE_H
+#define mISDN_CORE_H
+
+extern struct mISDNdevice	*get_mdevice(u_int);
+extern int			get_mdevice_count(void);
+
+/* stack status flag */
+#define mISDN_STACK_ACTION_MASK		0x0000ffff
+#define mISDN_STACK_COMMAND_MASK	0x000f0000
+#define mISDN_STACK_STATUS_MASK		0xfff00000
+/* action bits 0-15 */
+#define mISDN_STACK_WORK	0
+#define mISDN_STACK_SETUP	1
+#define mISDN_STACK_CLEARING	2
+#define mISDN_STACK_RESTART	3
+#define mISDN_STACK_WAKEUP	4
+#define mISDN_STACK_ABORT	15
+/* command bits 16-19 */
+#define mISDN_STACK_STOPPED	16
+#define mISDN_STACK_INIT	17
+#define mISDN_STACK_THREADSTART	18
+/* status bits 20-31 */
+#define mISDN_STACK_BCHANNEL	20
+#define mISDN_STACK_ACTIVE      29
+#define mISDN_STACK_RUNNING     30
+#define mISDN_STACK_KILLED      31
+
+
+/* manager options */
+#define MGR_OPT_USER		24
+#define MGR_OPT_NETWORK		25
+
+extern int	connect_Bstack(struct mISDNdevice *, struct mISDNchannel *,
+			u_int, struct sockaddr_mISDN *);
+extern int	connect_layer1(struct mISDNdevice *, struct mISDNchannel *,
+			u_int, struct sockaddr_mISDN *);
+extern int	create_l2entity(struct mISDNdevice *, struct mISDNchannel *,
+			u_int, struct sockaddr_mISDN *);
+
+extern int	create_stack(struct mISDNdevice *);
+extern int	create_teimanager(struct mISDNdevice *);
+extern void	delete_teimanager(struct mISDNchannel *);
+extern void	delete_channel(struct mISDNchannel *);
+extern void	delete_stack(struct mISDNdevice *);
+extern void	mISDN_initstack(u_int *);
+extern int      misdn_sock_init(u_int *);
+extern void     misdn_sock_cleanup(void);
+extern void	add_layer2(struct mISDNchannel *, struct mISDNstack *);
+extern void	__add_layer2(struct mISDNchannel *, struct mISDNstack *);
+
+extern u_int		get_all_Bprotocols(void);
+struct Bprotocol	*get_Bprotocol4mask(u_int);
+struct Bprotocol	*get_Bprotocol4id(u_int);
+
+extern int	mISDN_inittimer(u_int *);
+extern void	mISDN_timer_cleanup(void);
+
+extern int	l1_init(u_int *);
+extern void	l1_cleanup(void);
+extern int 	Isdnl2_Init(u_int *);
+extern void	Isdnl2_cleanup(void);
+
+#endif
diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h
new file mode 100644
index 0000000..6c3fed6
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp.h
@@ -0,0 +1,263 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DEBUG_DSP_CTRL		0x0001
+#define DEBUG_DSP_CORE		0x0002
+#define DEBUG_DSP_DTMF		0x0004
+#define DEBUG_DSP_CMX		0x0010
+#define DEBUG_DSP_TONE		0x0020
+#define DEBUG_DSP_BLOWFISH	0x0040
+#define DEBUG_DSP_DELAY		0x0100
+#define DEBUG_DSP_DTMFCOEFF	0x8000 /* heavy output */
+
+/* options may be:
+ *
+ * bit 0 = use ulaw instead of alaw
+ * bit 1 = enable hfc hardware accelleration for all channels
+ *
+ */
+#define DSP_OPT_ULAW		(1<<0)
+#define DSP_OPT_NOHARDWARE	(1<<1)
+
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include "dsp_ecdis.h"
+
+extern int dsp_options;
+extern int dsp_debug;
+extern int dsp_poll;
+extern int dsp_tics;
+extern spinlock_t dsp_lock;
+extern struct work_struct dsp_workq;
+extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */
+
+/***************
+ * audio stuff *
+ ***************/
+
+extern s32 dsp_audio_alaw_to_s32[256];
+extern s32 dsp_audio_ulaw_to_s32[256];
+extern s32 *dsp_audio_law_to_s32;
+extern u8 dsp_audio_s16_to_law[65536];
+extern u8 dsp_audio_alaw_to_ulaw[256];
+extern u8 dsp_audio_mix_law[65536];
+extern u8 dsp_audio_seven2law[128];
+extern u8 dsp_audio_law2seven[256];
+extern void dsp_audio_generate_law_tables(void);
+extern void dsp_audio_generate_s2law_table(void);
+extern void dsp_audio_generate_seven(void);
+extern void dsp_audio_generate_mix_table(void);
+extern void dsp_audio_generate_ulaw_samples(void);
+extern void dsp_audio_generate_volume_changes(void);
+extern u8 dsp_silence;
+
+
+/*************
+ * cmx stuff *
+ *************/
+
+#define MAX_POLL	256	/* maximum number of send-chunks */
+
+#define CMX_BUFF_SIZE	0x8000	/* must be 2**n (0x1000 about 1/2 second) */
+#define CMX_BUFF_HALF	0x4000	/* CMX_BUFF_SIZE / 2 */
+#define CMX_BUFF_MASK	0x7fff	/* CMX_BUFF_SIZE - 1 */
+
+/* how many seconds will we check the lowest delay until the jitter buffer
+   is reduced by that delay */
+#define MAX_SECONDS_JITTER_CHECK 5
+
+extern struct timer_list dsp_spl_tl;
+extern u32 dsp_spl_jiffies;
+
+/* the structure of conferences:
+ *
+ * each conference has a unique number, given by user space.
+ * the conferences are linked in a chain.
+ * each conference has members linked in a chain.
+ * each dsplayer points to a member, each member points to a dsplayer.
+ */
+
+/* all members within a conference (this is linked 1:1 with the dsp) */
+struct dsp;
+struct dsp_conf_member {
+	struct list_head	list;
+	struct dsp		*dsp;
+};
+
+/* the list of all conferences */
+struct dsp_conf {
+	struct list_head	list;
+	u32			id;
+				/* all cmx stacks with the same ID are
+				 connected */
+	struct list_head	mlist;
+	int			software; /* conf is processed by software */
+	int			hardware; /* conf is processed by hardware */
+				/* note: if both unset, has only one member */
+};
+
+
+/**************
+ * DTMF stuff *
+ **************/
+
+#define DSP_DTMF_NPOINTS 102
+
+#define ECHOCAN_BUFLEN (4*128)
+
+struct dsp_dtmf {
+	int		treshold; /* above this is dtmf (square of) */
+	int		software; /* dtmf uses software decoding */
+	int		hardware; /* dtmf uses hardware decoding */
+	int		size; /* number of bytes in buffer */
+	signed short	buffer[DSP_DTMF_NPOINTS];
+		/* buffers one full dtmf frame */
+	u8		lastwhat, lastdigit;
+	int		count;
+	u8		digits[16]; /* just the dtmf result */
+};
+
+
+/******************
+ * pipeline stuff *
+ ******************/
+struct dsp_pipeline {
+	rwlock_t  lock;
+	struct list_head list;
+	int inuse;
+};
+
+/***************
+ * tones stuff *
+ ***************/
+
+struct dsp_tone {
+	int		software; /* tones are generated by software */
+	int		hardware; /* tones are generated by hardware */
+	int		tone;
+	void		*pattern;
+	int		count;
+	int		index;
+	struct timer_list tl;
+};
+
+/*****************
+ * general stuff *
+ *****************/
+
+struct dsp {
+	struct list_head list;
+	struct mISDNchannel	ch;
+	struct mISDNchannel	*up;
+	unsigned char	name[64];
+	int		b_active;
+	int		echo; /* echo is enabled */
+	int		rx_disabled; /* what the user wants */
+	int		rx_is_off; /* what the card is */
+	int		tx_mix;
+	struct dsp_tone	tone;
+	struct dsp_dtmf	dtmf;
+	int		tx_volume, rx_volume;
+
+	/* queue for sending frames */
+	struct work_struct	workq;
+	struct sk_buff_head	sendq;
+	int		hdlc;	/* if mode is hdlc */
+	int		data_pending;	/* currently an unconfirmed frame */
+
+	/* conference stuff */
+	u32		conf_id;
+	struct dsp_conf	*conf;
+	struct dsp_conf_member
+			*member;
+
+	/* buffer stuff */
+	int		rx_W; /* current write pos for data without timestamp */
+	int		rx_R; /* current read pos for transmit clock */
+	int		rx_init; /* if set, pointers will be adjusted first */
+	int		tx_W; /* current write pos for transmit data */
+	int		tx_R; /* current read pos for transmit clock */
+	int		rx_delay[MAX_SECONDS_JITTER_CHECK];
+	int		tx_delay[MAX_SECONDS_JITTER_CHECK];
+	u8		tx_buff[CMX_BUFF_SIZE];
+	u8		rx_buff[CMX_BUFF_SIZE];
+	int		last_tx; /* if set, we transmitted last poll interval */
+	int		cmx_delay; /* initial delay of buffers,
+				or 0 for dynamic jitter buffer */
+	int		tx_dejitter; /* if set, dejitter tx buffer */
+	int		tx_data; /* enables tx-data of CMX to upper layer */
+
+	/* hardware stuff */
+	struct dsp_features features;
+	int		features_rx_off; /* set if rx_off is featured */
+	int		pcm_slot_rx; /* current PCM slot (or -1) */
+	int		pcm_bank_rx;
+	int		pcm_slot_tx;
+	int		pcm_bank_tx;
+	int		hfc_conf; /* unique id of current conference (or -1) */
+
+	/* encryption stuff */
+	int		bf_enable;
+	u32		bf_p[18];
+	u32		bf_s[1024];
+	int		bf_crypt_pos;
+	u8		bf_data_in[9];
+	u8		bf_crypt_out[9];
+	int		bf_decrypt_in_pos;
+	int		bf_decrypt_out_pos;
+	u8		bf_crypt_inring[16];
+	u8		bf_data_out[9];
+	int		bf_sync;
+
+	struct dsp_pipeline
+			pipeline;
+};
+
+/* functions */
+
+extern void dsp_change_volume(struct sk_buff *skb, int volume);
+
+extern struct list_head dsp_ilist;
+extern struct list_head conf_ilist;
+extern void dsp_cmx_debug(struct dsp *dsp);
+extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp);
+extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id);
+extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_send(void *arg);
+extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb);
+extern int dsp_cmx_del_conf_member(struct dsp *dsp);
+extern int dsp_cmx_del_conf(struct dsp_conf *conf);
+
+extern void dsp_dtmf_goertzel_init(struct dsp *dsp);
+extern void dsp_dtmf_hardware(struct dsp *dsp);
+extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len,
+		int fmt);
+
+extern int dsp_tone(struct dsp *dsp, int tone);
+extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len);
+extern void dsp_tone_timeout(void *arg);
+
+extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len);
+extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len);
+extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen);
+extern void dsp_bf_cleanup(struct dsp *dsp);
+
+extern int  dsp_pipeline_module_init(void);
+extern void dsp_pipeline_module_exit(void);
+extern int  dsp_pipeline_init(struct dsp_pipeline *pipeline);
+extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline);
+extern int  dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg);
+extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data,
+		int len);
+extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data,
+		int len);
+
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c
new file mode 100644
index 0000000..1c2dd56
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_audio.c
@@ -0,0 +1,434 @@
+/*
+ * Audio support data for mISDN_dsp.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ * Rewritten by Peter
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+/* ulaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_ulaw_to_s32[256];
+/* alaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_alaw_to_s32[256];
+
+s32 *dsp_audio_law_to_s32;
+EXPORT_SYMBOL(dsp_audio_law_to_s32);
+
+/* signed 16-bit -> law */
+u8 dsp_audio_s16_to_law[65536];
+EXPORT_SYMBOL(dsp_audio_s16_to_law);
+
+/* alaw -> ulaw */
+u8 dsp_audio_alaw_to_ulaw[256];
+/* ulaw -> alaw */
+u8 dsp_audio_ulaw_to_alaw[256];
+u8 dsp_silence;
+
+
+/*****************************************************
+ * generate table for conversion of s16 to alaw/ulaw *
+ *****************************************************/
+
+#define AMI_MASK 0x55
+
+static inline unsigned char linear2alaw(short int linear)
+{
+	int mask;
+	int seg;
+	int pcm_val;
+	static int seg_end[8] = {
+		0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+	};
+
+	pcm_val = linear;
+	if (pcm_val >= 0) {
+		/* Sign (7th) bit = 1 */
+		mask = AMI_MASK | 0x80;
+	} else {
+		/* Sign bit = 0 */
+		mask = AMI_MASK;
+		pcm_val = -pcm_val;
+	}
+
+	/* Convert the scaled magnitude to segment number. */
+	for (seg = 0;  seg < 8;  seg++) {
+		if (pcm_val <= seg_end[seg])
+			break;
+	}
+	/* Combine the sign, segment, and quantization bits. */
+	return  ((seg << 4) |
+		 ((pcm_val >> ((seg)  ?  (seg + 3)  :  4)) & 0x0F)) ^ mask;
+}
+
+
+static inline short int alaw2linear(unsigned char alaw)
+{
+	int i;
+	int seg;
+
+	alaw ^= AMI_MASK;
+	i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
+	seg = (((int) alaw & 0x70) >> 4);
+	if (seg)
+		i = (i + 0x100) << (seg - 1);
+	return (short int) ((alaw & 0x80)  ?  i  :  -i);
+}
+
+static inline short int ulaw2linear(unsigned char ulaw)
+{
+	short mu, e, f, y;
+	static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
+
+	mu = 255 - ulaw;
+	e = (mu & 0x70) / 16;
+	f = mu & 0x0f;
+	y = f * (1 << (e + 3));
+	y += etab[e];
+	if (mu & 0x80)
+		y = -y;
+	return y;
+}
+
+#define BIAS 0x84   /*!< define the add-in bias for 16 bit samples */
+
+static unsigned char linear2ulaw(short sample)
+{
+	static int exp_lut[256] = {
+		0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+	int sign, exponent, mantissa;
+	unsigned char ulawbyte;
+
+	/* Get the sample into sign-magnitude. */
+	sign = (sample >> 8) & 0x80;	  /* set aside the sign */
+	if (sign != 0)
+		sample = -sample;	      /* get magnitude */
+
+	/* Convert from 16 bit linear to ulaw. */
+	sample = sample + BIAS;
+	exponent = exp_lut[(sample >> 7) & 0xFF];
+	mantissa = (sample >> (exponent + 3)) & 0x0F;
+	ulawbyte = ~(sign | (exponent << 4) | mantissa);
+
+	return ulawbyte;
+}
+
+static int reverse_bits(int i)
+{
+	int z, j;
+	z = 0;
+
+	for (j = 0; j < 8; j++) {
+		if ((i & (1 << j)) != 0)
+			z |= 1 << (7 - j);
+	}
+	return z;
+}
+
+
+void dsp_audio_generate_law_tables(void)
+{
+	int i;
+	for (i = 0; i < 256; i++)
+		dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i));
+
+	for (i = 0; i < 256; i++)
+		dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i));
+
+	for (i = 0; i < 256; i++) {
+		dsp_audio_alaw_to_ulaw[i] =
+			linear2ulaw(dsp_audio_alaw_to_s32[i]);
+		dsp_audio_ulaw_to_alaw[i] =
+			linear2alaw(dsp_audio_ulaw_to_s32[i]);
+	}
+}
+
+void
+dsp_audio_generate_s2law_table(void)
+{
+	int i;
+
+	if (dsp_options & DSP_OPT_ULAW) {
+		/* generating ulaw-table */
+		for (i = -32768; i < 32768; i++) {
+			dsp_audio_s16_to_law[i & 0xffff] =
+				reverse_bits(linear2ulaw(i));
+		}
+	} else {
+		/* generating alaw-table */
+		for (i = -32768; i < 32768; i++) {
+			dsp_audio_s16_to_law[i & 0xffff] =
+				reverse_bits(linear2alaw(i));
+		}
+	}
+}
+
+
+/*
+ * the seven bit sample is the number of every second alaw-sample ordered by
+ * aplitude. 0x00 is negative, 0x7f is positive amplitude.
+ */
+u8 dsp_audio_seven2law[128];
+u8 dsp_audio_law2seven[256];
+
+/********************************************************************
+ * generate table for conversion law from/to 7-bit alaw-like sample *
+ ********************************************************************/
+
+void
+dsp_audio_generate_seven(void)
+{
+	int i, j, k;
+	u8 spl;
+	u8 sorted_alaw[256];
+
+	/* generate alaw table, sorted by the linear value */
+	for (i = 0; i < 256; i++) {
+		j = 0;
+		for (k = 0; k < 256; k++) {
+			if (dsp_audio_alaw_to_s32[k]
+				< dsp_audio_alaw_to_s32[i]) {
+			j++;
+			}
+		}
+		sorted_alaw[j] = i;
+	}
+
+	/* generate tabels */
+	for (i = 0; i < 256; i++) {
+		/* spl is the source: the law-sample (converted to alaw) */
+		spl = i;
+		if (dsp_options & DSP_OPT_ULAW)
+			spl = dsp_audio_ulaw_to_alaw[i];
+		/* find the 7-bit-sample */
+		for (j = 0; j < 256; j++) {
+			if (sorted_alaw[j] == spl)
+				break;
+		}
+		/* write 7-bit audio value */
+		dsp_audio_law2seven[i] = j >> 1;
+	}
+	for (i = 0; i < 128; i++) {
+		spl = sorted_alaw[i << 1];
+		if (dsp_options & DSP_OPT_ULAW)
+			spl = dsp_audio_alaw_to_ulaw[spl];
+		dsp_audio_seven2law[i] = spl;
+	}
+}
+
+
+/* mix 2*law -> law */
+u8 dsp_audio_mix_law[65536];
+
+/******************************************************
+ * generate mix table to mix two law samples into one *
+ ******************************************************/
+
+void
+dsp_audio_generate_mix_table(void)
+{
+	int i, j;
+	s32 sample;
+
+	i = 0;
+	while (i < 256) {
+		j = 0;
+		while (j < 256) {
+			sample = dsp_audio_law_to_s32[i];
+			sample += dsp_audio_law_to_s32[j];
+			if (sample > 32767)
+				sample = 32767;
+			if (sample < -32768)
+				sample = -32768;
+			dsp_audio_mix_law[(i<<8)|j] =
+				dsp_audio_s16_to_law[sample & 0xffff];
+			j++;
+		}
+		i++;
+	}
+}
+
+
+/*************************************
+ * generate different volume changes *
+ *************************************/
+
+static u8 dsp_audio_reduce8[256];
+static u8 dsp_audio_reduce7[256];
+static u8 dsp_audio_reduce6[256];
+static u8 dsp_audio_reduce5[256];
+static u8 dsp_audio_reduce4[256];
+static u8 dsp_audio_reduce3[256];
+static u8 dsp_audio_reduce2[256];
+static u8 dsp_audio_reduce1[256];
+static u8 dsp_audio_increase1[256];
+static u8 dsp_audio_increase2[256];
+static u8 dsp_audio_increase3[256];
+static u8 dsp_audio_increase4[256];
+static u8 dsp_audio_increase5[256];
+static u8 dsp_audio_increase6[256];
+static u8 dsp_audio_increase7[256];
+static u8 dsp_audio_increase8[256];
+
+static u8 *dsp_audio_volume_change[16] = {
+	dsp_audio_reduce8,
+	dsp_audio_reduce7,
+	dsp_audio_reduce6,
+	dsp_audio_reduce5,
+	dsp_audio_reduce4,
+	dsp_audio_reduce3,
+	dsp_audio_reduce2,
+	dsp_audio_reduce1,
+	dsp_audio_increase1,
+	dsp_audio_increase2,
+	dsp_audio_increase3,
+	dsp_audio_increase4,
+	dsp_audio_increase5,
+	dsp_audio_increase6,
+	dsp_audio_increase7,
+	dsp_audio_increase8,
+};
+
+void
+dsp_audio_generate_volume_changes(void)
+{
+	register s32 sample;
+	int i;
+	int num[]   = { 110, 125, 150, 175, 200, 300, 400, 500 };
+	int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 };
+
+	i = 0;
+	while (i < 256) {
+		dsp_audio_reduce8[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff];
+		dsp_audio_reduce7[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff];
+		dsp_audio_reduce6[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff];
+		dsp_audio_reduce5[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff];
+		dsp_audio_reduce4[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff];
+		dsp_audio_reduce3[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff];
+		dsp_audio_reduce2[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff];
+		dsp_audio_reduce1[i] = dsp_audio_s16_to_law[
+			(dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[0] / denum[0];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[1] / denum[1];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[2] / denum[2];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[3] / denum[3];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[4] / denum[4];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[5] / denum[5];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[6] / denum[6];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff];
+		sample = dsp_audio_law_to_s32[i] * num[7] / denum[7];
+		if (sample < -32768)
+			sample = -32768;
+		else if (sample > 32767)
+			sample = 32767;
+		dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff];
+
+		i++;
+	}
+}
+
+
+/**************************************
+ * change the volume of the given skb *
+ **************************************/
+
+/* this is a helper function for changing volume of skb. the range may be
+ * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8
+ */
+void
+dsp_change_volume(struct sk_buff *skb, int volume)
+{
+	u8 *volume_change;
+	int i, ii;
+	u8 *p;
+	int shift;
+
+	if (volume == 0)
+		return;
+
+	/* get correct conversion table */
+	if (volume < 0) {
+		shift = volume + 8;
+		if (shift < 0)
+			shift = 0;
+	} else {
+		shift = volume + 7;
+		if (shift > 15)
+			shift = 15;
+	}
+	volume_change = dsp_audio_volume_change[shift];
+	i = 0;
+	ii = skb->len;
+	p = skb->data;
+	/* change volume */
+	while (i < ii) {
+		*p = volume_change[*p];
+		p++;
+		i++;
+	}
+}
+
diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h
new file mode 100644
index 0000000..038191b
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_biquad.h
@@ -0,0 +1,65 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * biquad.h - General telephony bi-quad section routines (currently this just
+ *            handles canonic/type 2 form)
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+struct biquad2_state {
+	int32_t gain;
+	int32_t a1;
+	int32_t a2;
+	int32_t b1;
+	int32_t b2;
+
+	int32_t z1;
+	int32_t z2;
+};
+
+static inline void biquad2_init(struct biquad2_state *bq,
+    int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2)
+{
+	bq->gain = gain;
+	bq->a1 = a1;
+	bq->a2 = a2;
+	bq->b1 = b1;
+	bq->b2 = b2;
+
+	bq->z1 = 0;
+	bq->z2 = 0;
+}
+
+static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample)
+{
+	int32_t y;
+	int32_t z0;
+
+	z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2;
+	y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2;
+
+	bq->z2 = bq->z1;
+	bq->z1 = z0 >> 15;
+	y >>= 15;
+	return  y;
+}
diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c
new file mode 100644
index 0000000..18e411e
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_blowfish.c
@@ -0,0 +1,672 @@
+/*
+ * Blowfish encryption/decryption for mISDN_dsp.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+/*
+ * how to encode a sample stream to 64-bit blocks that will be encryped
+ *
+ * first of all, data is collected until a block of 9 samples are received.
+ * of course, a packet may have much more than 9 sample, but is may have
+ * not excacly the multiple of 9 samples. if there is a rest, the next
+ * received data will complete the block.
+ *
+ * the block is then converted to 9 uLAW samples without the least sigificant
+ * bit. the result is a 7-bit encoded sample.
+ *
+ * the samples will be reoganised to form 8 bytes of data:
+ * (5(6) means: encoded sample no. 5, bit 6)
+ *
+ * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6)
+ * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5)
+ * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4)
+ * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3)
+ * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2)
+ * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1)
+ * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
+ * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0)
+ *
+ * the missing bit 0 of the last byte is filled with some
+ * random noise, to fill all 8 bytes.
+ *
+ * the 8 bytes will be encrypted using blowfish.
+ *
+ * the result will be converted into 9 bytes. the bit 7 is used for
+ * checksumme (CS) for sync (0, 1) and for the last bit:
+ * (5(6) means: crypted byte 5, bit 6)
+ *
+ * 1    0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1)
+ * 0    0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2)
+ * 0    1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3)
+ * 0    2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4)
+ * 0    3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5)
+ * CS   4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6)
+ * CS   5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7)
+ * CS   6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0)
+ * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
+ *
+ * the checksum is used to detect transmission errors and frame drops.
+ *
+ * synchronisation of received block is done by shifting the upper bit of each
+ * byte (bit 7) to a shift register. if the rigister has the first five bits
+ * (10000), this is used to find the sync. only if sync has been found, the
+ * current block of 9 received bytes are decrypted. before that the check
+ * sum is calculated. if it is incorrect the block is dropped.
+ * this will avoid loud noise due to corrupt encrypted data.
+ *
+ * if the last block is corrupt, the current decoded block is repeated
+ * until a valid block has been received.
+ */
+
+/*
+ *  some blowfish parts are taken from the
+ * crypto-api for faster implementation
+ */
+
+struct bf_ctx {
+	u32 p[18];
+	u32 s[1024];
+};
+
+static const u32 bf_pbox[16 + 2] = {
+	0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+	0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+	0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+	0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+	0x9216d5d9, 0x8979fb1b,
+};
+
+static const u32 bf_sbox[256 * 4] = {
+	0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+	0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+	0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+	0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+	0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+	0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+	0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+	0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+	0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+	0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+	0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+	0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+	0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+	0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+	0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+	0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+	0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+	0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+	0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+	0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+	0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+	0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+	0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+	0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+	0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+	0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+	0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+	0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+	0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+	0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+	0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+	0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+	0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+	0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+	0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+	0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+	0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+	0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+	0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+	0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+	0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+	0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+	0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+	0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+	0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+	0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+	0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+	0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+	0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+	0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+	0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+	0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+	0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+	0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+	0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+	0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+	0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+	0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+	0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+	0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+	0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+	0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+	0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+	0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+	0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+	0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+	0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+	0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+	0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+	0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+	0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+	0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+	0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+	0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+	0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+	0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+	0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+	0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+	0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+	0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+	0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+	0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+	0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+	0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+	0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+	0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+	0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+	0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+	0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+	0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+	0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+	0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+	0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+	0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+	0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+	0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+	0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+	0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+	0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+	0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+	0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+	0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+	0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+	0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+	0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+	0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+	0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+	0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+	0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+	0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+	0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+	0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+	0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+	0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+	0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+	0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+	0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+	0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+	0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+	0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+	0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+	0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+	0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+	0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+	0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+	0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+	0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+	0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+	0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+	0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+	0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+	0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+	0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+	0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+	0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+	0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+	0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+	0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+	0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+	0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+	0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+	0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+	0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+	0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+	0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+	0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+	0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+	0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+	0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+	0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+	0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+	0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+	0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+	0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+	0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+	0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+	0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+	0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+	0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+	0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+	0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+	0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+	0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+	0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+	0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+	0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+	0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+	0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+	0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+	0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+	0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+	0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+	0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+	0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+	0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+	0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+	0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+	0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+	0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+	0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+	0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+	0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+	0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+	0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+	0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+	0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+	0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+	0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+	0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+	0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+	0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+	0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+	0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+	0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+	0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+	0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+	0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+	0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+	0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+	0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+	0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+	0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+	0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+	0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+	0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+	0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+	0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+	0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+	0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+	0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+	0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+	0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+	0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+	0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+	0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+	0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+	0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+	0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+	0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+	0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+	0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+	0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+	0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+	0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+	0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+	0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+	0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+	0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+	0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+	0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+	0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+	0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+	0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+	0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+	0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+	0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+	0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+	0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+	0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+	0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+	0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+	0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+	0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+	0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+	0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+	0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+	0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+	0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+	0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+	0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+	0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+	0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+	0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+	0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+	0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+	0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+};
+
+/*
+ * Round loop unrolling macros, S is a pointer to a S-Box array
+ * organized in 4 unsigned longs at a row.
+ */
+#define GET32_3(x) (((x) & 0xff))
+#define GET32_2(x) (((x) >> (8)) & (0xff))
+#define GET32_1(x) (((x) >> (16)) & (0xff))
+#define GET32_0(x) (((x) >> (24)) & (0xff))
+
+#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \
+    S[512 + GET32_2(x)]) + S[768 + GET32_3(x)])
+
+#define EROUND(a, b, n)  do { b ^= P[n]; a ^= bf_F(b); } while (0)
+#define DROUND(a, b, n)  do { a ^= bf_F(b); b ^= P[n]; } while (0)
+
+
+/*
+ * encrypt isdn data frame
+ * every block with 9 samples is encrypted
+ */
+void
+dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len)
+{
+	int i = 0, j = dsp->bf_crypt_pos;
+	u8 *bf_data_in = dsp->bf_data_in;
+	u8 *bf_crypt_out = dsp->bf_crypt_out;
+	u32 *P = dsp->bf_p;
+	u32 *S = dsp->bf_s;
+	u32 yl, yr;
+	u32 cs;
+	u8 nibble;
+
+	while (i < len) {
+		/* collect a block of 9 samples */
+		if (j < 9) {
+			bf_data_in[j] = *data;
+			*data++ = bf_crypt_out[j++];
+			i++;
+			continue;
+		}
+		j = 0;
+		/* transcode 9 samples xlaw to 8 bytes */
+		yl = dsp_audio_law2seven[bf_data_in[0]];
+		yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]];
+		yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]];
+		yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]];
+		nibble = dsp_audio_law2seven[bf_data_in[4]];
+		yr = nibble;
+		yl = (yl<<4) | (nibble>>3);
+		yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]];
+		yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]];
+		yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]];
+		yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]];
+		yr = (yr<<1) | (bf_data_in[0] & 1);
+
+		/* fill unused bit with random noise of audio input */
+		/* encrypt */
+
+		EROUND(yr, yl, 0);
+		EROUND(yl, yr, 1);
+		EROUND(yr, yl, 2);
+		EROUND(yl, yr, 3);
+		EROUND(yr, yl, 4);
+		EROUND(yl, yr, 5);
+		EROUND(yr, yl, 6);
+		EROUND(yl, yr, 7);
+		EROUND(yr, yl, 8);
+		EROUND(yl, yr, 9);
+		EROUND(yr, yl, 10);
+		EROUND(yl, yr, 11);
+		EROUND(yr, yl, 12);
+		EROUND(yl, yr, 13);
+		EROUND(yr, yl, 14);
+		EROUND(yl, yr, 15);
+		yl ^= P[16];
+		yr ^= P[17];
+
+		/* calculate 3-bit checksumme */
+		cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15)
+			^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30)
+			^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10)
+			^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25)
+			^ (yr>>28) ^ (yr>>31);
+
+		/*
+		 * transcode 8 crypted bytes to 9 data bytes with sync
+		 * and checksum information
+		 */
+		bf_crypt_out[0] = (yl>>25) | 0x80;
+		bf_crypt_out[1] = (yl>>18) & 0x7f;
+		bf_crypt_out[2] = (yl>>11) & 0x7f;
+		bf_crypt_out[3] = (yl>>4) & 0x7f;
+		bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07);
+		bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80);
+		bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80);
+		bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7);
+		bf_crypt_out[8] = yr;
+	}
+
+	/* write current count */
+	dsp->bf_crypt_pos = j;
+
+}
+
+
+/*
+ * decrypt isdn data frame
+ * every block with 9 bytes is decrypted
+ */
+void
+dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len)
+{
+	int i = 0;
+	u8 j = dsp->bf_decrypt_in_pos;
+	u8 k = dsp->bf_decrypt_out_pos;
+	u8 *bf_crypt_inring = dsp->bf_crypt_inring;
+	u8 *bf_data_out = dsp->bf_data_out;
+	u16 sync = dsp->bf_sync;
+	u32 *P = dsp->bf_p;
+	u32 *S = dsp->bf_s;
+	u32 yl, yr;
+	u8 nibble;
+	u8 cs, cs0, cs1, cs2;
+
+	while (i < len) {
+		/*
+		 * shift upper bit and rotate data to buffer ring
+		 * send current decrypted data
+		 */
+		sync = (sync<<1) | ((*data)>>7);
+		bf_crypt_inring[j++ & 15] = *data;
+		*data++ = bf_data_out[k++];
+		i++;
+		if (k == 9)
+			k = 0; /* repeat if no sync has been found */
+		/* check if not in sync */
+		if ((sync&0x1f0) != 0x100)
+			continue;
+		j -= 9;
+		/* transcode receive data to 64 bit block of encrypted data */
+		yl = bf_crypt_inring[j++ & 15];
+		yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+		yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+		yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+		nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+		yr = nibble;
+		yl = (yl<<4) | (nibble>>3);
+		cs2 = bf_crypt_inring[j++ & 15];
+		yr = (yr<<7) | (cs2 & 0x7f);
+		cs1 = bf_crypt_inring[j++ & 15];
+		yr = (yr<<7) | (cs1 & 0x7f);
+		cs0 = bf_crypt_inring[j++ & 15];
+		yr = (yr<<7) | (cs0 & 0x7f);
+		yr = (yr<<8) | bf_crypt_inring[j++ & 15];
+
+		/* calculate 3-bit checksumme */
+		cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15)
+			^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30)
+			^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10)
+			^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25)
+			^ (yr>>28) ^ (yr>>31);
+
+		/* check if frame is valid */
+		if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) {
+			if (dsp_debug & DEBUG_DSP_BLOWFISH)
+				printk(KERN_DEBUG
+				    "DSP BLOWFISH: received corrupt frame, "
+				    "checksumme is not correct\n");
+			continue;
+		}
+
+		/* decrypt */
+		yr ^= P[17];
+		yl ^= P[16];
+		DROUND(yl, yr, 15);
+		DROUND(yr, yl, 14);
+		DROUND(yl, yr, 13);
+		DROUND(yr, yl, 12);
+		DROUND(yl, yr, 11);
+		DROUND(yr, yl, 10);
+		DROUND(yl, yr, 9);
+		DROUND(yr, yl, 8);
+		DROUND(yl, yr, 7);
+		DROUND(yr, yl, 6);
+		DROUND(yl, yr, 5);
+		DROUND(yr, yl, 4);
+		DROUND(yl, yr, 3);
+		DROUND(yr, yl, 2);
+		DROUND(yl, yr, 1);
+		DROUND(yr, yl, 0);
+
+		/* transcode 8 crypted bytes to 9 sample bytes */
+		bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f];
+		bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f];
+		bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f];
+		bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f];
+		bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) |
+		    ((yr>>29) & 0x07)];
+
+		bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f];
+		bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f];
+		bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f];
+		bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f];
+		k = 0; /* start with new decoded frame */
+	}
+
+	/* write current count and sync */
+	dsp->bf_decrypt_in_pos = j;
+	dsp->bf_decrypt_out_pos = k;
+	dsp->bf_sync = sync;
+}
+
+
+/* used to encrypt S and P boxes */
+static inline void
+encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src)
+{
+	u32 yl = src[0];
+	u32 yr = src[1];
+
+	EROUND(yr, yl, 0);
+	EROUND(yl, yr, 1);
+	EROUND(yr, yl, 2);
+	EROUND(yl, yr, 3);
+	EROUND(yr, yl, 4);
+	EROUND(yl, yr, 5);
+	EROUND(yr, yl, 6);
+	EROUND(yl, yr, 7);
+	EROUND(yr, yl, 8);
+	EROUND(yl, yr, 9);
+	EROUND(yr, yl, 10);
+	EROUND(yl, yr, 11);
+	EROUND(yr, yl, 12);
+	EROUND(yl, yr, 13);
+	EROUND(yr, yl, 14);
+	EROUND(yl, yr, 15);
+
+	yl ^= P[16];
+	yr ^= P[17];
+
+	dst[0] = yr;
+	dst[1] = yl;
+}
+
+/*
+ * initialize the dsp for encryption and decryption using the same key
+ * Calculates the blowfish S and P boxes for encryption and decryption.
+ * The margin of keylen must be 4-56 bytes.
+ * returns 0 if ok.
+ */
+int
+dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen)
+{
+	short i, j, count;
+	u32 data[2], temp;
+	u32 *P = (u32 *)dsp->bf_p;
+	u32 *S = (u32 *)dsp->bf_s;
+
+	if (keylen < 4 || keylen > 56)
+		return 1;
+
+	/* Set dsp states */
+	i = 0;
+	while (i < 9) {
+		dsp->bf_crypt_out[i] = 0xff;
+		dsp->bf_data_out[i] = dsp_silence;
+		i++;
+	}
+	dsp->bf_crypt_pos = 0;
+	dsp->bf_decrypt_in_pos = 0;
+	dsp->bf_decrypt_out_pos = 0;
+	dsp->bf_sync = 0x1ff;
+	dsp->bf_enable = 1;
+
+	/* Copy the initialization s-boxes */
+	for (i = 0, count = 0; i < 256; i++)
+		for (j = 0; j < 4; j++, count++)
+			S[count] = bf_sbox[count];
+
+	/* Set the p-boxes */
+	for (i = 0; i < 16 + 2; i++)
+		P[i] = bf_pbox[i];
+
+	/* Actual subkey generation */
+	for (j = 0, i = 0; i < 16 + 2; i++) {
+		temp = (((u32)key[j] << 24) |
+		    ((u32)key[(j + 1) % keylen] << 16) |
+		    ((u32)key[(j + 2) % keylen] << 8) |
+		    ((u32)key[(j + 3) % keylen]));
+
+		P[i] = P[i] ^ temp;
+		j = (j + 4) % keylen;
+	}
+
+	data[0] = 0x00000000;
+	data[1] = 0x00000000;
+
+	for (i = 0; i < 16 + 2; i += 2) {
+		encrypt_block(P, S, data, data);
+
+		P[i] = data[0];
+		P[i + 1] = data[1];
+	}
+
+	for (i = 0; i < 4; i++) {
+		for (j = 0, count = i * 256; j < 256; j += 2, count += 2) {
+			encrypt_block(P, S, data, data);
+
+			S[count] = data[0];
+			S[count + 1] = data[1];
+		}
+	}
+
+	return 0;
+}
+
+
+/*
+ * turn encryption off
+ */
+void
+dsp_bf_cleanup(struct dsp *dsp)
+{
+	dsp->bf_enable = 0;
+}
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
new file mode 100644
index 0000000..e92b1ba
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -0,0 +1,1886 @@
+/*
+ * Audio crossconnecting/conferrencing (hardware level).
+ *
+ * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*
+ * The process of adding and removing parties to/from a conference:
+ *
+ * There is a chain of struct dsp_conf which has one or more members in a chain
+ * of struct dsp_conf_member.
+ *
+ * After a party is added, the conference is checked for hardware capability.
+ * Also if a party is removed, the conference is checked again.
+ *
+ * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect
+ * 1-n = hardware-conference. The n will give the conference number.
+ *
+ * Depending on the change after removal or insertion of a party, hardware
+ * commands are given.
+ *
+ * The current solution is stored within the struct dsp_conf entry.
+ */
+
+/*
+ * HOW THE CMX WORKS:
+ *
+ * There are 3 types of interaction: One member is alone, in this case only
+ * data flow from upper to lower layer is done.
+ * Two members will also exchange their data so they are crossconnected.
+ * Three or more members will be added in a conference and will hear each
+ * other but will not receive their own speech (echo) if not enabled.
+ *
+ * Features of CMX are:
+ *  - Crossconnecting or even conference, if more than two members are together.
+ *  - Force mixing of transmit data with other crossconnect/conference members.
+ *  - Echo generation to benchmark the delay of audio processing.
+ *  - Use hardware to minimize cpu load, disable FIFO load and minimize delay.
+ *  - Dejittering and clock generation.
+ *
+ * There are 2 buffers:
+ *
+ *
+ * RX-Buffer
+ *                 R             W
+ *                 |             |
+ * ----------------+-------------+-------------------
+ *
+ * The rx-buffer is a ring buffer used to store the received data for each
+ * individual member. This is only the case if data needs to be dejittered
+ * or in case of a conference where different clocks require reclocking.
+ * The transmit-clock (R) will read the buffer.
+ * If the clock overruns the write-pointer, we will have a buffer underrun.
+ * If the write pointer always has a certain distance from the transmit-
+ * clock, we will have a delay. The delay will dynamically be increased and
+ * reduced.
+ *
+ *
+ * TX-Buffer
+ *                  R        W
+ *                  |        |
+ * -----------------+--------+-----------------------
+ *
+ * The tx-buffer is a ring buffer to queue the transmit data from user space
+ * until it will be mixed or sent. There are two pointers, R and W. If the write
+ * pointer W would reach or overrun R, the buffer would overrun. In this case
+ * (some) data is dropped so that it will not overrun.
+ * Additionally a dynamic dejittering can be enabled. this allows data from
+ * user space that have jitter and different clock source.
+ *
+ *
+ * Clock:
+ *
+ * A Clock is not required, if the data source has exactly one clock. In this
+ * case the data source is forwarded to the destination.
+ *
+ * A Clock is required, because the data source
+ *  - has multiple clocks.
+ *  - has no usable clock due to jitter or packet loss (VoIP).
+ * In this case the system's clock is used. The clock resolution depends on
+ * the jiffie resolution.
+ *
+ * If a member joins a conference:
+ *
+ * - If a member joins, its rx_buff is set to silence and change read pointer
+ *   to transmit clock.
+ *
+ * The procedure of received data from card is explained in cmx_receive.
+ * The procedure of received data from user space is explained in cmx_transmit.
+ * The procedure of transmit data to card is cmx_send.
+ *
+ *
+ * Interaction with other features:
+ *
+ * DTMF:
+ * DTMF decoding is done before the data is crossconnected.
+ *
+ * Volume change:
+ * Changing rx-volume is done before the data is crossconnected. The tx-volume
+ * must be changed whenever data is transmitted to the card by the cmx.
+ *
+ * Tones:
+ * If a tone is enabled, it will be processed whenever data is transmitted to
+ * the card. It will replace the tx-data from the user space.
+ * If tones are generated by hardware, this conference member is removed for
+ * this time.
+ *
+ * Disable rx-data:
+ * If cmx is realized in hardware, rx data will be disabled if requested by
+ * the upper layer. If dtmf decoding is done by software and enabled, rx data
+ * will not be diabled but blocked to the upper layer.
+ *
+ * HFC conference engine:
+ * If it is possible to realize all features using hardware, hardware will be
+ * used if not forbidden by control command. Disabling rx-data provides
+ * absolutely traffic free audio processing. (except for the quick 1-frame
+ * upload of a tone loop, only once for a new tone)
+ *
+ */
+
+/* delay.h is required for hw_lock.h */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+/*
+ * debugging of multi party conference,
+ * by using conference even with two members
+ */
+
+/* #define CMX_CONF_DEBUG */
+
+/*#define CMX_DEBUG * massive read/write pointer output */
+/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */
+
+static inline int
+count_list_member(struct list_head *head)
+{
+	int			cnt = 0;
+	struct list_head	*m;
+
+	list_for_each(m, head)
+		cnt++;
+	return cnt;
+}
+
+/*
+ * debug cmx memory structure
+ */
+void
+dsp_cmx_debug(struct dsp *dsp)
+{
+	struct dsp_conf	*conf;
+	struct dsp_conf_member	*member;
+	struct dsp		*odsp;
+
+	printk(KERN_DEBUG "-----Current DSP\n");
+	list_for_each_entry(odsp, &dsp_ilist, list) {
+		printk(KERN_DEBUG "* %s echo=%d txmix=%d",
+		    odsp->name, odsp->echo, odsp->tx_mix);
+		if (odsp->conf)
+			printk(" (Conf %d)", odsp->conf->id);
+		if (dsp == odsp)
+			printk(" *this*");
+		printk("\n");
+	}
+	printk(KERN_DEBUG "-----Current Conf:\n");
+	list_for_each_entry(conf, &conf_ilist, list) {
+		printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf);
+		list_for_each_entry(member, &conf->mlist, list) {
+			printk(KERN_DEBUG
+			    "  - member = %s (slot_tx %d, bank_tx %d, "
+			    "slot_rx %d, bank_rx %d hfc_conf %d)%s\n",
+			    member->dsp->name, member->dsp->pcm_slot_tx,
+			    member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx,
+			    member->dsp->pcm_bank_rx, member->dsp->hfc_conf,
+			    (member->dsp == dsp) ? " *this*" : "");
+		}
+	}
+	printk(KERN_DEBUG "-----end\n");
+}
+
+/*
+ * search conference
+ */
+static struct dsp_conf *
+dsp_cmx_search_conf(u32 id)
+{
+	struct dsp_conf *conf;
+
+	if (!id) {
+		printk(KERN_WARNING "%s: conference ID is 0.\n", __func__);
+		return NULL;
+	}
+
+	/* search conference */
+	list_for_each_entry(conf, &conf_ilist, list)
+		if (conf->id == id)
+			return conf;
+
+	return NULL;
+}
+
+
+/*
+ * add member to conference
+ */
+static int
+dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf)
+{
+	struct dsp_conf_member *member;
+
+	if (!conf || !dsp) {
+		printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__);
+		return -EINVAL;
+	}
+	if (dsp->member) {
+		printk(KERN_WARNING "%s: dsp is already member in a conf.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (dsp->conf) {
+		printk(KERN_WARNING "%s: dsp is already in a conf.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC);
+	if (!member) {
+		printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n");
+		return -ENOMEM;
+	}
+	member->dsp = dsp;
+	/* clear rx buffer */
+	memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+	dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */
+	dsp->rx_W = 0;
+	dsp->rx_R = 0;
+
+	list_add_tail(&member->list, &conf->mlist);
+
+	dsp->conf = conf;
+	dsp->member = member;
+
+	return 0;
+}
+
+
+/*
+ * del member from conference
+ */
+int
+dsp_cmx_del_conf_member(struct dsp *dsp)
+{
+	struct dsp_conf_member *member;
+
+	if (!dsp) {
+		printk(KERN_WARNING "%s: dsp is 0.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!dsp->conf) {
+		printk(KERN_WARNING "%s: dsp is not in a conf.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (list_empty(&dsp->conf->mlist)) {
+		printk(KERN_WARNING "%s: dsp has linked an empty conf.\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/* find us in conf */
+	list_for_each_entry(member, &dsp->conf->mlist, list) {
+		if (member->dsp == dsp) {
+			list_del(&member->list);
+			dsp->conf = NULL;
+			dsp->member = NULL;
+			kfree(member);
+			return 0;
+		}
+	}
+	printk(KERN_WARNING
+	    "%s: dsp is not present in its own conf_meber list.\n",
+	    __func__);
+
+	return -EINVAL;
+}
+
+
+/*
+ * new conference
+ */
+static struct dsp_conf
+*dsp_cmx_new_conf(u32 id)
+{
+	struct dsp_conf *conf;
+
+	if (!id) {
+		printk(KERN_WARNING "%s: id is 0.\n",
+		    __func__);
+		return NULL;
+	}
+
+	conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC);
+	if (!conf) {
+		printk(KERN_ERR "kmalloc struct dsp_conf failed\n");
+		return NULL;
+	}
+	INIT_LIST_HEAD(&conf->mlist);
+	conf->id = id;
+
+	list_add_tail(&conf->list, &conf_ilist);
+
+	return conf;
+}
+
+
+/*
+ * del conference
+ */
+int
+dsp_cmx_del_conf(struct dsp_conf *conf)
+{
+	if (!conf) {
+		printk(KERN_WARNING "%s: conf is null.\n",
+		    __func__);
+		return -EINVAL;
+	}
+
+	if (!list_empty(&conf->mlist)) {
+		printk(KERN_WARNING "%s: conf not empty.\n",
+		    __func__);
+		return -EINVAL;
+	}
+	list_del(&conf->list);
+	kfree(conf);
+
+	return 0;
+}
+
+
+/*
+ * send HW message to hfc card
+ */
+static void
+dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2,
+    u32 param3, u32 param4)
+{
+	struct mISDN_ctrl_req cq;
+
+	memset(&cq, 0, sizeof(cq));
+	cq.op = message;
+	cq.p1 = param1 | (param2 << 8);
+	cq.p2 = param3 | (param4 << 8);
+	if (dsp->ch.peer)
+		dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq);
+}
+
+
+/*
+ * do hardware update and set the software/hardware flag
+ *
+ * either a conference or a dsp instance can be given
+ * if only dsp instance is given, the instance is not associated with a conf
+ * and therefore removed. if a conference is given, the dsp is expected to
+ * be member of that conference.
+ */
+void
+dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp)
+{
+	struct dsp_conf_member	*member, *nextm;
+	struct dsp		*finddsp;
+	int		memb = 0, i, ii, i1, i2;
+	int		freeunits[8];
+	u_char		freeslots[256];
+	int		same_hfc = -1, same_pcm = -1, current_conf = -1,
+	    all_conf = 1;
+
+	/* dsp gets updated (no conf) */
+	if (!conf) {
+		if (!dsp)
+			return;
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG "%s checking dsp %s\n",
+			    __func__, dsp->name);
+one_member:
+		/* remove HFC conference if enabled */
+		if (dsp->hfc_conf >= 0) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s removing %s from HFC conf %d "
+				    "because dsp is split\n", __func__,
+				    dsp->name, dsp->hfc_conf);
+			dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT,
+			    0, 0, 0, 0);
+			dsp->hfc_conf = -1;
+		}
+		/* process hw echo */
+		if (dsp->features.pcm_banks < 1)
+			return;
+		if (!dsp->echo) {
+			/* NO ECHO: remove PCM slot if assigned */
+			if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) {
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG "%s removing %s from"
+					    " PCM slot %d (TX) %d (RX) because"
+					    " dsp is split (no echo)\n",
+					    __func__, dsp->name,
+					    dsp->pcm_slot_tx, dsp->pcm_slot_rx);
+				dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC,
+				    0, 0, 0, 0);
+				dsp->pcm_slot_tx = -1;
+				dsp->pcm_bank_tx = -1;
+				dsp->pcm_slot_rx = -1;
+				dsp->pcm_bank_rx = -1;
+			}
+			return;
+		}
+		/* ECHO: already echo */
+		if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 &&
+		    dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2)
+			return;
+		/* ECHO: if slot already assigned */
+		if (dsp->pcm_slot_tx >= 0) {
+			dsp->pcm_slot_rx = dsp->pcm_slot_tx;
+			dsp->pcm_bank_tx = 2; /* 2 means loop */
+			dsp->pcm_bank_rx = 2;
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s refresh %s for echo using slot %d\n",
+				    __func__, dsp->name,
+				    dsp->pcm_slot_tx);
+			dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
+			    dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
+			return;
+		}
+		/* ECHO: find slot */
+		dsp->pcm_slot_tx = -1;
+		dsp->pcm_slot_rx = -1;
+		memset(freeslots, 1, sizeof(freeslots));
+		list_for_each_entry(finddsp, &dsp_ilist, list) {
+			if (finddsp->features.pcm_id == dsp->features.pcm_id) {
+				if (finddsp->pcm_slot_rx >= 0 &&
+				    finddsp->pcm_slot_rx < sizeof(freeslots))
+					freeslots[finddsp->pcm_slot_tx] = 0;
+				if (finddsp->pcm_slot_tx >= 0 &&
+				    finddsp->pcm_slot_tx < sizeof(freeslots))
+					freeslots[finddsp->pcm_slot_rx] = 0;
+			}
+		}
+		i = 0;
+		ii = dsp->features.pcm_slots;
+		while (i < ii) {
+			if (freeslots[i])
+				break;
+			i++;
+		}
+		if (i == ii) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s no slot available for echo\n",
+				    __func__);
+			/* no more slots available */
+			return;
+		}
+		/* assign free slot */
+		dsp->pcm_slot_tx = i;
+		dsp->pcm_slot_rx = i;
+		dsp->pcm_bank_tx = 2; /* loop */
+		dsp->pcm_bank_rx = 2;
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG
+			    "%s assign echo for %s using slot %d\n",
+			    __func__, dsp->name, dsp->pcm_slot_tx);
+		dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
+		    dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
+		return;
+	}
+
+	/* conf gets updated (all members) */
+	if (dsp_debug & DEBUG_DSP_CMX)
+		printk(KERN_DEBUG "%s checking conference %d\n",
+		    __func__, conf->id);
+
+	if (list_empty(&conf->mlist)) {
+		printk(KERN_ERR "%s: conference whithout members\n",
+		    __func__);
+		return;
+	}
+	member = list_entry(conf->mlist.next, struct dsp_conf_member, list);
+	same_hfc = member->dsp->features.hfc_id;
+	same_pcm = member->dsp->features.pcm_id;
+	/* check all members in our conference */
+	list_for_each_entry(member, &conf->mlist, list) {
+		/* check if member uses mixing */
+		if (member->dsp->tx_mix) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "tx_mix is turned on\n", __func__,
+				    member->dsp->name);
+conf_software:
+			list_for_each_entry(member, &conf->mlist, list) {
+				dsp = member->dsp;
+				/* remove HFC conference if enabled */
+				if (dsp->hfc_conf >= 0) {
+					if (dsp_debug & DEBUG_DSP_CMX)
+						printk(KERN_DEBUG
+						    "%s removing %s from HFC "
+						    "conf %d because not "
+						    "possible with hardware\n",
+						    __func__,
+						    dsp->name,
+						    dsp->hfc_conf);
+					dsp_cmx_hw_message(dsp,
+					    MISDN_CTRL_HFC_CONF_SPLIT,
+					    0, 0, 0, 0);
+					dsp->hfc_conf = -1;
+				}
+				/* remove PCM slot if assigned */
+				if (dsp->pcm_slot_tx >= 0 ||
+				    dsp->pcm_slot_rx >= 0) {
+					if (dsp_debug & DEBUG_DSP_CMX)
+						printk(KERN_DEBUG "%s removing "
+						    "%s from PCM slot %d (TX)"
+						    " slot %d (RX) because not"
+						    " possible with hardware\n",
+						    __func__,
+						    dsp->name,
+						    dsp->pcm_slot_tx,
+						    dsp->pcm_slot_rx);
+					dsp_cmx_hw_message(dsp,
+					    MISDN_CTRL_HFC_PCM_DISC,
+					    0, 0, 0, 0);
+					dsp->pcm_slot_tx = -1;
+					dsp->pcm_bank_tx = -1;
+					dsp->pcm_slot_rx = -1;
+					dsp->pcm_bank_rx = -1;
+				}
+			}
+			conf->hardware = 0;
+			conf->software = 1;
+			return;
+		}
+		/* check if member has echo turned on */
+		if (member->dsp->echo) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "echo is turned on\n", __func__,
+				    member->dsp->name);
+			goto conf_software;
+		}
+		/* check if member has tx_mix turned on */
+		if (member->dsp->tx_mix) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "tx_mix is turned on\n",
+				    __func__, member->dsp->name);
+			goto conf_software;
+		}
+		/* check if member changes volume at an not suppoted level */
+		if (member->dsp->tx_volume) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "tx_volume is changed\n",
+				    __func__, member->dsp->name);
+			goto conf_software;
+		}
+		if (member->dsp->rx_volume) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "rx_volume is changed\n",
+				    __func__, member->dsp->name);
+			goto conf_software;
+		}
+		/* check if tx-data turned on */
+		if (member->dsp->tx_data) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "tx_data is turned on\n",
+				    __func__, member->dsp->name);
+			goto conf_software;
+		}
+		/* check if pipeline exists */
+		if (member->dsp->pipeline.inuse) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "pipeline exists\n", __func__,
+				    member->dsp->name);
+			goto conf_software;
+		}
+		/* check if encryption is enabled */
+		if (member->dsp->bf_enable) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG "%s dsp %s cannot form a "
+				    "conf, because encryption is enabled\n",
+				    __func__, member->dsp->name);
+			goto conf_software;
+		}
+		/* check if member is on a card with PCM support */
+		if (member->dsp->features.pcm_id < 0) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "dsp has no PCM bus\n",
+				    __func__, member->dsp->name);
+			goto conf_software;
+		}
+		/* check if relations are on the same PCM bus */
+		if (member->dsp->features.pcm_id != same_pcm) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s dsp %s cannot form a conf, because "
+				    "dsp is on a different PCM bus than the "
+				    "first dsp\n",
+				    __func__, member->dsp->name);
+			goto conf_software;
+		}
+		/* determine if members are on the same hfc chip */
+		if (same_hfc != member->dsp->features.hfc_id)
+			same_hfc = -1;
+		/* if there are members already in a conference */
+		if (current_conf < 0 && member->dsp->hfc_conf >= 0)
+			current_conf = member->dsp->hfc_conf;
+		/* if any member is not in a conference */
+		if (member->dsp->hfc_conf < 0)
+			all_conf = 0;
+
+		memb++;
+	}
+
+	/* if no member, this is an error */
+	if (memb < 1)
+		return;
+
+	/* one member */
+	if (memb == 1) {
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG
+			    "%s conf %d cannot form a HW conference, "
+			    "because dsp is alone\n", __func__, conf->id);
+		conf->hardware = 0;
+		conf->software = 0;
+		member = list_entry(conf->mlist.next, struct dsp_conf_member,
+			list);
+		dsp = member->dsp;
+		goto one_member;
+	}
+
+	/*
+	 * ok, now we are sure that all members are on the same pcm.
+	 * now we will see if we have only two members, so we can do
+	 * crossconnections, which don't have any limitations.
+	 */
+
+	/* if we have only two members */
+	if (memb == 2) {
+		member = list_entry(conf->mlist.next, struct dsp_conf_member,
+			list);
+		nextm = list_entry(member->list.next, struct dsp_conf_member,
+			list);
+		/* remove HFC conference if enabled */
+		if (member->dsp->hfc_conf >= 0) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s removing %s from HFC conf %d because "
+				    "two parties require only a PCM slot\n",
+				    __func__, member->dsp->name,
+				    member->dsp->hfc_conf);
+			dsp_cmx_hw_message(member->dsp,
+			    MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
+			member->dsp->hfc_conf = -1;
+		}
+		if (nextm->dsp->hfc_conf >= 0) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s removing %s from HFC conf %d because "
+				    "two parties require only a PCM slot\n",
+				    __func__, nextm->dsp->name,
+				    nextm->dsp->hfc_conf);
+			dsp_cmx_hw_message(nextm->dsp,
+			    MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
+			nextm->dsp->hfc_conf = -1;
+		}
+		/* if members have two banks (and not on the same chip) */
+		if (member->dsp->features.pcm_banks > 1 &&
+		    nextm->dsp->features.pcm_banks > 1 &&
+		    member->dsp->features.hfc_id !=
+		    nextm->dsp->features.hfc_id) {
+			/* if both members have same slots with crossed banks */
+			if (member->dsp->pcm_slot_tx >= 0 &&
+			    member->dsp->pcm_slot_rx >= 0 &&
+			    nextm->dsp->pcm_slot_tx >= 0 &&
+			    nextm->dsp->pcm_slot_rx >= 0 &&
+			    nextm->dsp->pcm_slot_tx ==
+			    member->dsp->pcm_slot_rx &&
+			    nextm->dsp->pcm_slot_rx ==
+			    member->dsp->pcm_slot_tx &&
+			    nextm->dsp->pcm_slot_tx ==
+			    member->dsp->pcm_slot_tx &&
+			    member->dsp->pcm_bank_tx !=
+			    member->dsp->pcm_bank_rx &&
+			    nextm->dsp->pcm_bank_tx !=
+			    nextm->dsp->pcm_bank_rx) {
+				/* all members have same slot */
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s dsp %s & %s stay joined on "
+					    "PCM slot %d bank %d (TX) bank %d "
+					    "(RX) (on different chips)\n",
+					    __func__,
+					    member->dsp->name,
+					    nextm->dsp->name,
+					    member->dsp->pcm_slot_tx,
+					    member->dsp->pcm_bank_tx,
+					    member->dsp->pcm_bank_rx);
+				conf->hardware = 0;
+				conf->software = 1;
+				return;
+			}
+			/* find a new slot */
+			memset(freeslots, 1, sizeof(freeslots));
+			list_for_each_entry(dsp, &dsp_ilist, list) {
+				if (dsp != member->dsp &&
+				    dsp != nextm->dsp &&
+				    member->dsp->features.pcm_id ==
+				    dsp->features.pcm_id) {
+					if (dsp->pcm_slot_rx >= 0 &&
+					    dsp->pcm_slot_rx <
+					    sizeof(freeslots))
+						freeslots[dsp->pcm_slot_tx] = 0;
+					if (dsp->pcm_slot_tx >= 0 &&
+					    dsp->pcm_slot_tx <
+					    sizeof(freeslots))
+						freeslots[dsp->pcm_slot_rx] = 0;
+				}
+			}
+			i = 0;
+			ii = member->dsp->features.pcm_slots;
+			while (i < ii) {
+				if (freeslots[i])
+					break;
+				i++;
+			}
+			if (i == ii) {
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s no slot available for "
+					    "%s & %s\n", __func__,
+					    member->dsp->name,
+					    nextm->dsp->name);
+				/* no more slots available */
+				goto conf_software;
+			}
+			/* assign free slot */
+			member->dsp->pcm_slot_tx = i;
+			member->dsp->pcm_slot_rx = i;
+			nextm->dsp->pcm_slot_tx = i;
+			nextm->dsp->pcm_slot_rx = i;
+			member->dsp->pcm_bank_rx = 0;
+			member->dsp->pcm_bank_tx = 1;
+			nextm->dsp->pcm_bank_rx = 1;
+			nextm->dsp->pcm_bank_tx = 0;
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s adding %s & %s to new PCM slot %d "
+				    "(TX and RX on different chips) because "
+				    "both members have not same slots\n",
+				    __func__,
+				    member->dsp->name,
+				    nextm->dsp->name,
+				    member->dsp->pcm_slot_tx);
+			dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+			    member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
+			    member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
+			dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
+			    nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
+			    nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
+			conf->hardware = 1;
+			conf->software = 0;
+			return;
+		/* if members have one bank (or on the same chip) */
+		} else {
+			/* if both members have different crossed slots */
+			if (member->dsp->pcm_slot_tx >= 0 &&
+			    member->dsp->pcm_slot_rx >= 0 &&
+			    nextm->dsp->pcm_slot_tx >= 0 &&
+			    nextm->dsp->pcm_slot_rx >= 0 &&
+			    nextm->dsp->pcm_slot_tx ==
+			    member->dsp->pcm_slot_rx &&
+			    nextm->dsp->pcm_slot_rx ==
+			    member->dsp->pcm_slot_tx &&
+			    member->dsp->pcm_slot_tx !=
+			    member->dsp->pcm_slot_rx &&
+			    member->dsp->pcm_bank_tx == 0 &&
+			    member->dsp->pcm_bank_rx == 0 &&
+			    nextm->dsp->pcm_bank_tx == 0 &&
+			    nextm->dsp->pcm_bank_rx == 0) {
+				/* all members have same slot */
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s dsp %s & %s stay joined on PCM "
+					    "slot %d (TX) %d (RX) on same chip "
+					    "or one bank PCM)\n", __func__,
+					    member->dsp->name,
+					    nextm->dsp->name,
+					    member->dsp->pcm_slot_tx,
+					    member->dsp->pcm_slot_rx);
+				conf->hardware = 0;
+				conf->software = 1;
+				return;
+			}
+			/* find two new slot */
+			memset(freeslots, 1, sizeof(freeslots));
+			list_for_each_entry(dsp, &dsp_ilist, list) {
+				if (dsp != member->dsp &&
+				    dsp != nextm->dsp &&
+				    member->dsp->features.pcm_id ==
+				    dsp->features.pcm_id) {
+					if (dsp->pcm_slot_rx >= 0 &&
+					    dsp->pcm_slot_rx <
+					    sizeof(freeslots))
+						freeslots[dsp->pcm_slot_tx] = 0;
+					if (dsp->pcm_slot_tx >= 0 &&
+					    dsp->pcm_slot_tx <
+					    sizeof(freeslots))
+						freeslots[dsp->pcm_slot_rx] = 0;
+				}
+			}
+			i1 = 0;
+			ii = member->dsp->features.pcm_slots;
+			while (i1 < ii) {
+				if (freeslots[i1])
+					break;
+				i1++;
+			}
+			if (i1 == ii) {
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s no slot available "
+					    "for %s & %s\n", __func__,
+					    member->dsp->name,
+					    nextm->dsp->name);
+				/* no more slots available */
+				goto conf_software;
+			}
+			i2 = i1+1;
+			while (i2 < ii) {
+				if (freeslots[i2])
+					break;
+				i2++;
+			}
+			if (i2 == ii) {
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s no slot available "
+					    "for %s & %s\n",
+					    __func__,
+					    member->dsp->name,
+					    nextm->dsp->name);
+				/* no more slots available */
+				goto conf_software;
+			}
+			/* assign free slots */
+			member->dsp->pcm_slot_tx = i1;
+			member->dsp->pcm_slot_rx = i2;
+			nextm->dsp->pcm_slot_tx = i2;
+			nextm->dsp->pcm_slot_rx = i1;
+			member->dsp->pcm_bank_rx = 0;
+			member->dsp->pcm_bank_tx = 0;
+			nextm->dsp->pcm_bank_rx = 0;
+			nextm->dsp->pcm_bank_tx = 0;
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s adding %s & %s to new PCM slot %d "
+				    "(TX) %d (RX) on same chip or one bank "
+				    "PCM, because both members have not "
+				    "crossed slots\n", __func__,
+				    member->dsp->name,
+				    nextm->dsp->name,
+				    member->dsp->pcm_slot_tx,
+				    member->dsp->pcm_slot_rx);
+			dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+			    member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
+			    member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
+			dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
+			    nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
+			    nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
+			conf->hardware = 1;
+			conf->software = 0;
+			return;
+		}
+	}
+
+	/*
+	 * if we have more than two, we may check if we have a conference
+	 * unit available on the chip. also all members must be on the same
+	 */
+
+	/* if not the same HFC chip */
+	if (same_hfc < 0) {
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG
+			    "%s conference %d cannot be formed, because "
+			    "members are on different chips or not "
+			    "on HFC chip\n",
+			    __func__, conf->id);
+		goto conf_software;
+	}
+
+	/* for more than two members.. */
+
+	/* in case of hdlc, we change to software */
+	if (dsp->hdlc)
+		goto conf_software;
+
+	/* if all members already have the same conference */
+	if (all_conf)
+		return;
+
+	/*
+	 * if there is an existing conference, but not all members have joined
+	 */
+	if (current_conf >= 0) {
+join_members:
+		list_for_each_entry(member, &conf->mlist, list) {
+			/* join to current conference */
+			if (member->dsp->hfc_conf == current_conf)
+				continue;
+			/* get a free timeslot first */
+			memset(freeslots, 1, sizeof(freeslots));
+			list_for_each_entry(dsp, &dsp_ilist, list) {
+				/*
+				 * not checking current member, because
+				 * slot will be overwritten.
+				 */
+				if (
+				    dsp != member->dsp &&
+				/* dsp must be on the same PCM */
+				    member->dsp->features.pcm_id ==
+				    dsp->features.pcm_id) {
+					/* dsp must be on a slot */
+					if (dsp->pcm_slot_tx >= 0 &&
+					    dsp->pcm_slot_tx <
+					    sizeof(freeslots))
+						freeslots[dsp->pcm_slot_tx] = 0;
+					if (dsp->pcm_slot_rx >= 0 &&
+					    dsp->pcm_slot_rx <
+					    sizeof(freeslots))
+						freeslots[dsp->pcm_slot_rx] = 0;
+				}
+			}
+			i = 0;
+			ii = member->dsp->features.pcm_slots;
+			while (i < ii) {
+				if (freeslots[i])
+					break;
+				i++;
+			}
+			if (i == ii) {
+				/* no more slots available */
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s conference %d cannot be formed,"
+					    " because no slot free\n",
+					    __func__, conf->id);
+				goto conf_software;
+			}
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "%s changing dsp %s to HW conference "
+				    "%d slot %d\n", __func__,
+				    member->dsp->name, current_conf, i);
+			/* assign free slot & set PCM & join conf */
+			member->dsp->pcm_slot_tx = i;
+			member->dsp->pcm_slot_rx = i;
+			member->dsp->pcm_bank_tx = 2; /* loop */
+			member->dsp->pcm_bank_rx = 2;
+			member->dsp->hfc_conf = current_conf;
+			dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+			    i, 2, i, 2);
+			dsp_cmx_hw_message(member->dsp,
+			    MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0);
+		}
+		return;
+	}
+
+	/*
+	 * no member is in a conference yet, so we find a free one
+	 */
+	memset(freeunits, 1, sizeof(freeunits));
+	list_for_each_entry(dsp, &dsp_ilist, list) {
+		/* dsp must be on the same chip */
+		if (dsp->features.hfc_id == same_hfc &&
+		    /* dsp must have joined a HW conference */
+		    dsp->hfc_conf >= 0 &&
+		    /* slot must be within range */
+		    dsp->hfc_conf < 8)
+			freeunits[dsp->hfc_conf] = 0;
+	}
+	i = 0;
+	ii = 8;
+	while (i < ii) {
+		if (freeunits[i])
+			break;
+		i++;
+	}
+	if (i == ii) {
+		/* no more conferences available */
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG
+			    "%s conference %d cannot be formed, because "
+			    "no conference number free\n",
+			    __func__, conf->id);
+		goto conf_software;
+	}
+	/* join all members */
+	current_conf = i;
+	goto join_members;
+}
+
+
+/*
+ * conf_id != 0: join or change conference
+ * conf_id == 0: split from conference if not already
+ */
+int
+dsp_cmx_conf(struct dsp *dsp, u32 conf_id)
+{
+	int err;
+	struct dsp_conf *conf;
+	struct dsp_conf_member	*member;
+
+	/* if conference doesn't change */
+	if (dsp->conf_id == conf_id)
+		return 0;
+
+	/* first remove us from current conf */
+	if (dsp->conf_id) {
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG "removing us from conference %d\n",
+				dsp->conf->id);
+		/* remove us from conf */
+		conf = dsp->conf;
+		err = dsp_cmx_del_conf_member(dsp);
+		if (err)
+			return err;
+		dsp->conf_id = 0;
+
+		/* update hardware */
+		dsp_cmx_hardware(NULL, dsp);
+
+		/* conf now empty? */
+		if (list_empty(&conf->mlist)) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "conference is empty, so we remove it.\n");
+			err = dsp_cmx_del_conf(conf);
+			if (err)
+				return err;
+		} else {
+			/* update members left on conf */
+			dsp_cmx_hardware(conf, NULL);
+		}
+	}
+
+	/* if split */
+	if (!conf_id)
+		return 0;
+
+	/* now add us to conf */
+	if (dsp_debug & DEBUG_DSP_CMX)
+		printk(KERN_DEBUG "searching conference %d\n",
+			conf_id);
+	conf = dsp_cmx_search_conf(conf_id);
+	if (!conf) {
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG
+			    "conference doesn't exist yet, creating.\n");
+		/* the conference doesn't exist, so we create */
+		conf = dsp_cmx_new_conf(conf_id);
+		if (!conf)
+			return -EINVAL;
+	} else if (!list_empty(&conf->mlist)) {
+		member = list_entry(conf->mlist.next, struct dsp_conf_member,
+			list);
+		if (dsp->hdlc && !member->dsp->hdlc) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "cannot join transparent conference.\n");
+			return -EINVAL;
+		}
+		if (!dsp->hdlc && member->dsp->hdlc) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "cannot join hdlc conference.\n");
+			return -EINVAL;
+		}
+	}
+	/* add conference member */
+	err = dsp_cmx_add_conf_member(dsp, conf);
+	if (err)
+		return err;
+	dsp->conf_id = conf_id;
+
+	/* if we are alone, we do nothing! */
+	if (list_empty(&conf->mlist)) {
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG
+			    "we are alone in this conference, so exit.\n");
+		/* update hardware */
+		dsp_cmx_hardware(NULL, dsp);
+		return 0;
+	}
+
+	/* update members on conf */
+	dsp_cmx_hardware(conf, NULL);
+
+	return 0;
+}
+
+
+/*
+ * audio data is received from card
+ */
+void
+dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
+{
+	u8 *d, *p;
+	int len = skb->len;
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+	int w, i, ii;
+
+	/* check if we have sompen */
+	if (len < 1)
+		return;
+
+	/* half of the buffer should be larger than maximum packet size */
+	if (len >= CMX_BUFF_HALF) {
+		printk(KERN_ERR
+		    "%s line %d: packet from card is too large (%d bytes). "
+		    "please make card send smaller packets OR increase "
+		    "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len);
+		return;
+	}
+
+	/*
+	 * initialize pointers if not already -
+	 * also add delay if requested by PH_SIGNAL
+	 */
+	if (dsp->rx_init) {
+		dsp->rx_init = 0;
+		if (dsp->features.unordered) {
+			dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+			dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+				& CMX_BUFF_MASK;
+		} else {
+			dsp->rx_R = 0;
+			dsp->rx_W = dsp->cmx_delay;
+		}
+	}
+	/* if frame contains time code, write directly */
+	if (dsp->features.unordered) {
+		dsp->rx_W = (hh->id & CMX_BUFF_MASK);
+		/* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */
+	}
+	/*
+	 * if we underrun (or maybe overrun),
+	 * we set our new read pointer, and write silence to buffer
+	 */
+	if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
+		if (dsp_debug & DEBUG_DSP_CMX)
+			printk(KERN_DEBUG
+			    "cmx_receive(dsp=%lx): UNDERRUN (or overrun the "
+			    "maximum delay), adjusting read pointer! "
+			    "(inst %s)\n", (u_long)dsp, dsp->name);
+		/* flush buffer */
+		if (dsp->features.unordered) {
+			dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+			dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+				& CMX_BUFF_MASK;
+		} else {
+			dsp->rx_R = 0;
+			dsp->rx_W = dsp->cmx_delay;
+		}
+		memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+	}
+	/* if we have reached double delay, jump back to middle */
+	if (dsp->cmx_delay)
+		if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >=
+		    (dsp->cmx_delay << 1)) {
+			if (dsp_debug & DEBUG_DSP_CMX)
+				printk(KERN_DEBUG
+				    "cmx_receive(dsp=%lx): OVERRUN (because "
+				    "twice the delay is reached), adjusting "
+				    "read pointer! (inst %s)\n",
+				    (u_long)dsp, dsp->name);
+		/* flush buffer */
+		if (dsp->features.unordered) {
+			dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+			dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+				& CMX_BUFF_MASK;
+		} else {
+			dsp->rx_R = 0;
+			dsp->rx_W = dsp->cmx_delay;
+		}
+		memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+	}
+
+	/* show where to write */
+#ifdef CMX_DEBUG
+	printk(KERN_DEBUG
+	    "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n",
+	    (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name);
+#endif
+
+	/* write data into rx_buffer */
+	p = skb->data;
+	d = dsp->rx_buff;
+	w = dsp->rx_W;
+	i = 0;
+	ii = len;
+	while (i < ii) {
+		d[w++ & CMX_BUFF_MASK] = *p++;
+		i++;
+	}
+
+	/* increase write-pointer */
+	dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
+}
+
+
+/*
+ * send (mixed) audio data to card and control jitter
+ */
+static void
+dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
+{
+	struct dsp_conf *conf = dsp->conf;
+	struct dsp *member, *other;
+	register s32 sample;
+	u8 *d, *p, *q, *o_q;
+	struct sk_buff *nskb, *txskb;
+	int r, rr, t, tt, o_r, o_rr;
+	int preload = 0;
+	struct mISDNhead *hh, *thh;
+
+	/* don't process if: */
+	if (!dsp->b_active) { /* if not active */
+		dsp->last_tx = 0;
+		return;
+	}
+	if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */
+	    dsp->tx_R == dsp->tx_W && /* AND no tx-data */
+	    !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */
+		dsp->last_tx = 0;
+		return;
+	}
+
+#ifdef CMX_DEBUG
+	printk(KERN_DEBUG
+	    "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n",
+	    members, dsp->name, conf, dsp->rx_R, dsp->rx_W);
+#endif
+
+	/* preload if we have delay set */
+	if (dsp->cmx_delay && !dsp->last_tx) {
+		preload = len;
+		if (preload < 128)
+			preload = 128;
+	}
+
+	/* PREPARE RESULT */
+	nskb = mI_alloc_skb(len + preload, GFP_ATOMIC);
+	if (!nskb) {
+		printk(KERN_ERR
+		    "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n",
+		    len + preload);
+		return;
+	}
+	hh = mISDN_HEAD_P(nskb);
+	hh->prim = PH_DATA_REQ;
+	hh->id = 0;
+	dsp->last_tx = 1;
+
+	/* set pointers, indexes and stuff */
+	member = dsp;
+	p = dsp->tx_buff; /* transmit data */
+	q = dsp->rx_buff; /* received data */
+	d = skb_put(nskb, preload + len); /* result */
+	t = dsp->tx_R; /* tx-pointers */
+	tt = dsp->tx_W;
+	r = dsp->rx_R; /* rx-pointers */
+	rr = (r + len) & CMX_BUFF_MASK;
+
+	/* preload with silence, if required */
+	if (preload) {
+		memset(d, dsp_silence, preload);
+		d += preload;
+	}
+
+	/* PROCESS TONES/TX-DATA ONLY */
+	if (dsp->tone.tone && dsp->tone.software) {
+		/* -> copy tone */
+		dsp_tone_copy(dsp, d, len);
+		dsp->tx_R = 0; /* clear tx buffer */
+		dsp->tx_W = 0;
+		goto send_packet;
+	}
+	/* if we have tx-data but do not use mixing */
+	if (!dsp->tx_mix && t != tt) {
+		/* -> send tx-data and continue when not enough */
+#ifdef CMX_TX_DEBUG
+	sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p);
+#endif
+		while (r != rr && t != tt) {
+#ifdef CMX_TX_DEBUG
+			if (strlen(debugbuf) < 48)
+			    sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]);
+#endif
+			*d++ = p[t]; /* write tx_buff */
+			t = (t+1) & CMX_BUFF_MASK;
+			r = (r+1) & CMX_BUFF_MASK;
+		}
+		if (r == rr) {
+			dsp->tx_R = t;
+#ifdef CMX_TX_DEBUG
+	printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+			goto send_packet;
+		}
+	}
+#ifdef CMX_TX_DEBUG
+	printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+	/* PROCESS DATA (one member / no conf) */
+	if (!conf || members <= 1) {
+		/* -> if echo is NOT enabled */
+		if (!dsp->echo) {
+			/* -> send tx-data if available or use 0-volume */
+			while (r != rr && t != tt) {
+				*d++ = p[t]; /* write tx_buff */
+				t = (t+1) & CMX_BUFF_MASK;
+				r = (r+1) & CMX_BUFF_MASK;
+			}
+			if (r != rr)
+				memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK);
+		/* -> if echo is enabled */
+		} else {
+			/*
+			 * -> mix tx-data with echo if available,
+			 * or use echo only
+			 */
+			while (r != rr && t != tt) {
+				*d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]];
+				t = (t+1) & CMX_BUFF_MASK;
+				r = (r+1) & CMX_BUFF_MASK;
+			}
+			while (r != rr) {
+				*d++ = q[r]; /* echo */
+				r = (r+1) & CMX_BUFF_MASK;
+			}
+		}
+		dsp->tx_R = t;
+		goto send_packet;
+	}
+	/* PROCESS DATA (two members) */
+#ifdef CMX_CONF_DEBUG
+	if (0) {
+#else
+	if (members == 2) {
+#endif
+		/* "other" becomes other party */
+		other = (list_entry(conf->mlist.next,
+		    struct dsp_conf_member, list))->dsp;
+		if (other == member)
+			other = (list_entry(conf->mlist.prev,
+			    struct dsp_conf_member, list))->dsp;
+		o_q = other->rx_buff; /* received data */
+		o_rr = (other->rx_R + len) & CMX_BUFF_MASK;
+			/* end of rx-pointer */
+		o_r = (o_rr - rr + r) & CMX_BUFF_MASK;
+			/* start rx-pointer at current read position*/
+		/* -> if echo is NOT enabled */
+		if (!dsp->echo) {
+			/*
+			 * -> copy other member's rx-data,
+			 * if tx-data is available, mix
+			 */
+			while (o_r != o_rr && t != tt) {
+				*d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]];
+				t = (t+1) & CMX_BUFF_MASK;
+				o_r = (o_r+1) & CMX_BUFF_MASK;
+			}
+			while (o_r != o_rr) {
+				*d++ = o_q[o_r];
+				o_r = (o_r+1) & CMX_BUFF_MASK;
+			}
+		/* -> if echo is enabled */
+		} else {
+			/*
+			 * -> mix other member's rx-data with echo,
+			 * if tx-data is available, mix
+			 */
+			while (r != rr && t != tt) {
+				sample = dsp_audio_law_to_s32[p[t]] +
+				    dsp_audio_law_to_s32[q[r]] +
+				    dsp_audio_law_to_s32[o_q[o_r]];
+				if (sample < -32768)
+					sample = -32768;
+				else if (sample > 32767)
+					sample = 32767;
+				*d++ = dsp_audio_s16_to_law[sample & 0xffff];
+				    /* tx-data + rx_data + echo */
+				t = (t+1) & CMX_BUFF_MASK;
+				r = (r+1) & CMX_BUFF_MASK;
+				o_r = (o_r+1) & CMX_BUFF_MASK;
+			}
+			while (r != rr) {
+				*d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]];
+				r = (r+1) & CMX_BUFF_MASK;
+				o_r = (o_r+1) & CMX_BUFF_MASK;
+			}
+		}
+		dsp->tx_R = t;
+		goto send_packet;
+	}
+#ifdef DSP_NEVER_DEFINED
+	}
+#endif
+	/* PROCESS DATA (three or more members) */
+	/* -> if echo is NOT enabled */
+	if (!dsp->echo) {
+		/*
+		 * -> substract rx-data from conf-data,
+		 * if tx-data is available, mix
+		 */
+		while (r != rr && t != tt) {
+			sample = dsp_audio_law_to_s32[p[t]] + *c++ -
+			    dsp_audio_law_to_s32[q[r]];
+			if (sample < -32768)
+				sample = -32768;
+			else if (sample > 32767)
+				sample = 32767;
+			*d++ = dsp_audio_s16_to_law[sample & 0xffff];
+			    /* conf-rx+tx */
+			r = (r+1) & CMX_BUFF_MASK;
+			t = (t+1) & CMX_BUFF_MASK;
+		}
+		while (r != rr) {
+			sample = *c++ - dsp_audio_law_to_s32[q[r]];
+			if (sample < -32768)
+				sample = -32768;
+			else if (sample > 32767)
+				sample = 32767;
+			*d++ = dsp_audio_s16_to_law[sample & 0xffff];
+			    /* conf-rx */
+			r = (r+1) & CMX_BUFF_MASK;
+		}
+	/* -> if echo is enabled */
+	} else {
+		/*
+		 * -> encode conf-data, if tx-data
+		 * is available, mix
+		 */
+		while (r != rr && t != tt) {
+			sample = dsp_audio_law_to_s32[p[t]] + *c++;
+			if (sample < -32768)
+				sample = -32768;
+			else if (sample > 32767)
+				sample = 32767;
+			*d++ = dsp_audio_s16_to_law[sample & 0xffff];
+			    /* conf(echo)+tx */
+			t = (t+1) & CMX_BUFF_MASK;
+			r = (r+1) & CMX_BUFF_MASK;
+		}
+		while (r != rr) {
+			sample = *c++;
+			if (sample < -32768)
+				sample = -32768;
+			else if (sample > 32767)
+				sample = 32767;
+			*d++ = dsp_audio_s16_to_law[sample & 0xffff];
+			    /* conf(echo) */
+			r = (r+1) & CMX_BUFF_MASK;
+		}
+	}
+	dsp->tx_R = t;
+	goto send_packet;
+
+send_packet:
+	/*
+	 * send tx-data if enabled - don't filter,
+	 * becuase we want what we send, not what we filtered
+	 */
+	if (dsp->tx_data) {
+		/* PREPARE RESULT */
+		txskb = mI_alloc_skb(len, GFP_ATOMIC);
+		if (!txskb) {
+			printk(KERN_ERR
+			    "FATAL ERROR in mISDN_dsp.o: "
+			    "cannot alloc %d bytes\n", len);
+		} else {
+			thh = mISDN_HEAD_P(txskb);
+			thh->prim = DL_DATA_REQ;
+			thh->id = 0;
+			memcpy(skb_put(txskb, len), nskb->data+preload, len);
+			/* queue (trigger later) */
+			skb_queue_tail(&dsp->sendq, txskb);
+		}
+	}
+	/* adjust volume */
+	if (dsp->tx_volume)
+		dsp_change_volume(nskb, dsp->tx_volume);
+	/* pipeline */
+	if (dsp->pipeline.inuse)
+		dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len);
+	/* crypt */
+	if (dsp->bf_enable)
+		dsp_bf_encrypt(dsp, nskb->data, nskb->len);
+	/* queue and trigger */
+	skb_queue_tail(&dsp->sendq, nskb);
+	schedule_work(&dsp->workq);
+}
+
+u32	samplecount;
+struct timer_list dsp_spl_tl;
+u32	dsp_spl_jiffies; /* calculate the next time to fire */
+u32	dsp_start_jiffies; /* jiffies at the time, the calculation begins */
+struct timeval dsp_start_tv; /* time at start of calculation */
+
+void
+dsp_cmx_send(void *arg)
+{
+	struct dsp_conf *conf;
+	struct dsp_conf_member *member;
+	struct dsp *dsp;
+	int mustmix, members;
+	s32 mixbuffer[MAX_POLL+100], *c;
+	u8 *p, *q;
+	int r, rr;
+	int jittercheck = 0, delay, i;
+	u_long flags;
+	struct timeval tv;
+	u32 elapsed;
+	s16 length;
+
+	/* lock */
+	spin_lock_irqsave(&dsp_lock, flags);
+
+	if (!dsp_start_tv.tv_sec) {
+		do_gettimeofday(&dsp_start_tv);
+		length = dsp_poll;
+	} else {
+		do_gettimeofday(&tv);
+		elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000)
+		    + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125));
+		dsp_start_tv.tv_sec = tv.tv_sec;
+		dsp_start_tv.tv_usec = tv.tv_usec;
+		length = elapsed;
+	}
+	if (length > MAX_POLL + 100)
+		length = MAX_POLL + 100;
+/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n",
+ length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16,
+ dsp_poll_diff & 0xffff);
+ */
+
+	/*
+	 * check if jitter needs to be checked
+	 * (this is about every second = 8192 samples)
+	 */
+	samplecount += length;
+	if ((samplecount & 8191) < length)
+		jittercheck = 1;
+
+	/* loop all members that do not require conference mixing */
+	list_for_each_entry(dsp, &dsp_ilist, list) {
+		if (dsp->hdlc)
+			continue;
+		conf = dsp->conf;
+		mustmix = 0;
+		members = 0;
+		if (conf) {
+			members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+			if (conf->software && members > 1)
+#else
+			if (conf->software && members > 2)
+#endif
+				mustmix = 1;
+		}
+
+		/* transmission required */
+		if (!mustmix) {
+			dsp_cmx_send_member(dsp, length, mixbuffer, members);
+
+			/*
+			 * unused mixbuffer is given to prevent a
+			 * potential null-pointer-bug
+			 */
+		}
+	}
+
+	/* loop all members that require conference mixing */
+	list_for_each_entry(conf, &conf_ilist, list) {
+		/* count members and check hardware */
+		members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+		if (conf->software && members > 1) {
+#else
+		if (conf->software && members > 2) {
+#endif
+			/* check for hdlc conf */
+			member = list_entry(conf->mlist.next,
+				struct dsp_conf_member, list);
+			if (member->dsp->hdlc)
+				continue;
+			/* mix all data */
+			memset(mixbuffer, 0, length*sizeof(s32));
+			list_for_each_entry(member, &conf->mlist, list) {
+				dsp = member->dsp;
+				/* get range of data to mix */
+				c = mixbuffer;
+				q = dsp->rx_buff;
+				r = dsp->rx_R;
+				rr = (r + length) & CMX_BUFF_MASK;
+				/* add member's data */
+				while (r != rr) {
+					*c++ += dsp_audio_law_to_s32[q[r]];
+					r = (r+1) & CMX_BUFF_MASK;
+				}
+			}
+
+			/* process each member */
+			list_for_each_entry(member, &conf->mlist, list) {
+				/* transmission */
+				dsp_cmx_send_member(member->dsp, length,
+				    mixbuffer, members);
+			}
+		}
+	}
+
+	/* delete rx-data, increment buffers, change pointers */
+	list_for_each_entry(dsp, &dsp_ilist, list) {
+		if (dsp->hdlc)
+			continue;
+		p = dsp->rx_buff;
+		q = dsp->tx_buff;
+		r = dsp->rx_R;
+		/* move receive pointer when receiving */
+		if (!dsp->rx_is_off) {
+			rr = (r + length) & CMX_BUFF_MASK;
+			/* delete rx-data */
+			while (r != rr) {
+				p[r] = dsp_silence;
+				r = (r+1) & CMX_BUFF_MASK;
+			}
+			/* increment rx-buffer pointer */
+			dsp->rx_R = r; /* write incremented read pointer */
+		}
+
+		/* check current rx_delay */
+		delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK;
+		if (delay >= CMX_BUFF_HALF)
+			delay = 0; /* will be the delay before next write */
+		/* check for lower delay */
+		if (delay < dsp->rx_delay[0])
+			dsp->rx_delay[0] = delay;
+		/* check current tx_delay */
+		delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK;
+		if (delay >= CMX_BUFF_HALF)
+			delay = 0; /* will be the delay before next write */
+		/* check for lower delay */
+		if (delay < dsp->tx_delay[0])
+			dsp->tx_delay[0] = delay;
+		if (jittercheck) {
+			/* find the lowest of all rx_delays */
+			delay = dsp->rx_delay[0];
+			i = 1;
+			while (i < MAX_SECONDS_JITTER_CHECK) {
+				if (delay > dsp->rx_delay[i])
+					delay = dsp->rx_delay[i];
+				i++;
+			}
+			/*
+			 * remove rx_delay only if we have delay AND we
+			 * have not preset cmx_delay
+			 */
+			if (delay && !dsp->cmx_delay) {
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s lowest rx_delay of %d bytes for"
+					    " dsp %s are now removed.\n",
+					    __func__, delay,
+					    dsp->name);
+				r = dsp->rx_R;
+				rr = (r + delay) & CMX_BUFF_MASK;
+				/* delete rx-data */
+				while (r != rr) {
+					p[r] = dsp_silence;
+					r = (r+1) & CMX_BUFF_MASK;
+				}
+				/* increment rx-buffer pointer */
+				dsp->rx_R = r;
+				    /* write incremented read pointer */
+			}
+			/* find the lowest of all tx_delays */
+			delay = dsp->tx_delay[0];
+			i = 1;
+			while (i < MAX_SECONDS_JITTER_CHECK) {
+				if (delay > dsp->tx_delay[i])
+					delay = dsp->tx_delay[i];
+				i++;
+			}
+			/*
+			 * remove delay only if we have delay AND we
+			 * have enabled tx_dejitter
+			 */
+			if (delay && dsp->tx_dejitter) {
+				if (dsp_debug & DEBUG_DSP_CMX)
+					printk(KERN_DEBUG
+					    "%s lowest tx_delay of %d bytes for"
+					    " dsp %s are now removed.\n",
+					    __func__, delay,
+					    dsp->name);
+				r = dsp->tx_R;
+				rr = (r + delay) & CMX_BUFF_MASK;
+				/* delete tx-data */
+				while (r != rr) {
+					q[r] = dsp_silence;
+					r = (r+1) & CMX_BUFF_MASK;
+				}
+				/* increment rx-buffer pointer */
+				dsp->tx_R = r;
+				    /* write incremented read pointer */
+			}
+			/* scroll up delays */
+			i = MAX_SECONDS_JITTER_CHECK - 1;
+			while (i) {
+				dsp->rx_delay[i] = dsp->rx_delay[i-1];
+				dsp->tx_delay[i] = dsp->tx_delay[i-1];
+				i--;
+			}
+			dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+			dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+		}
+	}
+
+	/* if next event would be in the past ... */
+	if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0)
+		dsp_spl_jiffies = jiffies + 1;
+	else
+		dsp_spl_jiffies += dsp_tics;
+
+	dsp_spl_tl.expires = dsp_spl_jiffies;
+	add_timer(&dsp_spl_tl);
+
+	/* unlock */
+	spin_unlock_irqrestore(&dsp_lock, flags);
+}
+
+/*
+ * audio data is transmitted from upper layer to the dsp
+ */
+void
+dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
+{
+	u_int w, ww;
+	u8 *d, *p;
+	int space; /* todo: , l = skb->len; */
+#ifdef CMX_TX_DEBUG
+	char debugbuf[256] = "";
+#endif
+
+	/* check if there is enough space, and then copy */
+	w = dsp->tx_W;
+	ww = dsp->tx_R;
+	p = dsp->tx_buff;
+	d = skb->data;
+	space = ww-w;
+	if (space <= 0)
+		space += CMX_BUFF_SIZE;
+	/* write-pointer should not overrun nor reach read pointer */
+	if (space-1 < skb->len)
+		/* write to the space we have left */
+		ww = (ww - 1) & CMX_BUFF_MASK;
+	else
+		/* write until all byte are copied */
+		ww = (w + skb->len) & CMX_BUFF_MASK;
+	dsp->tx_W = ww;
+
+	/* show current buffer */
+#ifdef CMX_DEBUG
+	printk(KERN_DEBUG
+	    "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n",
+	    (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name);
+#endif
+
+	/* copy transmit data to tx-buffer */
+#ifdef CMX_TX_DEBUG
+	sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p);
+#endif
+	while (w != ww) {
+#ifdef CMX_TX_DEBUG
+		if (strlen(debugbuf) < 48)
+			sprintf(debugbuf+strlen(debugbuf), " %02x", *d);
+#endif
+		p[w] = *d++;
+		w = (w+1) & CMX_BUFF_MASK;
+	}
+#ifdef CMX_TX_DEBUG
+	printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+}
+
+/*
+ * hdlc data is received from card and sent to all members.
+ */
+void
+dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb)
+{
+	struct sk_buff *nskb = NULL;
+	struct dsp_conf_member *member;
+	struct mISDNhead *hh;
+
+	/* not if not active */
+	if (!dsp->b_active)
+		return;
+
+	/* check if we have sompen */
+	if (skb->len < 1)
+		return;
+
+	/* no conf */
+	if (!dsp->conf) {
+		/* in case of hardware (echo) */
+		if (dsp->pcm_slot_tx >= 0)
+			return;
+		if (dsp->echo)
+			nskb = skb_clone(skb, GFP_ATOMIC);
+			if (nskb) {
+				hh = mISDN_HEAD_P(nskb);
+				hh->prim = PH_DATA_REQ;
+				hh->id = 0;
+				skb_queue_tail(&dsp->sendq, nskb);
+				schedule_work(&dsp->workq);
+			}
+		return;
+	}
+	/* in case of hardware conference */
+	if (dsp->conf->hardware)
+		return;
+	list_for_each_entry(member, &dsp->conf->mlist, list) {
+		if (dsp->echo || member->dsp != dsp) {
+			nskb = skb_clone(skb, GFP_ATOMIC);
+			if (nskb) {
+				hh = mISDN_HEAD_P(nskb);
+				hh->prim = PH_DATA_REQ;
+				hh->id = 0;
+				skb_queue_tail(&member->dsp->sendq, nskb);
+				schedule_work(&member->dsp->workq);
+			}
+		}
+	}
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
new file mode 100644
index 0000000..2f10ed8
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -0,0 +1,1191 @@
+/*
+ * Author       Andreas Eversberg (jolly@eversberg.eu)
+ * Based on source code structure by
+ *		Karsten Keil (keil@isdn4linux.de)
+ *
+ *		This file is (c) under GNU PUBLIC LICENSE
+ *		For changes and modifications please read
+ *		../../../Documentation/isdn/mISDN.cert
+ *
+ * Thanks to    Karsten Keil (great drivers)
+ *              Cologne Chip (great chips)
+ *
+ * This module does:
+ *		Real-time tone generation
+ *		DTMF detection
+ *		Real-time cross-connection and conferrence
+ *		Compensate jitter due to system load and hardware fault.
+ *		All features are done in kernel space and will be realized
+ *		using hardware, if available and supported by chip set.
+ *		Blowfish encryption/decryption
+ */
+
+/* STRUCTURE:
+ *
+ * The dsp module provides layer 2 for b-channels (64kbit). It provides
+ * transparent audio forwarding with special digital signal processing:
+ *
+ * - (1) generation of tones
+ * - (2) detection of dtmf tones
+ * - (3) crossconnecting and conferences (clocking)
+ * - (4) echo generation for delay test
+ * - (5) volume control
+ * - (6) disable receive data
+ * - (7) pipeline
+ * - (8) encryption/decryption
+ *
+ * Look:
+ *             TX            RX
+ *         ------upper layer------
+ *             |             ^
+ *             |             |(6)
+ *             v             |
+ *       +-----+-------------+-----+
+ *       |(3)(4)                   |
+ *       |           CMX           |
+ *       |                         |
+ *       |           +-------------+
+ *       |           |       ^
+ *       |           |       |
+ *       |+---------+|  +----+----+
+ *       ||(1)      ||  |(2)      |
+ *       ||         ||  |         |
+ *       ||  Tones  ||  |  DTMF   |
+ *       ||         ||  |         |
+ *       ||         ||  |         |
+ *       |+----+----+|  +----+----+
+ *       +-----+-----+       ^
+ *             |             |
+ *             v             |
+ *        +----+----+   +----+----+
+ *        |(5)      |   |(5)      |
+ *        |         |   |         |
+ *        |TX Volume|   |RX Volume|
+ *        |         |   |         |
+ *        |         |   |         |
+ *        +----+----+   +----+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *        +----+-------------+----+
+ *        |(7)                    |
+ *        |                       |
+ *        |  Pipeline Processing  |
+ *        |                       |
+ *        |                       |
+ *        +----+-------------+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *        +----+----+   +----+----+
+ *        |(8)      |   |(8)      |
+ *        |         |   |         |
+ *        | Encrypt |   | Decrypt |
+ *        |         |   |         |
+ *        |         |   |         |
+ *        +----+----+   +----+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *         ------card  layer------
+ *             TX            RX
+ *
+ * Above you can see the logical data flow. If software is used to do the
+ * process, it is actually the real data flow. If hardware is used, data
+ * may not flow, but hardware commands to the card, to provide the data flow
+ * as shown.
+ *
+ * NOTE: The channel must be activated in order to make dsp work, even if
+ * no data flow to the upper layer is intended. Activation can be done
+ * after and before controlling the setting using PH_CONTROL requests.
+ *
+ * DTMF: Will be detected by hardware if possible. It is done before CMX
+ * processing.
+ *
+ * Tones: Will be generated via software if endless looped audio fifos are
+ * not supported by hardware. Tones will override all data from CMX.
+ * It is not required to join a conference to use tones at any time.
+ *
+ * CMX: Is transparent when not used. When it is used, it will do
+ * crossconnections and conferences via software if not possible through
+ * hardware. If hardware capability is available, hardware is used.
+ *
+ * Echo: Is generated by CMX and is used to check performane of hard and
+ * software CMX.
+ *
+ * The CMX has special functions for conferences with one, two and more
+ * members. It will allow different types of data flow. Receive and transmit
+ * data to/form upper layer may be swithed on/off individually without loosing
+ * features of CMX, Tones and DTMF.
+ *
+ * Echo Cancellation: Sometimes we like to cancel echo from the interface.
+ * Note that a VoIP call may not have echo caused by the IP phone. The echo
+ * is generated by the telephone line connected to it. Because the delay
+ * is high, it becomes an echo. RESULT: Echo Cachelation is required if
+ * both echo AND delay is applied to an interface.
+ * Remember that software CMX always generates a more or less delay.
+ *
+ * If all used features can be realized in hardware, and if transmit and/or
+ * receive data ist disabled, the card may not send/receive any data at all.
+ * Not receiving is usefull if only announcements are played. Not sending is
+ * usefull if an answering machine records audio. Not sending and receiving is
+ * usefull during most states of the call. If supported by hardware, tones
+ * will be played without cpu load. Small PBXs and NT-Mode applications will
+ * not need expensive hardware when processing calls.
+ *
+ *
+ * LOCKING:
+ *
+ * When data is received from upper or lower layer (card), the complete dsp
+ * module is locked by a global lock.  This lock MUST lock irq, because it
+ * must lock timer events by DSP poll timer.
+ * When data is ready to be transmitted down, the data is queued and sent
+ * outside lock and timer event.
+ * PH_CONTROL must not change any settings, join or split conference members
+ * during process of data.
+ *
+ * HDLC:
+ *
+ * It works quite the same as transparent, except that HDLC data is forwarded
+ * to all other conference members if no hardware bridging is possible.
+ * Send data will be writte to sendq. Sendq will be sent if confirm is received.
+ * Conference cannot join, if one member is not hdlc.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include "core.h"
+#include "dsp.h"
+
+const char *mISDN_dsp_revision = "2.0";
+
+static int debug;
+static int options;
+static int poll;
+static int dtmfthreshold = 100;
+
+MODULE_AUTHOR("Andreas Eversberg");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(options, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR);
+MODULE_LICENSE("GPL");
+
+/*int spinnest = 0;*/
+
+spinlock_t dsp_lock; /* global dsp lock */
+struct list_head dsp_ilist;
+struct list_head conf_ilist;
+int dsp_debug;
+int dsp_options;
+int dsp_poll, dsp_tics;
+
+/* check if rx may be turned off or must be turned on */
+static void
+dsp_rx_off_member(struct dsp *dsp)
+{
+	struct mISDN_ctrl_req	cq;
+	int rx_off = 1;
+
+	if (!dsp->features_rx_off)
+		return;
+
+	/* not disabled */
+	if (!dsp->rx_disabled)
+		rx_off = 0;
+	/* software dtmf */
+	else if (dsp->dtmf.software)
+		rx_off = 0;
+	/* echo in software */
+	else if (dsp->echo && dsp->pcm_slot_tx < 0)
+		rx_off = 0;
+	/* bridge in software */
+	else if (dsp->conf) {
+		if (dsp->conf->software)
+			rx_off = 0;
+	}
+
+	if (rx_off == dsp->rx_is_off)
+		return;
+
+	if (!dsp->ch.peer) {
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: no peer, no rx_off\n",
+				__func__);
+		return;
+	}
+	cq.op = MISDN_CTRL_RX_OFF;
+	cq.p1 = rx_off;
+	if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
+		printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+			__func__);
+		return;
+	}
+	dsp->rx_is_off = rx_off;
+	if (dsp_debug & DEBUG_DSP_CORE)
+		printk(KERN_DEBUG "%s: %s set rx_off = %d\n",
+			__func__, dsp->name, rx_off);
+}
+static void
+dsp_rx_off(struct dsp *dsp)
+{
+	struct dsp_conf_member	*member;
+
+	if (dsp_options & DSP_OPT_NOHARDWARE)
+		return;
+
+	/* no conf */
+	if (!dsp->conf) {
+		dsp_rx_off_member(dsp);
+		return;
+	}
+	/* check all members in conf */
+	list_for_each_entry(member, &dsp->conf->mlist, list) {
+		dsp_rx_off_member(member->dsp);
+	}
+}
+
+static int
+dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb)
+{
+	struct		sk_buff *nskb;
+	int ret = 0;
+	int cont;
+	u8 *data;
+	int len;
+
+	if (skb->len < sizeof(int))
+		printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__);
+	cont = *((int *)skb->data);
+	len = skb->len - sizeof(int);
+	data = skb->data + sizeof(int);
+
+	switch (cont) {
+	case DTMF_TONE_START: /* turn on DTMF */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: start dtmf\n", __func__);
+		if (len == sizeof(int)) {
+			printk(KERN_NOTICE "changing DTMF Threshold "
+				"to %d\n", *((int *)data));
+			dsp->dtmf.treshold = (*(int *)data) * 10000;
+		}
+		/* init goertzel */
+		dsp_dtmf_goertzel_init(dsp);
+
+		/* check dtmf hardware */
+		dsp_dtmf_hardware(dsp);
+		break;
+	case DTMF_TONE_STOP: /* turn off DTMF */
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: stop dtmf\n", __func__);
+		dsp->dtmf.hardware = 0;
+		dsp->dtmf.software = 0;
+		break;
+	case DSP_CONF_JOIN: /* join / update conference */
+		if (len < sizeof(int)) {
+			ret = -EINVAL;
+			break;
+		}
+		if (*((u32 *)data) == 0)
+			goto conf_split;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: join conference %d\n",
+				__func__, *((u32 *)data));
+		ret = dsp_cmx_conf(dsp, *((u32 *)data));
+			/* dsp_cmx_hardware will also be called here */
+		dsp_rx_off(dsp);
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		break;
+	case DSP_CONF_SPLIT: /* remove from conference */
+conf_split:
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: release conference\n", __func__);
+		ret = dsp_cmx_conf(dsp, 0);
+			/* dsp_cmx_hardware will also be called here */
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		dsp_rx_off(dsp);
+		break;
+	case DSP_TONE_PATT_ON: /* play tone */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (len < sizeof(int)) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: turn tone 0x%x on\n",
+				__func__, *((int *)skb->data));
+		ret = dsp_tone(dsp, *((int *)data));
+		if (!ret) {
+			dsp_cmx_hardware(dsp->conf, dsp);
+			dsp_rx_off(dsp);
+		}
+		if (!dsp->tone.tone)
+			goto tone_off;
+		break;
+	case DSP_TONE_PATT_OFF: /* stop tone */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: turn tone off\n", __func__);
+		dsp_tone(dsp, 0);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		/* reset tx buffers (user space data) */
+tone_off:
+		dsp->rx_W = 0;
+		dsp->rx_R = 0;
+		break;
+	case DSP_VOL_CHANGE_TX: /* change volume */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (len < sizeof(int)) {
+			ret = -EINVAL;
+			break;
+		}
+		dsp->tx_volume = *((int *)data);
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: change tx vol to %d\n",
+				__func__, dsp->tx_volume);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_dtmf_hardware(dsp);
+		dsp_rx_off(dsp);
+		break;
+	case DSP_VOL_CHANGE_RX: /* change volume */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (len < sizeof(int)) {
+			ret = -EINVAL;
+			break;
+		}
+		dsp->rx_volume = *((int *)data);
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: change rx vol to %d\n",
+				__func__, dsp->tx_volume);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_dtmf_hardware(dsp);
+		dsp_rx_off(dsp);
+		break;
+	case DSP_ECHO_ON: /* enable echo */
+		dsp->echo = 1; /* soft echo */
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		break;
+	case DSP_ECHO_OFF: /* disable echo */
+		dsp->echo = 0;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		break;
+	case DSP_RECEIVE_ON: /* enable receive to user space */
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: enable receive to user "
+				"space\n", __func__);
+		dsp->rx_disabled = 0;
+		dsp_rx_off(dsp);
+		break;
+	case DSP_RECEIVE_OFF: /* disable receive to user space */
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: disable receive to "
+				"user space\n", __func__);
+		dsp->rx_disabled = 1;
+		dsp_rx_off(dsp);
+		break;
+	case DSP_MIX_ON: /* enable mixing of tx data */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: enable mixing of "
+				"tx-data with conf mebers\n", __func__);
+		dsp->tx_mix = 1;
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		break;
+	case DSP_MIX_OFF: /* disable mixing of tx data */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: disable mixing of "
+				"tx-data with conf mebers\n", __func__);
+		dsp->tx_mix = 0;
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		break;
+	case DSP_TXDATA_ON: /* enable txdata */
+		dsp->tx_data = 1;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: enable tx-data\n", __func__);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		break;
+	case DSP_TXDATA_OFF: /* disable txdata */
+		dsp->tx_data = 0;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: disable tx-data\n", __func__);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		if (dsp_debug & DEBUG_DSP_CMX)
+			dsp_cmx_debug(dsp);
+		break;
+	case DSP_DELAY: /* use delay algorithm instead of dynamic
+			   jitter algorithm */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (len < sizeof(int)) {
+			ret = -EINVAL;
+			break;
+		}
+		dsp->cmx_delay = (*((int *)data)) << 3;
+			/* miliseconds to samples */
+		if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1))
+			/* clip to half of maximum usable buffer
+			(half of half buffer) */
+			dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: use delay algorithm to "
+				"compensate jitter (%d samples)\n",
+				__func__, dsp->cmx_delay);
+		break;
+	case DSP_JITTER: /* use dynamic jitter algorithm instead of
+		    delay algorithm */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		dsp->cmx_delay = 0;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: use jitter algorithm to "
+				"compensate jitter\n", __func__);
+		break;
+	case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		dsp->tx_dejitter = 1;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: use dejitter on TX "
+				"buffer\n", __func__);
+		break;
+	case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		dsp->tx_dejitter = 0;
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: use TX buffer without "
+				"dejittering\n", __func__);
+		break;
+	case DSP_PIPELINE_CFG:
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (len > 0 && ((char *)data)[len - 1]) {
+			printk(KERN_DEBUG "%s: pipeline config string "
+				"is not NULL terminated!\n", __func__);
+			ret = -EINVAL;
+		} else {
+			dsp->pipeline.inuse = 1;
+			dsp_cmx_hardware(dsp->conf, dsp);
+			ret = dsp_pipeline_build(&dsp->pipeline,
+				len > 0 ? (char *)data : NULL);
+			dsp_cmx_hardware(dsp->conf, dsp);
+			dsp_rx_off(dsp);
+		}
+		break;
+	case DSP_BF_ENABLE_KEY: /* turn blowfish on */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (len < 4 || len > 56) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: turn blowfish on (key "
+				"not shown)\n", __func__);
+		ret = dsp_bf_init(dsp, (u8 *)data, len);
+		/* set new cont */
+		if (!ret)
+			cont = DSP_BF_ACCEPT;
+		else
+			cont = DSP_BF_REJECT;
+		/* send indication if it worked to set it */
+		nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY,
+			sizeof(int), &cont, GFP_ATOMIC);
+		if (nskb) {
+			if (dsp->up) {
+				if (dsp->up->send(dsp->up, nskb))
+					dev_kfree_skb(nskb);
+			} else
+				dev_kfree_skb(nskb);
+		}
+		if (!ret) {
+			dsp_cmx_hardware(dsp->conf, dsp);
+			dsp_dtmf_hardware(dsp);
+			dsp_rx_off(dsp);
+		}
+		break;
+	case DSP_BF_DISABLE: /* turn blowfish off */
+		if (dsp->hdlc) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: turn blowfish off\n", __func__);
+		dsp_bf_cleanup(dsp);
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_dtmf_hardware(dsp);
+		dsp_rx_off(dsp);
+		break;
+	default:
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: ctrl req %x unhandled\n",
+				__func__, cont);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static void
+get_features(struct mISDNchannel *ch)
+{
+	struct dsp		*dsp = container_of(ch, struct dsp, ch);
+	struct mISDN_ctrl_req	cq;
+
+	if (dsp_options & DSP_OPT_NOHARDWARE)
+		return;
+	if (!ch->peer) {
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: no peer, no features\n",
+				__func__);
+		return;
+	}
+	memset(&cq, 0, sizeof(cq));
+	cq.op = MISDN_CTRL_GETOP;
+	if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) {
+		printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+			__func__);
+		return;
+	}
+	if (cq.op & MISDN_CTRL_RX_OFF)
+		dsp->features_rx_off = 1;
+	if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) {
+		cq.op = MISDN_CTRL_HW_FEATURES;
+		*((u_long *)&cq.p1) = (u_long)&dsp->features;
+		if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) {
+			printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+				__func__);
+		}
+	} else
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: features not supported for %s\n",
+				__func__, dsp->name);
+}
+
+static int
+dsp_function(struct mISDNchannel *ch,  struct sk_buff *skb)
+{
+	struct dsp			*dsp = container_of(ch, struct dsp, ch);
+	struct mISDNhead	*hh;
+	int			ret = 0;
+	u8			*digits;
+	int			cont;
+	struct			sk_buff *nskb;
+	u_long			flags;
+
+	hh = mISDN_HEAD_P(skb);
+	switch (hh->prim) {
+	/* FROM DOWN */
+	case (PH_DATA_CNF):
+		dsp->data_pending = 0;
+		/* trigger next hdlc frame, if any */
+		if (dsp->hdlc) {
+			spin_lock_irqsave(&dsp_lock, flags);
+			if (dsp->b_active)
+				schedule_work(&dsp->workq);
+			spin_unlock_irqrestore(&dsp_lock, flags);
+		}
+		break;
+	case (PH_DATA_IND):
+	case (DL_DATA_IND):
+		if (skb->len < 1) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp->rx_is_off) {
+			if (dsp_debug & DEBUG_DSP_CORE)
+				printk(KERN_DEBUG "%s: rx-data during rx_off"
+					" for %s\n",
+				__func__, dsp->name);
+		}
+		if (dsp->hdlc) {
+			/* hdlc */
+			spin_lock_irqsave(&dsp_lock, flags);
+			dsp_cmx_hdlc(dsp, skb);
+			spin_unlock_irqrestore(&dsp_lock, flags);
+			if (dsp->rx_disabled) {
+				/* if receive is not allowed */
+				break;
+			}
+			hh->prim = DL_DATA_IND;
+			if (dsp->up)
+				return dsp->up->send(dsp->up, skb);
+			break;
+		}
+
+		/* decrypt if enabled */
+		if (dsp->bf_enable)
+			dsp_bf_decrypt(dsp, skb->data, skb->len);
+		/* pipeline */
+		if (dsp->pipeline.inuse)
+			dsp_pipeline_process_rx(&dsp->pipeline, skb->data,
+				skb->len);
+		/* change volume if requested */
+		if (dsp->rx_volume)
+			dsp_change_volume(skb, dsp->rx_volume);
+
+		/* check if dtmf soft decoding is turned on */
+		if (dsp->dtmf.software) {
+			digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
+				skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
+			while (*digits) {
+				if (dsp_debug & DEBUG_DSP_DTMF)
+					printk(KERN_DEBUG "%s: digit"
+					    "(%c) to layer %s\n",
+					    __func__, *digits, dsp->name);
+				cont = DTMF_TONE_VAL | *digits;
+				nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+				    MISDN_ID_ANY, sizeof(int), &cont,
+				    GFP_ATOMIC);
+				if (nskb) {
+					if (dsp->up) {
+						if (dsp->up->send(
+						    dsp->up, nskb))
+						dev_kfree_skb(nskb);
+					} else
+						dev_kfree_skb(nskb);
+				}
+				digits++;
+			}
+		}
+		/* we need to process receive data if software */
+		spin_lock_irqsave(&dsp_lock, flags);
+		if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) {
+			/* process data from card at cmx */
+			dsp_cmx_receive(dsp, skb);
+		}
+		spin_unlock_irqrestore(&dsp_lock, flags);
+
+		if (dsp->rx_disabled) {
+			/* if receive is not allowed */
+			break;
+		}
+		hh->prim = DL_DATA_IND;
+		if (dsp->up)
+			return dsp->up->send(dsp->up, skb);
+		break;
+	case (PH_CONTROL_IND):
+		if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+			printk(KERN_DEBUG "%s: PH_CONTROL INDICATION "
+				"received: %x (len %d) %s\n", __func__,
+				hh->id, skb->len, dsp->name);
+		switch (hh->id) {
+		case (DTMF_HFC_COEF): /* getting coefficients */
+			if (!dsp->dtmf.hardware) {
+				if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+					printk(KERN_DEBUG "%s: ignoring DTMF "
+						"coefficients from HFC\n",
+						__func__);
+				break;
+			}
+			digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
+				skb->len, 2);
+			while (*digits) {
+				int k;
+				struct sk_buff *nskb;
+				if (dsp_debug & DEBUG_DSP_DTMF)
+					printk(KERN_DEBUG "%s: digit"
+					    "(%c) to layer %s\n",
+					    __func__, *digits, dsp->name);
+				k = *digits | DTMF_TONE_VAL;
+				nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+					MISDN_ID_ANY, sizeof(int), &k,
+					GFP_ATOMIC);
+				if (nskb) {
+					if (dsp->up) {
+						if (dsp->up->send(
+						    dsp->up, nskb))
+						dev_kfree_skb(nskb);
+					} else
+						dev_kfree_skb(nskb);
+				}
+				digits++;
+			}
+			break;
+		case (HFC_VOL_CHANGE_TX): /* change volume */
+			if (skb->len != sizeof(int)) {
+				ret = -EINVAL;
+				break;
+			}
+			spin_lock_irqsave(&dsp_lock, flags);
+			dsp->tx_volume = *((int *)skb->data);
+			if (dsp_debug & DEBUG_DSP_CORE)
+				printk(KERN_DEBUG "%s: change tx volume to "
+					"%d\n", __func__, dsp->tx_volume);
+			dsp_cmx_hardware(dsp->conf, dsp);
+			dsp_dtmf_hardware(dsp);
+			dsp_rx_off(dsp);
+			spin_unlock_irqrestore(&dsp_lock, flags);
+			break;
+		default:
+			if (dsp_debug & DEBUG_DSP_CORE)
+				printk(KERN_DEBUG "%s: ctrl ind %x unhandled "
+					"%s\n", __func__, hh->id, dsp->name);
+			ret = -EINVAL;
+		}
+		break;
+	case (PH_ACTIVATE_IND):
+	case (PH_ACTIVATE_CNF):
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: b_channel is now active %s\n",
+				__func__, dsp->name);
+		/* bchannel now active */
+		spin_lock_irqsave(&dsp_lock, flags);
+		dsp->b_active = 1;
+		dsp->data_pending = 0;
+		dsp->rx_init = 1;
+			/* rx_W and rx_R will be adjusted on first frame */
+		dsp->rx_W = 0;
+		dsp->rx_R = 0;
+		memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_dtmf_hardware(dsp);
+		dsp_rx_off(dsp);
+		spin_unlock_irqrestore(&dsp_lock, flags);
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: done with activation, sending "
+				"confirm to user space. %s\n", __func__,
+				dsp->name);
+		/* send activation to upper layer */
+		hh->prim = DL_ESTABLISH_CNF;
+		if (dsp->up)
+			return dsp->up->send(dsp->up, skb);
+		break;
+	case (PH_DEACTIVATE_IND):
+	case (PH_DEACTIVATE_CNF):
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: b_channel is now inactive %s\n",
+				__func__, dsp->name);
+		/* bchannel now inactive */
+		spin_lock_irqsave(&dsp_lock, flags);
+		dsp->b_active = 0;
+		dsp->data_pending = 0;
+		dsp_cmx_hardware(dsp->conf, dsp);
+		dsp_rx_off(dsp);
+		spin_unlock_irqrestore(&dsp_lock, flags);
+		hh->prim = DL_RELEASE_CNF;
+		if (dsp->up)
+			return dsp->up->send(dsp->up, skb);
+		break;
+	/* FROM UP */
+	case (DL_DATA_REQ):
+	case (PH_DATA_REQ):
+		if (skb->len < 1) {
+			ret = -EINVAL;
+			break;
+		}
+		if (dsp->hdlc) {
+			/* hdlc */
+			spin_lock_irqsave(&dsp_lock, flags);
+			if (dsp->b_active) {
+				skb_queue_tail(&dsp->sendq, skb);
+				schedule_work(&dsp->workq);
+			}
+			spin_unlock_irqrestore(&dsp_lock, flags);
+			return 0;
+		}
+		/* send data to tx-buffer (if no tone is played) */
+		if (!dsp->tone.tone) {
+			spin_lock_irqsave(&dsp_lock, flags);
+			dsp_cmx_transmit(dsp, skb);
+			spin_unlock_irqrestore(&dsp_lock, flags);
+		}
+		break;
+	case (PH_CONTROL_REQ):
+		spin_lock_irqsave(&dsp_lock, flags);
+		ret = dsp_control_req(dsp, hh, skb);
+		spin_unlock_irqrestore(&dsp_lock, flags);
+		break;
+	case (DL_ESTABLISH_REQ):
+	case (PH_ACTIVATE_REQ):
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: activating b_channel %s\n",
+				__func__, dsp->name);
+		if (dsp->dtmf.hardware || dsp->dtmf.software)
+			dsp_dtmf_goertzel_init(dsp);
+		get_features(ch);
+		/* send ph_activate */
+		hh->prim = PH_ACTIVATE_REQ;
+		if (ch->peer)
+			return ch->recv(ch->peer, skb);
+		break;
+	case (DL_RELEASE_REQ):
+	case (PH_DEACTIVATE_REQ):
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: releasing b_channel %s\n",
+				__func__, dsp->name);
+		spin_lock_irqsave(&dsp_lock, flags);
+		dsp->tone.tone = 0;
+		dsp->tone.hardware = 0;
+		dsp->tone.software = 0;
+		if (timer_pending(&dsp->tone.tl))
+			del_timer(&dsp->tone.tl);
+		if (dsp->conf)
+			dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be
+						 called here */
+		skb_queue_purge(&dsp->sendq);
+		spin_unlock_irqrestore(&dsp_lock, flags);
+		hh->prim = PH_DEACTIVATE_REQ;
+		if (ch->peer)
+			return ch->recv(ch->peer, skb);
+		break;
+	default:
+		if (dsp_debug & DEBUG_DSP_CORE)
+			printk(KERN_DEBUG "%s: msg %x unhandled %s\n",
+				__func__, hh->prim, dsp->name);
+		ret = -EINVAL;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct dsp		*dsp = container_of(ch, struct dsp, ch);
+	u_long		flags;
+	int		err = 0;
+
+	if (debug & DEBUG_DSP_CTRL)
+	printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		break;
+	case CLOSE_CHANNEL:
+		if (dsp->ch.peer)
+			dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL);
+
+		/* wait until workqueue has finished,
+		 * must lock here, or we may hit send-process currently
+		 * queueing. */
+		spin_lock_irqsave(&dsp_lock, flags);
+		dsp->b_active = 0;
+		spin_unlock_irqrestore(&dsp_lock, flags);
+		/* MUST not be locked, because it waits until queue is done. */
+		cancel_work_sync(&dsp->workq);
+		spin_lock_irqsave(&dsp_lock, flags);
+		if (timer_pending(&dsp->tone.tl))
+			del_timer(&dsp->tone.tl);
+		skb_queue_purge(&dsp->sendq);
+		if (dsp_debug & DEBUG_DSP_CTRL)
+			printk(KERN_DEBUG "%s: releasing member %s\n",
+				__func__, dsp->name);
+		dsp->b_active = 0;
+		dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called
+					 here */
+		dsp_pipeline_destroy(&dsp->pipeline);
+
+		if (dsp_debug & DEBUG_DSP_CTRL)
+			printk(KERN_DEBUG "%s: remove & destroy object %s\n",
+				__func__, dsp->name);
+		list_del(&dsp->list);
+		spin_unlock_irqrestore(&dsp_lock, flags);
+
+		if (dsp_debug & DEBUG_DSP_CTRL)
+			printk(KERN_DEBUG "%s: dsp instance released\n",
+				__func__);
+		vfree(dsp);
+		module_put(THIS_MODULE);
+		break;
+	}
+	return err;
+}
+
+static void
+dsp_send_bh(struct work_struct *work)
+{
+	struct dsp *dsp = container_of(work, struct dsp, workq);
+	struct sk_buff *skb;
+	struct mISDNhead	*hh;
+
+	if (dsp->hdlc && dsp->data_pending)
+		return; /* wait until data has been acknowledged */
+
+	/* send queued data */
+	while ((skb = skb_dequeue(&dsp->sendq))) {
+		/* in locked date, we must have still data in queue */
+		if (dsp->data_pending) {
+			if (dsp_debug & DEBUG_DSP_CORE)
+				printk(KERN_DEBUG "%s: fifo full %s, this is "
+					"no bug!\n", __func__, dsp->name);
+			/* flush transparent data, if not acked */
+			dev_kfree_skb(skb);
+			continue;
+		}
+		hh = mISDN_HEAD_P(skb);
+		if (hh->prim == DL_DATA_REQ) {
+			/* send packet up */
+			if (dsp->up) {
+				if (dsp->up->send(dsp->up, skb))
+					dev_kfree_skb(skb);
+			} else
+				dev_kfree_skb(skb);
+		} else {
+			/* send packet down */
+			if (dsp->ch.peer) {
+				dsp->data_pending = 1;
+				if (dsp->ch.recv(dsp->ch.peer, skb)) {
+					dev_kfree_skb(skb);
+					dsp->data_pending = 0;
+				}
+			} else
+				dev_kfree_skb(skb);
+		}
+	}
+}
+
+static int
+dspcreate(struct channel_req *crq)
+{
+	struct dsp		*ndsp;
+	u_long		flags;
+
+	if (crq->protocol != ISDN_P_B_L2DSP
+	 && crq->protocol != ISDN_P_B_L2DSPHDLC)
+		return -EPROTONOSUPPORT;
+	ndsp = vmalloc(sizeof(struct dsp));
+	if (!ndsp) {
+		printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__);
+		return -ENOMEM;
+	}
+	memset(ndsp, 0, sizeof(struct dsp));
+	if (dsp_debug & DEBUG_DSP_CTRL)
+		printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__);
+
+	/* default enabled */
+	INIT_WORK(&ndsp->workq, (void *)dsp_send_bh);
+	skb_queue_head_init(&ndsp->sendq);
+	ndsp->ch.send = dsp_function;
+	ndsp->ch.ctrl = dsp_ctrl;
+	ndsp->up = crq->ch;
+	crq->ch = &ndsp->ch;
+	if (crq->protocol == ISDN_P_B_L2DSP) {
+		crq->protocol = ISDN_P_B_RAW;
+		ndsp->hdlc = 0;
+	} else {
+		crq->protocol = ISDN_P_B_HDLC;
+		ndsp->hdlc = 1;
+	}
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s:cannot get module\n",
+			__func__);
+
+	sprintf(ndsp->name, "DSP_C%x(0x%p)",
+		ndsp->up->st->dev->id + 1, ndsp);
+	/* set frame size to start */
+	ndsp->features.hfc_id = -1; /* current PCM id */
+	ndsp->features.pcm_id = -1; /* current PCM id */
+	ndsp->pcm_slot_rx = -1; /* current CPM slot */
+	ndsp->pcm_slot_tx = -1;
+	ndsp->pcm_bank_rx = -1;
+	ndsp->pcm_bank_tx = -1;
+	ndsp->hfc_conf = -1; /* current conference number */
+	/* set tone timer */
+	ndsp->tone.tl.function = (void *)dsp_tone_timeout;
+	ndsp->tone.tl.data = (long) ndsp;
+	init_timer(&ndsp->tone.tl);
+
+	if (dtmfthreshold < 20 || dtmfthreshold > 500)
+		dtmfthreshold = 200;
+	ndsp->dtmf.treshold = dtmfthreshold*10000;
+
+	/* init pipeline append to list */
+	spin_lock_irqsave(&dsp_lock, flags);
+	dsp_pipeline_init(&ndsp->pipeline);
+	list_add_tail(&ndsp->list, &dsp_ilist);
+	spin_unlock_irqrestore(&dsp_lock, flags);
+
+	return 0;
+}
+
+
+static struct Bprotocol DSP = {
+	.Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK))
+		| (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)),
+	.name = "dsp",
+	.create = dspcreate
+};
+
+static int dsp_init(void)
+{
+	int err;
+	int tics;
+
+	printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision);
+
+	dsp_options = options;
+	dsp_debug = debug;
+
+	/* set packet size */
+	dsp_poll = poll;
+	if (dsp_poll) {
+		if (dsp_poll > MAX_POLL) {
+			printk(KERN_ERR "%s: Wrong poll value (%d), use %d "
+				"maximum.\n", __func__, poll, MAX_POLL);
+			err = -EINVAL;
+			return err;
+		}
+		if (dsp_poll < 8) {
+			printk(KERN_ERR "%s: Wrong poll value (%d), use 8 "
+				"minimum.\n", __func__, dsp_poll);
+			err = -EINVAL;
+			return err;
+		}
+		dsp_tics = poll * HZ / 8000;
+		if (dsp_tics * 8000 != poll * HZ) {
+			printk(KERN_INFO "mISDN_dsp: Cannot clock every %d "
+				"samples (0,125 ms). It is not a multiple of "
+				"%d HZ.\n", poll, HZ);
+			err = -EINVAL;
+			return err;
+		}
+	} else {
+		poll = 8;
+		while (poll <= MAX_POLL) {
+			tics = poll * HZ / 8000;
+			if (tics * 8000 == poll * HZ) {
+				dsp_tics = tics;
+				dsp_poll = poll;
+				if (poll >= 64)
+					break;
+			}
+			poll++;
+		}
+	}
+	if (dsp_poll == 0) {
+		printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel "
+			"clock that equals exactly the duration of 8-256 "
+			"samples. (Choose kernel clock speed like 100, 250, "
+			"300, 1000)\n");
+		err = -EINVAL;
+		return err;
+	}
+	printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals "
+		"%d jiffies.\n", dsp_poll, dsp_tics);
+
+	spin_lock_init(&dsp_lock);
+	INIT_LIST_HEAD(&dsp_ilist);
+	INIT_LIST_HEAD(&conf_ilist);
+
+	/* init conversion tables */
+	dsp_audio_generate_law_tables();
+	dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a;
+	dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32:
+		dsp_audio_alaw_to_s32;
+	dsp_audio_generate_s2law_table();
+	dsp_audio_generate_seven();
+	dsp_audio_generate_mix_table();
+	if (dsp_options & DSP_OPT_ULAW)
+		dsp_audio_generate_ulaw_samples();
+	dsp_audio_generate_volume_changes();
+
+	err = dsp_pipeline_module_init();
+	if (err) {
+		printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, "
+			"error(%d)\n", err);
+		return err;
+	}
+
+	err = mISDN_register_Bprotocol(&DSP);
+	if (err) {
+		printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err);
+		return err;
+	}
+
+	/* set sample timer */
+	dsp_spl_tl.function = (void *)dsp_cmx_send;
+	dsp_spl_tl.data = 0;
+	init_timer(&dsp_spl_tl);
+	dsp_spl_tl.expires = jiffies + dsp_tics;
+	dsp_spl_jiffies = dsp_spl_tl.expires;
+	add_timer(&dsp_spl_tl);
+
+	return 0;
+}
+
+
+static void dsp_cleanup(void)
+{
+	mISDN_unregister_Bprotocol(&DSP);
+
+	if (timer_pending(&dsp_spl_tl))
+		del_timer(&dsp_spl_tl);
+
+	if (!list_empty(&dsp_ilist)) {
+		printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not "
+			"empty.\n");
+	}
+	if (!list_empty(&conf_ilist)) {
+		printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not "
+			"all memory freed.\n");
+	}
+
+	dsp_pipeline_module_exit();
+}
+
+module_init(dsp_init);
+module_exit(dsp_cleanup);
+
diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c
new file mode 100644
index 0000000..efc371c
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_dtmf.c
@@ -0,0 +1,303 @@
+/*
+ * DTMF decoder.
+ *
+ * Copyright            by Andreas Eversberg (jolly@eversberg.eu)
+ *			based on different decoders such as ISDN4Linux
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+#define NCOEFF            8     /* number of frequencies to be analyzed */
+
+/* For DTMF recognition:
+ * 2 * cos(2 * PI * k / N) precalculated for all k
+ */
+static u64 cos2pik[NCOEFF] =
+{
+	/* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */
+	55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630
+};
+
+/* digit matrix */
+static char dtmf_matrix[4][4] =
+{
+	{'1', '2', '3', 'A'},
+	{'4', '5', '6', 'B'},
+	{'7', '8', '9', 'C'},
+	{'*', '0', '#', 'D'}
+};
+
+/* dtmf detection using goertzel algorithm
+ * init function
+ */
+void dsp_dtmf_goertzel_init(struct dsp *dsp)
+{
+	dsp->dtmf.size = 0;
+	dsp->dtmf.lastwhat = '\0';
+	dsp->dtmf.lastdigit = '\0';
+	dsp->dtmf.count = 0;
+}
+
+/* check for hardware or software features
+ */
+void dsp_dtmf_hardware(struct dsp *dsp)
+{
+	int hardware = 1;
+
+	if (!dsp->features.hfc_dtmf)
+		hardware = 0;
+
+	/* check for volume change */
+	if (dsp->tx_volume) {
+		if (dsp_debug & DEBUG_DSP_DTMF)
+			printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+				"because tx_volume is changed\n",
+				__func__, dsp->name);
+		hardware = 0;
+	}
+	if (dsp->rx_volume) {
+		if (dsp_debug & DEBUG_DSP_DTMF)
+			printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+				"because rx_volume is changed\n",
+				__func__, dsp->name);
+		hardware = 0;
+	}
+	/* check if encryption is enabled */
+	if (dsp->bf_enable) {
+		if (dsp_debug & DEBUG_DSP_DTMF)
+			printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+				"because encryption is enabled\n",
+				__func__, dsp->name);
+		hardware = 0;
+	}
+	/* check if pipeline exists */
+	if (dsp->pipeline.inuse) {
+		if (dsp_debug & DEBUG_DSP_DTMF)
+			printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+				"because pipeline exists.\n",
+				__func__, dsp->name);
+		hardware = 0;
+	}
+
+	dsp->dtmf.hardware = hardware;
+	dsp->dtmf.software = !hardware;
+}
+
+
+/*************************************************************
+ * calculate the coefficients of the given sample and decode *
+ *************************************************************/
+
+/* the given sample is decoded. if the sample is not long enough for a
+ * complete frame, the decoding is finished and continued with the next
+ * call of this function.
+ *
+ * the algorithm is very good for detection with a minimum of errors. i
+ * tested it allot. it even works with very short tones (40ms). the only
+ * disadvantage is, that it doesn't work good with different volumes of both
+ * tones. this will happen, if accoustically coupled dialers are used.
+ * it sometimes detects tones during speach, which is normal for decoders.
+ * use sequences to given commands during calls.
+ *
+ * dtmf - points to a structure of the current dtmf state
+ * spl and len - the sample
+ * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder
+ */
+
+u8
+*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt)
+{
+	u8 what;
+	int size;
+	signed short *buf;
+	s32 sk, sk1, sk2;
+	int k, n, i;
+	s32 *hfccoeff;
+	s32 result[NCOEFF], tresh, treshl;
+	int lowgroup, highgroup;
+	s64 cos2pik_;
+
+	dsp->dtmf.digits[0] = '\0';
+
+	/* Note: The function will loop until the buffer has not enough samples
+	 * left to decode a full frame.
+	 */
+again:
+	/* convert samples */
+	size = dsp->dtmf.size;
+	buf = dsp->dtmf.buffer;
+	switch (fmt) {
+	case 0: /* alaw */
+	case 1: /* ulaw */
+		while (size < DSP_DTMF_NPOINTS && len) {
+			buf[size++] = dsp_audio_law_to_s32[*data++];
+			len--;
+		}
+		break;
+
+	case 2: /* HFC coefficients */
+	default:
+		if (len < 64) {
+			if (len > 0)
+				printk(KERN_ERR "%s: coefficients have invalid "
+					"size. (is=%d < must=%d)\n",
+					__func__, len, 64);
+			return dsp->dtmf.digits;
+		}
+		hfccoeff = (s32 *)data;
+		for (k = 0; k < NCOEFF; k++) {
+			sk2 = (*hfccoeff++)>>4;
+			sk = (*hfccoeff++)>>4;
+			if (sk > 32767 || sk < -32767 || sk2 > 32767
+			    || sk2 < -32767)
+				printk(KERN_WARNING
+					"DTMF-Detection overflow\n");
+			/* compute |X(k)|**2 */
+			result[k] =
+				 (sk * sk) -
+				 (((cos2pik[k] * sk) >> 15) * sk2) +
+				 (sk2 * sk2);
+		}
+		data += 64;
+		len -= 64;
+		goto coefficients;
+		break;
+	}
+	dsp->dtmf.size = size;
+
+	if (size < DSP_DTMF_NPOINTS)
+		return dsp->dtmf.digits;
+
+	dsp->dtmf.size = 0;
+
+	/* now we have a full buffer of signed long samples - we do goertzel */
+	for (k = 0; k < NCOEFF; k++) {
+		sk = 0;
+		sk1 = 0;
+		sk2 = 0;
+		buf = dsp->dtmf.buffer;
+		cos2pik_ = cos2pik[k];
+		for (n = 0; n < DSP_DTMF_NPOINTS; n++) {
+			sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++);
+			sk2 = sk1;
+			sk1 = sk;
+		}
+		sk >>= 8;
+		sk2 >>= 8;
+		if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767)
+			printk(KERN_WARNING "DTMF-Detection overflow\n");
+		/* compute |X(k)|**2 */
+		result[k] =
+			(sk * sk) -
+			(((cos2pik[k] * sk) >> 15) * sk2) +
+			(sk2 * sk2);
+	}
+
+	/* our (squared) coefficients have been calculated, we need to process
+	 * them.
+	 */
+coefficients:
+	tresh = 0;
+	for (i = 0; i < NCOEFF; i++) {
+		if (result[i] < 0)
+			result[i] = 0;
+		if (result[i] > dsp->dtmf.treshold) {
+			if (result[i] > tresh)
+				tresh = result[i];
+		}
+	}
+
+	if (tresh == 0) {
+		what = 0;
+		goto storedigit;
+	}
+
+	if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+		printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d"
+			" tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n",
+			result[0]/10000, result[1]/10000, result[2]/10000,
+			result[3]/10000, result[4]/10000, result[5]/10000,
+			result[6]/10000, result[7]/10000, tresh/10000,
+			result[0]/(tresh/100), result[1]/(tresh/100),
+			result[2]/(tresh/100), result[3]/(tresh/100),
+			result[4]/(tresh/100), result[5]/(tresh/100),
+			result[6]/(tresh/100), result[7]/(tresh/100));
+
+	/* calc digit (lowgroup/highgroup) */
+	lowgroup = -1;
+	highgroup = -1;
+	treshl = tresh >> 3;  /* tones which are not on, must be below 9 dB */
+	tresh = tresh >> 2;  /* touchtones must match within 6 dB */
+	for (i = 0; i < NCOEFF; i++) {
+		if (result[i] < treshl)
+			continue;  /* ignore */
+		if (result[i] < tresh) {
+			lowgroup = -1;
+			highgroup = -1;
+			break;  /* noise inbetween */
+		}
+		/* good level found. This is allowed only one time per group */
+		if (i < NCOEFF/2) {
+			/* lowgroup */
+			if (lowgroup >= 0) {
+				/* Bad. Another tone found. */
+				lowgroup = -1;
+				break;
+			} else
+				lowgroup = i;
+		} else {
+			/* higroup */
+			if (highgroup >= 0) {
+				/* Bad. Another tone found. */
+				highgroup = -1;
+				break;
+			} else
+				highgroup = i-(NCOEFF/2);
+		}
+	}
+
+	/* get digit or null */
+	what = 0;
+	if (lowgroup >= 0 && highgroup >= 0)
+		what = dtmf_matrix[lowgroup][highgroup];
+
+storedigit:
+	if (what && (dsp_debug & DEBUG_DSP_DTMF))
+		printk(KERN_DEBUG "DTMF what: %c\n", what);
+
+	if (dsp->dtmf.lastwhat != what)
+		dsp->dtmf.count = 0;
+
+	/* the tone (or no tone) must remain 3 times without change */
+	if (dsp->dtmf.count == 2) {
+		if (dsp->dtmf.lastdigit != what) {
+			dsp->dtmf.lastdigit = what;
+			if (what) {
+				if (dsp_debug & DEBUG_DSP_DTMF)
+					printk(KERN_DEBUG "DTMF digit: %c\n",
+						what);
+				if ((strlen(dsp->dtmf.digits)+1)
+					< sizeof(dsp->dtmf.digits)) {
+					dsp->dtmf.digits[strlen(
+						dsp->dtmf.digits)+1] = '\0';
+					dsp->dtmf.digits[strlen(
+						dsp->dtmf.digits)] = what;
+				}
+			}
+		}
+	} else
+		dsp->dtmf.count++;
+
+	dsp->dtmf.lastwhat = what;
+
+	goto again;
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h
new file mode 100644
index 0000000..8a20af4
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_ecdis.h
@@ -0,0 +1,110 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * ec_disable_detector.h - A detector which should eventually meet the
+ *                         G.164/G.165 requirements for detecting the
+ *                         2100Hz echo cancellor disable tone.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "dsp_biquad.h"
+
+struct ec_disable_detector_state {
+	struct biquad2_state notch;
+	int notch_level;
+	int channel_level;
+	int tone_present;
+	int tone_cycle_duration;
+	int good_cycles;
+	int hit;
+};
+
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+static inline void
+echo_can_disable_detector_init(struct ec_disable_detector_state *det)
+{
+    /* Elliptic notch */
+    /* This is actually centred at 2095Hz, but gets the balance we want, due
+       to the asymmetric walls of the notch */
+	biquad2_init(&det->notch,
+		(int32_t) (-0.7600000*32768.0),
+		(int32_t) (-0.1183852*32768.0),
+		(int32_t) (-0.5104039*32768.0),
+		(int32_t) (0.1567596*32768.0),
+		(int32_t) (1.0000000*32768.0));
+
+	det->channel_level = 0;
+	det->notch_level = 0;
+	det->tone_present = FALSE;
+	det->tone_cycle_duration = 0;
+	det->good_cycles = 0;
+	det->hit = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static inline int
+echo_can_disable_detector_update(struct ec_disable_detector_state *det,
+int16_t amp)
+{
+	int16_t notched;
+
+	notched = biquad2(&det->notch, amp);
+	/* Estimate the overall energy in the channel, and the energy in
+	   the notch (i.e. overall channel energy - tone energy => noise).
+	   Use abs instead of multiply for speed (is it really faster?).
+	   Damp the overall energy a little more for a stable result.
+	   Damp the notch energy a little less, so we don't damp out the
+	   blip every time the phase reverses */
+	det->channel_level += ((abs(amp) - det->channel_level) >> 5);
+	det->notch_level += ((abs(notched) - det->notch_level) >> 4);
+	if (det->channel_level > 280) {
+		/* There is adequate energy in the channel.
+		 Is it mostly at 2100Hz? */
+		if (det->notch_level*6 < det->channel_level) {
+			/* The notch says yes, so we have the tone. */
+			if (!det->tone_present) {
+				/* Do we get a kick every 450+-25ms? */
+				if (det->tone_cycle_duration >= 425*8
+					&& det->tone_cycle_duration <= 475*8) {
+					det->good_cycles++;
+					if (det->good_cycles > 2)
+					det->hit = TRUE;
+				}
+				det->tone_cycle_duration = 0;
+			}
+			det->tone_present = TRUE;
+		} else
+			det->tone_present = FALSE;
+		det->tone_cycle_duration++;
+	} else {
+		det->tone_present = FALSE;
+		det->tone_cycle_duration = 0;
+		det->good_cycles = 0;
+	}
+	return det->hit;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c
new file mode 100644
index 0000000..eb892d9
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_hwec.c
@@ -0,0 +1,138 @@
+/*
+ * dsp_hwec.c:
+ * builtin mISDN dsp pipeline element for enabling the hw echocanceller
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mISDNdsp.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+static struct mISDN_dsp_element_arg args[] = {
+	{ "deftaps", "128", "Set the number of taps of cancellation." },
+};
+
+static struct mISDN_dsp_element dsp_hwec_p = {
+	.name = "hwec",
+	.new = NULL,
+	.free = NULL,
+	.process_tx = NULL,
+	.process_rx = NULL,
+	.num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg),
+	.args = args,
+};
+struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p;
+
+void dsp_hwec_enable(struct dsp *dsp, const char *arg)
+{
+	int deftaps = 128,
+		len;
+	struct mISDN_ctrl_req	cq;
+
+	if (!dsp) {
+		printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n",
+			__func__);
+		return;
+	}
+
+	if (!arg)
+		goto _do;
+
+	len = strlen(arg);
+	if (!len)
+		goto _do;
+
+	{
+		char _dup[len + 1];
+		char *dup, *tok, *name, *val;
+		int tmp;
+
+		strcpy(_dup, arg);
+		dup = _dup;
+
+		while ((tok = strsep(&dup, ","))) {
+			if (!strlen(tok))
+				continue;
+			name = strsep(&tok, "=");
+			val = tok;
+
+			if (!val)
+				continue;
+
+			if (!strcmp(name, "deftaps")) {
+				if (sscanf(val, "%d", &tmp) == 1)
+					deftaps = tmp;
+			}
+		}
+	}
+
+_do:
+	printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n",
+		__func__, deftaps);
+	memset(&cq, 0, sizeof(cq));
+	cq.op = MISDN_CTRL_HFC_ECHOCAN_ON;
+	cq.p1 = deftaps;
+	if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+		printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+			__func__);
+		return;
+	}
+}
+
+void dsp_hwec_disable(struct dsp *dsp)
+{
+	struct mISDN_ctrl_req	cq;
+
+	if (!dsp) {
+		printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n",
+			__func__);
+		return;
+	}
+
+	printk(KERN_DEBUG "%s: disabling hwec\n", __func__);
+	memset(&cq, 0, sizeof(cq));
+	cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF;
+	if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+		printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+			__func__);
+		return;
+	}
+}
+
+int dsp_hwec_init(void)
+{
+	mISDN_dsp_element_register(dsp_hwec);
+
+	return 0;
+}
+
+void dsp_hwec_exit(void)
+{
+	mISDN_dsp_element_unregister(dsp_hwec);
+}
+
diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h
new file mode 100644
index 0000000..eebe80c
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_hwec.h
@@ -0,0 +1,10 @@
+/*
+ * dsp_hwec.h
+ */
+
+extern struct mISDN_dsp_element *dsp_hwec;
+extern void dsp_hwec_enable(struct dsp *dsp, const char *arg);
+extern void dsp_hwec_disable(struct dsp *dsp);
+extern int  dsp_hwec_init(void);
+extern void dsp_hwec_exit(void);
+
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
new file mode 100644
index 0000000..850260a
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_pipeline.c
@@ -0,0 +1,348 @@
+/*
+ * dsp_pipeline.c: pipelined audio processing
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+/* uncomment for debugging */
+/*#define PIPELINE_DEBUG*/
+
+struct dsp_pipeline_entry {
+	struct mISDN_dsp_element *elem;
+	void                *p;
+	struct list_head     list;
+};
+struct dsp_element_entry {
+	struct mISDN_dsp_element *elem;
+	struct device	     dev;
+	struct list_head     list;
+};
+
+static LIST_HEAD(dsp_elements);
+
+/* sysfs */
+static struct class *elements_class;
+
+static ssize_t
+attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
+	ssize_t len = 0;
+	int i = 0;
+
+	*buf = 0;
+	for (; i < elem->num_args; ++i)
+		len = sprintf(buf, "%sName:        %s\n%s%s%sDescription: %s\n"
+			"\n", buf,
+			  elem->args[i].name,
+			  elem->args[i].def ? "Default:     " : "",
+			  elem->args[i].def ? elem->args[i].def : "",
+			  elem->args[i].def ? "\n" : "",
+			  elem->args[i].desc);
+
+	return len;
+}
+
+static struct device_attribute element_attributes[] = {
+	__ATTR(args, 0444, attr_show_args, NULL),
+};
+
+int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
+{
+	struct dsp_element_entry *entry;
+	int ret, i;
+
+	if (!elem)
+		return -EINVAL;
+
+	entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->elem = elem;
+
+	entry->dev.class = elements_class;
+	dev_set_drvdata(&entry->dev, elem);
+	snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name);
+	ret = device_register(&entry->dev);
+	if (ret) {
+		printk(KERN_ERR "%s: failed to register %s\n",
+			__func__, elem->name);
+		goto err1;
+	}
+
+	for (i = 0; i < (sizeof(element_attributes)
+		/ sizeof(struct device_attribute)); ++i)
+		ret = device_create_file(&entry->dev,
+				&element_attributes[i]);
+		if (ret) {
+			printk(KERN_ERR "%s: failed to create device file\n",
+				__func__);
+			goto err2;
+		}
+
+	list_add_tail(&entry->list, &dsp_elements);
+
+	printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
+
+	return 0;
+
+err2:
+	device_unregister(&entry->dev);
+err1:
+	kfree(entry);
+	return ret;
+}
+EXPORT_SYMBOL(mISDN_dsp_element_register);
+
+void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
+{
+	struct dsp_element_entry *entry, *n;
+
+	if (!elem)
+		return;
+
+	list_for_each_entry_safe(entry, n, &dsp_elements, list)
+		if (entry->elem == elem) {
+			list_del(&entry->list);
+			device_unregister(&entry->dev);
+			kfree(entry);
+			printk(KERN_DEBUG "%s: %s unregistered\n",
+				__func__, elem->name);
+			return;
+		}
+	printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
+}
+EXPORT_SYMBOL(mISDN_dsp_element_unregister);
+
+int dsp_pipeline_module_init(void)
+{
+	elements_class = class_create(THIS_MODULE, "dsp_pipeline");
+	if (IS_ERR(elements_class))
+		return PTR_ERR(elements_class);
+
+#ifdef PIPELINE_DEBUG
+	printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
+#endif
+
+	dsp_hwec_init();
+
+	return 0;
+}
+
+void dsp_pipeline_module_exit(void)
+{
+	struct dsp_element_entry *entry, *n;
+
+	dsp_hwec_exit();
+
+	class_destroy(elements_class);
+
+	list_for_each_entry_safe(entry, n, &dsp_elements, list) {
+		list_del(&entry->list);
+		printk(KERN_WARNING "%s: element was still registered: %s\n",
+			__func__, entry->elem->name);
+		kfree(entry);
+	}
+
+	printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
+}
+
+int dsp_pipeline_init(struct dsp_pipeline *pipeline)
+{
+	if (!pipeline)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(&pipeline->list);
+
+#ifdef PIPELINE_DEBUG
+	printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
+#endif
+
+	return 0;
+}
+
+static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+	struct dsp_pipeline_entry *entry, *n;
+
+	list_for_each_entry_safe(entry, n, &pipeline->list, list) {
+		list_del(&entry->list);
+		if (entry->elem == dsp_hwec)
+			dsp_hwec_disable(container_of(pipeline, struct dsp,
+				pipeline));
+		else
+			entry->elem->free(entry->p);
+		kfree(entry);
+	}
+}
+
+void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+
+	if (!pipeline)
+		return;
+
+	_dsp_pipeline_destroy(pipeline);
+
+#ifdef PIPELINE_DEBUG
+	printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
+#endif
+}
+
+int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
+{
+	int len, incomplete = 0, found = 0;
+	char *dup, *tok, *name, *args;
+	struct dsp_element_entry *entry, *n;
+	struct dsp_pipeline_entry *pipeline_entry;
+	struct mISDN_dsp_element *elem;
+
+	if (!pipeline)
+		return -EINVAL;
+
+	if (!list_empty(&pipeline->list))
+		_dsp_pipeline_destroy(pipeline);
+
+	if (!cfg)
+		return 0;
+
+	len = strlen(cfg);
+	if (!len)
+		return 0;
+
+	dup = kmalloc(len + 1, GFP_KERNEL);
+	if (!dup)
+		return 0;
+	strcpy(dup, cfg);
+	while ((tok = strsep(&dup, "|"))) {
+		if (!strlen(tok))
+			continue;
+		name = strsep(&tok, "(");
+		args = strsep(&tok, ")");
+		if (args && !*args)
+			args = 0;
+
+		list_for_each_entry_safe(entry, n, &dsp_elements, list)
+			if (!strcmp(entry->elem->name, name)) {
+				elem = entry->elem;
+
+				pipeline_entry = kmalloc(sizeof(struct
+					dsp_pipeline_entry), GFP_KERNEL);
+				if (!pipeline_entry) {
+					printk(KERN_DEBUG "%s: failed to add "
+					    "entry to pipeline: %s (out of "
+					    "memory)\n", __func__, elem->name);
+					incomplete = 1;
+					goto _out;
+				}
+				pipeline_entry->elem = elem;
+
+				if (elem == dsp_hwec) {
+					/* This is a hack to make the hwec
+					   available as a pipeline module */
+					dsp_hwec_enable(container_of(pipeline,
+						struct dsp, pipeline), args);
+					list_add_tail(&pipeline_entry->list,
+						&pipeline->list);
+				} else {
+					pipeline_entry->p = elem->new(args);
+					if (pipeline_entry->p) {
+						list_add_tail(&pipeline_entry->
+							list, &pipeline->list);
+#ifdef PIPELINE_DEBUG
+						printk(KERN_DEBUG "%s: created "
+						    "instance of %s%s%s\n",
+						    __func__, name, args ?
+						    " with args " : "", args ?
+						    args : "");
+#endif
+					} else {
+						printk(KERN_DEBUG "%s: failed "
+						  "to add entry to pipeline: "
+						  "%s (new() returned NULL)\n",
+						  __func__, elem->name);
+						kfree(pipeline_entry);
+						incomplete = 1;
+					}
+				}
+				found = 1;
+				break;
+			}
+
+		if (found)
+			found = 0;
+		else {
+			printk(KERN_DEBUG "%s: element not found, skipping: "
+				"%s\n", __func__, name);
+			incomplete = 1;
+		}
+	}
+
+_out:
+	if (!list_empty(&pipeline->list))
+		pipeline->inuse = 1;
+	else
+		pipeline->inuse = 0;
+
+#ifdef PIPELINE_DEBUG
+	printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
+		__func__, incomplete ? " incomplete" : "", cfg);
+#endif
+	kfree(dup);
+	return 0;
+}
+
+void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+	struct dsp_pipeline_entry *entry;
+
+	if (!pipeline)
+		return;
+
+	list_for_each_entry(entry, &pipeline->list, list)
+		if (entry->elem->process_tx)
+			entry->elem->process_tx(entry->p, data, len);
+}
+
+void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+	struct dsp_pipeline_entry *entry;
+
+	if (!pipeline)
+		return;
+
+	list_for_each_entry_reverse(entry, &pipeline->list, list)
+		if (entry->elem->process_rx)
+			entry->elem->process_rx(entry->p, data, len);
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c
new file mode 100644
index 0000000..23dd0dd
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_tones.c
@@ -0,0 +1,551 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+
+#define DATA_S sample_silence
+#define SIZE_S (&sizeof_silence)
+#define DATA_GA sample_german_all
+#define SIZE_GA (&sizeof_german_all)
+#define DATA_GO sample_german_old
+#define SIZE_GO (&sizeof_german_old)
+#define DATA_DT sample_american_dialtone
+#define SIZE_DT (&sizeof_american_dialtone)
+#define DATA_RI sample_american_ringing
+#define SIZE_RI (&sizeof_american_ringing)
+#define DATA_BU sample_american_busy
+#define SIZE_BU (&sizeof_american_busy)
+#define DATA_S1 sample_special1
+#define SIZE_S1 (&sizeof_special1)
+#define DATA_S2 sample_special2
+#define SIZE_S2 (&sizeof_special2)
+#define DATA_S3 sample_special3
+#define SIZE_S3 (&sizeof_special3)
+
+/***************/
+/* tones loops */
+/***************/
+
+/* all tones are alaw encoded */
+/* the last sample+1 is in phase with the first sample. the error is low */
+
+static u8 sample_german_all[] = {
+	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+	0xdc, 0xfc, 0x6c,
+	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+	0xdc, 0xfc, 0x6c,
+	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+	0xdc, 0xfc, 0x6c,
+	0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+	0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+	0xdc, 0xfc, 0x6c,
+};
+static u32 sizeof_german_all = sizeof(sample_german_all);
+
+static u8 sample_german_old[] = {
+	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+	0x8c,
+	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+	0x8c,
+	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+	0x8c,
+	0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+	0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+	0x8c,
+};
+static u32 sizeof_german_old = sizeof(sample_german_old);
+
+static u8 sample_american_dialtone[] = {
+	0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c,
+	0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d,
+	0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0,
+	0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67,
+	0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67,
+	0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef,
+	0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8,
+	0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61,
+	0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e,
+	0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30,
+	0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d,
+	0x6d, 0x91, 0x19,
+};
+static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone);
+
+static u8 sample_american_ringing[] = {
+	0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90,
+	0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed,
+	0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c,
+	0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d,
+	0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec,
+	0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11,
+	0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00,
+	0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39,
+	0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6,
+	0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3,
+	0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b,
+	0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f,
+	0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56,
+	0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59,
+	0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30,
+	0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d,
+	0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c,
+	0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd,
+	0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc,
+	0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d,
+	0x4d, 0xbd, 0x0d, 0xad, 0xe1,
+};
+static u32 sizeof_american_ringing = sizeof(sample_american_ringing);
+
+static u8 sample_american_busy[] = {
+	0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66,
+	0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96,
+	0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57,
+	0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f,
+	0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40,
+	0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d,
+	0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c,
+	0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d,
+	0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40,
+	0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7,
+	0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a,
+	0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7,
+	0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40,
+	0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d,
+	0x4d, 0x4d, 0x6d, 0x01,
+};
+static u32 sizeof_american_busy = sizeof(sample_american_busy);
+
+static u8 sample_special1[] = {
+	0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d,
+	0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd,
+	0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd,
+	0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd,
+	0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed,
+	0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41,
+	0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7,
+	0x6d, 0xbd, 0x2d,
+};
+static u32 sizeof_special1 = sizeof(sample_special1);
+
+static u8 sample_special2[] = {
+	0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
+	0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
+	0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
+	0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
+	0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
+	0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
+	0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
+	0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
+	0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
+	0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
+};
+static u32 sizeof_special2 = sizeof(sample_special2);
+
+static u8 sample_special3[] = {
+	0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
+	0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
+	0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
+	0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
+	0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
+	0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
+	0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
+	0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
+	0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
+	0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
+};
+static u32 sizeof_special3 = sizeof(sample_special3);
+
+static u8 sample_silence[] = {
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+	0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+};
+static u32 sizeof_silence = sizeof(sample_silence);
+
+struct tones_samples {
+	u32 *len;
+	u8 *data;
+};
+static struct
+tones_samples samples[] = {
+	{&sizeof_german_all, sample_german_all},
+	{&sizeof_german_old, sample_german_old},
+	{&sizeof_american_dialtone, sample_american_dialtone},
+	{&sizeof_american_ringing, sample_american_ringing},
+	{&sizeof_american_busy, sample_american_busy},
+	{&sizeof_special1, sample_special1},
+	{&sizeof_special2, sample_special2},
+	{&sizeof_special3, sample_special3},
+	{NULL, NULL},
+};
+
+/***********************************
+ * generate ulaw from alaw samples *
+ ***********************************/
+
+void
+dsp_audio_generate_ulaw_samples(void)
+{
+	int i, j;
+
+	i = 0;
+	while (samples[i].len) {
+		j = 0;
+		while (j < (*samples[i].len)) {
+			samples[i].data[j] =
+				dsp_audio_alaw_to_ulaw[samples[i].data[j]];
+			j++;
+		}
+		i++;
+	}
+}
+
+
+/****************************
+ * tone sequence definition *
+ ****************************/
+
+struct pattern {
+	int tone;
+	u8 *data[10];
+	u32 *siz[10];
+	u32 seq[10];
+} pattern[] = {
+	{TONE_GERMAN_DIALTONE,
+	{DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_OLDDIALTONE,
+	{DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_AMERICAN_DIALTONE,
+	{DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_DIALPBX,
+	{DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0},
+	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0},
+	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_OLDDIALPBX,
+	{DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0},
+	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0},
+	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+	{TONE_AMERICAN_DIALPBX,
+	{DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0},
+	{SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0},
+	{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_RINGING,
+	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_OLDRINGING,
+	{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_AMERICAN_RINGING,
+	{DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_RINGPBX,
+	{DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0},
+	{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_OLDRINGPBX,
+	{DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
+	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_AMERICAN_RINGPBX,
+	{DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0},
+	{SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_BUSY,
+	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_OLDBUSY,
+	{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_AMERICAN_BUSY,
+	{DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_HANGUP,
+	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_OLDHANGUP,
+	{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_AMERICAN_HANGUP,
+	{DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_SPECIAL_INFO,
+	{DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0},
+	{SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_GASSENBESETZT,
+	{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+	{2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+	{TONE_GERMAN_AUFSCHALTTON,
+	{DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
+	{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+	{1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
+
+	{0,
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+/******************
+ * copy tone data *
+ ******************/
+
+/* an sk_buff is generated from the number of samples needed.
+ * the count will be changed and may begin from 0 each pattern period.
+ * the clue is to precalculate the pointers and legths to use only one
+ * memcpy per function call, or two memcpy if the tone sequence changes.
+ *
+ * pattern - the type of the pattern
+ * count - the sample from the beginning of the pattern (phase)
+ * len - the number of bytes
+ *
+ * return - the sk_buff with the sample
+ *
+ * if tones has finished (e.g. knocking tone), dsp->tones is turned off
+ */
+void dsp_tone_copy(struct dsp *dsp, u8 *data, int len)
+{
+	int index, count, start, num;
+	struct pattern *pat;
+	struct dsp_tone *tone = &dsp->tone;
+
+	/* if we have no tone, we copy silence */
+	if (!tone->tone) {
+		memset(data, dsp_silence, len);
+		return;
+	}
+
+	/* process pattern */
+	pat = (struct pattern *)tone->pattern;
+		/* points to the current pattern */
+	index = tone->index; /* gives current sequence index */
+	count = tone->count; /* gives current sample */
+
+	/* copy sample */
+	while (len) {
+		/* find sample to start with */
+		while (42) {
+			/* warp arround */
+			if (!pat->seq[index]) {
+				count = 0;
+				index = 0;
+			}
+			/* check if we are currently playing this tone */
+			if (count < pat->seq[index])
+				break;
+			if (dsp_debug & DEBUG_DSP_TONE)
+				printk(KERN_DEBUG "%s: reaching next sequence "
+					"(index=%d)\n", __func__, index);
+			count -= pat->seq[index];
+			index++;
+		}
+		/* calculate start and number of samples */
+		start = count % (*(pat->siz[index]));
+		num = len;
+		if (num+count > pat->seq[index])
+			num = pat->seq[index] - count;
+		if (num+start > (*(pat->siz[index])))
+			num = (*(pat->siz[index])) - start;
+		/* copy memory */
+		memcpy(data, pat->data[index]+start, num);
+		/* reduce length */
+		data += num;
+		count += num;
+		len -= num;
+	}
+	tone->index = index;
+	tone->count = count;
+
+	/* return sk_buff */
+	return;
+}
+
+
+/*******************************
+ * send HW message to hfc card *
+ *******************************/
+
+static void
+dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len)
+{
+	struct sk_buff *nskb;
+
+	/* unlocking is not required, because we don't expect a response */
+	nskb = _alloc_mISDN_skb(PH_CONTROL_REQ,
+		(len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample,
+		GFP_ATOMIC);
+	if (nskb) {
+		if (dsp->ch.peer) {
+			if (dsp->ch.recv(dsp->ch.peer, nskb))
+				dev_kfree_skb(nskb);
+		} else
+			dev_kfree_skb(nskb);
+	}
+}
+
+
+/*****************
+ * timer expires *
+ *****************/
+void
+dsp_tone_timeout(void *arg)
+{
+	struct dsp *dsp = arg;
+	struct dsp_tone *tone = &dsp->tone;
+	struct pattern *pat = (struct pattern *)tone->pattern;
+	int index = tone->index;
+
+	if (!tone->tone)
+		return;
+
+	index++;
+	if (!pat->seq[index])
+		index = 0;
+	tone->index = index;
+
+	/* set next tone */
+	if (pat->data[index] == DATA_S)
+		dsp_tone_hw_message(dsp, 0, 0);
+	else
+		dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index]));
+	/* set timer */
+	init_timer(&tone->tl);
+	tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000;
+	add_timer(&tone->tl);
+}
+
+
+/********************
+ * set/release tone *
+ ********************/
+
+/*
+ * tones are relaized by streaming or by special loop commands if supported
+ * by hardware. when hardware is used, the patterns will be controlled by
+ * timers.
+ */
+int
+dsp_tone(struct dsp *dsp, int tone)
+{
+	struct pattern *pat;
+	int i;
+	struct dsp_tone *tonet = &dsp->tone;
+
+	tonet->software = 0;
+	tonet->hardware = 0;
+
+	/* we turn off the tone */
+	if (!tone) {
+		if (dsp->features.hfc_loops)
+		if (timer_pending(&tonet->tl))
+			del_timer(&tonet->tl);
+		if (dsp->features.hfc_loops)
+			dsp_tone_hw_message(dsp, NULL, 0);
+		tonet->tone = 0;
+		return 0;
+	}
+
+	pat = NULL;
+	i = 0;
+	while (pattern[i].tone) {
+		if (pattern[i].tone == tone) {
+			pat = &pattern[i];
+			break;
+		}
+		i++;
+	}
+	if (!pat) {
+		printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone);
+		return -EINVAL;
+	}
+	if (dsp_debug & DEBUG_DSP_TONE)
+		printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n",
+			__func__, tone, 0);
+	tonet->tone = tone;
+	tonet->pattern = pat;
+	tonet->index = 0;
+	tonet->count = 0;
+
+	if (dsp->features.hfc_loops) {
+		tonet->hardware = 1;
+		/* set first tone */
+		dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0]));
+		/* set timer */
+		if (timer_pending(&tonet->tl))
+			del_timer(&tonet->tl);
+		init_timer(&tonet->tl);
+		tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000;
+		add_timer(&tonet->tl);
+	} else {
+		tonet->software = 1;
+	}
+
+	return 0;
+}
+
+
+
+
+
diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c
new file mode 100644
index 0000000..b5d6553
--- /dev/null
+++ b/drivers/isdn/mISDN/fsm.c
@@ -0,0 +1,183 @@
+/*
+ * finite state machine implementation
+ *
+ * Author       Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "fsm.h"
+
+#define FSM_TIMER_DEBUG 0
+
+void
+mISDN_FsmNew(struct Fsm *fsm,
+       struct FsmNode *fnlist, int fncount)
+{
+	int i;
+
+	fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count *
+		fsm->event_count, GFP_KERNEL);
+
+	for (i = 0; i < fncount; i++)
+		if ((fnlist[i].state >= fsm->state_count) ||
+		    (fnlist[i].event >= fsm->event_count)) {
+			printk(KERN_ERR
+			    "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
+			    i, (long)fnlist[i].state, (long)fsm->state_count,
+			    (long)fnlist[i].event, (long)fsm->event_count);
+		} else
+			fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+			    fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
+}
+EXPORT_SYMBOL(mISDN_FsmNew);
+
+void
+mISDN_FsmFree(struct Fsm *fsm)
+{
+	kfree((void *) fsm->jumpmatrix);
+}
+EXPORT_SYMBOL(mISDN_FsmFree);
+
+int
+mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+	FSMFNPTR r;
+
+	if ((fi->state >= fi->fsm->state_count) ||
+	    (event >= fi->fsm->event_count)) {
+		printk(KERN_ERR
+		    "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
+		    (long)fi->state, (long)fi->fsm->state_count, event,
+		    (long)fi->fsm->event_count);
+		return 1;
+	}
+	r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+	if (r) {
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+		r(fi, event, arg);
+		return 0;
+	} else {
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s no action",
+				fi->fsm->strState[fi->state],
+				fi->fsm->strEvent[event]);
+		return 1;
+	}
+}
+EXPORT_SYMBOL(mISDN_FsmEvent);
+
+void
+mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
+{
+	fi->state = newstate;
+	if (fi->debug)
+		fi->printdebug(fi, "ChangeState %s",
+			fi->fsm->strState[newstate]);
+}
+EXPORT_SYMBOL(mISDN_FsmChangeState);
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
+#endif
+	mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+	ft->fi = fi;
+	ft->tl.function = (void *) FsmExpireTimer;
+	ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
+#endif
+	init_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmInitTimer);
+
+void
+mISDN_FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
+			(long) ft, where);
+#endif
+	del_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmDelTimer);
+
+int
+mISDN_FsmAddTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (timer_pending(&ft->tl)) {
+		if (ft->fi->debug) {
+			printk(KERN_WARNING
+				"mISDN_FsmAddTimer: timer already active!\n");
+			ft->fi->printdebug(ft->fi,
+				"mISDN_FsmAddTimer already active!");
+		}
+		return -1;
+	}
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+	return 0;
+}
+EXPORT_SYMBOL(mISDN_FsmAddTimer);
+
+void
+mISDN_FsmRestartTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (timer_pending(&ft->tl))
+		del_timer(&ft->tl);
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmRestartTimer);
diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h
new file mode 100644
index 0000000..928f5be
--- /dev/null
+++ b/drivers/isdn/mISDN/fsm.h
@@ -0,0 +1,67 @@
+/*
+ *
+ * Author       Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MISDN_FSM_H
+#define _MISDN_FSM_H
+
+#include <linux/timer.h>
+
+/* Statemachine */
+
+struct FsmInst;
+
+typedef void (*FSMFNPTR)(struct FsmInst *, int, void *);
+
+struct Fsm {
+	FSMFNPTR *jumpmatrix;
+	int state_count, event_count;
+	char **strEvent, **strState;
+};
+
+struct FsmInst {
+	struct Fsm *fsm;
+	int state;
+	int debug;
+	void *userdata;
+	int userint;
+	void (*printdebug) (struct FsmInst *, char *, ...);
+};
+
+struct FsmNode {
+	int state, event;
+	void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+	struct FsmInst *fi;
+	struct timer_list tl;
+	int event;
+	void *arg;
+};
+
+extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int);
+extern void mISDN_FsmFree(struct Fsm *);
+extern int mISDN_FsmEvent(struct FsmInst *, int , void *);
+extern void mISDN_FsmChangeState(struct FsmInst *, int);
+extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *);
+extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int);
+extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int);
+extern void mISDN_FsmDelTimer(struct FsmTimer *, int);
+
+#endif
diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c
new file mode 100644
index 0000000..2596fba
--- /dev/null
+++ b/drivers/isdn/mISDN/hwchannel.c
@@ -0,0 +1,365 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+
+static void
+dchannel_bh(struct work_struct *ws)
+{
+	struct dchannel	*dch  = container_of(ws, struct dchannel, workq);
+	struct sk_buff	*skb;
+	int		err;
+
+	if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
+		while ((skb = skb_dequeue(&dch->rqueue))) {
+			if (likely(dch->dev.D.peer)) {
+				err = dch->dev.D.recv(dch->dev.D.peer, skb);
+				if (err)
+					dev_kfree_skb(skb);
+			} else
+				dev_kfree_skb(skb);
+		}
+	}
+	if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
+		if (dch->phfunc)
+			dch->phfunc(dch);
+	}
+}
+
+static void
+bchannel_bh(struct work_struct *ws)
+{
+	struct bchannel	*bch  = container_of(ws, struct bchannel, workq);
+	struct sk_buff	*skb;
+	int		err;
+
+	if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
+		while ((skb = skb_dequeue(&bch->rqueue))) {
+			if (bch->rcount >= 64)
+				printk(KERN_WARNING "B-channel %p receive "
+					"queue if full, but empties...\n", bch);
+			bch->rcount--;
+			if (likely(bch->ch.peer)) {
+				err = bch->ch.recv(bch->ch.peer, skb);
+				if (err)
+					dev_kfree_skb(skb);
+			} else
+				dev_kfree_skb(skb);
+		}
+	}
+}
+
+int
+mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
+{
+	test_and_set_bit(FLG_HDLC, &ch->Flags);
+	ch->maxlen = maxlen;
+	ch->hw = NULL;
+	ch->rx_skb = NULL;
+	ch->tx_skb = NULL;
+	ch->tx_idx = 0;
+	ch->phfunc = phf;
+	skb_queue_head_init(&ch->squeue);
+	skb_queue_head_init(&ch->rqueue);
+	INIT_LIST_HEAD(&ch->dev.bchannels);
+	INIT_WORK(&ch->workq, dchannel_bh);
+	return 0;
+}
+EXPORT_SYMBOL(mISDN_initdchannel);
+
+int
+mISDN_initbchannel(struct bchannel *ch, int maxlen)
+{
+	ch->Flags = 0;
+	ch->maxlen = maxlen;
+	ch->hw = NULL;
+	ch->rx_skb = NULL;
+	ch->tx_skb = NULL;
+	ch->tx_idx = 0;
+	skb_queue_head_init(&ch->rqueue);
+	ch->rcount = 0;
+	ch->next_skb = NULL;
+	INIT_WORK(&ch->workq, bchannel_bh);
+	return 0;
+}
+EXPORT_SYMBOL(mISDN_initbchannel);
+
+int
+mISDN_freedchannel(struct dchannel *ch)
+{
+	if (ch->tx_skb) {
+		dev_kfree_skb(ch->tx_skb);
+		ch->tx_skb = NULL;
+	}
+	if (ch->rx_skb) {
+		dev_kfree_skb(ch->rx_skb);
+		ch->rx_skb = NULL;
+	}
+	skb_queue_purge(&ch->squeue);
+	skb_queue_purge(&ch->rqueue);
+	flush_scheduled_work();
+	return 0;
+}
+EXPORT_SYMBOL(mISDN_freedchannel);
+
+int
+mISDN_freebchannel(struct bchannel *ch)
+{
+	if (ch->tx_skb) {
+		dev_kfree_skb(ch->tx_skb);
+		ch->tx_skb = NULL;
+	}
+	if (ch->rx_skb) {
+		dev_kfree_skb(ch->rx_skb);
+		ch->rx_skb = NULL;
+	}
+	if (ch->next_skb) {
+		dev_kfree_skb(ch->next_skb);
+		ch->next_skb = NULL;
+	}
+	skb_queue_purge(&ch->rqueue);
+	ch->rcount = 0;
+	flush_scheduled_work();
+	return 0;
+}
+EXPORT_SYMBOL(mISDN_freebchannel);
+
+static inline u_int
+get_sapi_tei(u_char *p)
+{
+	u_int	sapi, tei;
+
+	sapi = *p >> 2;
+	tei = p[1] >> 1;
+	return sapi | (tei << 8);
+}
+
+void
+recv_Dchannel(struct dchannel *dch)
+{
+	struct mISDNhead *hh;
+
+	if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
+		dev_kfree_skb(dch->rx_skb);
+		dch->rx_skb = NULL;
+		return;
+	}
+	hh = mISDN_HEAD_P(dch->rx_skb);
+	hh->prim = PH_DATA_IND;
+	hh->id = get_sapi_tei(dch->rx_skb->data);
+	skb_queue_tail(&dch->rqueue, dch->rx_skb);
+	dch->rx_skb = NULL;
+	schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel);
+
+void
+recv_Bchannel(struct bchannel *bch)
+{
+	struct mISDNhead *hh;
+
+	hh = mISDN_HEAD_P(bch->rx_skb);
+	hh->prim = PH_DATA_IND;
+	hh->id = MISDN_ID_ANY;
+	if (bch->rcount >= 64) {
+		dev_kfree_skb(bch->rx_skb);
+		bch->rx_skb = NULL;
+		return;
+	}
+	bch->rcount++;
+	skb_queue_tail(&bch->rqueue, bch->rx_skb);
+	bch->rx_skb = NULL;
+	schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel);
+
+void
+recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
+{
+	skb_queue_tail(&dch->rqueue, skb);
+	schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel_skb);
+
+void
+recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
+{
+	if (bch->rcount >= 64) {
+		dev_kfree_skb(skb);
+		return;
+	}
+	bch->rcount++;
+	skb_queue_tail(&bch->rqueue, skb);
+	schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel_skb);
+
+static void
+confirm_Dsend(struct dchannel *dch)
+{
+	struct sk_buff	*skb;
+
+	skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
+	    0, NULL, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_ERR "%s: no skb id %x\n", __func__,
+		    mISDN_HEAD_ID(dch->tx_skb));
+		return;
+	}
+	skb_queue_tail(&dch->rqueue, skb);
+	schedule_event(dch, FLG_RECVQUEUE);
+}
+
+int
+get_next_dframe(struct dchannel *dch)
+{
+	dch->tx_idx = 0;
+	dch->tx_skb = skb_dequeue(&dch->squeue);
+	if (dch->tx_skb) {
+		confirm_Dsend(dch);
+		return 1;
+	}
+	dch->tx_skb = NULL;
+	test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+	return 0;
+}
+EXPORT_SYMBOL(get_next_dframe);
+
+void
+confirm_Bsend(struct bchannel *bch)
+{
+	struct sk_buff	*skb;
+
+	if (bch->rcount >= 64)
+		return;
+	skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
+	    0, NULL, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_ERR "%s: no skb id %x\n", __func__,
+		    mISDN_HEAD_ID(bch->tx_skb));
+		return;
+	}
+	bch->rcount++;
+	skb_queue_tail(&bch->rqueue, skb);
+	schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(confirm_Bsend);
+
+int
+get_next_bframe(struct bchannel *bch)
+{
+	bch->tx_idx = 0;
+	if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
+		bch->tx_skb = bch->next_skb;
+		if (bch->tx_skb) {
+			bch->next_skb = NULL;
+			test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+				confirm_Bsend(bch); /* not for transparent */
+			return 1;
+		} else {
+			test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+			printk(KERN_WARNING "B TX_NEXT without skb\n");
+		}
+	}
+	bch->tx_skb = NULL;
+	test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+	return 0;
+}
+EXPORT_SYMBOL(get_next_bframe);
+
+void
+queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
+{
+	struct mISDNhead *hh;
+
+	if (!skb) {
+		_queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
+	} else {
+		if (ch->peer) {
+			hh = mISDN_HEAD_P(skb);
+			hh->prim = pr;
+			hh->id = id;
+			if (!ch->recv(ch->peer, skb))
+				return;
+		}
+		dev_kfree_skb(skb);
+	}
+}
+EXPORT_SYMBOL(queue_ch_frame);
+
+int
+dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
+{
+	/* check oversize */
+	if (skb->len <= 0) {
+		printk(KERN_WARNING "%s: skb too small\n", __func__);
+		return -EINVAL;
+	}
+	if (skb->len > ch->maxlen) {
+		printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+			__func__, skb->len, ch->maxlen);
+		return -EINVAL;
+	}
+	/* HW lock must be obtained */
+	if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+		skb_queue_tail(&ch->squeue, skb);
+		return 0;
+	} else {
+		/* write to fifo */
+		ch->tx_skb = skb;
+		ch->tx_idx = 0;
+		return 1;
+	}
+}
+EXPORT_SYMBOL(dchannel_senddata);
+
+int
+bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
+{
+
+	/* check oversize */
+	if (skb->len <= 0) {
+		printk(KERN_WARNING "%s: skb too small\n", __func__);
+		return -EINVAL;
+	}
+	if (skb->len > ch->maxlen) {
+		printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+			__func__, skb->len, ch->maxlen);
+		return -EINVAL;
+	}
+	/* HW lock must be obtained */
+	/* check for pending next_skb */
+	if (ch->next_skb) {
+		printk(KERN_WARNING
+		    "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
+		    __func__, skb->len, ch->next_skb->len);
+		return -EBUSY;
+	}
+	if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+		test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
+		ch->next_skb = skb;
+		return 0;
+	} else {
+		/* write to fifo */
+		ch->tx_skb = skb;
+		ch->tx_idx = 0;
+		return 1;
+	}
+}
+EXPORT_SYMBOL(bchannel_senddata);
diff --git a/drivers/isdn/mISDN/l1oip.h b/drivers/isdn/mISDN/l1oip.h
new file mode 100644
index 0000000..a23d575
--- /dev/null
+++ b/drivers/isdn/mISDN/l1oip.h
@@ -0,0 +1,91 @@
+/*
+ * see notice in l1oip.c
+ */
+
+/* debugging */
+#define DEBUG_L1OIP_INIT	0x00010000
+#define DEBUG_L1OIP_SOCKET	0x00020000
+#define DEBUG_L1OIP_MGR		0x00040000
+#define DEBUG_L1OIP_MSG		0x00080000
+
+/* enable to disorder received bchannels by sequence 2143658798... */
+/*
+#define REORDER_DEBUG
+*/
+
+/* frames */
+#define L1OIP_MAX_LEN		2048		/* max packet size form l2 */
+#define L1OIP_MAX_PERFRAME	1400		/* max data size in one frame */
+
+
+/* timers */
+#define L1OIP_KEEPALIVE		15
+#define L1OIP_TIMEOUT		65
+
+
+/* socket */
+#define L1OIP_DEFAULTPORT	931
+
+
+/* channel structure */
+struct l1oip_chan {
+	struct dchannel       	*dch;
+	struct bchannel       	*bch;
+	u32			tx_counter;	/* counts xmit bytes/packets */
+	u32			rx_counter;	/* counts recv bytes/packets */
+	u32			codecstate;	/* used by codec to save data */
+#ifdef REORDER_DEBUG
+	int			disorder_flag;
+	struct sk_buff		*disorder_skb;
+	u32			disorder_cnt;
+#endif
+};
+
+
+/* card structure */
+struct l1oip {
+	struct list_head        list;
+
+	/* card */
+	int			registered;	/* if registered with mISDN */
+	char			name[MISDN_MAX_IDLEN];
+	int			idx;		/* card index */
+	int			pri;		/* 1=pri, 0=bri */
+	int			d_idx;		/* current dchannel number */
+	int			b_num;		/* number of bchannels */
+	u32			id;		/* id of connection */
+	int			ondemand;	/* if transmis. is on demand */
+	int			bundle;		/* bundle channels in one frm */
+	int			codec;		/* codec to use for transmis. */
+	int			limit;		/* limit number of bchannels */
+
+	/* timer */
+	struct timer_list 	keep_tl;
+	struct timer_list 	timeout_tl;
+	int			timeout_on;
+	struct work_struct	workq;
+
+	/* socket */
+	struct socket 		*socket;	/* if set, socket is created */
+	struct completion 	socket_complete;/* completion of sock thread */
+	struct task_struct	*socket_thread;
+	spinlock_t 		socket_lock;	/* access sock outside thread */
+	u32			remoteip;	/* if all set, ip is assigned */
+	u16	 		localport;	/* must always be set */
+	u16	 		remoteport;	/* must always be set */
+	struct sockaddr_in	sin_local;	/* local socket name */
+	struct sockaddr_in	sin_remote;	/* remote socket name */
+	struct msghdr		sendmsg;	/* ip message to send */
+	struct iovec		sendiov;	/* iov for message */
+
+	/* frame */
+	struct l1oip_chan	chan[128];	/* channel instances */
+};
+
+extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state);
+extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result);
+extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result);
+extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result);
+extern void l1oip_4bit_free(void);
+extern int l1oip_4bit_alloc(int ulaw);
+
diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c
new file mode 100644
index 0000000..a2dc457
--- /dev/null
+++ b/drivers/isdn/mISDN/l1oip_codec.c
@@ -0,0 +1,374 @@
+/*
+
+ * l1oip_codec.c  generic codec using lookup table
+ *  -> conversion from a-Law to u-Law
+ *  -> conversion from u-Law to a-Law
+ *  -> compression by reducing the number of sample resolution to 4
+ *
+ * NOTE: It is not compatible with any standard codec like ADPCM.
+ *
+ * Author	Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+/*
+
+How the codec works:
+--------------------
+
+The volume is increased to increase the dynamic range of the audio signal.
+Each sample is converted to a-LAW with only 16 steps of level resolution.
+A pair of two samples are stored in one byte.
+
+The first byte is stored in the upper bits, the second byte is stored in the
+lower bits.
+
+To speed up compression and decompression, two lookup tables are formed:
+
+- 16 bits index for two samples (law encoded) with 8 bit compressed result.
+- 8 bits index for one compressed data with 16 bits decompressed result.
+
+NOTE: The bytes are handled as they are law-encoded.
+
+*/
+
+#include <linux/vmalloc.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+/* definitions of codec. don't use calculations, code may run slower. */
+
+static u8 *table_com;
+static u16 *table_dec;
+
+
+/* alaw -> ulaw */
+static u8 alaw_to_ulaw[256] =
+{
+	0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+	0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+	0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+	0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+	0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+	0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+	0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+	0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+	0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+	0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+	0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+	0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+	0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+	0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+	0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+	0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+	0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+	0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+	0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+	0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+	0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+	0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+	0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+	0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+	0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+	0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+	0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+	0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+	0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+	0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+	0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+	0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+};
+
+/* ulaw -> alaw */
+static u8 ulaw_to_alaw[256] =
+{
+	0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+	0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+	0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+	0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+	0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+	0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+	0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+	0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+	0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+	0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+	0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+	0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+	0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+	0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+	0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+	0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+	0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+	0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+	0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+	0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+	0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+	0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+	0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+	0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+	0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+	0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+	0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+	0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+	0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+	0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+	0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+	0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+};
+
+/* alaw -> 4bit compression */
+static u8 alaw_to_4bit[256] = {
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02,
+	0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+	0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+	0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+};
+
+/* 4bit -> alaw decompression */
+static u8 _4bit_to_alaw[16] = {
+	0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b,
+	0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c,
+};
+
+/* ulaw -> 4bit compression */
+static u8 ulaw_to_4bit[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+	0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
+	0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+	0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+	0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08,
+	0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+	0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+	0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+	0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a,
+	0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+	0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+	0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+	0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+};
+
+/* 4bit -> ulaw decompression */
+static u8 _4bit_to_ulaw[16] = {
+	0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71,
+	0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f,
+};
+
+
+/*
+ * Compresses data to the result buffer
+ * The result size must be at least half of the input buffer.
+ * The number of samples also must be even!
+ */
+int
+l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state)
+{
+	int ii, i = 0, o = 0;
+
+	if (!len)
+		return 0;
+
+	/* send saved byte and first input byte */
+	if (*state) {
+		*result++ = table_com[(((*state)<<8)&0xff00) | (*data++)];
+		len--;
+		o++;
+	}
+
+	ii = len >> 1;
+
+	while (i < ii) {
+		*result++ = table_com[(data[0]<<8) | (data[1])];
+		data += 2;
+		i++;
+		o++;
+	}
+
+	/* if len has an odd number, we save byte for next call */
+	if (len & 1)
+		*state = 0x100 + *data;
+	else
+		*state = 0;
+
+	return o;
+}
+
+/* Decompress data to the result buffer
+ * The result size must be the number of sample in packet. (2 * input data)
+ * The number of samples in the result are even!
+ */
+int
+l1oip_4bit_to_law(u8 *data, int len, u8 *result)
+{
+	int i = 0;
+	u16 r;
+
+	while (i < len) {
+		r = table_dec[*data++];
+		*result++ = r>>8;
+		*result++ = r;
+		i++;
+	}
+
+	return len << 1;
+}
+
+
+/*
+ * law conversion
+ */
+int
+l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result)
+{
+	int i = 0;
+
+	while (i < len) {
+		*result++ = alaw_to_ulaw[*data++];
+		i++;
+	}
+
+	return len;
+}
+
+int
+l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result)
+{
+	int i = 0;
+
+	while (i < len) {
+		*result++ = ulaw_to_alaw[*data++];
+		i++;
+	}
+
+	return len;
+}
+
+
+/*
+ * generate/free compression and decompression table
+ */
+void
+l1oip_4bit_free(void)
+{
+	if (table_dec)
+		vfree(table_dec);
+	if (table_com)
+		vfree(table_com);
+	table_com = NULL;
+	table_dec = NULL;
+}
+
+int
+l1oip_4bit_alloc(int ulaw)
+{
+	int i1, i2, c, sample;
+
+	/* in case, it is called again */
+	if (table_dec)
+		return 0;
+
+	/* alloc conversion tables */
+	table_com = vmalloc(65536);
+	table_dec = vmalloc(512);
+	if (!table_com | !table_dec) {
+		l1oip_4bit_free();
+		return -ENOMEM;
+	}
+	memset(table_com, 0, 65536);
+	memset(table_dec, 0, 512);
+	/* generate compression table */
+	i1 = 0;
+	while (i1 < 256) {
+		if (ulaw)
+			c = ulaw_to_4bit[i1];
+		else
+			c = alaw_to_4bit[i1];
+		i2 = 0;
+		while (i2 < 256) {
+			table_com[(i1<<8) | i2] |= (c<<4);
+			table_com[(i2<<8) | i1] |= c;
+			i2++;
+		}
+		i1++;
+	}
+
+	/* generate decompression table */
+	i1 = 0;
+	while (i1 < 16) {
+		if (ulaw)
+			sample = _4bit_to_ulaw[i1];
+		else
+			sample = _4bit_to_alaw[i1];
+		i2 = 0;
+		while (i2 < 16) {
+			table_dec[(i1<<4) | i2] |= (sample<<8);
+			table_dec[(i2<<4) | i1] |= sample;
+			i2++;
+		}
+		i1++;
+	}
+
+	return 0;
+}
+
+
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
new file mode 100644
index 0000000..155b997
--- /dev/null
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -0,0 +1,1518 @@
+/*
+
+ * l1oip.c  low level driver for tunneling layer 1 over IP
+ *
+ * NOTE: It is not compatible with TDMoIP nor "ISDN over IP".
+ *
+ * Author	Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* module parameters:
+ * type:
+	Value 1	= BRI
+	Value 2	= PRI
+	Value 3 = BRI (multi channel frame, not supported yet)
+	Value 4 = PRI (multi channel frame, not supported yet)
+	A multi channel frame reduces overhead to a single frame for all
+	b-channels, but increases delay.
+	(NOTE: Multi channel frames are not implemented yet.)
+
+ * codec:
+	Value 0 = transparent (default)
+	Value 1 = transfer ALAW
+	Value 2 = transfer ULAW
+	Value 3 = transfer generic 4 bit compression.
+
+ * ulaw:
+	0 = we use a-Law (default)
+	1 = we use u-Law
+
+ * limit:
+	limitation of B-channels to control bandwidth (1...126)
+	BRI: 1 or 2
+	PRI: 1-30, 31-126 (126, because dchannel ist not counted here)
+	Also limited ressources are used for stack, resulting in less channels.
+	It is possible to have more channels than 30 in PRI mode, this must
+	be supported by the application.
+
+ * ip:
+	byte representation of remote ip address (127.0.0.1 -> 127,0,0,1)
+	If not given or four 0, no remote address is set.
+	For multiple interfaces, concat ip addresses. (127,0,0,1,127,0,0,1)
+
+ * port:
+	port number (local interface)
+	If not given or 0, port 931 is used for fist instance, 932 for next...
+	For multiple interfaces, different ports must be given.
+
+ * remoteport:
+	port number (remote interface)
+	If not given or 0, remote port equals local port
+	For multiple interfaces on equal sites, different ports must be given.
+
+ * ondemand:
+	0 = fixed (always transmit packets, even when remote side timed out)
+	1 = on demand (only transmit packets, when remote side is detected)
+	the default is 0
+	NOTE: ID must also be set for on demand.
+
+ * id:
+	optional value to identify frames. This value must be equal on both
+	peers and should be random. If omitted or 0, no ID is transmitted.
+
+ * debug:
+	NOTE: only one debug value must be given for all cards
+	enable debugging (see l1oip.h for debug options)
+
+
+Special mISDN controls:
+
+ op = MISDN_CTRL_SETPEER*
+ p1 = bytes 0-3 : remote IP address in network order (left element first)
+ p2 = bytes 1-2 : remote port in network order (high byte first)
+ optional:
+ p2 = bytes 3-4 : local port in network order (high byte first)
+
+ op = MISDN_CTRL_UNSETPEER*
+
+ * Use l1oipctrl for comfortable setting or removing ip address.
+   (Layer 1 Over IP CTRL)
+
+
+L1oIP-Protocol
+--------------
+
+Frame Header:
+
+ 7 6 5 4 3 2 1 0
++---------------+
+|Ver|T|I|Coding |
++---------------+
+|  ID byte 3 *  |
++---------------+
+|  ID byte 2 *  |
++---------------+
+|  ID byte 1 *  |
++---------------+
+|  ID byte 0 *  |
++---------------+
+|M|   Channel   |
++---------------+
+|    Length *   |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data....	|
+
+...
+
+|               |
++---------------+
+|M|   Channel   |
++---------------+
+|    Length *   |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data....	|
+
+...
+
+
+* Only included in some cases.
+
+- Ver = Version
+If version is missmatch, the frame must be ignored.
+
+- T = Type of interface
+Must be 0 for S0 or 1 for E1.
+
+- I = Id present
+If bit is set, four ID bytes are included in frame.
+
+- ID = Connection ID
+Additional ID to prevent Denial of Service attacs. Also it prevents hijacking
+connections with dynamic IP. The ID should be random and must not be 0.
+
+- Coding = Type of codec
+Must be 0 for no transcoding. Also for D-channel and other HDLC frames.
+ 1 and 2 are reserved for explicitly use of a-LAW or u-LAW codec.
+ 3 is used for generic table compressor.
+
+- M = More channels to come. If this flag is 1, the following byte contains
+the length of the channel data. After the data block, the next channel will
+be defined. The flag for the last channel block (or if only one channel is
+transmitted), must be 0 and no length is given.
+
+- Channel = Channel number
+0 reserved
+1-3 channel data for S0 (3 is D-channel)
+1-31 channel data for E1 (16 is D-channel)
+32-127 channel data for extended E1 (16 is D-channel)
+
+- The length is used if the M-flag is 1. It is used to find the next channel
+inside frame.
+NOTE: A value of 0 equals 256 bytes of data.
+ -> For larger data blocks, a single frame must be used.
+ -> For larger streams, a single frame or multiple blocks with same channel ID
+   must be used.
+
+- Time Base = Timestamp of first sample in frame
+The "Time Base" is used to rearange packets and to detect packet loss.
+The 16 bits are sent in network order (MSB first) and count 1/8000 th of a
+second. This causes a wrap arround each 8,192 seconds. There is no requirement
+for the initial "Time Base", but 0 should be used for the first packet.
+In case of HDLC data, this timestamp counts the packet or byte number.
+
+
+Two Timers:
+
+After initialisation, a timer of 15 seconds is started. Whenever a packet is
+transmitted, the timer is reset to 15 seconds again. If the timer expires, an
+empty packet is transmitted. This keep the connection alive.
+
+When a valid packet is received, a timer 65 seconds is started. The interface
+become ACTIVE. If the timer expires, the interface becomes INACTIVE.
+
+
+Dynamic IP handling:
+
+To allow dynamic IP, the ID must be non 0. In this case, any packet with the
+correct port number and ID will be accepted. If the remote side changes its IP
+the new IP is used for all transmitted packets until it changes again.
+
+
+On Demand:
+
+If the ondemand parameter is given, the remote IP is set to 0 on timeout.
+This will stop keepalive traffic to remote. If the remote is online again,
+traffic will continue to the remote address. This is usefull for road warriors.
+This feature only works with ID set, otherwhise it is highly unsecure.
+
+
+Socket and Thread
+-----------------
+
+The complete socket opening and closing is done by a thread.
+When the thread opened a socket, the hc->socket descriptor is set. Whenever a
+packet shall be sent to the socket, the hc->socket must be checked wheter not
+NULL. To prevent change in socket descriptor, the hc->socket_lock must be used.
+To change the socket, a recall of l1oip_socket_open() will safely kill the
+socket process and create a new one.
+
+*/
+
+#define L1OIP_VERSION	0	/* 0...3 */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+#include <linux/init.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+#include "core.h"
+#include "l1oip.h"
+
+static const char *l1oip_revision = "2.00";
+
+static int l1oip_cnt;
+static spinlock_t l1oip_lock;
+static struct list_head l1oip_ilist;
+
+#define MAX_CARDS	16
+static u_int type[MAX_CARDS];
+static u_int codec[MAX_CARDS];
+static u_int ip[MAX_CARDS*4];
+static u_int port[MAX_CARDS];
+static u_int remoteport[MAX_CARDS];
+static u_int ondemand[MAX_CARDS];
+static u_int limit[MAX_CARDS];
+static u_int id[MAX_CARDS];
+static int debug;
+static int ulaw;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ip, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(remoteport, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ondemand, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(limit, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(id, uint, NULL, S_IRUGO | S_IWUSR);
+module_param(ulaw, uint, S_IRUGO | S_IWUSR);
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+/*
+ * send a frame via socket, if open and restart timer
+ */
+static int
+l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask,
+	u16 timebase, u8 *buf, int len)
+{
+	u8 *p;
+	int multi = 0;
+	u8 frame[len+32];
+	struct socket *socket = NULL;
+	mm_segment_t oldfs;
+
+	if (debug & DEBUG_L1OIP_MSG)
+		printk(KERN_DEBUG "%s: sending data to socket (len = %d)\n",
+			__func__, len);
+
+	p = frame;
+
+	/* restart timer */
+	if ((int)(hc->keep_tl.expires-jiffies) < 5*HZ) {
+		del_timer(&hc->keep_tl);
+		hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+		add_timer(&hc->keep_tl);
+	} else
+		hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+
+	if (debug & DEBUG_L1OIP_MSG)
+		printk(KERN_DEBUG "%s: resetting timer\n", __func__);
+
+	/* drop if we have no remote ip or port */
+	if (!hc->sin_remote.sin_addr.s_addr || !hc->sin_remote.sin_port) {
+		if (debug & DEBUG_L1OIP_MSG)
+			printk(KERN_DEBUG "%s: dropping frame, because remote "
+				"IP is not set.\n", __func__);
+		return len;
+	}
+
+	/* assemble frame */
+	*p++ = (L1OIP_VERSION<<6) /* version and coding */
+	     | (hc->pri?0x20:0x00) /* type */
+	     | (hc->id?0x10:0x00) /* id */
+	     | localcodec;
+	if (hc->id) {
+		*p++ = hc->id>>24; /* id */
+		*p++ = hc->id>>16;
+		*p++ = hc->id>>8;
+		*p++ = hc->id;
+	}
+	*p++ = (multi == 1)?0x80:0x00 + channel; /* m-flag, channel */
+	if (multi == 1)
+		*p++ = len; /* length */
+	*p++ = timebase>>8; /* time base */
+	*p++ = timebase;
+
+	if (buf && len) { /* add data to frame */
+		if (localcodec == 1 && ulaw)
+			l1oip_ulaw_to_alaw(buf, len, p);
+		else if (localcodec == 2 && !ulaw)
+			l1oip_alaw_to_ulaw(buf, len, p);
+		else if (localcodec == 3)
+			len = l1oip_law_to_4bit(buf, len, p,
+				&hc->chan[channel].codecstate);
+		else
+			memcpy(p, buf, len);
+	}
+	len += p - frame;
+
+	/* check for socket in safe condition */
+	spin_lock(&hc->socket_lock);
+	if (!hc->socket) {
+		spin_unlock(&hc->socket_lock);
+		return 0;
+	}
+	/* seize socket */
+	socket = hc->socket;
+	hc->socket = NULL;
+	spin_unlock(&hc->socket_lock);
+	/* send packet */
+	if (debug & DEBUG_L1OIP_MSG)
+		printk(KERN_DEBUG "%s: sending packet to socket (len "
+			"= %d)\n", __func__, len);
+	hc->sendiov.iov_base = frame;
+	hc->sendiov.iov_len  = len;
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	len = sock_sendmsg(socket, &hc->sendmsg, len);
+	set_fs(oldfs);
+	/* give socket back */
+	hc->socket = socket; /* no locking required */
+
+	return len;
+}
+
+
+/*
+ * receive channel data from socket
+ */
+static void
+l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase,
+	u8 *buf, int len)
+{
+	struct sk_buff *nskb;
+	struct bchannel *bch;
+	struct dchannel *dch;
+	u8 *p;
+	u32 rx_counter;
+
+	if (len == 0) {
+		if (debug & DEBUG_L1OIP_MSG)
+			printk(KERN_DEBUG "%s: received empty keepalive data, "
+				"ignoring\n", __func__);
+		return;
+	}
+
+	if (debug & DEBUG_L1OIP_MSG)
+		printk(KERN_DEBUG "%s: received data, sending to mISDN (%d)\n",
+			__func__, len);
+
+	if (channel < 1 || channel > 127) {
+		printk(KERN_WARNING "%s: packet error - channel %d out of "
+			"range\n", __func__, channel);
+		return;
+	}
+	dch = hc->chan[channel].dch;
+	bch = hc->chan[channel].bch;
+	if (!dch && !bch) {
+		printk(KERN_WARNING "%s: packet error - channel %d not in "
+			"stack\n", __func__, channel);
+		return;
+	}
+
+	/* prepare message */
+	nskb = mI_alloc_skb((remotecodec == 3)?(len<<1):len, GFP_ATOMIC);
+	if (!nskb) {
+		printk(KERN_ERR "%s: No mem for skb.\n", __func__);
+		return;
+	}
+	p = skb_put(nskb, (remotecodec == 3)?(len<<1):len);
+
+	if (remotecodec == 1 && ulaw)
+		l1oip_alaw_to_ulaw(buf, len, p);
+	else if (remotecodec == 2 && !ulaw)
+		l1oip_ulaw_to_alaw(buf, len, p);
+	else if (remotecodec == 3)
+		len = l1oip_4bit_to_law(buf, len, p);
+	else
+		memcpy(p, buf, len);
+
+	/* send message up */
+	if (dch && len >= 2) {
+		dch->rx_skb = nskb;
+		recv_Dchannel(dch);
+	}
+	if (bch) {
+		/* expand 16 bit sequence number to 32 bit sequence number */
+		rx_counter = hc->chan[channel].rx_counter;
+		if (((s16)(timebase - rx_counter)) >= 0) {
+			/* time has changed forward */
+			if (timebase >= (rx_counter & 0xffff))
+				rx_counter =
+					(rx_counter & 0xffff0000) | timebase;
+			else
+				rx_counter = ((rx_counter & 0xffff0000)+0x10000)
+					| timebase;
+		} else {
+			/* time has changed backwards */
+			if (timebase < (rx_counter & 0xffff))
+				rx_counter =
+					(rx_counter & 0xffff0000) | timebase;
+			else
+				rx_counter = ((rx_counter & 0xffff0000)-0x10000)
+					| timebase;
+		}
+		hc->chan[channel].rx_counter = rx_counter;
+
+#ifdef REORDER_DEBUG
+		if (hc->chan[channel].disorder_flag) {
+			struct sk_buff *skb;
+			int cnt;
+			skb = hc->chan[channel].disorder_skb;
+			hc->chan[channel].disorder_skb = nskb;
+			nskb = skb;
+			cnt = hc->chan[channel].disorder_cnt;
+			hc->chan[channel].disorder_cnt = rx_counter;
+			rx_counter = cnt;
+		}
+		hc->chan[channel].disorder_flag ^= 1;
+		if (nskb)
+#endif
+		queue_ch_frame(&bch->ch, PH_DATA_IND, rx_counter, nskb);
+	}
+}
+
+
+/*
+ * parse frame and extract channel data
+ */
+static void
+l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
+{
+	u32			id;
+	u8			channel;
+	u8			remotecodec;
+	u16			timebase;
+	int			m, mlen;
+	int			len_start = len; /* initial frame length */
+	struct dchannel		*dch = hc->chan[hc->d_idx].dch;
+
+	if (debug & DEBUG_L1OIP_MSG)
+		printk(KERN_DEBUG "%s: received frame, parsing... (%d)\n",
+			__func__, len);
+
+	/* check lenght */
+	if (len < 1+1+2) {
+		printk(KERN_WARNING "%s: packet error - length %d below "
+			"4 bytes\n", __func__, len);
+		return;
+	}
+
+	/* check version */
+	if (((*buf)>>6) != L1OIP_VERSION) {
+		printk(KERN_WARNING "%s: packet error - unknown version %d\n",
+			__func__, buf[0]>>6);
+		return;
+	}
+
+	/* check type */
+	if (((*buf)&0x20) && !hc->pri) {
+		printk(KERN_WARNING "%s: packet error - received E1 packet "
+			"on S0 interface\n", __func__);
+		return;
+	}
+	if (!((*buf)&0x20) && hc->pri) {
+		printk(KERN_WARNING "%s: packet error - received S0 packet "
+			"on E1 interface\n", __func__);
+		return;
+	}
+
+	/* get id flag */
+	id = (*buf>>4)&1;
+
+	/* check coding */
+	remotecodec = (*buf) & 0x0f;
+	if (remotecodec > 3) {
+		printk(KERN_WARNING "%s: packet error - remotecodec %d "
+			"unsupported\n", __func__, remotecodec);
+		return;
+	}
+	buf++;
+	len--;
+
+	/* check id */
+	if (id) {
+		if (!hc->id) {
+			printk(KERN_WARNING "%s: packet error - packet has id "
+				"0x%x, but we have not\n", __func__, id);
+			return;
+		}
+		if (len < 4) {
+			printk(KERN_WARNING "%s: packet error - packet too "
+				"short for ID value\n", __func__);
+			return;
+		}
+		id = (*buf++) << 24;
+		id += (*buf++) << 16;
+		id += (*buf++) << 8;
+		id += (*buf++);
+		len -= 4;
+
+		if (id != hc->id) {
+			printk(KERN_WARNING "%s: packet error - ID mismatch, "
+				"got 0x%x, we 0x%x\n",
+				__func__, id, hc->id);
+			return;
+		}
+	} else {
+		if (hc->id) {
+			printk(KERN_WARNING "%s: packet error - packet has no "
+				"ID, but we have\n", __func__);
+			return;
+		}
+	}
+
+multiframe:
+	if (len < 1) {
+		printk(KERN_WARNING "%s: packet error - packet too short, "
+			"channel expected at position %d.\n",
+			__func__, len-len_start+1);
+		return;
+	}
+
+	/* get channel and multiframe flag */
+	channel = *buf&0x7f;
+	m = *buf >> 7;
+	buf++;
+	len--;
+
+	/* check length on multiframe */
+	if (m) {
+		if (len < 1) {
+			printk(KERN_WARNING "%s: packet error - packet too "
+				"short, length expected at position %d.\n",
+				__func__, len_start-len-1);
+			return;
+		}
+
+		mlen = *buf++;
+		len--;
+		if (mlen == 0)
+			mlen = 256;
+		if (len < mlen+3) {
+			printk(KERN_WARNING "%s: packet error - length %d at "
+				"position %d exceeds total length %d.\n",
+				__func__, mlen, len_start-len-1, len_start);
+			return;
+		}
+		if (len == mlen+3) {
+			printk(KERN_WARNING "%s: packet error - length %d at "
+				"position %d will not allow additional "
+				"packet.\n",
+				__func__, mlen, len_start-len+1);
+			return;
+		}
+	} else
+		mlen = len-2; /* single frame, substract timebase */
+
+	if (len < 2) {
+		printk(KERN_WARNING "%s: packet error - packet too short, time "
+			"base expected at position %d.\n",
+			__func__, len-len_start+1);
+		return;
+	}
+
+	/* get time base */
+	timebase = (*buf++) << 8;
+	timebase |= (*buf++);
+	len -= 2;
+
+	/* if inactive, we send up a PH_ACTIVATE and activate */
+	if (!test_bit(FLG_ACTIVE, &dch->Flags)) {
+		if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+			printk(KERN_DEBUG "%s: interface become active due to "
+				"received packet\n", __func__);
+		test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_ATOMIC);
+	}
+
+	/* distribute packet */
+	l1oip_socket_recv(hc, remotecodec, channel, timebase, buf, mlen);
+	buf += mlen;
+	len -= mlen;
+
+	/* multiframe */
+	if (m)
+		goto multiframe;
+
+	/* restart timer */
+	if ((int)(hc->timeout_tl.expires-jiffies) < 5*HZ || !hc->timeout_on) {
+		hc->timeout_on = 1;
+		del_timer(&hc->timeout_tl);
+		hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+		add_timer(&hc->timeout_tl);
+	} else /* only adjust timer */
+		hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+
+	/* if ip or source port changes */
+	if ((hc->sin_remote.sin_addr.s_addr != sin->sin_addr.s_addr)
+	 || (hc->sin_remote.sin_port != sin->sin_port)) {
+		if (debug & DEBUG_L1OIP_SOCKET)
+			printk(KERN_DEBUG "%s: remote address changes from "
+				"0x%08x to 0x%08x (port %d to %d)\n", __func__,
+				ntohl(hc->sin_remote.sin_addr.s_addr),
+				ntohl(sin->sin_addr.s_addr),
+				ntohs(hc->sin_remote.sin_port),
+				ntohs(sin->sin_port));
+		hc->sin_remote.sin_addr.s_addr = sin->sin_addr.s_addr;
+		hc->sin_remote.sin_port = sin->sin_port;
+	}
+}
+
+
+/*
+ * socket stuff
+ */
+static int
+l1oip_socket_thread(void *data)
+{
+	struct l1oip *hc = (struct l1oip *)data;
+	int ret = 0;
+	struct msghdr msg;
+	struct iovec iov;
+	mm_segment_t oldfs;
+	struct sockaddr_in sin_rx;
+	unsigned char recvbuf[1500];
+	int recvlen;
+	struct socket *socket = NULL;
+	DECLARE_COMPLETION(wait);
+
+	/* make daemon */
+	allow_signal(SIGTERM);
+
+	/* create socket */
+	if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &socket)) {
+		printk(KERN_ERR "%s: Failed to create socket.\n", __func__);
+		return -EIO;
+	}
+
+	/* set incoming address */
+	hc->sin_local.sin_family = AF_INET;
+	hc->sin_local.sin_addr.s_addr = INADDR_ANY;
+	hc->sin_local.sin_port = htons((unsigned short)hc->localport);
+
+	/* set outgoing address */
+	hc->sin_remote.sin_family = AF_INET;
+	hc->sin_remote.sin_addr.s_addr = htonl(hc->remoteip);
+	hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport);
+
+	/* bind to incomming port */
+	if (socket->ops->bind(socket, (struct sockaddr *)&hc->sin_local,
+	    sizeof(hc->sin_local))) {
+		printk(KERN_ERR "%s: Failed to bind socket to port %d.\n",
+			__func__, hc->localport);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	/* check sk */
+	if (socket->sk == NULL) {
+		printk(KERN_ERR "%s: socket->sk == NULL\n", __func__);
+		ret = -EIO;
+		goto fail;
+	}
+
+	/* build receive message */
+	msg.msg_name = &sin_rx;
+	msg.msg_namelen = sizeof(sin_rx);
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	/* build send message */
+	hc->sendmsg.msg_name = &hc->sin_remote;
+	hc->sendmsg.msg_namelen = sizeof(hc->sin_remote);
+	hc->sendmsg.msg_control = NULL;
+	hc->sendmsg.msg_controllen = 0;
+	hc->sendmsg.msg_iov    = &hc->sendiov;
+	hc->sendmsg.msg_iovlen = 1;
+
+	/* give away socket */
+	spin_lock(&hc->socket_lock);
+	hc->socket = socket;
+	spin_unlock(&hc->socket_lock);
+
+	/* read loop */
+	if (debug & DEBUG_L1OIP_SOCKET)
+		printk(KERN_DEBUG "%s: socket created and open\n",
+			__func__);
+	while (!signal_pending(current)) {
+		iov.iov_base = recvbuf;
+		iov.iov_len = sizeof(recvbuf);
+		oldfs = get_fs();
+		set_fs(KERNEL_DS);
+		recvlen = sock_recvmsg(socket, &msg, sizeof(recvbuf), 0);
+		set_fs(oldfs);
+		if (recvlen > 0) {
+			l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen);
+		} else {
+			if (debug & DEBUG_L1OIP_SOCKET)
+			    printk(KERN_WARNING "%s: broken pipe on socket\n",
+				__func__);
+		}
+	}
+
+	/* get socket back, check first if in use, maybe by send function */
+	spin_lock(&hc->socket_lock);
+	/* if hc->socket is NULL, it is in use until it is given back */
+	while (!hc->socket) {
+		spin_unlock(&hc->socket_lock);
+		schedule_timeout(HZ/10);
+		spin_lock(&hc->socket_lock);
+	}
+	hc->socket = NULL;
+	spin_unlock(&hc->socket_lock);
+
+	if (debug & DEBUG_L1OIP_SOCKET)
+		printk(KERN_DEBUG "%s: socket thread terminating\n",
+			__func__);
+
+fail:
+	/* close socket */
+	if (socket)
+		sock_release(socket);
+
+	/* if we got killed, signal completion */
+	complete(&hc->socket_complete);
+	hc->socket_thread = NULL; /* show termination of thread */
+
+	if (debug & DEBUG_L1OIP_SOCKET)
+		printk(KERN_DEBUG "%s: socket thread terminated\n",
+			__func__);
+	return ret;
+}
+
+static void
+l1oip_socket_close(struct l1oip *hc)
+{
+	/* kill thread */
+	if (hc->socket_thread) {
+		if (debug & DEBUG_L1OIP_SOCKET)
+			printk(KERN_DEBUG "%s: socket thread exists, "
+				"killing...\n", __func__);
+		send_sig(SIGTERM, hc->socket_thread, 0);
+		wait_for_completion(&hc->socket_complete);
+	}
+}
+
+static int
+l1oip_socket_open(struct l1oip *hc)
+{
+	/* in case of reopen, we need to close first */
+	l1oip_socket_close(hc);
+
+	init_completion(&hc->socket_complete);
+
+	/* create receive process */
+	hc->socket_thread = kthread_run(l1oip_socket_thread, hc, "l1oip_%s",
+		hc->name);
+	if (IS_ERR(hc->socket_thread)) {
+		int err = PTR_ERR(hc->socket_thread);
+		printk(KERN_ERR "%s: Failed (%d) to create socket process.\n",
+			__func__, err);
+		hc->socket_thread = NULL;
+		sock_release(hc->socket);
+		return err;
+	}
+	if (debug & DEBUG_L1OIP_SOCKET)
+		printk(KERN_DEBUG "%s: socket thread created\n", __func__);
+
+	return 0;
+}
+
+
+static void
+l1oip_send_bh(struct work_struct *work)
+{
+	struct l1oip *hc = container_of(work, struct l1oip, workq);
+
+	if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+		printk(KERN_DEBUG "%s: keepalive timer expired, sending empty "
+			"frame on dchannel\n", __func__);
+
+	/* send an empty l1oip frame at D-channel */
+	l1oip_socket_send(hc, 0, hc->d_idx, 0, 0, NULL, 0);
+}
+
+
+/*
+ * timer stuff
+ */
+static void
+l1oip_keepalive(void *data)
+{
+	struct l1oip *hc = (struct l1oip *)data;
+
+	schedule_work(&hc->workq);
+}
+
+static void
+l1oip_timeout(void *data)
+{
+	struct l1oip			*hc = (struct l1oip *)data;
+	struct dchannel		*dch = hc->chan[hc->d_idx].dch;
+
+	if (debug & DEBUG_L1OIP_MSG)
+		printk(KERN_DEBUG "%s: timeout timer expired, turn layer one "
+			"down.\n", __func__);
+
+	hc->timeout_on = 0; /* state that timer must be initialized next time */
+
+	/* if timeout, we send up a PH_DEACTIVATE and deactivate */
+	if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+		if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+			printk(KERN_DEBUG "%s: interface become deactivated "
+				"due to timeout\n", __func__);
+		test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+		_queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+			NULL, GFP_ATOMIC);
+	}
+
+	/* if we have ondemand set, we remove ip address */
+	if (hc->ondemand) {
+		if (debug & DEBUG_L1OIP_MSG)
+			printk(KERN_DEBUG "%s: on demand causes ip address to "
+				"be removed\n", __func__);
+		hc->sin_remote.sin_addr.s_addr = 0;
+	}
+}
+
+
+/*
+ * message handling
+ */
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct l1oip			*hc = dch->hw;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	int			ret = -EINVAL;
+	int			l, ll;
+	unsigned char		*p;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		if (skb->len < 1) {
+			printk(KERN_WARNING "%s: skb too small\n",
+				__func__);
+			break;
+		}
+		if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+			printk(KERN_WARNING "%s: skb too large\n",
+				__func__);
+			break;
+		}
+		/* send frame */
+		p = skb->data;
+		l = skb->len;
+		while (l) {
+			ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+			l1oip_socket_send(hc, 0, dch->slot, 0,
+				hc->chan[dch->slot].tx_counter++, p, ll);
+			p += ll;
+			l -= ll;
+		}
+		skb_trim(skb, 0);
+		queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+		return 0;
+	case PH_ACTIVATE_REQ:
+		if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+			printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+				, __func__, dch->slot, hc->b_num+1);
+		skb_trim(skb, 0);
+		if (test_bit(FLG_ACTIVE, &dch->Flags))
+			queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+		else
+			queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+		return 0;
+	case PH_DEACTIVATE_REQ:
+		if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+			printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+				"(1..%d)\n", __func__, dch->slot,
+				hc->b_num+1);
+		skb_trim(skb, 0);
+		if (test_bit(FLG_ACTIVE, &dch->Flags))
+			queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+		else
+			queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+		return 0;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+	int	ret = 0;
+	struct l1oip	*hc = dch->hw;
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER;
+		break;
+	case MISDN_CTRL_SETPEER:
+		hc->remoteip = (u32)cq->p1;
+		hc->remoteport = cq->p2 & 0xffff;
+		hc->localport = cq->p2 >> 16;
+		if (!hc->remoteport)
+			hc->remoteport = hc->localport;
+		if (debug & DEBUG_L1OIP_SOCKET)
+			printk(KERN_DEBUG "%s: got new ip address from user "
+				"space.\n", __func__);
+			l1oip_socket_open(hc);
+		break;
+	case MISDN_CTRL_UNSETPEER:
+		if (debug & DEBUG_L1OIP_SOCKET)
+			printk(KERN_DEBUG "%s: removing ip address.\n",
+				__func__);
+		hc->remoteip = 0;
+		l1oip_socket_open(hc);
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown Op %x\n",
+		    __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+	if (debug & DEBUG_HW_OPEN)
+		printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+		    dch->dev.id, __builtin_return_address(0));
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+	    (dch->dev.D.protocol != rq->protocol)) {
+		if (debug & DEBUG_HW_OPEN)
+			printk(KERN_WARNING "%s: change protocol %x to %x\n",
+			__func__, dch->dev.D.protocol, rq->protocol);
+	}
+	if (dch->dev.D.protocol != rq->protocol)
+		dch->dev.D.protocol = rq->protocol;
+
+	if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+		_queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+		    0, NULL, GFP_KERNEL);
+	}
+	rq->ch = &dch->dev.D;
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s:cannot get module\n", __func__);
+	return 0;
+}
+
+static int
+open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+	struct bchannel	*bch;
+	int		ch;
+
+	if (!test_bit(rq->adr.channel & 0x1f,
+		&dch->dev.channelmap[rq->adr.channel >> 5]))
+		return -EINVAL;
+	if (rq->protocol == ISDN_P_NONE)
+		return -EINVAL;
+	ch = rq->adr.channel; /* BRI: 1=B1 2=B2  PRI: 1..15,17.. */
+	bch = hc->chan[ch].bch;
+	if (!bch) {
+		printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+		    __func__, ch);
+		return -EINVAL;
+	}
+	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+		return -EBUSY; /* b-channel can be only open once */
+	bch->ch.protocol = rq->protocol;
+	rq->ch = &bch->ch;
+	if (!try_module_get(THIS_MODULE))
+		printk(KERN_WARNING "%s:cannot get module\n", __func__);
+	return 0;
+}
+
+static int
+l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
+	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
+	struct l1oip			*hc = dch->hw;
+	struct channel_req	*rq;
+	int			err = 0;
+
+	if (dch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: cmd:%x %p\n",
+		    __func__, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		rq = arg;
+		switch (rq->protocol) {
+		case ISDN_P_TE_S0:
+		case ISDN_P_NT_S0:
+			if (hc->pri) {
+				err = -EINVAL;
+				break;
+			}
+			err = open_dchannel(hc, dch, rq);
+			break;
+		case ISDN_P_TE_E1:
+		case ISDN_P_NT_E1:
+			if (!hc->pri) {
+				err = -EINVAL;
+				break;
+			}
+			err = open_dchannel(hc, dch, rq);
+			break;
+		default:
+			err = open_bchannel(hc, dch, rq);
+		}
+		break;
+	case CLOSE_CHANNEL:
+		if (debug & DEBUG_HW_OPEN)
+			printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+			    __func__, dch->dev.id,
+			    __builtin_return_address(0));
+		module_put(THIS_MODULE);
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_dctrl(dch, arg);
+		break;
+	default:
+		if (dch->debug & DEBUG_HW)
+			printk(KERN_DEBUG "%s: unknown command %x\n",
+			    __func__, cmd);
+		err = -EINVAL;
+	}
+	return err;
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct bchannel		*bch = container_of(ch, struct bchannel, ch);
+	struct l1oip			*hc = bch->hw;
+	int			ret = -EINVAL;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	int			l, ll, i;
+	unsigned char		*p;
+
+	switch (hh->prim) {
+	case PH_DATA_REQ:
+		if (skb->len <= 0) {
+			printk(KERN_WARNING "%s: skb too small\n",
+				__func__);
+			break;
+		}
+		if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+			printk(KERN_WARNING "%s: skb too large\n",
+				__func__);
+			break;
+		}
+		/* check for AIS / ulaw-silence */
+		p = skb->data;
+		l = skb->len;
+		for (i = 0; i < l; i++) {
+			if (*p++ != 0xff)
+				break;
+		}
+		if (i == l) {
+			if (debug & DEBUG_L1OIP_MSG)
+				printk(KERN_DEBUG "%s: got AIS, not sending, "
+					"but counting\n", __func__);
+			hc->chan[bch->slot].tx_counter += l;
+			skb_trim(skb, 0);
+			queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+			return 0;
+		}
+		/* check for silence */
+		p = skb->data;
+		l = skb->len;
+		for (i = 0; i < l; i++) {
+			if (*p++ != 0x2a)
+				break;
+		}
+		if (i == l) {
+			if (debug & DEBUG_L1OIP_MSG)
+				printk(KERN_DEBUG "%s: got silence, not sending"
+					", but counting\n", __func__);
+			hc->chan[bch->slot].tx_counter += l;
+			skb_trim(skb, 0);
+			queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+			return 0;
+		}
+
+		/* send frame */
+		p = skb->data;
+		l = skb->len;
+		while (l) {
+			ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+			l1oip_socket_send(hc, hc->codec, bch->slot, 0,
+				hc->chan[bch->slot].tx_counter, p, ll);
+			hc->chan[bch->slot].tx_counter += ll;
+			p += ll;
+			l -= ll;
+		}
+		skb_trim(skb, 0);
+		queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+		return 0;
+	case PH_ACTIVATE_REQ:
+		if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+			printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+				, __func__, bch->slot, hc->b_num+1);
+		hc->chan[bch->slot].codecstate = 0;
+		test_and_set_bit(FLG_ACTIVE, &bch->Flags);
+		skb_trim(skb, 0);
+		queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+		return 0;
+	case PH_DEACTIVATE_REQ:
+		if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+			printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+				"(1..%d)\n", __func__, bch->slot,
+				hc->b_num+1);
+		test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+		skb_trim(skb, 0);
+		queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+		return 0;
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+	int			ret = 0;
+	struct dsp_features	*features =
+		(struct dsp_features *)(*((u_long *)&cq->p1));
+
+	switch (cq->op) {
+	case MISDN_CTRL_GETOP:
+		cq->op = MISDN_CTRL_HW_FEATURES_OP;
+		break;
+	case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+		if (debug & DEBUG_L1OIP_MSG)
+			printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+			    __func__);
+		/* create confirm */
+		features->unclocked = 1;
+		features->unordered = 1;
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown Op %x\n",
+		    __func__, cq->op);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int
+l1oip_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct bchannel	*bch = container_of(ch, struct bchannel, ch);
+	int		err = -EINVAL;
+
+	if (bch->debug & DEBUG_HW)
+		printk(KERN_DEBUG "%s: cmd:%x %p\n",
+		    __func__, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		test_and_clear_bit(FLG_OPEN, &bch->Flags);
+		test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+		ch->protocol = ISDN_P_NONE;
+		ch->peer = NULL;
+		module_put(THIS_MODULE);
+		err = 0;
+		break;
+	case CONTROL_CHANNEL:
+		err = channel_bctrl(bch, arg);
+		break;
+	default:
+		printk(KERN_WARNING "%s: unknown prim(%x)\n",
+			__func__, cmd);
+	}
+	return err;
+}
+
+
+/*
+ * cleanup module and stack
+ */
+static void
+release_card(struct l1oip *hc)
+{
+	int	ch;
+
+	if (timer_pending(&hc->keep_tl))
+		del_timer(&hc->keep_tl);
+
+	if (timer_pending(&hc->timeout_tl))
+		del_timer(&hc->timeout_tl);
+
+	if (hc->socket_thread)
+		l1oip_socket_close(hc);
+
+	if (hc->registered && hc->chan[hc->d_idx].dch)
+		mISDN_unregister_device(&hc->chan[hc->d_idx].dch->dev);
+	for (ch = 0; ch < 128; ch++) {
+		if (hc->chan[ch].dch) {
+			mISDN_freedchannel(hc->chan[ch].dch);
+			kfree(hc->chan[ch].dch);
+		}
+		if (hc->chan[ch].bch) {
+			mISDN_freebchannel(hc->chan[ch].bch);
+			kfree(hc->chan[ch].bch);
+#ifdef REORDER_DEBUG
+			if (hc->chan[ch].disorder_skb)
+				dev_kfree_skb(hc->chan[ch].disorder_skb);
+#endif
+		}
+	}
+
+	spin_lock(&l1oip_lock);
+	list_del(&hc->list);
+	spin_unlock(&l1oip_lock);
+
+	kfree(hc);
+}
+
+static void
+l1oip_cleanup(void)
+{
+	struct l1oip *hc, *next;
+
+	list_for_each_entry_safe(hc, next, &l1oip_ilist, list)
+		release_card(hc);
+
+	l1oip_4bit_free();
+}
+
+
+/*
+ * module and stack init
+ */
+static int
+init_card(struct l1oip *hc, int pri, int bundle)
+{
+	struct dchannel	*dch;
+	struct bchannel	*bch;
+	int		ret;
+	int		i, ch;
+
+	spin_lock_init(&hc->socket_lock);
+	hc->idx = l1oip_cnt;
+	hc->pri = pri;
+	hc->d_idx = pri?16:3;
+	hc->b_num = pri?30:2;
+	hc->bundle = bundle;
+	if (hc->pri)
+		sprintf(hc->name, "l1oip-e1.%d", l1oip_cnt + 1);
+	else
+		sprintf(hc->name, "l1oip-s0.%d", l1oip_cnt + 1);
+
+	switch (codec[l1oip_cnt]) {
+	case 0: /* as is */
+	case 1: /* alaw */
+	case 2: /* ulaw */
+	case 3: /* 4bit */
+		break;
+	default:
+		printk(KERN_ERR "Codec(%d) not supported.\n",
+			codec[l1oip_cnt]);
+		return -EINVAL;
+	}
+	hc->codec = codec[l1oip_cnt];
+	if (debug & DEBUG_L1OIP_INIT)
+		printk(KERN_DEBUG "%s: using codec %d\n",
+			__func__, hc->codec);
+
+	if (id[l1oip_cnt] == 0) {
+		printk(KERN_WARNING "Warning: No 'id' value given or "
+			"0, this is highly unsecure. Please use 32 "
+			"bit randmom number 0x...\n");
+	}
+	hc->id = id[l1oip_cnt];
+	if (debug & DEBUG_L1OIP_INIT)
+		printk(KERN_DEBUG "%s: using id 0x%x\n", __func__, hc->id);
+
+	hc->ondemand = ondemand[l1oip_cnt];
+	if (hc->ondemand && !hc->id) {
+		printk(KERN_ERR "%s: ondemand option only allowed in "
+			"conjunction with non 0 ID\n", __func__);
+		return -EINVAL;
+	}
+
+	if (limit[l1oip_cnt])
+		hc->b_num = limit[l1oip_cnt];
+	if (!pri && hc->b_num > 2) {
+		printk(KERN_ERR "Maximum limit for BRI interface is 2 "
+			"channels.\n");
+		return -EINVAL;
+	}
+	if (pri && hc->b_num > 126) {
+		printk(KERN_ERR "Maximum limit for PRI interface is 126 "
+			"channels.\n");
+		return -EINVAL;
+	}
+	if (pri && hc->b_num > 30) {
+		printk(KERN_WARNING "Maximum limit for BRI interface is 30 "
+			"channels.\n");
+		printk(KERN_WARNING "Your selection of %d channels must be "
+			"supported by application.\n", hc->limit);
+	}
+
+	hc->remoteip = ip[l1oip_cnt<<2] << 24
+		     | ip[(l1oip_cnt<<2)+1] << 16
+		     | ip[(l1oip_cnt<<2)+2] << 8
+		     | ip[(l1oip_cnt<<2)+3];
+	hc->localport = port[l1oip_cnt]?:(L1OIP_DEFAULTPORT+l1oip_cnt);
+	if (remoteport[l1oip_cnt])
+		hc->remoteport = remoteport[l1oip_cnt];
+	else
+		hc->remoteport = hc->localport;
+	if (debug & DEBUG_L1OIP_INIT)
+		printk(KERN_DEBUG "%s: using local port %d remote ip "
+			"%d.%d.%d.%d port %d ondemand %d\n", __func__,
+			hc->localport, hc->remoteip >> 24,
+			(hc->remoteip >> 16) & 0xff,
+			(hc->remoteip >> 8) & 0xff, hc->remoteip & 0xff,
+			hc->remoteport, hc->ondemand);
+
+	dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+	if (!dch)
+		return -ENOMEM;
+	dch->debug = debug;
+	mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL);
+	dch->hw = hc;
+	if (pri)
+		dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+	else
+		dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+	dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+	    (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+	dch->dev.D.send = handle_dmsg;
+	dch->dev.D.ctrl = l1oip_dctrl;
+	dch->dev.nrbchan = hc->b_num;
+	dch->slot = hc->d_idx;
+	hc->chan[hc->d_idx].dch = dch;
+	i = 1;
+	for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+		if (ch == 15)
+			i++;
+		bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+		if (!bch) {
+			printk(KERN_ERR "%s: no memory for bchannel\n",
+			    __func__);
+			return -ENOMEM;
+		}
+		bch->nr = i + ch;
+		bch->slot = i + ch;
+		bch->debug = debug;
+		mISDN_initbchannel(bch, MAX_DATA_MEM);
+		bch->hw = hc;
+		bch->ch.send = handle_bmsg;
+		bch->ch.ctrl = l1oip_bctrl;
+		bch->ch.nr = i + ch;
+		list_add(&bch->ch.list, &dch->dev.bchannels);
+		hc->chan[i + ch].bch = bch;
+		test_and_set_bit(bch->nr & 0x1f,
+			&dch->dev.channelmap[bch->nr >> 5]);
+	}
+	ret = mISDN_register_device(&dch->dev, hc->name);
+	if (ret)
+		return ret;
+	hc->registered = 1;
+
+	if (debug & DEBUG_L1OIP_INIT)
+		printk(KERN_DEBUG "%s: Setting up network card(%d)\n",
+			__func__, l1oip_cnt + 1);
+	ret = l1oip_socket_open(hc);
+	if (ret)
+		return ret;
+
+	hc->keep_tl.function = (void *)l1oip_keepalive;
+	hc->keep_tl.data = (ulong)hc;
+	init_timer(&hc->keep_tl);
+	hc->keep_tl.expires = jiffies + 2*HZ; /* two seconds first time */
+	add_timer(&hc->keep_tl);
+
+	hc->timeout_tl.function = (void *)l1oip_timeout;
+	hc->timeout_tl.data = (ulong)hc;
+	init_timer(&hc->timeout_tl);
+	hc->timeout_on = 0; /* state that we have timer off */
+
+	return 0;
+}
+
+static int __init
+l1oip_init(void)
+{
+	int		pri, bundle;
+	struct l1oip		*hc;
+	int		ret;
+
+	printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n",
+		l1oip_revision);
+
+	INIT_LIST_HEAD(&l1oip_ilist);
+	spin_lock_init(&l1oip_lock);
+
+	if (l1oip_4bit_alloc(ulaw))
+		return -ENOMEM;
+
+	l1oip_cnt = 0;
+	while (type[l1oip_cnt] && l1oip_cnt < MAX_CARDS) {
+		switch (type[l1oip_cnt] & 0xff) {
+		case 1:
+			pri = 0;
+			bundle = 0;
+			break;
+		case 2:
+			pri = 1;
+			bundle = 0;
+			break;
+		case 3:
+			pri = 0;
+			bundle = 1;
+			break;
+		case 4:
+			pri = 1;
+			bundle = 1;
+			break;
+		default:
+			printk(KERN_ERR "Card type(%d) not supported.\n",
+				type[l1oip_cnt] & 0xff);
+			l1oip_cleanup();
+			return -EINVAL;
+		}
+
+		if (debug & DEBUG_L1OIP_INIT)
+			printk(KERN_DEBUG "%s: interface %d is %s with %s.\n",
+				__func__, l1oip_cnt, pri?"PRI":"BRI",
+				bundle?"bundled IP packet for all B-channels"
+				 :"seperate IP packets for every B-channel");
+
+		hc = kzalloc(sizeof(struct l1oip), GFP_ATOMIC);
+		if (!hc) {
+			printk(KERN_ERR "No kmem for L1-over-IP driver.\n");
+			l1oip_cleanup();
+			return -ENOMEM;
+		}
+		INIT_WORK(&hc->workq, (void *)l1oip_send_bh);
+
+		spin_lock(&l1oip_lock);
+		list_add_tail(&hc->list, &l1oip_ilist);
+		spin_unlock(&l1oip_lock);
+
+		ret = init_card(hc, pri, bundle);
+		if (ret) {
+			l1oip_cleanup();
+			return ret;
+		}
+
+		l1oip_cnt++;
+	}
+	printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt);
+	return 0;
+}
+
+module_init(l1oip_init);
+module_exit(l1oip_cleanup);
+
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c
new file mode 100644
index 0000000..fced1a2
--- /dev/null
+++ b/drivers/isdn/mISDN/layer1.c
@@ -0,0 +1,403 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+#include "layer1.h"
+#include "fsm.h"
+
+static int *debug;
+
+struct layer1 {
+	u_long			Flags;
+	struct FsmInst		l1m;
+	struct FsmTimer 	timer;
+	int			delay;
+	struct dchannel		*dch;
+	dchannel_l1callback	*dcb;
+};
+
+#define TIMER3_VALUE 7000
+
+static
+struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L1_F2,
+	ST_L1_F3,
+	ST_L1_F4,
+	ST_L1_F5,
+	ST_L1_F6,
+	ST_L1_F7,
+	ST_L1_F8,
+};
+
+#define L1S_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1SState[] =
+{
+	"ST_L1_F2",
+	"ST_L1_F3",
+	"ST_L1_F4",
+	"ST_L1_F5",
+	"ST_L1_F6",
+	"ST_L1_F7",
+	"ST_L1_F8",
+};
+
+enum {
+	EV_PH_ACTIVATE,
+	EV_PH_DEACTIVATE,
+	EV_RESET_IND,
+	EV_DEACT_CNF,
+	EV_DEACT_IND,
+	EV_POWER_UP,
+	EV_ANYSIG_IND,
+	EV_INFO2_IND,
+	EV_INFO4_IND,
+	EV_TIMER_DEACT,
+	EV_TIMER_ACT,
+	EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+	"EV_PH_ACTIVATE",
+	"EV_PH_DEACTIVATE",
+	"EV_RESET_IND",
+	"EV_DEACT_CNF",
+	"EV_DEACT_IND",
+	"EV_POWER_UP",
+	"EV_ANYSIG_IND",
+	"EV_INFO2_IND",
+	"EV_INFO4_IND",
+	"EV_TIMER_DEACT",
+	"EV_TIMER_ACT",
+	"EV_TIMER3",
+};
+
+static void
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	struct layer1 *l1 = fi->userdata;
+	va_list va;
+
+	va_start(va, fmt);
+	printk(KERN_DEBUG "%s: ", l1->dch->dev.name);
+	vprintk(fmt, va);
+	printk("\n");
+	va_end(va);
+}
+
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
+{
+	mISDN_FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	mISDN_FsmChangeState(fi, ST_L1_F3);
+	if (test_bit(FLG_L1_ACTIVATING, &l1->Flags))
+		l1->dcb(l1->dch, HW_POWERUP_REQ);
+}
+
+static void
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	mISDN_FsmChangeState(fi, ST_L1_F3);
+	mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2);
+	test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags);
+}
+
+static void
+l1_power_up_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
+		mISDN_FsmChangeState(fi, ST_L1_F4);
+		l1->dcb(l1->dch, INFO3_P8);
+	} else
+		mISDN_FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
+{
+	mISDN_FsmChangeState(fi, ST_L1_F5);
+}
+
+static void
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
+{
+	mISDN_FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	mISDN_FsmChangeState(fi, ST_L1_F6);
+	l1->dcb(l1->dch, INFO3_P8);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	mISDN_FsmChangeState(fi, ST_L1_F7);
+	l1->dcb(l1->dch, INFO3_P8);
+	if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags))
+		mISDN_FsmDelTimer(&l1->timer, 4);
+	if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) {
+		if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags))
+			mISDN_FsmDelTimer(&l1->timer, 3);
+		mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2);
+		test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags);
+	}
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags);
+	if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
+		if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+			l1->dcb(l1->dch, HW_D_NOBLOCKED);
+		l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+	}
+	if (l1->l1m.state != ST_L1_F6) {
+		mISDN_FsmChangeState(fi, ST_L1_F3);
+		l1->dcb(l1->dch, HW_POWERUP_REQ);
+	}
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags);
+	test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags);
+	l1->dcb(l1->dch, PH_ACTIVATE_IND);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags);
+	test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags);
+	if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+		l1->dcb(l1->dch, HW_D_NOBLOCKED);
+	l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+	l1->dcb(l1->dch, HW_DEACT_REQ);
+}
+
+static void
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+	test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
+	l1->dcb(l1->dch, HW_RESET_REQ);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer1 *l1 = fi->userdata;
+
+	if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) &&
+	    (!test_bit(FLG_L1_T3RUN, &l1->Flags))) {
+		test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags);
+		if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+			l1->dcb(l1->dch, HW_D_NOBLOCKED);
+		l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+	}
+}
+
+static struct FsmNode L1SFnList[] =
+{
+	{ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
+	{ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
+	{ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
+	{ST_L1_F3, EV_RESET_IND, l1_reset},
+	{ST_L1_F4, EV_RESET_IND, l1_reset},
+	{ST_L1_F5, EV_RESET_IND, l1_reset},
+	{ST_L1_F6, EV_RESET_IND, l1_reset},
+	{ST_L1_F7, EV_RESET_IND, l1_reset},
+	{ST_L1_F8, EV_RESET_IND, l1_reset},
+	{ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
+	{ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
+	{ST_L1_F3, EV_POWER_UP,  l1_power_up_s},
+	{ST_L1_F4, EV_ANYSIG_IND, l1_go_F5},
+	{ST_L1_F6, EV_ANYSIG_IND, l1_go_F8},
+	{ST_L1_F7, EV_ANYSIG_IND, l1_go_F8},
+	{ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
+	{ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
+	{ST_L1_F3, EV_TIMER3, l1_timer3},
+	{ST_L1_F4, EV_TIMER3, l1_timer3},
+	{ST_L1_F5, EV_TIMER3, l1_timer3},
+	{ST_L1_F6, EV_TIMER3, l1_timer3},
+	{ST_L1_F8, EV_TIMER3, l1_timer3},
+	{ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
+	{ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
+	{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
+};
+
+static void
+release_l1(struct layer1 *l1) {
+	mISDN_FsmDelTimer(&l1->timer, 0);
+	if (l1->dch)
+		l1->dch->l1 = NULL;
+	module_put(THIS_MODULE);
+	kfree(l1);
+}
+
+int
+l1_event(struct layer1 *l1, u_int event)
+{
+	int		err = 0;
+
+	if (!l1)
+		return -EINVAL;
+	switch (event) {
+	case HW_RESET_IND:
+		mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
+		break;
+	case HW_DEACT_IND:
+		mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL);
+		break;
+	case HW_POWERUP_IND:
+		mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL);
+		break;
+	case HW_DEACT_CNF:
+		mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
+		break;
+	case ANYSIGNAL:
+		mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+		break;
+	case LOSTFRAMING:
+		mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+		break;
+	case INFO2:
+		mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL);
+		break;
+	case INFO4_P8:
+		mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+		break;
+	case INFO4_P10:
+		mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+		break;
+	case PH_ACTIVATE_REQ:
+		if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
+			l1->dcb(l1->dch, PH_ACTIVATE_IND);
+		else {
+			test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags);
+			mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL);
+		}
+		break;
+	case CLOSE_CHANNEL:
+		release_l1(l1);
+		break;
+	default:
+		if (*debug & DEBUG_L1)
+			printk(KERN_DEBUG "%s %x unhandled\n",
+			    __func__, event);
+		err = -EINVAL;
+	}
+	return err;
+}
+EXPORT_SYMBOL(l1_event);
+
+int
+create_l1(struct dchannel *dch, dchannel_l1callback *dcb) {
+	struct layer1	*nl1;
+
+	nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC);
+	if (!nl1) {
+		printk(KERN_ERR "kmalloc struct layer1 failed\n");
+		return -ENOMEM;
+	}
+	nl1->l1m.fsm = &l1fsm_s;
+	nl1->l1m.state = ST_L1_F3;
+	nl1->Flags = 0;
+	nl1->l1m.debug = *debug & DEBUG_L1_FSM;
+	nl1->l1m.userdata = nl1;
+	nl1->l1m.userint = 0;
+	nl1->l1m.printdebug = l1m_debug;
+	nl1->dch = dch;
+	nl1->dcb = dcb;
+	mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer);
+	__module_get(THIS_MODULE);
+	dch->l1 = nl1;
+	return 0;
+}
+EXPORT_SYMBOL(create_l1);
+
+int
+l1_init(u_int *deb)
+{
+	debug = deb;
+	l1fsm_s.state_count = L1S_STATE_COUNT;
+	l1fsm_s.event_count = L1_EVENT_COUNT;
+	l1fsm_s.strEvent = strL1Event;
+	l1fsm_s.strState = strL1SState;
+	mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
+	return 0;
+}
+
+void
+l1_cleanup(void)
+{
+	mISDN_FsmFree(&l1fsm_s);
+}
diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h
new file mode 100644
index 0000000..9c8125f
--- /dev/null
+++ b/drivers/isdn/mISDN/layer1.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Layer 1 defines
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define FLG_L1_ACTIVATING	1
+#define FLG_L1_ACTIVATED	2
+#define FLG_L1_DEACTTIMER	3
+#define FLG_L1_ACTTIMER		4
+#define FLG_L1_T3RUN		5
+#define FLG_L1_PULL_REQ		6
+#define FLG_L1_UINT		7
+#define FLG_L1_DBLOCKED		8
+
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
new file mode 100644
index 0000000..f5ad888
--- /dev/null
+++ b/drivers/isdn/mISDN/layer2.c
@@ -0,0 +1,2216 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "fsm.h"
+#include "layer2.h"
+
+static int *debug;
+
+static
+struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL};
+
+static char *strL2State[] =
+{
+	"ST_L2_1",
+	"ST_L2_2",
+	"ST_L2_3",
+	"ST_L2_4",
+	"ST_L2_5",
+	"ST_L2_6",
+	"ST_L2_7",
+	"ST_L2_8",
+};
+
+enum {
+	EV_L2_UI,
+	EV_L2_SABME,
+	EV_L2_DISC,
+	EV_L2_DM,
+	EV_L2_UA,
+	EV_L2_FRMR,
+	EV_L2_SUPER,
+	EV_L2_I,
+	EV_L2_DL_DATA,
+	EV_L2_ACK_PULL,
+	EV_L2_DL_UNITDATA,
+	EV_L2_DL_ESTABLISH_REQ,
+	EV_L2_DL_RELEASE_REQ,
+	EV_L2_MDL_ASSIGN,
+	EV_L2_MDL_REMOVE,
+	EV_L2_MDL_ERROR,
+	EV_L1_DEACTIVATE,
+	EV_L2_T200,
+	EV_L2_T203,
+	EV_L2_SET_OWN_BUSY,
+	EV_L2_CLEAR_OWN_BUSY,
+	EV_L2_FRAME_ERROR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1)
+
+static char *strL2Event[] =
+{
+	"EV_L2_UI",
+	"EV_L2_SABME",
+	"EV_L2_DISC",
+	"EV_L2_DM",
+	"EV_L2_UA",
+	"EV_L2_FRMR",
+	"EV_L2_SUPER",
+	"EV_L2_I",
+	"EV_L2_DL_DATA",
+	"EV_L2_ACK_PULL",
+	"EV_L2_DL_UNITDATA",
+	"EV_L2_DL_ESTABLISH_REQ",
+	"EV_L2_DL_RELEASE_REQ",
+	"EV_L2_MDL_ASSIGN",
+	"EV_L2_MDL_REMOVE",
+	"EV_L2_MDL_ERROR",
+	"EV_L1_DEACTIVATE",
+	"EV_L2_T200",
+	"EV_L2_T203",
+	"EV_L2_SET_OWN_BUSY",
+	"EV_L2_CLEAR_OWN_BUSY",
+	"EV_L2_FRAME_ERROR",
+};
+
+static void
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	struct layer2 *l2 = fi->userdata;
+	va_list va;
+
+	if (!(*debug & DEBUG_L2_FSM))
+		return;
+	va_start(va, fmt);
+	printk(KERN_DEBUG "l2 (tei %d): ", l2->tei);
+	vprintk(fmt, va);
+	printk("\n");
+	va_end(va);
+}
+
+inline u_int
+l2headersize(struct layer2 *l2, int ui)
+{
+	return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+		(test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
+}
+
+inline u_int
+l2addrsize(struct layer2 *l2)
+{
+	return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1;
+}
+
+static u_int
+l2_newid(struct layer2 *l2)
+{
+	u_int	id;
+
+	id = l2->next_id++;
+	if (id == 0x7fff)
+		l2->next_id = 1;
+	id <<= 16;
+	id |= l2->tei << 8;
+	id |= l2->sapi;
+	return id;
+}
+
+static void
+l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb)
+{
+	int	err;
+
+	if (!l2->up)
+		return;
+	mISDN_HEAD_PRIM(skb) = prim;
+	mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr;
+	err = l2->up->send(l2->up, skb);
+	if (err) {
+		printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+		dev_kfree_skb(skb);
+	}
+}
+
+static void
+l2up_create(struct layer2 *l2, u_int prim, int len, void *arg)
+{
+	struct sk_buff	*skb;
+	struct mISDNhead *hh;
+	int		err;
+
+	if (!l2->up)
+		return;
+	skb = mI_alloc_skb(len, GFP_ATOMIC);
+	if (!skb)
+		return;
+	hh = mISDN_HEAD_P(skb);
+	hh->prim = prim;
+	hh->id = (l2->ch.nr << 16) | l2->ch.addr;
+	if (len)
+		memcpy(skb_put(skb, len), arg, len);
+	err = l2->up->send(l2->up, skb);
+	if (err) {
+		printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+		dev_kfree_skb(skb);
+	}
+}
+
+static int
+l2down_skb(struct layer2 *l2, struct sk_buff *skb) {
+	int ret;
+
+	ret = l2->ch.recv(l2->ch.peer, skb);
+	if (ret && (*debug & DEBUG_L2_RECV))
+		printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret);
+	return ret;
+}
+
+static int
+l2down_raw(struct layer2 *l2, struct sk_buff *skb)
+{
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+	if (hh->prim == PH_DATA_REQ) {
+		if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+			skb_queue_tail(&l2->down_queue, skb);
+			return 0;
+		}
+		l2->down_id = mISDN_HEAD_ID(skb);
+	}
+	return l2down_skb(l2, skb);
+}
+
+static int
+l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb)
+{
+	struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+	hh->prim = prim;
+	hh->id = id;
+	return l2down_raw(l2, skb);
+}
+
+static int
+l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg)
+{
+	struct sk_buff	*skb;
+	int		err;
+	struct mISDNhead *hh;
+
+	skb = mI_alloc_skb(len, GFP_ATOMIC);
+	if (!skb)
+		return -ENOMEM;
+	hh = mISDN_HEAD_P(skb);
+	hh->prim = prim;
+	hh->id = id;
+	if (len)
+		memcpy(skb_put(skb, len), arg, len);
+	err = l2down_raw(l2, skb);
+	if (err)
+		dev_kfree_skb(skb);
+	return err;
+}
+
+static int
+ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) {
+	struct sk_buff *nskb = skb;
+	int ret = -EAGAIN;
+
+	if (test_bit(FLG_L1_NOTREADY, &l2->flag)) {
+		if (hh->id == l2->down_id) {
+			nskb = skb_dequeue(&l2->down_queue);
+			if (nskb) {
+				l2->down_id = mISDN_HEAD_ID(nskb);
+				if (l2down_skb(l2, nskb)) {
+					dev_kfree_skb(nskb);
+					l2->down_id = MISDN_ID_NONE;
+				}
+			} else
+				l2->down_id = MISDN_ID_NONE;
+			if (ret) {
+				dev_kfree_skb(skb);
+				ret = 0;
+			}
+			if (l2->down_id == MISDN_ID_NONE) {
+				test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+				mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+			}
+		}
+	}
+	if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+		nskb = skb_dequeue(&l2->down_queue);
+		if (nskb) {
+			l2->down_id = mISDN_HEAD_ID(nskb);
+			if (l2down_skb(l2, nskb)) {
+				dev_kfree_skb(nskb);
+				l2->down_id = MISDN_ID_NONE;
+				test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+			}
+		} else
+			test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+	}
+	return ret;
+}
+
+static int
+l2mgr(struct layer2 *l2, u_int prim, void *arg) {
+	long c = (long)arg;
+
+	printk(KERN_WARNING
+	    "l2mgr: addr:%x prim %x %c\n", l2->id, prim, (char)c);
+	if (test_bit(FLG_LAPD, &l2->flag) &&
+		!test_bit(FLG_FIXED_TEI, &l2->flag)) {
+		switch (c) {
+		case 'C':
+		case 'D':
+		case 'G':
+		case 'H':
+			l2_tei(l2, prim, (u_long)arg);
+			break;
+		}
+	}
+	return 0;
+}
+
+static void
+set_peer_busy(struct layer2 *l2) {
+	test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+	if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue))
+		test_and_set_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+clear_peer_busy(struct layer2 *l2) {
+	if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
+		test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+InitWin(struct layer2 *l2)
+{
+	int i;
+
+	for (i = 0; i < MAX_WINDOW; i++)
+		l2->windowar[i] = NULL;
+}
+
+static int
+freewin(struct layer2 *l2)
+{
+	int i, cnt = 0;
+
+	for (i = 0; i < MAX_WINDOW; i++) {
+		if (l2->windowar[i]) {
+			cnt++;
+			dev_kfree_skb(l2->windowar[i]);
+			l2->windowar[i] = NULL;
+		}
+	}
+	return cnt;
+}
+
+static void
+ReleaseWin(struct layer2 *l2)
+{
+	int cnt = freewin(l2);
+
+	if (cnt)
+		printk(KERN_WARNING
+		    "isdnl2 freed %d skbuffs in release\n", cnt);
+}
+
+inline unsigned int
+cansend(struct layer2 *l2)
+{
+	unsigned int p1;
+
+	if (test_bit(FLG_MOD128, &l2->flag))
+		p1 = (l2->vs - l2->va) % 128;
+	else
+		p1 = (l2->vs - l2->va) % 8;
+	return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag);
+}
+
+inline void
+clear_exception(struct layer2 *l2)
+{
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	test_and_clear_bit(FLG_REJEXC, &l2->flag);
+	test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
+	clear_peer_busy(l2);
+}
+
+static int
+sethdraddr(struct layer2 *l2, u_char *header, int rsp)
+{
+	u_char *ptr = header;
+	int crbit = rsp;
+
+	if (test_bit(FLG_LAPD, &l2->flag)) {
+		if (test_bit(FLG_LAPD_NET, &l2->flag))
+			crbit = !crbit;
+		*ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0);
+		*ptr++ = (l2->tei << 1) | 1;
+		return 2;
+	} else {
+		if (test_bit(FLG_ORIG, &l2->flag))
+			crbit = !crbit;
+		if (crbit)
+			*ptr++ = l2->addr.B;
+		else
+			*ptr++ = l2->addr.A;
+		return 1;
+	}
+}
+
+static inline void
+enqueue_super(struct layer2 *l2, struct sk_buff *skb)
+{
+	if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb))
+		dev_kfree_skb(skb);
+}
+
+static inline void
+enqueue_ui(struct layer2 *l2, struct sk_buff *skb)
+{
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_UI_IND, 0);
+	if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb))
+		dev_kfree_skb(skb);
+}
+
+inline int
+IsUI(u_char *data)
+{
+	return (data[0] & 0xef) == UI;
+}
+
+inline int
+IsUA(u_char *data)
+{
+	return (data[0] & 0xef) == UA;
+}
+
+inline int
+IsDM(u_char *data)
+{
+	return (data[0] & 0xef) == DM;
+}
+
+inline int
+IsDISC(u_char *data)
+{
+	return (data[0] & 0xef) == DISC;
+}
+
+inline int
+IsRR(u_char *data, struct layer2 *l2)
+{
+	if (test_bit(FLG_MOD128, &l2->flag))
+		return data[0] == RR;
+	else
+		return (data[0] & 0xf) == 1;
+}
+
+inline int
+IsSFrame(u_char *data, struct layer2 *l2)
+{
+	register u_char d = *data;
+
+	if (!test_bit(FLG_MOD128, &l2->flag))
+		d &= 0xf;
+	return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c);
+}
+
+inline int
+IsSABME(u_char *data, struct layer2 *l2)
+{
+	u_char d = data[0] & ~0x10;
+
+	return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM;
+}
+
+inline int
+IsREJ(u_char *data, struct layer2 *l2)
+{
+	return test_bit(FLG_MOD128, &l2->flag) ?
+		data[0] == REJ : (data[0] & 0xf) == REJ;
+}
+
+inline int
+IsFRMR(u_char *data)
+{
+	return (data[0] & 0xef) == FRMR;
+}
+
+inline int
+IsRNR(u_char *data, struct layer2 *l2)
+{
+	return test_bit(FLG_MOD128, &l2->flag) ?
+	    data[0] == RNR : (data[0] & 0xf) == RNR;
+}
+
+int
+iframe_error(struct layer2 *l2, struct sk_buff *skb)
+{
+	u_int	i;
+	int	rsp = *skb->data & 0x2;
+
+	i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1);
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+	if (rsp)
+		return 'L';
+	if (skb->len < i)
+		return 'N';
+	if ((skb->len - i) > l2->maxlen)
+		return 'O';
+	return 0;
+}
+
+int
+super_error(struct layer2 *l2, struct sk_buff *skb)
+{
+	if (skb->len != l2addrsize(l2) +
+	    (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1))
+		return 'N';
+	return 0;
+}
+
+int
+unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp)
+{
+	int rsp = (*skb->data & 0x2) >> 1;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+	if (rsp != wantrsp)
+		return 'L';
+	if (skb->len != l2addrsize(l2) + 1)
+		return 'N';
+	return 0;
+}
+
+int
+UI_error(struct layer2 *l2, struct sk_buff *skb)
+{
+	int rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+	if (rsp)
+		return 'L';
+	if (skb->len > l2->maxlen + l2addrsize(l2) + 1)
+		return 'O';
+	return 0;
+}
+
+int
+FRMR_error(struct layer2 *l2, struct sk_buff *skb)
+{
+	u_int	headers = l2addrsize(l2) + 1;
+	u_char	*datap = skb->data + headers;
+	int	rsp = *skb->data & 0x2;
+
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+	if (!rsp)
+		return 'L';
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		if (skb->len < headers + 5)
+			return 'N';
+		else if (*debug & DEBUG_L2)
+			l2m_debug(&l2->l2m,
+			    "FRMR information %2x %2x %2x %2x %2x",
+			    datap[0], datap[1], datap[2], datap[3], datap[4]);
+	} else {
+		if (skb->len < headers + 3)
+			return 'N';
+		else if (*debug & DEBUG_L2)
+			l2m_debug(&l2->l2m,
+			    "FRMR information %2x %2x %2x",
+			    datap[0], datap[1], datap[2]);
+	}
+	return 0;
+}
+
+static unsigned int
+legalnr(struct layer2 *l2, unsigned int nr)
+{
+	if (test_bit(FLG_MOD128, &l2->flag))
+		return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128);
+	else
+		return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8);
+}
+
+static void
+setva(struct layer2 *l2, unsigned int nr)
+{
+	struct sk_buff	*skb;
+
+	while (l2->va != nr) {
+		l2->va++;
+		if (test_bit(FLG_MOD128, &l2->flag))
+			l2->va %= 128;
+		else
+			l2->va %= 8;
+		if (l2->windowar[l2->sow]) {
+			skb_trim(l2->windowar[l2->sow], 0);
+			skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]);
+			l2->windowar[l2->sow] = NULL;
+		}
+		l2->sow = (l2->sow + 1) % l2->window;
+	}
+	skb = skb_dequeue(&l2->tmp_queue);
+	while (skb) {
+		dev_kfree_skb(skb);
+		skb = skb_dequeue(&l2->tmp_queue);
+	}
+}
+
+static void
+send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr)
+{
+	u_char tmp[MAX_L2HEADER_LEN];
+	int i;
+
+	i = sethdraddr(l2, tmp, cr);
+	tmp[i++] = cmd;
+	if (skb)
+		skb_trim(skb, 0);
+	else {
+		skb = mI_alloc_skb(i, GFP_ATOMIC);
+		if (!skb) {
+			printk(KERN_WARNING "%s: can't alloc skbuff\n",
+				__func__);
+			return;
+		}
+	}
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(l2, skb);
+}
+
+
+inline u_char
+get_PollFlag(struct layer2 *l2, struct sk_buff *skb)
+{
+	return skb->data[l2addrsize(l2)] & 0x10;
+}
+
+inline u_char
+get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb)
+{
+	u_char PF;
+
+	PF = get_PollFlag(l2, skb);
+	dev_kfree_skb(skb);
+	return PF;
+}
+
+inline void
+start_t200(struct layer2 *l2, int i)
+{
+	mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i);
+	test_and_set_bit(FLG_T200_RUN, &l2->flag);
+}
+
+inline void
+restart_t200(struct layer2 *l2, int i)
+{
+	mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i);
+	test_and_set_bit(FLG_T200_RUN, &l2->flag);
+}
+
+inline void
+stop_t200(struct layer2 *l2, int i)
+{
+	if (test_and_clear_bit(FLG_T200_RUN, &l2->flag))
+		mISDN_FsmDelTimer(&l2->t200, i);
+}
+
+inline void
+st5_dl_release_l2l3(struct layer2 *l2)
+{
+	int pr;
+
+	if (test_and_clear_bit(FLG_PEND_REL, &l2->flag))
+		pr = DL_RELEASE_CNF;
+	else
+		pr = DL_RELEASE_IND;
+	l2up_create(l2, pr, 0, NULL);
+}
+
+inline void
+lapb_dl_release_l2l3(struct layer2 *l2, int f)
+{
+	if (test_bit(FLG_LAPB, &l2->flag))
+		l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL);
+	l2up_create(l2, f, 0, NULL);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+	struct layer2 *l2 = fi->userdata;
+	u_char cmd;
+
+	clear_exception(l2);
+	l2->rc = 0;
+	cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10;
+	send_uframe(l2, NULL, cmd, CMD);
+	mISDN_FsmDelTimer(&l2->t203, 1);
+	restart_t200(l2, 1);
+	test_and_clear_bit(FLG_PEND_REL, &l2->flag);
+	freewin(l2);
+	mISDN_FsmChangeState(fi, ST_L2_5);
+}
+
+static void
+l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct layer2 *l2 = fi->userdata;
+
+	if (get_PollFlagFree(l2, skb))
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'C');
+	else
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'D');
+
+}
+
+static void
+l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct layer2 *l2 = fi->userdata;
+
+	if (get_PollFlagFree(l2, skb))
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+	else {
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'E');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+	}
+}
+
+static void
+l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct layer2 *l2 = fi->userdata;
+
+	if (get_PollFlagFree(l2, skb))
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+	else
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'E');
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static void
+l2_go_st3(struct FsmInst *fi, int event, void *arg)
+{
+	dev_kfree_skb((struct sk_buff *)arg);
+	mISDN_FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2_mdl_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+
+	mISDN_FsmChangeState(fi, ST_L2_3);
+	dev_kfree_skb((struct sk_buff *)arg);
+	l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&l2->ui_queue, skb);
+	mISDN_FsmChangeState(fi, ST_L2_2);
+	l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&l2->ui_queue, skb);
+}
+
+static void
+tx_ui(struct layer2 *l2)
+{
+	struct sk_buff *skb;
+	u_char header[MAX_L2HEADER_LEN];
+	int i;
+
+	i = sethdraddr(l2, header, CMD);
+	if (test_bit(FLG_LAPD_NET, &l2->flag))
+		header[1] = 0xff; /* tei 127 */
+	header[i++] = UI;
+	while ((skb = skb_dequeue(&l2->ui_queue))) {
+		memcpy(skb_push(skb, i), header, i);
+		enqueue_ui(l2, skb);
+	}
+}
+
+static void
+l2_send_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&l2->ui_queue, skb);
+	tx_ui(l2);
+}
+
+static void
+l2_got_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_pull(skb, l2headersize(l2, 1));
+/*
+ *		in states 1-3 for broadcast
+ */
+
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_UI_IND, 0);
+	l2up(l2, DL_UNITDATA_IND, skb);
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct layer2 *l2 = fi->userdata;
+
+	establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &l2->flag);
+	dev_kfree_skb(skb);
+}
+
+static void
+l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct layer2 *l2 = fi->userdata;
+
+	skb_queue_purge(&l2->i_queue);
+	test_and_set_bit(FLG_L3_INIT, &l2->flag);
+	test_and_clear_bit(FLG_PEND_REL, &l2->flag);
+	dev_kfree_skb(skb);
+}
+
+static void
+l2_l3_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct layer2 *l2 = fi->userdata;
+
+	skb_queue_purge(&l2->i_queue);
+	establishlink(fi);
+	test_and_set_bit(FLG_L3_INIT, &l2->flag);
+	dev_kfree_skb(skb);
+}
+
+static void
+l2_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_trim(skb, 0);
+	l2up(l2, DL_RELEASE_CNF, skb);
+}
+
+static void
+l2_pend_rel(struct FsmInst *fi, int event, void *arg)
+{
+	struct sk_buff *skb = arg;
+	struct layer2 *l2 = fi->userdata;
+
+	test_and_set_bit(FLG_PEND_REL, &l2->flag);
+	dev_kfree_skb(skb);
+}
+
+static void
+l2_disconnect(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_purge(&l2->i_queue);
+	freewin(l2);
+	mISDN_FsmChangeState(fi, ST_L2_6);
+	l2->rc = 0;
+	send_uframe(l2, NULL, DISC | 0x10, CMD);
+	mISDN_FsmDelTimer(&l2->t203, 1);
+	restart_t200(l2, 2);
+	if (skb)
+		dev_kfree_skb(skb);
+}
+
+static void
+l2_start_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+	struct sk_buff	*skb = arg;
+
+	l2->vs = 0;
+	l2->va = 0;
+	l2->vr = 0;
+	l2->sow = 0;
+	clear_exception(l2);
+	send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP);
+	mISDN_FsmChangeState(fi, ST_L2_7);
+	mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3);
+	skb_trim(skb, 0);
+	l2up(l2, DL_ESTABLISH_IND, skb);
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_send_UA(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+}
+
+static void
+l2_send_DM(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP);
+}
+
+static void
+l2_restart_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+	struct sk_buff	*skb = arg;
+	int		est = 0;
+
+	send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+
+	l2mgr(l2, MDL_ERROR_IND, (void *) 'F');
+
+	if (l2->vs != l2->va) {
+		skb_queue_purge(&l2->i_queue);
+		est = 1;
+	}
+
+	clear_exception(l2);
+	l2->vs = 0;
+	l2->va = 0;
+	l2->vr = 0;
+	l2->sow = 0;
+	mISDN_FsmChangeState(fi, ST_L2_7);
+	stop_t200(l2, 3);
+	mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3);
+
+	if (est)
+		l2up_create(l2, DL_ESTABLISH_IND, 0, NULL);
+/*		mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ *		    MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED,
+ *		    0, NULL, 0);
+ */
+	if (skb_queue_len(&l2->i_queue) && cansend(l2))
+		mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+}
+
+static void
+l2_stop_multi(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+	struct sk_buff	*skb = arg;
+
+	mISDN_FsmChangeState(fi, ST_L2_4);
+	mISDN_FsmDelTimer(&l2->t203, 3);
+	stop_t200(l2, 4);
+
+	send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+	skb_queue_purge(&l2->i_queue);
+	freewin(l2);
+	lapb_dl_release_l2l3(l2, DL_RELEASE_IND);
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_connected(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+	struct sk_buff	*skb = arg;
+	int pr = -1;
+
+	if (!get_PollFlag(l2, skb)) {
+		l2_mdl_error_ua(fi, event, arg);
+		return;
+	}
+	dev_kfree_skb(skb);
+	if (test_and_clear_bit(FLG_PEND_REL, &l2->flag))
+		l2_disconnect(fi, event, NULL);
+	if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) {
+		pr = DL_ESTABLISH_CNF;
+	} else if (l2->vs != l2->va) {
+		skb_queue_purge(&l2->i_queue);
+		pr = DL_ESTABLISH_IND;
+	}
+	stop_t200(l2, 5);
+	l2->vr = 0;
+	l2->vs = 0;
+	l2->va = 0;
+	l2->sow = 0;
+	mISDN_FsmChangeState(fi, ST_L2_7);
+	mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4);
+	if (pr != -1)
+		l2up_create(l2, pr, 0, NULL);
+
+	if (skb_queue_len(&l2->i_queue) && cansend(l2))
+		mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_released(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!get_PollFlag(l2, skb)) {
+		l2_mdl_error_ua(fi, event, arg);
+		return;
+	}
+	dev_kfree_skb(skb);
+	stop_t200(l2, 6);
+	lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+	mISDN_FsmChangeState(fi, ST_L2_4);
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!get_PollFlagFree(l2, skb)) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &l2->flag);
+	}
+}
+
+static void
+l2_st5_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (get_PollFlagFree(l2, skb)) {
+		stop_t200(l2, 7);
+		if (!test_bit(FLG_L3_INIT, &l2->flag))
+			skb_queue_purge(&l2->i_queue);
+		if (test_bit(FLG_LAPB, &l2->flag))
+			l2down_create(l2, PH_DEACTIVATE_REQ,
+				l2_newid(l2), 0, NULL);
+		st5_dl_release_l2l3(l2);
+		mISDN_FsmChangeState(fi, ST_L2_4);
+		if (l2->tm)
+			l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+	}
+}
+
+static void
+l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (get_PollFlagFree(l2, skb)) {
+		stop_t200(l2, 8);
+		lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+		mISDN_FsmChangeState(fi, ST_L2_4);
+		if (l2->tm)
+			l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+	}
+}
+
+void
+enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf)
+{
+	struct sk_buff *skb;
+	u_char tmp[MAX_L2HEADER_LEN];
+	int i;
+
+	i = sethdraddr(l2, tmp, cr);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		tmp[i++] = typ;
+		tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0);
+	} else
+		tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0);
+	skb = mI_alloc_skb(i, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_WARNING
+		    "isdnl2 can't alloc sbbuff for enquiry_cr\n");
+		return;
+	}
+	memcpy(skb_put(skb, i), tmp, i);
+	enqueue_super(l2, skb);
+}
+
+inline void
+enquiry_response(struct layer2 *l2)
+{
+	if (test_bit(FLG_OWN_BUSY, &l2->flag))
+		enquiry_cr(l2, RNR, RSP, 1);
+	else
+		enquiry_cr(l2, RR, RSP, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+}
+
+inline void
+transmit_enquiry(struct layer2 *l2)
+{
+	if (test_bit(FLG_OWN_BUSY, &l2->flag))
+		enquiry_cr(l2, RNR, CMD, 1);
+	else
+		enquiry_cr(l2, RR, CMD, 1);
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	start_t200(l2, 9);
+}
+
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	l2mgr(l2, MDL_ERROR_IND, (void *) 'J');
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static void
+invoke_retransmission(struct layer2 *l2, unsigned int nr)
+{
+	u_int	p1;
+
+	if (l2->vs != nr) {
+		while (l2->vs != nr) {
+			(l2->vs)--;
+			if (test_bit(FLG_MOD128, &l2->flag)) {
+				l2->vs %= 128;
+				p1 = (l2->vs - l2->va) % 128;
+			} else {
+				l2->vs %= 8;
+				p1 = (l2->vs - l2->va) % 8;
+			}
+			p1 = (p1 + l2->sow) % l2->window;
+			if (l2->windowar[p1])
+				skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+			else
+				printk(KERN_WARNING
+				    "%s: windowar[%d] is NULL\n",
+				    __func__, p1);
+			l2->windowar[p1] = NULL;
+		}
+		mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+	}
+}
+
+static void
+l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, rsp, typ = RR;
+	unsigned int nr;
+
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+	if (IsRNR(skb->data, l2)) {
+		set_peer_busy(l2);
+		typ = RNR;
+	} else
+		clear_peer_busy(l2);
+	if (IsREJ(skb->data, l2))
+		typ = REJ;
+
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		PollFlag = (skb->data[1] & 0x1) == 0x1;
+		nr = skb->data[1] >> 1;
+	} else {
+		PollFlag = (skb->data[0] & 0x10);
+		nr = (skb->data[0] >> 5) & 0x7;
+	}
+	dev_kfree_skb(skb);
+
+	if (PollFlag) {
+		if (rsp)
+			l2mgr(l2, MDL_ERROR_IND, (void *) 'A');
+		else
+			enquiry_response(l2);
+	}
+	if (legalnr(l2, nr)) {
+		if (typ == REJ) {
+			setva(l2, nr);
+			invoke_retransmission(l2, nr);
+			stop_t200(l2, 10);
+			if (mISDN_FsmAddTimer(&l2->t203, l2->T203,
+					EV_L2_T203, NULL, 6))
+				l2m_debug(&l2->l2m, "Restart T203 ST7 REJ");
+		} else if ((nr == l2->vs) && (typ == RR)) {
+			setva(l2, nr);
+			stop_t200(l2, 11);
+			mISDN_FsmRestartTimer(&l2->t203, l2->T203,
+					EV_L2_T203, NULL, 7);
+		} else if ((l2->va != nr) || (typ == RNR)) {
+			setva(l2, nr);
+			if (typ != RR)
+				mISDN_FsmDelTimer(&l2->t203, 9);
+			restart_t200(l2, 12);
+		}
+		if (skb_queue_len(&l2->i_queue) && (typ == RR))
+			mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+	} else
+		nrerrorrecovery(fi);
+}
+
+static void
+l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!test_bit(FLG_L3_INIT, &l2->flag))
+		skb_queue_tail(&l2->i_queue, skb);
+	else
+		dev_kfree_skb(skb);
+}
+
+static void
+l2_feed_i_pull(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&l2->i_queue, skb);
+	mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+}
+
+static void
+l2_feed_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_tail(&l2->i_queue, skb);
+}
+
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+	struct sk_buff	*skb = arg;
+	int		PollFlag, i;
+	u_int		ns, nr;
+
+	i = l2addrsize(l2);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		PollFlag = ((skb->data[i + 1] & 0x1) == 0x1);
+		ns = skb->data[i] >> 1;
+		nr = (skb->data[i + 1] >> 1) & 0x7f;
+	} else {
+		PollFlag = (skb->data[i] & 0x10);
+		ns = (skb->data[i] >> 1) & 0x7;
+		nr = (skb->data[i] >> 5) & 0x7;
+	}
+	if (test_bit(FLG_OWN_BUSY, &l2->flag)) {
+		dev_kfree_skb(skb);
+		if (PollFlag)
+			enquiry_response(l2);
+	} else {
+		if (l2->vr == ns) {
+			l2->vr++;
+			if (test_bit(FLG_MOD128, &l2->flag))
+				l2->vr %= 128;
+			else
+				l2->vr %= 8;
+			test_and_clear_bit(FLG_REJEXC, &l2->flag);
+			if (PollFlag)
+				enquiry_response(l2);
+			else
+				test_and_set_bit(FLG_ACK_PEND, &l2->flag);
+			skb_pull(skb, l2headersize(l2, 0));
+			l2up(l2, DL_DATA_IND, skb);
+		} else {
+			/* n(s)!=v(r) */
+			dev_kfree_skb(skb);
+			if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+				if (PollFlag)
+					enquiry_response(l2);
+			} else {
+				enquiry_cr(l2, REJ, RSP, PollFlag);
+				test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+			}
+		}
+	}
+	if (legalnr(l2, nr)) {
+		if (!test_bit(FLG_PEER_BUSY, &l2->flag) &&
+		    (fi->state == ST_L2_7)) {
+			if (nr == l2->vs) {
+				stop_t200(l2, 13);
+				mISDN_FsmRestartTimer(&l2->t203, l2->T203,
+						EV_L2_T203, NULL, 7);
+			} else if (nr != l2->va)
+				restart_t200(l2, 14);
+		}
+		setva(l2, nr);
+	} else {
+		nrerrorrecovery(fi);
+		return;
+	}
+	if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7))
+		mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+	if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag))
+		enquiry_cr(l2, RR, RSP, 0);
+}
+
+static void
+l2_got_tei(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+	u_int		info;
+
+	l2->tei = (signed char)(long)arg;
+	set_channel_address(&l2->ch, l2->sapi, l2->tei);
+	info = DL_INFO_L2_CONNECT;
+	l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info);
+	if (fi->state == ST_L2_3) {
+		establishlink(fi);
+		test_and_set_bit(FLG_L3_INIT, &l2->flag);
+	} else
+		mISDN_FsmChangeState(fi, ST_L2_4);
+	if (skb_queue_len(&l2->ui_queue))
+		tx_ui(l2);
+}
+
+static void
+l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &l2->flag) &&
+		test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+		mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+	} else if (l2->rc == l2->N200) {
+		mISDN_FsmChangeState(fi, ST_L2_4);
+		test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+		skb_queue_purge(&l2->i_queue);
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'G');
+		if (test_bit(FLG_LAPB, &l2->flag))
+			l2down_create(l2, PH_DEACTIVATE_REQ,
+				l2_newid(l2), 0, NULL);
+		st5_dl_release_l2l3(l2);
+		if (l2->tm)
+			l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+	} else {
+		l2->rc++;
+		mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+		send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ?
+			SABME : SABM) | 0x10, CMD);
+	}
+}
+
+static void
+l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &l2->flag) &&
+		test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+		mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+	} else if (l2->rc == l2->N200) {
+		mISDN_FsmChangeState(fi, ST_L2_4);
+		test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'H');
+		lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+		if (l2->tm)
+			l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+	} else {
+		l2->rc++;
+		mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200,
+			    NULL, 9);
+		send_uframe(l2, NULL, DISC | 0x10, CMD);
+	}
+}
+
+static void
+l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &l2->flag) &&
+		test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+		mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+	l2->rc = 0;
+	mISDN_FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(l2);
+	l2->rc++;
+}
+
+static void
+l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &l2->flag) &&
+		test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+		mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+		return;
+	}
+	test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+	if (l2->rc == l2->N200) {
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'I');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+	} else {
+		transmit_enquiry(l2);
+		l2->rc++;
+	}
+}
+
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	if (test_bit(FLG_LAPD, &l2->flag) &&
+		test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+		mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9);
+		return;
+	}
+	mISDN_FsmChangeState(fi, ST_L2_8);
+	transmit_enquiry(l2);
+	l2->rc = 0;
+}
+
+static void
+l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2	*l2 = fi->userdata;
+	struct sk_buff	*skb, *nskb, *oskb;
+	u_char		header[MAX_L2HEADER_LEN];
+	u_int		i, p1;
+
+	if (!cansend(l2))
+		return;
+
+	skb = skb_dequeue(&l2->i_queue);
+	if (!skb)
+		return;
+
+	if (test_bit(FLG_MOD128, &l2->flag))
+		p1 = (l2->vs - l2->va) % 128;
+	else
+		p1 = (l2->vs - l2->va) % 8;
+	p1 = (p1 + l2->sow) % l2->window;
+	if (l2->windowar[p1]) {
+		printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n",
+		    p1);
+		dev_kfree_skb(l2->windowar[p1]);
+	}
+	l2->windowar[p1] = skb;
+	i = sethdraddr(l2, header, CMD);
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		header[i++] = l2->vs << 1;
+		header[i++] = l2->vr << 1;
+		l2->vs = (l2->vs + 1) % 128;
+	} else {
+		header[i++] = (l2->vr << 5) | (l2->vs << 1);
+		l2->vs = (l2->vs + 1) % 8;
+	}
+
+	nskb = skb_clone(skb, GFP_ATOMIC);
+	p1 = skb_headroom(nskb);
+	if (p1 >= i)
+		memcpy(skb_push(nskb, i), header, i);
+	else {
+		printk(KERN_WARNING
+		    "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+		oskb = nskb;
+		nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC);
+		if (!nskb) {
+			dev_kfree_skb(oskb);
+			printk(KERN_WARNING "%s: no skb mem\n", __func__);
+			return;
+		}
+		memcpy(skb_put(nskb, i), header, i);
+		memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len);
+		dev_kfree_skb(oskb);
+	}
+	l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb);
+	test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) {
+		mISDN_FsmDelTimer(&l2->t203, 13);
+		mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11);
+	}
+}
+
+static void
+l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+	int PollFlag, rsp, rnr = 0;
+	unsigned int nr;
+
+	rsp = *skb->data & 0x2;
+	if (test_bit(FLG_ORIG, &l2->flag))
+		rsp = !rsp;
+
+	skb_pull(skb, l2addrsize(l2));
+
+	if (IsRNR(skb->data, l2)) {
+		set_peer_busy(l2);
+		rnr = 1;
+	} else
+		clear_peer_busy(l2);
+
+	if (test_bit(FLG_MOD128, &l2->flag)) {
+		PollFlag = (skb->data[1] & 0x1) == 0x1;
+		nr = skb->data[1] >> 1;
+	} else {
+		PollFlag = (skb->data[0] & 0x10);
+		nr = (skb->data[0] >> 5) & 0x7;
+	}
+	dev_kfree_skb(skb);
+	if (rsp && PollFlag) {
+		if (legalnr(l2, nr)) {
+			if (rnr) {
+				restart_t200(l2, 15);
+			} else {
+				stop_t200(l2, 16);
+				mISDN_FsmAddTimer(&l2->t203, l2->T203,
+					    EV_L2_T203, NULL, 5);
+				setva(l2, nr);
+			}
+			invoke_retransmission(l2, nr);
+			mISDN_FsmChangeState(fi, ST_L2_7);
+			if (skb_queue_len(&l2->i_queue) && cansend(l2))
+				mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+		} else
+			nrerrorrecovery(fi);
+	} else {
+		if (!rsp && PollFlag)
+			enquiry_response(l2);
+		if (legalnr(l2, nr))
+			setva(l2, nr);
+		else
+			nrerrorrecovery(fi);
+	}
+}
+
+static void
+l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_pull(skb, l2addrsize(l2) + 1);
+
+	if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */
+	    (IsUA(skb->data) && (fi->state == ST_L2_7))) {
+		l2mgr(l2, MDL_ERROR_IND, (void *) 'K');
+		establishlink(fi);
+		test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+	}
+	dev_kfree_skb(skb);
+}
+
+static void
+l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	skb_queue_purge(&l2->ui_queue);
+	l2->tei = GROUP_TEI;
+	mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	skb_queue_purge(&l2->ui_queue);
+	l2->tei = GROUP_TEI;
+	l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+	mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	skb_queue_purge(&l2->i_queue);
+	skb_queue_purge(&l2->ui_queue);
+	freewin(l2);
+	l2->tei = GROUP_TEI;
+	stop_t200(l2, 17);
+	st5_dl_release_l2l3(l2);
+	mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	skb_queue_purge(&l2->ui_queue);
+	l2->tei = GROUP_TEI;
+	stop_t200(l2, 18);
+	l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+	mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	skb_queue_purge(&l2->i_queue);
+	skb_queue_purge(&l2->ui_queue);
+	freewin(l2);
+	l2->tei = GROUP_TEI;
+	stop_t200(l2, 17);
+	mISDN_FsmDelTimer(&l2->t203, 19);
+	l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+/*	mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ *		MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED,
+ *		0, NULL, 0);
+ */
+	mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_purge(&l2->i_queue);
+	skb_queue_purge(&l2->ui_queue);
+	if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+		l2up(l2, DL_RELEASE_IND, skb);
+	else
+		dev_kfree_skb(skb);
+}
+
+static void
+l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_purge(&l2->i_queue);
+	skb_queue_purge(&l2->ui_queue);
+	freewin(l2);
+	stop_t200(l2, 19);
+	st5_dl_release_l2l3(l2);
+	mISDN_FsmChangeState(fi, ST_L2_4);
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+	dev_kfree_skb(skb);
+}
+
+static void
+l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_purge(&l2->ui_queue);
+	stop_t200(l2, 20);
+	l2up(l2, DL_RELEASE_CNF, skb);
+	mISDN_FsmChangeState(fi, ST_L2_4);
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	skb_queue_purge(&l2->i_queue);
+	skb_queue_purge(&l2->ui_queue);
+	freewin(l2);
+	stop_t200(l2, 19);
+	mISDN_FsmDelTimer(&l2->t203, 19);
+	l2up(l2, DL_RELEASE_IND, skb);
+	mISDN_FsmChangeState(fi, ST_L2_4);
+	if (l2->tm)
+		l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) {
+		enquiry_cr(l2, RNR, RSP, 0);
+		test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	}
+	if (skb)
+		dev_kfree_skb(skb);
+}
+
+static void
+l2_clear_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+	struct sk_buff *skb = arg;
+
+	if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) {
+		enquiry_cr(l2, RR, RSP, 0);
+		test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+	}
+	if (skb)
+		dev_kfree_skb(skb);
+}
+
+static void
+l2_frame_error(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	l2mgr(l2, MDL_ERROR_IND, arg);
+}
+
+static void
+l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
+{
+	struct layer2 *l2 = fi->userdata;
+
+	l2mgr(l2, MDL_ERROR_IND, arg);
+	establishlink(fi);
+	test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static struct FsmNode L2FnList[] =
+{
+	{ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign},
+	{ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3},
+	{ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish},
+	{ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3},
+	{ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+	{ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+	{ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release},
+	{ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel},
+	{ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+	{ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+	{ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest},
+	{ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull},
+	{ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue},
+	{ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign},
+	{ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui},
+	{ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui},
+	{ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui},
+	{ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui},
+	{ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui},
+	{ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui},
+	{ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui},
+	{ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
+	{ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove},
+	{ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove},
+	{ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove},
+	{ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove},
+	{ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove},
+	{ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove},
+	{ST_L2_4, EV_L2_SABME, l2_start_multi},
+	{ST_L2_5, EV_L2_SABME, l2_send_UA},
+	{ST_L2_6, EV_L2_SABME, l2_send_DM},
+	{ST_L2_7, EV_L2_SABME, l2_restart_multi},
+	{ST_L2_8, EV_L2_SABME, l2_restart_multi},
+	{ST_L2_4, EV_L2_DISC, l2_send_DM},
+	{ST_L2_5, EV_L2_DISC, l2_send_DM},
+	{ST_L2_6, EV_L2_DISC, l2_send_UA},
+	{ST_L2_7, EV_L2_DISC, l2_stop_multi},
+	{ST_L2_8, EV_L2_DISC, l2_stop_multi},
+	{ST_L2_4, EV_L2_UA, l2_mdl_error_ua},
+	{ST_L2_5, EV_L2_UA, l2_connected},
+	{ST_L2_6, EV_L2_UA, l2_released},
+	{ST_L2_7, EV_L2_UA, l2_mdl_error_ua},
+	{ST_L2_8, EV_L2_UA, l2_mdl_error_ua},
+	{ST_L2_4, EV_L2_DM, l2_reestablish},
+	{ST_L2_5, EV_L2_DM, l2_st5_dm_release},
+	{ST_L2_6, EV_L2_DM, l2_st6_dm_release},
+	{ST_L2_7, EV_L2_DM, l2_mdl_error_dm},
+	{ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm},
+	{ST_L2_1, EV_L2_UI, l2_got_ui},
+	{ST_L2_2, EV_L2_UI, l2_got_ui},
+	{ST_L2_3, EV_L2_UI, l2_got_ui},
+	{ST_L2_4, EV_L2_UI, l2_got_ui},
+	{ST_L2_5, EV_L2_UI, l2_got_ui},
+	{ST_L2_6, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_UI, l2_got_ui},
+	{ST_L2_8, EV_L2_UI, l2_got_ui},
+	{ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
+	{ST_L2_8, EV_L2_FRMR, l2_got_FRMR},
+	{ST_L2_7, EV_L2_SUPER, l2_st7_got_super},
+	{ST_L2_8, EV_L2_SUPER, l2_st8_got_super},
+	{ST_L2_7, EV_L2_I, l2_got_iframe},
+	{ST_L2_8, EV_L2_I, l2_got_iframe},
+	{ST_L2_5, EV_L2_T200, l2_st5_tout_200},
+	{ST_L2_6, EV_L2_T200, l2_st6_tout_200},
+	{ST_L2_7, EV_L2_T200, l2_st7_tout_200},
+	{ST_L2_8, EV_L2_T200, l2_st8_tout_200},
+	{ST_L2_7, EV_L2_T203, l2_st7_tout_203},
+	{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+	{ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+	{ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+	{ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+	{ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+	{ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error},
+	{ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error},
+	{ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error},
+	{ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+	{ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+	{ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+	{ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove},
+	{ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove},
+	{ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+	{ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da},
+	{ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da},
+	{ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da},
+	{ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static int
+ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb)
+{
+	u_char	*datap = skb->data;
+	int	ret = -EINVAL;
+	int	psapi, ptei;
+	u_int	l;
+	int	c = 0;
+
+	l = l2addrsize(l2);
+	if (skb->len <= l) {
+		mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N');
+		return ret;
+	}
+	if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */
+		psapi = *datap++;
+		ptei = *datap++;
+		if ((psapi & 1) || !(ptei & 1)) {
+			printk(KERN_WARNING
+			    "l2 D-channel frame wrong EA0/EA1\n");
+			return ret;
+		}
+		psapi >>= 2;
+		ptei >>= 1;
+		if (psapi != l2->sapi) {
+			/* not our bussiness
+			 * printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n",
+			 *  __func__,
+			 *	psapi, l2->sapi);
+			 */
+			dev_kfree_skb(skb);
+			return 0;
+		}
+		if ((ptei != l2->tei) && (ptei != GROUP_TEI)) {
+			/* not our bussiness
+			 * printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n",
+			 *  __func__,
+			 *	ptei, l2->tei, psapi);
+			 */
+			dev_kfree_skb(skb);
+			return 0;
+		}
+	} else
+		datap += l;
+	if (!(*datap & 1)) {	/* I-Frame */
+		c = iframe_error(l2, skb);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb);
+	} else if (IsSFrame(datap, l2)) {	/* S-Frame */
+		c = super_error(l2, skb);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb);
+	} else if (IsUI(datap)) {
+		c = UI_error(l2, skb);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb);
+	} else if (IsSABME(datap, l2)) {
+		c = unnum_error(l2, skb, CMD);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb);
+	} else if (IsUA(datap)) {
+		c = unnum_error(l2, skb, RSP);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb);
+	} else if (IsDISC(datap)) {
+		c = unnum_error(l2, skb, CMD);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb);
+	} else if (IsDM(datap)) {
+		c = unnum_error(l2, skb, RSP);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb);
+	} else if (IsFRMR(datap)) {
+		c = FRMR_error(l2, skb);
+		if (!c)
+			ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb);
+	} else
+		c = 'L';
+	if (c) {
+		printk(KERN_WARNING "l2 D-channel frame error %c\n", c);
+		mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c);
+	}
+	return ret;
+}
+
+static int
+l2_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct layer2		*l2 = container_of(ch, struct layer2, ch);
+	struct mISDNhead	*hh =  mISDN_HEAD_P(skb);
+	int 			ret = -EINVAL;
+
+	if (*debug & DEBUG_L2_RECV)
+		printk(KERN_DEBUG "%s: prim(%x) id(%x) tei(%d)\n",
+		    __func__, hh->prim, hh->id, l2->tei);
+	switch (hh->prim) {
+	case PH_DATA_IND:
+		ret = ph_data_indication(l2, hh, skb);
+		break;
+	case PH_DATA_CNF:
+		ret = ph_data_confirm(l2, hh, skb);
+		break;
+	case PH_ACTIVATE_IND:
+		test_and_set_bit(FLG_L1_ACTIV, &l2->flag);
+		l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL);
+		if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+			ret = mISDN_FsmEvent(&l2->l2m,
+				EV_L2_DL_ESTABLISH_REQ, skb);
+		break;
+	case PH_DEACTIVATE_IND:
+		test_and_clear_bit(FLG_L1_ACTIV, &l2->flag);
+		l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL);
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb);
+		break;
+	case MPH_INFORMATION_IND:
+		if (!l2->up)
+			break;
+		ret = l2->up->send(l2->up, skb);
+		break;
+	case DL_DATA_REQ:
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb);
+		break;
+	case DL_UNITDATA_REQ:
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb);
+		break;
+	case DL_ESTABLISH_REQ:
+		if (test_bit(FLG_LAPB, &l2->flag))
+			test_and_set_bit(FLG_ORIG, &l2->flag);
+		if (test_bit(FLG_L1_ACTIV, &l2->flag)) {
+			if (test_bit(FLG_LAPD, &l2->flag) ||
+				test_bit(FLG_ORIG, &l2->flag))
+				ret = mISDN_FsmEvent(&l2->l2m,
+					EV_L2_DL_ESTABLISH_REQ, skb);
+		} else {
+			if (test_bit(FLG_LAPD, &l2->flag) ||
+				test_bit(FLG_ORIG, &l2->flag)) {
+				test_and_set_bit(FLG_ESTAB_PEND,
+					&l2->flag);
+			}
+			ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2),
+			    skb);
+		}
+		break;
+	case DL_RELEASE_REQ:
+		if (test_bit(FLG_LAPB, &l2->flag))
+			l2down_create(l2, PH_DEACTIVATE_REQ,
+				l2_newid(l2), 0, NULL);
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ,
+		    skb);
+		break;
+	default:
+		if (*debug & DEBUG_L2)
+			l2m_debug(&l2->l2m, "l2 unknown pr %04x",
+			    hh->prim);
+	}
+	if (ret) {
+		dev_kfree_skb(skb);
+		ret = 0;
+	}
+	return ret;
+}
+
+int
+tei_l2(struct layer2 *l2, u_int cmd, u_long arg)
+{
+	int		ret = -EINVAL;
+
+	if (*debug & DEBUG_L2_TEI)
+		printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+	switch (cmd) {
+	case (MDL_ASSIGN_REQ):
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg);
+		break;
+	case (MDL_REMOVE_REQ):
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL);
+		break;
+	case (MDL_ERROR_IND):
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+		break;
+	case (MDL_ERROR_RSP):
+		/* ETS 300-125 5.3.2.1 Test: TC13010 */
+		printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n");
+		ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+		break;
+	}
+	return ret;
+}
+
+static void
+release_l2(struct layer2 *l2)
+{
+	mISDN_FsmDelTimer(&l2->t200, 21);
+	mISDN_FsmDelTimer(&l2->t203, 16);
+	skb_queue_purge(&l2->i_queue);
+	skb_queue_purge(&l2->ui_queue);
+	skb_queue_purge(&l2->down_queue);
+	ReleaseWin(l2);
+	if (test_bit(FLG_LAPD, &l2->flag)) {
+		release_tei(l2);
+		if (l2->ch.st)
+			l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D,
+			    CLOSE_CHANNEL, NULL);
+	}
+	kfree(l2);
+}
+
+static int
+l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct layer2		*l2 = container_of(ch, struct layer2, ch);
+	u_int			info;
+
+	if (*debug & DEBUG_L2_CTRL)
+		printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		if (test_bit(FLG_LAPD, &l2->flag)) {
+			set_channel_address(&l2->ch, l2->sapi, l2->tei);
+			info = DL_INFO_L2_CONNECT;
+			l2up_create(l2, DL_INFORMATION_IND,
+			    sizeof(info), &info);
+		}
+		break;
+	case CLOSE_CHANNEL:
+		if (l2->ch.peer)
+			l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL);
+		release_l2(l2);
+		break;
+	}
+	return 0;
+}
+
+struct layer2 *
+create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, u_long arg)
+{
+	struct layer2		*l2;
+	struct channel_req	rq;
+
+	l2 = kzalloc(sizeof(struct layer2), GFP_KERNEL);
+	if (!l2) {
+		printk(KERN_ERR "kzalloc layer2 failed\n");
+		return NULL;
+	}
+	l2->next_id = 1;
+	l2->down_id = MISDN_ID_NONE;
+	l2->up = ch;
+	l2->ch.st = ch->st;
+	l2->ch.send = l2_send;
+	l2->ch.ctrl = l2_ctrl;
+	switch (protocol) {
+	case ISDN_P_LAPD_NT:
+		test_and_set_bit(FLG_LAPD, &l2->flag);
+		test_and_set_bit(FLG_LAPD_NET, &l2->flag);
+		test_and_set_bit(FLG_MOD128, &l2->flag);
+		l2->sapi = 0;
+		l2->maxlen = MAX_DFRAME_LEN;
+		if (test_bit(OPTION_L2_PMX, &options))
+			l2->window = 7;
+		else
+			l2->window = 1;
+		if (test_bit(OPTION_L2_PTP, &options))
+			test_and_set_bit(FLG_PTP, &l2->flag);
+		if (test_bit(OPTION_L2_FIXEDTEI, &options))
+			test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+		l2->tei = (u_int)arg;
+		l2->T200 = 1000;
+		l2->N200 = 3;
+		l2->T203 = 10000;
+		if (test_bit(OPTION_L2_PMX, &options))
+			rq.protocol = ISDN_P_NT_E1;
+		else
+			rq.protocol = ISDN_P_NT_S0;
+		rq.adr.channel = 0;
+		l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+		break;
+	case ISDN_P_LAPD_TE:
+		test_and_set_bit(FLG_LAPD, &l2->flag);
+		test_and_set_bit(FLG_MOD128, &l2->flag);
+		test_and_set_bit(FLG_ORIG, &l2->flag);
+		l2->sapi = 0;
+		l2->maxlen = MAX_DFRAME_LEN;
+		if (test_bit(OPTION_L2_PMX, &options))
+			l2->window = 7;
+		else
+			l2->window = 1;
+		if (test_bit(OPTION_L2_PTP, &options))
+			test_and_set_bit(FLG_PTP, &l2->flag);
+		if (test_bit(OPTION_L2_FIXEDTEI, &options))
+			test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+		l2->tei = (u_int)arg;
+		l2->T200 = 1000;
+		l2->N200 = 3;
+		l2->T203 = 10000;
+		if (test_bit(OPTION_L2_PMX, &options))
+			rq.protocol = ISDN_P_TE_E1;
+		else
+			rq.protocol = ISDN_P_TE_S0;
+		rq.adr.channel = 0;
+		l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+		break;
+	case ISDN_P_B_X75SLP:
+		test_and_set_bit(FLG_LAPB, &l2->flag);
+		l2->window = 7;
+		l2->maxlen = MAX_DATA_SIZE;
+		l2->T200 = 1000;
+		l2->N200 = 4;
+		l2->T203 = 5000;
+		l2->addr.A = 3;
+		l2->addr.B = 1;
+		break;
+	default:
+		printk(KERN_ERR "layer2 create failed prt %x\n",
+			protocol);
+		kfree(l2);
+		return NULL;
+	}
+	skb_queue_head_init(&l2->i_queue);
+	skb_queue_head_init(&l2->ui_queue);
+	skb_queue_head_init(&l2->down_queue);
+	skb_queue_head_init(&l2->tmp_queue);
+	InitWin(l2);
+	l2->l2m.fsm = &l2fsm;
+	if (test_bit(FLG_LAPB, &l2->flag) ||
+		test_bit(FLG_PTP, &l2->flag) ||
+		test_bit(FLG_LAPD_NET, &l2->flag))
+		l2->l2m.state = ST_L2_4;
+	else
+		l2->l2m.state = ST_L2_1;
+	l2->l2m.debug = *debug;
+	l2->l2m.userdata = l2;
+	l2->l2m.userint = 0;
+	l2->l2m.printdebug = l2m_debug;
+
+	mISDN_FsmInitTimer(&l2->l2m, &l2->t200);
+	mISDN_FsmInitTimer(&l2->l2m, &l2->t203);
+	return l2;
+}
+
+static int
+x75create(struct channel_req *crq)
+{
+	struct layer2	*l2;
+
+	if (crq->protocol != ISDN_P_B_X75SLP)
+		return -EPROTONOSUPPORT;
+	l2 = create_l2(crq->ch, crq->protocol, 0, 0);
+	if (!l2)
+		return -ENOMEM;
+	crq->ch = &l2->ch;
+	crq->protocol = ISDN_P_B_HDLC;
+	return 0;
+}
+
+static struct Bprotocol X75SLP = {
+	.Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)),
+	.name = "X75SLP",
+	.create = x75create
+};
+
+int
+Isdnl2_Init(u_int *deb)
+{
+	debug = deb;
+	mISDN_register_Bprotocol(&X75SLP);
+	l2fsm.state_count = L2_STATE_COUNT;
+	l2fsm.event_count = L2_EVENT_COUNT;
+	l2fsm.strEvent = strL2Event;
+	l2fsm.strState = strL2State;
+	mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList));
+	TEIInit(deb);
+	return 0;
+}
+
+void
+Isdnl2_cleanup(void)
+{
+	mISDN_unregister_Bprotocol(&X75SLP);
+	TEIFree();
+	mISDN_FsmFree(&l2fsm);
+}
+
diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h
new file mode 100644
index 0000000..de2dd02
--- /dev/null
+++ b/drivers/isdn/mISDN/layer2.h
@@ -0,0 +1,140 @@
+/*
+ * Layer 2 defines
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/skbuff.h>
+#include "fsm.h"
+
+#define MAX_WINDOW	8
+
+struct manager {
+	struct mISDNchannel	ch;
+	struct mISDNchannel	bcast;
+	u_long			options;
+	struct list_head	layer2;
+	rwlock_t		lock;
+	struct FsmInst		deact;
+	struct FsmTimer		datimer;
+	struct sk_buff_head	sendq;
+	struct mISDNchannel	*up;
+	u_int			nextid;
+	u_int			lastid;
+};
+
+struct teimgr {
+	int			ri;
+	int			rcnt;
+	struct FsmInst		tei_m;
+	struct FsmTimer		timer;
+	int			tval, nval;
+	struct layer2		*l2;
+	struct manager		*mgr;
+};
+
+struct laddr {
+	u_char	A;
+	u_char	B;
+};
+
+struct layer2 {
+	struct list_head	list;
+	struct mISDNchannel	ch;
+	u_long			flag;
+	int			id;
+	struct mISDNchannel	*up;
+	signed char		sapi;
+	signed char		tei;
+	struct laddr		addr;
+	u_int			maxlen;
+	struct teimgr		*tm;
+	u_int			vs, va, vr;
+	int			rc;
+	u_int			window;
+	u_int			sow;
+	struct FsmInst		l2m;
+	struct FsmTimer		t200, t203;
+	int			T200, N200, T203;
+	u_int			next_id;
+	u_int			down_id;
+	struct sk_buff		*windowar[MAX_WINDOW];
+	struct sk_buff_head	i_queue;
+	struct sk_buff_head	ui_queue;
+	struct sk_buff_head	down_queue;
+	struct sk_buff_head	tmp_queue;
+};
+
+enum {
+	ST_L2_1,
+	ST_L2_2,
+	ST_L2_3,
+	ST_L2_4,
+	ST_L2_5,
+	ST_L2_6,
+	ST_L2_7,
+	ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+extern struct layer2	*create_l2(struct mISDNchannel *, u_int,
+				u_long, u_long);
+extern int		tei_l2(struct layer2 *, u_int, u_long arg);
+
+
+/* from tei.c */
+extern int 		l2_tei(struct layer2 *, u_int, u_long arg);
+extern void 		release_tei(struct layer2 *);
+extern int 		TEIInit(u_int *);
+extern void 		TEIFree(void);
+
+#define MAX_L2HEADER_LEN 4
+
+#define RR	0x01
+#define RNR	0x05
+#define REJ	0x09
+#define SABME	0x6f
+#define SABM	0x2f
+#define DM	0x0f
+#define UI	0x03
+#define DISC	0x43
+#define UA	0x63
+#define FRMR	0x87
+#define XID	0xaf
+
+#define CMD	0
+#define RSP	1
+
+#define LC_FLUSH_WAIT 1
+
+#define FLG_LAPB	0
+#define FLG_LAPD	1
+#define FLG_ORIG	2
+#define FLG_MOD128	3
+#define FLG_PEND_REL	4
+#define FLG_L3_INIT	5
+#define FLG_T200_RUN	6
+#define FLG_ACK_PEND	7
+#define FLG_REJEXC	8
+#define FLG_OWN_BUSY	9
+#define FLG_PEER_BUSY	10
+#define FLG_DCHAN_BUSY	11
+#define FLG_L1_ACTIV	12
+#define FLG_ESTAB_PEND	13
+#define FLG_PTP		14
+#define FLG_FIXED_TEI	15
+#define FLG_L2BLOCK	16
+#define FLG_L1_NOTREADY	17
+#define FLG_LAPD_NET	18
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
new file mode 100644
index 0000000..4ba4cc3
--- /dev/null
+++ b/drivers/isdn/mISDN/socket.c
@@ -0,0 +1,781 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static int	*debug;
+
+static struct proto mISDN_proto = {
+	.name		= "misdn",
+	.owner		= THIS_MODULE,
+	.obj_size	= sizeof(struct mISDN_sock)
+};
+
+#define _pms(sk)	((struct mISDN_sock *)sk)
+
+static struct mISDN_sock_list	data_sockets = {
+	.lock = __RW_LOCK_UNLOCKED(data_sockets.lock)
+};
+
+static struct mISDN_sock_list	base_sockets = {
+	.lock = __RW_LOCK_UNLOCKED(base_sockets.lock)
+};
+
+#define L2_HEADER_LEN	4
+
+static inline struct sk_buff *
+_l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
+{
+	struct sk_buff  *skb;
+
+	skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
+	if (likely(skb))
+		skb_reserve(skb, L2_HEADER_LEN);
+	return skb;
+}
+
+static void
+mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk)
+{
+	write_lock_bh(&l->lock);
+	sk_add_node(sk, &l->head);
+	write_unlock_bh(&l->lock);
+}
+
+static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk)
+{
+	write_lock_bh(&l->lock);
+	sk_del_node_init(sk);
+	write_unlock_bh(&l->lock);
+}
+
+static int
+mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct mISDN_sock *msk;
+	int	err;
+
+	msk = container_of(ch, struct mISDN_sock, ch);
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
+	if (msk->sk.sk_state == MISDN_CLOSED)
+		return -EUNATCH;
+	__net_timestamp(skb);
+	err = sock_queue_rcv_skb(&msk->sk, skb);
+	if (err)
+		printk(KERN_WARNING "%s: error %d\n", __func__, err);
+	return err;
+}
+
+static int
+mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct mISDN_sock *msk;
+
+	msk = container_of(ch, struct mISDN_sock, ch);
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
+	switch (cmd) {
+	case CLOSE_CHANNEL:
+		msk->sk.sk_state = MISDN_CLOSED;
+		break;
+	}
+	return 0;
+}
+
+static inline void
+mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
+{
+	struct timeval	tv;
+
+	if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
+		skb_get_timestamp(skb, &tv);
+		put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
+	}
+}
+
+static int
+mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+    struct msghdr *msg, size_t len, int flags)
+{
+	struct sk_buff		*skb;
+	struct sock		*sk = sock->sk;
+	struct sockaddr_mISDN	*maddr;
+
+	int		copied, err;
+
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
+			__func__, (int)len, flags, _pms(sk)->ch.nr,
+			sk->sk_protocol);
+	if (flags & (MSG_OOB))
+		return -EOPNOTSUPP;
+
+	if (sk->sk_state == MISDN_CLOSED)
+		return 0;
+
+	skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
+	if (!skb)
+		return err;
+
+	if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+		msg->msg_namelen = sizeof(struct sockaddr_mISDN);
+		maddr = (struct sockaddr_mISDN *)msg->msg_name;
+		maddr->family = AF_ISDN;
+		maddr->dev = _pms(sk)->dev->id;
+		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+		    (sk->sk_protocol == ISDN_P_LAPD_NT)) {
+			maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
+			maddr->tei =  (mISDN_HEAD_ID(skb) >> 8) & 0xff;
+			maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
+		} else {
+			maddr->channel = _pms(sk)->ch.nr;
+			maddr->sapi = _pms(sk)->ch.addr & 0xFF;
+			maddr->tei =  (_pms(sk)->ch.addr >> 8) & 0xFF;
+		}
+	} else {
+		if (msg->msg_namelen)
+			printk(KERN_WARNING "%s: too small namelen %d\n",
+			    __func__, msg->msg_namelen);
+		msg->msg_namelen = 0;
+	}
+
+	copied = skb->len + MISDN_HEADER_LEN;
+	if (len < copied) {
+		if (flags & MSG_PEEK)
+			atomic_dec(&skb->users);
+		else
+			skb_queue_head(&sk->sk_receive_queue, skb);
+		return -ENOSPC;
+	}
+	memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
+	    MISDN_HEADER_LEN);
+
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+	mISDN_sock_cmsg(sk, msg, skb);
+
+	skb_free_datagram(sk, skb);
+
+	return err ? : copied;
+}
+
+static int
+mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+    struct msghdr *msg, size_t len)
+{
+	struct sock		*sk = sock->sk;
+	struct sk_buff		*skb;
+	int			err = -ENOMEM;
+	struct sockaddr_mISDN	*maddr;
+
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
+		     __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
+		     sk->sk_protocol);
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+		return -EINVAL;
+
+	if (len < MISDN_HEADER_LEN)
+		return -EINVAL;
+
+	if (sk->sk_state != MISDN_BOUND)
+		return -EBADFD;
+
+	lock_sock(sk);
+
+	skb = _l2_alloc_skb(len, GFP_KERNEL);
+	if (!skb)
+		goto done;
+
+	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+		err = -EFAULT;
+		goto drop;
+	}
+
+	memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
+	skb_pull(skb, MISDN_HEADER_LEN);
+
+	if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+		/* if we have a address, we use it */
+		maddr = (struct sockaddr_mISDN *)msg->msg_name;
+		mISDN_HEAD_ID(skb) = maddr->channel;
+	} else { /* use default for L2 messages */
+		if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+		    (sk->sk_protocol == ISDN_P_LAPD_NT))
+		    mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
+	}
+
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s: ID:%x\n",
+		     __func__, mISDN_HEAD_ID(skb));
+
+	err = -ENODEV;
+	if (!_pms(sk)->ch.peer ||
+	    (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb)))
+		goto drop;
+
+	err = len;
+
+done:
+	release_sock(sk);
+	return err;
+
+drop:
+	kfree_skb(skb);
+	goto done;
+}
+
+static int
+data_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+	if (!sk)
+		return 0;
+	switch (sk->sk_protocol) {
+	case ISDN_P_TE_S0:
+	case ISDN_P_NT_S0:
+	case ISDN_P_TE_E1:
+	case ISDN_P_NT_E1:
+		if (sk->sk_state == MISDN_BOUND)
+			delete_channel(&_pms(sk)->ch);
+		else
+			mISDN_sock_unlink(&data_sockets, sk);
+		break;
+	case ISDN_P_LAPD_TE:
+	case ISDN_P_LAPD_NT:
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_HDLC:
+	case ISDN_P_B_X75SLP:
+	case ISDN_P_B_L2DTMF:
+	case ISDN_P_B_L2DSP:
+	case ISDN_P_B_L2DSPHDLC:
+		delete_channel(&_pms(sk)->ch);
+		mISDN_sock_unlink(&data_sockets, sk);
+		break;
+	}
+
+	lock_sock(sk);
+
+	sock_orphan(sk);
+	skb_queue_purge(&sk->sk_receive_queue);
+
+	release_sock(sk);
+	sock_put(sk);
+
+	return 0;
+}
+
+static int
+data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p)
+{
+	struct mISDN_ctrl_req	cq;
+	int			err = -EINVAL, val;
+	struct mISDNchannel	*bchan, *next;
+
+	lock_sock(sk);
+	if (!_pms(sk)->dev) {
+		err = -ENODEV;
+		goto done;
+	}
+	switch (cmd) {
+	case IMCTRLREQ:
+		if (copy_from_user(&cq, p, sizeof(cq))) {
+			err = -EFAULT;
+			break;
+		}
+		if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
+			list_for_each_entry_safe(bchan, next,
+				&_pms(sk)->dev->bchannels, list) {
+				if (bchan->nr == cq.channel) {
+					err = bchan->ctrl(bchan,
+						CONTROL_CHANNEL, &cq);
+					break;
+				}
+			}
+		} else
+			err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D,
+				CONTROL_CHANNEL, &cq);
+		if (err)
+			break;
+		if (copy_to_user(p, &cq, sizeof(cq)))
+			err = -EFAULT;
+		break;
+	case IMCLEAR_L2:
+		if (sk->sk_protocol != ISDN_P_LAPD_NT) {
+			err = -EINVAL;
+			break;
+		}
+		if (get_user(val, (int __user *)p)) {
+			err = -EFAULT;
+			break;
+		}
+		err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
+		    CONTROL_CHANNEL, &val);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int
+data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	int 			err = 0, id;
+	struct sock		*sk = sock->sk;
+	struct mISDNdevice	*dev;
+	struct mISDNversion	ver;
+
+	switch (cmd) {
+	case IMGETVERSION:
+		ver.major = MISDN_MAJOR_VERSION;
+		ver.minor = MISDN_MINOR_VERSION;
+		ver.release = MISDN_RELEASE;
+		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+			err = -EFAULT;
+		break;
+	case IMGETCOUNT:
+		id = get_mdevice_count();
+		if (put_user(id, (int __user *)arg))
+			err = -EFAULT;
+		break;
+	case IMGETDEVINFO:
+		if (get_user(id, (int __user *)arg)) {
+			err = -EFAULT;
+			break;
+		}
+		dev = get_mdevice(id);
+		if (dev) {
+			struct mISDN_devinfo di;
+
+			di.id = dev->id;
+			di.Dprotocols = dev->Dprotocols;
+			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+			di.protocol = dev->D.protocol;
+			memcpy(di.channelmap, dev->channelmap,
+				MISDN_CHMAP_SIZE * 4);
+			di.nrbchan = dev->nrbchan;
+			strcpy(di.name, dev->name);
+			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+				err = -EFAULT;
+		} else
+			err = -ENODEV;
+		break;
+	default:
+		if (sk->sk_state == MISDN_BOUND)
+			err = data_sock_ioctl_bound(sk, cmd,
+				(void __user *)arg);
+		else
+			err = -ENOTCONN;
+	}
+	return err;
+}
+
+static int data_sock_setsockopt(struct socket *sock, int level, int optname,
+	char __user *optval, int len)
+{
+	struct sock *sk = sock->sk;
+	int err = 0, opt = 0;
+
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock,
+		    level, optname, optval, len);
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case MISDN_TIME_STAMP:
+		if (get_user(opt, (int __user *)optval)) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (opt)
+			_pms(sk)->cmask |= MISDN_TIME_STAMP;
+		else
+			_pms(sk)->cmask &= ~MISDN_TIME_STAMP;
+		break;
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	}
+	release_sock(sk);
+	return err;
+}
+
+static int data_sock_getsockopt(struct socket *sock, int level, int optname,
+	char __user *optval, int __user *optlen)
+{
+	struct sock *sk = sock->sk;
+	int len, opt;
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	switch (optname) {
+	case MISDN_TIME_STAMP:
+		if (_pms(sk)->cmask & MISDN_TIME_STAMP)
+			opt = 1;
+		else
+			opt = 0;
+
+		if (put_user(opt, optval))
+			return -EFAULT;
+		break;
+	default:
+		return -ENOPROTOOPT;
+	}
+
+	return 0;
+}
+
+static int
+data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	if (*debug & DEBUG_SOCKET)
+		printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+	if (addr_len != sizeof(struct sockaddr_mISDN))
+		return -EINVAL;
+	if (!maddr || maddr->family != AF_ISDN)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (_pms(sk)->dev) {
+		err = -EALREADY;
+		goto done;
+	}
+	_pms(sk)->dev = get_mdevice(maddr->dev);
+	if (!_pms(sk)->dev) {
+		err = -ENODEV;
+		goto done;
+	}
+	_pms(sk)->ch.send = mISDN_send;
+	_pms(sk)->ch.ctrl = mISDN_ctrl;
+
+	switch (sk->sk_protocol) {
+	case ISDN_P_TE_S0:
+	case ISDN_P_NT_S0:
+	case ISDN_P_TE_E1:
+	case ISDN_P_NT_E1:
+		mISDN_sock_unlink(&data_sockets, sk);
+		err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch,
+		    sk->sk_protocol, maddr);
+		if (err)
+			mISDN_sock_link(&data_sockets, sk);
+		break;
+	case ISDN_P_LAPD_TE:
+	case ISDN_P_LAPD_NT:
+		err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch,
+		    sk->sk_protocol, maddr);
+		break;
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_HDLC:
+	case ISDN_P_B_X75SLP:
+	case ISDN_P_B_L2DTMF:
+	case ISDN_P_B_L2DSP:
+	case ISDN_P_B_L2DSPHDLC:
+		err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch,
+		    sk->sk_protocol, maddr);
+		break;
+	default:
+		err = -EPROTONOSUPPORT;
+	}
+	if (err)
+		goto done;
+	sk->sk_state = MISDN_BOUND;
+	_pms(sk)->ch.protocol = sk->sk_protocol;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int
+data_sock_getname(struct socket *sock, struct sockaddr *addr,
+    int *addr_len, int peer)
+{
+	struct sockaddr_mISDN 	*maddr = (struct sockaddr_mISDN *) addr;
+	struct sock		*sk = sock->sk;
+
+	if (!_pms(sk)->dev)
+		return -EBADFD;
+
+	lock_sock(sk);
+
+	*addr_len = sizeof(*maddr);
+	maddr->dev = _pms(sk)->dev->id;
+	maddr->channel = _pms(sk)->ch.nr;
+	maddr->sapi = _pms(sk)->ch.addr & 0xff;
+	maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
+	release_sock(sk);
+	return 0;
+}
+
+static const struct proto_ops data_sock_ops = {
+	.family		= PF_ISDN,
+	.owner		= THIS_MODULE,
+	.release	= data_sock_release,
+	.ioctl		= data_sock_ioctl,
+	.bind		= data_sock_bind,
+	.getname	= data_sock_getname,
+	.sendmsg	= mISDN_sock_sendmsg,
+	.recvmsg	= mISDN_sock_recvmsg,
+	.poll		= datagram_poll,
+	.listen		= sock_no_listen,
+	.shutdown	= sock_no_shutdown,
+	.setsockopt	= data_sock_setsockopt,
+	.getsockopt	= data_sock_getsockopt,
+	.connect	= sock_no_connect,
+	.socketpair	= sock_no_socketpair,
+	.accept		= sock_no_accept,
+	.mmap		= sock_no_mmap
+};
+
+static int
+data_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	if (sock->type != SOCK_DGRAM)
+		return -ESOCKTNOSUPPORT;
+
+	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+	if (!sk)
+		return -ENOMEM;
+
+	sock_init_data(sock, sk);
+
+	sock->ops = &data_sock_ops;
+	sock->state = SS_UNCONNECTED;
+	sock_reset_flag(sk, SOCK_ZAPPED);
+
+	sk->sk_protocol = protocol;
+	sk->sk_state    = MISDN_OPEN;
+	mISDN_sock_link(&data_sockets, sk);
+
+	return 0;
+}
+
+static int
+base_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+	if (!sk)
+		return 0;
+
+	mISDN_sock_unlink(&base_sockets, sk);
+	sock_orphan(sk);
+	sock_put(sk);
+
+	return 0;
+}
+
+static int
+base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	int 			err = 0, id;
+	struct mISDNdevice	*dev;
+	struct mISDNversion	ver;
+
+	switch (cmd) {
+	case IMGETVERSION:
+		ver.major = MISDN_MAJOR_VERSION;
+		ver.minor = MISDN_MINOR_VERSION;
+		ver.release = MISDN_RELEASE;
+		if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+			err = -EFAULT;
+		break;
+	case IMGETCOUNT:
+		id = get_mdevice_count();
+		if (put_user(id, (int __user *)arg))
+			err = -EFAULT;
+		break;
+	case IMGETDEVINFO:
+		if (get_user(id, (int __user *)arg)) {
+			err = -EFAULT;
+			break;
+		}
+		dev = get_mdevice(id);
+		if (dev) {
+			struct mISDN_devinfo di;
+
+			di.id = dev->id;
+			di.Dprotocols = dev->Dprotocols;
+			di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+			di.protocol = dev->D.protocol;
+			memcpy(di.channelmap, dev->channelmap,
+				MISDN_CHMAP_SIZE * 4);
+			di.nrbchan = dev->nrbchan;
+			strcpy(di.name, dev->name);
+			if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+				err = -EFAULT;
+		} else
+			err = -ENODEV;
+		break;
+	default:
+		err = -EINVAL;
+	}
+	return err;
+}
+
+static int
+base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	if (!maddr || maddr->family != AF_ISDN)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (_pms(sk)->dev) {
+		err = -EALREADY;
+		goto done;
+	}
+
+	_pms(sk)->dev = get_mdevice(maddr->dev);
+	if (!_pms(sk)->dev) {
+		err = -ENODEV;
+		goto done;
+	}
+	sk->sk_state = MISDN_BOUND;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static const struct proto_ops base_sock_ops = {
+	.family		= PF_ISDN,
+	.owner		= THIS_MODULE,
+	.release	= base_sock_release,
+	.ioctl		= base_sock_ioctl,
+	.bind		= base_sock_bind,
+	.getname	= sock_no_getname,
+	.sendmsg	= sock_no_sendmsg,
+	.recvmsg	= sock_no_recvmsg,
+	.poll		= sock_no_poll,
+	.listen		= sock_no_listen,
+	.shutdown	= sock_no_shutdown,
+	.setsockopt	= sock_no_setsockopt,
+	.getsockopt	= sock_no_getsockopt,
+	.connect	= sock_no_connect,
+	.socketpair	= sock_no_socketpair,
+	.accept		= sock_no_accept,
+	.mmap		= sock_no_mmap
+};
+
+
+static int
+base_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+	if (!sk)
+		return -ENOMEM;
+
+	sock_init_data(sock, sk);
+	sock->ops = &base_sock_ops;
+	sock->state = SS_UNCONNECTED;
+	sock_reset_flag(sk, SOCK_ZAPPED);
+	sk->sk_protocol = protocol;
+	sk->sk_state    = MISDN_OPEN;
+	mISDN_sock_link(&base_sockets, sk);
+
+	return 0;
+}
+
+static int
+mISDN_sock_create(struct net *net, struct socket *sock, int proto)
+{
+	int err = -EPROTONOSUPPORT;
+
+	switch	(proto) {
+	case ISDN_P_BASE:
+		err = base_sock_create(net, sock, proto);
+		break;
+	case ISDN_P_TE_S0:
+	case ISDN_P_NT_S0:
+	case ISDN_P_TE_E1:
+	case ISDN_P_NT_E1:
+	case ISDN_P_LAPD_TE:
+	case ISDN_P_LAPD_NT:
+	case ISDN_P_B_RAW:
+	case ISDN_P_B_HDLC:
+	case ISDN_P_B_X75SLP:
+	case ISDN_P_B_L2DTMF:
+	case ISDN_P_B_L2DSP:
+	case ISDN_P_B_L2DSPHDLC:
+		err = data_sock_create(net, sock, proto);
+		break;
+	default:
+		return err;
+	}
+
+	return err;
+}
+
+static struct
+net_proto_family mISDN_sock_family_ops = {
+	.owner  = THIS_MODULE,
+	.family = PF_ISDN,
+	.create = mISDN_sock_create,
+};
+
+int
+misdn_sock_init(u_int *deb)
+{
+	int err;
+
+	debug = deb;
+	err = sock_register(&mISDN_sock_family_ops);
+	if (err)
+		printk(KERN_ERR "%s: error(%d)\n", __func__, err);
+	return err;
+}
+
+void
+misdn_sock_cleanup(void)
+{
+	sock_unregister(PF_ISDN);
+}
+
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
new file mode 100644
index 0000000..54cfddc
--- /dev/null
+++ b/drivers/isdn/mISDN/stack.c
@@ -0,0 +1,674 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/kthread.h>
+#include "core.h"
+
+static u_int	*debug;
+
+static inline void
+_queue_message(struct mISDNstack *st, struct sk_buff *skb)
+{
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+
+	if (*debug & DEBUG_QUEUE_FUNC)
+		printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+		    __func__, hh->prim, hh->id, skb);
+	skb_queue_tail(&st->msgq, skb);
+	if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) {
+		test_and_set_bit(mISDN_STACK_WORK, &st->status);
+		wake_up_interruptible(&st->workq);
+	}
+}
+
+int
+mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	_queue_message(ch->st, skb);
+	return 0;
+}
+
+static struct mISDNchannel *
+get_channel4id(struct mISDNstack *st, u_int id)
+{
+	struct mISDNchannel	*ch;
+
+	mutex_lock(&st->lmutex);
+	list_for_each_entry(ch, &st->layer2, list) {
+		if (id == ch->nr)
+			goto unlock;
+	}
+	ch = NULL;
+unlock:
+	mutex_unlock(&st->lmutex);
+	return ch;
+}
+
+static void
+send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb)
+{
+	struct hlist_node	*node;
+	struct sock		*sk;
+	struct sk_buff		*cskb = NULL;
+
+	read_lock(&sl->lock);
+	sk_for_each(sk, node, &sl->head) {
+		if (sk->sk_state != MISDN_BOUND)
+			continue;
+		if (!cskb)
+			cskb = skb_copy(skb, GFP_KERNEL);
+		if (!cskb) {
+			printk(KERN_WARNING "%s no skb\n", __func__);
+			break;
+		}
+		if (!sock_queue_rcv_skb(sk, cskb))
+			cskb = NULL;
+	}
+	read_unlock(&sl->lock);
+	if (cskb)
+		dev_kfree_skb(cskb);
+}
+
+static void
+send_layer2(struct mISDNstack *st, struct sk_buff *skb)
+{
+	struct sk_buff		*cskb;
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	struct mISDNchannel	*ch;
+	int			ret;
+
+	if (!st)
+		return;
+	mutex_lock(&st->lmutex);
+	if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */
+		list_for_each_entry(ch, &st->layer2, list) {
+			if (list_is_last(&ch->list, &st->layer2)) {
+				cskb = skb;
+				skb = NULL;
+			} else {
+				cskb = skb_copy(skb, GFP_KERNEL);
+			}
+			if (cskb) {
+				ret = ch->send(ch, cskb);
+				if (ret) {
+					if (*debug & DEBUG_SEND_ERR)
+						printk(KERN_DEBUG
+						    "%s ch%d prim(%x) addr(%x)"
+						    " err %d\n",
+						    __func__, ch->nr,
+						    hh->prim, ch->addr, ret);
+					dev_kfree_skb(cskb);
+				}
+			} else {
+				printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+				    __func__, ch->nr, ch->addr);
+				goto out;
+			}
+		}
+	} else {
+		list_for_each_entry(ch, &st->layer2, list) {
+			if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) {
+				ret = ch->send(ch, skb);
+				if (!ret)
+					skb = NULL;
+				goto out;
+			}
+		}
+		ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb);
+		if (!ret)
+			skb = NULL;
+		else if (*debug & DEBUG_SEND_ERR)
+			printk(KERN_DEBUG
+			    "%s ch%d mgr prim(%x) addr(%x) err %d\n",
+			    __func__, ch->nr, hh->prim, ch->addr, ret);
+	}
+out:
+	mutex_unlock(&st->lmutex);
+	if (skb)
+		dev_kfree_skb(skb);
+}
+
+static inline int
+send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
+{
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	struct mISDNchannel	*ch;
+	int	lm;
+
+	lm = hh->prim & MISDN_LAYERMASK;
+	if (*debug & DEBUG_QUEUE_FUNC)
+		printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+		    __func__, hh->prim, hh->id, skb);
+	if (lm == 0x1) {
+		if (!hlist_empty(&st->l1sock.head)) {
+			__net_timestamp(skb);
+			send_socklist(&st->l1sock, skb);
+		}
+		return st->layer1->send(st->layer1, skb);
+	} else if (lm == 0x2) {
+		if (!hlist_empty(&st->l1sock.head))
+			send_socklist(&st->l1sock, skb);
+		send_layer2(st, skb);
+		return 0;
+	} else if (lm == 0x4) {
+		ch = get_channel4id(st, hh->id);
+		if (ch)
+			return ch->send(ch, skb);
+		else
+			printk(KERN_WARNING
+			    "%s: dev(%s) prim(%x) id(%x) no channel\n",
+			    __func__, st->dev->name, hh->prim, hh->id);
+	} else if (lm == 0x8) {
+		WARN_ON(lm == 0x8);
+		ch = get_channel4id(st, hh->id);
+		if (ch)
+			return ch->send(ch, skb);
+		else
+			printk(KERN_WARNING
+			    "%s: dev(%s) prim(%x) id(%x) no channel\n",
+			    __func__, st->dev->name, hh->prim, hh->id);
+	} else {
+		/* broadcast not handled yet */
+		printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
+		    __func__, st->dev->name, hh->prim);
+	}
+	return -ESRCH;
+}
+
+static void
+do_clear_stack(struct mISDNstack *st)
+{
+}
+
+static int
+mISDNStackd(void *data)
+{
+	struct mISDNstack *st = data;
+	int err = 0;
+
+#ifdef CONFIG_SMP
+	lock_kernel();
+#endif
+	sigfillset(&current->blocked);
+#ifdef CONFIG_SMP
+	unlock_kernel();
+#endif
+	if (*debug & DEBUG_MSG_THREAD)
+		printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name);
+
+	if (st->notify != NULL) {
+		complete(st->notify);
+		st->notify = NULL;
+	}
+
+	for (;;) {
+		struct sk_buff	*skb;
+
+		if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) {
+			test_and_clear_bit(mISDN_STACK_WORK, &st->status);
+			test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+		} else
+			test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+		while (test_bit(mISDN_STACK_WORK, &st->status)) {
+			skb = skb_dequeue(&st->msgq);
+			if (!skb) {
+				test_and_clear_bit(mISDN_STACK_WORK,
+					&st->status);
+				/* test if a race happens */
+				skb = skb_dequeue(&st->msgq);
+				if (!skb)
+					continue;
+				test_and_set_bit(mISDN_STACK_WORK,
+				    &st->status);
+			}
+#ifdef MISDN_MSG_STATS
+			st->msg_cnt++;
+#endif
+			err = send_msg_to_layer(st, skb);
+			if (unlikely(err)) {
+				if (*debug & DEBUG_SEND_ERR)
+					printk(KERN_DEBUG
+					    "%s: %s prim(%x) id(%x) "
+					    "send call(%d)\n",
+					    __func__, st->dev->name,
+					    mISDN_HEAD_PRIM(skb),
+					    mISDN_HEAD_ID(skb), err);
+				dev_kfree_skb(skb);
+				continue;
+			}
+			if (unlikely(test_bit(mISDN_STACK_STOPPED,
+			    &st->status))) {
+				test_and_clear_bit(mISDN_STACK_WORK,
+				    &st->status);
+				test_and_clear_bit(mISDN_STACK_RUNNING,
+				    &st->status);
+				break;
+			}
+		}
+		if (test_bit(mISDN_STACK_CLEARING, &st->status)) {
+			test_and_set_bit(mISDN_STACK_STOPPED, &st->status);
+			test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+			do_clear_stack(st);
+			test_and_clear_bit(mISDN_STACK_CLEARING, &st->status);
+			test_and_set_bit(mISDN_STACK_RESTART, &st->status);
+		}
+		if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) {
+			test_and_clear_bit(mISDN_STACK_STOPPED, &st->status);
+			test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+			if (!skb_queue_empty(&st->msgq))
+				test_and_set_bit(mISDN_STACK_WORK,
+				    &st->status);
+		}
+		if (test_bit(mISDN_STACK_ABORT, &st->status))
+			break;
+		if (st->notify != NULL) {
+			complete(st->notify);
+			st->notify = NULL;
+		}
+#ifdef MISDN_MSG_STATS
+		st->sleep_cnt++;
+#endif
+		test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+		wait_event_interruptible(st->workq, (st->status &
+		    mISDN_STACK_ACTION_MASK));
+		if (*debug & DEBUG_MSG_THREAD)
+			printk(KERN_DEBUG "%s: %s wake status %08lx\n",
+			    __func__, st->dev->name, st->status);
+		test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
+
+		test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
+
+		if (test_bit(mISDN_STACK_STOPPED, &st->status)) {
+			test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+#ifdef MISDN_MSG_STATS
+			st->stopped_cnt++;
+#endif
+		}
+	}
+#ifdef MISDN_MSG_STATS
+	printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
+	    "msg %d sleep %d stopped\n",
+	    st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt);
+	printk(KERN_DEBUG
+	    "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n",
+	    st->dev->name, st->thread->utime, st->thread->stime);
+	printk(KERN_DEBUG
+	    "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
+	    st->dev->name, st->thread->nvcsw, st->thread->nivcsw);
+	printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
+	    st->dev->name);
+#endif
+	test_and_set_bit(mISDN_STACK_KILLED, &st->status);
+	test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+	test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+	test_and_clear_bit(mISDN_STACK_ABORT, &st->status);
+	skb_queue_purge(&st->msgq);
+	st->thread = NULL;
+	if (st->notify != NULL) {
+		complete(st->notify);
+		st->notify = NULL;
+	}
+	return 0;
+}
+
+static int
+l1_receive(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	if (!ch->st)
+		return -ENODEV;
+	__net_timestamp(skb);
+	_queue_message(ch->st, skb);
+	return 0;
+}
+
+void
+set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei)
+{
+	ch->addr = sapi | (tei << 8);
+}
+
+void
+__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+	list_add_tail(&ch->list, &st->layer2);
+}
+
+void
+add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+	mutex_lock(&st->lmutex);
+	__add_layer2(ch, st);
+	mutex_unlock(&st->lmutex);
+}
+
+static int
+st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	if (!ch->st || ch->st->layer1)
+		return -EINVAL;
+	return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg);
+}
+
+int
+create_stack(struct mISDNdevice *dev)
+{
+	struct mISDNstack	*newst;
+	int			err;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL);
+	if (!newst) {
+		printk(KERN_ERR "kmalloc mISDN_stack failed\n");
+		return -ENOMEM;
+	}
+	newst->dev = dev;
+	INIT_LIST_HEAD(&newst->layer2);
+	INIT_HLIST_HEAD(&newst->l1sock.head);
+	rwlock_init(&newst->l1sock.lock);
+	init_waitqueue_head(&newst->workq);
+	skb_queue_head_init(&newst->msgq);
+	mutex_init(&newst->lmutex);
+	dev->D.st = newst;
+	err = create_teimanager(dev);
+	if (err) {
+		printk(KERN_ERR "kmalloc teimanager failed\n");
+		kfree(newst);
+		return err;
+	}
+	dev->teimgr->peer = &newst->own;
+	dev->teimgr->recv = mISDN_queue_message;
+	dev->teimgr->st = newst;
+	newst->layer1 = &dev->D;
+	dev->D.recv = l1_receive;
+	dev->D.peer = &newst->own;
+	newst->own.st = newst;
+	newst->own.ctrl = st_own_ctrl;
+	newst->own.send = mISDN_queue_message;
+	newst->own.recv = mISDN_queue_message;
+	if (*debug & DEBUG_CORE_FUNC)
+		printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name);
+	newst->notify = &done;
+	newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
+		newst->dev->name);
+	if (IS_ERR(newst->thread)) {
+		err = PTR_ERR(newst->thread);
+		printk(KERN_ERR
+			"mISDN:cannot create kernel thread for %s (%d)\n",
+			newst->dev->name, err);
+		delete_teimanager(dev->teimgr);
+		kfree(newst);
+	} else
+		wait_for_completion(&done);
+	return err;
+}
+
+int
+connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
+		u_int protocol, struct sockaddr_mISDN *adr)
+{
+	struct mISDN_sock	*msk = container_of(ch, struct mISDN_sock, ch);
+	struct channel_req	rq;
+	int			err;
+
+
+	if (*debug &  DEBUG_CORE_FUNC)
+		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+			__func__, dev->name, protocol, adr->dev, adr->channel,
+			 adr->sapi, adr->tei);
+	switch (protocol) {
+	case ISDN_P_NT_S0:
+	case ISDN_P_NT_E1:
+	case ISDN_P_TE_S0:
+	case ISDN_P_TE_E1:
+#ifdef PROTOCOL_CHECK
+		/* this should be enhanced */
+		if (!list_empty(&dev->D.st->layer2)
+			&& dev->D.protocol != protocol)
+			return -EBUSY;
+		if (!hlist_empty(&dev->D.st->l1sock.head)
+			&& dev->D.protocol != protocol)
+			return -EBUSY;
+#endif
+		ch->recv = mISDN_queue_message;
+		ch->peer = &dev->D.st->own;
+		ch->st = dev->D.st;
+		rq.protocol = protocol;
+		rq.adr.channel = 0;
+		err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+		printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+		if (err)
+			return err;
+		write_lock_bh(&dev->D.st->l1sock.lock);
+		sk_add_node(&msk->sk, &dev->D.st->l1sock.head);
+		write_unlock_bh(&dev->D.st->l1sock.lock);
+		break;
+	default:
+		return -ENOPROTOOPT;
+	}
+	return 0;
+}
+
+int
+connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
+    u_int protocol, struct sockaddr_mISDN *adr)
+{
+	struct channel_req	rq, rq2;
+	int			pmask, err;
+	struct Bprotocol	*bp;
+
+	if (*debug &  DEBUG_CORE_FUNC)
+		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+			__func__, dev->name, protocol,
+			adr->dev, adr->channel, adr->sapi,
+			adr->tei);
+	ch->st = dev->D.st;
+	pmask = 1 << (protocol & ISDN_P_B_MASK);
+	if (pmask & dev->Bprotocols) {
+		rq.protocol = protocol;
+		rq.adr = *adr;
+		err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+		if (err)
+			return err;
+		ch->recv = rq.ch->send;
+		ch->peer = rq.ch;
+		rq.ch->recv = ch->send;
+		rq.ch->peer = ch;
+		rq.ch->st = dev->D.st;
+	} else {
+		bp = get_Bprotocol4mask(pmask);
+		if (!bp)
+			return -ENOPROTOOPT;
+		rq2.protocol = protocol;
+		rq2.adr = *adr;
+		rq2.ch = ch;
+		err = bp->create(&rq2);
+		if (err)
+			return err;
+		ch->recv = rq2.ch->send;
+		ch->peer = rq2.ch;
+		rq2.ch->st = dev->D.st;
+		rq.protocol = rq2.protocol;
+		rq.adr = *adr;
+		err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+		if (err) {
+			rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL);
+			return err;
+		}
+		rq2.ch->recv = rq.ch->send;
+		rq2.ch->peer = rq.ch;
+		rq.ch->recv = rq2.ch->send;
+		rq.ch->peer = rq2.ch;
+		rq.ch->st = dev->D.st;
+	}
+	ch->protocol = protocol;
+	ch->nr = rq.ch->nr;
+	return 0;
+}
+
+int
+create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
+    u_int protocol, struct sockaddr_mISDN *adr)
+{
+	struct channel_req	rq;
+	int			err;
+
+	if (*debug &  DEBUG_CORE_FUNC)
+		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+			__func__, dev->name, protocol,
+			adr->dev, adr->channel, adr->sapi,
+			adr->tei);
+	rq.protocol = ISDN_P_TE_S0;
+	if (dev->Dprotocols & (1 << ISDN_P_TE_E1))
+		rq.protocol = ISDN_P_TE_E1;
+	switch (protocol) {
+	case ISDN_P_LAPD_NT:
+		rq.protocol = ISDN_P_NT_S0;
+		if (dev->Dprotocols & (1 << ISDN_P_NT_E1))
+			rq.protocol = ISDN_P_NT_E1;
+	case ISDN_P_LAPD_TE:
+#ifdef PROTOCOL_CHECK
+		/* this should be enhanced */
+		if (!list_empty(&dev->D.st->layer2)
+			&& dev->D.protocol != protocol)
+			return -EBUSY;
+		if (!hlist_empty(&dev->D.st->l1sock.head)
+			&& dev->D.protocol != protocol)
+			return -EBUSY;
+#endif
+		ch->recv = mISDN_queue_message;
+		ch->peer = &dev->D.st->own;
+		ch->st = dev->D.st;
+		rq.adr.channel = 0;
+		err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+		printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+		if (err)
+			break;
+		rq.protocol = protocol;
+		rq.adr = *adr;
+		rq.ch = ch;
+		err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq);
+		printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err);
+		if (!err) {
+			if ((protocol == ISDN_P_LAPD_NT) && !rq.ch)
+				break;
+			add_layer2(rq.ch, dev->D.st);
+			rq.ch->recv = mISDN_queue_message;
+			rq.ch->peer = &dev->D.st->own;
+			rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */
+		}
+		break;
+	default:
+		err = -EPROTONOSUPPORT;
+	}
+	return err;
+}
+
+void
+delete_channel(struct mISDNchannel *ch)
+{
+	struct mISDN_sock	*msk = container_of(ch, struct mISDN_sock, ch);
+	struct mISDNchannel	*pch;
+
+	if (!ch->st) {
+		printk(KERN_WARNING "%s: no stack\n", __func__);
+		return;
+	}
+	if (*debug & DEBUG_CORE_FUNC)
+		printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
+		    ch->st->dev->name, ch->protocol);
+	if (ch->protocol >= ISDN_P_B_START) {
+		if (ch->peer) {
+			ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL);
+			ch->peer = NULL;
+		}
+		return;
+	}
+	switch (ch->protocol) {
+	case ISDN_P_NT_S0:
+	case ISDN_P_TE_S0:
+	case ISDN_P_NT_E1:
+	case ISDN_P_TE_E1:
+		write_lock_bh(&ch->st->l1sock.lock);
+		sk_del_node_init(&msk->sk);
+		write_unlock_bh(&ch->st->l1sock.lock);
+		ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL);
+		break;
+	case ISDN_P_LAPD_TE:
+		pch = get_channel4id(ch->st, ch->nr);
+		if (pch) {
+			mutex_lock(&ch->st->lmutex);
+			list_del(&pch->list);
+			mutex_unlock(&ch->st->lmutex);
+			pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+			pch = ch->st->dev->teimgr;
+			pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+		} else
+			printk(KERN_WARNING "%s: no l2 channel\n",
+			    __func__);
+		break;
+	case ISDN_P_LAPD_NT:
+		pch = ch->st->dev->teimgr;
+		if (pch) {
+			pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+		} else
+			printk(KERN_WARNING "%s: no l2 channel\n",
+			    __func__);
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+void
+delete_stack(struct mISDNdevice *dev)
+{
+	struct mISDNstack	*st = dev->D.st;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	if (*debug & DEBUG_CORE_FUNC)
+		printk(KERN_DEBUG "%s: st(%s)\n", __func__,
+		    st->dev->name);
+	if (dev->teimgr)
+		delete_teimanager(dev->teimgr);
+	if (st->thread) {
+		if (st->notify) {
+			printk(KERN_WARNING "%s: notifier in use\n",
+			    __func__);
+				complete(st->notify);
+		}
+		st->notify = &done;
+		test_and_set_bit(mISDN_STACK_ABORT, &st->status);
+		test_and_set_bit(mISDN_STACK_WAKEUP, &st->status);
+		wake_up_interruptible(&st->workq);
+		wait_for_completion(&done);
+	}
+	if (!list_empty(&st->layer2))
+		printk(KERN_WARNING "%s: layer2 list not empty\n",
+		    __func__);
+	if (!hlist_empty(&st->l1sock.head))
+		printk(KERN_WARNING "%s: layer1 list not empty\n",
+		    __func__);
+	kfree(st);
+}
+
+void
+mISDN_initstack(u_int *dp)
+{
+	debug = dp;
+}
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
new file mode 100644
index 0000000..56a76a0
--- /dev/null
+++ b/drivers/isdn/mISDN/tei.c
@@ -0,0 +1,1340 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "layer2.h"
+#include <linux/random.h>
+#include "core.h"
+
+#define ID_REQUEST	1
+#define ID_ASSIGNED	2
+#define ID_DENIED	3
+#define ID_CHK_REQ	4
+#define ID_CHK_RES	5
+#define ID_REMOVE	6
+#define ID_VERIFY	7
+
+#define TEI_ENTITY_ID	0xf
+
+#define MGR_PH_ACTIVE	16
+#define MGR_PH_NOTREADY	17
+
+#define DATIMER_VAL	10000
+
+static 	u_int	*debug;
+
+static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL};
+
+enum {
+	ST_L1_DEACT,
+	ST_L1_DEACT_PENDING,
+	ST_L1_ACTIV,
+};
+#define DEACT_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strDeactState[] =
+{
+	"ST_L1_DEACT",
+	"ST_L1_DEACT_PENDING",
+	"ST_L1_ACTIV",
+};
+
+enum {
+	EV_ACTIVATE,
+	EV_ACTIVATE_IND,
+	EV_DEACTIVATE,
+	EV_DEACTIVATE_IND,
+	EV_UI,
+	EV_DATIMER,
+};
+
+#define DEACT_EVENT_COUNT (EV_DATIMER+1)
+
+static char *strDeactEvent[] =
+{
+	"EV_ACTIVATE",
+	"EV_ACTIVATE_IND",
+	"EV_DEACTIVATE",
+	"EV_DEACTIVATE_IND",
+	"EV_UI",
+	"EV_DATIMER",
+};
+
+static void
+da_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	struct manager	*mgr = fi->userdata;
+	va_list va;
+
+	if (!(*debug & DEBUG_L2_TEIFSM))
+		return;
+	va_start(va, fmt);
+	printk(KERN_DEBUG "mgr(%d): ", mgr->ch.st->dev->id);
+	vprintk(fmt, va);
+	printk("\n");
+	va_end(va);
+}
+
+static void
+da_activate(struct FsmInst *fi, int event, void *arg)
+{
+	struct manager	*mgr = fi->userdata;
+
+	if (fi->state == ST_L1_DEACT_PENDING)
+		mISDN_FsmDelTimer(&mgr->datimer, 1);
+	mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+}
+
+static void
+da_deactivate_ind(struct FsmInst *fi, int event, void *arg)
+{
+	mISDN_FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+da_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+	struct manager	*mgr = fi->userdata;
+	struct layer2	*l2;
+	u_long		flags;
+
+	read_lock_irqsave(&mgr->lock, flags);
+	list_for_each_entry(l2, &mgr->layer2, list) {
+		if (l2->l2m.state > ST_L2_4) {
+			/* have still activ TEI */
+			read_unlock_irqrestore(&mgr->lock, flags);
+			return;
+		}
+	}
+	read_unlock_irqrestore(&mgr->lock, flags);
+	/* All TEI are inactiv */
+	mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 1);
+	mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING);
+}
+
+static void
+da_ui(struct FsmInst *fi, int event, void *arg)
+{
+	struct manager	*mgr = fi->userdata;
+
+	/* restart da timer */
+	mISDN_FsmDelTimer(&mgr->datimer, 2);
+	mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 2);
+
+}
+
+static void
+da_timer(struct FsmInst *fi, int event, void *arg)
+{
+	struct manager	*mgr = fi->userdata;
+	struct layer2	*l2;
+	u_long		flags;
+
+	/* check again */
+	read_lock_irqsave(&mgr->lock, flags);
+	list_for_each_entry(l2, &mgr->layer2, list) {
+		if (l2->l2m.state > ST_L2_4) {
+			/* have still activ TEI */
+			read_unlock_irqrestore(&mgr->lock, flags);
+			mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+			return;
+		}
+	}
+	read_unlock_irqrestore(&mgr->lock, flags);
+	/* All TEI are inactiv */
+	mISDN_FsmChangeState(fi, ST_L1_DEACT);
+	_queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL,
+	    GFP_ATOMIC);
+}
+
+static struct FsmNode DeactFnList[] =
+{
+	{ST_L1_DEACT, EV_ACTIVATE_IND, da_activate},
+	{ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind},
+	{ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate},
+	{ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate},
+	{ST_L1_DEACT_PENDING, EV_UI, da_ui},
+	{ST_L1_DEACT_PENDING, EV_DATIMER, da_timer},
+};
+
+enum {
+	ST_TEI_NOP,
+	ST_TEI_IDREQ,
+	ST_TEI_IDVERIFY,
+};
+
+#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1)
+
+static char *strTeiState[] =
+{
+	"ST_TEI_NOP",
+	"ST_TEI_IDREQ",
+	"ST_TEI_IDVERIFY",
+};
+
+enum {
+	EV_IDREQ,
+	EV_ASSIGN,
+	EV_ASSIGN_REQ,
+	EV_DENIED,
+	EV_CHKREQ,
+	EV_CHKRESP,
+	EV_REMOVE,
+	EV_VERIFY,
+	EV_TIMER,
+};
+
+#define TEI_EVENT_COUNT (EV_TIMER+1)
+
+static char *strTeiEvent[] =
+{
+	"EV_IDREQ",
+	"EV_ASSIGN",
+	"EV_ASSIGN_REQ",
+	"EV_DENIED",
+	"EV_CHKREQ",
+	"EV_CHKRESP",
+	"EV_REMOVE",
+	"EV_VERIFY",
+	"EV_TIMER",
+};
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+	struct teimgr	*tm = fi->userdata;
+	va_list va;
+
+	if (!(*debug & DEBUG_L2_TEIFSM))
+		return;
+	va_start(va, fmt);
+	printk(KERN_DEBUG "tei(%d): ", tm->l2->tei);
+	vprintk(fmt, va);
+	printk("\n");
+	va_end(va);
+}
+
+
+
+static int
+get_free_id(struct manager *mgr)
+{
+	u64		ids = 0;
+	int		i;
+	struct layer2	*l2;
+
+	list_for_each_entry(l2, &mgr->layer2, list) {
+		if (l2->ch.nr > 63) {
+			printk(KERN_WARNING
+			    "%s: more as 63 layer2 for one device\n",
+			    __func__);
+			return -EBUSY;
+		}
+		test_and_set_bit(l2->ch.nr, (u_long *)&ids);
+	}
+	for (i = 1; i < 64; i++)
+		if (!test_bit(i, (u_long *)&ids))
+			return i;
+	printk(KERN_WARNING "%s: more as 63 layer2 for one device\n",
+	    __func__);
+	return -EBUSY;
+}
+
+static int
+get_free_tei(struct manager *mgr)
+{
+	u64		ids = 0;
+	int		i;
+	struct layer2	*l2;
+
+	list_for_each_entry(l2, &mgr->layer2, list) {
+		if (l2->ch.nr == 0)
+			continue;
+		if ((l2->ch.addr & 0xff) != 0)
+			continue;
+		i = l2->ch.addr >> 8;
+		if (i < 64)
+			continue;
+		i -= 64;
+
+		test_and_set_bit(i, (u_long *)&ids);
+	}
+	for (i = 0; i < 64; i++)
+		if (!test_bit(i, (u_long *)&ids))
+			return i + 64;
+	printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n",
+	    __func__);
+	return -1;
+}
+
+static void
+teiup_create(struct manager *mgr, u_int prim, int len, void *arg)
+{
+	struct sk_buff	*skb;
+	struct mISDNhead *hh;
+	int		err;
+
+	skb = mI_alloc_skb(len, GFP_ATOMIC);
+	if (!skb)
+		return;
+	hh = mISDN_HEAD_P(skb);
+	hh->prim = prim;
+	hh->id = (mgr->ch.nr << 16) | mgr->ch.addr;
+	if (len)
+		memcpy(skb_put(skb, len), arg, len);
+	err = mgr->up->send(mgr->up, skb);
+	if (err) {
+		printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+		dev_kfree_skb(skb);
+	}
+}
+
+static u_int
+new_id(struct manager *mgr)
+{
+	u_int	id;
+
+	id = mgr->nextid++;
+	if (id == 0x7fff)
+		mgr->nextid = 1;
+	id <<= 16;
+	id |= GROUP_TEI << 8;
+	id |= TEI_SAPI;
+	return id;
+}
+
+static void
+do_send(struct manager *mgr)
+{
+	if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+		return;
+
+	if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) {
+		struct sk_buff	*skb = skb_dequeue(&mgr->sendq);
+
+		if (!skb) {
+			test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+			return;
+		}
+		mgr->lastid = mISDN_HEAD_ID(skb);
+		mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+		if (mgr->ch.recv(mgr->ch.peer, skb)) {
+			dev_kfree_skb(skb);
+			test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+			mgr->lastid = MISDN_ID_NONE;
+		}
+	}
+}
+
+static void
+do_ack(struct manager *mgr, u_int id)
+{
+	if (test_bit(MGR_PH_NOTREADY, &mgr->options)) {
+		if (id == mgr->lastid) {
+			if (test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+				struct sk_buff	*skb;
+
+				skb = skb_dequeue(&mgr->sendq);
+				if (skb) {
+					mgr->lastid = mISDN_HEAD_ID(skb);
+					if (!mgr->ch.recv(mgr->ch.peer, skb))
+						return;
+					dev_kfree_skb(skb);
+				}
+			}
+			mgr->lastid = MISDN_ID_NONE;
+			test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+		}
+	}
+}
+
+static void
+mgr_send_down(struct manager *mgr, struct sk_buff *skb)
+{
+	skb_queue_tail(&mgr->sendq, skb);
+	if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+		_queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+		    NULL, GFP_KERNEL);
+	} else {
+		do_send(mgr);
+	}
+}
+
+static int
+dl_unit_data(struct manager *mgr, struct sk_buff *skb)
+{
+	if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */
+		return -EINVAL;
+	if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+		_queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+		    NULL, GFP_KERNEL);
+	skb_push(skb, 3);
+	skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */
+	skb->data[1] = 0xff; /* TEI 127 */
+	skb->data[2] = UI;   /* UI frame */
+	mISDN_HEAD_PRIM(skb) = PH_DATA_REQ;
+	mISDN_HEAD_ID(skb) = new_id(mgr);
+	skb_queue_tail(&mgr->sendq, skb);
+	do_send(mgr);
+	return 0;
+}
+
+unsigned int
+random_ri(void)
+{
+	u16 x;
+
+	get_random_bytes(&x, sizeof(x));
+	return x;
+}
+
+static struct layer2 *
+findtei(struct manager *mgr, int tei)
+{
+	struct layer2	*l2;
+	u_long		flags;
+
+	read_lock_irqsave(&mgr->lock, flags);
+	list_for_each_entry(l2, &mgr->layer2, list) {
+		if ((l2->sapi == 0) && (l2->tei > 0) &&
+		    (l2->tei != GROUP_TEI) && (l2->tei == tei))
+			goto done;
+	}
+	l2 = NULL;
+done:
+	read_unlock_irqrestore(&mgr->lock, flags);
+	return l2;
+}
+
+static void
+put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, u_char tei)
+{
+	struct sk_buff *skb;
+	u_char bp[8];
+
+	bp[0] = (TEI_SAPI << 2);
+	if (test_bit(MGR_OPT_NETWORK, &mgr->options))
+		bp[0] |= 2; /* CR:=1 for net command */
+	bp[1] = (GROUP_TEI << 1) | 0x1;
+	bp[2] = UI;
+	bp[3] = TEI_ENTITY_ID;
+	bp[4] = ri >> 8;
+	bp[5] = ri & 0xff;
+	bp[6] = m_id;
+	bp[7] = (tei << 1) | 1;
+	skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr),
+	    8, bp, GFP_ATOMIC);
+	if (!skb) {
+		printk(KERN_WARNING "%s: no skb for tei msg\n", __func__);
+		return;
+	}
+	mgr_send_down(mgr, skb);
+}
+
+static void
+tei_id_request(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+
+	if (tm->l2->tei != GROUP_TEI) {
+		tm->tei_m.printdebug(&tm->tei_m,
+			"assign request for allready assigned tei %d",
+			tm->l2->tei);
+		return;
+	}
+	tm->ri = random_ri();
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(&tm->tei_m,
+			"assign request ri %d", tm->ri);
+	put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+	mISDN_FsmChangeState(fi, ST_TEI_IDREQ);
+	mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1);
+	tm->nval = 3;
+}
+
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr	*tm = fi->userdata;
+	struct layer2	*l2;
+	u_char *dp = arg;
+	int ri, tei;
+
+	ri = ((unsigned int) *dp++ << 8);
+	ri += *dp++;
+	dp++;
+	tei = *dp >> 1;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "identity assign ri %d tei %d",
+			ri, tei);
+	l2 = findtei(tm->mgr, tei);
+	if (l2) {	/* same tei is in use */
+		if (ri != l2->tm->ri) {
+			tm->tei_m.printdebug(fi,
+				"possible duplicate assignment tei %d", tei);
+			tei_l2(l2, MDL_ERROR_RSP, 0);
+		}
+	} else if (ri == tm->ri) {
+		mISDN_FsmDelTimer(&tm->timer, 1);
+		mISDN_FsmChangeState(fi, ST_TEI_NOP);
+		tei_l2(tm->l2, MDL_ASSIGN_REQ, tei);
+	}
+}
+
+static void
+tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr	*tm = fi->userdata;
+	struct layer2	*l2;
+	u_char *dp = arg;
+	int tei, ri;
+
+	ri = ((unsigned int) *dp++ << 8);
+	ri += *dp++;
+	dp++;
+	tei = *dp >> 1;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d",
+			ri, tei);
+	l2 = findtei(tm->mgr, tei);
+	if (l2) {	/* same tei is in use */
+		if (ri != l2->tm->ri) {	/* and it wasn't our request */
+			tm->tei_m.printdebug(fi,
+				"possible duplicate assignment tei %d", tei);
+			mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL);
+		}
+	}
+}
+
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+	u_char *dp = arg;
+	int ri, tei;
+
+	ri = ((unsigned int) *dp++ << 8);
+	ri += *dp++;
+	dp++;
+	tei = *dp >> 1;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "identity denied ri %d tei %d",
+			ri, tei);
+}
+
+static void
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+	u_char *dp = arg;
+	int tei;
+
+	tei = *(dp+3) >> 1;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "identity check req tei %d", tei);
+	if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) ||
+	    (tei == tm->l2->tei))) {
+		mISDN_FsmDelTimer(&tm->timer, 4);
+		mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+		put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei);
+	}
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+	u_char *dp = arg;
+	int tei;
+
+	tei = *(dp+3) >> 1;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "identity remove tei %d", tei);
+	if ((tm->l2->tei != GROUP_TEI) &&
+	    ((tei == GROUP_TEI) || (tei == tm->l2->tei))) {
+		mISDN_FsmDelTimer(&tm->timer, 5);
+		mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+		tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+	}
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "id verify request for tei %d",
+			tm->l2->tei);
+	put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+	mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+	mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+	tm->nval = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+
+	if (--tm->nval) {
+		tm->ri = random_ri();
+		if (*debug & DEBUG_L2_TEI)
+			tm->tei_m.printdebug(fi, "assign req(%d) ri %d",
+				4 - tm->nval, tm->ri);
+		put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+		mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3);
+	} else {
+		tm->tei_m.printdebug(fi, "assign req failed");
+		tei_l2(tm->l2, MDL_ERROR_RSP, 0);
+		mISDN_FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+
+	if (--tm->nval) {
+		if (*debug & DEBUG_L2_TEI)
+			tm->tei_m.printdebug(fi,
+				"id verify req(%d) for tei %d",
+				3 - tm->nval, tm->l2->tei);
+		put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+		mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+	} else {
+		tm->tei_m.printdebug(fi, "verify req for tei %d failed",
+			tm->l2->tei);
+		tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+		mISDN_FsmChangeState(fi, ST_TEI_NOP);
+	}
+}
+
+static struct FsmNode TeiFnListUser[] =
+{
+	{ST_TEI_NOP, EV_IDREQ, tei_id_request},
+	{ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
+	{ST_TEI_NOP, EV_VERIFY, tei_id_verify},
+	{ST_TEI_NOP, EV_REMOVE, tei_id_remove},
+	{ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req},
+	{ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout},
+	{ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
+	{ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
+	{ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout},
+	{ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
+	{ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
+};
+
+static void
+tei_l2remove(struct layer2 *l2)
+{
+	put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei);
+	tei_l2(l2, MDL_REMOVE_REQ, 0);
+	list_del(&l2->ch.list);
+	l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+}
+
+static void
+tei_assign_req(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+	u_char *dp = arg;
+
+	if (tm->l2->tei == GROUP_TEI) {
+		tm->tei_m.printdebug(&tm->tei_m,
+			"net tei assign request without tei");
+		return;
+	}
+	tm->ri = ((unsigned int) *dp++ << 8);
+	tm->ri += *dp++;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(&tm->tei_m,
+			"net assign request ri %d teim %d", tm->ri, *dp);
+	put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei);
+	mISDN_FsmChangeState(fi, ST_TEI_NOP);
+}
+
+static void
+tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr	*tm = fi->userdata;
+
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "id check request for tei %d",
+		    tm->l2->tei);
+	tm->rcnt = 0;
+	put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+	mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+	mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+	tm->nval = 2;
+}
+
+static void
+tei_id_chk_resp(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+	u_char *dp = arg;
+	int tei;
+
+	tei = dp[3] >> 1;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "identity check resp tei %d", tei);
+	if (tei == tm->l2->tei)
+		tm->rcnt++;
+}
+
+static void
+tei_id_verify_net(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+	u_char *dp = arg;
+	int tei;
+
+	tei = dp[3] >> 1;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(fi, "identity verify req tei %d/%d",
+		    tei, tm->l2->tei);
+	if (tei == tm->l2->tei)
+		tei_id_chk_req_net(fi, event, arg);
+}
+
+static void
+tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg)
+{
+	struct teimgr *tm = fi->userdata;
+
+	if (tm->rcnt == 1) {
+		if (*debug & DEBUG_L2_TEI)
+			tm->tei_m.printdebug(fi,
+			    "check req for tei %d sucessful\n", tm->l2->tei);
+		mISDN_FsmChangeState(fi, ST_TEI_NOP);
+	} else if (tm->rcnt > 1) {
+		/* duplicate assignment; remove */
+		tei_l2remove(tm->l2);
+	} else if (--tm->nval) {
+		if (*debug & DEBUG_L2_TEI)
+			tm->tei_m.printdebug(fi,
+				"id check req(%d) for tei %d",
+				3 - tm->nval, tm->l2->tei);
+		put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+		mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+	} else {
+		tm->tei_m.printdebug(fi, "check req for tei %d failed",
+			tm->l2->tei);
+		mISDN_FsmChangeState(fi, ST_TEI_NOP);
+		tei_l2remove(tm->l2);
+	}
+}
+
+static struct FsmNode TeiFnListNet[] =
+{
+	{ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req},
+	{ST_TEI_NOP, EV_VERIFY, tei_id_verify_net},
+	{ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net},
+	{ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net},
+	{ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp},
+};
+
+static void
+tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len)
+{
+	if (test_bit(FLG_FIXED_TEI, &tm->l2->flag))
+		return;
+	if (*debug & DEBUG_L2_TEI)
+		tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt);
+	if (mt == ID_ASSIGNED)
+		mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp);
+	else if (mt == ID_DENIED)
+		mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp);
+	else if (mt == ID_CHK_REQ)
+		mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp);
+	else if (mt == ID_REMOVE)
+		mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp);
+	else if (mt == ID_VERIFY)
+		mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp);
+	else if (mt == ID_CHK_RES)
+		mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp);
+}
+
+static struct layer2 *
+create_new_tei(struct manager *mgr, int tei)
+{
+	u_long		opt = 0;
+	u_long		flags;
+	int		id;
+	struct layer2	*l2;
+
+	if (!mgr->up)
+		return NULL;
+	if (tei < 64)
+		test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+	if (mgr->ch.st->dev->Dprotocols
+	  & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+		test_and_set_bit(OPTION_L2_PMX, &opt);
+	l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, (u_int)opt, (u_long)tei);
+	if (!l2) {
+		printk(KERN_WARNING "%s:no memory for layer2\n", __func__);
+		return NULL;
+	}
+	l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+	if (!l2->tm) {
+		kfree(l2);
+		printk(KERN_WARNING "%s:no memory for teimgr\n", __func__);
+		return NULL;
+	}
+	l2->tm->mgr = mgr;
+	l2->tm->l2 = l2;
+	l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+	l2->tm->tei_m.userdata = l2->tm;
+	l2->tm->tei_m.printdebug = tei_debug;
+	l2->tm->tei_m.fsm = &teifsmn;
+	l2->tm->tei_m.state = ST_TEI_NOP;
+	l2->tm->tval = 2000; /* T202  2 sec */
+	mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+	write_lock_irqsave(&mgr->lock, flags);
+	id = get_free_id(mgr);
+	list_add_tail(&l2->list, &mgr->layer2);
+	write_unlock_irqrestore(&mgr->lock, flags);
+	if (id < 0) {
+		l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+		printk(KERN_WARNING "%s:no free id\n", __func__);
+		return NULL;
+	} else {
+		l2->ch.nr = id;
+		__add_layer2(&l2->ch, mgr->ch.st);
+		l2->ch.recv = mgr->ch.recv;
+		l2->ch.peer = mgr->ch.peer;
+		l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+	}
+	return l2;
+}
+
+static void
+new_tei_req(struct manager *mgr, u_char *dp)
+{
+	int		tei, ri;
+	struct layer2	*l2;
+
+	ri = dp[0] << 8;
+	ri += dp[1];
+	if (!mgr->up)
+		goto denied;
+	tei = get_free_tei(mgr);
+	if (tei < 0) {
+		printk(KERN_WARNING "%s:No free tei\n", __func__);
+		goto denied;
+	}
+	l2 = create_new_tei(mgr, tei);
+	if (!l2)
+		goto denied;
+	else
+		mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp);
+	return;
+denied:
+	put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI);
+}
+
+static int
+ph_data_ind(struct manager *mgr, struct sk_buff *skb)
+{
+	int		ret = -EINVAL;
+	struct layer2	*l2;
+	u_long		flags;
+	u_char		mt;
+
+	if (skb->len < 8) {
+		if (*debug  & DEBUG_L2_TEI)
+			printk(KERN_DEBUG "%s: short mgr frame %d/8\n",
+			    __func__, skb->len);
+		goto done;
+	}
+	if (*debug  & DEBUG_L2_TEI)
+
+	if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */
+		goto done;
+	if (skb->data[0] & 1) /* EA0 formal error */
+		goto done;
+	if (!(skb->data[1] & 1)) /* EA1 formal error */
+		goto done;
+	if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */
+		goto done;
+	if ((skb->data[2] & 0xef) != UI) /* not UI */
+		goto done;
+	if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */
+		goto done;
+	mt = skb->data[6];
+	switch (mt) {
+	case ID_REQUEST:
+	case ID_CHK_RES:
+	case ID_VERIFY:
+		if (!test_bit(MGR_OPT_NETWORK, &mgr->options))
+			goto done;
+		break;
+	case ID_ASSIGNED:
+	case ID_DENIED:
+	case ID_CHK_REQ:
+	case ID_REMOVE:
+		if (test_bit(MGR_OPT_NETWORK, &mgr->options))
+			goto done;
+		break;
+	default:
+		goto done;
+	}
+	ret = 0;
+	if (mt == ID_REQUEST) {
+		new_tei_req(mgr, &skb->data[4]);
+		goto done;
+	}
+	read_lock_irqsave(&mgr->lock, flags);
+	list_for_each_entry(l2, &mgr->layer2, list) {
+		tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4);
+	}
+	read_unlock_irqrestore(&mgr->lock, flags);
+done:
+	return ret;
+}
+
+int
+l2_tei(struct layer2 *l2, u_int cmd, u_long arg)
+{
+	struct teimgr	*tm = l2->tm;
+
+	if (test_bit(FLG_FIXED_TEI, &l2->flag))
+		return 0;
+	if (*debug & DEBUG_L2_TEI)
+		printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+	switch (cmd) {
+	case MDL_ASSIGN_IND:
+		mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL);
+		break;
+	case MDL_ERROR_IND:
+		if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+			mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei);
+		if (test_bit(MGR_OPT_USER, &tm->mgr->options))
+			mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL);
+		break;
+	case MDL_STATUS_UP_IND:
+		if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+			mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL);
+		break;
+	case MDL_STATUS_DOWN_IND:
+		if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+			mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL);
+		break;
+	case MDL_STATUS_UI_IND:
+		if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+			mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL);
+		break;
+	}
+	return 0;
+}
+
+void
+release_tei(struct layer2 *l2)
+{
+	struct teimgr	*tm = l2->tm;
+	u_long		flags;
+
+	mISDN_FsmDelTimer(&tm->timer, 1);
+	write_lock_irqsave(&tm->mgr->lock, flags);
+	list_del(&l2->list);
+	write_unlock_irqrestore(&tm->mgr->lock, flags);
+	l2->tm = NULL;
+	kfree(tm);
+}
+
+static int
+create_teimgr(struct manager *mgr, struct channel_req *crq)
+{
+	struct layer2	*l2;
+	u_long 		opt = 0;
+	u_long		flags;
+	int		id;
+
+	if (*debug & DEBUG_L2_TEI)
+		printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+			__func__, mgr->ch.st->dev->name, crq->protocol,
+			crq->adr.dev, crq->adr.channel, crq->adr.sapi,
+			crq->adr.tei);
+	if (crq->adr.sapi != 0) /* not supported yet */
+		return -EINVAL;
+	if (crq->adr.tei > GROUP_TEI)
+		return -EINVAL;
+	if (crq->adr.tei < 64)
+		test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+	if (crq->adr.tei == 0)
+		test_and_set_bit(OPTION_L2_PTP, &opt);
+	if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+		if (crq->protocol == ISDN_P_LAPD_TE)
+			return -EPROTONOSUPPORT;
+		if ((crq->adr.tei != 0) && (crq->adr.tei != 127))
+			return -EINVAL;
+		if (mgr->up) {
+			printk(KERN_WARNING
+			    "%s: only one network manager is allowed\n",
+			    __func__);
+			return -EBUSY;
+		}
+	} else if (test_bit(MGR_OPT_USER, &mgr->options)) {
+		if (crq->protocol == ISDN_P_LAPD_NT)
+			return -EPROTONOSUPPORT;
+		if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI))
+			return -EINVAL; /* dyn tei */
+	} else {
+		if (crq->protocol == ISDN_P_LAPD_NT)
+			test_and_set_bit(MGR_OPT_NETWORK, &mgr->options);
+		if (crq->protocol == ISDN_P_LAPD_TE)
+			test_and_set_bit(MGR_OPT_USER, &mgr->options);
+	}
+	if (mgr->ch.st->dev->Dprotocols
+	  & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+		test_and_set_bit(OPTION_L2_PMX, &opt);
+	if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) {
+		mgr->up = crq->ch;
+		id = DL_INFO_L2_CONNECT;
+		teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id);
+		crq->ch = NULL;
+		if (!list_empty(&mgr->layer2)) {
+			read_lock_irqsave(&mgr->lock, flags);
+			list_for_each_entry(l2, &mgr->layer2, list) {
+				l2->up = mgr->up;
+				l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+			}
+			read_unlock_irqrestore(&mgr->lock, flags);
+		}
+		return 0;
+	}
+	l2 = create_l2(crq->ch, crq->protocol, (u_int)opt,
+		(u_long)crq->adr.tei);
+	if (!l2)
+		return -ENOMEM;
+	l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+	if (!l2->tm) {
+		kfree(l2);
+		printk(KERN_ERR "kmalloc teimgr failed\n");
+		return -ENOMEM;
+	}
+	l2->tm->mgr = mgr;
+	l2->tm->l2 = l2;
+	l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+	l2->tm->tei_m.userdata = l2->tm;
+	l2->tm->tei_m.printdebug = tei_debug;
+	if (crq->protocol == ISDN_P_LAPD_TE) {
+		l2->tm->tei_m.fsm = &teifsmu;
+		l2->tm->tei_m.state = ST_TEI_NOP;
+		l2->tm->tval = 1000; /* T201  1 sec */
+	} else {
+		l2->tm->tei_m.fsm = &teifsmn;
+		l2->tm->tei_m.state = ST_TEI_NOP;
+		l2->tm->tval = 2000; /* T202  2 sec */
+	}
+	mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+	write_lock_irqsave(&mgr->lock, flags);
+	id = get_free_id(mgr);
+	list_add_tail(&l2->list, &mgr->layer2);
+	write_unlock_irqrestore(&mgr->lock, flags);
+	if (id < 0) {
+		l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+	} else {
+		l2->ch.nr = id;
+		l2->up->nr = id;
+		crq->ch = &l2->ch;
+		id = 0;
+	}
+	return id;
+}
+
+static int
+mgr_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct manager	*mgr;
+	struct mISDNhead	*hh =  mISDN_HEAD_P(skb);
+	int			ret = -EINVAL;
+
+	mgr = container_of(ch, struct manager, ch);
+	if (*debug & DEBUG_L2_RECV)
+		printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+		    __func__, hh->prim, hh->id);
+	switch (hh->prim) {
+	case PH_DATA_IND:
+		mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+		ret = ph_data_ind(mgr, skb);
+		break;
+	case PH_DATA_CNF:
+		do_ack(mgr, hh->id);
+		ret = 0;
+		break;
+	case PH_ACTIVATE_IND:
+		test_and_set_bit(MGR_PH_ACTIVE, &mgr->options);
+		mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL);
+		do_send(mgr);
+		ret = 0;
+		break;
+	case PH_DEACTIVATE_IND:
+		test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options);
+		mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL);
+		ret = 0;
+		break;
+	case DL_UNITDATA_REQ:
+		return dl_unit_data(mgr, skb);
+	}
+	if (!ret)
+		dev_kfree_skb(skb);
+	return ret;
+}
+
+static int
+free_teimanager(struct manager *mgr)
+{
+	struct layer2	*l2, *nl2;
+
+	if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+		/* not locked lock is taken in release tei */
+		mgr->up = NULL;
+		if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) {
+			list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+				put_tei_msg(mgr, ID_REMOVE, 0, l2->tei);
+				mutex_lock(&mgr->ch.st->lmutex);
+				list_del(&l2->ch.list);
+				mutex_unlock(&mgr->ch.st->lmutex);
+				l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+			}
+			test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options);
+		} else {
+			list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+				l2->up = NULL;
+			}
+		}
+	}
+	if (test_bit(MGR_OPT_USER, &mgr->options)) {
+		if (list_empty(&mgr->layer2))
+			test_and_clear_bit(MGR_OPT_USER, &mgr->options);
+	}
+	mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL);
+	return 0;
+}
+
+static int
+ctrl_teimanager(struct manager *mgr, void *arg)
+{
+	/* currently we only have one option */
+	int	clean = *((int *)arg);
+
+	if (clean)
+		test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options);
+	else
+		test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options);
+	return 0;
+}
+
+/* This function does create a L2 for fixed TEI in NT Mode */
+static int
+check_data(struct manager *mgr, struct sk_buff *skb)
+{
+	struct mISDNhead	*hh =  mISDN_HEAD_P(skb);
+	int			ret, tei;
+	struct layer2		*l2;
+
+	if (*debug & DEBUG_L2_CTRL)
+		printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+		    __func__, hh->prim, hh->id);
+	if (test_bit(MGR_OPT_USER, &mgr->options))
+		return -ENOTCONN;
+	if (hh->prim != PH_DATA_IND)
+		return -ENOTCONN;
+	if (skb->len != 3)
+		return -ENOTCONN;
+	if (skb->data[0] != 0)
+		/* only SAPI 0 command */
+		return -ENOTCONN;
+	if (!(skb->data[1] & 1)) /* invalid EA1 */
+		return -EINVAL;
+	tei = skb->data[1] >> 0;
+	if (tei > 63) /* not a fixed tei */
+		return -ENOTCONN;
+	if ((skb->data[2] & ~0x10) != SABME)
+		return -ENOTCONN;
+	/* We got a SABME for a fixed TEI */
+	l2 = create_new_tei(mgr, tei);
+	if (!l2)
+		return -ENOMEM;
+	ret = l2->ch.send(&l2->ch, skb);
+	return ret;
+}
+
+void
+delete_teimanager(struct mISDNchannel *ch)
+{
+	struct manager	*mgr;
+	struct layer2	*l2, *nl2;
+
+	mgr = container_of(ch, struct manager, ch);
+	/* not locked lock is taken in release tei */
+	list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+		mutex_lock(&mgr->ch.st->lmutex);
+		list_del(&l2->ch.list);
+		mutex_unlock(&mgr->ch.st->lmutex);
+		l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+	}
+	list_del(&mgr->ch.list);
+	list_del(&mgr->bcast.list);
+	skb_queue_purge(&mgr->sendq);
+	kfree(mgr);
+}
+
+static int
+mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+	struct manager	*mgr;
+	int		ret = -EINVAL;
+
+	mgr = container_of(ch, struct manager, ch);
+	if (*debug & DEBUG_L2_CTRL)
+		printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg);
+	switch (cmd) {
+	case OPEN_CHANNEL:
+		ret = create_teimgr(mgr, arg);
+		break;
+	case CLOSE_CHANNEL:
+		ret = free_teimanager(mgr);
+		break;
+	case CONTROL_CHANNEL:
+		ret = ctrl_teimanager(mgr, arg);
+		break;
+	case CHECK_DATA:
+		ret = check_data(mgr, arg);
+		break;
+	}
+	return ret;
+}
+
+static int
+mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+	struct manager		*mgr = container_of(ch, struct manager, bcast);
+	struct mISDNhead	*hh = mISDN_HEAD_P(skb);
+	struct sk_buff		*cskb = NULL;
+	struct layer2		*l2;
+	u_long			flags;
+	int			ret;
+
+	read_lock_irqsave(&mgr->lock, flags);
+	list_for_each_entry(l2, &mgr->layer2, list) {
+		if ((hh->id & MISDN_ID_SAPI_MASK) ==
+		    (l2->ch.addr & MISDN_ID_SAPI_MASK)) {
+			if (list_is_last(&l2->list, &mgr->layer2)) {
+				cskb = skb;
+				skb = NULL;
+			} else {
+				if (!cskb)
+					cskb = skb_copy(skb, GFP_KERNEL);
+			}
+			if (cskb) {
+				ret = l2->ch.send(&l2->ch, cskb);
+				if (ret) {
+					if (*debug & DEBUG_SEND_ERR)
+						printk(KERN_DEBUG
+						    "%s ch%d prim(%x) addr(%x)"
+						    " err %d\n",
+						    __func__, l2->ch.nr,
+						    hh->prim, l2->ch.addr, ret);
+				} else
+					cskb = NULL;
+			} else {
+				printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+				    __func__, ch->nr, ch->addr);
+				goto out;
+			}
+		}
+	}
+out:
+	read_unlock_irqrestore(&mgr->lock, flags);
+	if (cskb)
+		dev_kfree_skb(cskb);
+	if (skb)
+		dev_kfree_skb(skb);
+	return 0;
+}
+
+static int
+mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+
+	return -EINVAL;
+}
+
+int
+create_teimanager(struct mISDNdevice *dev)
+{
+	struct manager *mgr;
+
+	mgr = kzalloc(sizeof(struct manager), GFP_KERNEL);
+	if (!mgr)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&mgr->layer2);
+	mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock);
+	skb_queue_head_init(&mgr->sendq);
+	mgr->nextid = 1;
+	mgr->lastid = MISDN_ID_NONE;
+	mgr->ch.send = mgr_send;
+	mgr->ch.ctrl = mgr_ctrl;
+	mgr->ch.st = dev->D.st;
+	set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI);
+	add_layer2(&mgr->ch, dev->D.st);
+	mgr->bcast.send = mgr_bcast;
+	mgr->bcast.ctrl = mgr_bcast_ctrl;
+	mgr->bcast.st = dev->D.st;
+	set_channel_address(&mgr->bcast, 0, GROUP_TEI);
+	add_layer2(&mgr->bcast, dev->D.st);
+	mgr->deact.debug = *debug & DEBUG_MANAGER;
+	mgr->deact.userdata = mgr;
+	mgr->deact.printdebug = da_debug;
+	mgr->deact.fsm = &deactfsm;
+	mgr->deact.state = ST_L1_DEACT;
+	mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer);
+	dev->teimgr = &mgr->ch;
+	return 0;
+}
+
+int TEIInit(u_int *deb)
+{
+	debug = deb;
+	teifsmu.state_count = TEI_STATE_COUNT;
+	teifsmu.event_count = TEI_EVENT_COUNT;
+	teifsmu.strEvent = strTeiEvent;
+	teifsmu.strState = strTeiState;
+	mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser));
+	teifsmn.state_count = TEI_STATE_COUNT;
+	teifsmn.event_count = TEI_EVENT_COUNT;
+	teifsmn.strEvent = strTeiEvent;
+	teifsmn.strState = strTeiState;
+	mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet));
+	deactfsm.state_count =  DEACT_STATE_COUNT;
+	deactfsm.event_count = DEACT_EVENT_COUNT;
+	deactfsm.strEvent = strDeactEvent;
+	deactfsm.strState = strDeactState;
+	mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList));
+	return 0;
+}
+
+void TEIFree(void)
+{
+	mISDN_FsmFree(&teifsmu);
+	mISDN_FsmFree(&teifsmn);
+	mISDN_FsmFree(&deactfsm);
+}
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
new file mode 100644
index 0000000..b5fabc7
--- /dev/null
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -0,0 +1,301 @@
+/*
+ *
+ * general timer device for using in ISDN stacks
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mISDNif.h>
+
+static int	*debug;
+
+
+struct mISDNtimerdev {
+	int			next_id;
+	struct list_head	pending;
+	struct list_head	expired;
+	wait_queue_head_t	wait;
+	u_int			work;
+	spinlock_t		lock; /* protect lists */
+};
+
+struct mISDNtimer {
+	struct list_head	list;
+	struct  mISDNtimerdev	*dev;
+	struct timer_list	tl;
+	int			id;
+};
+
+static int
+mISDN_open(struct inode *ino, struct file *filep)
+{
+	struct mISDNtimerdev	*dev;
+
+	if (*debug & DEBUG_TIMER)
+		printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+	dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	dev->next_id = 1;
+	INIT_LIST_HEAD(&dev->pending);
+	INIT_LIST_HEAD(&dev->expired);
+	spin_lock_init(&dev->lock);
+	dev->work = 0;
+	init_waitqueue_head(&dev->wait);
+	filep->private_data = dev;
+	__module_get(THIS_MODULE);
+	return 0;
+}
+
+static int
+mISDN_close(struct inode *ino, struct file *filep)
+{
+	struct mISDNtimerdev	*dev = filep->private_data;
+	struct mISDNtimer	*timer, *next;
+
+	if (*debug & DEBUG_TIMER)
+		printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+	list_for_each_entry_safe(timer, next, &dev->pending, list) {
+		del_timer(&timer->tl);
+		kfree(timer);
+	}
+	list_for_each_entry_safe(timer, next, &dev->expired, list) {
+		kfree(timer);
+	}
+	kfree(dev);
+	module_put(THIS_MODULE);
+	return 0;
+}
+
+static ssize_t
+mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
+{
+	struct mISDNtimerdev	*dev = filep->private_data;
+	struct mISDNtimer	*timer;
+	u_long	flags;
+	int	ret = 0;
+
+	if (*debug & DEBUG_TIMER)
+		printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
+			filep, buf, (int)count, off);
+	if (*off != filep->f_pos)
+		return -ESPIPE;
+
+	if (list_empty(&dev->expired) && (dev->work == 0)) {
+		if (filep->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		wait_event_interruptible(dev->wait, (dev->work ||
+		    !list_empty(&dev->expired)));
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+	if (count < sizeof(int))
+		return -ENOSPC;
+	if (dev->work)
+		dev->work = 0;
+	if (!list_empty(&dev->expired)) {
+		spin_lock_irqsave(&dev->lock, flags);
+		timer = (struct mISDNtimer *)dev->expired.next;
+		list_del(&timer->list);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		if (put_user(timer->id, (int *)buf))
+			ret = -EFAULT;
+		else
+			ret = sizeof(int);
+		kfree(timer);
+	}
+	return ret;
+}
+
+static loff_t
+mISDN_llseek(struct file *filep, loff_t offset, int orig)
+{
+	return -ESPIPE;
+}
+
+static ssize_t
+mISDN_write(struct file *filep, const char *buf, size_t count, loff_t *off)
+{
+	return -EOPNOTSUPP;
+}
+
+static unsigned int
+mISDN_poll(struct file *filep, poll_table *wait)
+{
+	struct mISDNtimerdev	*dev = filep->private_data;
+	unsigned int		mask = POLLERR;
+
+	if (*debug & DEBUG_TIMER)
+		printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
+	if (dev) {
+		poll_wait(filep, &dev->wait, wait);
+		mask = 0;
+		if (dev->work || !list_empty(&dev->expired))
+			mask |= (POLLIN | POLLRDNORM);
+		if (*debug & DEBUG_TIMER)
+			printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
+				dev->work, list_empty(&dev->expired));
+	}
+	return mask;
+}
+
+static void
+dev_expire_timer(struct mISDNtimer *timer)
+{
+	u_long			flags;
+
+	spin_lock_irqsave(&timer->dev->lock, flags);
+	list_del(&timer->list);
+	list_add_tail(&timer->list, &timer->dev->expired);
+	spin_unlock_irqrestore(&timer->dev->lock, flags);
+	wake_up_interruptible(&timer->dev->wait);
+}
+
+static int
+misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
+{
+	int 			id;
+	u_long			flags;
+	struct mISDNtimer	*timer;
+
+	if (!timeout) {
+		dev->work = 1;
+		wake_up_interruptible(&dev->wait);
+		id = 0;
+	} else {
+		timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
+		if (!timer)
+			return -ENOMEM;
+		spin_lock_irqsave(&dev->lock, flags);
+		timer->id = dev->next_id++;
+		if (dev->next_id < 0)
+			dev->next_id = 1;
+		list_add_tail(&timer->list, &dev->pending);
+		spin_unlock_irqrestore(&dev->lock, flags);
+		timer->dev = dev;
+		timer->tl.data = (long)timer;
+		timer->tl.function = (void *) dev_expire_timer;
+		init_timer(&timer->tl);
+		timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
+		add_timer(&timer->tl);
+		id = timer->id;
+	}
+	return id;
+}
+
+static int
+misdn_del_timer(struct mISDNtimerdev *dev, int id)
+{
+	u_long			flags;
+	struct mISDNtimer	*timer;
+	int			ret = 0;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	list_for_each_entry(timer, &dev->pending, list) {
+		if (timer->id == id) {
+			list_del_init(&timer->list);
+			del_timer(&timer->tl);
+			ret = timer->id;
+			kfree(timer);
+			goto unlock;
+		}
+	}
+unlock:
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return ret;
+}
+
+static int
+mISDN_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+    unsigned long arg)
+{
+	struct mISDNtimerdev	*dev = filep->private_data;
+	int			id, tout, ret = 0;
+
+
+	if (*debug & DEBUG_TIMER)
+		printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
+		    filep, cmd, arg);
+	switch (cmd) {
+	case IMADDTIMER:
+		if (get_user(tout, (int __user *)arg)) {
+			ret = -EFAULT;
+			break;
+		}
+		id = misdn_add_timer(dev, tout);
+		if (*debug & DEBUG_TIMER)
+			printk(KERN_DEBUG "%s add %d id %d\n", __func__,
+			    tout, id);
+		if (id < 0) {
+			ret = id;
+			break;
+		}
+		if (put_user(id, (int __user *)arg))
+			ret = -EFAULT;
+		break;
+	case IMDELTIMER:
+		if (get_user(id, (int __user *)arg)) {
+			ret = -EFAULT;
+			break;
+		}
+		if (*debug & DEBUG_TIMER)
+			printk(KERN_DEBUG "%s del id %d\n", __func__, id);
+		id = misdn_del_timer(dev, id);
+		if (put_user(id, (int __user *)arg))
+			ret = -EFAULT;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static struct file_operations mISDN_fops = {
+	.llseek		= mISDN_llseek,
+	.read		= mISDN_read,
+	.write		= mISDN_write,
+	.poll		= mISDN_poll,
+	.ioctl		= mISDN_ioctl,
+	.open		= mISDN_open,
+	.release	= mISDN_close,
+};
+
+static struct miscdevice mISDNtimer = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "mISDNtimer",
+	.fops	= &mISDN_fops,
+};
+
+int
+mISDN_inittimer(int *deb)
+{
+	int	err;
+
+	debug = deb;
+	err = misc_register(&mISDNtimer);
+	if (err)
+		printk(KERN_WARNING "mISDN: Could not register timer device\n");
+	return err;
+}
+
+void mISDN_timer_cleanup(void)
+{
+	misc_deregister(&mISDNtimer);
+}
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 19a1a25..889e5f8 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -12,3 +12,4 @@
 				   sdio.o sdio_ops.o sdio_bus.o \
 				   sdio_cis.o sdio_io.o sdio_irq.o
 
+mmc_core-$(CONFIG_DEBUG_FS)	+= debugfs.o
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index fd95b18..0d9b2d6 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -252,6 +252,10 @@
 	if (ret)
 		return ret;
 
+#ifdef CONFIG_DEBUG_FS
+	mmc_add_card_debugfs(card);
+#endif
+
 	mmc_card_set_present(card);
 
 	return 0;
@@ -263,6 +267,10 @@
  */
 void mmc_remove_card(struct mmc_card *card)
 {
+#ifdef CONFIG_DEBUG_FS
+	mmc_remove_card_debugfs(card);
+#endif
+
 	if (mmc_card_present(card)) {
 		if (mmc_host_is_spi(card->host)) {
 			printk(KERN_INFO "%s: SPI card removed\n",
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index cdb332b..c819eff 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -52,5 +52,12 @@
 
 extern int use_spi_crc;
 
+/* Debugfs information for hosts and cards */
+void mmc_add_host_debugfs(struct mmc_host *host);
+void mmc_remove_host_debugfs(struct mmc_host *host);
+
+void mmc_add_card_debugfs(struct mmc_card *card);
+void mmc_remove_card_debugfs(struct mmc_card *card);
+
 #endif
 
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
new file mode 100644
index 0000000..1237bb4
--- /dev/null
+++ b/drivers/mmc/core/debugfs.c
@@ -0,0 +1,225 @@
+/*
+ * Debugfs support for hosts and cards
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include "core.h"
+#include "mmc_ops.h"
+
+/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
+static int mmc_ios_show(struct seq_file *s, void *data)
+{
+	static const char *vdd_str[] = {
+		[8]	= "2.0",
+		[9]	= "2.1",
+		[10]	= "2.2",
+		[11]	= "2.3",
+		[12]	= "2.4",
+		[13]	= "2.5",
+		[14]	= "2.6",
+		[15]	= "2.7",
+		[16]	= "2.8",
+		[17]	= "2.9",
+		[18]	= "3.0",
+		[19]	= "3.1",
+		[20]	= "3.2",
+		[21]	= "3.3",
+		[22]	= "3.4",
+		[23]	= "3.5",
+		[24]	= "3.6",
+	};
+	struct mmc_host	*host = s->private;
+	struct mmc_ios	*ios = &host->ios;
+	const char *str;
+
+	seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
+	seq_printf(s, "vdd:\t\t%u ", ios->vdd);
+	if ((1 << ios->vdd) & MMC_VDD_165_195)
+		seq_printf(s, "(1.65 - 1.95 V)\n");
+	else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1)
+			&& vdd_str[ios->vdd] && vdd_str[ios->vdd + 1])
+		seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd],
+				vdd_str[ios->vdd + 1]);
+	else
+		seq_printf(s, "(invalid)\n");
+
+	switch (ios->bus_mode) {
+	case MMC_BUSMODE_OPENDRAIN:
+		str = "open drain";
+		break;
+	case MMC_BUSMODE_PUSHPULL:
+		str = "push-pull";
+		break;
+	default:
+		str = "invalid";
+		break;
+	}
+	seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str);
+
+	switch (ios->chip_select) {
+	case MMC_CS_DONTCARE:
+		str = "don't care";
+		break;
+	case MMC_CS_HIGH:
+		str = "active high";
+		break;
+	case MMC_CS_LOW:
+		str = "active low";
+		break;
+	default:
+		str = "invalid";
+		break;
+	}
+	seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str);
+
+	switch (ios->power_mode) {
+	case MMC_POWER_OFF:
+		str = "off";
+		break;
+	case MMC_POWER_UP:
+		str = "up";
+		break;
+	case MMC_POWER_ON:
+		str = "on";
+		break;
+	default:
+		str = "invalid";
+		break;
+	}
+	seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str);
+	seq_printf(s, "bus width:\t%u (%u bits)\n",
+			ios->bus_width, 1 << ios->bus_width);
+
+	switch (ios->timing) {
+	case MMC_TIMING_LEGACY:
+		str = "legacy";
+		break;
+	case MMC_TIMING_MMC_HS:
+		str = "mmc high-speed";
+		break;
+	case MMC_TIMING_SD_HS:
+		str = "sd high-speed";
+		break;
+	default:
+		str = "invalid";
+		break;
+	}
+	seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
+
+	return 0;
+}
+
+static int mmc_ios_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, mmc_ios_show, inode->i_private);
+}
+
+static const struct file_operations mmc_ios_fops = {
+	.open		= mmc_ios_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+void mmc_add_host_debugfs(struct mmc_host *host)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(mmc_hostname(host), NULL);
+	if (IS_ERR(root))
+		/* Don't complain -- debugfs just isn't enabled */
+		return;
+	if (!root)
+		/* Complain -- debugfs is enabled, but it failed to
+		 * create the directory. */
+		goto err_root;
+
+	host->debugfs_root = root;
+
+	if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
+		goto err_ios;
+
+	return;
+
+err_ios:
+	debugfs_remove_recursive(root);
+	host->debugfs_root = NULL;
+err_root:
+	dev_err(&host->class_dev, "failed to initialize debugfs\n");
+}
+
+void mmc_remove_host_debugfs(struct mmc_host *host)
+{
+	debugfs_remove_recursive(host->debugfs_root);
+}
+
+static int mmc_dbg_card_status_get(void *data, u64 *val)
+{
+	struct mmc_card	*card = data;
+	u32		status;
+	int		ret;
+
+	mmc_claim_host(card->host);
+
+	ret = mmc_send_status(data, &status);
+	if (!ret)
+		*val = status;
+
+	mmc_release_host(card->host);
+
+	return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
+		NULL, "%08llx\n");
+
+void mmc_add_card_debugfs(struct mmc_card *card)
+{
+	struct mmc_host	*host = card->host;
+	struct dentry	*root;
+
+	if (!host->debugfs_root)
+		return;
+
+	root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
+	if (IS_ERR(root))
+		/* Don't complain -- debugfs just isn't enabled */
+		return;
+	if (!root)
+		/* Complain -- debugfs is enabled, but it failed to
+		 * create the directory. */
+		goto err;
+
+	card->debugfs_root = root;
+
+	if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
+		goto err;
+
+	if (mmc_card_mmc(card) || mmc_card_sd(card))
+		if (!debugfs_create_file("status", S_IRUSR, root, card,
+					&mmc_dbg_card_status_fops))
+			goto err;
+
+	return;
+
+err:
+	debugfs_remove_recursive(root);
+	card->debugfs_root = NULL;
+	dev_err(&card->dev, "failed to initialize debugfs\n");
+}
+
+void mmc_remove_card_debugfs(struct mmc_card *card)
+{
+	debugfs_remove_recursive(card->debugfs_root);
+}
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 1d795c5..6da80fd 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -127,6 +127,10 @@
 	if (err)
 		return err;
 
+#ifdef CONFIG_DEBUG_FS
+	mmc_add_host_debugfs(host);
+#endif
+
 	mmc_start_host(host);
 
 	return 0;
@@ -146,6 +150,10 @@
 {
 	mmc_stop_host(host);
 
+#ifdef CONFIG_DEBUG_FS
+	mmc_remove_host_debugfs(host);
+#endif
+
 	device_del(&host->class_dev);
 
 	led_trigger_unregister_simple(host->led);
diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h
index a9a5657..26bd80e 100644
--- a/drivers/mmc/host/atmel-mci-regs.h
+++ b/drivers/mmc/host/atmel-mci-regs.h
@@ -82,6 +82,8 @@
 # define MCI_OVRE		(  1 <<  30)	/* RX Overrun Error */
 # define MCI_UNRE		(  1 <<  31)	/* TX Underrun Error */
 
+#define MCI_REGS_SIZE		0x100
+
 /* Register access macros */
 #define mci_readl(port,reg)				\
 	__raw_readl((port)->regs + MCI_##reg)
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index cce873c..b68381f 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -9,6 +9,7 @@
  */
 #include <linux/blkdev.h>
 #include <linux/clk.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -16,6 +17,8 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
 
 #include <linux/mmc/host.h>
 
@@ -88,6 +91,188 @@
 #define atmci_clear_pending(host, event)			\
 	clear_bit(event, &host->pending_events)
 
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int atmci_req_show(struct seq_file *s, void *v)
+{
+	struct atmel_mci	*host = s->private;
+	struct mmc_request	*mrq = host->mrq;
+	struct mmc_command	*cmd;
+	struct mmc_command	*stop;
+	struct mmc_data		*data;
+
+	/* Make sure we get a consistent snapshot */
+	spin_lock_irq(&host->mmc->lock);
+
+	if (mrq) {
+		cmd = mrq->cmd;
+		data = mrq->data;
+		stop = mrq->stop;
+
+		if (cmd)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				cmd->opcode, cmd->arg, cmd->flags,
+				cmd->resp[0], cmd->resp[1], cmd->resp[2],
+				cmd->resp[2], cmd->error);
+		if (data)
+			seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+				data->bytes_xfered, data->blocks,
+				data->blksz, data->flags, data->error);
+		if (stop)
+			seq_printf(s,
+				"CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+				stop->opcode, stop->arg, stop->flags,
+				stop->resp[0], stop->resp[1], stop->resp[2],
+				stop->resp[2], stop->error);
+	}
+
+	spin_unlock_irq(&host->mmc->lock);
+
+	return 0;
+}
+
+static int atmci_req_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, atmci_req_show, inode->i_private);
+}
+
+static const struct file_operations atmci_req_fops = {
+	.owner		= THIS_MODULE,
+	.open		= atmci_req_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void atmci_show_status_reg(struct seq_file *s,
+		const char *regname, u32 value)
+{
+	static const char	*sr_bit[] = {
+		[0]	= "CMDRDY",
+		[1]	= "RXRDY",
+		[2]	= "TXRDY",
+		[3]	= "BLKE",
+		[4]	= "DTIP",
+		[5]	= "NOTBUSY",
+		[8]	= "SDIOIRQA",
+		[9]	= "SDIOIRQB",
+		[16]	= "RINDE",
+		[17]	= "RDIRE",
+		[18]	= "RCRCE",
+		[19]	= "RENDE",
+		[20]	= "RTOE",
+		[21]	= "DCRCE",
+		[22]	= "DTOE",
+		[30]	= "OVRE",
+		[31]	= "UNRE",
+	};
+	unsigned int		i;
+
+	seq_printf(s, "%s:\t0x%08x", regname, value);
+	for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
+		if (value & (1 << i)) {
+			if (sr_bit[i])
+				seq_printf(s, " %s", sr_bit[i]);
+			else
+				seq_puts(s, " UNKNOWN");
+		}
+	}
+	seq_putc(s, '\n');
+}
+
+static int atmci_regs_show(struct seq_file *s, void *v)
+{
+	struct atmel_mci	*host = s->private;
+	u32			*buf;
+
+	buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* Grab a more or less consistent snapshot */
+	spin_lock_irq(&host->mmc->lock);
+	memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
+	spin_unlock_irq(&host->mmc->lock);
+
+	seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
+			buf[MCI_MR / 4],
+			buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
+			buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
+			buf[MCI_MR / 4] & 0xff);
+	seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
+	seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
+	seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
+	seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
+			buf[MCI_BLKR / 4],
+			buf[MCI_BLKR / 4] & 0xffff,
+			(buf[MCI_BLKR / 4] >> 16) & 0xffff);
+
+	/* Don't read RSPR and RDR; it will consume the data there */
+
+	atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
+	atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
+
+	return 0;
+}
+
+static int atmci_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, atmci_regs_show, inode->i_private);
+}
+
+static const struct file_operations atmci_regs_fops = {
+	.owner		= THIS_MODULE,
+	.open		= atmci_regs_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static void atmci_init_debugfs(struct atmel_mci *host)
+{
+	struct mmc_host	*mmc;
+	struct dentry	*root;
+	struct dentry	*node;
+	struct resource	*res;
+
+	mmc = host->mmc;
+	root = mmc->debugfs_root;
+	if (!root)
+		return;
+
+	node = debugfs_create_file("regs", S_IRUSR, root, host,
+			&atmci_regs_fops);
+	if (IS_ERR(node))
+		return;
+	if (!node)
+		goto err;
+
+	res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
+	node->d_inode->i_size = res->end - res->start + 1;
+
+	node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("pending_events", S_IRUSR, root,
+				     (u32 *)&host->pending_events);
+	if (!node)
+		goto err;
+
+	node = debugfs_create_x32("completed_events", S_IRUSR, root,
+				     (u32 *)&host->completed_events);
+	if (!node)
+		goto err;
+
+	return;
+
+err:
+	dev_err(&host->pdev->dev,
+		"failed to initialize debugfs for controller\n");
+}
 
 static void atmci_enable(struct atmel_mci *host)
 {
@@ -905,6 +1090,8 @@
 			"Atmel MCI controller at 0x%08lx irq %d\n",
 			host->mapbase, irq);
 
+	atmci_init_debugfs(host);
+
 	return 0;
 
 err_request_irq:
@@ -923,6 +1110,8 @@
 	platform_set_drvdata(pdev, NULL);
 
 	if (host) {
+		/* Debugfs stuff is cleaned up by mmc core */
+
 		if (host->detect_pin >= 0) {
 			int pin = host->detect_pin;
 
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
index 5e880c0..f61406d 100644
--- a/drivers/mmc/host/imxmmc.c
+++ b/drivers/mmc/host/imxmmc.c
@@ -26,12 +26,6 @@
  *
  */
 
-#ifdef CONFIG_MMC_DEBUG
-#define DEBUG
-#else
-#undef  DEBUG
-#endif
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
@@ -907,31 +901,12 @@
 	.get_ro		= imxmci_get_ro,
 };
 
-static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
-{
-	int i;
-
-	for (i = 0; i < dev->num_resources; i++)
-		if (dev->resource[i].flags == mask && nr-- == 0)
-			return &dev->resource[i];
-	return NULL;
-}
-
-static int platform_device_irq(struct platform_device *dev, int nr)
-{
-	int i;
-
-	for (i = 0; i < dev->num_resources; i++)
-		if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0)
-			return dev->resource[i].start;
-	return NO_IRQ;
-}
-
 static void imxmci_check_status(unsigned long data)
 {
 	struct imxmci_host *host = (struct imxmci_host *)data;
 
-	if( host->pdata->card_present(mmc_dev(host->mmc)) != host->present ) {
+	if (host->pdata && host->pdata->card_present &&
+	    host->pdata->card_present(mmc_dev(host->mmc)) != host->present) {
 		host->present ^= 1;
 		dev_info(mmc_dev(host->mmc), "card %s\n",
 		      host->present ? "inserted" : "removed");
@@ -962,13 +937,12 @@
 
 	printk(KERN_INFO "i.MX mmc driver\n");
 
-	r = platform_device_resource(pdev, IORESOURCE_MEM, 0);
-	irq = platform_device_irq(pdev, 0);
-	if (!r || irq == NO_IRQ)
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!r || irq < 0)
 		return -ENXIO;
 
-	r = request_mem_region(r->start, 0x100, "IMXMCI");
-	if (!r)
+	if (!request_mem_region(r->start, 0x100, pdev->name))
 		return -EBUSY;
 
 	mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
@@ -995,6 +969,8 @@
 	host->mmc = mmc;
 	host->dma_allocated = 0;
 	host->pdata = pdev->dev.platform_data;
+	if (!host->pdata)
+		dev_warn(&pdev->dev, "No platform data provided!\n");
 
 	spin_lock_init(&host->lock);
 	host->res = r;
@@ -1047,7 +1023,11 @@
 	if (ret)
 		goto out;
 
-	host->present = host->pdata->card_present(mmc_dev(mmc));
+	if (host->pdata && host->pdata->card_present)
+		host->present = host->pdata->card_present(mmc_dev(mmc));
+	else	/* if there is no way to detect assume that card is present */
+		host->present = 1;
+
 	init_timer(&host->timer);
 	host->timer.data = (unsigned long)host;
 	host->timer.function = imxmci_check_status;
@@ -1073,7 +1053,7 @@
 	}
 	if (mmc)
 		mmc_free_host(mmc);
-	release_resource(r);
+	release_mem_region(r->start, 0x100);
 	return ret;
 }
 
@@ -1102,7 +1082,7 @@
 		clk_disable(host->clk);
 		clk_put(host->clk);
 
-		release_resource(host->res);
+		release_mem_region(host->res->start, 0x100);
 
 		mmc_free_host(mmc);
 	}
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 41cc633..7503b81 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1076,6 +1076,7 @@
 		 */
 		if (canpower && ios->power_mode == MMC_POWER_OFF) {
 			int mres;
+			u8 nullbyte = 0;
 
 			host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
 			mres = spi_setup(host->spi);
@@ -1083,7 +1084,7 @@
 				dev_dbg(&host->spi->dev,
 					"switch to SPI mode 0 failed\n");
 
-			if (spi_w8r8(host->spi, 0x00) < 0)
+			if (spi_write(host->spi, &nullbyte, 1) < 0)
 				dev_dbg(&host->spi->dev,
 					"put spi signals to low failed\n");
 
diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c
index c7cc760..af251a5 100644
--- a/drivers/net/bnx2x_main.c
+++ b/drivers/net/bnx2x_main.c
@@ -814,7 +814,7 @@
 	}
 
 	/* release skb */
-	BUG_TRAP(skb);
+	WARN_ON(!skb);
 	dev_kfree_skb(skb);
 	tx_buf->first_bd = 0;
 	tx_buf->skb = NULL;
@@ -837,9 +837,9 @@
 	used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS;
 
 #ifdef BNX2X_STOP_ON_ERROR
-	BUG_TRAP(used >= 0);
-	BUG_TRAP(used <= fp->bp->tx_ring_size);
-	BUG_TRAP((fp->bp->tx_ring_size - used) <= MAX_TX_AVAIL);
+	WARN_ON(used < 0);
+	WARN_ON(used > fp->bp->tx_ring_size);
+	WARN_ON((fp->bp->tx_ring_size - used) > MAX_TX_AVAIL);
 #endif
 
 	return (s16)(fp->bp->tx_ring_size) - used;
@@ -4374,7 +4374,7 @@
 			}
 			ring_prod = NEXT_RX_IDX(ring_prod);
 			cqe_ring_prod = NEXT_RCQ_IDX(cqe_ring_prod);
-			BUG_TRAP(ring_prod > i);
+			WARN_ON(ring_prod <= i);
 		}
 
 		fp->rx_bd_prod = ring_prod;
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 739b3ab..ddccc07 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -581,12 +581,12 @@
 			if (file == ppp->owner)
 				ppp_shutdown_interface(ppp);
 		}
-		if (atomic_read(&file->f_count) <= 2) {
+		if (atomic_long_read(&file->f_count) <= 2) {
 			ppp_release(NULL, file);
 			err = 0;
 		} else
-			printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n",
-			       atomic_read(&file->f_count));
+			printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n",
+			       atomic_long_read(&file->f_count));
 		unlock_kernel();
 		return err;
 	}
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index c3ad89e..cebb25e 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -3321,7 +3321,7 @@
 	struct qeth_card *card;
 	char dbf_text[15];
 
-	card = netdev_priv(dev);
+	card = dev->ml_priv;
 
 	QETH_DBF_TEXT(TRACE, 4, "chgmtu");
 	sprintf(dbf_text, "%8x", new_mtu);
@@ -3343,7 +3343,7 @@
 {
 	struct qeth_card *card;
 
-	card = netdev_priv(dev);
+	card = dev->ml_priv;
 
 	QETH_DBF_TEXT(TRACE, 5, "getstat");
 
@@ -3395,7 +3395,7 @@
 {
 	struct qeth_card *card;
 
-	card = netdev_priv(dev);
+	card = dev->ml_priv;
 	card->stats.tx_errors++;
 	qeth_schedule_recovery(card);
 }
@@ -3403,7 +3403,7 @@
 
 int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	int rc = 0;
 
 	switch (regnum) {
@@ -4253,7 +4253,7 @@
 void qeth_core_get_ethtool_stats(struct net_device *dev,
 		struct ethtool_stats *stats, u64 *data)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	data[0] = card->stats.rx_packets -
 				card->perf_stats.initial_rx_packets;
 	data[1] = card->perf_stats.bufs_rec;
@@ -4313,7 +4313,7 @@
 void qeth_core_get_drvinfo(struct net_device *dev,
 		struct ethtool_drvinfo *info)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	if (card->options.layer2)
 		strcpy(info->driver, "qeth_l2");
 	else
@@ -4331,7 +4331,7 @@
 int qeth_core_ethtool_get_settings(struct net_device *netdev,
 					struct ethtool_cmd *ecmd)
 {
-	struct qeth_card *card = netdev_priv(netdev);
+	struct qeth_card *card = netdev->ml_priv;
 	enum qeth_link_types link_type;
 
 	if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 3fbc3bd..a8b069c 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -35,7 +35,7 @@
 
 static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	struct mii_ioctl_data *mii_data;
 	int rc = 0;
 
@@ -317,7 +317,7 @@
 
 static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	struct qeth_vlan_vid *id;
 
 	QETH_DBF_TEXT_(TRACE, 4, "aid:%d", vid);
@@ -334,7 +334,7 @@
 static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
 	struct qeth_vlan_vid *id, *tmpid = NULL;
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
 	spin_lock_bh(&card->vlanlock);
@@ -566,7 +566,7 @@
 static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 {
 	struct sockaddr *addr = p;
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	int rc = 0;
 
 	QETH_DBF_TEXT(TRACE, 3, "setmac");
@@ -590,7 +590,7 @@
 
 static void qeth_l2_set_multicast_list(struct net_device *dev)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	struct dev_mc_list *dm;
 
 	if (card->info.type == QETH_CARD_TYPE_OSN)
@@ -612,7 +612,7 @@
 	int rc;
 	struct qeth_hdr *hdr = NULL;
 	int elements = 0;
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	struct sk_buff *new_skb = skb;
 	int ipv = qeth_get_ip_version(skb);
 	int cast_type = qeth_get_cast_type(card, skb);
@@ -767,7 +767,7 @@
 
 static int qeth_l2_open(struct net_device *dev)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	QETH_DBF_TEXT(TRACE, 4, "qethopen");
 	if (card->state != CARD_STATE_SOFTSETUP)
@@ -791,7 +791,7 @@
 
 static int qeth_l2_stop(struct net_device *dev)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	QETH_DBF_TEXT(TRACE, 4, "qethstop");
 	netif_tx_disable(dev);
@@ -838,7 +838,7 @@
 
 static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	if (data) {
 		if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -894,7 +894,7 @@
 	if (!card->dev)
 		return -ENODEV;
 
-	card->dev->priv = card;
+	card->dev->ml_priv = card;
 	card->dev->tx_timeout = &qeth_tx_timeout;
 	card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
 	card->dev->open = qeth_l2_open;
@@ -1178,7 +1178,7 @@
 	QETH_DBF_TEXT(TRACE, 2, "osnsdmc");
 	if (!dev)
 		return -ENODEV;
-	card = netdev_priv(dev);
+	card = dev->ml_priv;
 	if (!card)
 		return -ENODEV;
 	if ((card->state != CARD_STATE_UP) &&
@@ -1201,7 +1201,7 @@
 	*dev = qeth_l2_netdev_by_devno(read_dev_no);
 	if (*dev == NULL)
 		return -ENODEV;
-	card = netdev_priv(*dev);
+	card = (*dev)->ml_priv;
 	if (!card)
 		return -ENODEV;
 	if ((assist_cb == NULL) || (data_cb == NULL))
@@ -1219,7 +1219,7 @@
 	QETH_DBF_TEXT(TRACE, 2, "osndereg");
 	if (!dev)
 		return;
-	card = netdev_priv(dev);
+	card = dev->ml_priv;
 	if (!card)
 		return;
 	card->osn_info.assist_cb = NULL;
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 38de31b..3e1d138 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1813,7 +1813,7 @@
 static void qeth_l3_vlan_rx_register(struct net_device *dev,
 			struct vlan_group *grp)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	unsigned long flags;
 
 	QETH_DBF_TEXT(TRACE, 4, "vlanreg");
@@ -1825,7 +1825,7 @@
 static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 {
 	struct net_device *vlandev;
-	struct qeth_card *card = (struct qeth_card *) dev->priv;
+	struct qeth_card *card = dev->ml_priv;
 	struct in_device *in_dev;
 
 	if (card->info.type == QETH_CARD_TYPE_IQD)
@@ -1851,7 +1851,7 @@
 
 static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	unsigned long flags;
 
 	QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
@@ -2013,7 +2013,7 @@
 		}
 	}
 
-	if (rc && !(netdev_priv(vlan_dev_real_dev(dev)) == (void *)card))
+	if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card))
 		return 0;
 
 	return rc;
@@ -2047,9 +2047,9 @@
 
 	rc = qeth_l3_verify_dev(dev);
 	if (rc == QETH_REAL_CARD)
-		card = netdev_priv(dev);
+		card = dev->ml_priv;
 	else if (rc == QETH_VLAN_CARD)
-		card = netdev_priv(vlan_dev_real_dev(dev));
+		card = vlan_dev_real_dev(dev)->ml_priv;
 	if (card && card->options.layer2)
 		card = NULL;
 	QETH_DBF_TEXT_(TRACE, 4, "%d", rc);
@@ -2110,7 +2110,7 @@
 
 static void qeth_l3_set_multicast_list(struct net_device *dev)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	QETH_DBF_TEXT(TRACE, 3, "setmulti");
 	qeth_l3_delete_mc_addresses(card);
@@ -2438,7 +2438,7 @@
 
 static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	struct qeth_arp_cache_entry arp_entry;
 	struct mii_ioctl_data *mii_data;
 	int rc = 0;
@@ -2595,7 +2595,7 @@
 	u16 *tag;
 	struct qeth_hdr *hdr = NULL;
 	int elements_needed = 0;
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	struct sk_buff *new_skb = NULL;
 	int ipv = qeth_get_ip_version(skb);
 	int cast_type = qeth_get_cast_type(card, skb);
@@ -2763,7 +2763,7 @@
 
 static int qeth_l3_open(struct net_device *dev)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	QETH_DBF_TEXT(TRACE, 4, "qethopen");
 	if (card->state != CARD_STATE_SOFTSETUP)
@@ -2780,7 +2780,7 @@
 
 static int qeth_l3_stop(struct net_device *dev)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	QETH_DBF_TEXT(TRACE, 4, "qethstop");
 	netif_tx_disable(dev);
@@ -2792,14 +2792,14 @@
 
 static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	return (card->options.checksum_type == HW_CHECKSUMMING);
 }
 
 static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 	enum qeth_card_states old_state;
 	enum qeth_checksum_types csum_type;
 
@@ -2825,7 +2825,7 @@
 
 static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data)
 {
-	struct qeth_card *card = netdev_priv(dev);
+	struct qeth_card *card = dev->ml_priv;
 
 	if (data) {
 		if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -2915,7 +2915,7 @@
 		return -ENODEV;
 
 	card->dev->hard_start_xmit = qeth_l3_hard_start_xmit;
-	card->dev->priv = card;
+	card->dev->ml_priv = card;
 	card->dev->tx_timeout = &qeth_tx_timeout;
 	card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
 	card->dev->open = qeth_l3_open;
diff --git a/fs/affs/file.c b/fs/affs/file.c
index 6eac7bd..1377b12 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -46,8 +46,6 @@
 static int
 affs_file_open(struct inode *inode, struct file *filp)
 {
-	if (atomic_read(&filp->f_count) != 1)
-		return 0;
 	pr_debug("AFFS: open(%lu,%d)\n",
 		 inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
 	atomic_inc(&AFFS_I(inode)->i_opencnt);
@@ -57,8 +55,6 @@
 static int
 affs_file_release(struct inode *inode, struct file *filp)
 {
-	if (atomic_read(&filp->f_count) != 0)
-		return 0;
 	pr_debug("AFFS: release(%lu, %d)\n",
 		 inode->i_ino, atomic_read(&AFFS_I(inode)->i_opencnt));
 
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 7102824..3cb6920 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -469,8 +469,6 @@
 extern const struct inode_operations afs_dir_inode_operations;
 extern const struct file_operations afs_dir_file_operations;
 
-extern int afs_permission(struct inode *, int, struct nameidata *);
-
 /*
  * file.c
  */
@@ -605,7 +603,7 @@
 extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
 extern void afs_zap_permits(struct rcu_head *);
 extern struct key *afs_request_key(struct afs_cell *);
-extern int afs_permission(struct inode *, int, struct nameidata *);
+extern int afs_permission(struct inode *, int);
 
 /*
  * server.c
diff --git a/fs/afs/security.c b/fs/afs/security.c
index 3bcbece..3ef5043 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -284,7 +284,7 @@
  * - AFS ACLs are attached to directories only, and a file is controlled by its
  *   parent directory's ACL
  */
-int afs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int afs_permission(struct inode *inode, int mask)
 {
 	struct afs_vnode *vnode = AFS_FS_I(inode);
 	afs_access_t uninitialized_var(access);
diff --git a/fs/aio.c b/fs/aio.c
index 0051fd9..f658441 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -512,8 +512,8 @@
  */
 static int __aio_put_req(struct kioctx *ctx, struct kiocb *req)
 {
-	dprintk(KERN_DEBUG "aio_put(%p): f_count=%d\n",
-		req, atomic_read(&req->ki_filp->f_count));
+	dprintk(KERN_DEBUG "aio_put(%p): f_count=%ld\n",
+		req, atomic_long_read(&req->ki_filp->f_count));
 
 	assert_spin_locked(&ctx->ctx_lock);
 
@@ -528,7 +528,7 @@
 	/* Must be done under the lock to serialise against cancellation.
 	 * Call this aio_fput as it duplicates fput via the fput_work.
 	 */
-	if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) {
+	if (unlikely(atomic_long_dec_and_test(&req->ki_filp->f_count))) {
 		get_ioctx(ctx);
 		spin_lock(&fput_lock);
 		list_add(&req->ki_list, &fput_head);
diff --git a/fs/attr.c b/fs/attr.c
index 966b73e..26c71ba 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -51,7 +51,7 @@
 	}
 
 	/* Check for setting the inode time. */
-	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) {
+	if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) {
 		if (!is_owner_or_cap(inode))
 			goto error;
 	}
@@ -108,6 +108,11 @@
 	struct timespec now;
 	unsigned int ia_valid = attr->ia_valid;
 
+	if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) {
+		if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+			return -EPERM;
+	}
+
 	now = current_fs_time(inode->i_sb);
 
 	attr->ia_ctime = now;
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index f1c2ea8..5f1538c 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -243,8 +243,7 @@
 	return -EIO;
 }
 
-static int bad_inode_permission(struct inode *inode, int mask,
-			struct nameidata *nd)
+static int bad_inode_permission(struct inode *inode, int mask)
 {
 	return -EIO;
 }
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index fe5f680..1ec7076 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -267,7 +267,7 @@
 	return 0;
 }
 
-static int cifs_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int cifs_permission(struct inode *inode, int mask)
 {
 	struct cifs_sb_info *cifs_sb;
 
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 3d2580e..c591622 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -137,9 +137,11 @@
 }
 
 
-int coda_permission(struct inode *inode, int mask, struct nameidata *nd)
+int coda_permission(struct inode *inode, int mask)
 {
         int error = 0;
+
+	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
  
 	if (!mask)
 		return 0; 
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
index c21a1f5..c513654 100644
--- a/fs/coda/pioctl.c
+++ b/fs/coda/pioctl.c
@@ -24,8 +24,7 @@
 #include <linux/coda_psdev.h>
 
 /* pioctl ops */
-static int coda_ioctl_permission(struct inode *inode, int mask,
-				 struct nameidata *nd);
+static int coda_ioctl_permission(struct inode *inode, int mask);
 static int coda_pioctl(struct inode * inode, struct file * filp, 
                        unsigned int cmd, unsigned long user_data);
 
@@ -42,8 +41,7 @@
 };
 
 /* the coda pioctl inode ops */
-static int coda_ioctl_permission(struct inode *inode, int mask,
-				 struct nameidata *nd)
+static int coda_ioctl_permission(struct inode *inode, int mask)
 {
         return 0;
 }
@@ -51,7 +49,7 @@
 static int coda_pioctl(struct inode * inode, struct file * filp, 
                        unsigned int cmd, unsigned long user_data)
 {
-	struct nameidata nd;
+	struct path path;
         int error;
 	struct PioctlData data;
         struct inode *target_inode = NULL;
@@ -66,21 +64,21 @@
          * Look up the pathname. Note that the pathname is in 
          * user memory, and namei takes care of this
          */
-        if ( data.follow ) {
-                error = user_path_walk(data.path, &nd);
+        if (data.follow) {
+                error = user_path(data.path, &path);
 	} else {
-	        error = user_path_walk_link(data.path, &nd);
+	        error = user_lpath(data.path, &path);
 	}
 		
 	if ( error ) {
 		return error;
         } else {
-		target_inode = nd.path.dentry->d_inode;
+		target_inode = path.dentry->d_inode;
 	}
 	
 	/* return if it is not a Coda inode */
 	if ( target_inode->i_sb != inode->i_sb ) {
-		path_put(&nd.path);
+		path_put(&path);
 	        return  -EINVAL;
 	}
 
@@ -89,7 +87,7 @@
 
 	error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
 
-	path_put(&nd.path);
+	path_put(&path);
         return error;
 }
 
diff --git a/fs/compat.c b/fs/compat.c
index 106eba2..c9d1472 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -234,18 +234,18 @@
  * The following statfs calls are copies of code from fs/open.c and
  * should be checked against those from time to time
  */
-asmlinkage long compat_sys_statfs(const char __user *path, struct compat_statfs __user *buf)
+asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (!error) {
 		struct kstatfs tmp;
-		error = vfs_statfs(nd.path.dentry, &tmp);
+		error = vfs_statfs(path.dentry, &tmp);
 		if (!error)
 			error = put_compat_statfs(buf, &tmp);
-		path_put(&nd.path);
+		path_put(&path);
 	}
 	return error;
 }
@@ -299,21 +299,21 @@
 	return 0;
 }
 
-asmlinkage long compat_sys_statfs64(const char __user *path, compat_size_t sz, struct compat_statfs64 __user *buf)
+asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
 	if (sz != sizeof(*buf))
 		return -EINVAL;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (!error) {
 		struct kstatfs tmp;
-		error = vfs_statfs(nd.path.dentry, &tmp);
+		error = vfs_statfs(path.dentry, &tmp);
 		if (!error)
 			error = put_compat_statfs64(buf, &tmp);
-		path_put(&nd.path);
+		path_put(&path);
 	}
 	return error;
 }
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index d755455..89209f0 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -465,7 +465,6 @@
 	int rc;
 	struct dentry *lower_dentry;
 	struct dentry *lower_dir_dentry;
-	umode_t mode;
 	char *encoded_symname;
 	int encoded_symlen;
 	struct ecryptfs_crypt_stat *crypt_stat = NULL;
@@ -473,7 +472,6 @@
 	lower_dentry = ecryptfs_dentry_to_lower(dentry);
 	dget(lower_dentry);
 	lower_dir_dentry = lock_parent(lower_dentry);
-	mode = S_IALLUGO;
 	encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname,
 						  strlen(symname),
 						  &encoded_symname);
@@ -482,7 +480,7 @@
 		goto out_lock;
 	}
 	rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
-			 encoded_symname, mode);
+			 encoded_symname);
 	kfree(encoded_symname);
 	if (rc || !lower_dentry->d_inode)
 		goto out_lock;
@@ -830,22 +828,9 @@
 }
 
 static int
-ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+ecryptfs_permission(struct inode *inode, int mask)
 {
-	int rc;
-
-        if (nd) {
-		struct vfsmount *vfsmnt_save = nd->path.mnt;
-		struct dentry *dentry_save = nd->path.dentry;
-
-		nd->path.mnt = ecryptfs_dentry_to_lower_mnt(nd->path.dentry);
-		nd->path.dentry = ecryptfs_dentry_to_lower(nd->path.dentry);
-		rc = permission(ecryptfs_inode_to_lower(inode), mask, nd);
-		nd->path.mnt = vfsmnt_save;
-		nd->path.dentry = dentry_save;
-        } else
-		rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL);
-        return rc;
+	return inode_permission(ecryptfs_inode_to_lower(inode), mask);
 }
 
 /**
diff --git a/fs/exec.c b/fs/exec.c
index b8792a1..9696bbf 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -106,11 +106,17 @@
  */
 asmlinkage long sys_uselib(const char __user * library)
 {
-	struct file * file;
+	struct file *file;
 	struct nameidata nd;
-	int error;
+	char *tmp = getname(library);
+	int error = PTR_ERR(tmp);
 
-	error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
+	if (!IS_ERR(tmp)) {
+		error = path_lookup_open(AT_FDCWD, tmp,
+					 LOOKUP_FOLLOW, &nd,
+					 FMODE_READ|FMODE_EXEC);
+		putname(tmp);
+	}
 	if (error)
 		goto out;
 
@@ -118,7 +124,11 @@
 	if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
 		goto exit;
 
-	error = vfs_permission(&nd, MAY_READ | MAY_EXEC);
+	error = -EACCES;
+	if (nd.path.mnt->mnt_flags & MNT_NOEXEC)
+		goto exit;
+
+	error = vfs_permission(&nd, MAY_READ | MAY_EXEC | MAY_OPEN);
 	if (error)
 		goto exit;
 
@@ -656,38 +666,43 @@
 struct file *open_exec(const char *name)
 {
 	struct nameidata nd;
-	int err;
 	struct file *file;
+	int err;
 
-	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC);
-	file = ERR_PTR(err);
+	err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd,
+				FMODE_READ|FMODE_EXEC);
+	if (err)
+		goto out;
 
-	if (!err) {
-		struct inode *inode = nd.path.dentry->d_inode;
-		file = ERR_PTR(-EACCES);
-		if (S_ISREG(inode->i_mode)) {
-			int err = vfs_permission(&nd, MAY_EXEC);
-			file = ERR_PTR(err);
-			if (!err) {
-				file = nameidata_to_filp(&nd,
-							O_RDONLY|O_LARGEFILE);
-				if (!IS_ERR(file)) {
-					err = deny_write_access(file);
-					if (err) {
-						fput(file);
-						file = ERR_PTR(err);
-					}
-				}
-out:
-				return file;
-			}
-		}
-		release_open_intent(&nd);
-		path_put(&nd.path);
+	err = -EACCES;
+	if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
+		goto out_path_put;
+
+	if (nd.path.mnt->mnt_flags & MNT_NOEXEC)
+		goto out_path_put;
+
+	err = vfs_permission(&nd, MAY_EXEC | MAY_OPEN);
+	if (err)
+		goto out_path_put;
+
+	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
+	if (IS_ERR(file))
+		return file;
+
+	err = deny_write_access(file);
+	if (err) {
+		fput(file);
+		goto out;
 	}
-	goto out;
-}
 
+	return file;
+
+ out_path_put:
+	release_open_intent(&nd);
+	path_put(&nd.path);
+ out:
+	return ERR_PTR(err);
+}
 EXPORT_SYMBOL(open_exec);
 
 int kernel_read(struct file *file, unsigned long offset,
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index e58669e..ae8c4f8 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -294,7 +294,7 @@
 }
 
 int
-ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext2_permission(struct inode *inode, int mask)
 {
 	return generic_permission(inode, mask, ext2_check_acl);
 }
diff --git a/fs/ext2/acl.h b/fs/ext2/acl.h
index 0bde85b..b42cf57 100644
--- a/fs/ext2/acl.h
+++ b/fs/ext2/acl.h
@@ -58,7 +58,7 @@
 #define EXT2_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext2_permission (struct inode *, int, struct nameidata *);
+extern int ext2_permission (struct inode *, int);
 extern int ext2_acl_chmod (struct inode *);
 extern int ext2_init_acl (struct inode *, struct inode *);
 
diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c
index a754d18..b60bb24 100644
--- a/fs/ext3/acl.c
+++ b/fs/ext3/acl.c
@@ -299,7 +299,7 @@
 }
 
 int
-ext3_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext3_permission(struct inode *inode, int mask)
 {
 	return generic_permission(inode, mask, ext3_check_acl);
 }
diff --git a/fs/ext3/acl.h b/fs/ext3/acl.h
index 0d1e627..42da16b 100644
--- a/fs/ext3/acl.h
+++ b/fs/ext3/acl.h
@@ -58,7 +58,7 @@
 #define EXT3_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext3_permission (struct inode *, int, struct nameidata *);
+extern int ext3_permission (struct inode *, int);
 extern int ext3_acl_chmod (struct inode *);
 extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);
 
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 3c8dab8..c7d04e1 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -299,7 +299,7 @@
 }
 
 int
-ext4_permission(struct inode *inode, int mask, struct nameidata *nd)
+ext4_permission(struct inode *inode, int mask)
 {
 	return generic_permission(inode, mask, ext4_check_acl);
 }
diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index 26a5c1a..cd2b855 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -58,7 +58,7 @@
 #define EXT4_ACL_NOT_CACHED ((void *)-1)
 
 /* acl.c */
-extern int ext4_permission (struct inode *, int, struct nameidata *);
+extern int ext4_permission (struct inode *, int);
 extern int ext4_acl_chmod (struct inode *);
 extern int ext4_init_acl (handle_t *, struct inode *, struct inode *);
 
diff --git a/fs/fat/file.c b/fs/fat/file.c
index c672df4..8707a8c 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -15,6 +15,8 @@
 #include <linux/writeback.h>
 #include <linux/backing-dev.h>
 #include <linux/blkdev.h>
+#include <linux/fsnotify.h>
+#include <linux/security.h>
 
 int fat_generic_ioctl(struct inode *inode, struct file *filp,
 		      unsigned int cmd, unsigned long arg)
@@ -64,6 +66,7 @@
 
 		/* Equivalent to a chmod() */
 		ia.ia_valid = ATTR_MODE | ATTR_CTIME;
+		ia.ia_ctime = current_fs_time(inode->i_sb);
 		if (is_dir) {
 			ia.ia_mode = MSDOS_MKMODE(attr,
 				S_IRWXUGO & ~sbi->options.fs_dmask)
@@ -90,11 +93,21 @@
 			}
 		}
 
-		/* This MUST be done before doing anything irreversible... */
-		err = notify_change(filp->f_path.dentry, &ia);
+		/*
+		 * The security check is questionable...  We single
+		 * out the RO attribute for checking by the security
+		 * module, just because it maps to a file mode.
+		 */
+		err = security_inode_setattr(filp->f_path.dentry, &ia);
 		if (err)
 			goto up;
 
+		/* This MUST be done before doing anything irreversible... */
+		err = fat_setattr(filp->f_path.dentry, &ia);
+		if (err)
+			goto up;
+
+		fsnotify_change(filp->f_path.dentry, ia.ia_valid);
 		if (sbi->options.sys_immutable) {
 			if (attr & ATTR_SYS)
 				inode->i_flags |= S_IMMUTABLE;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 9679fcb..61d6251 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -64,11 +64,6 @@
 	struct fdtable *fdt;
 
 	spin_lock(&files->file_lock);
-
-	error = -EINVAL;
-	if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-		goto out;
-
 repeat:
 	fdt = files_fdtable(files);
 	/*
@@ -83,10 +78,6 @@
 	if (start < fdt->max_fds)
 		newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
 					   fdt->max_fds, start);
-	
-	error = -EMFILE;
-	if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-		goto out;
 
 	error = expand_files(files, newfd);
 	if (error < 0)
@@ -135,20 +126,20 @@
 	if ((flags & ~O_CLOEXEC) != 0)
 		return -EINVAL;
 
+	if (unlikely(oldfd == newfd))
+		return -EINVAL;
+
 	spin_lock(&files->file_lock);
 	if (!(file = fcheck(oldfd)))
 		goto out_unlock;
-	err = newfd;
-	if (newfd == oldfd)
-		goto out_unlock;
-	err = -EBADF;
-	if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-		goto out_unlock;
 	get_file(file);			/* We are now finished with oldfd */
 
 	err = expand_files(files, newfd);
-	if (err < 0)
+	if (unlikely(err < 0)) {
+		if (err == -EMFILE)
+			err = -EBADF;
 		goto out_fput;
+	}
 
 	/* To avoid races with open() and dup(), we will mark the fd as
 	 * in-use in the open-file bitmap throughout the entire dup2()
@@ -189,6 +180,14 @@
 
 asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
 {
+	if (unlikely(newfd == oldfd)) { /* corner case */
+		struct files_struct *files = current->files;
+		rcu_read_lock();
+		if (!fcheck_files(files, oldfd))
+			oldfd = -EBADF;
+		rcu_read_unlock();
+		return oldfd;
+	}
 	return sys_dup3(oldfd, newfd, 0);
 }
 
@@ -321,6 +320,8 @@
 	switch (cmd) {
 	case F_DUPFD:
 	case F_DUPFD_CLOEXEC:
+		if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+			break;
 		get_file(filp);
 		err = dupfd(filp, arg, cmd == F_DUPFD_CLOEXEC);
 		break;
diff --git a/fs/fifo.c b/fs/fifo.c
index 9785e36..987bf94 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -57,7 +57,7 @@
 	 *  POSIX.1 says that O_NONBLOCK means return with the FIFO
 	 *  opened, even when there is no process writing the FIFO.
 	 */
-		filp->f_op = &read_fifo_fops;
+		filp->f_op = &read_pipefifo_fops;
 		pipe->r_counter++;
 		if (pipe->readers++ == 0)
 			wake_up_partner(inode);
@@ -86,7 +86,7 @@
 		if ((filp->f_flags & O_NONBLOCK) && !pipe->readers)
 			goto err;
 
-		filp->f_op = &write_fifo_fops;
+		filp->f_op = &write_pipefifo_fops;
 		pipe->w_counter++;
 		if (!pipe->writers++)
 			wake_up_partner(inode);
@@ -105,7 +105,7 @@
 	 *  This implementation will NEVER block on a O_RDWR open, since
 	 *  the process can at least talk to itself.
 	 */
-		filp->f_op = &rdwr_fifo_fops;
+		filp->f_op = &rdwr_pipefifo_fops;
 
 		pipe->readers++;
 		pipe->writers++;
@@ -151,5 +151,5 @@
  * depending on the access mode of the file...
  */
 const struct file_operations def_fifo_fops = {
-	.open		= fifo_open,	/* will set read or write pipe_fops */
+	.open		= fifo_open,	/* will set read_ or write_pipefifo_fops */
 };
diff --git a/fs/file.c b/fs/file.c
index 7b3887e..d8773b1 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -250,9 +250,18 @@
 	struct fdtable *fdt;
 
 	fdt = files_fdtable(files);
+
+	/*
+	 * N.B. For clone tasks sharing a files structure, this test
+	 * will limit the total number of files that can be opened.
+	 */
+	if (nr >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+		return -EMFILE;
+
 	/* Do we need to expand? */
 	if (nr < fdt->max_fds)
 		return 0;
+
 	/* Can we expand? */
 	if (nr >= sysctl_nr_open)
 		return -EMFILE;
diff --git a/fs/file_table.c b/fs/file_table.c
index 8308422..f45a449 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -120,7 +120,7 @@
 
 	tsk = current;
 	INIT_LIST_HEAD(&f->f_u.fu_list);
-	atomic_set(&f->f_count, 1);
+	atomic_long_set(&f->f_count, 1);
 	rwlock_init(&f->f_owner.lock);
 	f->f_uid = tsk->fsuid;
 	f->f_gid = tsk->fsgid;
@@ -219,7 +219,7 @@
 
 void fput(struct file *file)
 {
-	if (atomic_dec_and_test(&file->f_count))
+	if (atomic_long_dec_and_test(&file->f_count))
 		__fput(file);
 }
 
@@ -294,7 +294,7 @@
 	rcu_read_lock();
 	file = fcheck_files(files, fd);
 	if (file) {
-		if (!atomic_inc_not_zero(&file->f_count)) {
+		if (!atomic_long_inc_not_zero(&file->f_count)) {
 			/* File object ref couldn't be taken */
 			rcu_read_unlock();
 			return NULL;
@@ -326,7 +326,7 @@
 		rcu_read_lock();
 		file = fcheck_files(files, fd);
 		if (file) {
-			if (atomic_inc_not_zero(&file->f_count))
+			if (atomic_long_inc_not_zero(&file->f_count))
 				*fput_needed = 1;
 			else
 				/* Didn't get the reference, someone's freed */
@@ -341,7 +341,7 @@
 
 void put_filp(struct file *file)
 {
-	if (atomic_dec_and_test(&file->f_count)) {
+	if (atomic_long_dec_and_test(&file->f_count)) {
 		security_file_free(file);
 		file_kill(file);
 		file_free(file);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 51d0035..fd03330 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -898,7 +898,7 @@
 		return PTR_ERR(req);
 
 	memset(&inarg, 0, sizeof(inarg));
-	inarg.mask = mask;
+	inarg.mask = mask & (MAY_READ | MAY_WRITE | MAY_EXEC);
 	req->in.h.opcode = FUSE_ACCESS;
 	req->in.h.nodeid = get_node_id(inode);
 	req->in.numargs = 1;
@@ -927,7 +927,7 @@
  * access request is sent.  Execute permission is still checked
  * locally based on file mode.
  */
-static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int fuse_permission(struct inode *inode, int mask)
 {
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	bool refreshed = false;
@@ -962,7 +962,7 @@
 		   exist.  So if permissions are revoked this won't be
 		   noticed immediately, only after the attribute
 		   timeout has expired */
-	} else if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) {
+	} else if (mask & MAY_ACCESS) {
 		err = fuse_access(inode, mask);
 	} else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
 		if (!(inode->i_mode & S_IXUGO)) {
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 67ff2c6..2bada6b 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -893,7 +893,7 @@
 	if (count == 0)
 		goto out;
 
-	err = remove_suid(file->f_path.dentry);
+	err = file_remove_suid(file);
 	if (err)
 		goto out;
 
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 6da0ab3..8b0806a 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -448,7 +448,7 @@
 	struct qstr qstr;
 	struct inode *inode;
 	gfs2_str2qstr(&qstr, name);
-	inode = gfs2_lookupi(dip, &qstr, 1, NULL);
+	inode = gfs2_lookupi(dip, &qstr, 1);
 	/* gfs2_lookupi has inconsistent callers: vfs
 	 * related routines expect NULL for no entry found,
 	 * gfs2_lookup_simple callers expect ENOENT
@@ -477,7 +477,7 @@
  */
 
 struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
-			   int is_root, struct nameidata *nd)
+			   int is_root)
 {
 	struct super_block *sb = dir->i_sb;
 	struct gfs2_inode *dip = GFS2_I(dir);
@@ -1173,7 +1173,7 @@
 			break;
 		}
 
-		tmp = gfs2_lookupi(dir, &dotdot, 1, NULL);
+		tmp = gfs2_lookupi(dir, &dotdot, 1);
 		if (IS_ERR(tmp)) {
 			error = PTR_ERR(tmp);
 			break;
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index 6074c25..58f9607 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -83,7 +83,7 @@
 int gfs2_dinode_dealloc(struct gfs2_inode *inode);
 int gfs2_change_nlink(struct gfs2_inode *ip, int diff);
 struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
-			   int is_root, struct nameidata *nd);
+			   int is_root);
 struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
 			   unsigned int mode, dev_t dev);
 int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name,
diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c
index 990d9f4..9cda853 100644
--- a/fs/gfs2/ops_export.c
+++ b/fs/gfs2/ops_export.c
@@ -134,7 +134,7 @@
 	struct dentry *dentry;
 
 	gfs2_str2qstr(&dotdot, "..");
-	inode = gfs2_lookupi(child->d_inode, &dotdot, 1, NULL);
+	inode = gfs2_lookupi(child->d_inode, &dotdot, 1);
 
 	if (!inode)
 		return ERR_PTR(-ENOENT);
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index 1e252df..e2c62f7 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -74,7 +74,7 @@
 			return PTR_ERR(inode);
 		}
 
-		inode = gfs2_lookupi(dir, &dentry->d_name, 0, nd);
+		inode = gfs2_lookupi(dir, &dentry->d_name, 0);
 		if (inode) {
 			if (!IS_ERR(inode)) {
 				gfs2_holder_uninit(ghs);
@@ -109,7 +109,7 @@
 
 	dentry->d_op = &gfs2_dops;
 
-	inode = gfs2_lookupi(dir, &dentry->d_name, 0, nd);
+	inode = gfs2_lookupi(dir, &dentry->d_name, 0);
 	if (inode && IS_ERR(inode))
 		return ERR_CAST(inode);
 
@@ -915,12 +915,6 @@
 	return error;
 }
 
-static int gfs2_iop_permission(struct inode *inode, int mask,
-			       struct nameidata *nd)
-{
-	return gfs2_permission(inode, mask);
-}
-
 static int setattr_size(struct inode *inode, struct iattr *attr)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
@@ -1150,7 +1144,7 @@
 }
 
 const struct inode_operations gfs2_file_iops = {
-	.permission = gfs2_iop_permission,
+	.permission = gfs2_permission,
 	.setattr = gfs2_setattr,
 	.getattr = gfs2_getattr,
 	.setxattr = gfs2_setxattr,
@@ -1169,7 +1163,7 @@
 	.rmdir = gfs2_rmdir,
 	.mknod = gfs2_mknod,
 	.rename = gfs2_rename,
-	.permission = gfs2_iop_permission,
+	.permission = gfs2_permission,
 	.setattr = gfs2_setattr,
 	.getattr = gfs2_getattr,
 	.setxattr = gfs2_setxattr,
@@ -1181,7 +1175,7 @@
 const struct inode_operations gfs2_symlink_iops = {
 	.readlink = gfs2_readlink,
 	.follow_link = gfs2_follow_link,
-	.permission = gfs2_iop_permission,
+	.permission = gfs2_permission,
 	.setattr = gfs2_setattr,
 	.getattr = gfs2_getattr,
 	.setxattr = gfs2_setxattr,
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 63a8a90..ca83199 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -389,7 +389,7 @@
 			break;
 
 		INIT_LIST_HEAD(&jd->extent_list);
-		jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1, NULL);
+		jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1);
 		if (!jd->jd_inode || IS_ERR(jd->jd_inode)) {
 			if (!jd->jd_inode)
 				error = -ENOENT;
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index dc4ec64..7e19835 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -511,8 +511,7 @@
 	}
 }
 
-static int hfs_permission(struct inode *inode, int mask,
-			  struct nameidata *nd)
+static int hfs_permission(struct inode *inode, int mask)
 {
 	if (S_ISREG(inode->i_mode) && mask & MAY_EXEC)
 		return 0;
@@ -523,8 +522,6 @@
 {
 	if (HFS_IS_RSRC(inode))
 		inode = HFS_I(inode)->rsrc_inode;
-	if (atomic_read(&file->f_count) != 1)
-		return 0;
 	atomic_inc(&HFS_I(inode)->opencnt);
 	return 0;
 }
@@ -535,8 +532,6 @@
 
 	if (HFS_IS_RSRC(inode))
 		inode = HFS_I(inode)->rsrc_inode;
-	if (atomic_read(&file->f_count) != 0)
-		return 0;
 	if (atomic_dec_and_test(&HFS_I(inode)->opencnt)) {
 		mutex_lock(&inode->i_mutex);
 		hfs_file_truncate(inode);
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index cc3b5e2..b085d64 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -238,7 +238,7 @@
 	perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev);
 }
 
-static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int hfsplus_permission(struct inode *inode, int mask)
 {
 	/* MAY_EXEC is also used for lookup, if no x bit is set allow lookup,
 	 * open_exec has the same test, so it's still not executable, if a x bit
@@ -254,8 +254,6 @@
 {
 	if (HFSPLUS_IS_RSRC(inode))
 		inode = HFSPLUS_I(inode).rsrc_inode;
-	if (atomic_read(&file->f_count) != 1)
-		return 0;
 	atomic_inc(&HFSPLUS_I(inode).opencnt);
 	return 0;
 }
@@ -266,8 +264,6 @@
 
 	if (HFSPLUS_IS_RSRC(inode))
 		inode = HFSPLUS_I(inode).rsrc_inode;
-	if (atomic_read(&file->f_count) != 0)
-		return 0;
 	if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) {
 		mutex_lock(&inode->i_mutex);
 		hfsplus_file_truncate(inode);
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 5222345..d6ecabf 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -822,7 +822,7 @@
 	return err;
 }
 
-int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
+int hostfs_permission(struct inode *ino, int desired)
 {
 	char *name;
 	int r = 0, w = 0, x = 0, err;
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index d256559..d9c59a7 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -415,7 +415,7 @@
 		d_drop(dentry);
 		spin_lock(&dentry->d_lock);
 		if (atomic_read(&dentry->d_count) > 1 ||
-		    permission(inode, MAY_WRITE, NULL) ||
+		    generic_permission(inode, MAY_WRITE, NULL) ||
 		    !S_ISREG(inode->i_mode) ||
 		    get_write_access(inode)) {
 			spin_unlock(&dentry->d_lock);
diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c
index 65077aa..2b3d182 100644
--- a/fs/hppfs/hppfs.c
+++ b/fs/hppfs/hppfs.c
@@ -655,20 +655,13 @@
 	return proc_dentry->d_inode->i_op->follow_link(proc_dentry, nd);
 }
 
-int hppfs_permission(struct inode *inode, int mask, struct nameidata *nd)
-{
-	return generic_permission(inode, mask, NULL);
-}
-
 static const struct inode_operations hppfs_dir_iops = {
 	.lookup		= hppfs_lookup,
-	.permission	= hppfs_permission,
 };
 
 static const struct inode_operations hppfs_link_iops = {
 	.readlink	= hppfs_readlink,
 	.follow_link	= hppfs_follow_link,
-	.permission	= hppfs_permission,
 };
 
 static struct inode *get_inode(struct super_block *sb, struct dentry *dentry)
diff --git a/fs/inotify_user.c b/fs/inotify_user.c
index fe79c25d..6024942 100644
--- a/fs/inotify_user.c
+++ b/fs/inotify_user.c
@@ -354,20 +354,20 @@
 }
 
 /*
- * find_inode - resolve a user-given path to a specific inode and return a nd
+ * find_inode - resolve a user-given path to a specific inode
  */
-static int find_inode(const char __user *dirname, struct nameidata *nd,
+static int find_inode(const char __user *dirname, struct path *path,
 		      unsigned flags)
 {
 	int error;
 
-	error = __user_walk(dirname, flags, nd);
+	error = user_path_at(AT_FDCWD, dirname, flags, path);
 	if (error)
 		return error;
 	/* you can only watch an inode if you have read permissions on it */
-	error = vfs_permission(nd, MAY_READ);
+	error = inode_permission(path->dentry->d_inode, MAY_READ);
 	if (error)
-		path_put(&nd->path);
+		path_put(path);
 	return error;
 }
 
@@ -650,11 +650,11 @@
 	return sys_inotify_init1(0);
 }
 
-asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
+asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask)
 {
 	struct inode *inode;
 	struct inotify_device *dev;
-	struct nameidata nd;
+	struct path path;
 	struct file *filp;
 	int ret, fput_needed;
 	unsigned flags = 0;
@@ -674,12 +674,12 @@
 	if (mask & IN_ONLYDIR)
 		flags |= LOOKUP_DIRECTORY;
 
-	ret = find_inode(path, &nd, flags);
+	ret = find_inode(pathname, &path, flags);
 	if (unlikely(ret))
 		goto fput_and_out;
 
-	/* inode held in place by reference to nd; dev by fget on fd */
-	inode = nd.path.dentry->d_inode;
+	/* inode held in place by reference to path; dev by fget on fd */
+	inode = path.dentry->d_inode;
 	dev = filp->private_data;
 
 	mutex_lock(&dev->up_mutex);
@@ -688,7 +688,7 @@
 		ret = create_watch(dev, inode, mask);
 	mutex_unlock(&dev->up_mutex);
 
-	path_put(&nd.path);
+	path_put(&path);
 fput_and_out:
 	fput_light(filp, fput_needed);
 	return ret;
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 4c80404..d987137 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -314,7 +314,7 @@
 	return -EAGAIN;
 }
 
-int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+int jffs2_permission(struct inode *inode, int mask)
 {
 	return generic_permission(inode, mask, jffs2_check_acl);
 }
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
index 0bb7f003..8ca058a 100644
--- a/fs/jffs2/acl.h
+++ b/fs/jffs2/acl.h
@@ -28,7 +28,7 @@
 
 #define JFFS2_ACL_NOT_CACHED ((void *)-1)
 
-extern int jffs2_permission(struct inode *, int, struct nameidata *);
+extern int jffs2_permission(struct inode *, int);
 extern int jffs2_acl_chmod(struct inode *);
 extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *);
 extern int jffs2_init_acl_post(struct inode *);
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index 4d84bdc..d3e5c33 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -140,7 +140,7 @@
 	return -EAGAIN;
 }
 
-int jfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int jfs_permission(struct inode *inode, int mask)
 {
 	return generic_permission(inode, mask, jfs_check_acl);
 }
diff --git a/fs/jfs/jfs_acl.h b/fs/jfs/jfs_acl.h
index 455fa42..88475f1 100644
--- a/fs/jfs/jfs_acl.h
+++ b/fs/jfs/jfs_acl.h
@@ -20,7 +20,7 @@
 
 #ifdef CONFIG_JFS_POSIX_ACL
 
-int jfs_permission(struct inode *, int, struct nameidata *);
+int jfs_permission(struct inode *, int);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
 int jfs_setattr(struct dentry *, struct iattr *);
 
diff --git a/fs/namei.c b/fs/namei.c
index 01e67dd..a7b0a0b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -31,7 +31,6 @@
 #include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/device_cgroup.h>
-#include <asm/namei.h>
 #include <asm/uaccess.h>
 
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
@@ -185,6 +184,8 @@
 {
 	umode_t			mode = inode->i_mode;
 
+	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
+
 	if (current->fsuid == inode->i_uid)
 		mode >>= 6;
 	else {
@@ -203,7 +204,7 @@
 	/*
 	 * If the DACs are ok we don't need any capability check.
 	 */
-	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
+	if ((mask & ~mode) == 0)
 		return 0;
 
  check_capabilities:
@@ -226,13 +227,9 @@
 	return -EACCES;
 }
 
-int permission(struct inode *inode, int mask, struct nameidata *nd)
+int inode_permission(struct inode *inode, int mask)
 {
-	int retval, submask;
-	struct vfsmount *mnt = NULL;
-
-	if (nd)
-		mnt = nd->path.mnt;
+	int retval;
 
 	if (mask & MAY_WRITE) {
 		umode_t mode = inode->i_mode;
@@ -251,19 +248,9 @@
 			return -EACCES;
 	}
 
-	if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
-		/*
-		 * MAY_EXEC on regular files is denied if the fs is mounted
-		 * with the "noexec" flag.
-		 */
-		if (mnt && (mnt->mnt_flags & MNT_NOEXEC))
-			return -EACCES;
-	}
-
 	/* Ordinary permission routines do not understand MAY_APPEND. */
-	submask = mask & ~MAY_APPEND;
 	if (inode->i_op && inode->i_op->permission) {
-		retval = inode->i_op->permission(inode, submask, nd);
+		retval = inode->i_op->permission(inode, mask);
 		if (!retval) {
 			/*
 			 * Exec permission on a regular file is denied if none
@@ -277,7 +264,7 @@
 				return -EACCES;
 		}
 	} else {
-		retval = generic_permission(inode, submask, NULL);
+		retval = generic_permission(inode, mask, NULL);
 	}
 	if (retval)
 		return retval;
@@ -286,7 +273,8 @@
 	if (retval)
 		return retval;
 
-	return security_inode_permission(inode, mask, nd);
+	return security_inode_permission(inode,
+			mask & (MAY_READ|MAY_WRITE|MAY_EXEC));
 }
 
 /**
@@ -301,7 +289,7 @@
  */
 int vfs_permission(struct nameidata *nd, int mask)
 {
-	return permission(nd->path.dentry->d_inode, mask, nd);
+	return inode_permission(nd->path.dentry->d_inode, mask);
 }
 
 /**
@@ -318,7 +306,7 @@
  */
 int file_permission(struct file *file, int mask)
 {
-	return permission(file->f_path.dentry->d_inode, mask, NULL);
+	return inode_permission(file->f_path.dentry->d_inode, mask);
 }
 
 /*
@@ -459,8 +447,7 @@
  * short-cut DAC fails, then call permission() to do more
  * complete permission check.
  */
-static int exec_permission_lite(struct inode *inode,
-				       struct nameidata *nd)
+static int exec_permission_lite(struct inode *inode)
 {
 	umode_t	mode = inode->i_mode;
 
@@ -486,7 +473,7 @@
 
 	return -EACCES;
 ok:
-	return security_inode_permission(inode, MAY_EXEC, nd);
+	return security_inode_permission(inode, MAY_EXEC);
 }
 
 /*
@@ -519,7 +506,14 @@
 	 */
 	result = d_lookup(parent, name);
 	if (!result) {
-		struct dentry * dentry = d_alloc(parent, name);
+		struct dentry *dentry;
+
+		/* Don't create child dentry for a dead directory. */
+		result = ERR_PTR(-ENOENT);
+		if (IS_DEADDIR(dir))
+			goto out_unlock;
+
+		dentry = d_alloc(parent, name);
 		result = ERR_PTR(-ENOMEM);
 		if (dentry) {
 			result = dir->i_op->lookup(dir, dentry, nd);
@@ -528,6 +522,7 @@
 			else
 				result = dentry;
 		}
+out_unlock:
 		mutex_unlock(&dir->i_mutex);
 		return result;
 	}
@@ -545,27 +540,16 @@
 	return result;
 }
 
-static int __emul_lookup_dentry(const char *, struct nameidata *);
-
 /* SMP-safe */
-static __always_inline int
+static __always_inline void
 walk_init_root(const char *name, struct nameidata *nd)
 {
 	struct fs_struct *fs = current->fs;
 
 	read_lock(&fs->lock);
-	if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
-		nd->path = fs->altroot;
-		path_get(&fs->altroot);
-		read_unlock(&fs->lock);
-		if (__emul_lookup_dentry(name,nd))
-			return 0;
-		read_lock(&fs->lock);
-	}
 	nd->path = fs->root;
 	path_get(&fs->root);
 	read_unlock(&fs->lock);
-	return 1;
 }
 
 /*
@@ -606,12 +590,9 @@
 
 	if (*link == '/') {
 		path_put(&nd->path);
-		if (!walk_init_root(link, nd))
-			/* weird __emul_prefix() stuff did it */
-			goto out;
+		walk_init_root(link, nd);
 	}
 	res = link_path_walk(link, nd);
-out:
 	if (nd->depth || res || nd->last_type!=LAST_NORM)
 		return res;
 	/*
@@ -889,7 +870,7 @@
 		unsigned int c;
 
 		nd->flags |= LOOKUP_CONTINUE;
-		err = exec_permission_lite(inode, nd);
+		err = exec_permission_lite(inode);
 		if (err == -EAGAIN)
 			err = vfs_permission(nd, MAY_EXEC);
  		if (err)
@@ -1060,67 +1041,6 @@
 	return link_path_walk(name, nd);
 }
 
-/* 
- * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if
- * everything is done. Returns 0 and drops input nd, if lookup failed;
- */
-static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
-{
-	if (path_walk(name, nd))
-		return 0;		/* something went wrong... */
-
-	if (!nd->path.dentry->d_inode ||
-	    S_ISDIR(nd->path.dentry->d_inode->i_mode)) {
-		struct path old_path = nd->path;
-		struct qstr last = nd->last;
-		int last_type = nd->last_type;
-		struct fs_struct *fs = current->fs;
-
-		/*
-		 * NAME was not found in alternate root or it's a directory.
-		 * Try to find it in the normal root:
-		 */
-		nd->last_type = LAST_ROOT;
-		read_lock(&fs->lock);
-		nd->path = fs->root;
-		path_get(&fs->root);
-		read_unlock(&fs->lock);
-		if (path_walk(name, nd) == 0) {
-			if (nd->path.dentry->d_inode) {
-				path_put(&old_path);
-				return 1;
-			}
-			path_put(&nd->path);
-		}
-		nd->path = old_path;
-		nd->last = last;
-		nd->last_type = last_type;
-	}
-	return 1;
-}
-
-void set_fs_altroot(void)
-{
-	char *emul = __emul_prefix();
-	struct nameidata nd;
-	struct path path = {}, old_path;
-	int err;
-	struct fs_struct *fs = current->fs;
-
-	if (!emul)
-		goto set_it;
-	err = path_lookup(emul, LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_NOALT, &nd);
-	if (!err)
-		path = nd.path;
-set_it:
-	write_lock(&fs->lock);
-	old_path = fs->altroot;
-	fs->altroot = path;
-	write_unlock(&fs->lock);
-	if (old_path.dentry)
-		path_put(&old_path);
-}
-
 /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
 static int do_path_lookup(int dfd, const char *name,
 				unsigned int flags, struct nameidata *nd)
@@ -1136,14 +1056,6 @@
 
 	if (*name=='/') {
 		read_lock(&fs->lock);
-		if (fs->altroot.dentry && !(nd->flags & LOOKUP_NOALT)) {
-			nd->path = fs->altroot;
-			path_get(&fs->altroot);
-			read_unlock(&fs->lock);
-			if (__emul_lookup_dentry(name,nd))
-				goto out; /* found in altroot */
-			read_lock(&fs->lock);
-		}
 		nd->path = fs->root;
 		path_get(&fs->root);
 		read_unlock(&fs->lock);
@@ -1177,7 +1089,6 @@
 	}
 
 	retval = path_walk(name, nd);
-out:
 	if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
 				nd->path.dentry->d_inode))
 		audit_inode(name, nd->path.dentry);
@@ -1282,19 +1193,6 @@
 			nd, open_flags, create_mode);
 }
 
-int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
-		struct nameidata *nd, int open_flags)
-{
-	char *tmp = getname(name);
-	int err = PTR_ERR(tmp);
-
-	if (!IS_ERR(tmp)) {
-		err = __path_lookup_intent_open(AT_FDCWD, tmp, lookup_flags, nd, open_flags, 0);
-		putname(tmp);
-	}
-	return err;
-}
-
 static struct dentry *__lookup_hash(struct qstr *name,
 		struct dentry *base, struct nameidata *nd)
 {
@@ -1317,7 +1215,14 @@
 
 	dentry = cached_lookup(base, name, nd);
 	if (!dentry) {
-		struct dentry *new = d_alloc(base, name);
+		struct dentry *new;
+
+		/* Don't create child dentry for a dead directory. */
+		dentry = ERR_PTR(-ENOENT);
+		if (IS_DEADDIR(inode))
+			goto out;
+
+		new = d_alloc(base, name);
 		dentry = ERR_PTR(-ENOMEM);
 		if (!new)
 			goto out;
@@ -1340,7 +1245,7 @@
 {
 	int err;
 
-	err = permission(nd->path.dentry->d_inode, MAY_EXEC, nd);
+	err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
 	if (err)
 		return ERR_PTR(err);
 	return __lookup_hash(&nd->last, nd->path.dentry, nd);
@@ -1388,7 +1293,7 @@
 	if (err)
 		return ERR_PTR(err);
 
-	err = permission(base->d_inode, MAY_EXEC, NULL);
+	err = inode_permission(base->d_inode, MAY_EXEC);
 	if (err)
 		return ERR_PTR(err);
 	return __lookup_hash(&this, base, NULL);
@@ -1416,22 +1321,40 @@
 	return __lookup_hash(&this, base, NULL);
 }
 
-int __user_walk_fd(int dfd, const char __user *name, unsigned flags,
-			    struct nameidata *nd)
+int user_path_at(int dfd, const char __user *name, unsigned flags,
+		 struct path *path)
 {
+	struct nameidata nd;
 	char *tmp = getname(name);
 	int err = PTR_ERR(tmp);
-
 	if (!IS_ERR(tmp)) {
-		err = do_path_lookup(dfd, tmp, flags, nd);
+
+		BUG_ON(flags & LOOKUP_PARENT);
+
+		err = do_path_lookup(dfd, tmp, flags, &nd);
 		putname(tmp);
+		if (!err)
+			*path = nd.path;
 	}
 	return err;
 }
 
-int __user_walk(const char __user *name, unsigned flags, struct nameidata *nd)
+static int user_path_parent(int dfd, const char __user *path,
+			struct nameidata *nd, char **name)
 {
-	return __user_walk_fd(AT_FDCWD, name, flags, nd);
+	char *s = getname(path);
+	int error;
+
+	if (IS_ERR(s))
+		return PTR_ERR(s);
+
+	error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd);
+	if (error)
+		putname(s);
+	else
+		*name = s;
+
+	return error;
 }
 
 /*
@@ -1478,7 +1401,7 @@
 	BUG_ON(victim->d_parent->d_inode != dir);
 	audit_inode_child(victim->d_name.name, victim, dir);
 
-	error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
+	error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
 	if (error)
 		return error;
 	if (IS_APPEND(dir))
@@ -1515,7 +1438,7 @@
 		return -EEXIST;
 	if (IS_DEADDIR(dir))
 		return -ENOENT;
-	return permission(dir,MAY_WRITE | MAY_EXEC, nd);
+	return inode_permission(dir, MAY_WRITE | MAY_EXEC);
 }
 
 /* 
@@ -1755,7 +1678,7 @@
 	int will_write;
 	int flag = open_to_namei_flags(open_flag);
 
-	acc_mode = ACC_MODE(flag);
+	acc_mode = MAY_OPEN | ACC_MODE(flag);
 
 	/* O_TRUNC implies we need access checks for write permissions */
 	if (flag & O_TRUNC)
@@ -2071,20 +1994,18 @@
 asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
 				unsigned dev)
 {
-	int error = 0;
-	char * tmp;
-	struct dentry * dentry;
+	int error;
+	char *tmp;
+	struct dentry *dentry;
 	struct nameidata nd;
 
 	if (S_ISDIR(mode))
 		return -EPERM;
-	tmp = getname(filename);
-	if (IS_ERR(tmp))
-		return PTR_ERR(tmp);
 
-	error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
+	error = user_path_parent(dfd, filename, &nd, &tmp);
 	if (error)
-		goto out;
+		return error;
+
 	dentry = lookup_create(&nd, 0);
 	if (IS_ERR(dentry)) {
 		error = PTR_ERR(dentry);
@@ -2116,7 +2037,6 @@
 out_unlock:
 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 	path_put(&nd.path);
-out:
 	putname(tmp);
 
 	return error;
@@ -2156,14 +2076,10 @@
 	struct dentry *dentry;
 	struct nameidata nd;
 
-	tmp = getname(pathname);
-	error = PTR_ERR(tmp);
-	if (IS_ERR(tmp))
+	error = user_path_parent(dfd, pathname, &nd, &tmp);
+	if (error)
 		goto out_err;
 
-	error = do_path_lookup(dfd, tmp, LOOKUP_PARENT, &nd);
-	if (error)
-		goto out;
 	dentry = lookup_create(&nd, 1);
 	error = PTR_ERR(dentry);
 	if (IS_ERR(dentry))
@@ -2181,7 +2097,6 @@
 out_unlock:
 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 	path_put(&nd.path);
-out:
 	putname(tmp);
 out_err:
 	return error;
@@ -2259,13 +2174,9 @@
 	struct dentry *dentry;
 	struct nameidata nd;
 
-	name = getname(pathname);
-	if(IS_ERR(name))
-		return PTR_ERR(name);
-
-	error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
+	error = user_path_parent(dfd, pathname, &nd, &name);
 	if (error)
-		goto exit;
+		return error;
 
 	switch(nd.last_type) {
 		case LAST_DOTDOT:
@@ -2294,7 +2205,6 @@
 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 exit1:
 	path_put(&nd.path);
-exit:
 	putname(name);
 	return error;
 }
@@ -2343,19 +2253,16 @@
  */
 static long do_unlinkat(int dfd, const char __user *pathname)
 {
-	int error = 0;
-	char * name;
+	int error;
+	char *name;
 	struct dentry *dentry;
 	struct nameidata nd;
 	struct inode *inode = NULL;
 
-	name = getname(pathname);
-	if(IS_ERR(name))
-		return PTR_ERR(name);
-
-	error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
+	error = user_path_parent(dfd, pathname, &nd, &name);
 	if (error)
-		goto exit;
+		return error;
+
 	error = -EISDIR;
 	if (nd.last_type != LAST_NORM)
 		goto exit1;
@@ -2382,7 +2289,6 @@
 		iput(inode);	/* truncate the inode here */
 exit1:
 	path_put(&nd.path);
-exit:
 	putname(name);
 	return error;
 
@@ -2408,7 +2314,7 @@
 	return do_unlinkat(AT_FDCWD, pathname);
 }
 
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 {
 	int error = may_create(dir, dentry, NULL);
 
@@ -2432,23 +2338,20 @@
 asmlinkage long sys_symlinkat(const char __user *oldname,
 			      int newdfd, const char __user *newname)
 {
-	int error = 0;
-	char * from;
-	char * to;
+	int error;
+	char *from;
+	char *to;
 	struct dentry *dentry;
 	struct nameidata nd;
 
 	from = getname(oldname);
-	if(IS_ERR(from))
+	if (IS_ERR(from))
 		return PTR_ERR(from);
-	to = getname(newname);
-	error = PTR_ERR(to);
-	if (IS_ERR(to))
+
+	error = user_path_parent(newdfd, newname, &nd, &to);
+	if (error)
 		goto out_putname;
 
-	error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
-	if (error)
-		goto out;
 	dentry = lookup_create(&nd, 0);
 	error = PTR_ERR(dentry);
 	if (IS_ERR(dentry))
@@ -2457,14 +2360,13 @@
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_dput;
-	error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO);
+	error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
 	mnt_drop_write(nd.path.mnt);
 out_dput:
 	dput(dentry);
 out_unlock:
 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 	path_put(&nd.path);
-out:
 	putname(to);
 out_putname:
 	putname(from);
@@ -2498,19 +2400,19 @@
 		return -EPERM;
 	if (!dir->i_op || !dir->i_op->link)
 		return -EPERM;
-	if (S_ISDIR(old_dentry->d_inode->i_mode))
+	if (S_ISDIR(inode->i_mode))
 		return -EPERM;
 
 	error = security_inode_link(old_dentry, dir, new_dentry);
 	if (error)
 		return error;
 
-	mutex_lock(&old_dentry->d_inode->i_mutex);
+	mutex_lock(&inode->i_mutex);
 	DQUOT_INIT(dir);
 	error = dir->i_op->link(old_dentry, dir, new_dentry);
-	mutex_unlock(&old_dentry->d_inode->i_mutex);
+	mutex_unlock(&inode->i_mutex);
 	if (!error)
-		fsnotify_link(dir, old_dentry->d_inode, new_dentry);
+		fsnotify_link(dir, inode, new_dentry);
 	return error;
 }
 
@@ -2528,27 +2430,25 @@
 			   int flags)
 {
 	struct dentry *new_dentry;
-	struct nameidata nd, old_nd;
+	struct nameidata nd;
+	struct path old_path;
 	int error;
-	char * to;
+	char *to;
 
 	if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
 		return -EINVAL;
 
-	to = getname(newname);
-	if (IS_ERR(to))
-		return PTR_ERR(to);
-
-	error = __user_walk_fd(olddfd, oldname,
-			       flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
-			       &old_nd);
+	error = user_path_at(olddfd, oldname,
+			     flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
+			     &old_path);
 	if (error)
-		goto exit;
-	error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd);
+		return error;
+
+	error = user_path_parent(newdfd, newname, &nd, &to);
 	if (error)
 		goto out;
 	error = -EXDEV;
-	if (old_nd.path.mnt != nd.path.mnt)
+	if (old_path.mnt != nd.path.mnt)
 		goto out_release;
 	new_dentry = lookup_create(&nd, 0);
 	error = PTR_ERR(new_dentry);
@@ -2557,7 +2457,7 @@
 	error = mnt_want_write(nd.path.mnt);
 	if (error)
 		goto out_dput;
-	error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry);
+	error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
 	mnt_drop_write(nd.path.mnt);
 out_dput:
 	dput(new_dentry);
@@ -2565,10 +2465,9 @@
 	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
 out_release:
 	path_put(&nd.path);
-out:
-	path_put(&old_nd.path);
-exit:
 	putname(to);
+out:
+	path_put(&old_path);
 
 	return error;
 }
@@ -2621,7 +2520,7 @@
 	 * we'll need to flip '..'.
 	 */
 	if (new_dir != old_dir) {
-		error = permission(old_dentry->d_inode, MAY_WRITE, NULL);
+		error = inode_permission(old_dentry->d_inode, MAY_WRITE);
 		if (error)
 			return error;
 	}
@@ -2724,20 +2623,22 @@
 	return error;
 }
 
-static int do_rename(int olddfd, const char *oldname,
-			int newdfd, const char *newname)
+asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
+			     int newdfd, const char __user *newname)
 {
-	int error = 0;
-	struct dentry * old_dir, * new_dir;
-	struct dentry * old_dentry, *new_dentry;
-	struct dentry * trap;
+	struct dentry *old_dir, *new_dir;
+	struct dentry *old_dentry, *new_dentry;
+	struct dentry *trap;
 	struct nameidata oldnd, newnd;
+	char *from;
+	char *to;
+	int error;
 
-	error = do_path_lookup(olddfd, oldname, LOOKUP_PARENT, &oldnd);
+	error = user_path_parent(olddfd, oldname, &oldnd, &from);
 	if (error)
 		goto exit;
 
-	error = do_path_lookup(newdfd, newname, LOOKUP_PARENT, &newnd);
+	error = user_path_parent(newdfd, newname, &newnd, &to);
 	if (error)
 		goto exit1;
 
@@ -2799,29 +2700,11 @@
 	unlock_rename(new_dir, old_dir);
 exit2:
 	path_put(&newnd.path);
+	putname(to);
 exit1:
 	path_put(&oldnd.path);
-exit:
-	return error;
-}
-
-asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
-			     int newdfd, const char __user *newname)
-{
-	int error;
-	char * from;
-	char * to;
-
-	from = getname(oldname);
-	if(IS_ERR(from))
-		return PTR_ERR(from);
-	to = getname(newname);
-	error = PTR_ERR(to);
-	if (!IS_ERR(to)) {
-		error = do_rename(olddfd, from, newdfd, to);
-		putname(to);
-	}
 	putname(from);
+exit:
 	return error;
 }
 
@@ -2959,8 +2842,7 @@
 	.put_link	= page_put_link,
 };
 
-EXPORT_SYMBOL(__user_walk);
-EXPORT_SYMBOL(__user_walk_fd);
+EXPORT_SYMBOL(user_path_at);
 EXPORT_SYMBOL(follow_down);
 EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(get_write_access); /* binfmt_aout */
@@ -2975,7 +2857,7 @@
 EXPORT_SYMBOL(page_symlink_inode_operations);
 EXPORT_SYMBOL(path_lookup);
 EXPORT_SYMBOL(vfs_path_lookup);
-EXPORT_SYMBOL(permission);
+EXPORT_SYMBOL(inode_permission);
 EXPORT_SYMBOL(vfs_permission);
 EXPORT_SYMBOL(file_permission);
 EXPORT_SYMBOL(unlock_rename);
diff --git a/fs/namespace.c b/fs/namespace.c
index f30b11e..411728c 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -112,9 +112,13 @@
 		int err;
 
 		err = mnt_alloc_id(mnt);
-		if (err) {
-			kmem_cache_free(mnt_cache, mnt);
-			return NULL;
+		if (err)
+			goto out_free_cache;
+
+		if (name) {
+			mnt->mnt_devname = kstrdup(name, GFP_KERNEL);
+			if (!mnt->mnt_devname)
+				goto out_free_id;
 		}
 
 		atomic_set(&mnt->mnt_count, 1);
@@ -127,16 +131,14 @@
 		INIT_LIST_HEAD(&mnt->mnt_slave_list);
 		INIT_LIST_HEAD(&mnt->mnt_slave);
 		atomic_set(&mnt->__mnt_writers, 0);
-		if (name) {
-			int size = strlen(name) + 1;
-			char *newname = kmalloc(size, GFP_KERNEL);
-			if (newname) {
-				memcpy(newname, name, size);
-				mnt->mnt_devname = newname;
-			}
-		}
 	}
 	return mnt;
+
+out_free_id:
+	mnt_free_id(mnt);
+out_free_cache:
+	kmem_cache_free(mnt_cache, mnt);
+	return NULL;
 }
 
 /*
@@ -1128,27 +1130,27 @@
 
 asmlinkage long sys_umount(char __user * name, int flags)
 {
-	struct nameidata nd;
+	struct path path;
 	int retval;
 
-	retval = __user_walk(name, LOOKUP_FOLLOW, &nd);
+	retval = user_path(name, &path);
 	if (retval)
 		goto out;
 	retval = -EINVAL;
-	if (nd.path.dentry != nd.path.mnt->mnt_root)
+	if (path.dentry != path.mnt->mnt_root)
 		goto dput_and_out;
-	if (!check_mnt(nd.path.mnt))
+	if (!check_mnt(path.mnt))
 		goto dput_and_out;
 
 	retval = -EPERM;
 	if (!capable(CAP_SYS_ADMIN))
 		goto dput_and_out;
 
-	retval = do_umount(nd.path.mnt, flags);
+	retval = do_umount(path.mnt, flags);
 dput_and_out:
 	/* we mustn't call path_put() as that would clear mnt_expiry_mark */
-	dput(nd.path.dentry);
-	mntput_no_expire(nd.path.mnt);
+	dput(path.dentry);
+	mntput_no_expire(path.mnt);
 out:
 	return retval;
 }
@@ -1972,7 +1974,7 @@
 		struct fs_struct *fs)
 {
 	struct mnt_namespace *new_ns;
-	struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL;
+	struct vfsmount *rootmnt = NULL, *pwdmnt = NULL;
 	struct vfsmount *p, *q;
 
 	new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
@@ -2015,10 +2017,6 @@
 				pwdmnt = p;
 				fs->pwd.mnt = mntget(q);
 			}
-			if (p == fs->altroot.mnt) {
-				altrootmnt = p;
-				fs->altroot.mnt = mntget(q);
-			}
 		}
 		p = next_mnt(p, mnt_ns->root);
 		q = next_mnt(q, new_ns->root);
@@ -2029,8 +2027,6 @@
 		mntput(rootmnt);
 	if (pwdmnt)
 		mntput(pwdmnt);
-	if (altrootmnt)
-		mntput(altrootmnt);
 
 	return new_ns;
 }
@@ -2183,28 +2179,26 @@
 			       const char __user * put_old)
 {
 	struct vfsmount *tmp;
-	struct nameidata new_nd, old_nd;
-	struct path parent_path, root_parent, root;
+	struct path new, old, parent_path, root_parent, root;
 	int error;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
-			    &new_nd);
+	error = user_path_dir(new_root, &new);
 	if (error)
 		goto out0;
 	error = -EINVAL;
-	if (!check_mnt(new_nd.path.mnt))
+	if (!check_mnt(new.mnt))
 		goto out1;
 
-	error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd);
+	error = user_path_dir(put_old, &old);
 	if (error)
 		goto out1;
 
-	error = security_sb_pivotroot(&old_nd.path, &new_nd.path);
+	error = security_sb_pivotroot(&old, &new);
 	if (error) {
-		path_put(&old_nd.path);
+		path_put(&old);
 		goto out1;
 	}
 
@@ -2213,69 +2207,69 @@
 	path_get(&current->fs->root);
 	read_unlock(&current->fs->lock);
 	down_write(&namespace_sem);
-	mutex_lock(&old_nd.path.dentry->d_inode->i_mutex);
+	mutex_lock(&old.dentry->d_inode->i_mutex);
 	error = -EINVAL;
-	if (IS_MNT_SHARED(old_nd.path.mnt) ||
-		IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) ||
+	if (IS_MNT_SHARED(old.mnt) ||
+		IS_MNT_SHARED(new.mnt->mnt_parent) ||
 		IS_MNT_SHARED(root.mnt->mnt_parent))
 		goto out2;
 	if (!check_mnt(root.mnt))
 		goto out2;
 	error = -ENOENT;
-	if (IS_DEADDIR(new_nd.path.dentry->d_inode))
+	if (IS_DEADDIR(new.dentry->d_inode))
 		goto out2;
-	if (d_unhashed(new_nd.path.dentry) && !IS_ROOT(new_nd.path.dentry))
+	if (d_unhashed(new.dentry) && !IS_ROOT(new.dentry))
 		goto out2;
-	if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry))
+	if (d_unhashed(old.dentry) && !IS_ROOT(old.dentry))
 		goto out2;
 	error = -EBUSY;
-	if (new_nd.path.mnt == root.mnt ||
-	    old_nd.path.mnt == root.mnt)
+	if (new.mnt == root.mnt ||
+	    old.mnt == root.mnt)
 		goto out2; /* loop, on the same file system  */
 	error = -EINVAL;
 	if (root.mnt->mnt_root != root.dentry)
 		goto out2; /* not a mountpoint */
 	if (root.mnt->mnt_parent == root.mnt)
 		goto out2; /* not attached */
-	if (new_nd.path.mnt->mnt_root != new_nd.path.dentry)
+	if (new.mnt->mnt_root != new.dentry)
 		goto out2; /* not a mountpoint */
-	if (new_nd.path.mnt->mnt_parent == new_nd.path.mnt)
+	if (new.mnt->mnt_parent == new.mnt)
 		goto out2; /* not attached */
 	/* make sure we can reach put_old from new_root */
-	tmp = old_nd.path.mnt;
+	tmp = old.mnt;
 	spin_lock(&vfsmount_lock);
-	if (tmp != new_nd.path.mnt) {
+	if (tmp != new.mnt) {
 		for (;;) {
 			if (tmp->mnt_parent == tmp)
 				goto out3; /* already mounted on put_old */
-			if (tmp->mnt_parent == new_nd.path.mnt)
+			if (tmp->mnt_parent == new.mnt)
 				break;
 			tmp = tmp->mnt_parent;
 		}
-		if (!is_subdir(tmp->mnt_mountpoint, new_nd.path.dentry))
+		if (!is_subdir(tmp->mnt_mountpoint, new.dentry))
 			goto out3;
-	} else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry))
+	} else if (!is_subdir(old.dentry, new.dentry))
 		goto out3;
-	detach_mnt(new_nd.path.mnt, &parent_path);
+	detach_mnt(new.mnt, &parent_path);
 	detach_mnt(root.mnt, &root_parent);
 	/* mount old root on put_old */
-	attach_mnt(root.mnt, &old_nd.path);
+	attach_mnt(root.mnt, &old);
 	/* mount new_root on / */
-	attach_mnt(new_nd.path.mnt, &root_parent);
+	attach_mnt(new.mnt, &root_parent);
 	touch_mnt_namespace(current->nsproxy->mnt_ns);
 	spin_unlock(&vfsmount_lock);
-	chroot_fs_refs(&root, &new_nd.path);
-	security_sb_post_pivotroot(&root, &new_nd.path);
+	chroot_fs_refs(&root, &new);
+	security_sb_post_pivotroot(&root, &new);
 	error = 0;
 	path_put(&root_parent);
 	path_put(&parent_path);
 out2:
-	mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex);
+	mutex_unlock(&old.dentry->d_inode->i_mutex);
 	up_write(&namespace_sem);
 	path_put(&root);
-	path_put(&old_nd.path);
+	path_put(&old);
 out1:
-	path_put(&new_nd.path);
+	path_put(&new);
 out0:
 	return error;
 out3:
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index 011ef0b..07e9715 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -266,7 +266,7 @@
 
 
 static int
-__ncp_lookup_validate(struct dentry * dentry, struct nameidata *nd)
+__ncp_lookup_validate(struct dentry *dentry)
 {
 	struct ncp_server *server;
 	struct dentry *parent;
@@ -340,7 +340,7 @@
 {
 	int res;
 	lock_kernel();
-	res = __ncp_lookup_validate(dentry, nd);
+	res = __ncp_lookup_validate(dentry);
 	unlock_kernel();
 	return res;
 }
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 28a238d..74f92b7 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1884,7 +1884,7 @@
 		return status;
 	nfs_access_add_cache(inode, &cache);
 out:
-	if ((cache.mask & mask) == mask)
+	if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
 		return 0;
 	return -EACCES;
 }
@@ -1907,17 +1907,17 @@
 	return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
 }
 
-int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int nfs_permission(struct inode *inode, int mask)
 {
 	struct rpc_cred *cred;
 	int res = 0;
 
 	nfs_inc_stats(inode, NFSIOS_VFSACCESS);
 
-	if (mask == 0)
+	if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
 		goto out;
 	/* Is this sys_access() ? */
-	if (nd != NULL && (nd->flags & LOOKUP_ACCESS))
+	if (mask & MAY_ACCESS)
 		goto force_lookup;
 
 	switch (inode->i_mode & S_IFMT) {
@@ -1926,8 +1926,7 @@
 		case S_IFREG:
 			/* NFSv4 has atomic_open... */
 			if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)
-					&& nd != NULL
-					&& (nd->flags & LOOKUP_OPEN))
+					&& (mask & MAY_OPEN))
 				goto out;
 			break;
 		case S_IFDIR:
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 1955a27..c53e65f 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -12,6 +12,7 @@
 #include <linux/time.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/fcntl.h>
 #include <linux/net.h>
 #include <linux/in.h>
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index f45451e..ea37c96 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -51,7 +51,7 @@
 		/* make sure parents give x permission to user */
 		int err;
 		parent = dget_parent(tdentry);
-		err = permission(parent->d_inode, MAY_EXEC, NULL);
+		err = inode_permission(parent->d_inode, MAY_EXEC);
 		if (err < 0) {
 			dput(parent);
 			break;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 0f4481e..18060be 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1516,7 +1516,6 @@
 	struct dentry	*dentry, *dnew;
 	__be32		err, cerr;
 	int		host_err;
-	umode_t		mode;
 
 	err = nfserr_noent;
 	if (!flen || !plen)
@@ -1535,11 +1534,6 @@
 	if (IS_ERR(dnew))
 		goto out_nfserr;
 
-	mode = S_IALLUGO;
-	/* Only the MODE ATTRibute is even vaguely meaningful */
-	if (iap && (iap->ia_valid & ATTR_MODE))
-		mode = iap->ia_mode & S_IALLUGO;
-
 	host_err = mnt_want_write(fhp->fh_export->ex_path.mnt);
 	if (host_err)
 		goto out_nfserr;
@@ -1551,11 +1545,11 @@
 		else {
 			strncpy(path_alloced, path, plen);
 			path_alloced[plen] = 0;
-			host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode);
+			host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced);
 			kfree(path_alloced);
 		}
 	} else
-		host_err = vfs_symlink(dentry->d_inode, dnew, path, mode);
+		host_err = vfs_symlink(dentry->d_inode, dnew, path);
 
 	if (!host_err) {
 		if (EX_ISSYNC(fhp->fh_export))
@@ -1959,12 +1953,12 @@
 		return 0;
 
 	/* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
-	err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL);
+	err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
 
 	/* Allow read access to binaries even when mode 111 */
 	if (err == -EACCES && S_ISREG(inode->i_mode) &&
 	    acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE))
-		err = permission(inode, MAY_EXEC, NULL);
+		err = inode_permission(inode, MAY_EXEC);
 
 	return err? nfserrno(err) : 0;
 }
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 3c5550c..d020866 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -2118,7 +2118,7 @@
 		goto out;
 	if (!count)
 		goto out;
-	err = remove_suid(file->f_path.dentry);
+	err = file_remove_suid(file);
 	if (err)
 		goto out;
 	file_update_time(file);
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index e8514e8..be2dd95 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1176,7 +1176,7 @@
 	return err;
 }
 
-int ocfs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+int ocfs2_permission(struct inode *inode, int mask)
 {
 	int ret;
 
diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
index 048ddca..1e27b4d 100644
--- a/fs/ocfs2/file.h
+++ b/fs/ocfs2/file.h
@@ -62,8 +62,7 @@
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
 int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		  struct kstat *stat);
-int ocfs2_permission(struct inode *inode, int mask,
-		     struct nameidata *nd);
+int ocfs2_permission(struct inode *inode, int mask);
 
 int ocfs2_should_update_atime(struct inode *inode,
 			      struct vfsmount *vfsmnt);
diff --git a/fs/open.c b/fs/open.c
index bb98d2f..52647be 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -122,37 +122,37 @@
 	return 0;
 }
 
-asmlinkage long sys_statfs(const char __user * path, struct statfs __user * buf)
+asmlinkage long sys_statfs(const char __user *pathname, struct statfs __user * buf)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (!error) {
 		struct statfs tmp;
-		error = vfs_statfs_native(nd.path.dentry, &tmp);
+		error = vfs_statfs_native(path.dentry, &tmp);
 		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
 			error = -EFAULT;
-		path_put(&nd.path);
+		path_put(&path);
 	}
 	return error;
 }
 
 
-asmlinkage long sys_statfs64(const char __user *path, size_t sz, struct statfs64 __user *buf)
+asmlinkage long sys_statfs64(const char __user *pathname, size_t sz, struct statfs64 __user *buf)
 {
-	struct nameidata nd;
+	struct path path;
 	long error;
 
 	if (sz != sizeof(*buf))
 		return -EINVAL;
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (!error) {
 		struct statfs64 tmp;
-		error = vfs_statfs64(nd.path.dentry, &tmp);
+		error = vfs_statfs64(path.dentry, &tmp);
 		if (!error && copy_to_user(buf, &tmp, sizeof(tmp)))
 			error = -EFAULT;
-		path_put(&nd.path);
+		path_put(&path);
 	}
 	return error;
 }
@@ -223,20 +223,20 @@
 	return err;
 }
 
-static long do_sys_truncate(const char __user * path, loff_t length)
+static long do_sys_truncate(const char __user *pathname, loff_t length)
 {
-	struct nameidata nd;
-	struct inode * inode;
+	struct path path;
+	struct inode *inode;
 	int error;
 
 	error = -EINVAL;
 	if (length < 0)	/* sorry, but loff_t says... */
 		goto out;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (error)
 		goto out;
-	inode = nd.path.dentry->d_inode;
+	inode = path.dentry->d_inode;
 
 	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
 	error = -EISDIR;
@@ -247,16 +247,16 @@
 	if (!S_ISREG(inode->i_mode))
 		goto dput_and_out;
 
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (error)
 		goto dput_and_out;
 
-	error = vfs_permission(&nd, MAY_WRITE);
+	error = inode_permission(inode, MAY_WRITE);
 	if (error)
 		goto mnt_drop_write_and_out;
 
 	error = -EPERM;
-	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+	if (IS_APPEND(inode))
 		goto mnt_drop_write_and_out;
 
 	error = get_write_access(inode);
@@ -274,15 +274,15 @@
 	error = locks_verify_truncate(inode, NULL, length);
 	if (!error) {
 		DQUOT_INIT(inode);
-		error = do_truncate(nd.path.dentry, length, 0, NULL);
+		error = do_truncate(path.dentry, length, 0, NULL);
 	}
 
 put_write_and_out:
 	put_write_access(inode);
 mnt_drop_write_and_out:
-	mnt_drop_write(nd.path.mnt);
+	mnt_drop_write(path.mnt);
 dput_and_out:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	return error;
 }
@@ -425,7 +425,8 @@
  */
 asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode)
 {
-	struct nameidata nd;
+	struct path path;
+	struct inode *inode;
 	int old_fsuid, old_fsgid;
 	kernel_cap_t uninitialized_var(old_cap);  /* !SECURE_NO_SETUID_FIXUP */
 	int res;
@@ -448,7 +449,7 @@
 		 * FIXME: There is a race here against sys_capset.  The
 		 * capabilities can change yet we will restore the old
 		 * value below.  We should hold task_capabilities_lock,
-		 * but we cannot because user_path_walk can sleep.
+		 * but we cannot because user_path_at can sleep.
 		 */
 #endif /* ndef CONFIG_SECURITY_FILE_CAPABILITIES */
 		if (current->uid)
@@ -457,14 +458,25 @@
 			old_cap = cap_set_effective(current->cap_permitted);
 	}
 
-	res = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW|LOOKUP_ACCESS, &nd);
+	res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
 	if (res)
 		goto out;
 
-	res = vfs_permission(&nd, mode);
+	inode = path.dentry->d_inode;
+
+	if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
+		/*
+		 * MAY_EXEC on regular files is denied if the fs is mounted
+		 * with the "noexec" flag.
+		 */
+		res = -EACCES;
+		if (path.mnt->mnt_flags & MNT_NOEXEC)
+			goto out_path_release;
+	}
+
+	res = inode_permission(inode, mode | MAY_ACCESS);
 	/* SuS v2 requires we report a read only fs too */
-	if(res || !(mode & S_IWOTH) ||
-	   special_file(nd.path.dentry->d_inode->i_mode))
+	if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
 		goto out_path_release;
 	/*
 	 * This is a rare case where using __mnt_is_readonly()
@@ -476,11 +488,11 @@
 	 * inherently racy and know that the fs may change
 	 * state before we even see this result.
 	 */
-	if (__mnt_is_readonly(nd.path.mnt))
+	if (__mnt_is_readonly(path.mnt))
 		res = -EROFS;
 
 out_path_release:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	current->fsuid = old_fsuid;
 	current->fsgid = old_fsgid;
@@ -498,22 +510,21 @@
 
 asmlinkage long sys_chdir(const char __user * filename)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = __user_walk(filename,
-			    LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
+	error = user_path_dir(filename, &path);
 	if (error)
 		goto out;
 
-	error = vfs_permission(&nd, MAY_EXEC);
+	error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
 	if (error)
 		goto dput_and_out;
 
-	set_fs_pwd(current->fs, &nd.path);
+	set_fs_pwd(current->fs, &path);
 
 dput_and_out:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	return error;
 }
@@ -535,7 +546,7 @@
 	if (!S_ISDIR(inode->i_mode))
 		goto out_putf;
 
-	error = file_permission(file, MAY_EXEC);
+	error = inode_permission(inode, MAY_EXEC | MAY_ACCESS);
 	if (!error)
 		set_fs_pwd(current->fs, &file->f_path);
 out_putf:
@@ -546,14 +557,14 @@
 
 asmlinkage long sys_chroot(const char __user * filename)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
+	error = user_path_dir(filename, &path);
 	if (error)
 		goto out;
 
-	error = vfs_permission(&nd, MAY_EXEC);
+	error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
 	if (error)
 		goto dput_and_out;
 
@@ -561,11 +572,10 @@
 	if (!capable(CAP_SYS_CHROOT))
 		goto dput_and_out;
 
-	set_fs_root(current->fs, &nd.path);
-	set_fs_altroot();
+	set_fs_root(current->fs, &path);
 	error = 0;
 dput_and_out:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	return error;
 }
@@ -590,9 +600,6 @@
 	err = mnt_want_write(file->f_path.mnt);
 	if (err)
 		goto out_putf;
-	err = -EPERM;
-	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-		goto out_drop_write;
 	mutex_lock(&inode->i_mutex);
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
@@ -600,8 +607,6 @@
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
 	err = notify_change(dentry, &newattrs);
 	mutex_unlock(&inode->i_mutex);
-
-out_drop_write:
 	mnt_drop_write(file->f_path.mnt);
 out_putf:
 	fput(file);
@@ -612,36 +617,29 @@
 asmlinkage long sys_fchmodat(int dfd, const char __user *filename,
 			     mode_t mode)
 {
-	struct nameidata nd;
-	struct inode * inode;
+	struct path path;
+	struct inode *inode;
 	int error;
 	struct iattr newattrs;
 
-	error = __user_walk_fd(dfd, filename, LOOKUP_FOLLOW, &nd);
+	error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
 	if (error)
 		goto out;
-	inode = nd.path.dentry->d_inode;
+	inode = path.dentry->d_inode;
 
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (error)
 		goto dput_and_out;
-
-	error = -EPERM;
-	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-		goto out_drop_write;
-
 	mutex_lock(&inode->i_mutex);
 	if (mode == (mode_t) -1)
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	error = notify_change(nd.path.dentry, &newattrs);
+	error = notify_change(path.dentry, &newattrs);
 	mutex_unlock(&inode->i_mutex);
-
-out_drop_write:
-	mnt_drop_write(nd.path.mnt);
+	mnt_drop_write(path.mnt);
 dput_and_out:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	return error;
 }
@@ -653,18 +651,10 @@
 
 static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
 {
-	struct inode * inode;
+	struct inode *inode = dentry->d_inode;
 	int error;
 	struct iattr newattrs;
 
-	error = -ENOENT;
-	if (!(inode = dentry->d_inode)) {
-		printk(KERN_ERR "chown_common: NULL inode\n");
-		goto out;
-	}
-	error = -EPERM;
-	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-		goto out;
 	newattrs.ia_valid =  ATTR_CTIME;
 	if (user != (uid_t) -1) {
 		newattrs.ia_valid |= ATTR_UID;
@@ -680,25 +670,25 @@
 	mutex_lock(&inode->i_mutex);
 	error = notify_change(dentry, &newattrs);
 	mutex_unlock(&inode->i_mutex);
-out:
+
 	return error;
 }
 
 asmlinkage long sys_chown(const char __user * filename, uid_t user, gid_t group)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk(filename, &nd);
+	error = user_path(filename, &path);
 	if (error)
 		goto out;
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, user, group);
-	mnt_drop_write(nd.path.mnt);
+	error = chown_common(path.dentry, user, group);
+	mnt_drop_write(path.mnt);
 out_release:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	return error;
 }
@@ -706,7 +696,7 @@
 asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
 			     gid_t group, int flag)
 {
-	struct nameidata nd;
+	struct path path;
 	int error = -EINVAL;
 	int follow;
 
@@ -714,35 +704,35 @@
 		goto out;
 
 	follow = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
-	error = __user_walk_fd(dfd, filename, follow, &nd);
+	error = user_path_at(dfd, filename, follow, &path);
 	if (error)
 		goto out;
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, user, group);
-	mnt_drop_write(nd.path.mnt);
+	error = chown_common(path.dentry, user, group);
+	mnt_drop_write(path.mnt);
 out_release:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	return error;
 }
 
 asmlinkage long sys_lchown(const char __user * filename, uid_t user, gid_t group)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk_link(filename, &nd);
+	error = user_lpath(filename, &path);
 	if (error)
 		goto out;
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (error)
 		goto out_release;
-	error = chown_common(nd.path.dentry, user, group);
-	mnt_drop_write(nd.path.mnt);
+	error = chown_common(path.dentry, user, group);
+	mnt_drop_write(path.mnt);
 out_release:
-	path_put(&nd.path);
+	path_put(&path);
 out:
 	return error;
 }
@@ -982,7 +972,6 @@
 	int fd, error;
 	struct fdtable *fdt;
 
-  	error = -EMFILE;
 	spin_lock(&files->file_lock);
 
 repeat:
@@ -990,13 +979,6 @@
 	fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds,
 				files->next_fd);
 
-	/*
-	 * N.B. For clone tasks sharing a files structure, this test
-	 * will limit the total number of files that can be opened.
-	 */
-	if (fd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
-		goto out;
-
 	/* Do we need to expand the fd array or fd set?  */
 	error = expand_files(files, fd);
 	if (error < 0)
@@ -1007,7 +989,6 @@
 	 	 * If we needed to expand the fs array we
 		 * might have blocked - try again.
 		 */
-		error = -EMFILE;
 		goto repeat;
 	}
 
diff --git a/fs/pipe.c b/fs/pipe.c
index 10c4e9aa5..fcba654 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -777,8 +777,10 @@
 /*
  * The file_operations structs are not static because they
  * are also used in linux/fs/fifo.c to do operations on FIFOs.
+ *
+ * Pipes reuse fifos' file_operations structs.
  */
-const struct file_operations read_fifo_fops = {
+const struct file_operations read_pipefifo_fops = {
 	.llseek		= no_llseek,
 	.read		= do_sync_read,
 	.aio_read	= pipe_read,
@@ -790,7 +792,7 @@
 	.fasync		= pipe_read_fasync,
 };
 
-const struct file_operations write_fifo_fops = {
+const struct file_operations write_pipefifo_fops = {
 	.llseek		= no_llseek,
 	.read		= bad_pipe_r,
 	.write		= do_sync_write,
@@ -802,44 +804,7 @@
 	.fasync		= pipe_write_fasync,
 };
 
-const struct file_operations rdwr_fifo_fops = {
-	.llseek		= no_llseek,
-	.read		= do_sync_read,
-	.aio_read	= pipe_read,
-	.write		= do_sync_write,
-	.aio_write	= pipe_write,
-	.poll		= pipe_poll,
-	.unlocked_ioctl	= pipe_ioctl,
-	.open		= pipe_rdwr_open,
-	.release	= pipe_rdwr_release,
-	.fasync		= pipe_rdwr_fasync,
-};
-
-static const struct file_operations read_pipe_fops = {
-	.llseek		= no_llseek,
-	.read		= do_sync_read,
-	.aio_read	= pipe_read,
-	.write		= bad_pipe_w,
-	.poll		= pipe_poll,
-	.unlocked_ioctl	= pipe_ioctl,
-	.open		= pipe_read_open,
-	.release	= pipe_read_release,
-	.fasync		= pipe_read_fasync,
-};
-
-static const struct file_operations write_pipe_fops = {
-	.llseek		= no_llseek,
-	.read		= bad_pipe_r,
-	.write		= do_sync_write,
-	.aio_write	= pipe_write,
-	.poll		= pipe_poll,
-	.unlocked_ioctl	= pipe_ioctl,
-	.open		= pipe_write_open,
-	.release	= pipe_write_release,
-	.fasync		= pipe_write_fasync,
-};
-
-static const struct file_operations rdwr_pipe_fops = {
+const struct file_operations rdwr_pipefifo_fops = {
 	.llseek		= no_llseek,
 	.read		= do_sync_read,
 	.aio_read	= pipe_read,
@@ -927,7 +892,7 @@
 	inode->i_pipe = pipe;
 
 	pipe->readers = pipe->writers = 1;
-	inode->i_fop = &rdwr_pipe_fops;
+	inode->i_fop = &rdwr_pipefifo_fops;
 
 	/*
 	 * Mark the inode dirty from the very beginning,
@@ -978,7 +943,7 @@
 	d_instantiate(dentry, inode);
 
 	err = -ENFILE;
-	f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipe_fops);
+	f = alloc_file(pipe_mnt, dentry, FMODE_WRITE, &write_pipefifo_fops);
 	if (!f)
 		goto err_dentry;
 	f->f_mapping = inode->i_mapping;
@@ -1020,7 +985,7 @@
 
 	f->f_pos = 0;
 	f->f_flags = O_RDONLY | (flags & O_NONBLOCK);
-	f->f_op = &read_pipe_fops;
+	f->f_op = &read_pipefifo_fops;
 	f->f_mode = FMODE_READ;
 	f->f_version = 0;
 
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 81bce67..e74308b 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1859,8 +1859,7 @@
  * /proc/pid/fd needs a special permission handler so that a process can still
  * access /proc/self/fd after it has executed a setuid().
  */
-static int proc_fd_permission(struct inode *inode, int mask,
-				struct nameidata *nd)
+static int proc_fd_permission(struct inode *inode, int mask)
 {
 	int rv;
 
@@ -2406,35 +2405,18 @@
 	u64 rchar, wchar, syscr, syscw;
 	struct task_io_accounting ioac;
 
-	if (!whole) {
-		rchar = task->rchar;
-		wchar = task->wchar;
-		syscr = task->syscr;
-		syscw = task->syscw;
-		memcpy(&ioac, &task->ioac, sizeof(ioac));
-	} else {
+	rchar = task->rchar;
+	wchar = task->wchar;
+	syscr = task->syscr;
+	syscw = task->syscw;
+	memcpy(&ioac, &task->ioac, sizeof(ioac));
+
+	if (whole) {
 		unsigned long flags;
-		struct task_struct *t = task;
-		rchar = wchar = syscr = syscw = 0;
-		memset(&ioac, 0, sizeof(ioac));
-
-		rcu_read_lock();
-		do {
-			rchar += t->rchar;
-			wchar += t->wchar;
-			syscr += t->syscr;
-			syscw += t->syscw;
-
-			ioac.read_bytes += t->ioac.read_bytes;
-			ioac.write_bytes += t->ioac.write_bytes;
-			ioac.cancelled_write_bytes +=
-					t->ioac.cancelled_write_bytes;
-			t = next_thread(t);
-		} while (t != task);
-		rcu_read_unlock();
 
 		if (lock_task_sighand(task, &flags)) {
 			struct signal_struct *sig = task->signal;
+			struct task_struct *t = task;
 
 			rchar += sig->rchar;
 			wchar += sig->wchar;
@@ -2445,11 +2427,20 @@
 			ioac.write_bytes += sig->ioac.write_bytes;
 			ioac.cancelled_write_bytes +=
 					sig->ioac.cancelled_write_bytes;
+			while_each_thread(task, t) {
+				rchar += t->rchar;
+				wchar += t->wchar;
+				syscr += t->syscr;
+				syscw += t->syscw;
 
+				ioac.read_bytes += t->ioac.read_bytes;
+				ioac.write_bytes += t->ioac.write_bytes;
+				ioac.cancelled_write_bytes +=
+					t->ioac.cancelled_write_bytes;
+			}
 			unlock_task_sighand(task, &flags);
 		}
 	}
-
 	return sprintf(buffer,
 			"rchar: %llu\n"
 			"wchar: %llu\n"
@@ -2458,13 +2449,9 @@
 			"read_bytes: %llu\n"
 			"write_bytes: %llu\n"
 			"cancelled_write_bytes: %llu\n",
-			(unsigned long long)rchar,
-			(unsigned long long)wchar,
-			(unsigned long long)syscr,
-			(unsigned long long)syscw,
-			(unsigned long long)ioac.read_bytes,
-			(unsigned long long)ioac.write_bytes,
-			(unsigned long long)ioac.cancelled_write_bytes);
+			rchar, wchar, syscr, syscw,
+			ioac.read_bytes, ioac.write_bytes,
+			ioac.cancelled_write_bytes);
 }
 
 static int proc_tid_io_accounting(struct task_struct *task, char *buffer)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index b37f25d..8bb03f0 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
+#include <linux/sysctl.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -65,6 +66,8 @@
 			module_put(de->owner);
 		de_put(de);
 	}
+	if (PROC_I(inode)->sysctl)
+		sysctl_head_put(PROC_I(inode)->sysctl);
 	clear_inode(inode);
 }
 
@@ -84,6 +87,8 @@
 	ei->fd = 0;
 	ei->op.proc_get_link = NULL;
 	ei->pde = NULL;
+	ei->sysctl = NULL;
+	ei->sysctl_entry = NULL;
 	inode = &ei->vfs_inode;
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 	return inode;
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 5acc001..f9a8b89 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -10,149 +10,110 @@
 static struct dentry_operations proc_sys_dentry_operations;
 static const struct file_operations proc_sys_file_operations;
 static const struct inode_operations proc_sys_inode_operations;
+static const struct file_operations proc_sys_dir_file_operations;
+static const struct inode_operations proc_sys_dir_operations;
 
-static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table)
-{
-	/* Refresh the cached information bits in the inode */
-	if (table) {
-		inode->i_uid = 0;
-		inode->i_gid = 0;
-		inode->i_mode = table->mode;
-		if (table->proc_handler) {
-			inode->i_mode |= S_IFREG;
-			inode->i_nlink = 1;
-		} else {
-			inode->i_mode |= S_IFDIR;
-			inode->i_nlink = 0;	/* It is too hard to figure out */
-		}
-	}
-}
-
-static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table)
+static struct inode *proc_sys_make_inode(struct super_block *sb,
+		struct ctl_table_header *head, struct ctl_table *table)
 {
 	struct inode *inode;
-	struct proc_inode *dir_ei, *ei;
-	int depth;
+	struct proc_inode *ei;
 
-	inode = new_inode(dir->i_sb);
+	inode = new_inode(sb);
 	if (!inode)
 		goto out;
 
-	/* A directory is always one deeper than it's parent */
-	dir_ei = PROC_I(dir);
-	depth = dir_ei->fd + 1;
-
+	sysctl_head_get(head);
 	ei = PROC_I(inode);
-	ei->fd = depth;
+	ei->sysctl = head;
+	ei->sysctl_entry = table;
+
 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-	inode->i_op = &proc_sys_inode_operations;
-	inode->i_fop = &proc_sys_file_operations;
 	inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */
-	proc_sys_refresh_inode(inode, table);
+	inode->i_mode = table->mode;
+	if (!table->child) {
+		inode->i_mode |= S_IFREG;
+		inode->i_op = &proc_sys_inode_operations;
+		inode->i_fop = &proc_sys_file_operations;
+	} else {
+		inode->i_mode |= S_IFDIR;
+		inode->i_nlink = 0;
+		inode->i_op = &proc_sys_dir_operations;
+		inode->i_fop = &proc_sys_dir_file_operations;
+	}
 out:
 	return inode;
 }
 
-static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth)
-{
-	for (;;) {
-		struct proc_inode *ei;
-
-		ei = PROC_I(dentry->d_inode);
-		if (ei->fd == depth)
-			break; /* found */
-
-		dentry = dentry->d_parent;
-	}
-	return dentry;
-}
-
-static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table,
-							struct qstr *name)
+static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name)
 {
 	int len;
-	for ( ; table->ctl_name || table->procname; table++) {
+	for ( ; p->ctl_name || p->procname; p++) {
 
-		if (!table->procname)
+		if (!p->procname)
 			continue;
 
-		len = strlen(table->procname);
+		len = strlen(p->procname);
 		if (len != name->len)
 			continue;
 
-		if (memcmp(table->procname, name->name, len) != 0)
+		if (memcmp(p->procname, name->name, len) != 0)
 			continue;
 
 		/* I have a match */
-		return table;
+		return p;
 	}
 	return NULL;
 }
 
-static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,
-						struct ctl_table *table)
+struct ctl_table_header *grab_header(struct inode *inode)
 {
-	struct dentry *ancestor;
-	struct proc_inode *ei;
-	int depth, i;
-
-	ei = PROC_I(dentry->d_inode);
-	depth = ei->fd;
-
-	if (depth == 0)
-		return table;
-
-	for (i = 1; table && (i <= depth); i++) {
-		ancestor = proc_sys_ancestor(dentry, i);
-		table = proc_sys_lookup_table_one(table, &ancestor->d_name);
-		if (table)
-			table = table->child;
-	}
-	return table;
-
-}
-static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,
-						struct qstr *name,
-						struct ctl_table *table)
-{
-	table = proc_sys_lookup_table(dparent, table);
-	if (table)
-		table = proc_sys_lookup_table_one(table, name);
-	return table;
-}
-
-static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,
-						struct qstr *name,
-						struct ctl_table_header **ptr)
-{
-	struct ctl_table_header *head;
-	struct ctl_table *table = NULL;
-
-	for (head = sysctl_head_next(NULL); head;
-			head = sysctl_head_next(head)) {
-		table = proc_sys_lookup_entry(parent, name, head->ctl_table);
-		if (table)
-			break;
-	}
-	*ptr = head;
-	return table;
+	if (PROC_I(inode)->sysctl)
+		return sysctl_head_grab(PROC_I(inode)->sysctl);
+	else
+		return sysctl_head_next(NULL);
 }
 
 static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
 					struct nameidata *nd)
 {
-	struct ctl_table_header *head;
+	struct ctl_table_header *head = grab_header(dir);
+	struct ctl_table *table = PROC_I(dir)->sysctl_entry;
+	struct ctl_table_header *h = NULL;
+	struct qstr *name = &dentry->d_name;
+	struct ctl_table *p;
 	struct inode *inode;
-	struct dentry *err;
-	struct ctl_table *table;
+	struct dentry *err = ERR_PTR(-ENOENT);
 
-	err = ERR_PTR(-ENOENT);
-	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
-	if (!table)
+	if (IS_ERR(head))
+		return ERR_CAST(head);
+
+	if (table && !table->child) {
+		WARN_ON(1);
+		goto out;
+	}
+
+	table = table ? table->child : head->ctl_table;
+
+	p = find_in_table(table, name);
+	if (!p) {
+		for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) {
+			if (h->attached_to != table)
+				continue;
+			p = find_in_table(h->attached_by, name);
+			if (p)
+				break;
+		}
+	}
+
+	if (!p)
 		goto out;
 
 	err = ERR_PTR(-ENOMEM);
-	inode = proc_sys_make_inode(dir, table);
+	inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
+	if (h)
+		sysctl_head_finish(h);
+
 	if (!inode)
 		goto out;
 
@@ -168,22 +129,14 @@
 static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
 		size_t count, loff_t *ppos, int write)
 {
-	struct dentry *dentry = filp->f_dentry;
-	struct ctl_table_header *head;
-	struct ctl_table *table;
+	struct inode *inode = filp->f_path.dentry->d_inode;
+	struct ctl_table_header *head = grab_header(inode);
+	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
 	ssize_t error;
 	size_t res;
 
-	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
-	/* Has the sysctl entry disappeared on us? */
-	error = -ENOENT;
-	if (!table)
-		goto out;
-
-	/* Has the sysctl entry been replaced by a directory? */
-	error = -EISDIR;
-	if (!table->proc_handler)
-		goto out;
+	if (IS_ERR(head))
+		return PTR_ERR(head);
 
 	/*
 	 * At this point we know that the sysctl was not unregistered
@@ -193,6 +146,11 @@
 	if (sysctl_perm(head->root, table, write ? MAY_WRITE : MAY_READ))
 		goto out;
 
+	/* if that can happen at all, it should be -EINVAL, not -EISDIR */
+	error = -EINVAL;
+	if (!table->proc_handler)
+		goto out;
+
 	/* careful: calling conventions are nasty here */
 	res = count;
 	error = table->proc_handler(table, write, filp, buf, &res, ppos);
@@ -218,82 +176,86 @@
 
 
 static int proc_sys_fill_cache(struct file *filp, void *dirent,
-				filldir_t filldir, struct ctl_table *table)
+				filldir_t filldir,
+				struct ctl_table_header *head,
+				struct ctl_table *table)
 {
-	struct ctl_table_header *head;
-	struct ctl_table *child_table = NULL;
 	struct dentry *child, *dir = filp->f_path.dentry;
 	struct inode *inode;
 	struct qstr qname;
 	ino_t ino = 0;
 	unsigned type = DT_UNKNOWN;
-	int ret;
 
 	qname.name = table->procname;
 	qname.len  = strlen(table->procname);
 	qname.hash = full_name_hash(qname.name, qname.len);
 
-	/* Suppress duplicates.
-	 * Only fill a directory entry if it is the value that
-	 * an ordinary lookup of that name returns.  Hide all
-	 * others.
-	 *
-	 * If we ever cache this translation in the dcache
-	 * I should do a dcache lookup first.  But for now
-	 * it is just simpler not to.
-	 */
-	ret = 0;
-	child_table = do_proc_sys_lookup(dir, &qname, &head);
-	sysctl_head_finish(head);
-	if (child_table != table)
-		return 0;
-
 	child = d_lookup(dir, &qname);
 	if (!child) {
-		struct dentry *new;
-		new = d_alloc(dir, &qname);
-		if (new) {
-			inode = proc_sys_make_inode(dir->d_inode, table);
-			if (!inode)
-				child = ERR_PTR(-ENOMEM);
-			else {
-				new->d_op = &proc_sys_dentry_operations;
-				d_add(new, inode);
+		child = d_alloc(dir, &qname);
+		if (child) {
+			inode = proc_sys_make_inode(dir->d_sb, head, table);
+			if (!inode) {
+				dput(child);
+				return -ENOMEM;
+			} else {
+				child->d_op = &proc_sys_dentry_operations;
+				d_add(child, inode);
 			}
-			if (child)
-				dput(new);
-			else
-				child = new;
+		} else {
+			return -ENOMEM;
 		}
 	}
-	if (!child || IS_ERR(child) || !child->d_inode)
-		goto end_instantiate;
 	inode = child->d_inode;
-	if (inode) {
-		ino  = inode->i_ino;
-		type = inode->i_mode >> 12;
-	}
+	ino  = inode->i_ino;
+	type = inode->i_mode >> 12;
 	dput(child);
-end_instantiate:
-	if (!ino)
-		ino= find_inode_number(dir, &qname);
-	if (!ino)
-		ino = 1;
-	return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
+	return !!filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
+}
+
+static int scan(struct ctl_table_header *head, ctl_table *table,
+		unsigned long *pos, struct file *file,
+		void *dirent, filldir_t filldir)
+{
+
+	for (; table->ctl_name || table->procname; table++, (*pos)++) {
+		int res;
+
+		/* Can't do anything without a proc name */
+		if (!table->procname)
+			continue;
+
+		if (*pos < file->f_pos)
+			continue;
+
+		res = proc_sys_fill_cache(file, dirent, filldir, head, table);
+		if (res)
+			return res;
+
+		file->f_pos = *pos + 1;
+	}
+	return 0;
 }
 
 static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	struct dentry *dentry = filp->f_dentry;
+	struct dentry *dentry = filp->f_path.dentry;
 	struct inode *inode = dentry->d_inode;
-	struct ctl_table_header *head = NULL;
-	struct ctl_table *table;
+	struct ctl_table_header *head = grab_header(inode);
+	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+	struct ctl_table_header *h = NULL;
 	unsigned long pos;
-	int ret;
+	int ret = -EINVAL;
 
-	ret = -ENOTDIR;
-	if (!S_ISDIR(inode->i_mode))
+	if (IS_ERR(head))
+		return PTR_ERR(head);
+
+	if (table && !table->child) {
+		WARN_ON(1);
 		goto out;
+	}
+
+	table = table ? table->child : head->ctl_table;
 
 	ret = 0;
 	/* Avoid a switch here: arm builds fail with missing __cmpdi2 */
@@ -311,30 +273,17 @@
 	}
 	pos = 2;
 
-	/* - Find each instance of the directory
-	 * - Read all entries in each instance
-	 * - Before returning an entry to user space lookup the entry
-	 *   by name and if I find a different entry don't return
-	 *   this one because it means it is a buried dup.
-	 * For sysctl this should only happen for directory entries.
-	 */
-	for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
-		table = proc_sys_lookup_table(dentry, head->ctl_table);
+	ret = scan(head, table, &pos, filp, dirent, filldir);
+	if (ret)
+		goto out;
 
-		if (!table)
+	for (h = sysctl_head_next(NULL); h; h = sysctl_head_next(h)) {
+		if (h->attached_to != table)
 			continue;
-
-		for (; table->ctl_name || table->procname; table++, pos++) {
-			/* Can't do anything without a proc name */
-			if (!table->procname)
-				continue;
-
-			if (pos < filp->f_pos)
-				continue;
-
-			if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)
-				goto out;
-			filp->f_pos = pos + 1;
+		ret = scan(h, h->attached_by, &pos, filp, dirent, filldir);
+		if (ret) {
+			sysctl_head_finish(h);
+			break;
 		}
 	}
 	ret = 1;
@@ -343,53 +292,24 @@
 	return ret;
 }
 
-static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
+static int proc_sys_permission(struct inode *inode, int mask)
 {
 	/*
 	 * sysctl entries that are not writeable,
 	 * are _NOT_ writeable, capabilities or not.
 	 */
-	struct ctl_table_header *head;
-	struct ctl_table *table;
-	struct dentry *dentry;
-	int mode;
-	int depth;
+	struct ctl_table_header *head = grab_header(inode);
+	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
 	int error;
 
-	head = NULL;
-	depth = PROC_I(inode)->fd;
+	if (IS_ERR(head))
+		return PTR_ERR(head);
 
-	/* First check the cached permissions, in case we don't have
-	 * enough information to lookup the sysctl table entry.
-	 */
-	error = -EACCES;
-	mode = inode->i_mode;
+	if (!table) /* global root - r-xr-xr-x */
+		error = mask & MAY_WRITE ? -EACCES : 0;
+	else /* Use the permissions on the sysctl table entry */
+		error = sysctl_perm(head->root, table, mask);
 
-	if (current->euid == 0)
-		mode >>= 6;
-	else if (in_group_p(0))
-		mode >>= 3;
-
-	if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
-		error = 0;
-
-	/* If we can't get a sysctl table entry the permission
-	 * checks on the cached mode will have to be enough.
-	 */
-	if (!nd || !depth)
-		goto out;
-
-	dentry = nd->path.dentry;
-	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
-
-	/* If the entry does not exist deny permission */
-	error = -EACCES;
-	if (!table)
-		goto out;
-
-	/* Use the permissions on the sysctl table entry */
-	error = sysctl_perm(head->root, table, mask);
-out:
 	sysctl_head_finish(head);
 	return error;
 }
@@ -409,33 +329,70 @@
 	return error;
 }
 
-/* I'm lazy and don't distinguish between files and directories,
- * until access time.
- */
+static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+	struct inode *inode = dentry->d_inode;
+	struct ctl_table_header *head = grab_header(inode);
+	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+
+	if (IS_ERR(head))
+		return PTR_ERR(head);
+
+	generic_fillattr(inode, stat);
+	if (table)
+		stat->mode = (stat->mode & S_IFMT) | table->mode;
+
+	sysctl_head_finish(head);
+	return 0;
+}
+
 static const struct file_operations proc_sys_file_operations = {
 	.read		= proc_sys_read,
 	.write		= proc_sys_write,
+};
+
+static const struct file_operations proc_sys_dir_file_operations = {
 	.readdir	= proc_sys_readdir,
 };
 
 static const struct inode_operations proc_sys_inode_operations = {
+	.permission	= proc_sys_permission,
+	.setattr	= proc_sys_setattr,
+	.getattr	= proc_sys_getattr,
+};
+
+static const struct inode_operations proc_sys_dir_operations = {
 	.lookup		= proc_sys_lookup,
 	.permission	= proc_sys_permission,
 	.setattr	= proc_sys_setattr,
+	.getattr	= proc_sys_getattr,
 };
 
 static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
 {
-	struct ctl_table_header *head;
-	struct ctl_table *table;
-	table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
-	proc_sys_refresh_inode(dentry->d_inode, table);
-	sysctl_head_finish(head);
-	return !!table;
+	return !PROC_I(dentry->d_inode)->sysctl->unregistering;
+}
+
+static int proc_sys_delete(struct dentry *dentry)
+{
+	return !!PROC_I(dentry->d_inode)->sysctl->unregistering;
+}
+
+static int proc_sys_compare(struct dentry *dir, struct qstr *qstr,
+			    struct qstr *name)
+{
+	struct dentry *dentry = container_of(qstr, struct dentry, d_name);
+	if (qstr->len != name->len)
+		return 1;
+	if (memcmp(qstr->name, name->name, name->len))
+		return 1;
+	return !sysctl_is_seen(PROC_I(dentry->d_inode)->sysctl);
 }
 
 static struct dentry_operations proc_sys_dentry_operations = {
 	.d_revalidate	= proc_sys_revalidate,
+	.d_delete	= proc_sys_delete,
+	.d_compare	= proc_sys_compare,
 };
 
 static struct proc_dir_entry *proc_sys_root;
@@ -443,8 +400,8 @@
 int proc_sys_init(void)
 {
 	proc_sys_root = proc_mkdir("sys", NULL);
-	proc_sys_root->proc_iops = &proc_sys_inode_operations;
-	proc_sys_root->proc_fops = &proc_sys_file_operations;
+	proc_sys_root->proc_iops = &proc_sys_dir_operations;
+	proc_sys_root->proc_fops = &proc_sys_dir_file_operations;
 	proc_sys_root->nlink = 0;
 	return 0;
 }
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index d7c4935..bb3cb5b 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -1250,7 +1250,7 @@
 	return error;
 }
 
-int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int reiserfs_permission(struct inode *inode, int mask)
 {
 	/*
 	 * We don't do permission checks on the internal objects.
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index 2294783..e4f8d51 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -408,7 +408,7 @@
  * privileges, so we need our own check for this.
  */
 static int
-smb_file_permission(struct inode *inode, int mask, struct nameidata *nd)
+smb_file_permission(struct inode *inode, int mask)
 {
 	int mode = inode->i_mode;
 	int error = 0;
@@ -417,7 +417,7 @@
 
 	/* Look at user permissions */
 	mode >>= 6;
-	if ((mode & 7 & mask) != mask)
+	if (mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC))
 		error = -EACCES;
 	return error;
 }
diff --git a/fs/splice.c b/fs/splice.c
index 47dc1a4..b30311b 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -772,7 +772,7 @@
 	ssize_t ret;
 	int err;
 
-	err = remove_suid(out->f_path.dentry);
+	err = file_remove_suid(out);
 	if (unlikely(err))
 		return err;
 
@@ -830,7 +830,7 @@
 	ssize_t ret;
 
 	inode_double_lock(inode, pipe->inode);
-	ret = remove_suid(out->f_path.dentry);
+	ret = file_remove_suid(out);
 	if (likely(!ret))
 		ret = __splice_from_pipe(pipe, &sd, pipe_to_file);
 	inode_double_unlock(inode, pipe->inode);
diff --git a/fs/stat.c b/fs/stat.c
index 9cf41f7..7c46fbe 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -57,13 +57,13 @@
 
 int vfs_stat_fd(int dfd, char __user *name, struct kstat *stat)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = __user_walk_fd(dfd, name, LOOKUP_FOLLOW, &nd);
+	error = user_path_at(dfd, name, LOOKUP_FOLLOW, &path);
 	if (!error) {
-		error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat);
-		path_put(&nd.path);
+		error = vfs_getattr(path.mnt, path.dentry, stat);
+		path_put(&path);
 	}
 	return error;
 }
@@ -77,13 +77,13 @@
 
 int vfs_lstat_fd(int dfd, char __user *name, struct kstat *stat)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = __user_walk_fd(dfd, name, 0, &nd);
+	error = user_path_at(dfd, name, 0, &path);
 	if (!error) {
-		error = vfs_getattr(nd.path.mnt, nd.path.dentry, stat);
-		path_put(&nd.path);
+		error = vfs_getattr(path.mnt, path.dentry, stat);
+		path_put(&path);
 	}
 	return error;
 }
@@ -291,29 +291,29 @@
 	return error;
 }
 
-asmlinkage long sys_readlinkat(int dfd, const char __user *path,
+asmlinkage long sys_readlinkat(int dfd, const char __user *pathname,
 				char __user *buf, int bufsiz)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
 	if (bufsiz <= 0)
 		return -EINVAL;
 
-	error = __user_walk_fd(dfd, path, 0, &nd);
+	error = user_path_at(dfd, pathname, 0, &path);
 	if (!error) {
-		struct inode *inode = nd.path.dentry->d_inode;
+		struct inode *inode = path.dentry->d_inode;
 
 		error = -EINVAL;
 		if (inode->i_op && inode->i_op->readlink) {
-			error = security_inode_readlink(nd.path.dentry);
+			error = security_inode_readlink(path.dentry);
 			if (!error) {
-				touch_atime(nd.path.mnt, nd.path.dentry);
-				error = inode->i_op->readlink(nd.path.dentry,
+				touch_atime(path.mnt, path.dentry);
+				error = inode->i_op->readlink(path.dentry,
 							      buf, bufsiz);
 			}
 		}
-		path_put(&nd.path);
+		path_put(&path);
 	}
 	return error;
 }
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 005a3b8..8565e58 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -53,6 +53,7 @@
 
 #include "ubifs.h"
 #include <linux/mount.h>
+#include <linux/namei.h>
 
 static int read_block(struct inode *inode, void *addr, unsigned int block,
 		      struct ubifs_data_node *dn)
diff --git a/fs/utimes.c b/fs/utimes.c
index b6b664e..6929e3e 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -48,66 +48,22 @@
 	return nsec >= 0 && nsec <= 999999999;
 }
 
-/* If times==NULL, set access and modification to current time,
- * must be owner or have write permission.
- * Else, update from *times, must be owner or super user.
- */
-long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
+static int utimes_common(struct path *path, struct timespec *times)
 {
 	int error;
-	struct nameidata nd;
-	struct dentry *dentry;
-	struct inode *inode;
 	struct iattr newattrs;
-	struct file *f = NULL;
-	struct vfsmount *mnt;
+	struct inode *inode = path->dentry->d_inode;
 
-	error = -EINVAL;
-	if (times && (!nsec_valid(times[0].tv_nsec) ||
-		      !nsec_valid(times[1].tv_nsec))) {
-		goto out;
-	}
-
-	if (flags & ~AT_SYMLINK_NOFOLLOW)
-		goto out;
-
-	if (filename == NULL && dfd != AT_FDCWD) {
-		error = -EINVAL;
-		if (flags & AT_SYMLINK_NOFOLLOW)
-			goto out;
-
-		error = -EBADF;
-		f = fget(dfd);
-		if (!f)
-			goto out;
-		dentry = f->f_path.dentry;
-		mnt = f->f_path.mnt;
-	} else {
-		error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
-		if (error)
-			goto out;
-
-		dentry = nd.path.dentry;
-		mnt = nd.path.mnt;
-	}
-
-	inode = dentry->d_inode;
-
-	error = mnt_want_write(mnt);
+	error = mnt_want_write(path->mnt);
 	if (error)
-		goto dput_and_out;
+		goto out;
 
 	if (times && times[0].tv_nsec == UTIME_NOW &&
 		     times[1].tv_nsec == UTIME_NOW)
 		times = NULL;
 
-	/* In most cases, the checks are done in inode_change_ok() */
 	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
 	if (times) {
-		error = -EPERM;
-                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-			goto mnt_drop_write_and_out;
-
 		if (times[0].tv_nsec == UTIME_OMIT)
 			newattrs.ia_valid &= ~ATTR_ATIME;
 		else if (times[0].tv_nsec != UTIME_NOW) {
@@ -123,21 +79,13 @@
 			newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
 			newattrs.ia_valid |= ATTR_MTIME_SET;
 		}
-
 		/*
-		 * For the UTIME_OMIT/UTIME_NOW and UTIME_NOW/UTIME_OMIT
-		 * cases, we need to make an extra check that is not done by
-		 * inode_change_ok().
+		 * Tell inode_change_ok(), that this is an explicit time
+		 * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET
+		 * were used.
 		 */
-		if (((times[0].tv_nsec == UTIME_NOW &&
-			    times[1].tv_nsec == UTIME_OMIT)
-		     ||
-		     (times[0].tv_nsec == UTIME_OMIT &&
-			    times[1].tv_nsec == UTIME_NOW))
-		    && !is_owner_or_cap(inode))
-			goto mnt_drop_write_and_out;
+		newattrs.ia_valid |= ATTR_TIMES_SET;
 	} else {
-
 		/*
 		 * If times is NULL (or both times are UTIME_NOW),
 		 * then we need to check permissions, because
@@ -148,21 +96,76 @@
 			goto mnt_drop_write_and_out;
 
 		if (!is_owner_or_cap(inode)) {
-			error = permission(inode, MAY_WRITE, NULL);
+			error = inode_permission(inode, MAY_WRITE);
 			if (error)
 				goto mnt_drop_write_and_out;
 		}
 	}
 	mutex_lock(&inode->i_mutex);
-	error = notify_change(dentry, &newattrs);
+	error = notify_change(path->dentry, &newattrs);
 	mutex_unlock(&inode->i_mutex);
+
 mnt_drop_write_and_out:
-	mnt_drop_write(mnt);
-dput_and_out:
-	if (f)
-		fput(f);
-	else
-		path_put(&nd.path);
+	mnt_drop_write(path->mnt);
+out:
+	return error;
+}
+
+/*
+ * do_utimes - change times on filename or file descriptor
+ * @dfd: open file descriptor, -1 or AT_FDCWD
+ * @filename: path name or NULL
+ * @times: new times or NULL
+ * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment)
+ *
+ * If filename is NULL and dfd refers to an open file, then operate on
+ * the file.  Otherwise look up filename, possibly using dfd as a
+ * starting point.
+ *
+ * If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
+{
+	int error = -EINVAL;
+
+	if (times && (!nsec_valid(times[0].tv_nsec) ||
+		      !nsec_valid(times[1].tv_nsec))) {
+		goto out;
+	}
+
+	if (flags & ~AT_SYMLINK_NOFOLLOW)
+		goto out;
+
+	if (filename == NULL && dfd != AT_FDCWD) {
+		struct file *file;
+
+		if (flags & AT_SYMLINK_NOFOLLOW)
+			goto out;
+
+		file = fget(dfd);
+		error = -EBADF;
+		if (!file)
+			goto out;
+
+		error = utimes_common(&file->f_path, times);
+		fput(file);
+	} else {
+		struct path path;
+		int lookup_flags = 0;
+
+		if (!(flags & AT_SYMLINK_NOFOLLOW))
+			lookup_flags |= LOOKUP_FOLLOW;
+
+		error = user_path_at(dfd, filename, lookup_flags, &path);
+		if (error)
+			goto out;
+
+		error = utimes_common(&path, times);
+		path_put(&path);
+	}
+
 out:
 	return error;
 }
diff --git a/fs/xattr.c b/fs/xattr.c
index 4706a8b..468377e 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -63,7 +63,7 @@
 			return -EPERM;
 	}
 
-	return permission(inode, mask, NULL);
+	return inode_permission(inode, mask);
 }
 
 int
@@ -252,40 +252,40 @@
 }
 
 asmlinkage long
-sys_setxattr(const char __user *path, const char __user *name,
+sys_setxattr(const char __user *pathname, const char __user *name,
 	     const void __user *value, size_t size, int flags)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (error)
 		return error;
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = setxattr(nd.path.dentry, name, value, size, flags);
-		mnt_drop_write(nd.path.mnt);
+		error = setxattr(path.dentry, name, value, size, flags);
+		mnt_drop_write(path.mnt);
 	}
-	path_put(&nd.path);
+	path_put(&path);
 	return error;
 }
 
 asmlinkage long
-sys_lsetxattr(const char __user *path, const char __user *name,
+sys_lsetxattr(const char __user *pathname, const char __user *name,
 	      const void __user *value, size_t size, int flags)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk_link(path, &nd);
+	error = user_lpath(pathname, &path);
 	if (error)
 		return error;
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = setxattr(nd.path.dentry, name, value, size, flags);
-		mnt_drop_write(nd.path.mnt);
+		error = setxattr(path.dentry, name, value, size, flags);
+		mnt_drop_write(path.mnt);
 	}
-	path_put(&nd.path);
+	path_put(&path);
 	return error;
 }
 
@@ -350,32 +350,32 @@
 }
 
 asmlinkage ssize_t
-sys_getxattr(const char __user *path, const char __user *name,
+sys_getxattr(const char __user *pathname, const char __user *name,
 	     void __user *value, size_t size)
 {
-	struct nameidata nd;
+	struct path path;
 	ssize_t error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (error)
 		return error;
-	error = getxattr(nd.path.dentry, name, value, size);
-	path_put(&nd.path);
+	error = getxattr(path.dentry, name, value, size);
+	path_put(&path);
 	return error;
 }
 
 asmlinkage ssize_t
-sys_lgetxattr(const char __user *path, const char __user *name, void __user *value,
+sys_lgetxattr(const char __user *pathname, const char __user *name, void __user *value,
 	      size_t size)
 {
-	struct nameidata nd;
+	struct path path;
 	ssize_t error;
 
-	error = user_path_walk_link(path, &nd);
+	error = user_lpath(pathname, &path);
 	if (error)
 		return error;
-	error = getxattr(nd.path.dentry, name, value, size);
-	path_put(&nd.path);
+	error = getxattr(path.dentry, name, value, size);
+	path_put(&path);
 	return error;
 }
 
@@ -425,30 +425,30 @@
 }
 
 asmlinkage ssize_t
-sys_listxattr(const char __user *path, char __user *list, size_t size)
+sys_listxattr(const char __user *pathname, char __user *list, size_t size)
 {
-	struct nameidata nd;
+	struct path path;
 	ssize_t error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (error)
 		return error;
-	error = listxattr(nd.path.dentry, list, size);
-	path_put(&nd.path);
+	error = listxattr(path.dentry, list, size);
+	path_put(&path);
 	return error;
 }
 
 asmlinkage ssize_t
-sys_llistxattr(const char __user *path, char __user *list, size_t size)
+sys_llistxattr(const char __user *pathname, char __user *list, size_t size)
 {
-	struct nameidata nd;
+	struct path path;
 	ssize_t error;
 
-	error = user_path_walk_link(path, &nd);
+	error = user_lpath(pathname, &path);
 	if (error)
 		return error;
-	error = listxattr(nd.path.dentry, list, size);
-	path_put(&nd.path);
+	error = listxattr(path.dentry, list, size);
+	path_put(&path);
 	return error;
 }
 
@@ -486,38 +486,38 @@
 }
 
 asmlinkage long
-sys_removexattr(const char __user *path, const char __user *name)
+sys_removexattr(const char __user *pathname, const char __user *name)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk(path, &nd);
+	error = user_path(pathname, &path);
 	if (error)
 		return error;
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = removexattr(nd.path.dentry, name);
-		mnt_drop_write(nd.path.mnt);
+		error = removexattr(path.dentry, name);
+		mnt_drop_write(path.mnt);
 	}
-	path_put(&nd.path);
+	path_put(&path);
 	return error;
 }
 
 asmlinkage long
-sys_lremovexattr(const char __user *path, const char __user *name)
+sys_lremovexattr(const char __user *pathname, const char __user *name)
 {
-	struct nameidata nd;
+	struct path path;
 	int error;
 
-	error = user_path_walk_link(path, &nd);
+	error = user_lpath(pathname, &path);
 	if (error)
 		return error;
-	error = mnt_want_write(nd.path.mnt);
+	error = mnt_want_write(path.mnt);
 	if (!error) {
-		error = removexattr(nd.path.dentry, name);
-		mnt_drop_write(nd.path.mnt);
+		error = removexattr(path.dentry, name);
+		mnt_drop_write(path.mnt);
 	}
-	path_put(&nd.path);
+	path_put(&path);
 	return error;
 }
 
diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c
index a42ba9d..01939ba 100644
--- a/fs/xfs/linux-2.6/xfs_ioctl.c
+++ b/fs/xfs/linux-2.6/xfs_ioctl.c
@@ -84,17 +84,15 @@
 	switch (cmd) {
 	case XFS_IOC_PATH_TO_FSHANDLE:
 	case XFS_IOC_PATH_TO_HANDLE: {
-		struct nameidata	nd;
-		int			error;
-
-		error = user_path_walk_link((const char __user *)hreq.path, &nd);
+		struct path path;
+		int error = user_lpath((const char __user *)hreq.path, &path);
 		if (error)
 			return error;
 
-		ASSERT(nd.path.dentry);
-		ASSERT(nd.path.dentry->d_inode);
-		inode = igrab(nd.path.dentry->d_inode);
-		path_put(&nd.path);
+		ASSERT(path.dentry);
+		ASSERT(path.dentry->d_inode);
+		inode = igrab(path.dentry->d_inode);
+		path_put(&path);
 		break;
 	}
 
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c
index 2bf287e..5fc61c8 100644
--- a/fs/xfs/linux-2.6/xfs_iops.c
+++ b/fs/xfs/linux-2.6/xfs_iops.c
@@ -589,8 +589,7 @@
 STATIC int
 xfs_vn_permission(
 	struct inode		*inode,
-	int			mask,
-	struct nameidata	*nd)
+	int			mask)
 {
 	return generic_permission(inode, mask, xfs_check_acl);
 }
diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c
index 5e3b575..82333b3 100644
--- a/fs/xfs/linux-2.6/xfs_lrw.c
+++ b/fs/xfs/linux-2.6/xfs_lrw.c
@@ -711,7 +711,7 @@
 	     !capable(CAP_FSETID)) {
 		error = xfs_write_clear_setuid(xip);
 		if (likely(!error))
-			error = -remove_suid(file->f_path.dentry);
+			error = -file_remove_suid(file);
 		if (unlikely(error)) {
 			goto out_unlock_internal;
 		}
diff --git a/include/asm-alpha/namei.h b/include/asm-alpha/namei.h
deleted file mode 100644
index 5cc9bb3..0000000
--- a/include/asm-alpha/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $
- * linux/include/asm-alpha/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ALPHA_NAMEI_H
-#define __ALPHA_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __ALPHA_NAMEI_H */
diff --git a/include/asm-arm/namei.h b/include/asm-arm/namei.h
deleted file mode 100644
index a402d3b..0000000
--- a/include/asm-arm/namei.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* 
- * linux/include/asm-arm/namei.h
- *
- * Routines to handle famous /usr/gnemul
- * Derived from the Sparc version of this file
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ASMARM_NAMEI_H
-#define __ASMARM_NAMEI_H
-
-#define ARM_BSD_EMUL "usr/gnemul/bsd/"
-
-static inline char *__emul_prefix(void)
-{
-	switch (current->personality) {
-	case PER_BSD:
-		return ARM_BSD_EMUL;
-	default:
-		return NULL;
-	}
-}
-
-#endif /* __ASMARM_NAMEI_H */
diff --git a/include/asm-avr32/namei.h b/include/asm-avr32/namei.h
deleted file mode 100644
index f0a26de..0000000
--- a/include/asm-avr32/namei.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_AVR32_NAMEI_H
-#define __ASM_AVR32_NAMEI_H
-
-/* This dummy routine may be changed to something useful */
-#define __emul_prefix() NULL
-
-#endif /* __ASM_AVR32_NAMEI_H */
diff --git a/include/asm-blackfin/namei.h b/include/asm-blackfin/namei.h
deleted file mode 100644
index 8b89a2d..0000000
--- a/include/asm-blackfin/namei.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * linux/include/asm/namei.h
- *
- * Included from linux/fs/namei.c
- *
- * Changes made by Lineo Inc.    May 2001
- */
-
-#ifndef __BFIN_NAMEI_H
-#define __BFIN_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
diff --git a/include/asm-cris/namei.h b/include/asm-cris/namei.h
deleted file mode 100644
index 8a3be7a..0000000
--- a/include/asm-cris/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $
- * linux/include/asm-cris/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __CRIS_NAMEI_H
-#define __CRIS_NAMEI_H
-
-/* used to find file-system prefixes for doing emulations
- * see for example asm-sparc/namei.h
- * we don't use it...
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __CRIS_NAMEI_H */
diff --git a/include/asm-frv/namei.h b/include/asm-frv/namei.h
deleted file mode 100644
index 4ea5717..0000000
--- a/include/asm-frv/namei.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * include/asm-frv/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ASM_NAMEI_H
-#define __ASM_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
-
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index a3034d2..c764a8f 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -13,7 +13,7 @@
  *
  * While the GPIO programming interface defines valid GPIO numbers
  * to be in the range 0..MAX_INT, this library restricts them to the
- * smaller range 0..ARCH_NR_GPIOS.
+ * smaller range 0..ARCH_NR_GPIOS-1.
  */
 
 #ifndef ARCH_NR_GPIOS
diff --git a/include/asm-h8300/namei.h b/include/asm-h8300/namei.h
deleted file mode 100644
index ab6f196..0000000
--- a/include/asm-h8300/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * linux/include/asm-h8300/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __H8300_NAMEI_H
-#define __H8300_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
diff --git a/include/asm-ia64/namei.h b/include/asm-ia64/namei.h
deleted file mode 100644
index 78e7680..0000000
--- a/include/asm-ia64/namei.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef _ASM_IA64_NAMEI_H
-#define _ASM_IA64_NAMEI_H
-
-/*
- * Modified 1998, 1999, 2001
- *	David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
- */
-
-#include <asm/ptrace.h>
-#include <asm/system.h>
-
-#define EMUL_PREFIX_LINUX_IA32 "/emul/ia32-linux/"
-
-static inline char *
-__emul_prefix (void)
-{
-	switch (current->personality) {
-	      case PER_LINUX32:
-		return EMUL_PREFIX_LINUX_IA32;
-	      default:
-		return NULL;
-	}
-}
-
-#endif /* _ASM_IA64_NAMEI_H */
diff --git a/include/asm-m32r/namei.h b/include/asm-m32r/namei.h
deleted file mode 100644
index 210f805..0000000
--- a/include/asm-m32r/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef _ASM_M32R_NAMEI_H
-#define _ASM_M32R_NAMEI_H
-
-/*
- * linux/include/asm-m32r/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_M32R_NAMEI_H */
diff --git a/include/asm-m68k/namei.h b/include/asm-m68k/namei.h
deleted file mode 100644
index f33f243..0000000
--- a/include/asm-m68k/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * linux/include/asm-m68k/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __M68K_NAMEI_H
-#define __M68K_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif
diff --git a/include/asm-m68knommu/namei.h b/include/asm-m68knommu/namei.h
deleted file mode 100644
index 31a85d2..0000000
--- a/include/asm-m68knommu/namei.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-m68k/namei.h>
diff --git a/include/asm-mips/namei.h b/include/asm-mips/namei.h
deleted file mode 100644
index a6605a7..0000000
--- a/include/asm-mips/namei.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _ASM_NAMEI_H
-#define _ASM_NAMEI_H
-
-/*
- * This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_NAMEI_H */
diff --git a/include/asm-mn10300/namei.h b/include/asm-mn10300/namei.h
deleted file mode 100644
index bd9ce94..0000000
--- a/include/asm-mn10300/namei.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Emulation stuff
- *
- * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
- */
-
-#ifndef _ASM_NAMEI_H
-#define _ASM_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_NAMEI_H */
diff --git a/include/asm-parisc/namei.h b/include/asm-parisc/namei.h
deleted file mode 100644
index 8d29b3d..0000000
--- a/include/asm-parisc/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.1 1996/12/13 14:48:21 jj Exp $
- * linux/include/asm-parisc/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __PARISC_NAMEI_H
-#define __PARISC_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __PARISC_NAMEI_H */
diff --git a/include/asm-powerpc/namei.h b/include/asm-powerpc/namei.h
deleted file mode 100644
index 6574434..0000000
--- a/include/asm-powerpc/namei.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _ASM_POWERPC_NAMEI_H
-#define _ASM_POWERPC_NAMEI_H
-
-#ifdef __KERNEL__
-
-/*
- * Adapted from include/asm-alpha/namei.h
- *
- * Included from fs/namei.c
- */
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif	/* __KERNEL__ */
-#endif	/* _ASM_POWERPC_NAMEI_H */
diff --git a/include/asm-s390/namei.h b/include/asm-s390/namei.h
deleted file mode 100644
index 3e286bd..0000000
--- a/include/asm-s390/namei.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- *  include/asm-s390/namei.h
- *
- *  S390 version
- *
- *  Derived from "include/asm-i386/namei.h"
- *
- *  Included from linux/fs/namei.c
- */
-
-#ifndef __S390_NAMEI_H
-#define __S390_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __S390_NAMEI_H */
diff --git a/include/asm-sh/namei.h b/include/asm-sh/namei.h
deleted file mode 100644
index 338a5d9..0000000
--- a/include/asm-sh/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* $Id: namei.h,v 1.3 2000/07/04 06:24:49 gniibe Exp $
- * linux/include/asm-sh/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __ASM_SH_NAMEI_H
-#define __ASM_SH_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __ASM_SH_NAMEI_H */
diff --git a/include/asm-sparc/namei.h b/include/asm-sparc/namei.h
deleted file mode 100644
index eff944b..0000000
--- a/include/asm-sparc/namei.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef ___ASM_SPARC_NAMEI_H
-#define ___ASM_SPARC_NAMEI_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm-sparc/namei_64.h>
-#else
-#include <asm-sparc/namei_32.h>
-#endif
-#endif
diff --git a/include/asm-sparc/namei_32.h b/include/asm-sparc/namei_32.h
deleted file mode 100644
index 0646102..0000000
--- a/include/asm-sparc/namei_32.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * linux/include/asm-sparc/namei.h
- *
- * Routines to handle famous /usr/gnemul/s*.
- * Included from linux/fs/namei.c
- */
-
-#ifndef __SPARC_NAMEI_H
-#define __SPARC_NAMEI_H
-
-#define __emul_prefix() NULL
-
-#endif /* __SPARC_NAMEI_H */
diff --git a/include/asm-sparc/namei_64.h b/include/asm-sparc/namei_64.h
deleted file mode 100644
index cbc1b4c..0000000
--- a/include/asm-sparc/namei_64.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * linux/include/asm-sparc64/namei.h
- *
- * Routines to handle famous /usr/gnemul/s*.
- * Included from linux/fs/namei.c
- */
-
-#ifndef __SPARC64_NAMEI_H
-#define __SPARC64_NAMEI_H
-
-#define __emul_prefix() NULL
-
-#endif /* __SPARC64_NAMEI_H */
diff --git a/include/asm-sparc64/namei.h b/include/asm-sparc64/namei.h
deleted file mode 100644
index 1344a91..0000000
--- a/include/asm-sparc64/namei.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-sparc/namei.h>
diff --git a/include/asm-um/namei.h b/include/asm-um/namei.h
deleted file mode 100644
index 002984d..0000000
--- a/include/asm-um/namei.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __UM_NAMEI_H
-#define __UM_NAMEI_H
-
-#include "asm/arch/namei.h"
-
-#endif
diff --git a/include/asm-v850/namei.h b/include/asm-v850/namei.h
deleted file mode 100644
index ee8339b..0000000
--- a/include/asm-v850/namei.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * linux/include/asm-v850/namei.h
- *
- * Included from linux/fs/namei.c
- */
-
-#ifndef __V850_NAMEI_H__
-#define __V850_NAMEI_H__
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* __V850_NAMEI_H__ */
diff --git a/include/asm-x86/namei.h b/include/asm-x86/namei.h
deleted file mode 100644
index 415ef5d..0000000
--- a/include/asm-x86/namei.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _ASM_X86_NAMEI_H
-#define _ASM_X86_NAMEI_H
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif /* _ASM_X86_NAMEI_H */
diff --git a/include/asm-xtensa/namei.h b/include/asm-xtensa/namei.h
deleted file mode 100644
index 3fdff03..0000000
--- a/include/asm-xtensa/namei.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * include/asm-xtensa/namei.h
- *
- * Included from linux/fs/namei.c
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
- */
-
-#ifndef _XTENSA_NAMEI_H
-#define _XTENSA_NAMEI_H
-
-#ifdef __KERNEL__
-
-/* This dummy routine maybe changed to something useful
- * for /usr/gnemul/ emulation stuff.
- * Look at asm-sparc/namei.h for details.
- */
-
-#define __emul_prefix() NULL
-
-#endif	/* __KERNEL__ */
-#endif	/* _XTENSA_NAMEI_H */
diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h
index 31b7531..dcc228a 100644
--- a/include/linux/coda_linux.h
+++ b/include/linux/coda_linux.h
@@ -37,7 +37,7 @@
 /* operations shared over more than one file */
 int coda_open(struct inode *i, struct file *f);
 int coda_release(struct inode *i, struct file *f);
-int coda_permission(struct inode *inode, int mask, struct nameidata *nd);
+int coda_permission(struct inode *inode, int mask);
 int coda_revalidate_inode(struct dentry *);
 int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 int coda_setattr(struct dentry *, struct iattr *);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 53d2edb..8252b04 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -60,6 +60,8 @@
 #define MAY_WRITE 2
 #define MAY_READ 4
 #define MAY_APPEND 8
+#define MAY_ACCESS 16
+#define MAY_OPEN 32
 
 #define FMODE_READ 1
 #define FMODE_WRITE 2
@@ -277,7 +279,7 @@
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 #include <linux/dcache.h>
-#include <linux/namei.h>
+#include <linux/path.h>
 #include <linux/stat.h>
 #include <linux/cache.h>
 #include <linux/kobject.h>
@@ -318,22 +320,23 @@
  * Attribute flags.  These should be or-ed together to figure out what
  * has been changed!
  */
-#define ATTR_MODE	1
-#define ATTR_UID	2
-#define ATTR_GID	4
-#define ATTR_SIZE	8
-#define ATTR_ATIME	16
-#define ATTR_MTIME	32
-#define ATTR_CTIME	64
-#define ATTR_ATIME_SET	128
-#define ATTR_MTIME_SET	256
-#define ATTR_FORCE	512	/* Not a change, but a change it */
-#define ATTR_ATTR_FLAG	1024
-#define ATTR_KILL_SUID	2048
-#define ATTR_KILL_SGID	4096
-#define ATTR_FILE	8192
-#define ATTR_KILL_PRIV	16384
-#define ATTR_OPEN	32768	/* Truncating from open(O_TRUNC) */
+#define ATTR_MODE	(1 << 0)
+#define ATTR_UID	(1 << 1)
+#define ATTR_GID	(1 << 2)
+#define ATTR_SIZE	(1 << 3)
+#define ATTR_ATIME	(1 << 4)
+#define ATTR_MTIME	(1 << 5)
+#define ATTR_CTIME	(1 << 6)
+#define ATTR_ATIME_SET	(1 << 7)
+#define ATTR_MTIME_SET	(1 << 8)
+#define ATTR_FORCE	(1 << 9) /* Not a change, but a change it */
+#define ATTR_ATTR_FLAG	(1 << 10)
+#define ATTR_KILL_SUID	(1 << 11)
+#define ATTR_KILL_SGID	(1 << 12)
+#define ATTR_FILE	(1 << 13)
+#define ATTR_KILL_PRIV	(1 << 14)
+#define ATTR_OPEN	(1 << 15) /* Truncating from open(O_TRUNC) */
+#define ATTR_TIMES_SET	(1 << 16)
 
 /*
  * This is the Inode Attributes structure, used for notify_change().  It
@@ -792,7 +795,7 @@
 #define f_dentry	f_path.dentry
 #define f_vfsmnt	f_path.mnt
 	const struct file_operations	*f_op;
-	atomic_t		f_count;
+	atomic_long_t		f_count;
 	unsigned int 		f_flags;
 	mode_t			f_mode;
 	loff_t			f_pos;
@@ -821,8 +824,8 @@
 #define file_list_lock() spin_lock(&files_lock);
 #define file_list_unlock() spin_unlock(&files_lock);
 
-#define get_file(x)	atomic_inc(&(x)->f_count)
-#define file_count(x)	atomic_read(&(x)->f_count)
+#define get_file(x)	atomic_long_inc(&(x)->f_count)
+#define file_count(x)	atomic_long_read(&(x)->f_count)
 
 #ifdef CONFIG_DEBUG_WRITECOUNT
 static inline void file_take_write(struct file *f)
@@ -1136,7 +1139,7 @@
 extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
 extern int vfs_mkdir(struct inode *, struct dentry *, int);
 extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
-extern int vfs_symlink(struct inode *, struct dentry *, const char *, int);
+extern int vfs_symlink(struct inode *, struct dentry *, const char *);
 extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
 extern int vfs_rmdir(struct inode *, struct dentry *);
 extern int vfs_unlink(struct inode *, struct dentry *);
@@ -1272,7 +1275,7 @@
 	void * (*follow_link) (struct dentry *, struct nameidata *);
 	void (*put_link) (struct dentry *, struct nameidata *, void *);
 	void (*truncate) (struct inode *);
-	int (*permission) (struct inode *, int, struct nameidata *);
+	int (*permission) (struct inode *, int);
 	int (*setattr) (struct dentry *, struct iattr *);
 	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
 	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -1696,9 +1699,9 @@
 extern void make_bad_inode(struct inode *);
 extern int is_bad_inode(struct inode *);
 
-extern const struct file_operations read_fifo_fops;
-extern const struct file_operations write_fifo_fops;
-extern const struct file_operations rdwr_fifo_fops;
+extern const struct file_operations read_pipefifo_fops;
+extern const struct file_operations write_pipefifo_fops;
+extern const struct file_operations rdwr_pipefifo_fops;
 
 extern int fs_may_remount_ro(struct super_block *);
 
@@ -1767,7 +1770,7 @@
 extern sector_t bmap(struct inode *, sector_t);
 #endif
 extern int notify_change(struct dentry *, struct iattr *);
-extern int permission(struct inode *, int, struct nameidata *);
+extern int inode_permission(struct inode *, int);
 extern int generic_permission(struct inode *, int,
 		int (*check_acl)(struct inode *, int));
 
@@ -1831,7 +1834,7 @@
 extern void destroy_inode(struct inode *);
 extern struct inode *new_inode(struct super_block *);
 extern int should_remove_suid(struct dentry *);
-extern int remove_suid(struct dentry *);
+extern int file_remove_suid(struct file *);
 
 extern void __insert_inode_hash(struct inode *, unsigned long hashval);
 extern void remove_inode_hash(struct inode *);
diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h
index 282f542..9e5a06e 100644
--- a/include/linux/fs_struct.h
+++ b/include/linux/fs_struct.h
@@ -7,7 +7,7 @@
 	atomic_t count;
 	rwlock_t lock;
 	int umask;
-	struct path root, pwd, altroot;
+	struct path root, pwd;
 };
 
 #define INIT_FS {				\
@@ -19,7 +19,6 @@
 extern struct kmem_cache *fs_cachep;
 
 extern void exit_fs(struct task_struct *);
-extern void set_fs_altroot(void);
 extern void set_fs_root(struct fs_struct *, struct path *);
 extern void set_fs_pwd(struct fs_struct *, struct path *);
 extern struct fs_struct *copy_fs_struct(struct fs_struct *);
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 9a71d4c..32e0ef0 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -273,7 +273,10 @@
 #define huge_page_mask(h) PAGE_MASK
 #define huge_page_order(h) 0
 #define huge_page_shift(h) PAGE_SHIFT
-#define pages_per_huge_page(h) 1
+static inline unsigned int pages_per_huge_page(struct hstate *h)
+{
+	return 1;
+}
 #endif
 
 #endif /* _LINUX_HUGETLB_H */
diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h
new file mode 100644
index 0000000..6b71d2d
--- /dev/null
+++ b/include/linux/mISDNdsp.h
@@ -0,0 +1,37 @@
+#ifndef __mISDNdsp_H__
+#define __mISDNdsp_H__
+
+struct mISDN_dsp_element_arg {
+	char	*name;
+	char	*def;
+	char	*desc;
+};
+
+struct mISDN_dsp_element {
+	char	*name;
+	void	*(*new)(const char *arg);
+	void	(*free)(void *p);
+	void	(*process_tx)(void *p, unsigned char *data, int len);
+	void	(*process_rx)(void *p, unsigned char *data, int len);
+	int	num_args;
+	struct mISDN_dsp_element_arg
+		*args;
+};
+
+extern int  mISDN_dsp_element_register(struct mISDN_dsp_element *elem);
+extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem);
+
+struct dsp_features {
+	int	hfc_id; /* unique id to identify the chip (or -1) */
+	int	hfc_dtmf; /* set if HFCmulti card supports dtmf */
+	int	hfc_loops; /* set if card supports tone loops */
+	int	hfc_echocanhw; /* set if card supports echocancelation*/
+	int	pcm_id; /* unique id to identify the pcm bus (or -1) */
+	int	pcm_slots; /* number of slots on the pcm bus */
+	int	pcm_banks; /* number of IO banks of pcm bus */
+	int	unclocked; /* data is not clocked (has jitter/loss) */
+	int	unordered; /* data is unordered (packets have index) */
+};
+
+#endif
+
diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h
new file mode 100644
index 0000000..e794dfb
--- /dev/null
+++ b/include/linux/mISDNhw.h
@@ -0,0 +1,193 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ *   Basic declarations for the mISDN HW channels
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MISDNHW_H
+#define MISDNHW_H
+#include <linux/mISDNif.h>
+#include <linux/timer.h>
+
+/*
+ * HW DEBUG 0xHHHHGGGG
+ * H - hardware driver specific bits
+ * G - for all drivers
+ */
+
+#define DEBUG_HW		0x00000001
+#define DEBUG_HW_OPEN		0x00000002
+#define DEBUG_HW_DCHANNEL	0x00000100
+#define DEBUG_HW_DFIFO		0x00000200
+#define DEBUG_HW_BCHANNEL	0x00001000
+#define DEBUG_HW_BFIFO		0x00002000
+
+#define MAX_DFRAME_LEN_L1	300
+#define MAX_MON_FRAME		32
+#define MAX_LOG_SPACE		2048
+#define MISDN_COPY_SIZE		32
+
+/* channel->Flags bit field */
+#define FLG_TX_BUSY		0	/* tx_buf in use */
+#define FLG_TX_NEXT		1	/* next_skb in use */
+#define FLG_L1_BUSY		2	/* L1 is permanent busy */
+#define FLG_L2_ACTIVATED	3	/* activated from L2 */
+#define FLG_OPEN		5	/* channel is in use */
+#define FLG_ACTIVE		6	/* channel is activated */
+#define FLG_BUSY_TIMER		7
+/* channel type */
+#define FLG_DCHANNEL		8	/* channel is D-channel */
+#define FLG_BCHANNEL		9	/* channel is B-channel */
+#define FLG_ECHANNEL		10	/* channel is E-channel */
+#define FLG_TRANSPARENT		12	/* channel use transparent data */
+#define FLG_HDLC		13	/* channel use hdlc data */
+#define FLG_L2DATA		14	/* channel use L2 DATA primitivs */
+#define FLG_ORIGIN		15	/* channel is on origin site */
+/* channel specific stuff */
+/* arcofi specific */
+#define FLG_ARCOFI_TIMER	16
+#define FLG_ARCOFI_ERROR	17
+/* isar specific */
+#define FLG_INITIALIZED		16
+#define FLG_DLEETX		17
+#define FLG_LASTDLE		18
+#define FLG_FIRST		19
+#define FLG_LASTDATA		20
+#define FLG_NMD_DATA		21
+#define FLG_FTI_RUN		22
+#define FLG_LL_OK		23
+#define FLG_LL_CONN		24
+#define FLG_DTMFSEND		25
+
+/* workq events */
+#define FLG_RECVQUEUE		30
+#define	FLG_PHCHANGE		31
+
+#define schedule_event(s, ev)	do { \
+					test_and_set_bit(ev, &((s)->Flags)); \
+					schedule_work(&((s)->workq)); \
+				} while (0)
+
+struct dchannel {
+	struct mISDNdevice	dev;
+	u_long			Flags;
+	struct work_struct	workq;
+	void			(*phfunc) (struct dchannel *);
+	u_int			state;
+	void			*l1;
+	/* HW access */
+	u_char			(*read_reg) (void *, u_char);
+	void			(*write_reg) (void *, u_char, u_char);
+	void			(*read_fifo) (void *, u_char *, int);
+	void			(*write_fifo) (void *, u_char *, int);
+	void			*hw;
+	int			slot;	/* multiport card channel slot */
+	struct timer_list	timer;
+	/* receive data */
+	struct sk_buff		*rx_skb;
+	int			maxlen;
+	/* send data */
+	struct sk_buff_head	squeue;
+	struct sk_buff_head	rqueue;
+	struct sk_buff		*tx_skb;
+	int			tx_idx;
+	int			debug;
+	/* statistics */
+	int			err_crc;
+	int			err_tx;
+	int			err_rx;
+};
+
+typedef int	(dchannel_l1callback)(struct dchannel *, u_int);
+extern int	create_l1(struct dchannel *, dchannel_l1callback *);
+
+/* private L1 commands */
+#define INFO0		0x8002
+#define INFO1		0x8102
+#define INFO2		0x8202
+#define INFO3_P8	0x8302
+#define INFO3_P10	0x8402
+#define INFO4_P8	0x8502
+#define INFO4_P10	0x8602
+#define LOSTFRAMING	0x8702
+#define ANYSIGNAL	0x8802
+#define HW_POWERDOWN	0x8902
+#define HW_RESET_REQ	0x8a02
+#define HW_POWERUP_REQ	0x8b02
+#define HW_DEACT_REQ	0x8c02
+#define HW_ACTIVATE_REQ	0x8e02
+#define HW_D_NOBLOCKED  0x8f02
+#define HW_RESET_IND	0x9002
+#define HW_POWERUP_IND	0x9102
+#define HW_DEACT_IND	0x9202
+#define HW_ACTIVATE_IND	0x9302
+#define HW_DEACT_CNF	0x9402
+#define HW_TESTLOOP	0x9502
+#define HW_TESTRX_RAW	0x9602
+#define HW_TESTRX_HDLC	0x9702
+#define HW_TESTRX_OFF	0x9802
+
+struct layer1;
+extern int	l1_event(struct layer1 *, u_int);
+
+
+struct bchannel {
+	struct mISDNchannel	ch;
+	int			nr;
+	u_long			Flags;
+	struct work_struct	workq;
+	u_int			state;
+	/* HW access */
+	u_char			(*read_reg) (void *, u_char);
+	void			(*write_reg) (void *, u_char, u_char);
+	void			(*read_fifo) (void *, u_char *, int);
+	void			(*write_fifo) (void *, u_char *, int);
+	void			*hw;
+	int			slot;	/* multiport card channel slot */
+	struct timer_list	timer;
+	/* receive data */
+	struct sk_buff		*rx_skb;
+	int			maxlen;
+	/* send data */
+	struct sk_buff		*next_skb;
+	struct sk_buff		*tx_skb;
+	struct sk_buff_head	rqueue;
+	int			rcount;
+	int			tx_idx;
+	int			debug;
+	/* statistics */
+	int			err_crc;
+	int			err_tx;
+	int			err_rx;
+};
+
+extern int	mISDN_initdchannel(struct dchannel *, int, void *);
+extern int	mISDN_initbchannel(struct bchannel *, int);
+extern int	mISDN_freedchannel(struct dchannel *);
+extern int	mISDN_freebchannel(struct bchannel *);
+extern void	queue_ch_frame(struct mISDNchannel *, u_int,
+			int, struct sk_buff *);
+extern int	dchannel_senddata(struct dchannel *, struct sk_buff *);
+extern int	bchannel_senddata(struct bchannel *, struct sk_buff *);
+extern void	recv_Dchannel(struct dchannel *);
+extern void	recv_Bchannel(struct bchannel *);
+extern void	recv_Dchannel_skb(struct dchannel *, struct sk_buff *);
+extern void	recv_Bchannel_skb(struct bchannel *, struct sk_buff *);
+extern void	confirm_Bsend(struct bchannel *bch);
+extern int	get_next_bframe(struct bchannel *);
+extern int	get_next_dframe(struct dchannel *);
+
+#endif
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
new file mode 100644
index 0000000..5c948f3
--- /dev/null
+++ b/include/linux/mISDNif.h
@@ -0,0 +1,487 @@
+/*
+ *
+ * Author	Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU LESSER GENERAL PUBLIC LICENSE for more details.
+ *
+ */
+
+#ifndef mISDNIF_H
+#define mISDNIF_H
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+
+/*
+ * ABI Version 32 bit
+ *
+ * <8 bit> Major version
+ *		- changed if any interface become backwards incompatible
+ *
+ * <8 bit> Minor version
+ *              - changed if any interface is extended but backwards compatible
+ *
+ * <16 bit> Release number
+ *              - should be incremented on every checkin
+ */
+#define	MISDN_MAJOR_VERSION	1
+#define	MISDN_MINOR_VERSION	0
+#define MISDN_RELEASE		18
+
+/* primitives for information exchange
+ * generell format
+ * <16  bit  0 >
+ * <8  bit command>
+ *    BIT 8 = 1 LAYER private
+ *    BIT 7 = 1 answer
+ *    BIT 6 = 1 DATA
+ * <8  bit target layer mask>
+ *
+ * Layer = 00 is reserved for general commands
+   Layer = 01  L2 -> HW
+   Layer = 02  HW -> L2
+   Layer = 04  L3 -> L2
+   Layer = 08  L2 -> L3
+ * Layer = FF is reserved for broadcast commands
+ */
+
+#define MISDN_CMDMASK		0xff00
+#define MISDN_LAYERMASK		0x00ff
+
+/* generell commands */
+#define OPEN_CHANNEL		0x0100
+#define CLOSE_CHANNEL		0x0200
+#define CONTROL_CHANNEL		0x0300
+#define CHECK_DATA		0x0400
+
+/* layer 2 -> layer 1 */
+#define PH_ACTIVATE_REQ		0x0101
+#define PH_DEACTIVATE_REQ	0x0201
+#define PH_DATA_REQ		0x2001
+#define MPH_ACTIVATE_REQ	0x0501
+#define MPH_DEACTIVATE_REQ	0x0601
+#define MPH_INFORMATION_REQ	0x0701
+#define PH_CONTROL_REQ		0x0801
+
+/* layer 1 -> layer 2 */
+#define PH_ACTIVATE_IND		0x0102
+#define PH_ACTIVATE_CNF		0x4102
+#define PH_DEACTIVATE_IND	0x0202
+#define PH_DEACTIVATE_CNF	0x4202
+#define PH_DATA_IND		0x2002
+#define MPH_ACTIVATE_IND	0x0502
+#define MPH_DEACTIVATE_IND	0x0602
+#define MPH_INFORMATION_IND	0x0702
+#define PH_DATA_CNF		0x6002
+#define PH_CONTROL_IND		0x0802
+#define PH_CONTROL_CNF		0x4802
+
+/* layer 3 -> layer 2 */
+#define DL_ESTABLISH_REQ	0x1004
+#define DL_RELEASE_REQ		0x1104
+#define DL_DATA_REQ		0x3004
+#define DL_UNITDATA_REQ		0x3104
+#define DL_INFORMATION_REQ	0x0004
+
+/* layer 2 -> layer 3 */
+#define DL_ESTABLISH_IND	0x1008
+#define DL_ESTABLISH_CNF	0x5008
+#define DL_RELEASE_IND		0x1108
+#define DL_RELEASE_CNF		0x5108
+#define DL_DATA_IND		0x3008
+#define DL_UNITDATA_IND		0x3108
+#define DL_INFORMATION_IND	0x0008
+
+/* intern layer 2 managment */
+#define MDL_ASSIGN_REQ		0x1804
+#define MDL_ASSIGN_IND		0x1904
+#define MDL_REMOVE_REQ		0x1A04
+#define MDL_REMOVE_IND		0x1B04
+#define MDL_STATUS_UP_IND	0x1C04
+#define MDL_STATUS_DOWN_IND	0x1D04
+#define MDL_STATUS_UI_IND	0x1E04
+#define MDL_ERROR_IND		0x1F04
+#define MDL_ERROR_RSP		0x5F04
+
+/* DL_INFORMATION_IND types */
+#define DL_INFO_L2_CONNECT	0x0001
+#define DL_INFO_L2_REMOVED	0x0002
+
+/* PH_CONTROL types */
+/* TOUCH TONE IS 0x20XX  XX "0"..."9", "A","B","C","D","*","#" */
+#define DTMF_TONE_VAL		0x2000
+#define DTMF_TONE_MASK		0x007F
+#define DTMF_TONE_START		0x2100
+#define DTMF_TONE_STOP		0x2200
+#define DTMF_HFC_COEF		0x4000
+#define DSP_CONF_JOIN		0x2403
+#define DSP_CONF_SPLIT		0x2404
+#define DSP_RECEIVE_OFF		0x2405
+#define DSP_RECEIVE_ON		0x2406
+#define DSP_ECHO_ON		0x2407
+#define DSP_ECHO_OFF		0x2408
+#define DSP_MIX_ON		0x2409
+#define DSP_MIX_OFF		0x240a
+#define DSP_DELAY		0x240b
+#define DSP_JITTER		0x240c
+#define DSP_TXDATA_ON		0x240d
+#define DSP_TXDATA_OFF		0x240e
+#define DSP_TX_DEJITTER		0x240f
+#define DSP_TX_DEJ_OFF		0x2410
+#define DSP_TONE_PATT_ON	0x2411
+#define DSP_TONE_PATT_OFF	0x2412
+#define DSP_VOL_CHANGE_TX	0x2413
+#define DSP_VOL_CHANGE_RX	0x2414
+#define DSP_BF_ENABLE_KEY	0x2415
+#define DSP_BF_DISABLE		0x2416
+#define DSP_BF_ACCEPT		0x2416
+#define DSP_BF_REJECT		0x2417
+#define DSP_PIPELINE_CFG	0x2418
+#define HFC_VOL_CHANGE_TX	0x2601
+#define HFC_VOL_CHANGE_RX	0x2602
+#define HFC_SPL_LOOP_ON		0x2603
+#define HFC_SPL_LOOP_OFF	0x2604
+
+/* DSP_TONE_PATT_ON parameter */
+#define TONE_OFF			0x0000
+#define TONE_GERMAN_DIALTONE		0x0001
+#define TONE_GERMAN_OLDDIALTONE		0x0002
+#define TONE_AMERICAN_DIALTONE		0x0003
+#define TONE_GERMAN_DIALPBX		0x0004
+#define TONE_GERMAN_OLDDIALPBX		0x0005
+#define TONE_AMERICAN_DIALPBX		0x0006
+#define TONE_GERMAN_RINGING		0x0007
+#define TONE_GERMAN_OLDRINGING		0x0008
+#define TONE_AMERICAN_RINGPBX		0x000b
+#define TONE_GERMAN_RINGPBX		0x000c
+#define TONE_GERMAN_OLDRINGPBX		0x000d
+#define TONE_AMERICAN_RINGING		0x000e
+#define TONE_GERMAN_BUSY		0x000f
+#define TONE_GERMAN_OLDBUSY		0x0010
+#define TONE_AMERICAN_BUSY		0x0011
+#define TONE_GERMAN_HANGUP		0x0012
+#define TONE_GERMAN_OLDHANGUP		0x0013
+#define TONE_AMERICAN_HANGUP		0x0014
+#define TONE_SPECIAL_INFO		0x0015
+#define TONE_GERMAN_GASSENBESETZT	0x0016
+#define TONE_GERMAN_AUFSCHALTTON	0x0016
+
+/* MPH_INFORMATION_IND */
+#define L1_SIGNAL_LOS_OFF	0x0010
+#define L1_SIGNAL_LOS_ON	0x0011
+#define L1_SIGNAL_AIS_OFF	0x0012
+#define L1_SIGNAL_AIS_ON	0x0013
+#define L1_SIGNAL_RDI_OFF	0x0014
+#define L1_SIGNAL_RDI_ON	0x0015
+#define L1_SIGNAL_SLIP_RX	0x0020
+#define L1_SIGNAL_SLIP_TX	0x0021
+
+/*
+ * protocol ids
+ * D channel 1-31
+ * B channel 33 - 63
+ */
+
+#define ISDN_P_NONE		0
+#define ISDN_P_BASE		0
+#define ISDN_P_TE_S0		0x01
+#define ISDN_P_NT_S0  		0x02
+#define ISDN_P_TE_E1		0x03
+#define ISDN_P_NT_E1  		0x04
+#define ISDN_P_LAPD_TE		0x10
+#define	ISDN_P_LAPD_NT		0x11
+
+#define ISDN_P_B_MASK		0x1f
+#define ISDN_P_B_START		0x20
+
+#define ISDN_P_B_RAW		0x21
+#define ISDN_P_B_HDLC		0x22
+#define ISDN_P_B_X75SLP		0x23
+#define ISDN_P_B_L2DTMF		0x24
+#define ISDN_P_B_L2DSP		0x25
+#define ISDN_P_B_L2DSPHDLC	0x26
+
+#define OPTION_L2_PMX		1
+#define OPTION_L2_PTP		2
+#define OPTION_L2_FIXEDTEI	3
+#define OPTION_L2_CLEANUP	4
+
+/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
+#define MISDN_MAX_IDLEN		20
+
+struct mISDNhead {
+	unsigned int	prim;
+	unsigned int	id;
+}  __attribute__((packed));
+
+#define MISDN_HEADER_LEN	sizeof(struct mISDNhead)
+#define MAX_DATA_SIZE		2048
+#define MAX_DATA_MEM		(MAX_DATA_SIZE + MISDN_HEADER_LEN)
+#define MAX_DFRAME_LEN		260
+
+#define MISDN_ID_ADDR_MASK	0xFFFF
+#define MISDN_ID_TEI_MASK	0xFF00
+#define MISDN_ID_SAPI_MASK	0x00FF
+#define MISDN_ID_TEI_ANY	0x7F00
+
+#define MISDN_ID_ANY		0xFFFF
+#define MISDN_ID_NONE		0xFFFE
+
+#define GROUP_TEI		127
+#define TEI_SAPI		63
+#define CTRL_SAPI		0
+
+#define MISDN_CHMAP_SIZE	4
+
+#define SOL_MISDN	0
+
+struct sockaddr_mISDN {
+	sa_family_t    family;
+	unsigned char	dev;
+	unsigned char	channel;
+	unsigned char	sapi;
+	unsigned char	tei;
+};
+
+/* timer device ioctl */
+#define IMADDTIMER	_IOR('I', 64, int)
+#define IMDELTIMER	_IOR('I', 65, int)
+/* socket ioctls */
+#define	IMGETVERSION	_IOR('I', 66, int)
+#define	IMGETCOUNT	_IOR('I', 67, int)
+#define IMGETDEVINFO	_IOR('I', 68, int)
+#define IMCTRLREQ	_IOR('I', 69, int)
+#define IMCLEAR_L2	_IOR('I', 70, int)
+
+struct mISDNversion {
+	unsigned char	major;
+	unsigned char	minor;
+	unsigned short	release;
+};
+
+struct mISDN_devinfo {
+	u_int			id;
+	u_int			Dprotocols;
+	u_int			Bprotocols;
+	u_int			protocol;
+	u_long			channelmap[MISDN_CHMAP_SIZE];
+	u_int			nrbchan;
+	char			name[MISDN_MAX_IDLEN];
+};
+
+/* CONTROL_CHANNEL parameters */
+#define MISDN_CTRL_GETOP		0x0000
+#define MISDN_CTRL_LOOP			0x0001
+#define MISDN_CTRL_CONNECT		0x0002
+#define MISDN_CTRL_DISCONNECT		0x0004
+#define MISDN_CTRL_PCMCONNECT		0x0010
+#define MISDN_CTRL_PCMDISCONNECT	0x0020
+#define MISDN_CTRL_SETPEER		0x0040
+#define MISDN_CTRL_UNSETPEER		0x0080
+#define MISDN_CTRL_RX_OFF		0x0100
+#define MISDN_CTRL_HW_FEATURES_OP	0x2000
+#define MISDN_CTRL_HW_FEATURES		0x2001
+#define MISDN_CTRL_HFC_OP		0x4000
+#define MISDN_CTRL_HFC_PCM_CONN		0x4001
+#define MISDN_CTRL_HFC_PCM_DISC		0x4002
+#define MISDN_CTRL_HFC_CONF_JOIN	0x4003
+#define MISDN_CTRL_HFC_CONF_SPLIT	0x4004
+#define MISDN_CTRL_HFC_RECEIVE_OFF	0x4005
+#define MISDN_CTRL_HFC_RECEIVE_ON	0x4006
+#define MISDN_CTRL_HFC_ECHOCAN_ON 	0x4007
+#define MISDN_CTRL_HFC_ECHOCAN_OFF 	0x4008
+
+
+/* socket options */
+#define MISDN_TIME_STAMP		0x0001
+
+struct mISDN_ctrl_req {
+	int		op;
+	int		channel;
+	int		p1;
+	int		p2;
+};
+
+/* muxer options */
+#define MISDN_OPT_ALL		1
+#define MISDN_OPT_TEIMGR	2
+
+#ifdef __KERNEL__
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/completion.h>
+
+#define DEBUG_CORE		0x000000ff
+#define DEBUG_CORE_FUNC		0x00000002
+#define DEBUG_SOCKET		0x00000004
+#define DEBUG_MANAGER		0x00000008
+#define DEBUG_SEND_ERR		0x00000010
+#define DEBUG_MSG_THREAD	0x00000020
+#define DEBUG_QUEUE_FUNC	0x00000040
+#define DEBUG_L1		0x0000ff00
+#define DEBUG_L1_FSM		0x00000200
+#define DEBUG_L2		0x00ff0000
+#define DEBUG_L2_FSM		0x00020000
+#define DEBUG_L2_CTRL		0x00040000
+#define DEBUG_L2_RECV		0x00080000
+#define DEBUG_L2_TEI		0x00100000
+#define DEBUG_L2_TEIFSM		0x00200000
+#define DEBUG_TIMER		0x01000000
+
+#define mISDN_HEAD_P(s)		((struct mISDNhead *)&s->cb[0])
+#define mISDN_HEAD_PRIM(s)	(((struct mISDNhead *)&s->cb[0])->prim)
+#define mISDN_HEAD_ID(s)	(((struct mISDNhead *)&s->cb[0])->id)
+
+/* socket states */
+#define MISDN_OPEN	1
+#define MISDN_BOUND	2
+#define MISDN_CLOSED	3
+
+struct mISDNchannel;
+struct mISDNdevice;
+struct mISDNstack;
+
+struct channel_req {
+	u_int			protocol;
+	struct sockaddr_mISDN	adr;
+	struct mISDNchannel	*ch;
+};
+
+typedef	int	(ctrl_func_t)(struct mISDNchannel *, u_int, void *);
+typedef	int	(send_func_t)(struct mISDNchannel *, struct sk_buff *);
+typedef int	(create_func_t)(struct channel_req *);
+
+struct Bprotocol {
+	struct list_head	list;
+	char			*name;
+	u_int			Bprotocols;
+	create_func_t		*create;
+};
+
+struct mISDNchannel {
+	struct list_head	list;
+	u_int			protocol;
+	u_int			nr;
+	u_long			opt;
+	u_int			addr;
+	struct mISDNstack	*st;
+	struct mISDNchannel	*peer;
+	send_func_t		*send;
+	send_func_t		*recv;
+	ctrl_func_t		*ctrl;
+};
+
+struct mISDN_sock_list {
+	struct hlist_head	head;
+	rwlock_t		lock;
+};
+
+struct mISDN_sock {
+	struct sock		sk;
+	struct mISDNchannel	ch;
+	u_int			cmask;
+	struct mISDNdevice	*dev;
+};
+
+
+
+struct mISDNdevice {
+	struct mISDNchannel	D;
+	u_int			id;
+	char			name[MISDN_MAX_IDLEN];
+	u_int			Dprotocols;
+	u_int			Bprotocols;
+	u_int			nrbchan;
+	u_long			channelmap[MISDN_CHMAP_SIZE];
+	struct list_head	bchannels;
+	struct mISDNchannel	*teimgr;
+	struct device		dev;
+};
+
+struct mISDNstack {
+	u_long			status;
+	struct mISDNdevice	*dev;
+	struct task_struct	*thread;
+	struct completion	*notify;
+	wait_queue_head_t	workq;
+	struct sk_buff_head	msgq;
+	struct list_head	layer2;
+	struct mISDNchannel	*layer1;
+	struct mISDNchannel	own;
+	struct mutex		lmutex; /* protect lists */
+	struct mISDN_sock_list	l1sock;
+#ifdef MISDN_MSG_STATS
+	u_int			msg_cnt;
+	u_int			sleep_cnt;
+	u_int			stopped_cnt;
+#endif
+};
+
+/* global alloc/queue dunctions */
+
+static inline struct sk_buff *
+mI_alloc_skb(unsigned int len, gfp_t gfp_mask)
+{
+	struct sk_buff	*skb;
+
+	skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask);
+	if (likely(skb))
+		skb_reserve(skb, MISDN_HEADER_LEN);
+	return skb;
+}
+
+static inline struct sk_buff *
+_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask)
+{
+	struct sk_buff	*skb = mI_alloc_skb(len, gfp_mask);
+	struct mISDNhead *hh;
+
+	if (!skb)
+		return NULL;
+	if (len)
+		memcpy(skb_put(skb, len), dp, len);
+	hh = mISDN_HEAD_P(skb);
+	hh->prim = prim;
+	hh->id = id;
+	return skb;
+}
+
+static inline void
+_queue_data(struct mISDNchannel *ch, u_int prim,
+    u_int id, u_int len, void *dp, gfp_t gfp_mask)
+{
+	struct sk_buff		*skb;
+
+	if (!ch->peer)
+		return;
+	skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask);
+	if (!skb)
+		return;
+	if (ch->recv(ch->peer, skb))
+		dev_kfree_skb(skb);
+}
+
+/* global register/unregister functions */
+
+extern int	mISDN_register_device(struct mISDNdevice *, char *name);
+extern void	mISDN_unregister_device(struct mISDNdevice *);
+extern int	mISDN_register_Bprotocol(struct Bprotocol *);
+extern void	mISDN_unregister_Bprotocol(struct Bprotocol *);
+
+extern void	set_channel_address(struct mISDNchannel *, u_int, u_int);
+
+#endif /* __KERNEL__ */
+#endif /* mISDNIF_H */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 0d508ac..ee6e822 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -111,6 +111,8 @@
 	unsigned		num_info;	/* number of info strings */
 	const char		**info;		/* info strings */
 	struct sdio_func_tuple	*tuples;	/* unknown common tuples */
+
+	struct dentry		*debugfs_root;
 };
 
 #define mmc_card_mmc(c)		((c)->type == MMC_TYPE_MMC)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 10a2080..9c288c9 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -157,6 +157,8 @@
 	struct led_trigger	*led;		/* activity led */
 #endif
 
+	struct dentry		*debugfs_root;
+
 	unsigned long		private[0] ____cacheline_aligned;
 };
 
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 4374d1a..b5efaa2 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -47,7 +47,7 @@
 	struct list_head mnt_child;	/* and going through their mnt_child */
 	int mnt_flags;
 	/* 4 bytes hole on 64bits arches */
-	char *mnt_devname;		/* Name of device e.g. /dev/dsk/hda1 */
+	const char *mnt_devname;	/* Name of device e.g. /dev/dsk/hda1 */
 	struct list_head mnt_list;
 	struct list_head mnt_expire;	/* link in fs-specific expiry list */
 	struct list_head mnt_share;	/* circular list of shared mounts */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 24d88e9..68f8c32 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -47,27 +47,24 @@
 #define LOOKUP_DIRECTORY	 2
 #define LOOKUP_CONTINUE		 4
 #define LOOKUP_PARENT		16
-#define LOOKUP_NOALT		32
 #define LOOKUP_REVAL		64
 /*
  * Intent data
  */
 #define LOOKUP_OPEN		(0x0100)
 #define LOOKUP_CREATE		(0x0200)
-#define LOOKUP_ACCESS		(0x0400)
-#define LOOKUP_CHDIR		(0x0800)
 
-extern int __user_walk(const char __user *, unsigned, struct nameidata *);
-extern int __user_walk_fd(int dfd, const char __user *, unsigned, struct nameidata *);
-#define user_path_walk(name,nd) \
-	__user_walk_fd(AT_FDCWD, name, LOOKUP_FOLLOW, nd)
-#define user_path_walk_link(name,nd) \
-	__user_walk_fd(AT_FDCWD, name, 0, nd)
+extern int user_path_at(int, const char __user *, unsigned, struct path *);
+
+#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
+#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)
+#define user_path_dir(name, path) \
+	user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path)
+
 extern int path_lookup(const char *, unsigned, struct nameidata *);
 extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
 			   const char *, unsigned int, struct nameidata *);
 
-extern int __user_path_lookup_open(const char __user *, unsigned lookup_flags, struct nameidata *nd, int open_flags);
 extern int path_lookup_open(int dfd, const char *name, unsigned lookup_flags, struct nameidata *, int open_flags);
 extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry,
 		int (*open)(struct inode *, struct file *));
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 29d2619..78a5922 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -42,7 +42,6 @@
 #include <linux/in.h>
 #include <linux/kref.h>
 #include <linux/mm.h>
-#include <linux/namei.h>
 #include <linux/pagemap.h>
 #include <linux/rbtree.h>
 #include <linux/rwsem.h>
@@ -332,7 +331,7 @@
 extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
 extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
-extern int nfs_permission(struct inode *, int, struct nameidata *);
+extern int nfs_permission(struct inode *, int);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
 extern int nfs_attribute_timeout(struct inode *inode);
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index c3b1761..ffe479b 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1832,7 +1832,13 @@
 #define PCI_DEVICE_ID_MOXA_C320		0x3200
 
 #define PCI_VENDOR_ID_CCD		0x1397
+#define PCI_DEVICE_ID_CCD_HFC4S		0x08B4
+#define PCI_SUBDEVICE_ID_CCD_PMX2S	0x1234
+#define PCI_DEVICE_ID_CCD_HFC8S		0x16B8
 #define PCI_DEVICE_ID_CCD_2BD0		0x2bd0
+#define PCI_DEVICE_ID_CCD_HFCE1		0x30B1
+#define PCI_SUBDEVICE_ID_CCD_SPD4S	0x3136
+#define PCI_SUBDEVICE_ID_CCD_SPDE1	0x3137
 #define PCI_DEVICE_ID_CCD_B000		0xb000
 #define PCI_DEVICE_ID_CCD_B006		0xb006
 #define PCI_DEVICE_ID_CCD_B007		0xb007
@@ -1842,8 +1848,32 @@
 #define PCI_DEVICE_ID_CCD_B00B		0xb00b
 #define PCI_DEVICE_ID_CCD_B00C		0xb00c
 #define PCI_DEVICE_ID_CCD_B100		0xb100
+#define PCI_SUBDEVICE_ID_CCD_IOB4ST	0xB520
+#define PCI_SUBDEVICE_ID_CCD_IOB8STR	0xB521
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST	0xB522
+#define PCI_SUBDEVICE_ID_CCD_IOB1E1	0xB523
+#define PCI_SUBDEVICE_ID_CCD_SWYX4S	0xB540
+#define PCI_SUBDEVICE_ID_CCD_JH4S20	0xB550
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1	0xB552
+#define PCI_SUBDEVICE_ID_CCD_BN4S	0xB560
+#define PCI_SUBDEVICE_ID_CCD_BN8S	0xB562
+#define PCI_SUBDEVICE_ID_CCD_BNE1	0xB563
+#define PCI_SUBDEVICE_ID_CCD_BNE1D	0xB564
+#define PCI_SUBDEVICE_ID_CCD_BNE1DP	0xB565
+#define PCI_SUBDEVICE_ID_CCD_BN2S	0xB566
+#define PCI_SUBDEVICE_ID_CCD_BN1SM	0xB567
+#define PCI_SUBDEVICE_ID_CCD_BN4SM	0xB568
+#define PCI_SUBDEVICE_ID_CCD_BN2SM	0xB569
+#define PCI_SUBDEVICE_ID_CCD_BNE1M	0xB56A
+#define PCI_SUBDEVICE_ID_CCD_BN8SP	0xB56B
+#define PCI_SUBDEVICE_ID_CCD_HFC4S	0xB620
+#define PCI_SUBDEVICE_ID_CCD_HFC8S	0xB622
 #define PCI_DEVICE_ID_CCD_B700		0xb700
 #define PCI_DEVICE_ID_CCD_B701		0xb701
+#define PCI_SUBDEVICE_ID_CCD_HFCE1	0xC523
+#define PCI_SUBDEVICE_ID_CCD_OV2S	0xE884
+#define PCI_SUBDEVICE_ID_CCD_OV4S	0xE888
+#define PCI_SUBDEVICE_ID_CCD_OV8S	0xE998
 
 #define PCI_VENDOR_ID_EXAR		0x13a8
 #define PCI_DEVICE_ID_EXAR_XR17C152	0x0152
@@ -2523,6 +2553,9 @@
 
 #define PCI_VENDOR_ID_3COM_2		0xa727
 
+#define PCI_VENDOR_ID_DIGIUM		0xd161
+#define PCI_DEVICE_ID_DIGIUM_HFC4S	0xb410
+
 #define PCI_SUBVENDOR_ID_EXSYS		0xd84d
 #define PCI_SUBDEVICE_ID_EXSYS_4014	0x4014
 #define PCI_SUBDEVICE_ID_EXSYS_4055	0x4055
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index f560d17..fb61850 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -282,11 +282,16 @@
 		struct task_struct *task);
 };
 
+struct ctl_table_header;
+struct ctl_table;
+
 struct proc_inode {
 	struct pid *pid;
 	int fd;
 	union proc_op op;
 	struct proc_dir_entry *pde;
+	struct ctl_table_header *sysctl;
+	struct ctl_table *sysctl_entry;
 	struct inode vfs_inode;
 };
 
diff --git a/include/linux/reiserfs_xattr.h b/include/linux/reiserfs_xattr.h
index 66a9681..af135ae 100644
--- a/include/linux/reiserfs_xattr.h
+++ b/include/linux/reiserfs_xattr.h
@@ -55,7 +55,7 @@
 int reiserfs_delete_xattrs(struct inode *inode);
 int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs);
 int reiserfs_xattr_init(struct super_block *sb, int mount_flags);
-int reiserfs_permission(struct inode *inode, int mask, struct nameidata *nd);
+int reiserfs_permission(struct inode *inode, int mask);
 
 int reiserfs_xattr_del(struct inode *, const char *);
 int reiserfs_xattr_get(const struct inode *, const char *, void *, size_t);
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index f4d386c..ca643b1 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -755,13 +755,6 @@
 	} \
 } while(0)
 
-#define BUG_TRAP(x) do { \
-	if (unlikely(!(x))) { \
-		printk(KERN_ERR "KERNEL: assertion (%s) failed at %s (%d)\n", \
-			#x,  __FILE__ , __LINE__); \
-	} \
-} while(0)
-
 static inline u32 rtm_get_table(struct rtattr **rta, u8 table)
 {
 	return RTA_GET_U32(rta[RTA_TABLE-1]);
diff --git a/include/linux/security.h b/include/linux/security.h
index f0e9adb..fd96e7f 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1362,7 +1362,7 @@
 			     struct inode *new_dir, struct dentry *new_dentry);
 	int (*inode_readlink) (struct dentry *dentry);
 	int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
-	int (*inode_permission) (struct inode *inode, int mask, struct nameidata *nd);
+	int (*inode_permission) (struct inode *inode, int mask);
 	int (*inode_setattr)	(struct dentry *dentry, struct iattr *attr);
 	int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
 	void (*inode_delete) (struct inode *inode);
@@ -1628,7 +1628,7 @@
 			  struct inode *new_dir, struct dentry *new_dentry);
 int security_inode_readlink(struct dentry *dentry);
 int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
-int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd);
+int security_inode_permission(struct inode *inode, int mask);
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
 void security_inode_delete(struct inode *inode);
@@ -2021,8 +2021,7 @@
 	return 0;
 }
 
-static inline int security_inode_permission(struct inode *inode, int mask,
-					     struct nameidata *nd)
+static inline int security_inode_permission(struct inode *inode, int mask)
 {
 	return 0;
 }
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index f2d12d5..fd83f25 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -43,7 +43,7 @@
 }
 
 #ifdef CONFIG_TMPFS_POSIX_ACL
-int shmem_permission(struct inode *, int, struct nameidata *);
+int shmem_permission(struct inode *, int);
 int shmem_acl_init(struct inode *, struct inode *);
 void shmem_acl_destroy_inode(struct inode *);
 
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 9ff8e84..5ff9676 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -96,6 +96,7 @@
 /*
  * Common kmalloc functions provided by all allocators
  */
+void * __must_check __krealloc(const void *, size_t, gfp_t);
 void * __must_check krealloc(const void *, size_t, gfp_t);
 void kfree(const void *);
 size_t ksize(const void *);
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 950af63..dc5086f 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -189,7 +189,8 @@
 #define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
 #define AF_IUCV		32	/* IUCV sockets			*/
 #define AF_RXRPC	33	/* RxRPC sockets 		*/
-#define AF_MAX		34	/* For now.. */
+#define AF_ISDN		34	/* mISDN sockets 		*/
+#define AF_MAX		35	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -225,6 +226,7 @@
 #define PF_BLUETOOTH	AF_BLUETOOTH
 #define PF_IUCV		AF_IUCV
 #define PF_RXRPC	AF_RXRPC
+#define PF_ISDN		AF_ISDN
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 24141b4..d0437f3 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -947,6 +947,22 @@
 struct nsproxy;
 struct ctl_table_root;
 
+struct ctl_table_set {
+	struct list_head list;
+	struct ctl_table_set *parent;
+	int (*is_seen)(struct ctl_table_set *);
+};
+
+extern void setup_sysctl_set(struct ctl_table_set *p,
+	struct ctl_table_set *parent,
+	int (*is_seen)(struct ctl_table_set *));
+
+struct ctl_table_header;
+
+extern void sysctl_head_get(struct ctl_table_header *);
+extern void sysctl_head_put(struct ctl_table_header *);
+extern int sysctl_is_seen(struct ctl_table_header *);
+extern struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *);
 extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev);
 extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
 						struct ctl_table_header *prev);
@@ -1049,8 +1065,8 @@
 
 struct ctl_table_root {
 	struct list_head root_list;
-	struct list_head header_list;
-	struct list_head *(*lookup)(struct ctl_table_root *root,
+	struct ctl_table_set default_set;
+	struct ctl_table_set *(*lookup)(struct ctl_table_root *root,
 					   struct nsproxy *namespaces);
 	int (*permissions)(struct ctl_table_root *root,
 			struct nsproxy *namespaces, struct ctl_table *table);
@@ -1063,9 +1079,14 @@
 	struct ctl_table *ctl_table;
 	struct list_head ctl_entry;
 	int used;
+	int count;
 	struct completion *unregistering;
 	struct ctl_table *ctl_table_arg;
 	struct ctl_table_root *root;
+	struct ctl_table_set *set;
+	struct ctl_table *attached_by;
+	struct ctl_table *attached_to;
+	struct ctl_table_header *parent;
 };
 
 /* struct ctl_path describes where in the hierarchy a table is added */
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index 2dfa96b..7dd29b7 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -51,7 +51,7 @@
         struct sock		*peer;
         struct sock		*other;
 	struct list_head	link;
-        atomic_t                inflight;
+        atomic_long_t           inflight;
         spinlock_t		lock;
 	unsigned int		gc_candidate : 1;
         wait_queue_head_t       peer_wait;
diff --git a/include/net/ip.h b/include/net/ip.h
index b5862b9..250e6ef 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -188,6 +188,8 @@
 
 extern void ipfrag_init(void);
 
+extern void ip_static_sysctl_init(void);
+
 #ifdef CONFIG_INET
 #include <net/dst.h>
 
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 3855620..a8eb43c 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -38,7 +38,9 @@
 	struct proc_dir_entry 	*proc_net;
 	struct proc_dir_entry 	*proc_net_stat;
 
-	struct list_head	sysctl_table_headers;
+#ifdef CONFIG_SYSCTL
+	struct ctl_table_set	sysctls;
+#endif
 
 	struct net_device       *loopback_dev;          /* The loopback */
 
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index 0c96e7b..8d6e991 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include <linux/bug.h>
 
 #include <net/sock.h>
 
@@ -170,7 +171,7 @@
 {
 	struct request_sock *req = queue->rskq_accept_head;
 
-	BUG_TRAP(req != NULL);
+	WARN_ON(req == NULL);
 
 	queue->rskq_accept_head = req->dl_next;
 	if (queue->rskq_accept_head == NULL)
@@ -185,7 +186,7 @@
 	struct request_sock *req = reqsk_queue_remove(queue);
 	struct sock *child = req->sk;
 
-	BUG_TRAP(child != NULL);
+	WARN_ON(child == NULL);
 
 	sk_acceptq_removed(parent);
 	__reqsk_free(req);
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 474984f..96fb36c 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -638,7 +638,7 @@
 		return ERR_PTR(-EINVAL);
 	}
 
-	if (permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE], NULL)) {
+	if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) {
 		dput(dentry);
 		mntput(mqueue_mnt);
 		return ERR_PTR(-EACCES);
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 89bd6fb..657f8f8 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -45,6 +45,7 @@
 #include <linux/delayacct.h>
 #include <linux/cgroupstats.h>
 #include <linux/hash.h>
+#include <linux/namei.h>
 
 #include <asm/atomic.h>
 
diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c
index c1ef192..0d407e8 100644
--- a/kernel/exec_domain.c
+++ b/kernel/exec_domain.c
@@ -168,7 +168,6 @@
 	current->personality = personality;
 	oep = current_thread_info()->exec_domain;
 	current_thread_info()->exec_domain = ep;
-	set_fs_altroot();
 
 	module_put(oep->module);
 	return 0;
diff --git a/kernel/exit.c b/kernel/exit.c
index 6cdf607..0caf590 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -565,8 +565,6 @@
 	if (atomic_dec_and_test(&fs->count)) {
 		path_put(&fs->root);
 		path_put(&fs->pwd);
-		if (fs->altroot.dentry)
-			path_put(&fs->altroot);
 		kmem_cache_free(fs_cachep, fs);
 	}
 }
diff --git a/kernel/fork.c b/kernel/fork.c
index abb3ed6..5e050c1 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -657,13 +657,6 @@
 		path_get(&old->root);
 		fs->pwd = old->pwd;
 		path_get(&old->pwd);
-		if (old->altroot.dentry) {
-			fs->altroot = old->altroot;
-			path_get(&old->altroot);
-		} else {
-			fs->altroot.mnt = NULL;
-			fs->altroot.dentry = NULL;
-		}
 		read_unlock(&old->lock);
 	}
 	return fs;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 35a50db..911d846 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -160,12 +160,13 @@
 static struct ctl_table_root sysctl_table_root;
 static struct ctl_table_header root_table_header = {
 	.ctl_table = root_table,
-	.ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list),
+	.ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),
 	.root = &sysctl_table_root,
+	.set = &sysctl_table_root.default_set,
 };
 static struct ctl_table_root sysctl_table_root = {
 	.root_list = LIST_HEAD_INIT(sysctl_table_root.root_list),
-	.header_list = LIST_HEAD_INIT(root_table_header.ctl_entry),
+	.default_set.list = LIST_HEAD_INIT(root_table_header.ctl_entry),
 };
 
 static struct ctl_table kern_table[];
@@ -1386,6 +1387,9 @@
 		spin_unlock(&sysctl_lock);
 		wait_for_completion(&wait);
 		spin_lock(&sysctl_lock);
+	} else {
+		/* anything non-NULL; we'll never dereference it */
+		p->unregistering = ERR_PTR(-EINVAL);
 	}
 	/*
 	 * do not remove from the list until nobody holds it; walking the
@@ -1394,6 +1398,32 @@
 	list_del_init(&p->ctl_entry);
 }
 
+void sysctl_head_get(struct ctl_table_header *head)
+{
+	spin_lock(&sysctl_lock);
+	head->count++;
+	spin_unlock(&sysctl_lock);
+}
+
+void sysctl_head_put(struct ctl_table_header *head)
+{
+	spin_lock(&sysctl_lock);
+	if (!--head->count)
+		kfree(head);
+	spin_unlock(&sysctl_lock);
+}
+
+struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head)
+{
+	if (!head)
+		BUG();
+	spin_lock(&sysctl_lock);
+	if (!use_table(head))
+		head = ERR_PTR(-ENOENT);
+	spin_unlock(&sysctl_lock);
+	return head;
+}
+
 void sysctl_head_finish(struct ctl_table_header *head)
 {
 	if (!head)
@@ -1403,14 +1433,20 @@
 	spin_unlock(&sysctl_lock);
 }
 
+static struct ctl_table_set *
+lookup_header_set(struct ctl_table_root *root, struct nsproxy *namespaces)
+{
+	struct ctl_table_set *set = &root->default_set;
+	if (root->lookup)
+		set = root->lookup(root, namespaces);
+	return set;
+}
+
 static struct list_head *
 lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces)
 {
-	struct list_head *header_list;
-	header_list = &root->header_list;
-	if (root->lookup)
-		header_list = root->lookup(root, namespaces);
-	return header_list;
+	struct ctl_table_set *set = lookup_header_set(root, namespaces);
+	return &set->list;
 }
 
 struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces,
@@ -1480,9 +1516,9 @@
 	int op = 0, rc;
 
 	if (oldval)
-		op |= 004;
+		op |= MAY_READ;
 	if (newval)
-		op |= 002;
+		op |= MAY_WRITE;
 	if (sysctl_perm(root, table, op))
 		return -EPERM;
 
@@ -1524,7 +1560,7 @@
 		if (n == table->ctl_name) {
 			int error;
 			if (table->child) {
-				if (sysctl_perm(root, table, 001))
+				if (sysctl_perm(root, table, MAY_EXEC))
 					return -EPERM;
 				name++;
 				nlen--;
@@ -1599,7 +1635,7 @@
 		mode >>= 6;
 	else if (in_egroup_p(0))
 		mode >>= 3;
-	if ((mode & op & 0007) == op)
+	if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0)
 		return 0;
 	return -EACCES;
 }
@@ -1609,7 +1645,7 @@
 	int error;
 	int mode;
 
-	error = security_sysctl(table, op);
+	error = security_sysctl(table, op & (MAY_READ | MAY_WRITE | MAY_EXEC));
 	if (error)
 		return error;
 
@@ -1644,6 +1680,52 @@
 
 core_initcall(sysctl_init);
 
+static int is_branch_in(struct ctl_table *branch, struct ctl_table *table)
+{
+	struct ctl_table *p;
+	const char *s = branch->procname;
+
+	/* branch should have named subdirectory as its first element */
+	if (!s || !branch->child)
+		return 0;
+
+	/* ... and nothing else */
+	if (branch[1].procname || branch[1].ctl_name)
+		return 0;
+
+	/* table should contain subdirectory with the same name */
+	for (p = table; p->procname || p->ctl_name; p++) {
+		if (!p->child)
+			continue;
+		if (p->procname && strcmp(p->procname, s) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/* see if attaching q to p would be an improvement */
+static void try_attach(struct ctl_table_header *p, struct ctl_table_header *q)
+{
+	struct ctl_table *to = p->ctl_table, *by = q->ctl_table;
+	int is_better = 0;
+	int not_in_parent = !p->attached_by;
+
+	while (is_branch_in(by, to)) {
+		if (by == q->attached_by)
+			is_better = 1;
+		if (to == p->attached_by)
+			not_in_parent = 1;
+		by = by->child;
+		to = to->child;
+	}
+
+	if (is_better && not_in_parent) {
+		q->attached_by = by;
+		q->attached_to = to;
+		q->parent = p;
+	}
+}
+
 /**
  * __register_sysctl_paths - register a sysctl hierarchy
  * @root: List of sysctl headers to register on
@@ -1720,10 +1802,10 @@
 	struct nsproxy *namespaces,
 	const struct ctl_path *path, struct ctl_table *table)
 {
-	struct list_head *header_list;
 	struct ctl_table_header *header;
 	struct ctl_table *new, **prevp;
 	unsigned int n, npath;
+	struct ctl_table_set *set;
 
 	/* Count the path components */
 	for (npath = 0; path[npath].ctl_name || path[npath].procname; ++npath)
@@ -1765,6 +1847,7 @@
 	header->unregistering = NULL;
 	header->root = root;
 	sysctl_set_parent(NULL, header->ctl_table);
+	header->count = 1;
 #ifdef CONFIG_SYSCTL_SYSCALL_CHECK
 	if (sysctl_check_table(namespaces, header->ctl_table)) {
 		kfree(header);
@@ -1772,8 +1855,20 @@
 	}
 #endif
 	spin_lock(&sysctl_lock);
-	header_list = lookup_header_list(root, namespaces);
-	list_add_tail(&header->ctl_entry, header_list);
+	header->set = lookup_header_set(root, namespaces);
+	header->attached_by = header->ctl_table;
+	header->attached_to = root_table;
+	header->parent = &root_table_header;
+	for (set = header->set; set; set = set->parent) {
+		struct ctl_table_header *p;
+		list_for_each_entry(p, &set->list, ctl_entry) {
+			if (p->unregistering)
+				continue;
+			try_attach(p, header);
+		}
+	}
+	header->parent->count++;
+	list_add_tail(&header->ctl_entry, &header->set->list);
 	spin_unlock(&sysctl_lock);
 
 	return header;
@@ -1828,8 +1923,37 @@
 
 	spin_lock(&sysctl_lock);
 	start_unregistering(header);
+	if (!--header->parent->count) {
+		WARN_ON(1);
+		kfree(header->parent);
+	}
+	if (!--header->count)
+		kfree(header);
 	spin_unlock(&sysctl_lock);
-	kfree(header);
+}
+
+int sysctl_is_seen(struct ctl_table_header *p)
+{
+	struct ctl_table_set *set = p->set;
+	int res;
+	spin_lock(&sysctl_lock);
+	if (p->unregistering)
+		res = 0;
+	else if (!set->is_seen)
+		res = 1;
+	else
+		res = set->is_seen(set);
+	spin_unlock(&sysctl_lock);
+	return res;
+}
+
+void setup_sysctl_set(struct ctl_table_set *p,
+	struct ctl_table_set *parent,
+	int (*is_seen)(struct ctl_table_set *))
+{
+	INIT_LIST_HEAD(&p->list);
+	p->parent = parent ? parent : &sysctl_table_root.default_set;
+	p->is_seen = is_seen;
 }
 
 #else /* !CONFIG_SYSCTL */
@@ -1848,6 +1972,16 @@
 {
 }
 
+void setup_sysctl_set(struct ctl_table_set *p,
+	struct ctl_table_set *parent,
+	int (*is_seen)(struct ctl_table_set *))
+{
+}
+
+void sysctl_head_put(struct ctl_table_header *head)
+{
+}
+
 #endif /* CONFIG_SYSCTL */
 
 /*
diff --git a/mm/filemap.c b/mm/filemap.c
index 2ed8b03..5de7633 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1758,8 +1758,9 @@
 	return notify_change(dentry, &newattrs);
 }
 
-int remove_suid(struct dentry *dentry)
+int file_remove_suid(struct file *file)
 {
+	struct dentry *dentry = file->f_path.dentry;
 	int killsuid = should_remove_suid(dentry);
 	int killpriv = security_inode_need_killpriv(dentry);
 	int error = 0;
@@ -1773,7 +1774,7 @@
 
 	return error;
 }
-EXPORT_SYMBOL(remove_suid);
+EXPORT_SYMBOL(file_remove_suid);
 
 static size_t __iovec_copy_from_user_inatomic(char *vaddr,
 			const struct iovec *iov, size_t base, size_t bytes)
@@ -2529,7 +2530,7 @@
 	if (count == 0)
 		goto out;
 
-	err = remove_suid(file->f_path.dentry);
+	err = file_remove_suid(file);
 	if (err)
 		goto out;
 
diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c
index 3e744ab..98a3f31 100644
--- a/mm/filemap_xip.c
+++ b/mm/filemap_xip.c
@@ -380,7 +380,7 @@
 	if (count == 0)
 		goto out_backing;
 
-	ret = remove_suid(filp->f_path.dentry);
+	ret = file_remove_suid(filp);
 	if (ret)
 		goto out_backing;
 
diff --git a/mm/shmem_acl.c b/mm/shmem_acl.c
index f5664c5..8e5aadd 100644
--- a/mm/shmem_acl.c
+++ b/mm/shmem_acl.c
@@ -191,7 +191,7 @@
  * shmem_permission  -  permission() inode operation
  */
 int
-shmem_permission(struct inode *inode, int mask, struct nameidata *nd)
+shmem_permission(struct inode *inode, int mask)
 {
 	return generic_permission(inode, mask, shmem_check_acl);
 }
diff --git a/mm/util.c b/mm/util.c
index 0efd830..9341ca7 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -3,6 +3,7 @@
 #include <linux/string.h>
 #include <linux/module.h>
 #include <linux/err.h>
+#include <linux/sched.h>
 #include <asm/uaccess.h>
 
 /**
@@ -69,6 +70,38 @@
 EXPORT_SYMBOL(kmemdup);
 
 /**
+ * __krealloc - like krealloc() but don't free @p.
+ * @p: object to reallocate memory for.
+ * @new_size: how many bytes of memory are required.
+ * @flags: the type of memory to allocate.
+ *
+ * This function is like krealloc() except it never frees the originally
+ * allocated buffer. Use this if you don't want to free the buffer immediately
+ * like, for example, with RCU.
+ */
+void *__krealloc(const void *p, size_t new_size, gfp_t flags)
+{
+	void *ret;
+	size_t ks = 0;
+
+	if (unlikely(!new_size))
+		return ZERO_SIZE_PTR;
+
+	if (p)
+		ks = ksize(p);
+
+	if (ks >= new_size)
+		return (void *)p;
+
+	ret = kmalloc_track_caller(new_size, flags);
+	if (ret && p)
+		memcpy(ret, p, ks);
+
+	return ret;
+}
+EXPORT_SYMBOL(__krealloc);
+
+/**
  * krealloc - reallocate memory. The contents will remain unchanged.
  * @p: object to reallocate memory for.
  * @new_size: how many bytes of memory are required.
@@ -82,24 +115,16 @@
 void *krealloc(const void *p, size_t new_size, gfp_t flags)
 {
 	void *ret;
-	size_t ks = 0;
 
 	if (unlikely(!new_size)) {
 		kfree(p);
 		return ZERO_SIZE_PTR;
 	}
 
-	if (p)
-		ks = ksize(p);
-
-	if (ks >= new_size)
-		return (void *)p;
-
-	ret = kmalloc_track_caller(new_size, flags);
-	if (ret && p) {
-		memcpy(ret, p, ks);
+	ret = __krealloc(p, new_size, flags);
+	if (ret && p != ret)
 		kfree(p);
-	}
+
 	return ret;
 }
 EXPORT_SYMBOL(krealloc);
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 07b5b82..0c85042 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -959,7 +959,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -986,7 +986,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index 690bc3a..1a58af5 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -93,28 +93,20 @@
 
 static int __init ebtable_filter_init(void)
 {
-	int i, j, ret;
+	int ret;
 
 	ret = ebt_register_table(&frame_filter);
 	if (ret < 0)
 		return ret;
-	for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
-		if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
-			goto cleanup;
-	return ret;
-cleanup:
-	for (j = 0; j < i; j++)
-		nf_unregister_hook(&ebt_ops_filter[j]);
-	ebt_unregister_table(&frame_filter);
+	ret = nf_register_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
+	if (ret < 0)
+		ebt_unregister_table(&frame_filter);
 	return ret;
 }
 
 static void __exit ebtable_filter_fini(void)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
-		nf_unregister_hook(&ebt_ops_filter[i]);
+	nf_unregister_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
 	ebt_unregister_table(&frame_filter);
 }
 
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index 5b495fe..f60c1e7 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -100,28 +100,20 @@
 
 static int __init ebtable_nat_init(void)
 {
-	int i, ret, j;
+	int ret;
 
 	ret = ebt_register_table(&frame_nat);
 	if (ret < 0)
 		return ret;
-	for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
-		if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
-			goto cleanup;
-	return ret;
-cleanup:
-	for (j = 0; j < i; j++)
-		nf_unregister_hook(&ebt_ops_nat[j]);
-	ebt_unregister_table(&frame_nat);
+	ret = nf_register_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
+	if (ret < 0)
+		ebt_unregister_table(&frame_nat);
 	return ret;
 }
 
 static void __exit ebtable_nat_fini(void)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
-		nf_unregister_hook(&ebt_ops_nat[i]);
+	nf_unregister_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
 	ebt_unregister_table(&frame_nat);
 }
 
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 8a28fc9..dd61dca 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -285,7 +285,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -315,7 +315,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
@@ -366,7 +366,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -402,7 +402,7 @@
 		for (; list; list=list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
diff --git a/net/core/dev.c b/net/core/dev.c
index 53af784..8d13a9b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1973,7 +1973,7 @@
 			struct sk_buff *skb = clist;
 			clist = clist->next;
 
-			BUG_TRAP(!atomic_read(&skb->users));
+			WARN_ON(atomic_read(&skb->users));
 			__kfree_skb(skb);
 		}
 	}
@@ -3847,7 +3847,7 @@
 		dev->uninit(dev);
 
 	/* Notifier chain MUST detach us from master device. */
-	BUG_TRAP(!dev->master);
+	WARN_ON(dev->master);
 
 	/* Remove entries from kobject tree */
 	netdev_unregister_kobject(dev);
@@ -4169,9 +4169,9 @@
 
 		/* paranoia */
 		BUG_ON(atomic_read(&dev->refcnt));
-		BUG_TRAP(!dev->ip_ptr);
-		BUG_TRAP(!dev->ip6_ptr);
-		BUG_TRAP(!dev->dn_ptr);
+		WARN_ON(dev->ip_ptr);
+		WARN_ON(dev->ip6_ptr);
+		WARN_ON(dev->dn_ptr);
 
 		if (dev->destructor)
 			dev->destructor(dev);
diff --git a/net/core/request_sock.c b/net/core/request_sock.c
index 2d3035d..7552495 100644
--- a/net/core/request_sock.c
+++ b/net/core/request_sock.c
@@ -123,7 +123,7 @@
 		}
 	}
 
-	BUG_TRAP(lopt->qlen == 0);
+	WARN_ON(lopt->qlen != 0);
 	if (lopt_size > PAGE_SIZE)
 		vfree(lopt);
 	else
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index e411567..4e0c922 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1200,7 +1200,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -1229,7 +1229,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
@@ -1475,7 +1475,7 @@
 		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + frag->size;
 		if ((copy = end - offset) > 0) {
@@ -1503,7 +1503,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
@@ -1552,7 +1552,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -1581,7 +1581,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
@@ -1629,7 +1629,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -1662,7 +1662,7 @@
 			__wsum csum2;
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
@@ -2373,7 +2373,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -2397,7 +2397,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
diff --git a/net/core/stream.c b/net/core/stream.c
index 4a0ad15..a6b3437 100644
--- a/net/core/stream.c
+++ b/net/core/stream.c
@@ -192,13 +192,13 @@
 	__skb_queue_purge(&sk->sk_error_queue);
 
 	/* Next, the write queue. */
-	BUG_TRAP(skb_queue_empty(&sk->sk_write_queue));
+	WARN_ON(!skb_queue_empty(&sk->sk_write_queue));
 
 	/* Account for returned memory. */
 	sk_mem_reclaim(sk);
 
-	BUG_TRAP(!sk->sk_wmem_queued);
-	BUG_TRAP(!sk->sk_forward_alloc);
+	WARN_ON(sk->sk_wmem_queued);
+	WARN_ON(sk->sk_forward_alloc);
 
 	/* It is _impossible_ for the backlog to contain anything
 	 * when we get here.  All user references to this socket
diff --git a/net/core/user_dma.c b/net/core/user_dma.c
index 8c6b706..164b090 100644
--- a/net/core/user_dma.c
+++ b/net/core/user_dma.c
@@ -27,7 +27,6 @@
 
 #include <linux/dmaengine.h>
 #include <linux/socket.h>
-#include <linux/rtnetlink.h> /* for BUG_TRAP */
 #include <net/tcp.h>
 #include <net/netdma.h>
 
@@ -72,7 +71,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		copy = end - offset;
@@ -101,7 +100,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			copy = end - offset;
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index 32617e0..743d85f 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -164,7 +164,7 @@
 {
 	s64 delta = dccp_delta_seqno(s1, s2);
 
-	BUG_TRAP(delta >= 0);
+	WARN_ON(delta < 0);
 	return (u64)delta <= ndp + 1;
 }
 
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 08392ed..df2f110 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -413,7 +413,7 @@
 
 		/* Stop the REQUEST timer */
 		inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
-		BUG_TRAP(sk->sk_send_head != NULL);
+		WARN_ON(sk->sk_send_head == NULL);
 		__kfree_skb(sk->sk_send_head);
 		sk->sk_send_head = NULL;
 
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 2622ace..a835b88 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -283,7 +283,7 @@
 		 * ICMPs are not backlogged, hence we cannot get an established
 		 * socket here.
 		 */
-		BUG_TRAP(!req->sk);
+		WARN_ON(req->sk);
 
 		if (seq != dccp_rsk(req)->dreq_iss) {
 			NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index b74e8b2..da50912 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -186,7 +186,7 @@
 		 * ICMPs are not backlogged, hence we cannot get an established
 		 * socket here.
 		 */
-		BUG_TRAP(req->sk == NULL);
+		WARN_ON(req->sk != NULL);
 
 		if (seq != dccp_rsk(req)->dreq_iss) {
 			NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index a0b5600..b622d974 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -327,7 +327,7 @@
 	inet_csk_delack_init(sk);
 	__sk_dst_reset(sk);
 
-	BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
+	WARN_ON(inet->num && !icsk->icsk_bind_hash);
 
 	sk->sk_error_report(sk);
 	return err;
@@ -981,7 +981,7 @@
 	 */
 	local_bh_disable();
 	bh_lock_sock(sk);
-	BUG_TRAP(!sock_owned_by_user(sk));
+	WARN_ON(sock_owned_by_user(sk));
 
 	/* Have we already been destroyed by a softirq or backlog? */
 	if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
diff --git a/net/dccp/timer.c b/net/dccp/timer.c
index 3608d53..6a5b961 100644
--- a/net/dccp/timer.c
+++ b/net/dccp/timer.c
@@ -106,7 +106,7 @@
 	 *	-- Acks     in client-PARTOPEN state (sec. 8.1.5)
 	 *	-- CloseReq in server-CLOSEREQ state (sec. 8.3)
 	 *	-- Close    in   node-CLOSING  state (sec. 8.3)                */
-	BUG_TRAP(sk->sk_send_head != NULL);
+	WARN_ON(sk->sk_send_head == NULL);
 
 	/*
 	 * More than than 4MSL (8 minutes) has passed, a RESET(aborted) was
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f440a9f..8a3ac1f 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -148,10 +148,10 @@
 		return;
 	}
 
-	BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-	BUG_TRAP(!sk->sk_wmem_queued);
-	BUG_TRAP(!sk->sk_forward_alloc);
+	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+	WARN_ON(sk->sk_wmem_queued);
+	WARN_ON(sk->sk_forward_alloc);
 
 	kfree(inet->opt);
 	dst_release(sk->sk_dst_cache);
@@ -338,7 +338,7 @@
 	answer_flags = answer->flags;
 	rcu_read_unlock();
 
-	BUG_TRAP(answer_prot->slab != NULL);
+	WARN_ON(answer_prot->slab == NULL);
 
 	err = -ENOBUFS;
 	sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
@@ -658,8 +658,8 @@
 
 	lock_sock(sk2);
 
-	BUG_TRAP((1 << sk2->sk_state) &
-		 (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
+	WARN_ON(!((1 << sk2->sk_state) &
+		  (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE)));
 
 	sock_graft(sk2, newsock);
 
@@ -1439,6 +1439,10 @@
 
 	(void)sock_register(&inet_family_ops);
 
+#ifdef CONFIG_SYSCTL
+	ip_static_sysctl_init();
+#endif
+
 	/*
 	 *	Add all the base protocols.
 	 */
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 2e667e2..91d3d96 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -138,8 +138,8 @@
 {
 	struct net_device *dev = idev->dev;
 
-	BUG_TRAP(!idev->ifa_list);
-	BUG_TRAP(!idev->mc_list);
+	WARN_ON(idev->ifa_list);
+	WARN_ON(idev->mc_list);
 #ifdef NET_REFCNT_DEBUG
 	printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
 	       idev, dev ? dev->name : "NIL");
@@ -399,7 +399,7 @@
 	}
 	ipv4_devconf_setall(in_dev);
 	if (ifa->ifa_dev != in_dev) {
-		BUG_TRAP(!ifa->ifa_dev);
+		WARN_ON(ifa->ifa_dev);
 		in_dev_hold(in_dev);
 		ifa->ifa_dev = in_dev;
 	}
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index bb81c95..0c1ae68e 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -167,7 +167,7 @@
 success:
 	if (!inet_csk(sk)->icsk_bind_hash)
 		inet_bind_hash(sk, tb, snum);
-	BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb);
+	WARN_ON(inet_csk(sk)->icsk_bind_hash != tb);
 	ret = 0;
 
 fail_unlock:
@@ -260,7 +260,7 @@
 	}
 
 	newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);
-	BUG_TRAP(newsk->sk_state != TCP_SYN_RECV);
+	WARN_ON(newsk->sk_state == TCP_SYN_RECV);
 out:
 	release_sock(sk);
 	return newsk;
@@ -386,7 +386,7 @@
 		    ireq->rmt_addr == raddr &&
 		    ireq->loc_addr == laddr &&
 		    AF_INET_FAMILY(req->rsk_ops->family)) {
-			BUG_TRAP(!req->sk);
+			WARN_ON(req->sk);
 			*prevp = prev;
 			break;
 		}
@@ -539,14 +539,14 @@
  */
 void inet_csk_destroy_sock(struct sock *sk)
 {
-	BUG_TRAP(sk->sk_state == TCP_CLOSE);
-	BUG_TRAP(sock_flag(sk, SOCK_DEAD));
+	WARN_ON(sk->sk_state != TCP_CLOSE);
+	WARN_ON(!sock_flag(sk, SOCK_DEAD));
 
 	/* It cannot be in hash table! */
-	BUG_TRAP(sk_unhashed(sk));
+	WARN_ON(!sk_unhashed(sk));
 
 	/* If it has not 0 inet_sk(sk)->num, it must be bound */
-	BUG_TRAP(!inet_sk(sk)->num || inet_csk(sk)->icsk_bind_hash);
+	WARN_ON(inet_sk(sk)->num && !inet_csk(sk)->icsk_bind_hash);
 
 	sk->sk_prot->destroy(sk);
 
@@ -629,7 +629,7 @@
 
 		local_bh_disable();
 		bh_lock_sock(child);
-		BUG_TRAP(!sock_owned_by_user(child));
+		WARN_ON(sock_owned_by_user(child));
 		sock_hold(child);
 
 		sk->sk_prot->disconnect(child, O_NONBLOCK);
@@ -647,7 +647,7 @@
 		sk_acceptq_removed(sk);
 		__reqsk_free(req);
 	}
-	BUG_TRAP(!sk->sk_ack_backlog);
+	WARN_ON(sk->sk_ack_backlog);
 }
 
 EXPORT_SYMBOL_GPL(inet_csk_listen_stop);
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index 0546a0b..6c52e08 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -134,8 +134,8 @@
 	struct sk_buff *fp;
 	struct netns_frags *nf;
 
-	BUG_TRAP(q->last_in & INET_FRAG_COMPLETE);
-	BUG_TRAP(del_timer(&q->timer) == 0);
+	WARN_ON(!(q->last_in & INET_FRAG_COMPLETE));
+	WARN_ON(del_timer(&q->timer) != 0);
 
 	/* Release all fragment data. */
 	fp = q->fragments;
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 115f537..4498190 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -305,7 +305,7 @@
 	inet->num = lport;
 	inet->sport = htons(lport);
 	sk->sk_hash = hash;
-	BUG_TRAP(sk_unhashed(sk));
+	WARN_ON(!sk_unhashed(sk));
 	__sk_add_node(sk, &head->chain);
 	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
 	write_unlock(lock);
@@ -342,7 +342,7 @@
 	rwlock_t *lock;
 	struct inet_ehash_bucket *head;
 
-	BUG_TRAP(sk_unhashed(sk));
+	WARN_ON(!sk_unhashed(sk));
 
 	sk->sk_hash = inet_sk_ehashfn(sk);
 	head = inet_ehash_bucket(hashinfo, sk->sk_hash);
@@ -367,7 +367,7 @@
 		return;
 	}
 
-	BUG_TRAP(sk_unhashed(sk));
+	WARN_ON(!sk_unhashed(sk));
 	list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
 	lock = &hashinfo->lhash_lock;
 
@@ -450,7 +450,7 @@
 			 */
 			inet_bind_bucket_for_each(tb, node, &head->chain) {
 				if (tb->ib_net == net && tb->port == port) {
-					BUG_TRAP(!hlist_empty(&tb->owners));
+					WARN_ON(hlist_empty(&tb->owners));
 					if (tb->fastreuse >= 0)
 						goto next_port;
 					if (!check_established(death_row, sk,
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index 75c2def..d985bd6 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -86,7 +86,7 @@
 			hashinfo->bhash_size)];
 	spin_lock(&bhead->lock);
 	tw->tw_tb = icsk->icsk_bind_hash;
-	BUG_TRAP(icsk->icsk_bind_hash);
+	WARN_ON(!icsk->icsk_bind_hash);
 	inet_twsk_add_bind_node(tw, &tw->tw_tb->owners);
 	spin_unlock(&bhead->lock);
 
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 38d38f0..2152d22 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -488,8 +488,8 @@
 		qp->q.fragments = head;
 	}
 
-	BUG_TRAP(head != NULL);
-	BUG_TRAP(FRAG_CB(head)->offset == 0);
+	WARN_ON(head == NULL);
+	WARN_ON(FRAG_CB(head)->offset != 0);
 
 	/* Allocate a new buffer for the datagram. */
 	ihlen = ip_hdrlen(head);
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 465544f..d533a89 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -118,7 +118,7 @@
 	__skb_pull(newskb, skb_network_offset(newskb));
 	newskb->pkt_type = PACKET_LOOPBACK;
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
-	BUG_TRAP(newskb->dst);
+	WARN_ON(!newskb->dst);
 	netif_rx(newskb);
 	return 0;
 }
diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c
index 3be4d07..082f5dd 100644
--- a/net/ipv4/netfilter/arptable_filter.c
+++ b/net/ipv4/netfilter/arptable_filter.c
@@ -55,32 +55,53 @@
 };
 
 /* The work comes in here from netfilter.c */
-static unsigned int arpt_hook(unsigned int hook,
-			      struct sk_buff *skb,
-			      const struct net_device *in,
-			      const struct net_device *out,
-			      int (*okfn)(struct sk_buff *))
+static unsigned int arpt_in_hook(unsigned int hook,
+				 struct sk_buff *skb,
+				 const struct net_device *in,
+				 const struct net_device *out,
+				 int (*okfn)(struct sk_buff *))
 {
-	return arpt_do_table(skb, hook, in, out, init_net.ipv4.arptable_filter);
+	return arpt_do_table(skb, hook, in, out,
+			     dev_net(in)->ipv4.arptable_filter);
+}
+
+static unsigned int arpt_out_hook(unsigned int hook,
+				  struct sk_buff *skb,
+				  const struct net_device *in,
+				  const struct net_device *out,
+				  int (*okfn)(struct sk_buff *))
+{
+	return arpt_do_table(skb, hook, in, out,
+			     dev_net(out)->ipv4.arptable_filter);
+}
+
+static unsigned int arpt_forward_hook(unsigned int hook,
+				      struct sk_buff *skb,
+				      const struct net_device *in,
+				      const struct net_device *out,
+				      int (*okfn)(struct sk_buff *))
+{
+	return arpt_do_table(skb, hook, in, out,
+			     dev_net(in)->ipv4.arptable_filter);
 }
 
 static struct nf_hook_ops arpt_ops[] __read_mostly = {
 	{
-		.hook		= arpt_hook,
+		.hook		= arpt_in_hook,
 		.owner		= THIS_MODULE,
 		.pf		= NF_ARP,
 		.hooknum	= NF_ARP_IN,
 		.priority	= NF_IP_PRI_FILTER,
 	},
 	{
-		.hook		= arpt_hook,
+		.hook		= arpt_out_hook,
 		.owner		= THIS_MODULE,
 		.pf		= NF_ARP,
 		.hooknum	= NF_ARP_OUT,
 		.priority	= NF_IP_PRI_FILTER,
 	},
 	{
-		.hook		= arpt_hook,
+		.hook		= arpt_forward_hook,
 		.owner		= THIS_MODULE,
 		.pf		= NF_ARP,
 		.hooknum	= NF_ARP_FORWARD,
diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c
index 2b472ac..db6d312 100644
--- a/net/ipv4/netfilter/iptable_security.c
+++ b/net/ipv4/netfilter/iptable_security.c
@@ -32,7 +32,7 @@
 	struct ipt_replace repl;
 	struct ipt_standard entries[3];
 	struct ipt_error term;
-} initial_table __initdata = {
+} initial_table __net_initdata = {
 	.repl = {
 		.name = "security",
 		.valid_hooks = SECURITY_VALID_HOOKS,
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index e4ab0ac..a507c5e 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1502,7 +1502,7 @@
 				    rth->fl.iif != 0 ||
 				    dst_metric_locked(&rth->u.dst, RTAX_MTU) ||
 				    !net_eq(dev_net(rth->u.dst.dev), net) ||
-				    !rt_is_expired(rth))
+				    rt_is_expired(rth))
 					continue;
 
 				if (new_mtu < 68 || new_mtu >= old_mtu) {
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 51bc24d..9d38005 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -299,6 +299,7 @@
 	ireq->rmt_port		= th->source;
 	ireq->loc_addr		= ip_hdr(skb)->daddr;
 	ireq->rmt_addr		= ip_hdr(skb)->saddr;
+	ireq->ecn_ok		= 0;
 	ireq->snd_wscale	= tcp_opt.snd_wscale;
 	ireq->rcv_wscale	= tcp_opt.rcv_wscale;
 	ireq->sack_ok		= tcp_opt.sack_ok;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 14ef202..d63e938 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -882,4 +882,11 @@
 	return 0;
 }
 
+/* set enough of tree skeleton to get rid of ordering problems */
+void __init ip_static_sysctl_init(void)
+{
+	static ctl_table table[1];
+	register_sysctl_paths(net_ipv4_ctl_path, table);
+}
+
 __initcall(sysctl_ipv4_init);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 0b491bf..1ab341e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1096,7 +1096,7 @@
 #if TCP_DEBUG
 	struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
 
-	BUG_TRAP(!skb || before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
+	WARN_ON(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
 #endif
 
 	if (inet_csk_ack_scheduled(sk)) {
@@ -1358,7 +1358,7 @@
 				goto found_ok_skb;
 			if (tcp_hdr(skb)->fin)
 				goto found_fin_ok;
-			BUG_TRAP(flags & MSG_PEEK);
+			WARN_ON(!(flags & MSG_PEEK));
 			skb = skb->next;
 		} while (skb != (struct sk_buff *)&sk->sk_receive_queue);
 
@@ -1421,8 +1421,8 @@
 
 			tp->ucopy.len = len;
 
-			BUG_TRAP(tp->copied_seq == tp->rcv_nxt ||
-				 (flags & (MSG_PEEK | MSG_TRUNC)));
+			WARN_ON(tp->copied_seq != tp->rcv_nxt &&
+				!(flags & (MSG_PEEK | MSG_TRUNC)));
 
 			/* Ugly... If prequeue is not empty, we have to
 			 * process it before releasing socket, otherwise
@@ -1844,7 +1844,7 @@
 	 */
 	local_bh_disable();
 	bh_lock_sock(sk);
-	BUG_TRAP(!sock_owned_by_user(sk));
+	WARN_ON(sock_owned_by_user(sk));
 
 	/* Have we already been destroyed by a softirq or backlog? */
 	if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
@@ -1973,7 +1973,7 @@
 	memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));
 	__sk_dst_reset(sk);
 
-	BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
+	WARN_ON(inet->num && !icsk->icsk_bind_hash);
 
 	sk->sk_error_report(sk);
 	return err;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 75efd24..67ccce2 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1629,10 +1629,10 @@
 out:
 
 #if FASTRETRANS_DEBUG > 0
-	BUG_TRAP((int)tp->sacked_out >= 0);
-	BUG_TRAP((int)tp->lost_out >= 0);
-	BUG_TRAP((int)tp->retrans_out >= 0);
-	BUG_TRAP((int)tcp_packets_in_flight(tp) >= 0);
+	WARN_ON((int)tp->sacked_out < 0);
+	WARN_ON((int)tp->lost_out < 0);
+	WARN_ON((int)tp->retrans_out < 0);
+	WARN_ON((int)tcp_packets_in_flight(tp) < 0);
 #endif
 	return flag;
 }
@@ -2181,7 +2181,7 @@
 	int err;
 	unsigned int mss;
 
-	BUG_TRAP(packets <= tp->packets_out);
+	WARN_ON(packets > tp->packets_out);
 	if (tp->lost_skb_hint) {
 		skb = tp->lost_skb_hint;
 		cnt = tp->lost_cnt_hint;
@@ -2610,7 +2610,7 @@
 	/* E. Check state exit conditions. State can be terminated
 	 *    when high_seq is ACKed. */
 	if (icsk->icsk_ca_state == TCP_CA_Open) {
-		BUG_TRAP(tp->retrans_out == 0);
+		WARN_ON(tp->retrans_out != 0);
 		tp->retrans_stamp = 0;
 	} else if (!before(tp->snd_una, tp->high_seq)) {
 		switch (icsk->icsk_ca_state) {
@@ -2972,9 +2972,9 @@
 	}
 
 #if FASTRETRANS_DEBUG > 0
-	BUG_TRAP((int)tp->sacked_out >= 0);
-	BUG_TRAP((int)tp->lost_out >= 0);
-	BUG_TRAP((int)tp->retrans_out >= 0);
+	WARN_ON((int)tp->sacked_out < 0);
+	WARN_ON((int)tp->lost_out < 0);
+	WARN_ON((int)tp->retrans_out < 0);
 	if (!tp->packets_out && tcp_is_sack(tp)) {
 		icsk = inet_csk(sk);
 		if (tp->lost_out) {
@@ -3877,7 +3877,7 @@
 			int i;
 
 			/* RCV.NXT must cover all the block! */
-			BUG_TRAP(!before(tp->rcv_nxt, sp->end_seq));
+			WARN_ON(before(tp->rcv_nxt, sp->end_seq));
 
 			/* Zap this SACK, by moving forward any other SACKS. */
 			for (i=this_sack+1; i < num_sacks; i++)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index a82df63..a2b06d0 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -418,7 +418,7 @@
 		/* ICMPs are not backlogged, hence we cannot get
 		   an established socket here.
 		 */
-		BUG_TRAP(!req->sk);
+		WARN_ON(req->sk);
 
 		if (seq != tcp_rsk(req)->snt_isn) {
 			NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 328e0cf..5ab6ba1 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -287,7 +287,7 @@
 	if (!tp->packets_out)
 		goto out;
 
-	BUG_TRAP(!tcp_write_queue_empty(sk));
+	WARN_ON(tcp_write_queue_empty(sk));
 
 	if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
 	    !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 74d543d..a7842c5 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -313,8 +313,10 @@
 void in6_dev_finish_destroy(struct inet6_dev *idev)
 {
 	struct net_device *dev = idev->dev;
-	BUG_TRAP(idev->addr_list==NULL);
-	BUG_TRAP(idev->mc_list==NULL);
+
+	WARN_ON(idev->addr_list != NULL);
+	WARN_ON(idev->mc_list != NULL);
+
 #ifdef NET_REFCNT_DEBUG
 	printk(KERN_DEBUG "in6_dev_finish_destroy: %s\n", dev ? dev->name : "NIL");
 #endif
@@ -517,8 +519,9 @@
 
 void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
 {
-	BUG_TRAP(ifp->if_next==NULL);
-	BUG_TRAP(ifp->lst_next==NULL);
+	WARN_ON(ifp->if_next != NULL);
+	WARN_ON(ifp->lst_next != NULL);
+
 #ifdef NET_REFCNT_DEBUG
 	printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");
 #endif
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 60461ad..c708ca8 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -150,7 +150,7 @@
 	answer_flags = answer->flags;
 	rcu_read_unlock();
 
-	BUG_TRAP(answer_prot->slab != NULL);
+	WARN_ON(answer_prot->slab == NULL);
 
 	err = -ENOBUFS;
 	sk = sk_alloc(net, PF_INET6, GFP_KERNEL, answer_prot);
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 87801cc..16d43f2 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -98,7 +98,7 @@
 		    ipv6_addr_equal(&treq->rmt_addr, raddr) &&
 		    ipv6_addr_equal(&treq->loc_addr, laddr) &&
 		    (!treq->iif || treq->iif == iif)) {
-			BUG_TRAP(req->sk == NULL);
+			WARN_ON(req->sk != NULL);
 			*prevp = prev;
 			return req;
 		}
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 00a8a5f..1646a56 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -28,7 +28,7 @@
 	struct hlist_head *list;
 	rwlock_t *lock;
 
-	BUG_TRAP(sk_unhashed(sk));
+	WARN_ON(!sk_unhashed(sk));
 
 	if (sk->sk_state == TCP_LISTEN) {
 		list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
@@ -202,7 +202,7 @@
 	 * in hash table socket with a funny identity. */
 	inet->num = lport;
 	inet->sport = htons(lport);
-	BUG_TRAP(sk_unhashed(sk));
+	WARN_ON(!sk_unhashed(sk));
 	__sk_add_node(sk, &head->chain);
 	sk->sk_hash = hash;
 	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 08ea2de..52dddc2 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -287,7 +287,7 @@
 			w->leaf = rt;
 			return 1;
 		}
-		BUG_TRAP(res!=0);
+		WARN_ON(res == 0);
 	}
 	w->leaf = NULL;
 	return 0;
@@ -778,7 +778,7 @@
 			pn->leaf = fib6_find_prefix(info->nl_net, pn);
 #if RT6_DEBUG >= 2
 			if (!pn->leaf) {
-				BUG_TRAP(pn->leaf != NULL);
+				WARN_ON(pn->leaf == NULL);
 				pn->leaf = info->nl_net->ipv6.ip6_null_entry;
 			}
 #endif
@@ -942,7 +942,7 @@
 
 #ifdef CONFIG_IPV6_SUBTREES
 	if (src_len) {
-		BUG_TRAP(saddr!=NULL);
+		WARN_ON(saddr == NULL);
 		if (fn && fn->subtree)
 			fn = fib6_locate_1(fn->subtree, saddr, src_len,
 					   offsetof(struct rt6_info, rt6i_src));
@@ -996,9 +996,9 @@
 		RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
 		iter++;
 
-		BUG_TRAP(!(fn->fn_flags&RTN_RTINFO));
-		BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT));
-		BUG_TRAP(fn->leaf==NULL);
+		WARN_ON(fn->fn_flags & RTN_RTINFO);
+		WARN_ON(fn->fn_flags & RTN_TL_ROOT);
+		WARN_ON(fn->leaf != NULL);
 
 		children = 0;
 		child = NULL;
@@ -1014,7 +1014,7 @@
 			fn->leaf = fib6_find_prefix(net, fn);
 #if RT6_DEBUG >= 2
 			if (fn->leaf==NULL) {
-				BUG_TRAP(fn->leaf);
+				WARN_ON(!fn->leaf);
 				fn->leaf = net->ipv6.ip6_null_entry;
 			}
 #endif
@@ -1025,16 +1025,17 @@
 		pn = fn->parent;
 #ifdef CONFIG_IPV6_SUBTREES
 		if (FIB6_SUBTREE(pn) == fn) {
-			BUG_TRAP(fn->fn_flags&RTN_ROOT);
+			WARN_ON(!(fn->fn_flags & RTN_ROOT));
 			FIB6_SUBTREE(pn) = NULL;
 			nstate = FWS_L;
 		} else {
-			BUG_TRAP(!(fn->fn_flags&RTN_ROOT));
+			WARN_ON(fn->fn_flags & RTN_ROOT);
 #endif
 			if (pn->right == fn) pn->right = child;
 			else if (pn->left == fn) pn->left = child;
 #if RT6_DEBUG >= 2
-			else BUG_TRAP(0);
+			else
+				WARN_ON(1);
 #endif
 			if (child)
 				child->parent = pn;
@@ -1154,14 +1155,14 @@
 
 #if RT6_DEBUG >= 2
 	if (rt->u.dst.obsolete>0) {
-		BUG_TRAP(fn==NULL);
+		WARN_ON(fn != NULL);
 		return -ENOENT;
 	}
 #endif
 	if (fn == NULL || rt == net->ipv6.ip6_null_entry)
 		return -ENOENT;
 
-	BUG_TRAP(fn->fn_flags&RTN_RTINFO);
+	WARN_ON(!(fn->fn_flags & RTN_RTINFO));
 
 	if (!(rt->rt6i_flags&RTF_CACHE)) {
 		struct fib6_node *pn = fn;
@@ -1266,7 +1267,7 @@
 			w->node = pn;
 #ifdef CONFIG_IPV6_SUBTREES
 			if (FIB6_SUBTREE(pn) == fn) {
-				BUG_TRAP(fn->fn_flags&RTN_ROOT);
+				WARN_ON(!(fn->fn_flags & RTN_ROOT));
 				w->state = FWS_L;
 				continue;
 			}
@@ -1281,7 +1282,7 @@
 				continue;
 			}
 #if RT6_DEBUG >= 2
-			BUG_TRAP(0);
+			WARN_ON(1);
 #endif
 		}
 	}
@@ -1323,7 +1324,7 @@
 			}
 			return 0;
 		}
-		BUG_TRAP(res==0);
+		WARN_ON(res != 0);
 	}
 	w->leaf = rt;
 	return 0;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 6407c64..6811901 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -116,7 +116,7 @@
 	__skb_pull(newskb, skb_network_offset(newskb));
 	newskb->pkt_type = PACKET_LOOPBACK;
 	newskb->ip_summed = CHECKSUM_UNNECESSARY;
-	BUG_TRAP(newskb->dst);
+	WARN_ON(!newskb->dst);
 
 	netif_rx(newskb);
 	return 0;
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index ad1cc5b..31295c8 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -164,8 +164,8 @@
 			calc_padlen(sizeof(*dstopt), 6));
 
 	hao->type = IPV6_TLV_HAO;
+	BUILD_BUG_ON(sizeof(*hao) != 18);
 	hao->length = sizeof(*hao) - 2;
-	BUG_TRAP(hao->length == 16);
 
 	len = ((char *)hao - (char *)dstopt) + sizeof(*hao);
 
@@ -174,7 +174,7 @@
 	memcpy(&iph->saddr, x->coaddr, sizeof(iph->saddr));
 	spin_unlock_bh(&x->lock);
 
-	BUG_TRAP(len == x->props.header_len);
+	WARN_ON(len != x->props.header_len);
 	dstopt->hdrlen = (x->props.header_len >> 3) - 1;
 
 	return 0;
@@ -317,7 +317,7 @@
 	x->props.header_len = sizeof(struct ipv6_destopt_hdr) +
 		calc_padlen(sizeof(struct ipv6_destopt_hdr), 6) +
 		sizeof(struct ipv6_destopt_hao);
-	BUG_TRAP(x->props.header_len == 24);
+	WARN_ON(x->props.header_len != 24);
 
 	return 0;
 }
@@ -380,7 +380,7 @@
 	rt2->rt_hdr.segments_left = 1;
 	memset(&rt2->reserved, 0, sizeof(rt2->reserved));
 
-	BUG_TRAP(rt2->rt_hdr.hdrlen == 2);
+	WARN_ON(rt2->rt_hdr.hdrlen != 2);
 
 	memcpy(&rt2->addr, &iph->daddr, sizeof(rt2->addr));
 	spin_lock_bh(&x->lock);
diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c
index a07abee..6e71310 100644
--- a/net/ipv6/netfilter/ip6table_security.c
+++ b/net/ipv6/netfilter/ip6table_security.c
@@ -31,7 +31,7 @@
 	struct ip6t_replace repl;
 	struct ip6t_standard entries[3];
 	struct ip6t_error term;
-} initial_table __initdata = {
+} initial_table __net_initdata = {
 	.repl = {
 		.name = "security",
 		.valid_hooks = SECURITY_VALID_HOOKS,
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index cf20bc4..52d06dd 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -416,8 +416,8 @@
 
 	fq_kill(fq);
 
-	BUG_TRAP(head != NULL);
-	BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0);
+	WARN_ON(head == NULL);
+	WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
 
 	/* Unfragmented part is taken from the first segment. */
 	payload_len = ((head->data - skb_network_header(head)) -
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 6ab957e..89184b5 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -473,8 +473,8 @@
 		fq->q.fragments = head;
 	}
 
-	BUG_TRAP(head != NULL);
-	BUG_TRAP(FRAG6_CB(head)->offset == 0);
+	WARN_ON(head == NULL);
+	WARN_ON(FRAG6_CB(head)->offset != 0);
 
 	/* Unfragmented part is taken from the first segment. */
 	payload_len = ((head->data - skb_network_header(head)) -
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 6a68eeb..a46badd 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -223,6 +223,7 @@
 
 	req->expires = 0UL;
 	req->retrans = 0;
+	ireq->ecn_ok		= 0;
 	ireq->snd_wscale	= tcp_opt.snd_wscale;
 	ireq->rcv_wscale	= tcp_opt.rcv_wscale;
 	ireq->sack_ok		= tcp_opt.sack_ok;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index ae45f98..cff778b 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -421,7 +421,7 @@
 		/* ICMPs are not backlogged, hence we cannot get
 		 * an established socket here.
 		 */
-		BUG_TRAP(req->sk == NULL);
+		WARN_ON(req->sk != NULL);
 
 		if (seq != tcp_rsk(req)->snt_isn) {
 			NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index f0fc46c..d628df9 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -96,8 +96,8 @@
 		return;
 	}
 
-	BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
 
 	atomic_dec(&pfkey_socks_nr);
 }
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index 3469bc7..4b2c769 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -95,7 +95,7 @@
 	newlen = newoff + t->len;
 	rcu_read_unlock();
 
-	new = krealloc(ct->ext, newlen, gfp);
+	new = __krealloc(ct->ext, newlen, gfp);
 	if (!new)
 		return NULL;
 
@@ -115,10 +115,10 @@
 		ct->ext = new;
 	}
 
-	ct->ext->offset[id] = newoff;
-	ct->ext->len = newlen;
-	memset((void *)ct->ext + newoff, 0, newlen - newoff);
-	return (void *)ct->ext + newoff;
+	new->offset[id] = newoff;
+	new->len = newlen;
+	memset((void *)new + newoff, 0, newlen - newoff);
+	return (void *)new + newoff;
 }
 EXPORT_SYMBOL(__nf_ct_ext_add);
 
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 98bfe27..b0eacc0 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -158,9 +158,10 @@
 		printk(KERN_ERR "Freeing alive netlink socket %p\n", sk);
 		return;
 	}
-	BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-	BUG_TRAP(!nlk_sk(sk)->groups);
+
+	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+	WARN_ON(nlk_sk(sk)->groups);
 }
 
 /* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index d56cae1..c718e7e 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -260,8 +260,8 @@
 
 static void packet_sock_destruct(struct sock *sk)
 {
-	BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+	WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
 
 	if (!sock_flag(sk, SOCK_DEAD)) {
 		printk("Attempt to release alive packet socket: %p\n", sk);
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 4b2682f..32e4891 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -660,9 +660,9 @@
 
 	rxrpc_purge_queue(&sk->sk_receive_queue);
 
-	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-	BUG_TRAP(sk_unhashed(sk));
-	BUG_TRAP(!sk->sk_socket);
+	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+	WARN_ON(!sk_unhashed(sk));
+	WARN_ON(sk->sk_socket);
 
 	if (!sock_flag(sk, SOCK_DEAD)) {
 		printk("Attempt to release alive rxrpc socket: %p\n", sk);
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 74e662c..d308c19 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -41,7 +41,7 @@
 			return;
 		}
 	}
-	BUG_TRAP(0);
+	WARN_ON(1);
 }
 EXPORT_SYMBOL(tcf_hash_destroy);
 
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 32c3f9d..38015b4 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -116,7 +116,7 @@
 			return;
 		}
 	}
-	BUG_TRAP(0);
+	WARN_ON(1);
 }
 
 static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 527db25..246f906 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -345,7 +345,7 @@
 			}
 		}
 	}
-	BUG_TRAP(0);
+	WARN_ON(1);
 	return 0;
 }
 
@@ -368,7 +368,7 @@
 	struct tc_u_common *tp_c = tp->data;
 	struct tc_u_hnode **hn;
 
-	BUG_TRAP(!ht->refcnt);
+	WARN_ON(ht->refcnt);
 
 	u32_clear_hnode(tp, ht);
 
@@ -380,7 +380,7 @@
 		}
 	}
 
-	BUG_TRAP(0);
+	WARN_ON(1);
 	return -ENOENT;
 }
 
@@ -389,7 +389,7 @@
 	struct tc_u_common *tp_c = tp->data;
 	struct tc_u_hnode *root_ht = xchg(&tp->root, NULL);
 
-	BUG_TRAP(root_ht != NULL);
+	WARN_ON(root_ht == NULL);
 
 	if (root_ht && --root_ht->refcnt == 0)
 		u32_destroy_hnode(tp, root_ht);
@@ -407,7 +407,7 @@
 		while ((ht = tp_c->hlist) != NULL) {
 			tp_c->hlist = ht->next;
 
-			BUG_TRAP(ht->refcnt == 0);
+			WARN_ON(ht->refcnt != 0);
 
 			kfree(ht);
 		}
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index 04faa83..6b517b9 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -162,7 +162,7 @@
 	qdisc_destroy(flow->q);
 	tcf_destroy_chain(&flow->filter_list);
 	if (flow->sock) {
-		pr_debug("atm_tc_put: f_count %d\n",
+		pr_debug("atm_tc_put: f_count %ld\n",
 			file_count(flow->sock->file));
 		flow->vcc->pop = flow->old_pop;
 		sockfd_put(flow->sock);
@@ -259,7 +259,7 @@
 	sock = sockfd_lookup(fd, &error);
 	if (!sock)
 		return error;	/* f_count++ */
-	pr_debug("atm_tc_change: f_count %d\n", file_count(sock->file));
+	pr_debug("atm_tc_change: f_count %ld\n", file_count(sock->file));
 	if (sock->ops->family != PF_ATMSVC && sock->ops->family != PF_ATMPVC) {
 		error = -EPROTOTYPE;
 		goto err_out;
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index f1d2f8e..14954bf 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -1175,7 +1175,7 @@
 				this->tparent->children = NULL;
 		}
 	} else {
-		BUG_TRAP(this->sibling == this);
+		WARN_ON(this->sibling != this);
 	}
 }
 
@@ -1699,7 +1699,7 @@
 {
 	struct cbq_sched_data *q = qdisc_priv(sch);
 
-	BUG_TRAP(!cl->filters);
+	WARN_ON(cl->filters);
 
 	tcf_destroy_chain(&cl->filter_list);
 	qdisc_destroy(cl->q);
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 43abd4d..fd2a6ca 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -746,5 +746,5 @@
 {
 	netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
 	shutdown_scheduler_queue(dev, &dev->rx_queue, NULL);
-	BUG_TRAP(!timer_pending(&dev->watchdog_timer));
+	WARN_ON(timer_pending(&dev->watchdog_timer));
 }
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 30c999c6..75a4095 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -524,7 +524,7 @@
  */
 static inline void htb_activate(struct htb_sched *q, struct htb_class *cl)
 {
-	BUG_TRAP(!cl->level && cl->un.leaf.q && cl->un.leaf.q->q.qlen);
+	WARN_ON(cl->level || !cl->un.leaf.q || !cl->un.leaf.q->q.qlen);
 
 	if (!cl->prio_activity) {
 		cl->prio_activity = 1 << (cl->un.leaf.aprio = cl->un.leaf.prio);
@@ -542,7 +542,7 @@
  */
 static inline void htb_deactivate(struct htb_sched *q, struct htb_class *cl)
 {
-	BUG_TRAP(cl->prio_activity);
+	WARN_ON(!cl->prio_activity);
 
 	htb_deactivate_prios(q, cl);
 	cl->prio_activity = 0;
@@ -757,7 +757,7 @@
 		u32 *pid;
 	} stk[TC_HTB_MAXDEPTH], *sp = stk;
 
-	BUG_TRAP(tree->rb_node);
+	WARN_ON(!tree->rb_node);
 	sp->root = tree->rb_node;
 	sp->pptr = pptr;
 	sp->pid = pid;
@@ -777,7 +777,7 @@
 				*sp->pptr = (*sp->pptr)->rb_left;
 			if (sp > stk) {
 				sp--;
-				BUG_TRAP(*sp->pptr);
+				WARN_ON(!*sp->pptr);
 				if (!*sp->pptr)
 					return NULL;
 				htb_next_rb_node(sp->pptr);
@@ -792,7 +792,7 @@
 			sp->pid = cl->un.inner.last_ptr_id + prio;
 		}
 	}
-	BUG_TRAP(0);
+	WARN_ON(1);
 	return NULL;
 }
 
@@ -810,7 +810,7 @@
 
 	do {
 next:
-		BUG_TRAP(cl);
+		WARN_ON(!cl);
 		if (!cl)
 			return NULL;
 
@@ -1185,7 +1185,7 @@
 {
 	struct htb_class *parent = cl->parent;
 
-	BUG_TRAP(!cl->level && cl->un.leaf.q && !cl->prio_activity);
+	WARN_ON(cl->level || !cl->un.leaf.q || cl->prio_activity);
 
 	if (parent->cmode != HTB_CAN_SEND)
 		htb_safe_rb_erase(&parent->pq_node, q->wait_pq + parent->level);
@@ -1205,7 +1205,7 @@
 static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
 {
 	if (!cl->level) {
-		BUG_TRAP(cl->un.leaf.q);
+		WARN_ON(!cl->un.leaf.q);
 		qdisc_destroy(cl->un.leaf.q);
 	}
 	gen_kill_estimator(&cl->bstats, &cl->rate_est);
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 73f5384..8589da6 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -536,14 +536,7 @@
 
 	opt.limit = q->limit;
 	opt.divisor = SFQ_HASH_DIVISOR;
-	opt.flows = 0;
-	if (q->tail != SFQ_DEPTH) {
-		unsigned int i;
-
-		for (i = 0; i < SFQ_HASH_DIVISOR; i++)
-			if (q->ht[i] != SFQ_DEPTH)
-				opt.flows++;
-	}
+	opt.flows = q->limit;
 
 	NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index ec2a0a3..8472b8b 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -464,7 +464,7 @@
 		spin_unlock_bh(&sctp_assocs_id_lock);
 	}
 
-	BUG_TRAP(!atomic_read(&asoc->rmem_alloc));
+	WARN_ON(atomic_read(&asoc->rmem_alloc));
 
 	if (asoc->base.malloced) {
 		kfree(asoc);
diff --git a/net/sysctl_net.c b/net/sysctl_net.c
index 63ada43..cefbc36 100644
--- a/net/sysctl_net.c
+++ b/net/sysctl_net.c
@@ -29,10 +29,15 @@
 #include <linux/if_tr.h>
 #endif
 
-static struct list_head *
+static struct ctl_table_set *
 net_ctl_header_lookup(struct ctl_table_root *root, struct nsproxy *namespaces)
 {
-	return &namespaces->net_ns->sysctl_table_headers;
+	return &namespaces->net_ns->sysctls;
+}
+
+static int is_seen(struct ctl_table_set *set)
+{
+	return &current->nsproxy->net_ns->sysctls == set;
 }
 
 /* Return standard mode bits for table entry. */
@@ -53,13 +58,6 @@
 	.permissions = net_ctl_permissions,
 };
 
-static LIST_HEAD(net_sysctl_ro_tables);
-static struct list_head *net_ctl_ro_header_lookup(struct ctl_table_root *root,
-		struct nsproxy *namespaces)
-{
-	return &net_sysctl_ro_tables;
-}
-
 static int net_ctl_ro_header_perms(struct ctl_table_root *root,
 		struct nsproxy *namespaces, struct ctl_table *table)
 {
@@ -70,19 +68,18 @@
 }
 
 static struct ctl_table_root net_sysctl_ro_root = {
-	.lookup = net_ctl_ro_header_lookup,
 	.permissions = net_ctl_ro_header_perms,
 };
 
 static int sysctl_net_init(struct net *net)
 {
-	INIT_LIST_HEAD(&net->sysctl_table_headers);
+	setup_sysctl_set(&net->sysctls, NULL, is_seen);
 	return 0;
 }
 
 static void sysctl_net_exit(struct net *net)
 {
-	WARN_ON(!list_empty(&net->sysctl_table_headers));
+	WARN_ON(!list_empty(&net->sysctls.list));
 	return;
 }
 
@@ -98,6 +95,7 @@
 	if (ret)
 		goto out;
 	register_sysctl_root(&net_sysctl_root);
+	setup_sysctl_set(&net_sysctl_ro_root.default_set, NULL, NULL);
 	register_sysctl_root(&net_sysctl_ro_root);
 out:
 	return ret;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 70ceb16..015606b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -227,7 +227,7 @@
 
 static void __unix_insert_socket(struct hlist_head *list, struct sock *sk)
 {
-	BUG_TRAP(sk_unhashed(sk));
+	WARN_ON(!sk_unhashed(sk));
 	sk_add_node(sk, list);
 }
 
@@ -350,9 +350,9 @@
 
 	skb_queue_purge(&sk->sk_receive_queue);
 
-	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-	BUG_TRAP(sk_unhashed(sk));
-	BUG_TRAP(!sk->sk_socket);
+	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+	WARN_ON(!sk_unhashed(sk));
+	WARN_ON(sk->sk_socket);
 	if (!sock_flag(sk, SOCK_DEAD)) {
 		printk("Attempt to release alive unix socket: %p\n", sk);
 		return;
@@ -603,7 +603,7 @@
 	u->dentry = NULL;
 	u->mnt	  = NULL;
 	spin_lock_init(&u->lock);
-	atomic_set(&u->inflight, 0);
+	atomic_long_set(&u->inflight, 0);
 	INIT_LIST_HEAD(&u->link);
 	mutex_init(&u->readlock); /* single task reading lock */
 	init_waitqueue_head(&u->peer_wait);
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index ebdff3d..2a27b84 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -127,7 +127,7 @@
 	if(s) {
 		struct unix_sock *u = unix_sk(s);
 		spin_lock(&unix_gc_lock);
-		if (atomic_inc_return(&u->inflight) == 1) {
+		if (atomic_long_inc_return(&u->inflight) == 1) {
 			BUG_ON(!list_empty(&u->link));
 			list_add_tail(&u->link, &gc_inflight_list);
 		} else {
@@ -145,7 +145,7 @@
 		struct unix_sock *u = unix_sk(s);
 		spin_lock(&unix_gc_lock);
 		BUG_ON(list_empty(&u->link));
-		if (atomic_dec_and_test(&u->inflight))
+		if (atomic_long_dec_and_test(&u->inflight))
 			list_del_init(&u->link);
 		unix_tot_inflight--;
 		spin_unlock(&unix_gc_lock);
@@ -237,17 +237,17 @@
 
 static void dec_inflight(struct unix_sock *usk)
 {
-	atomic_dec(&usk->inflight);
+	atomic_long_dec(&usk->inflight);
 }
 
 static void inc_inflight(struct unix_sock *usk)
 {
-	atomic_inc(&usk->inflight);
+	atomic_long_inc(&usk->inflight);
 }
 
 static void inc_inflight_move_tail(struct unix_sock *u)
 {
-	atomic_inc(&u->inflight);
+	atomic_long_inc(&u->inflight);
 	/*
 	 * If this is still a candidate, move it to the end of the
 	 * list, so that it's checked even if it was already passed
@@ -288,11 +288,11 @@
 	 * before the detach without atomicity guarantees.
 	 */
 	list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
-		int total_refs;
-		int inflight_refs;
+		long total_refs;
+		long inflight_refs;
 
 		total_refs = file_count(u->sk.sk_socket->file);
-		inflight_refs = atomic_read(&u->inflight);
+		inflight_refs = atomic_long_read(&u->inflight);
 
 		BUG_ON(inflight_refs < 1);
 		BUG_ON(total_refs < inflight_refs);
@@ -324,7 +324,7 @@
 		/* Move cursor to after the current position. */
 		list_move(&cursor, &u->link);
 
-		if (atomic_read(&u->inflight) > 0) {
+		if (atomic_long_read(&u->inflight) > 0) {
 			list_move_tail(&u->link, &gc_inflight_list);
 			u->gc_candidate = 0;
 			scan_children(&u->sk, inc_inflight_move_tail, NULL);
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index 23a2cc0..96036cf 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -718,7 +718,7 @@
 	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
 		int end;
 
-		BUG_TRAP(start <= offset + len);
+		WARN_ON(start > offset + len);
 
 		end = start + skb_shinfo(skb)->frags[i].size;
 		if ((copy = end - offset) > 0) {
@@ -748,7 +748,7 @@
 		for (; list; list = list->next) {
 			int end;
 
-			BUG_TRAP(start <= offset + len);
+			WARN_ON(start > offset + len);
 
 			end = start + list->len;
 			if ((copy = end - offset) > 0) {
diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
index 800f669..c609a4b 100644
--- a/net/xfrm/xfrm_ipcomp.c
+++ b/net/xfrm/xfrm_ipcomp.c
@@ -22,7 +22,6 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/percpu.h>
-#include <linux/rtnetlink.h>
 #include <linux/smp.h>
 #include <linux/vmalloc.h>
 #include <net/ip.h>
@@ -251,7 +250,7 @@
 			break;
 	}
 
-	BUG_TRAP(pos);
+	WARN_ON(!pos);
 
 	if (--pos->users)
 		return;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 72fddaf..4c6914e 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -538,7 +538,7 @@
 
 void __xfrm_state_destroy(struct xfrm_state *x)
 {
-	BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
+	WARN_ON(x->km.state != XFRM_STATE_DEAD);
 
 	spin_lock_bh(&xfrm_state_lock);
 	list_del(&x->all);
diff --git a/security/capability.c b/security/capability.c
index 5b01c0b..63d10da 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -211,8 +211,7 @@
 	return 0;
 }
 
-static int cap_inode_permission(struct inode *inode, int mask,
-				struct nameidata *nd)
+static int cap_inode_permission(struct inode *inode, int mask)
 {
 	return 0;
 }
diff --git a/security/security.c b/security/security.c
index 59f23b5..ff70687 100644
--- a/security/security.c
+++ b/security/security.c
@@ -429,11 +429,11 @@
 	return security_ops->inode_follow_link(dentry, nd);
 }
 
-int security_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+int security_inode_permission(struct inode *inode, int mask)
 {
 	if (unlikely(IS_PRIVATE(inode)))
 		return 0;
-	return security_ops->inode_permission(inode, mask, nd);
+	return security_ops->inode_permission(inode, mask);
 }
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
@@ -442,6 +442,7 @@
 		return 0;
 	return security_ops->inode_setattr(dentry, attr);
 }
+EXPORT_SYMBOL_GPL(security_inode_setattr);
 
 int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 {
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3481cde..40d06c5 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2624,12 +2624,11 @@
 	return dentry_has_perm(current, NULL, dentry, FILE__READ);
 }
 
-static int selinux_inode_permission(struct inode *inode, int mask,
-				    struct nameidata *nd)
+static int selinux_inode_permission(struct inode *inode, int mask)
 {
 	int rc;
 
-	rc = secondary_ops->inode_permission(inode, mask, nd);
+	rc = secondary_ops->inode_permission(inode, mask);
 	if (rc)
 		return rc;
 
@@ -5654,27 +5653,20 @@
 static int __init selinux_nf_ip_init(void)
 {
 	int err = 0;
-	u32 iter;
 
 	if (!selinux_enabled)
 		goto out;
 
 	printk(KERN_DEBUG "SELinux:  Registering netfilter hooks\n");
 
-	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) {
-		err = nf_register_hook(&selinux_ipv4_ops[iter]);
-		if (err)
-			panic("SELinux: nf_register_hook for IPv4: error %d\n",
-			      err);
-	}
+	err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
+	if (err)
+		panic("SELinux: nf_register_hooks for IPv4: error %d\n", err);
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) {
-		err = nf_register_hook(&selinux_ipv6_ops[iter]);
-		if (err)
-			panic("SELinux: nf_register_hook for IPv6: error %d\n",
-			      err);
-	}
+	err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
+	if (err)
+		panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
 #endif	/* IPV6 */
 
 out:
@@ -5686,15 +5678,11 @@
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 static void selinux_nf_ip_exit(void)
 {
-	u32 iter;
-
 	printk(KERN_DEBUG "SELinux:  Unregistering netfilter hooks\n");
 
-	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
-		nf_unregister_hook(&selinux_ipv4_ops[iter]);
+	nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
-		nf_unregister_hook(&selinux_ipv6_ops[iter]);
+	nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
 #endif	/* IPV6 */
 }
 #endif
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ee5a51c..1b40e55 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -522,8 +522,7 @@
  *
  * Returns 0 if access is permitted, -EACCES otherwise
  */
-static int smack_inode_permission(struct inode *inode, int mask,
-				  struct nameidata *nd)
+static int smack_inode_permission(struct inode *inode, int mask)
 {
 	/*
 	 * No permission to check. Existence test. Yup, it's there.