Linux-2.6.12-rc2

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

Let it rip!
diff --git a/drivers/isdn/pcbit/Kconfig b/drivers/isdn/pcbit/Kconfig
new file mode 100644
index 0000000..f06997f
--- /dev/null
+++ b/drivers/isdn/pcbit/Kconfig
@@ -0,0 +1,14 @@
+#
+# Config.in for PCBIT ISDN driver
+#
+config ISDN_DRV_PCBIT
+	tristate "PCBIT-D support"
+	depends on ISDN_I4L && ISA && (BROKEN || !PPC)
+	help
+	  This enables support for the PCBIT ISDN-card.  This card is
+	  manufactured in Portugal by Octal.  For running this card,
+	  additional firmware is necessary, which has to be downloaded into
+	  the card using a utility which is distributed separately.  See
+	  <file:Documentation/isdn/README> and
+	  <file:Documentation/isdn/README.pcbit> for more information.
+
diff --git a/drivers/isdn/pcbit/Makefile b/drivers/isdn/pcbit/Makefile
new file mode 100644
index 0000000..2d026c3
--- /dev/null
+++ b/drivers/isdn/pcbit/Makefile
@@ -0,0 +1,9 @@
+# Makefile for the pcbit ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_PCBIT)	+= pcbit.o
+
+# Multipart objects.
+
+pcbit-y				:= module.o edss1.o drv.o layer2.o capi.o callbacks.o
diff --git a/drivers/isdn/pcbit/callbacks.c b/drivers/isdn/pcbit/callbacks.c
new file mode 100644
index 0000000..692ec72
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.c
@@ -0,0 +1,367 @@
+/*
+ * Callbacks for the FSM
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * Fix: 19981230 - Carlos Morgado <chbm@techie.com>
+ * Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN 
+ * NULL pointer dereference in cb_in_1 (originally fixed in 2.0)
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+#include "callbacks.h"
+#include "capi.h"
+
+ushort last_ref_num = 1;
+
+/*
+ *  send_conn_req
+ *
+ */
+
+void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	      struct callb_data *cbdata) 
+{
+	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+
+#ifdef DEBUG
+        printk(KERN_DEBUG "Called Party Number: %s\n", 
+               cbdata->data.setup.CalledPN);
+#endif
+        /*
+         * hdr - kmalloc in capi_conn_req
+         *     - kfree   when msg has been sent
+         */
+
+        if ((len = capi_conn_req(cbdata->data.setup.CalledPN, &skb, 
+				 chan->proto)) < 0)
+        {
+                printk("capi_conn_req failed\n");
+                return;
+        }
+
+
+        refnum = last_ref_num++ & 0x7fffU;
+
+        chan->callref = 0;
+        chan->layer2link = 0;
+        chan->snum = 0;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_CONN_REQ, refnum, skb, len);
+}
+
+/*
+ *  rcv CONNECT
+ *  will go into ACTIVE state
+ *  send CONN_ACTIVE_RESP
+ *  send Select protocol request 
+ */
+
+void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	      struct callb_data *data) 
+{
+        isdn_ctrl ictl;
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+        if ((len=capi_conn_active_resp(chan, &skb)) < 0)
+        {
+                printk("capi_conn_active_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_CONN_ACTV_RESP, refnum, skb, len);
+
+
+        ictl.command = ISDN_STAT_DCONN;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+
+        /* ACTIVE D-channel */
+
+        /* Select protocol  */
+
+        if ((len=capi_select_proto_req(chan, &skb, 1 /*outgoing*/)) < 0) { 
+                printk("capi_select_proto_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+}
+
+
+/*
+ * Disconnect received (actually RELEASE COMPLETE) 
+ * This means we were not able to establish connection with remote
+ * Inform the big boss above
+ */
+void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+	      struct callb_data *data) 
+{
+        isdn_ctrl ictl;
+
+        ictl.command = ISDN_STAT_DHUP;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+
+/*
+ * Incoming call received
+ * inform user
+ */
+
+void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+	     struct callb_data *cbdata) 
+{
+        isdn_ctrl ictl;
+        unsigned short refnum;
+ 	struct sk_buff *skb;
+	int len;
+
+
+        ictl.command = ISDN_STAT_ICALL;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        
+        /*
+         *  ictl.num >= strlen() + strlen() + 5
+         */
+
+	if (cbdata->data.setup.CallingPN == NULL) {
+		printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n");
+		strcpy(ictl.parm.setup.phone, "0");
+	}
+	else {
+		strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+	}
+	if (cbdata->data.setup.CalledPN == NULL) {
+		printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n");
+		strcpy(ictl.parm.setup.eazmsn, "0");
+	}
+	else {
+		strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
+	}
+	ictl.parm.setup.si1 = 7;
+	ictl.parm.setup.si2 = 0;
+	ictl.parm.setup.plan = 0;
+	ictl.parm.setup.screen = 0;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "statstr: %s\n", ictl.num);
+#endif
+
+        dev->dev_if->statcallb(&ictl);
+
+        
+        if ((len=capi_conn_resp(chan, &skb)) < 0) {
+                printk(KERN_DEBUG "capi_conn_resp failed\n");
+                return;
+	}
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_CONN_RESP, refnum, skb, len);
+}
+
+/*
+ * user has replied
+ * open the channel
+ * send CONNECT message CONNECT_ACTIVE_REQ in CAPI
+ */
+
+void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+	     struct callb_data *data)
+{
+        unsigned short refnum;
+	struct sk_buff *skb;
+        int len;
+        
+        if ((len = capi_conn_active_req(chan, &skb)) < 0) {        
+                printk(KERN_DEBUG "capi_conn_active_req failed\n");
+                return;
+        }
+
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+	printk(KERN_DEBUG "sending MSG_CONN_ACTV_REQ\n");
+        pcbit_l2_write(dev, MSG_CONN_ACTV_REQ, refnum, skb, len);
+}
+
+/*
+ * CONN_ACK arrived
+ * start b-proto selection
+ *
+ */
+
+void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	     struct callb_data *data)
+{
+        unsigned short refnum;
+ 	struct sk_buff *skb;
+	int len;
+        
+        if ((len = capi_select_proto_req(chan, &skb, 0 /*incoming*/)) < 0)
+        {
+                printk("capi_select_proto_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+
+}
+
+
+/*
+ * Received disconnect ind on active state
+ * send disconnect resp
+ * send msg to user
+ */
+void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data)
+{
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+        isdn_ctrl ictl;
+  
+        if ((len = capi_disc_resp(chan, &skb)) < 0) {
+                printk("capi_disc_resp failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_DISC_RESP, refnum, skb, len);    
+
+        ictl.command = ISDN_STAT_BHUP;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+        
+/*
+ *  User HANGUP on active/call proceeding state
+ *  send disc.req
+ */
+void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data)
+{
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+        if ((len = capi_disc_req(chan->callref, &skb, CAUSE_NORMAL)) < 0)
+        {
+                printk("capi_disc_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb, len);  
+}
+
+/*
+ *  Disc confirm received send BHUP
+ *  Problem: when the HL driver sends the disc req itself
+ *           LL receives BHUP
+ */
+void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data)
+{
+        isdn_ctrl ictl;
+
+        ictl.command = ISDN_STAT_BHUP;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		struct callb_data *data)
+{
+}
+
+/*
+ * send activate b-chan protocol
+ */
+void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	       struct callb_data *data) 
+{
+ 	struct sk_buff *skb;
+	int len;
+        ushort refnum;
+
+        if ((len = capi_activate_transp_req(chan, &skb)) < 0)
+        {
+                printk("capi_conn_activate_transp_req failed\n");
+                return;
+        }
+
+        refnum = last_ref_num++ & 0x7fffU;
+        chan->s_refnum = refnum;
+
+        pcbit_l2_write(dev, MSG_ACT_TRANSP_REQ, refnum, skb, len);
+}
+
+/*
+ *  Inform User that the B-channel is available
+ */
+void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+	     struct callb_data *data) 
+{
+        isdn_ctrl ictl;
+
+        ictl.command = ISDN_STAT_BCONN;
+        ictl.driver=dev->id;
+        ictl.arg=chan->id;
+        dev->dev_if->statcallb(&ictl);
+}
+
+
+
diff --git a/drivers/isdn/pcbit/callbacks.h b/drivers/isdn/pcbit/callbacks.h
new file mode 100644
index 0000000..f510dc5
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.h
@@ -0,0 +1,49 @@
+/*
+ * Callbacks prototypes for FSM
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef CALLBACKS_H
+#define CALLBACKS_H
+
+
+extern void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		     struct callb_data *data);
+
+extern void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		     struct callb_data *data);
+
+extern void cb_out_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		     struct callb_data *data);
+
+extern void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+extern void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+extern void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+
+extern void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+extern void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+extern void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+
+extern void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		       struct callb_data *data);
+
+extern void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		      struct callb_data *data);
+extern void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan, 
+		    struct callb_data *data);
+
+#endif
+
+
diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c
new file mode 100644
index 0000000..29eb03a
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.c
@@ -0,0 +1,663 @@
+/*
+ * CAPI encoder/decoder for
+ * Portugal Telecom CAPI 2.0
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ *
+ * Not compatible with the AVM Gmbh. CAPI 2.0
+ *
+ */
+
+/*
+ *        Documentation:
+ *        - "Common ISDN API - Perfil Português - Versão 2.1",
+ *           Telecom Portugal, Fev 1992.
+ *        - "Common ISDN API - Especificação de protocolos para 
+ *           acesso aos canais B", Inesc, Jan 1994.
+ */
+
+/*
+ *        TODO: better decoding of Information Elements
+ *              for debug purposes mainly
+ *              encode our number in CallerPN and ConnectedPN
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/string.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "capi.h"
+
+
+/*
+ *  Encoding of CAPI messages
+ *
+ */
+
+int capi_conn_req(const char * calledPN, struct sk_buff **skb, int proto)
+{
+        ushort len;
+
+        /*
+         * length
+         *   AppInfoMask - 2
+         *   BC0         - 3
+         *   BC1         - 1
+         *   Chan        - 2
+         *   Keypad      - 1
+         *   CPN         - 1
+         *   CPSA        - 1
+         *   CalledPN    - 2 + strlen
+         *   CalledPSA   - 1
+         *   rest...     - 4
+         *   ----------------
+         *   Total        18 + strlen
+         */
+
+        len = 18 + strlen(calledPN);
+
+	if (proto == ISDN_PROTO_L2_TRANS)
+		len++;
+
+	if ((*skb = dev_alloc_skb(len)) == NULL) {
+    
+	        printk(KERN_WARNING "capi_conn_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        /* InfoElmMask */
+        *((ushort*) skb_put(*skb, 2)) = AppInfoMask; 
+
+	if (proto == ISDN_PROTO_L2_TRANS)
+	{
+		/* Bearer Capability - Mandatory*/
+		*(skb_put(*skb, 1)) = 3;        /* BC0.Length		*/
+		*(skb_put(*skb, 1)) = 0x80;     /* Speech		*/
+		*(skb_put(*skb, 1)) = 0x10;     /* Circuit Mode		*/
+		*(skb_put(*skb, 1)) = 0x23;     /* A-law		*/
+	}
+	else
+	{
+		/* Bearer Capability - Mandatory*/
+		*(skb_put(*skb, 1)) = 2;        /* BC0.Length		*/
+		*(skb_put(*skb, 1)) = 0x88;     /* Digital Information	*/
+		*(skb_put(*skb, 1)) = 0x90;     /* BC0.Octect4		*/
+	}
+
+        /* Bearer Capability - Optional*/
+        *(skb_put(*skb, 1)) = 0;        /* BC1.Length = 0                    */
+
+        *(skb_put(*skb, 1)) = 1;        /* ChannelID.Length = 1              */
+        *(skb_put(*skb, 1)) = 0x83;     /* Basic Interface - Any Channel     */
+
+        *(skb_put(*skb, 1)) = 0;        /* Keypad.Length = 0                 */
+                  
+
+        *(skb_put(*skb, 1)) = 0;        /* CallingPN.Length = 0              */
+        *(skb_put(*skb, 1)) = 0;        /* CallingPSA.Length = 0             */
+
+        /* Called Party Number */
+        *(skb_put(*skb, 1)) = strlen(calledPN) + 1;
+        *(skb_put(*skb, 1)) = 0x81;
+        memcpy(skb_put(*skb, strlen(calledPN)), calledPN, strlen(calledPN));
+
+        /* '#' */
+
+        *(skb_put(*skb, 1)) = 0;       /* CalledPSA.Length = 0     */
+
+        /* LLC.Length  = 0; */
+        /* HLC0.Length = 0; */
+        /* HLC1.Length = 0; */ 
+        /* UTUS.Length = 0; */
+        memset(skb_put(*skb, 4), 0, 4);
+
+        return len;
+}
+
+int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+        
+	if ((*skb = dev_alloc_skb(5)) == NULL) {
+    
+		printk(KERN_WARNING "capi_conn_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+        *(skb_put(*skb, 1)) = 0x01;  /* ACCEPT_CALL */
+        *(skb_put(*skb, 1)) = 0;
+        *(skb_put(*skb, 1)) = 0;
+
+        return 5;
+}
+
+int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+        /*
+         * 8 bytes
+         */
+        
+	if ((*skb = dev_alloc_skb(8)) == NULL) {
+    
+		printk(KERN_WARNING "capi_conn_active_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); 
+#endif
+
+        *(skb_put(*skb, 1)) = 0;       /*  BC.Length = 0;          */
+        *(skb_put(*skb, 1)) = 0;       /*  ConnectedPN.Length = 0  */
+        *(skb_put(*skb, 1)) = 0;       /*  PSA.Length              */
+        *(skb_put(*skb, 1)) = 0;       /*  LLC.Length = 0;         */
+        *(skb_put(*skb, 1)) = 0;       /*  HLC.Length = 0;         */
+        *(skb_put(*skb, 1)) = 0;       /*  UTUS.Length = 0;        */
+
+	return 8;
+}
+
+int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+        /*
+         * 2 bytes
+         */
+  
+	if ((*skb = dev_alloc_skb(2)) == NULL) {
+    
+		printk(KERN_WARNING "capi_conn_active_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        return 2;
+}
+
+
+int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb, 
+                          int outgoing)
+{
+
+        /*
+         * 18 bytes
+         */
+
+	if ((*skb = dev_alloc_skb(18)) == NULL) {
+    
+		printk(KERN_WARNING "capi_select_proto_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        /* Layer2 protocol */
+
+        switch (chan->proto) {
+        case ISDN_PROTO_L2_X75I: 
+                *(skb_put(*skb, 1)) = 0x05;            /* LAPB */
+                break;
+        case ISDN_PROTO_L2_HDLC:
+                *(skb_put(*skb, 1)) = 0x02;
+                break;
+	case ISDN_PROTO_L2_TRANS:
+		/* 
+		 *	Voice (a-law)
+		 */
+		*(skb_put(*skb, 1)) = 0x06;
+		break;
+        default:
+#ifdef DEBUG 
+                printk(KERN_DEBUG "Transparent\n");
+#endif
+                *(skb_put(*skb, 1)) = 0x03;
+                break;
+        }
+
+        *(skb_put(*skb, 1)) = (outgoing ? 0x02 : 0x42);    /* Don't ask */
+        *(skb_put(*skb, 1)) = 0x00;
+  
+        *((ushort *) skb_put(*skb, 2)) = MRU;
+
+ 
+        *(skb_put(*skb, 1)) = 0x08;           /* Modulo */
+        *(skb_put(*skb, 1)) = 0x07;           /* Max Window */
+  
+        *(skb_put(*skb, 1)) = 0x01;           /* No Layer3 Protocol */
+
+        /*
+         * 2 - layer3 MTU       [10]
+         *   - Modulo           [12]
+         *   - Window           
+         *   - layer1 proto     [14]
+         *   - bitrate
+         *   - sub-channel      [16]
+         *   - layer1dataformat [17]
+         */
+
+        memset(skb_put(*skb, 8), 0, 8);
+
+        return 18;
+}
+
+
+int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+
+	if ((*skb = dev_alloc_skb(7)) == NULL) {
+    
+		printk(KERN_WARNING "capi_activate_transp_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        
+        *(skb_put(*skb, 1)) = chan->layer2link; /* Layer2 id */
+        *(skb_put(*skb, 1)) = 0x00;             /* Transmit by default */
+
+        *((ushort *) skb_put(*skb, 2)) = MRU;
+
+        *(skb_put(*skb, 1)) = 0x01;             /* Enables reception*/
+
+        return 7;
+}
+
+int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb)
+{
+	ushort data_len;
+	
+
+	/*  
+	 * callref      - 2  
+	 * layer2link   - 1
+	 * wBlockLength - 2 
+	 * data         - 4
+	 * sernum       - 1
+	 */
+	
+	data_len = skb->len;
+
+	if(skb_headroom(skb) < 10)
+	{
+		printk(KERN_CRIT "No headspace (%u) on headroom %p for capi header\n", skb_headroom(skb), skb);
+	}
+	else
+	{	
+		skb_push(skb, 10);
+	}
+
+	*((u16 *) (skb->data)) = chan->callref;
+	skb->data[2] = chan->layer2link;
+	*((u16 *) (skb->data + 3)) = data_len;
+
+	chan->s_refnum = (chan->s_refnum + 1) % 8;
+	*((u32 *) (skb->data + 5)) = chan->s_refnum;
+
+	skb->data[9] = 0;                           /* HDLC frame number */
+
+	return 10;
+}
+
+int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb)
+		    
+{
+	if ((*skb = dev_alloc_skb(4)) == NULL) {
+    
+		printk(KERN_WARNING "capi_tdata_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = chan->callref;  
+
+        *(skb_put(*skb, 1)) = chan->layer2link;
+        *(skb_put(*skb, 1)) = chan->r_refnum;
+
+        return (*skb)->len;
+}
+
+int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause)
+{
+
+	if ((*skb = dev_alloc_skb(6)) == NULL) {
+    
+		printk(KERN_WARNING "capi_disc_req: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2) ) = callref;  
+
+        *(skb_put(*skb, 1)) = 2;                  /* Cause.Length = 2; */
+        *(skb_put(*skb, 1)) = 0x80;
+        *(skb_put(*skb, 1)) = 0x80 | cause;           
+
+        /* 
+         * Change it: we should send 'Sic transit gloria Mundi' here ;-) 
+         */
+
+        *(skb_put(*skb, 1)) = 0;                   /* UTUS.Length = 0;  */
+
+        return 6;
+}
+
+int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+	if ((*skb = dev_alloc_skb(2)) == NULL) {
+    
+		printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n");
+		return -1;
+	}
+
+        *((ushort*) skb_put(*skb, 2)) = chan->callref;  
+
+        return 2;
+}
+
+
+/*
+ *  Decoding of CAPI messages
+ *
+ */
+
+int capi_decode_conn_ind(struct pcbit_chan * chan, 
+                         struct sk_buff *skb,
+                         struct callb_data *info) 
+{
+        int CIlen, len;
+
+        /* Call Reference [CAPI] */
+        chan->callref = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref); 
+#endif
+
+        /* Channel Identification */
+
+        /* Expect  
+           Len = 1 
+           Octect 3 = 0100 10CC - [ 7 Basic, 4 , 2-1 chan ]
+           */
+
+        CIlen = skb->data[0];
+#ifdef DEBUG
+        if (CIlen == 1) {
+
+                if ( ((skb->data[1]) & 0xFC) == 0x48 )
+                        printk(KERN_DEBUG "decode_conn_ind: chan ok\n");
+                printk(KERN_DEBUG "phyChan = %d\n", skb->data[1] & 0x03); 
+        }
+	else
+		printk(KERN_DEBUG "conn_ind: CIlen = %d\n", CIlen);
+#endif
+        skb_pull(skb, CIlen + 1);
+
+        /* Calling Party Number */
+        /* An "additional service" as far as Portugal Telecom is concerned */
+
+        len = skb->data[0];
+
+	if (len > 0) {
+		int count = 1;
+		
+#ifdef DEBUG
+		printk(KERN_DEBUG "CPN: Octect 3 %02x\n", skb->data[1]);
+#endif
+		if ((skb->data[1] & 0x80) == 0)
+			count = 2;
+		
+		if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+			return -1;
+       
+		memcpy(info->data.setup.CallingPN, skb->data + count + 1, 
+		       len - count);
+		info->data.setup.CallingPN[len - count] = 0;
+
+	}
+	else {
+		info->data.setup.CallingPN = NULL;
+		printk(KERN_DEBUG "NULL CallingPN\n");
+	}
+
+	skb_pull(skb, len + 1);
+
+        /* Calling Party Subaddress */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* Called Party Number */
+
+        len = skb->data[0];
+
+	if (len > 0) {
+		int count = 1;
+		
+		if ((skb->data[1] & 0x80) == 0)
+			count = 2;
+        
+		if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+			return -1;
+        
+		memcpy(info->data.setup.CalledPN, skb->data + count + 1, 
+		       len - count); 
+		info->data.setup.CalledPN[len - count] = 0;
+
+	}
+	else {
+		info->data.setup.CalledPN = NULL;
+		printk(KERN_DEBUG "NULL CalledPN\n");
+	}
+
+	skb_pull(skb, len + 1);
+
+        /* Called Party Subaddress */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* LLC */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* HLC */
+        skb_pull(skb, skb->data[0] + 1);
+
+        /* U2U */
+        skb_pull(skb, skb->data[0] + 1);
+
+        return 0;
+}
+
+/*
+ *  returns errcode
+ */
+
+int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+			  int *complete) 
+{
+        int errcode;
+  
+        chan->callref = *((ushort *) skb->data);     /* Update CallReference */
+        skb_pull(skb, 2);
+
+        errcode = *((ushort *) skb->data);   /* read errcode */
+        skb_pull(skb, 2);
+
+        *complete = *(skb->data);
+        skb_pull(skb, 1);
+
+        /* FIX ME */
+        /* This is actually a firmware bug */
+        if (!*complete)
+        {
+                printk(KERN_DEBUG "complete=%02x\n", *complete);
+                *complete = 1;
+        }
+
+
+        /* Optional Bearer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+        
+        /* Channel Identification */
+        skb_pull(skb, *(skb->data) + 1);
+
+        /* High Layer Compatibility follows */
+        skb_pull(skb, *(skb->data) + 1);
+
+        return errcode;
+}
+
+int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+        ushort len;
+#ifdef DEBUG
+        char str[32];
+#endif
+
+        /* Yet Another Bearer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+  
+
+        /* Connected Party Number */
+        len=*(skb->data);
+
+#ifdef DEBUG
+	if (len > 1 && len < 31) {
+		memcpy(str, skb->data + 2, len - 1);
+		str[len] = 0;
+		printk(KERN_DEBUG "Connected Party Number: %s\n", str);
+	}
+	else
+		printk(KERN_DEBUG "actv_ind CPN len = %d\n", len);
+#endif
+
+        skb_pull(skb, len + 1);
+
+        /* Connected Subaddress */
+        skb_pull(skb, *(skb->data) + 1);
+
+        /* Low Layer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+
+        /* High Layer Capability */
+        skb_pull(skb, *(skb->data) + 1);
+
+        return 0;
+}
+
+int capi_decode_conn_actv_conf(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+        ushort errcode;
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+        
+        /* Channel Identification 
+        skb_pull(skb, skb->data[0] + 1);
+        */
+        return errcode;
+}
+
+
+int capi_decode_sel_proto_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort errcode;
+        
+        chan->layer2link = *(skb->data);
+        skb_pull(skb, 1);
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+        return errcode;
+}
+
+int capi_decode_actv_trans_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort errcode;
+
+        if (chan->layer2link != *(skb->data) )
+                printk("capi_decode_actv_trans_conf: layer2link doesn't match\n");
+
+        skb_pull(skb, 1);
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+        return errcode;        
+}
+
+int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort len;
+#ifdef DEBUG
+        int i;
+#endif
+        /* Cause */
+        
+        len = *(skb->data);
+        skb_pull(skb, 1);
+
+#ifdef DEBUG
+
+        for (i=0; i<len; i++)
+                printk(KERN_DEBUG "Cause Octect %d: %02x\n", i+3, 
+                       *(skb->data + i));
+#endif
+
+        skb_pull(skb, len);
+
+        return 0;
+}
+
+int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+        ushort errcode;
+
+        errcode = *((ushort*) skb->data);
+        skb_pull(skb, 2);
+
+        return errcode;                
+}
+
+#ifdef DEBUG
+int capi_decode_debug_188(u_char *hdr, ushort hdrlen)
+{
+        char str[64];
+        int len;
+        
+        len = hdr[0];
+
+        if (len < 64 && len == hdrlen - 1) {        
+                memcpy(str, hdr + 1, hdrlen - 1);
+                str[hdrlen - 1] = 0;
+                printk("%s\n", str);
+        }
+        else
+                printk("debug message incorrect\n");
+
+        return 0;
+}
+#endif
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/capi.h b/drivers/isdn/pcbit/capi.h
new file mode 100644
index 0000000..18e6aa3
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.h
@@ -0,0 +1,88 @@
+/*
+ * CAPI encode/decode prototypes and defines
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef CAPI_H
+#define CAPI_H
+
+
+#define REQ_CAUSE         0x01
+#define REQ_DISPLAY       0x04
+#define REQ_USER_TO_USER  0x08 
+
+#define AppInfoMask  REQ_CAUSE|REQ_DISPLAY|REQ_USER_TO_USER 
+
+/* Connection Setup */
+extern int capi_conn_req(const char * calledPN, struct sk_buff **buf,
+			 int proto);
+extern int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+				 int *complete); 
+
+extern int capi_decode_conn_ind(struct pcbit_chan * chan, struct sk_buff *skb,
+				struct callb_data *info);
+extern int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb);
+
+extern int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb);
+extern int capi_decode_conn_actv_conf(struct pcbit_chan * chan, 
+				      struct sk_buff *skb);
+
+extern int capi_decode_conn_actv_ind(struct pcbit_chan * chan, 
+				     struct sk_buff *skb);
+extern int capi_conn_active_resp(struct pcbit_chan* chan, 
+				 struct sk_buff **skb);
+
+/* Data */
+extern int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
+				 int outgoing);
+extern int capi_decode_sel_proto_conf(struct pcbit_chan *chan, 
+				      struct sk_buff *skb);
+
+extern int capi_activate_transp_req(struct pcbit_chan *chan, 
+				    struct sk_buff **skb);
+extern int capi_decode_actv_trans_conf(struct pcbit_chan *chan, 
+				       struct sk_buff *skb);
+
+extern int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb);
+extern int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb);
+
+/* Connection Termination */
+extern int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause);
+extern int capi_decode_disc_conf(struct pcbit_chan *chan, struct sk_buff *skb);
+
+extern int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb);
+extern int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb);
+
+#ifdef DEBUG
+extern int capi_decode_debug_188(u_char *hdr, ushort hdrlen);
+#endif
+
+static inline struct pcbit_chan * 
+capi_channel(struct pcbit_dev *dev, struct sk_buff *skb)
+{
+	ushort callref;
+
+	callref = *((ushort*) skb->data);
+	skb_pull(skb, 2);
+
+	if (dev->b1->callref == callref)
+		return dev->b1;
+	else if (dev->b2->callref == callref)
+		return dev->b2;
+
+	return NULL;
+}
+
+#endif
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c
new file mode 100644
index 0000000..e98f9c4
--- /dev/null
+++ b/drivers/isdn/pcbit/drv.c
@@ -0,0 +1,1088 @@
+/*
+ * PCBIT-D interface with isdn4linux
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ *	Fixes:
+ *
+ *	Nuno Grilo	<l38486@alfa.ist.utl.pt>
+ *      fixed msn_list NULL pointer dereference.
+ *		
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include <asm/string.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "capi.h"
+
+
+extern ushort last_ref_num;
+
+static int pcbit_ioctl(isdn_ctrl* ctl);
+
+static char* pcbit_devname[MAX_PCBIT_CARDS] = {
+	"pcbit0",
+	"pcbit1",
+	"pcbit2",
+	"pcbit3"
+};
+
+/*
+ * prototypes
+ */
+
+int pcbit_command(isdn_ctrl* ctl);
+int pcbit_stat(u_char __user * buf, int len, int, int);
+int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb);
+int pcbit_writecmd(const u_char __user *, int, int, int);
+
+static int set_protocol_running(struct pcbit_dev * dev);
+
+static void pcbit_clear_msn(struct pcbit_dev *dev);
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list);
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn);
+
+
+extern void pcbit_deliver(void * data);
+
+int pcbit_init_dev(int board, int mem_base, int irq)
+{
+	struct pcbit_dev *dev;
+	isdn_if *dev_if;
+
+	if ((dev=kmalloc(sizeof(struct pcbit_dev), GFP_KERNEL)) == NULL)
+	{
+		printk("pcbit_init: couldn't malloc pcbit_dev struct\n");
+		return -ENOMEM;
+	}
+
+	dev_pcbit[board] = dev;
+	memset(dev, 0, sizeof(struct pcbit_dev));
+	init_waitqueue_head(&dev->set_running_wq);
+	spin_lock_init(&dev->lock);
+
+	if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) {
+		dev->ph_mem = mem_base;
+		if (!request_mem_region(dev->ph_mem, 4096, "PCBIT mem")) {
+			printk(KERN_WARNING
+				"PCBIT: memory region %lx-%lx already in use\n",
+				dev->ph_mem, dev->ph_mem + 4096);
+			kfree(dev);
+			dev_pcbit[board] = NULL;
+			return -EACCES;
+		}
+		dev->sh_mem = ioremap(dev->ph_mem, 4096);
+	}
+	else 
+	{
+		printk("memory address invalid");
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EACCES;
+	}
+
+	dev->b1 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+	if (!dev->b1) {
+		printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		return -ENOMEM;
+	}
+    
+	dev->b2 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+	if (!dev->b2) {
+		printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+		kfree(dev->b1);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		return -ENOMEM;
+	}
+
+	memset(dev->b1, 0, sizeof(struct pcbit_chan));
+	memset(dev->b2, 0, sizeof(struct pcbit_chan));
+	dev->b2->id = 1;
+
+	INIT_WORK(&dev->qdelivery, pcbit_deliver, dev);
+
+	/*
+	 *  interrupts
+	 */
+
+	if (request_irq(irq, &pcbit_irq_handler, 0, pcbit_devname[board], dev) != 0) 
+	{
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EIO;
+	}
+
+	dev->irq = irq;
+
+	/* next frame to be received */
+	dev->rcv_seq = 0;
+	dev->send_seq = 0;
+	dev->unack_seq = 0;
+
+	dev->hl_hdrlen = 16;
+
+	dev_if = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+
+	if (!dev_if) {
+		free_irq(irq, dev);
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EIO;
+	}
+
+	dev->dev_if = dev_if;
+
+	dev_if->owner = THIS_MODULE;
+
+	dev_if->channels = 2;
+	
+	dev_if->features = (ISDN_FEATURE_P_EURO  | ISDN_FEATURE_L3_TRANS | 
+			    ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS );
+
+	dev_if->writebuf_skb = pcbit_xmit;
+	dev_if->hl_hdrlen = 16;
+
+	dev_if->maxbufsize = MAXBUFSIZE;
+	dev_if->command  = pcbit_command;
+	
+	dev_if->writecmd = pcbit_writecmd;
+	dev_if->readstat = pcbit_stat;
+
+
+	strcpy(dev_if->id, pcbit_devname[board]);
+
+	if (!register_isdn(dev_if)) {
+		free_irq(irq, dev);
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+		dev_pcbit[board] = NULL;
+		return -EIO;
+	}
+
+	dev->id = dev_if->channels;
+
+
+	dev->l2_state = L2_DOWN;
+	dev->free = 511;
+
+	/*
+	 * set_protocol_running(dev);
+	 */
+
+	return 0;
+}
+
+#ifdef MODULE
+void pcbit_terminate(int board)
+{
+	struct pcbit_dev * dev;
+
+	dev = dev_pcbit[board];
+
+	if (dev) {
+	     /* unregister_isdn(dev->dev_if); */
+		free_irq(dev->irq, dev);
+		pcbit_clear_msn(dev);
+		kfree(dev->dev_if);
+		if (dev->b1->fsm_timer.function)
+			del_timer(&dev->b1->fsm_timer);
+		if (dev->b2->fsm_timer.function)
+			del_timer(&dev->b2->fsm_timer);
+		kfree(dev->b1);
+		kfree(dev->b2);
+		iounmap(dev->sh_mem);
+		release_mem_region(dev->ph_mem, 4096);
+		kfree(dev);
+	}
+}
+#endif
+
+int pcbit_command(isdn_ctrl* ctl)
+{
+	struct pcbit_dev  *dev;
+	struct pcbit_chan *chan;
+	struct callb_data info;
+
+	dev = finddev(ctl->driver);
+
+	if (!dev)
+	{
+		printk("pcbit_command: unknown device\n");
+		return -1;
+	}
+
+	chan = (ctl->arg & 0x0F) ? dev->b2 : dev->b1;
+
+
+	switch(ctl->command) {
+	case ISDN_CMD_IOCTL:
+		return pcbit_ioctl(ctl);
+		break;
+	case ISDN_CMD_DIAL:
+		info.type = EV_USR_SETUP_REQ;
+		info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone;
+		pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info);
+		break;
+	case ISDN_CMD_ACCEPTD:
+		pcbit_fsm_event(dev, chan, EV_USR_SETUP_RESP, NULL);
+		break;
+	case ISDN_CMD_ACCEPTB:
+		printk("ISDN_CMD_ACCEPTB - not really needed\n");
+		break;
+	case ISDN_CMD_HANGUP:
+		pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+		break;
+	case ISDN_CMD_SETL2:
+		chan->proto = (ctl->arg >> 8);
+		break;
+	case ISDN_CMD_CLREAZ:
+		pcbit_clear_msn(dev);
+		break;
+	case ISDN_CMD_SETEAZ:
+		pcbit_set_msn(dev, ctl->parm.num);
+		break;
+	case ISDN_CMD_SETL3:
+		if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS)
+			printk(KERN_DEBUG "L3 protocol unknown\n");
+		break;
+	default:
+		printk(KERN_DEBUG "pcbit_command: unknown command\n");
+		break;
+	};
+
+	return 0;
+}
+
+/*
+ * Another Hack :-(
+ * on some conditions the board stops sending TDATA_CONFs
+ * let's see if we can turn around the problem
+ */
+
+#ifdef BLOCK_TIMER
+static void pcbit_block_timer(unsigned long data)
+{
+	struct pcbit_chan *chan;
+	struct pcbit_dev * dev;
+	isdn_ctrl ictl;
+
+	chan = (struct pcbit_chan *) data;
+
+	dev = chan2dev(chan);
+
+	if (dev == NULL) {
+		printk(KERN_DEBUG "pcbit: chan2dev failed\n");
+		return;
+	}
+
+	del_timer(&chan->block_timer);
+	chan->block_timer.function = NULL;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "pcbit_block_timer\n");
+#endif	
+	chan->queued = 0;
+	ictl.driver = dev->id;
+	ictl.command = ISDN_STAT_BSENT;
+	ictl.arg = chan->id;
+	dev->dev_if->statcallb(&ictl);     
+}
+#endif
+
+int pcbit_xmit(int driver, int chnum, int ack, struct sk_buff *skb)
+{
+	ushort hdrlen;
+	int refnum, len;
+	struct pcbit_chan * chan;
+	struct pcbit_dev *dev;
+
+	dev = finddev(driver);
+	if (dev == NULL)
+	{
+		printk("finddev returned NULL");
+		return -1;
+	}
+
+	chan = chnum ? dev->b2 : dev->b1;
+
+
+	if (chan->fsm_state != ST_ACTIVE)
+		return -1;
+
+	if (chan->queued >= MAX_QUEUED )
+	{
+#ifdef DEBUG_QUEUE
+		printk(KERN_DEBUG 
+		       "pcbit: %d packets already in queue - write fails\n",
+		       chan->queued);
+#endif
+		/*
+		 * packet stays on the head of the device queue
+		 * since dev_start_xmit will fail
+		 * see net/core/dev.c
+		 */
+#ifdef BLOCK_TIMER
+		if (chan->block_timer.function == NULL) {
+			init_timer(&chan->block_timer);
+			chan->block_timer.function =  &pcbit_block_timer;
+			chan->block_timer.data = (long) chan;
+			chan->block_timer.expires = jiffies + 1 * HZ;
+			add_timer(&chan->block_timer);
+		}
+#endif		
+		return 0;	                 
+	}
+
+
+	chan->queued++;
+	
+        len = skb->len;
+
+	hdrlen = capi_tdata_req(chan, skb);
+
+	refnum = last_ref_num++ & 0x7fffU;
+	chan->s_refnum = refnum;
+
+	pcbit_l2_write(dev, MSG_TDATA_REQ, refnum, skb, hdrlen);
+
+	return len;
+}
+
+int pcbit_writecmd(const u_char __user *buf, int len, int driver, int channel)
+{
+	struct pcbit_dev * dev;
+	int i, j;
+	const u_char * loadbuf;
+	u_char * ptr = NULL;
+	u_char *cbuf;
+
+	int errstat;
+
+	dev = finddev(driver);
+
+	if (!dev)
+	{
+		printk("pcbit_writecmd: couldn't find device");
+		return -ENODEV;
+	}
+
+	switch(dev->l2_state) {
+	case L2_LWMODE:
+		/* check (size <= rdp_size); write buf into board */
+		if (len < 0 || len > BANK4 + 1 || len > 1024)
+		{
+			printk("pcbit_writecmd: invalid length %d\n", len);
+			return -EINVAL;
+		}
+
+		cbuf = kmalloc(len, GFP_KERNEL);
+		if (!cbuf)
+			return -ENOMEM;
+
+		if (copy_from_user(cbuf, buf, len)) {
+			kfree(cbuf);
+			return -EFAULT;
+		}
+		memcpy_toio(dev->sh_mem, cbuf, len);
+		kfree(cbuf);
+		return len;
+	case L2_FWMODE:
+		/* this is the hard part */
+		/* dumb board */
+		/* get it into kernel space */
+		if ((ptr = kmalloc(len, GFP_KERNEL))==NULL)
+			return -ENOMEM;
+		if (copy_from_user(ptr, buf, len)) {
+			kfree(ptr);
+			return -EFAULT;
+		}
+		loadbuf = ptr;
+    
+		errstat = 0;
+
+		for (i=0; i < len; i++)
+		{
+			for(j=0; j < LOAD_RETRY; j++)
+				if (!(readb(dev->sh_mem + dev->loadptr)))
+					break;
+
+			if (j == LOAD_RETRY)
+			{
+				errstat = -ETIME;
+				printk("TIMEOUT i=%d\n", i);
+				break;
+			}
+			writeb(loadbuf[i], dev->sh_mem + dev->loadptr + 1);
+			writeb(0x01, dev->sh_mem + dev->loadptr);
+
+			dev->loadptr += 2;
+			if (dev->loadptr > LOAD_ZONE_END)
+				dev->loadptr = LOAD_ZONE_START;
+		}
+		kfree(ptr);
+
+		return errstat ? errstat : len;
+	default:
+		return -EBUSY;
+	}
+}
+
+/*
+ *  demultiplexing of messages
+ *
+ */
+
+void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, 
+			     struct sk_buff * skb,
+			     ushort hdr_len, ushort refnum)
+{
+	struct pcbit_chan *chan;
+	struct sk_buff *skb2;
+	unsigned short len;
+	struct callb_data cbdata;
+	int complete, err;
+	isdn_ctrl ictl;
+
+	switch(msg) {
+
+	case MSG_TDATA_IND:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+		chan->r_refnum = skb->data[7];
+		skb_pull(skb, 8);
+
+		dev->dev_if->rcvcallb_skb(dev->id, chan->id, skb);
+
+		if (capi_tdata_resp(chan, &skb2) > 0) 
+			pcbit_l2_write(dev, MSG_TDATA_RESP, refnum, 
+				       skb2, skb2->len);
+		return;
+		break;  
+	case MSG_TDATA_CONF:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+#ifdef DEBUG
+		if ( (*((ushort *) (skb->data + 2) )) != 0) {
+                        printk(KERN_DEBUG "TDATA_CONF error\n");
+		}
+#endif
+#ifdef BLOCK_TIMER
+                if (chan->queued == MAX_QUEUED) {
+                        del_timer(&chan->block_timer);
+			chan->block_timer.function = NULL;
+		}
+                
+#endif		
+		chan->queued--;
+
+		ictl.driver = dev->id;
+		ictl.command = ISDN_STAT_BSENT;
+		ictl.arg = chan->id;
+		dev->dev_if->statcallb(&ictl);
+		break;
+
+	case MSG_CONN_IND:
+		/*
+		 *  channel: 1st not used will do
+		 *           if both are used we're in trouble 
+		 */
+
+		if (!dev->b1->fsm_state)
+			chan = dev->b1;
+		else if (!dev->b2->fsm_state)
+			chan = dev->b2;
+		else {
+			printk(KERN_INFO 
+			       "Incoming connection: no channels available");
+
+			if ((len = capi_disc_req(*(ushort*)(skb->data), &skb2, CAUSE_NOCHAN)) > 0)
+				pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb2, len);
+			break;  
+		}
+
+		cbdata.data.setup.CalledPN = NULL;
+		cbdata.data.setup.CallingPN = NULL;
+
+		capi_decode_conn_ind(chan, skb, &cbdata);
+		cbdata.type = EV_NET_SETUP;
+
+		pcbit_fsm_event(dev, chan, EV_NET_SETUP, NULL);
+
+		if (pcbit_check_msn(dev, cbdata.data.setup.CallingPN)) 
+			pcbit_fsm_event(dev, chan, EV_USR_PROCED_REQ, &cbdata);
+		else
+			pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+
+		if (cbdata.data.setup.CalledPN)
+			kfree(cbdata.data.setup.CalledPN);
+		if (cbdata.data.setup.CallingPN)
+			kfree(cbdata.data.setup.CallingPN);
+		break;
+    
+	case MSG_CONN_CONF:
+		/* 
+		 * We should be able to find the channel by the message
+		 * reference number. The current version of the firmware
+		 * doesn't sent the ref number correctly.
+		 */
+#ifdef DEBUG
+		printk(KERN_DEBUG "refnum=%04x b1=%04x b2=%04x\n", refnum, 
+		       dev->b1->s_refnum, 
+		       dev->b2->s_refnum);
+#endif
+		/* We just try to find a channel in the right state */
+
+		if (dev->b1->fsm_state == ST_CALL_INIT)
+			chan = dev->b1;
+		else { 		   
+			if (dev->b2->s_refnum == ST_CALL_INIT)
+				chan = dev->b2;
+			else {			
+				chan = NULL;
+				printk(KERN_WARNING "Connection Confirm - no channel in Call Init state\n");
+				break;
+			}
+		}
+		if (capi_decode_conn_conf(chan, skb, &complete)) {
+			printk(KERN_DEBUG "conn_conf indicates error\n");
+			pcbit_fsm_event(dev, chan, EV_ERROR, NULL);
+		}
+		else
+			if (complete)
+				pcbit_fsm_event(dev, chan, EV_NET_CALL_PROC, NULL);
+			else
+				pcbit_fsm_event(dev, chan, EV_NET_SETUP_ACK, NULL);
+		break; 
+	case MSG_CONN_ACTV_IND:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+		
+		if (capi_decode_conn_actv_ind(chan, skb)) {
+			printk("error in capi_decode_conn_actv_ind\n");
+		     /* pcbit_fsm_event(dev, chan, EV_ERROR, NULL); */
+			break;
+		}
+		chan->r_refnum = refnum;
+		pcbit_fsm_event(dev, chan, EV_NET_CONN, NULL);
+		break;
+	case MSG_CONN_ACTV_CONF:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (capi_decode_conn_actv_conf(chan, skb) == 0)
+			pcbit_fsm_event(dev, chan, EV_NET_CONN_ACK, NULL);
+		
+		else
+			printk(KERN_DEBUG "decode_conn_actv_conf failed\n");
+		break;
+
+	case  MSG_SELP_CONF:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!(err = capi_decode_sel_proto_conf(chan, skb)))
+			pcbit_fsm_event(dev, chan, EV_NET_SELP_RESP, NULL);
+		else {
+			/* Error */
+			printk("error %d - capi_decode_sel_proto_conf\n", err);
+		}
+		break;
+	case MSG_ACT_TRANSP_CONF:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!capi_decode_actv_trans_conf(chan, skb))
+			pcbit_fsm_event(dev, chan, EV_NET_ACTV_RESP, NULL);
+		break;
+
+	case MSG_DISC_IND:
+
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!capi_decode_disc_ind(chan, skb))
+			pcbit_fsm_event(dev, chan, EV_NET_DISC, NULL);
+		else
+			printk(KERN_WARNING "capi_decode_disc_ind - error\n");
+		break;
+	case MSG_DISC_CONF:
+		if (!(chan = capi_channel(dev, skb))) {
+			printk(KERN_WARNING 
+			       "CAPI header: unknown channel id\n");
+			break;
+		}
+
+		if (!capi_decode_disc_ind(chan, skb))
+			pcbit_fsm_event(dev, chan, EV_NET_RELEASE, NULL);
+		else
+			printk(KERN_WARNING "capi_decode_disc_conf - error\n");
+		break;
+	case MSG_INFO_IND:
+#ifdef DEBUG
+		printk(KERN_DEBUG "received Info Indication - discarded\n");
+#endif
+		break;
+#ifdef DEBUG
+	case MSG_DEBUG_188:
+		capi_decode_debug_188(skb->data, skb->len);
+		break;
+
+	default:
+		printk(KERN_DEBUG "pcbit_l3_receive: unknown message %08lx\n",
+		       msg);
+		break;
+#endif
+	}
+
+	kfree_skb(skb);
+
+}
+
+/*
+ *   Single statbuf
+ *   should be a statbuf per device
+ */
+
+static char statbuf[STATBUF_LEN];
+static int stat_st = 0;
+static int stat_end = 0;
+
+int pcbit_stat(u_char __user *buf, int len, int driver, int channel)
+{
+	int stat_count;
+	stat_count = stat_end - stat_st;
+
+	if (stat_count < 0)
+		stat_count = STATBUF_LEN - stat_st + stat_end;
+
+	/* FIXME: should we sleep and wait for more cookies ? */
+	if (len > stat_count)            
+		len = stat_count;
+
+	if (stat_st < stat_end)
+	{
+		copy_to_user(buf, statbuf + stat_st, len);
+		stat_st += len;	   
+	}
+	else
+	{
+		if (len > STATBUF_LEN - stat_st)
+		{
+			copy_to_user(buf, statbuf + stat_st, 
+				       STATBUF_LEN - stat_st);
+			copy_to_user(buf, statbuf, 
+				       len - (STATBUF_LEN - stat_st));
+
+			stat_st = len - (STATBUF_LEN - stat_st);
+		}
+		else
+		{
+			copy_to_user(buf, statbuf + stat_st, len);
+
+			stat_st += len;
+			
+			if (stat_st == STATBUF_LEN)
+				stat_st = 0;
+		}
+	}
+
+	if (stat_st == stat_end)
+		stat_st = stat_end = 0;
+
+	return len;
+}
+
+static void pcbit_logstat(struct pcbit_dev *dev, char *str)
+{
+	int i;
+	isdn_ctrl ictl;
+
+	for (i=stat_end; i<strlen(str); i++)
+	{
+		statbuf[i]=str[i];
+		stat_end = (stat_end + 1) % STATBUF_LEN;
+		if (stat_end == stat_st)
+			stat_st = (stat_st + 1) % STATBUF_LEN;
+	}
+
+	ictl.command=ISDN_STAT_STAVAIL;
+	ictl.driver=dev->id;
+	ictl.arg=strlen(str);
+	dev->dev_if->statcallb(&ictl);
+}
+	
+extern char * isdn_state_table[];
+extern char * strisdnevent(unsigned short);
+
+
+void pcbit_state_change(struct pcbit_dev * dev, struct pcbit_chan * chan, 
+			unsigned short i, unsigned short ev, unsigned short f)
+{
+	char buf[256];
+  
+	sprintf(buf, "change on device: %d channel:%d\n%s -> %s -> %s\n",
+		dev->id, chan->id, 
+		isdn_state_table[i], strisdnevent(ev), isdn_state_table[f]
+		);
+
+#ifdef DEBUG
+	printk("%s", buf);
+#endif
+
+	pcbit_logstat(dev, buf);
+}
+
+static void set_running_timeout(unsigned long ptr)
+{
+	struct pcbit_dev * dev;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "set_running_timeout\n");
+#endif
+	dev = (struct pcbit_dev *) ptr;
+
+	wake_up_interruptible(&dev->set_running_wq);
+}
+
+static int set_protocol_running(struct pcbit_dev * dev)
+{
+	isdn_ctrl ctl;
+
+	init_timer(&dev->set_running_timer);
+
+	dev->set_running_timer.function = &set_running_timeout;
+	dev->set_running_timer.data = (ulong) dev;
+	dev->set_running_timer.expires = jiffies + SET_RUN_TIMEOUT;
+
+	/* kick it */
+
+	dev->l2_state = L2_STARTING;
+
+	writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)), 
+	       dev->sh_mem + BANK4);
+
+	add_timer(&dev->set_running_timer);
+
+	interruptible_sleep_on(&dev->set_running_wq);
+
+	del_timer(&dev->set_running_timer);
+
+	if (dev->l2_state == L2_RUNNING)
+	{
+		printk(KERN_DEBUG "pcbit: running\n");
+
+		dev->unack_seq = dev->send_seq;
+
+		dev->writeptr = dev->sh_mem;
+		dev->readptr = dev->sh_mem + BANK2;
+    
+		/* tell the good news to the upper layer */  
+		ctl.driver = dev->id;
+		ctl.command = ISDN_STAT_RUN;
+
+		dev->dev_if->statcallb(&ctl);
+	}
+	else
+	{
+		printk(KERN_DEBUG "pcbit: initialization failed\n");
+		printk(KERN_DEBUG "pcbit: firmware not loaded\n");
+
+		dev->l2_state = L2_DOWN;
+
+#ifdef DEBUG
+		printk(KERN_DEBUG "Bank3 = %02x\n", 
+		       readb(dev->sh_mem + BANK3));
+#endif
+		writeb(0x40, dev->sh_mem + BANK4);
+
+		/* warn the upper layer */
+		ctl.driver = dev->id;
+		ctl.command = ISDN_STAT_STOP;
+
+		dev->dev_if->statcallb(&ctl);
+
+		return -EL2HLT;	/* Level 2 halted */
+	}
+
+	return 0;
+}
+
+static int pcbit_ioctl(isdn_ctrl* ctl)
+{
+	struct pcbit_dev * dev;
+	struct pcbit_ioctl *cmd;
+
+	dev = finddev(ctl->driver);
+  
+	if (!dev)
+	{
+		printk(KERN_DEBUG "pcbit_ioctl: unknown device\n");
+		return -ENODEV;
+	}
+
+	cmd = (struct pcbit_ioctl *) ctl->parm.num;
+
+	switch(ctl->arg) {
+	case PCBIT_IOCTL_GETSTAT:
+		cmd->info.l2_status = dev->l2_state;
+		break;
+
+	case PCBIT_IOCTL_STRLOAD:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+
+		dev->unack_seq = dev->send_seq = dev->rcv_seq = 0;
+
+		dev->writeptr = dev->sh_mem;
+		dev->readptr = dev->sh_mem + BANK2;
+    
+		dev->l2_state = L2_LOADING;
+		break;
+
+	case PCBIT_IOCTL_LWMODE:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+
+		dev->l2_state = L2_LWMODE;
+		break;
+
+	case PCBIT_IOCTL_FWMODE:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+		dev->loadptr = LOAD_ZONE_START;
+		dev->l2_state = L2_FWMODE;
+
+		break; 
+	case PCBIT_IOCTL_ENDLOAD:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+		dev->l2_state = L2_DOWN;
+		break; 
+
+	case PCBIT_IOCTL_SETBYTE: 
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+
+		/* check addr */
+		if (cmd->info.rdp_byte.addr > BANK4)
+			return -EFAULT;
+		
+		writeb(cmd->info.rdp_byte.value, dev->sh_mem + cmd->info.rdp_byte.addr);
+		break;
+	case PCBIT_IOCTL_GETBYTE:
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+
+		/* check addr */
+
+		if (cmd->info.rdp_byte.addr > BANK4)
+		{
+			printk("getbyte: invalid addr %04x\n", cmd->info.rdp_byte.addr);
+			return -EFAULT;
+		}
+		
+		cmd->info.rdp_byte.value = readb(dev->sh_mem + cmd->info.rdp_byte.addr); 
+		break;
+	case PCBIT_IOCTL_RUNNING: 
+		if (dev->l2_state == L2_RUNNING)
+			return -EBUSY;
+		return set_protocol_running(dev);
+		break;
+	case PCBIT_IOCTL_WATCH188:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+		pcbit_l2_write(dev, MSG_WATCH188, 0x0001, NULL, 0);
+		break;
+	case PCBIT_IOCTL_PING188:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+		pcbit_l2_write(dev, MSG_PING188_REQ, 0x0001, NULL, 0);
+		break;
+	case PCBIT_IOCTL_APION:
+		if (dev->l2_state != L2_LOADING)
+			return -EINVAL;
+		pcbit_l2_write(dev, MSG_API_ON, 0x0001, NULL, 0);
+		break;
+	case PCBIT_IOCTL_STOP:
+		dev->l2_state = L2_DOWN;
+		writeb(0x40, dev->sh_mem + BANK4);
+		dev->rcv_seq = 0;
+		dev->send_seq = 0;
+		dev->unack_seq = 0;
+		break;
+	default:
+		printk("error: unknown ioctl\n");
+		break;
+	};
+	return 0;
+}
+
+/* 
+ *        MSN list handling
+ *
+ *        if null reject all calls
+ *        if first entry has null MSN accept all calls 
+ */
+
+static void pcbit_clear_msn(struct pcbit_dev *dev)
+{
+	struct msn_entry *ptr, *back;
+
+	for (ptr=dev->msn_list; ptr; )
+	{
+		back = ptr->next;
+		kfree(ptr);
+		ptr = back;
+	}
+
+	dev->msn_list = NULL; 
+}
+
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list)
+{
+	struct msn_entry *ptr;
+	struct msn_entry *back = NULL;
+	char *cp, *sp;
+	int len;
+
+	if (strlen(list) == 0) {
+		ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+		if (!ptr) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+
+		ptr->msn = NULL;
+
+		ptr->next = dev->msn_list;
+		dev->msn_list = ptr;
+
+		return;
+	}
+
+	if (dev->msn_list)
+		for (back=dev->msn_list; back->next; back=back->next);
+	
+	sp = list;
+
+	do {
+		cp=strchr(sp, ',');
+		if (cp)
+			len = cp - sp;
+		else
+			len = strlen(sp);
+
+		ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+
+		if (!ptr) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+		ptr->next = NULL;
+		
+		ptr->msn = kmalloc(len, GFP_ATOMIC);
+		if (!ptr->msn) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			kfree(ptr);
+			return;
+		}
+
+		memcpy(ptr->msn, sp, len - 1);
+		ptr->msn[len] = 0;
+
+#ifdef DEBUG
+		printk(KERN_DEBUG "msn: %s\n", ptr->msn);
+#endif
+		if (dev->msn_list == NULL)
+			dev->msn_list = ptr;
+		else
+			back->next = ptr;
+		back = ptr;
+		sp += len;
+	} while(cp);
+}
+
+/*
+ *  check if we do signal or reject an incoming call
+ */
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn)
+{
+	struct msn_entry *ptr;
+	
+	for (ptr=dev->msn_list; ptr; ptr=ptr->next) {
+
+		if (ptr->msn == NULL) 
+			return 1;
+		
+		if (strcmp(ptr->msn, msn) == 0)
+			return 1;
+	}
+
+	return 0;
+}
diff --git a/drivers/isdn/pcbit/edss1.c b/drivers/isdn/pcbit/edss1.c
new file mode 100644
index 0000000..93ca7de
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.c
@@ -0,0 +1,325 @@
+/*
+ * DSS.1 Finite State Machine
+ * base: ITU-T Rec Q.931
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ *        TODO: complete the FSM
+ *              move state/event descriptions to a user space logger
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <linux/timer.h>
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "callbacks.h"
+
+
+extern void pcbit_state_change(struct pcbit_dev *, struct pcbit_chan *, 
+                               unsigned short i, unsigned short ev, 
+                               unsigned short f);
+
+extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+char * isdn_state_table[] = {
+  "Closed",
+  "Call initiated",
+  "Overlap sending",
+  "Outgoing call proceeding",
+  "NOT DEFINED",
+  "Call delivered",
+  "Call present",
+  "Call received",
+  "Connect request",
+  "Incoming call proceeding",
+  "Active",
+  "Disconnect request",
+  "Disconnect indication",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "Suspend request",
+  "NOT DEFINED",
+  "Resume request",
+  "NOT DEFINED",
+  "Release Request",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "NOT DEFINED",
+  "Overlap receiving",
+  "Select protocol on B-Channel",
+  "Activate B-channel protocol"
+};
+
+#ifdef DEBUG_ERRS
+static
+struct CauseValue {
+  byte nr;
+  char *descr;
+} cvlist[]={
+  {0x01,"Unallocated (unassigned) number"},
+  {0x02,"No route to specified transit network"},
+  {0x03,"No route to destination"},
+  {0x04,"Send special information tone"},
+  {0x05,"Misdialled trunk prefix"},
+  {0x06,"Channel unacceptable"},
+  {0x07,"Channel awarded and being delivered in an established channel"},
+  {0x08,"Preemption"},
+  {0x09,"Preemption - circuit reserved for reuse"},
+  {0x10,"Normal call clearing"},
+  {0x11,"User busy"},
+  {0x12,"No user responding"},
+  {0x13,"No answer from user (user alerted)"},
+  {0x14,"Subscriber absent"},
+  {0x15,"Call rejected"},
+  {0x16,"Number changed"},
+  {0x1a,"non-selected user clearing"},
+  {0x1b,"Destination out of order"},
+  {0x1c,"Invalid number format (address incomplete)"},
+  {0x1d,"Facility rejected"},
+  {0x1e,"Response to Status enquiry"},
+  {0x1f,"Normal, unspecified"},
+  {0x22,"No circuit/channel available"},
+  {0x26,"Network out of order"},
+  {0x27,"Permanent frame mode connection out-of-service"},
+  {0x28,"Permanent frame mode connection operational"},
+  {0x29,"Temporary failure"},
+  {0x2a,"Switching equipment congestion"},
+  {0x2b,"Access information discarded"},
+  {0x2c,"Requested circuit/channel not available"},
+  {0x2e,"Precedence call blocked"},
+  {0x2f,"Resource unavailable, unspecified"},
+  {0x31,"Quality of service unavailable"},
+  {0x32,"Requested facility not subscribed"},
+  {0x35,"Outgoing calls barred within CUG"},
+  {0x37,"Incoming calls barred within CUG"},
+  {0x39,"Bearer capability not authorized"},
+  {0x3a,"Bearer capability not presently available"},
+  {0x3e,"Inconsistency in designated outgoing access information and subscriber class"},
+  {0x3f,"Service or option not available, unspecified"},
+  {0x41,"Bearer capability not implemented"},
+  {0x42,"Channel type not implemented"},
+  {0x43,"Requested facility not implemented"},
+  {0x44,"Only restricted digital information bearer capability is available"},
+  {0x4f,"Service or option not implemented"},
+  {0x51,"Invalid call reference value"},
+  {0x52,"Identified channel does not exist"},
+  {0x53,"A suspended call exists, but this call identity does not"},
+  {0x54,"Call identity in use"},
+  {0x55,"No call suspended"},
+  {0x56,"Call having the requested call identity has been cleared"},
+  {0x57,"User not member of CUG"},
+  {0x58,"Incompatible destination"},
+  {0x5a,"Non-existent CUG"},
+  {0x5b,"Invalid transit network selection"},
+  {0x5f,"Invalid message, unspecified"},
+  {0x60,"Mandatory information element is missing"},
+  {0x61,"Message type non-existent or not implemented"},
+  {0x62,"Message not compatible with call state or message type non-existent or not implemented"},
+  {0x63,"Information element/parameter non-existent or not implemented"},
+  {0x64,"Invalid information element contents"},
+  {0x65,"Message not compatible with call state"},
+  {0x66,"Recovery on timer expiry"},
+  {0x67,"Parameter non-existent or not implemented - passed on"},
+  {0x6e,"Message with unrecognized parameter discarded"},
+  {0x6f,"Protocol error, unspecified"},
+  {0x7f,"Interworking, unspecified"}
+};
+
+#endif
+
+static struct isdn_event_desc {
+  unsigned short ev;
+  char * desc;
+} isdn_event_table [] = {
+  {EV_USR_SETUP_REQ,     "CC->L3: Setup Request"},
+  {EV_USR_SETUP_RESP,    "CC->L3: Setup Response"},
+  {EV_USR_PROCED_REQ,    "CC->L3: Proceeding Request"},
+  {EV_USR_RELEASE_REQ,   "CC->L3: Release Request"},
+
+  {EV_NET_SETUP,        "NET->TE: setup "},
+  {EV_NET_CALL_PROC,    "NET->TE: call proceeding"},
+  {EV_NET_SETUP_ACK,    "NET->TE: setup acknowledge (more info needed)"},
+  {EV_NET_CONN,         "NET->TE: connect"},
+  {EV_NET_CONN_ACK,     "NET->TE: connect acknowledge"},
+  {EV_NET_DISC,         "NET->TE: disconnect indication"},
+  {EV_NET_RELEASE,      "NET->TE: release"},
+  {EV_NET_RELEASE_COMP, "NET->TE: release complete"},
+  {EV_NET_SELP_RESP,    "Board: Select B-channel protocol ack"},
+  {EV_NET_ACTV_RESP,    "Board: Activate B-channel protocol ack"},
+  {EV_TIMER,            "Timeout"},
+  {0, "NULL"}
+};
+
+char * strisdnevent(ushort ev)
+{
+  struct isdn_event_desc * entry;
+ 
+  for (entry = isdn_event_table; entry->ev; entry++)
+    if (entry->ev == ev)
+      break;
+
+  return entry->desc;
+}
+
+/*
+ * Euro ISDN finite state machine
+ */
+
+static struct fsm_timer_entry fsm_timers[] = {
+  {ST_CALL_PROC, 10},
+  {ST_DISC_REQ, 2},
+  {ST_ACTIVE_SELP, 5},
+  {ST_ACTIVE_ACTV, 5},
+  {ST_INCM_PROC, 10},
+  {ST_CONN_REQ, 2},
+  {0xff, 0}
+};
+
+static struct fsm_entry fsm_table[] = {
+/* Connect Phase */
+  /* Outgoing */
+  {ST_NULL, ST_CALL_INIT, EV_USR_SETUP_REQ, cb_out_1},
+
+  {ST_CALL_INIT, ST_OVER_SEND, EV_NET_SETUP_ACK, cb_notdone},
+  {ST_CALL_INIT, ST_CALL_PROC, EV_NET_CALL_PROC, NULL},
+  {ST_CALL_INIT, ST_NULL, EV_NET_DISC, cb_out_2},
+
+  {ST_CALL_PROC, ST_ACTIVE_SELP, EV_NET_CONN, cb_out_2},
+  {ST_CALL_PROC, ST_NULL, EV_NET_DISC, cb_disc_1},
+  {ST_CALL_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  /* Incoming */
+  {ST_NULL, ST_CALL_PRES, EV_NET_SETUP, NULL},
+
+  {ST_CALL_PRES, ST_INCM_PROC, EV_USR_PROCED_REQ, cb_in_1},
+  {ST_CALL_PRES, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  {ST_INCM_PROC, ST_CONN_REQ, EV_USR_SETUP_RESP, cb_in_2},
+  {ST_INCM_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  {ST_CONN_REQ, ST_ACTIVE_SELP, EV_NET_CONN_ACK, cb_in_3},
+
+  /* Active */
+  {ST_ACTIVE, ST_NULL, EV_NET_DISC, cb_disc_1},
+  {ST_ACTIVE, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+  {ST_ACTIVE, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+  /* Disconnect */
+
+  {ST_DISC_REQ, ST_NULL, EV_NET_DISC, cb_disc_1},
+  {ST_DISC_REQ, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+  /* protocol selection */
+  {ST_ACTIVE_SELP, ST_ACTIVE_ACTV, EV_NET_SELP_RESP, cb_selp_1},
+  {ST_ACTIVE_SELP, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  {ST_ACTIVE_ACTV, ST_ACTIVE, EV_NET_ACTV_RESP, cb_open},
+  {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+  /* Timers */
+  {ST_CALL_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+  {ST_DISC_REQ, ST_NULL, EV_TIMER, cb_disc_3},
+  {ST_ACTIVE_SELP, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+  {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_TIMER, cb_disc_2},        
+  {ST_INCM_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+  {ST_CONN_REQ, ST_CONN_REQ, EV_TIMER, cb_in_2},
+        
+  {0xff, 0, 0, NULL}
+};
+
+
+static void pcbit_fsm_timer(unsigned long data)
+{
+        struct pcbit_dev *dev;
+        struct pcbit_chan *chan;
+
+        chan = (struct pcbit_chan *) data;
+
+        del_timer(&chan->fsm_timer);
+        chan->fsm_timer.function = NULL;
+
+        dev = chan2dev(chan);
+
+        if (dev == NULL) {
+                printk(KERN_WARNING "pcbit: timer for unknown device\n");
+                return;
+        }
+
+        pcbit_fsm_event(dev, chan, EV_TIMER, NULL);
+}
+
+
+void pcbit_fsm_event(struct pcbit_dev *dev, struct pcbit_chan *chan,
+		   unsigned short event, struct callb_data *data)
+{
+	struct fsm_entry * action;	
+	struct fsm_timer_entry *tentry;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+        for (action = fsm_table; action->init != 0xff; action++)
+                if (action->init == chan->fsm_state && action->event == event)
+                        break;
+  
+	if (action->init == 0xff) {
+		
+		spin_unlock_irqrestore(&dev->lock, flags);
+		printk(KERN_DEBUG "fsm error: event %x on state %x\n", 
+                       event, chan->fsm_state);
+		return;
+	}
+
+        if (chan->fsm_timer.function) {
+                del_timer(&chan->fsm_timer);
+                chan->fsm_timer.function = NULL;
+        }
+
+	chan->fsm_state = action->final;
+  
+	pcbit_state_change(dev, chan, action->init, event, action->final);
+
+        for (tentry = fsm_timers; tentry->init != 0xff; tentry++)
+                if (tentry->init == chan->fsm_state)
+                        break;
+
+        if (tentry->init != 0xff) {
+                init_timer(&chan->fsm_timer);
+                chan->fsm_timer.function = &pcbit_fsm_timer;
+                chan->fsm_timer.data = (ulong) chan;
+                chan->fsm_timer.expires = jiffies + tentry->timeout * HZ;
+                add_timer(&chan->fsm_timer);
+        }
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (action->callb)
+		action->callb(dev, chan, data);
+
+}
+
+
+
+
diff --git a/drivers/isdn/pcbit/edss1.h b/drivers/isdn/pcbit/edss1.h
new file mode 100644
index 0000000..6bb5870
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.h
@@ -0,0 +1,99 @@
+/*
+ * DSS.1 module definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef EDSS1_H
+#define EDSS1_H
+
+/* ISDN states */
+
+#define ST_NULL      0
+#define ST_CALL_INIT 1    /* Call initiated */
+#define ST_OVER_SEND 2    /* Overlap sending - Requests More Info 4 call */
+#define ST_CALL_PROC 3    /* Call Proceeding */
+#define ST_CALL_DELV 4
+#define ST_CALL_PRES 6    /* Call Present - Received CONN.IND */
+#define ST_CALL_RECV 7    /* Alerting sent */
+#define ST_CONN_REQ  8    /* Answered - waiting 4 CONN.CONF */
+#define ST_INCM_PROC 9
+#define ST_ACTIVE    10
+#define ST_DISC_REQ  11
+#define ST_DISC_IND  12
+#define ST_SUSP_REQ  15
+#define ST_RESM_REQ  17
+#define ST_RELS_REQ  19
+#define ST_OVER_RECV 25
+
+#define ST_ACTIVE_SELP 26 /* Select protocol on B-Channel */
+#define ST_ACTIVE_ACTV 27 /* Activate B-channel protocol  */
+
+#define MAX_STATE ST_ACTIVE_ACTV
+
+#define EV_NULL               0
+#define EV_USR_SETUP_REQ      1
+#define EV_USR_SETUP_RESP     2
+#define EV_USR_PROCED_REQ     3
+#define EV_USR_RELEASE_REQ    4
+#define EV_USR_REJECT_REQ     4
+
+#define EV_NET_SETUP          16
+#define EV_NET_CALL_PROC      17
+#define EV_NET_SETUP_ACK      18
+#define EV_NET_CONN           19
+#define EV_NET_CONN_ACK       20
+
+#define EV_NET_SELP_RESP      21
+#define EV_NET_ACTV_RESP      22
+
+#define EV_NET_DISC           23
+#define EV_NET_RELEASE        24
+#define EV_NET_RELEASE_COMP   25
+
+#define EV_TIMER              26
+#define EV_ERROR              32
+
+/*
+ *  Cause values
+ *  only the ones we use
+ */ 
+
+#define CAUSE_NORMAL          0x10U 
+#define CAUSE_NOCHAN          0x22U
+
+struct callb_data {
+	unsigned short type;
+	union {
+		struct ConnInfo {
+			char *CalledPN;
+			char *CallingPN;
+		} setup;
+		unsigned short cause;
+	} data;
+};
+
+struct fsm_entry {
+	unsigned short init;
+	unsigned short final;
+	unsigned short event;
+	void (*callb)(struct pcbit_dev *, struct pcbit_chan *, struct callb_data*);
+};
+
+struct fsm_timer_entry {
+	unsigned short init;
+	unsigned long timeout;          /* in seconds */
+};
+
+
+extern void pcbit_fsm_event(struct pcbit_dev *, struct pcbit_chan *,
+			    unsigned short event, struct callb_data *);
+#endif
+
+
+
diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c
new file mode 100644
index 0000000..ba76693
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.c
@@ -0,0 +1,732 @@
+/*
+ * PCBIT-D low-layer interface
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * 19991203 - Fernando Carvalho - takion@superbofh.org
+ * Hacked to compile with egcs and run with current version of isdn modules
+*/
+
+/*
+ *        Based on documentation provided by Inesc:
+ *        - "Interface com bus do PC para o PCBIT e PCBIT-D", Inesc, Jan 93
+ */
+
+/*
+ *        TODO: better handling of errors
+ *              re-write/remove debug printks
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+
+#undef DEBUG_FRAG
+
+
+
+/*
+ *  task queue struct
+ */
+
+
+
+/*
+ *  Layer 3 packet demultiplexer
+ *  drv.c
+ */
+
+extern void pcbit_l3_receive(struct pcbit_dev *dev, ulong msg,
+			     struct sk_buff *skb,
+			     ushort hdr_len, ushort refnum);
+
+/*
+ *  Prototypes
+ */
+
+void pcbit_deliver(void *data);
+static void pcbit_transmit(struct pcbit_dev *dev);
+
+static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack);
+
+static void pcbit_l2_error(struct pcbit_dev *dev);
+static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info);
+static void pcbit_l2_err_recover(unsigned long data);
+
+static void pcbit_firmware_bug(struct pcbit_dev *dev);
+
+static __inline__ void
+pcbit_sched_delivery(struct pcbit_dev *dev)
+{
+	schedule_work(&dev->qdelivery);
+}
+
+
+/*
+ *  Called from layer3
+ */
+
+int
+pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
+	       struct sk_buff *skb, unsigned short hdr_len)
+{
+	struct frame_buf *frame,
+	*ptr;
+	unsigned long flags;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev_kfree_skb(skb);
+		return -1;
+	}
+	if ((frame = (struct frame_buf *) kmalloc(sizeof(struct frame_buf),
+						  GFP_ATOMIC)) == NULL) {
+		printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
+		dev_kfree_skb(skb);
+		return -1;
+	}
+	frame->msg = msg;
+	frame->refnum = refnum;
+	frame->copied = 0;
+	frame->hdr_len = hdr_len;
+
+	if (skb)
+		frame->dt_len = skb->len - hdr_len;
+	else
+		frame->dt_len = 0;
+
+	frame->skb = skb;
+
+	frame->next = NULL;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->write_queue == NULL) {
+		dev->write_queue = frame;
+		spin_unlock_irqrestore(&dev->lock, flags);
+		pcbit_transmit(dev);
+	} else {
+		for (ptr = dev->write_queue; ptr->next; ptr = ptr->next);
+		ptr->next = frame;
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+	}
+	return 0;
+}
+
+static __inline__ void
+pcbit_tx_update(struct pcbit_dev *dev, ushort len)
+{
+	u_char info;
+
+	dev->send_seq = (dev->send_seq + 1) % 8;
+
+	dev->fsize[dev->send_seq] = len;
+	info = 0;
+	info |= dev->rcv_seq << 3;
+	info |= dev->send_seq;
+
+	writeb(info, dev->sh_mem + BANK4);
+
+}
+
+/*
+ * called by interrupt service routine or by write_2
+ */
+
+static void
+pcbit_transmit(struct pcbit_dev *dev)
+{
+	struct frame_buf *frame = NULL;
+	unsigned char unacked;
+	int flen;               /* fragment frame length including all headers */
+	int free;
+	int count,
+	 cp_len;
+	unsigned long flags;
+	unsigned short tt;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
+
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (dev->free > 16 && dev->write_queue && unacked < 7) {
+
+		if (!dev->w_busy)
+			dev->w_busy = 1;
+		else {
+			spin_unlock_irqrestore(&dev->lock, flags);
+			return;
+		}
+
+
+		frame = dev->write_queue;
+		free = dev->free;
+
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		if (frame->copied == 0) {
+
+			/* Type 0 frame */
+
+			ulong 	msg;
+
+			if (frame->skb)
+				flen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len;
+			else
+				flen = FRAME_HDR_LEN + PREHDR_LEN;
+
+			if (flen > free)
+				flen = free;
+
+			msg = frame->msg;
+
+			/*
+			 *  Board level 2 header
+			 */
+
+			pcbit_writew(dev, flen - FRAME_HDR_LEN);
+
+			pcbit_writeb(dev, GET_MSG_CPU(msg));
+
+			pcbit_writeb(dev, GET_MSG_PROC(msg));
+
+			/* TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+			/* TD */
+			pcbit_writew(dev, frame->dt_len);
+
+
+			/*
+			 *  Board level 3 fixed-header
+			 */
+
+			/* LEN = TH */
+			pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+			/* XX */
+			pcbit_writew(dev, 0);
+
+			/* C + S */
+			pcbit_writeb(dev, GET_MSG_CMD(msg));
+			pcbit_writeb(dev, GET_MSG_SCMD(msg));
+
+			/* NUM */
+			pcbit_writew(dev, frame->refnum);
+
+			count = FRAME_HDR_LEN + PREHDR_LEN;
+		} else {
+			/* Type 1 frame */
+
+			flen = 2 + (frame->skb->len - frame->copied);
+
+			if (flen > free)
+				flen = free;
+
+			/* TT */
+			tt = ((ushort) (flen - 2)) | 0x8000U;	/* Type 1 */
+			pcbit_writew(dev, tt);
+
+			count = 2;
+		}
+
+		if (frame->skb) {
+			cp_len = frame->skb->len - frame->copied;
+			if (cp_len > flen - count)
+				cp_len = flen - count;
+
+			memcpy_topcbit(dev, frame->skb->data + frame->copied,
+				       cp_len);
+			frame->copied += cp_len;
+		}
+		/* bookkeeping */
+		dev->free -= flen;
+		pcbit_tx_update(dev, flen);
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		if (frame->skb == NULL || frame->copied == frame->skb->len) {
+
+			dev->write_queue = frame->next;
+
+			if (frame->skb != NULL) {
+				/* free frame */
+				dev_kfree_skb(frame->skb);
+			}
+			kfree(frame);
+		}
+		dev->w_busy = 0;
+		spin_unlock_irqrestore(&dev->lock, flags);
+	} else {
+		spin_unlock_irqrestore(&dev->lock, flags);
+#ifdef DEBUG
+		printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
+		     unacked, dev->free, dev->write_queue ? "not empty" :
+		       "empty");
+#endif
+	}
+}
+
+
+/*
+ *  deliver a queued frame to the upper layer
+ */
+
+void
+pcbit_deliver(void *data)
+{
+	struct frame_buf *frame;
+	unsigned long flags, msg;
+	struct pcbit_dev *dev = (struct pcbit_dev *) data;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	while ((frame = dev->read_queue)) {
+		dev->read_queue = frame->next;
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		SET_MSG_CPU(msg, 0);
+		SET_MSG_PROC(msg, 0);
+		SET_MSG_CMD(msg, frame->skb->data[2]);
+		SET_MSG_SCMD(msg, frame->skb->data[3]);
+
+		frame->refnum = *((ushort *) frame->skb->data + 4);
+		frame->msg = *((ulong *) & msg);
+
+		skb_pull(frame->skb, 6);
+
+		pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len,
+				 frame->refnum);
+
+		kfree(frame);
+
+		spin_lock_irqsave(&dev->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Reads BANK 2 & Reassembles
+ */
+
+static void
+pcbit_receive(struct pcbit_dev *dev)
+{
+	unsigned short tt;
+	u_char cpu,
+	 proc;
+	struct frame_buf *frame = NULL;
+	unsigned long flags;
+	u_char type1;
+
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+		return;
+
+	tt = pcbit_readw(dev);
+
+	if ((tt & 0x7fffU) > 511) {
+		printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n",
+		       tt);
+		pcbit_l2_error(dev);
+		return;
+	}
+	if (!(tt & 0x8000U)) {  /* Type 0 */
+		type1 = 0;
+
+		if (dev->read_frame) {
+			printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
+			/* discard previous queued frame */
+			if (dev->read_frame->skb)
+				kfree_skb(dev->read_frame->skb);
+			kfree(dev->read_frame);
+			dev->read_frame = NULL;
+		}
+		frame = kmalloc(sizeof(struct frame_buf), GFP_ATOMIC);
+
+		if (frame == NULL) {
+			printk(KERN_WARNING "kmalloc failed\n");
+			return;
+		}
+		memset(frame, 0, sizeof(struct frame_buf));
+
+		cpu = pcbit_readb(dev);
+		proc = pcbit_readb(dev);
+
+
+		if (cpu != 0x06 && cpu != 0x02) {
+			printk(KERN_DEBUG "pcbit: invalid cpu value\n");
+			kfree(frame);
+			pcbit_l2_error(dev);
+			return;
+		}
+		/*
+		 * we discard cpu & proc on receiving
+		 * but we read it to update the pointer
+		 */
+
+		frame->hdr_len = pcbit_readw(dev);
+		frame->dt_len = pcbit_readw(dev);
+
+		/*
+		   * 0 sized packet
+		   * I don't know if they are an error or not...
+		   * But they are very frequent
+		   * Not documented
+		 */
+
+		if (frame->hdr_len == 0) {
+			kfree(frame);
+#ifdef DEBUG
+			printk(KERN_DEBUG "0 sized frame\n");
+#endif
+			pcbit_firmware_bug(dev);
+			return;
+		}
+		/* sanity check the length values */
+		if (frame->hdr_len > 1024 || frame->dt_len > 2048) {
+#ifdef DEBUG
+			printk(KERN_DEBUG "length problem: ");
+			printk(KERN_DEBUG "TH=%04x TD=%04x\n",
+			       frame->hdr_len,
+			       frame->dt_len);
+#endif
+			pcbit_l2_error(dev);
+			kfree(frame);
+			return;
+		}
+		/* minimum frame read */
+
+		frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len +
+					   ((frame->hdr_len + 15) & ~15));
+
+		if (!frame->skb) {
+			printk(KERN_DEBUG "pcbit_receive: out of memory\n");
+			kfree(frame);
+			return;
+		}
+		/* 16 byte alignment for IP */
+		if (frame->dt_len)
+			skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
+
+	} else {
+		/* Type 1 */
+		type1 = 1;
+		tt &= 0x7fffU;
+
+		if (!(frame = dev->read_frame)) {
+			printk("Type 1 frame and no frame queued\n");
+			/* usually after an error: toss frame */
+			dev->readptr += tt;
+			if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN)
+				dev->readptr -= BANKLEN;
+			return;
+
+		}
+	}
+
+	memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt);
+
+	frame->copied += tt;
+	spin_lock_irqsave(&dev->lock, flags);
+	if (frame->copied == frame->hdr_len + frame->dt_len) {
+
+		if (type1) {
+			dev->read_frame = NULL;
+		}
+		if (dev->read_queue) {
+			struct frame_buf *ptr;
+			for (ptr = dev->read_queue; ptr->next; ptr = ptr->next);
+			ptr->next = frame;
+		} else
+			dev->read_queue = frame;
+
+	} else {
+		dev->read_frame = frame;
+	}
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ *  The board sends 0 sized frames
+ *  They are TDATA_CONFs that get messed up somehow
+ *  gotta send a fake acknowledgment to the upper layer somehow
+ */
+
+static __inline__ void
+pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan)
+{
+	isdn_ctrl ictl;
+
+	if (chan->queued) {
+		chan->queued--;
+
+		ictl.driver = dev->id;
+		ictl.command = ISDN_STAT_BSENT;
+		ictl.arg = chan->id;
+		dev->dev_if->statcallb(&ictl);
+	}
+}
+
+static void
+pcbit_firmware_bug(struct pcbit_dev *dev)
+{
+	struct pcbit_chan *chan;
+
+	chan = dev->b1;
+
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
+	chan = dev->b2;
+
+	if (chan->fsm_state == ST_ACTIVE) {
+		pcbit_fake_conf(dev, chan);
+	}
+}
+
+irqreturn_t
+pcbit_irq_handler(int interrupt, void *devptr, struct pt_regs *regs)
+{
+	struct pcbit_dev *dev;
+	u_char info,
+	 ack_seq,
+	 read_seq;
+
+	dev = (struct pcbit_dev *) devptr;
+
+	if (!dev) {
+		printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
+		return IRQ_NONE;
+	}
+	if (dev->interrupt) {
+		printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
+		return IRQ_HANDLED;
+	}
+	dev->interrupt = 1;
+
+	info = readb(dev->sh_mem + BANK3);
+
+	if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) {
+		pcbit_l2_active_conf(dev, info);
+		dev->interrupt = 0;
+		return IRQ_HANDLED;
+	}
+	if (info & 0x40U) {     /* E bit set */
+#ifdef DEBUG
+		printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n");
+#endif
+		pcbit_l2_error(dev);
+		dev->interrupt = 0;
+		return IRQ_HANDLED;
+	}
+	if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+		dev->interrupt = 0;
+		return IRQ_HANDLED;
+	}
+	ack_seq = (info >> 3) & 0x07U;
+	read_seq = (info & 0x07U);
+
+	dev->interrupt = 0;
+
+	if (read_seq != dev->rcv_seq) {
+		while (read_seq != dev->rcv_seq) {
+			pcbit_receive(dev);
+			dev->rcv_seq = (dev->rcv_seq + 1) % 8;
+		}
+		pcbit_sched_delivery(dev);
+	}
+	if (ack_seq != dev->unack_seq) {
+		pcbit_recv_ack(dev, ack_seq);
+	}
+	info = dev->rcv_seq << 3;
+	info |= dev->send_seq;
+
+	writeb(info, dev->sh_mem + BANK4);
+	return IRQ_HANDLED;
+}
+
+
+static void
+pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
+{
+	u_char state;
+
+	state = dev->l2_state;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "layer2_active_confirm\n");
+#endif
+
+
+	if (info & 0x80U) {
+		dev->rcv_seq = info & 0x07U;
+		dev->l2_state = L2_RUNNING;
+	} else
+		dev->l2_state = L2_DOWN;
+
+	if (state == L2_STARTING)
+		wake_up_interruptible(&dev->set_running_wq);
+
+	if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
+		pcbit_transmit(dev);
+	}
+}
+
+static void
+pcbit_l2_err_recover(unsigned long data)
+{
+
+	struct pcbit_dev *dev;
+	struct frame_buf *frame;
+
+	dev = (struct pcbit_dev *) data;
+
+	del_timer(&dev->error_recover_timer);
+	if (dev->w_busy || dev->r_busy) {
+		init_timer(&dev->error_recover_timer);
+		dev->error_recover_timer.expires = jiffies + ERRTIME;
+		add_timer(&dev->error_recover_timer);
+		return;
+	}
+	dev->w_busy = dev->r_busy = 1;
+
+	if (dev->read_frame) {
+		if (dev->read_frame->skb)
+			kfree_skb(dev->read_frame->skb);
+		kfree(dev->read_frame);
+		dev->read_frame = NULL;
+	}
+	if (dev->write_queue) {
+		frame = dev->write_queue;
+#ifdef FREE_ON_ERROR
+		dev->write_queue = dev->write_queue->next;
+
+		if (frame->skb) {
+			dev_kfree_skb(frame->skb);
+		}
+		kfree(frame);
+#else
+		frame->copied = 0;
+#endif
+	}
+	dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
+	dev->free = 511;
+	dev->l2_state = L2_ERROR;
+
+	/* this is an hack... */
+	pcbit_firmware_bug(dev);
+
+	dev->writeptr = dev->sh_mem;
+	dev->readptr = dev->sh_mem + BANK2;
+
+	writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
+	       dev->sh_mem + BANK4);
+	dev->w_busy = dev->r_busy = 0;
+
+}
+
+static void
+pcbit_l2_error(struct pcbit_dev *dev)
+{
+	if (dev->l2_state == L2_RUNNING) {
+
+		printk(KERN_INFO "pcbit: layer 2 error\n");
+
+#ifdef DEBUG
+		log_state(dev);
+#endif
+
+		dev->l2_state = L2_DOWN;
+
+		init_timer(&dev->error_recover_timer);
+		dev->error_recover_timer.function = &pcbit_l2_err_recover;
+		dev->error_recover_timer.data = (ulong) dev;
+		dev->error_recover_timer.expires = jiffies + ERRTIME;
+		add_timer(&dev->error_recover_timer);
+	}
+}
+
+/*
+ * Description:
+ * if board acks frames
+ *   update dev->free
+ *   call pcbit_transmit to write possible queued frames
+ */
+
+static void
+pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
+{
+	int i,
+	 count;
+	int unacked;
+
+	unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
+
+	/* dev->unack_seq < ack <= dev->send_seq; */
+
+	if (unacked) {
+
+		if (dev->send_seq > dev->unack_seq) {
+			if (ack <= dev->unack_seq || ack > dev->send_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
+				       dev->id);
+
+				pcbit_l2_error(dev);
+			} else if (ack > dev->send_seq && ack <= dev->unack_seq) {
+				printk(KERN_DEBUG
+				     "layer 2 ack unacceptable - dev %d",
+				       dev->id);
+				pcbit_l2_error(dev);
+			}
+		}
+		/* ack is acceptable */
+
+
+		i = dev->unack_seq;
+
+		do {
+			dev->unack_seq = i = (i + 1) % 8;
+			dev->free += dev->fsize[i];
+		} while (i != ack);
+
+		count = 0;
+		while (count < 7 && dev->write_queue) {
+			u8 lsend_seq = dev->send_seq;
+
+			pcbit_transmit(dev);
+
+			if (dev->send_seq == lsend_seq)
+				break;
+			count++;
+		}
+	} else
+		printk(KERN_DEBUG "recv_ack: unacked = 0\n");
+}
diff --git a/drivers/isdn/pcbit/layer2.h b/drivers/isdn/pcbit/layer2.h
new file mode 100644
index 0000000..0d99da3
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.h
@@ -0,0 +1,288 @@
+/*
+ * PCBIT-D low-layer interface definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * 19991203 - Fernando Carvalho - takion@superbofh.org
+ * Hacked to compile with egcs and run with current version of isdn modules
+*/
+
+#ifndef LAYER2_H
+#define LAYER2_H
+
+#include <linux/interrupt.h>
+
+#include <asm/byteorder.h>
+
+#define BANK1 0x0000U /* PC -> Board */
+#define BANK2 0x01ffU /* Board -> PC */
+#define BANK3 0x03feU /* Att Board */
+#define BANK4 0x03ffU /* Att PC */
+
+#define BANKLEN 0x01FFU
+
+#define LOAD_ZONE_START 0x03f8U
+#define LOAD_ZONE_END   0x03fdU
+
+#define LOAD_RETRY      18000000
+
+
+
+/* TAM - XX - C - S  - NUM */
+#define PREHDR_LEN 8
+/* TT  - M  - I - TH - TD  */      
+#define FRAME_HDR_LEN  8   
+
+#define MSG_CONN_REQ		0x08000100
+#define MSG_CONN_CONF		0x00000101
+#define MSG_CONN_IND		0x00000102
+#define MSG_CONN_RESP		0x08000103
+
+#define MSG_CONN_ACTV_REQ	0x08000300
+#define MSG_CONN_ACTV_CONF	0x00000301
+#define MSG_CONN_ACTV_IND	0x00000302
+#define MSG_CONN_ACTV_RESP	0x08000303
+
+#define MSG_DISC_REQ		0x08000400
+#define MSG_DISC_CONF		0x00000401
+#define MSG_DISC_IND		0x00000402
+#define MSG_DISC_RESP		0x08000403
+
+#define MSG_TDATA_REQ		0x0908E200
+#define MSG_TDATA_CONF		0x0000E201
+#define MSG_TDATA_IND		0x0000E202
+#define MSG_TDATA_RESP		0x0908E203
+
+#define MSG_SELP_REQ		0x09004000
+#define MSG_SELP_CONF		0x00004001
+
+#define MSG_ACT_TRANSP_REQ      0x0908E000
+#define MSG_ACT_TRANSP_CONF     0x0000E001
+
+#define MSG_STPROT_REQ		0x09004100
+#define MSG_STPROT_CONF		0x00004101
+
+#define MSG_PING188_REQ		0x09030500
+#define MSG_PING188_CONF        0x000005bc
+
+#define MSG_WATCH188	        0x09030400
+
+#define MSG_API_ON              0x08020102
+#define MSG_POOL_PCBIT          0x08020400
+#define MSG_POOL_PCBIT_CONF     0x00000401
+
+#define MSG_INFO_IND            0x00002602
+#define MSG_INFO_RESP           0x08002603
+
+#define MSG_DEBUG_188           0x0000ff00
+
+/*
+   
+   long  4 3 2 1
+   Intel 1 2 3 4
+*/
+
+#ifdef __LITTLE_ENDIAN
+#define SET_MSG_SCMD(msg, ch) 	(msg = (msg & 0xffffff00) | (((ch) & 0xff)))
+#define SET_MSG_CMD(msg, ch) 	(msg = (msg & 0xffff00ff) | (((ch) & 0xff) << 8))
+#define SET_MSG_PROC(msg, ch) 	(msg = (msg & 0xff00ffff) | (((ch) & 0xff) << 16))
+#define SET_MSG_CPU(msg, ch) 	(msg = (msg & 0x00ffffff) | (((ch) & 0xff) << 24))
+
+#define GET_MSG_SCMD(msg) 	((msg) & 0xFF)
+#define GET_MSG_CMD(msg) 	((msg) >> 8 & 0xFF)
+#define GET_MSG_PROC(msg) 	((msg) >> 16 & 0xFF)
+#define GET_MSG_CPU(msg) 	((msg) >> 24)
+
+#else
+#error "Non-Intel CPU"
+#endif
+
+#define MAX_QUEUED 7
+
+#define SCHED_READ    0x01
+#define SCHED_WRITE   0x02
+
+#define SET_RUN_TIMEOUT 2*HZ /* 2 seconds */
+     
+struct frame_buf {
+        ulong msg;
+        unsigned int refnum;
+        unsigned int dt_len;
+        unsigned int hdr_len;
+        struct sk_buff *skb;
+	unsigned int copied;
+        struct frame_buf * next;
+};
+
+extern int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum, 
+                          struct sk_buff *skb, unsigned short hdr_len);
+
+extern irqreturn_t pcbit_irq_handler(int interrupt, void *, struct pt_regs *regs);
+
+extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+#ifdef DEBUG
+static __inline__ void log_state(struct pcbit_dev *dev) {
+        printk(KERN_DEBUG "writeptr = %ld\n", 
+	       (ulong) (dev->writeptr - dev->sh_mem));
+        printk(KERN_DEBUG "readptr  = %ld\n", 
+	       (ulong) (dev->readptr - (dev->sh_mem + BANK2)));
+        printk(KERN_DEBUG "{rcv_seq=%01x, send_seq=%01x, unack_seq=%01x}\n", 
+	       dev->rcv_seq, dev->send_seq, dev->unack_seq);
+}
+#endif
+
+static __inline__ struct pcbit_dev * chan2dev(struct pcbit_chan * chan) 
+{
+        struct pcbit_dev * dev;
+        int i;
+
+
+        for (i=0; i<MAX_PCBIT_CARDS; i++)
+                if ((dev=dev_pcbit[i]))
+                        if (dev->b1 == chan || dev->b2 == chan)
+                                return dev;
+        return NULL;
+
+}
+
+static __inline__ struct pcbit_dev * finddev(int id)
+{
+  struct pcbit_dev * dev;
+  int i;
+
+  for (i=0; i<MAX_PCBIT_CARDS; i++)
+    if ((dev=dev_pcbit[i]))
+      if (dev->id == id)
+	return dev;
+  return NULL;
+}
+
+
+/*
+ *  Support routines for reading and writing in the board
+ */
+
+static __inline__ void pcbit_writeb(struct pcbit_dev *dev, unsigned char dt)
+{
+  writeb(dt, dev->writeptr++);
+  if (dev->writeptr == dev->sh_mem + BANKLEN)
+    dev->writeptr = dev->sh_mem;
+}
+
+static __inline__ void pcbit_writew(struct pcbit_dev *dev, unsigned short dt)
+{
+  int dist;
+
+  dist = BANKLEN - (dev->writeptr - dev->sh_mem);
+  switch (dist) {
+  case 2:
+    writew(dt, dev->writeptr);
+    dev->writeptr = dev->sh_mem;
+    break;
+  case 1:
+    writeb((u_char) (dt & 0x00ffU), dev->writeptr);    
+    dev->writeptr = dev->sh_mem;
+    writeb((u_char) (dt >> 8), dev->writeptr++);    
+    break;
+  default:
+    writew(dt, dev->writeptr);
+    dev->writeptr += 2;
+    break;
+  };
+}
+
+static __inline__ void memcpy_topcbit(struct pcbit_dev * dev, u_char * data, 
+				      int len)
+{
+  int diff;
+
+  diff = len - (BANKLEN - (dev->writeptr - dev->sh_mem) );
+
+  if (diff > 0)
+    {
+      memcpy_toio(dev->writeptr, data, len - diff);
+      memcpy_toio(dev->sh_mem, data + (len - diff), diff);
+      dev->writeptr = dev->sh_mem + diff;
+    }
+  else
+    {
+      memcpy_toio(dev->writeptr, data, len);
+
+      dev->writeptr += len;
+      if (diff == 0)
+	dev->writeptr = dev->sh_mem;
+    }
+}
+
+static __inline__ unsigned char pcbit_readb(struct pcbit_dev *dev)
+{
+  unsigned char val;
+
+  val = readb(dev->readptr++);
+  if (dev->readptr == dev->sh_mem + BANK2 + BANKLEN)
+    dev->readptr = dev->sh_mem + BANK2;
+
+  return val;
+}
+
+static __inline__ unsigned short pcbit_readw(struct pcbit_dev *dev)
+{
+  int dist;
+  unsigned short val;
+
+  dist = BANKLEN - ( dev->readptr - (dev->sh_mem + BANK2 ) );
+  switch (dist) {
+  case 2:
+    val = readw(dev->readptr);
+    dev->readptr = dev->sh_mem + BANK2;
+    break;
+  case 1:
+    val = readb(dev->readptr);
+    dev->readptr = dev->sh_mem + BANK2;
+    val = (readb(dev->readptr++) << 8) | val;
+    break;
+  default:
+    val = readw(dev->readptr);
+    dev->readptr += 2;
+    break;
+  };
+  return val;
+}
+
+static __inline__ void memcpy_frompcbit(struct pcbit_dev * dev, u_char * data, int len)
+{
+  int diff;
+
+  diff = len - (BANKLEN - (dev->readptr - (dev->sh_mem + BANK2) ) ); 
+  if (diff > 0)
+    {
+      memcpy_fromio(data, dev->readptr, len - diff);
+      memcpy_fromio(data + (len - diff), dev->sh_mem + BANK2 , diff);
+      dev->readptr = dev->sh_mem + BANK2 + diff;
+    }
+  else
+    {
+      memcpy_fromio(data, dev->readptr, len);
+      dev->readptr += len;
+      if (diff == 0)
+	dev->readptr = dev->sh_mem + BANK2;
+    }
+}
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c
new file mode 100644
index 0000000..282073a
--- /dev/null
+++ b/drivers/isdn/pcbit/module.c
@@ -0,0 +1,130 @@
+/*
+ * PCBIT-D module support
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include "pcbit.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for PCBIT-T card");
+MODULE_AUTHOR("Pedro Roque Marques");
+MODULE_LICENSE("GPL");
+
+static int mem[MAX_PCBIT_CARDS];
+static int irq[MAX_PCBIT_CARDS];
+
+module_param_array(mem, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+
+static int num_boards;
+struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+extern void pcbit_terminate(int board);
+extern int pcbit_init_dev(int board, int mem_base, int irq);
+
+static int __init pcbit_init(void)
+{
+	int board;
+
+	num_boards = 0;
+
+	printk(KERN_NOTICE 
+	       "PCBIT-D device driver v 0.5-fjpc0 19991204 - "
+	       "Copyright (C) 1996 Universidade de Lisboa\n");
+
+	if (mem[0] || irq[0]) 
+	{
+		for (board=0; board < MAX_PCBIT_CARDS && mem[board] && irq[board]; board++)
+		{
+			if (!mem[board])
+				mem[board] = 0xD0000;
+			if (!irq[board])
+				irq[board] = 5;
+			
+			if (pcbit_init_dev(board, mem[board], irq[board]) == 0)
+				num_boards++;
+		
+			else 
+			{
+				printk(KERN_WARNING 
+				       "pcbit_init failed for dev %d", 
+				       board + 1);
+				return -EIO;
+			}
+		}
+	}
+
+	/* Hardcoded default settings detection */
+
+	if (!num_boards)
+	{
+		printk(KERN_INFO 
+		       "Trying to detect board using default settings\n");
+		if (pcbit_init_dev(0, 0xD0000, 5) == 0)
+			num_boards++;
+		else
+			return -EIO;
+	}
+	return 0;
+}
+
+static void __exit pcbit_exit(void)
+{
+#ifdef MODULE
+	int board;
+
+	for (board = 0; board < num_boards; board++)
+		pcbit_terminate(board);
+	printk(KERN_NOTICE 
+	       "PCBIT-D module unloaded\n");
+#endif
+}
+
+#ifndef MODULE
+#define MAX_PARA	(MAX_PCBIT_CARDS * 2)
+static int __init pcbit_setup(char *line)
+{
+	int i, j, argc;
+	char *str;
+	int ints[MAX_PARA+1];
+
+	str = get_options(line, MAX_PARA, ints);
+	argc = ints[0];
+	i = 0;
+	j = 1;
+
+	while (argc && (i<MAX_PCBIT_CARDS)) {
+
+		if (argc) {
+			mem[i]	= ints[j];
+			j++; argc--;
+		}
+		
+		if (argc) {
+			irq[i]	= ints[j];
+			j++; argc--;
+		}
+
+		i++;
+	}
+	return(1);
+}
+__setup("pcbit=", pcbit_setup);
+#endif
+
+module_init(pcbit_init);
+module_exit(pcbit_exit);
+
diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h
new file mode 100644
index 0000000..388bace
--- /dev/null
+++ b/drivers/isdn/pcbit/pcbit.h
@@ -0,0 +1,169 @@
+/*
+ * PCBIT-D device driver definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ * 
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of 
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef PCBIT_H
+#define PCBIT_H
+
+#include <linux/workqueue.h>
+
+#define MAX_PCBIT_CARDS 4
+
+
+#define BLOCK_TIMER
+
+#ifdef __KERNEL__
+
+struct pcbit_chan {
+	unsigned short id;
+	unsigned short callref;                   /* Call Reference */
+	unsigned char  proto;                     /* layer2protocol  */
+	unsigned char  queued;                    /* unacked data messages */
+	unsigned char  layer2link;                /* used in TData */
+	unsigned char  snum;                      /* used in TData */
+	unsigned short s_refnum;
+	unsigned short r_refnum;
+	unsigned short fsm_state;
+	struct timer_list fsm_timer;
+#ifdef  BLOCK_TIMER
+	struct timer_list block_timer;
+#endif
+};
+
+struct msn_entry {
+	char *msn;
+	struct msn_entry * next;
+};
+
+struct pcbit_dev {
+	/* board */
+
+	volatile unsigned char __iomem *sh_mem;		/* RDP address	*/
+	unsigned long ph_mem;
+	unsigned int irq;
+	unsigned int id;
+	unsigned int interrupt;			/* set during interrupt 
+						   processing */
+	spinlock_t lock;
+	/* isdn4linux */
+
+	struct msn_entry * msn_list;		/* ISDN address list */
+	
+	isdn_if * dev_if;
+	
+	ushort ll_hdrlen;
+	ushort hl_hdrlen;
+
+	/* link layer */
+	unsigned char l2_state;
+
+	struct frame_buf *read_queue;
+	struct frame_buf *read_frame;
+	struct frame_buf *write_queue;
+
+	/* Protocol start */
+	wait_queue_head_t set_running_wq;
+	struct timer_list set_running_timer;
+
+	struct timer_list error_recover_timer;
+
+	struct work_struct qdelivery;
+
+	u_char w_busy;
+	u_char r_busy;
+
+	volatile unsigned char __iomem *readptr;
+	volatile unsigned char __iomem *writeptr;
+
+	ushort loadptr;
+
+	unsigned short fsize[8];		/* sent layer2 frames size */
+
+	unsigned char send_seq;
+	unsigned char rcv_seq;
+	unsigned char unack_seq;
+  
+	unsigned short free;
+
+	/* channels */
+
+	struct pcbit_chan *b1;
+	struct pcbit_chan *b2;  
+};
+
+#define STATS_TIMER (10*HZ)
+#define ERRTIME     (HZ/10)
+
+/* MRU */
+#define MAXBUFSIZE  1534
+#define MRU   MAXBUFSIZE
+
+#define STATBUF_LEN 2048
+/*
+ * 
+ */
+
+#endif /* __KERNEL__ */
+
+/* isdn_ctrl only allows a long sized argument */
+
+struct pcbit_ioctl {
+	union {
+		struct byte_op {
+			ushort addr;
+			ushort value;
+		} rdp_byte;
+		unsigned long l2_status;
+	} info;
+};
+
+
+
+#define PCBIT_IOCTL_GETSTAT  0x01    /* layer2 status */
+#define PCBIT_IOCTL_LWMODE   0x02    /* linear write mode */
+#define PCBIT_IOCTL_STRLOAD  0x03    /* start load mode */
+#define PCBIT_IOCTL_ENDLOAD  0x04    /* end load mode */
+#define PCBIT_IOCTL_SETBYTE  0x05    /* set byte */
+#define PCBIT_IOCTL_GETBYTE  0x06    /* get byte */
+#define PCBIT_IOCTL_RUNNING  0x07    /* set protocol running */
+#define PCBIT_IOCTL_WATCH188 0x08    /* set watch 188 */
+#define PCBIT_IOCTL_PING188  0x09    /* ping 188 */
+#define PCBIT_IOCTL_FWMODE   0x0A    /* firmware write mode */
+#define PCBIT_IOCTL_STOP     0x0B    /* stop protocol */
+#define PCBIT_IOCTL_APION    0x0C    /* issue API_ON  */
+
+#ifndef __KERNEL__
+
+#define PCBIT_GETSTAT  (PCBIT_IOCTL_GETSTAT  + IIOCDRVCTL)
+#define PCBIT_LWMODE   (PCBIT_IOCTL_LWMODE   + IIOCDRVCTL)
+#define PCBIT_STRLOAD  (PCBIT_IOCTL_STRLOAD  + IIOCDRVCTL)
+#define PCBIT_ENDLOAD  (PCBIT_IOCTL_ENDLOAD  + IIOCDRVCTL)
+#define PCBIT_SETBYTE  (PCBIT_IOCTL_SETBYTE  + IIOCDRVCTL)
+#define PCBIT_GETBYTE  (PCBIT_IOCTL_GETBYTE  + IIOCDRVCTL)
+#define PCBIT_RUNNING  (PCBIT_IOCTL_RUNNING  + IIOCDRVCTL)
+#define PCBIT_WATCH188 (PCBIT_IOCTL_WATCH188 + IIOCDRVCTL)
+#define PCBIT_PING188  (PCBIT_IOCTL_PING188  + IIOCDRVCTL)
+#define PCBIT_FWMODE   (PCBIT_IOCTL_FWMODE   + IIOCDRVCTL)
+#define PCBIT_STOP     (PCBIT_IOCTL_STOP     + IIOCDRVCTL)
+#define PCBIT_APION    (PCBIT_IOCTL_APION    + IIOCDRVCTL)
+
+#define MAXSUPERLINE 3000
+
+#endif
+
+#define L2_DOWN     0
+#define L2_LOADING  1
+#define L2_LWMODE   2
+#define L2_FWMODE   3
+#define L2_STARTING 4
+#define L2_RUNNING  5
+#define L2_ERROR    6
+
+#endif