]> git.hungrycats.org Git - linux/commitdiff
ISDN: Add support for Eicon Diva 2.02
authorKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
Thu, 6 Jun 2002 14:48:39 +0000 (09:48 -0500)
committerKai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
Thu, 6 Jun 2002 14:48:39 +0000 (09:48 -0500)
(by Joerg Petersohn/Karsten Keil)

Documentation/isdn/README.HiSax
drivers/isdn/hisax/Makefile
drivers/isdn/hisax/config.c
drivers/isdn/hisax/diva.c
drivers/isdn/hisax/ipacx.c [new file with mode: 0644]
drivers/isdn/hisax/ipacx.h [new file with mode: 0644]
include/linux/pci_ids.h

index ab5a306e5570fde9765ffd4be3195e53ac0bee27..ce646436842ff5d64015d46ba52e3845a955917a 100644 (file)
@@ -41,9 +41,10 @@ ELSA Quickstep 3000 (same settings as QS1000)
 ELSA Quickstep 3000PCI
 ELSA PCMCIA
 ITK ix1-micro Rev.2
-Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
-Eicon.Diehl Diva 2.01 ISA and PCI
-Eicon.Diehl Diva Piccola
+Eicon Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
+Eicon Diva 2.01 ISA and PCI
+Eicon Diva 2.02 PCI
+Eicon Diva Piccola
 ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D)
 Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter)
 PCBIT-DP (OEM version of ASUSCOM NETWORK INC. ISDNLink)
index b06f3428e6694fe87c9d43463a114011ca101900..de0bd72ab434fd7800571179f0f9deff8057422f 100644 (file)
@@ -39,7 +39,7 @@ hisax-objs-$(CONFIG_HISAX_AVM_A1_PCMCIA) += avm_a1p.o isac.o arcofi.o hscx.o
 hisax-objs-$(CONFIG_HISAX_FRITZPCI) += avm_pci.o isac.o arcofi.o
 hisax-objs-$(CONFIG_HISAX_ELSA) += elsa.o isac.o arcofi.o hscx.o
 hisax-objs-$(CONFIG_HISAX_IX1MICROR2) += ix1_micro.o isac.o arcofi.o hscx.o
-hisax-objs-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o
+hisax-objs-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o ipacx.o
 hisax-objs-$(CONFIG_HISAX_ASUSCOM) += asuscom.o isac.o arcofi.o hscx.o
 hisax-objs-$(CONFIG_HISAX_TELEINT) += teleint.o isac.o arcofi.o hfc_2bs0.o
 hisax-objs-$(CONFIG_HISAX_SEDLBAUER) += sedlbauer.o isac.o arcofi.o hscx.o isar.o
index f8f8d32097b5d3cfffb8d3f589fb2922936ab567..5efd91c392d8293dc776e1af8c6920c7f7d3b662 100644 (file)
@@ -2094,6 +2094,7 @@ static struct pci_device_id hisax_pci_tbl[] __initdata = {
        {PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA20,     PCI_ANY_ID, PCI_ANY_ID},
        {PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA20_U,   PCI_ANY_ID, PCI_ANY_ID},
        {PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA201,    PCI_ANY_ID, PCI_ANY_ID},
+       {PCI_VENDOR_ID_EICON,    PCI_DEVICE_ID_EICON_DIVA202,    PCI_ANY_ID, PCI_ANY_ID},
 #endif
 #ifdef CONFIG_HISAX_ELSA
        {PCI_VENDOR_ID_ELSA,     PCI_DEVICE_ID_ELSA_MICROLINK,   PCI_ANY_ID, PCI_ANY_ID},
index 02478dc5e3ab9e2d4739315fc0e3d0080f4e55df..dfc147c7044088a1ef042fd9de900d9ed8c1b28e 100644 (file)
@@ -22,6 +22,7 @@
 #include "isac.h"
 #include "hscx.h"
 #include "ipac.h"
+#include "ipacx.h"
 #include "isdnl1.h"
 #include <linux/pci.h>
 
@@ -49,6 +50,7 @@ const char *Diva_revision = "$Revision: 1.25.6.5 $";
 #define DIVA_PCI       2
 #define DIVA_IPAC_ISA  3
 #define DIVA_IPAC_PCI  4
+#define DIVA_IPACX_PCI 5
 
 /* CTRL (Read) */
 #define DIVA_IRQ_STAT  0x01
@@ -68,10 +70,12 @@ const char *Diva_revision = "$Revision: 1.25.6.5 $";
 #define PITA_MISC_REG          0x1c
 #ifdef __BIG_ENDIAN
 #define PITA_PARA_SOFTRESET    0x00000001
+#define PITA_SER_SOFTRESET     0x00000002
 #define PITA_PARA_MPX_MODE     0x00000004
 #define PITA_INT0_ENABLE       0x00000200
 #else
 #define PITA_PARA_SOFTRESET    0x01000000
+#define PITA_SER_SOFTRESET     0x02000000
 #define PITA_PARA_MPX_MODE     0x04000000
 #define PITA_INT0_ENABLE       0x00020000
 #endif
@@ -239,6 +243,47 @@ MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
        memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value);
 }
 
+/* IO-Functions for IPACX type cards */
+static u_char
+MemReadISAC_IPACX(struct IsdnCardState *cs, u_char offset)
+{
+       return (memreadreg(cs->hw.diva.cfg_reg, offset));
+}
+
+static void
+MemWriteISAC_IPACX(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+       memwritereg(cs->hw.diva.cfg_reg, offset, value);
+}
+
+static void
+MemReadISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+       while(size--)
+               *data++ = memreadreg(cs->hw.diva.cfg_reg, 0);
+}
+
+static void
+MemWriteISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+       while(size--)
+               memwritereg(cs->hw.diva.cfg_reg, 0, *data++);
+}
+
+static u_char
+MemReadHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+       return(memreadreg(cs->hw.diva.cfg_reg, offset + 
+                    (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1)));
+}
+
+static void
+MemWriteHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+       memwritereg(cs->hw.diva.cfg_reg, offset + 
+              (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1), value);
+}
+
 /*
  * fast interrupt HSCX stuff goes here
  */
@@ -549,7 +594,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val)
        u_char exval;
        struct BCState *bcs;
 
-       if (val & 0x01) {
+       if (val & 0x01) { // EXB
                bcs = cs->bcs + 1;
                exval = MemReadHSCX(cs, 1, HSCX_EXIR);
                if (exval & 0x40) {
@@ -576,7 +621,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val)
                        debugl1(cs, "HSCX B interrupt %x", val);
                Memhscx_interrupt(cs, val, 1);
        }
-       if (val & 0x02) {
+       if (val & 0x02) {       // EXA
                bcs = cs->bcs;
                exval = MemReadHSCX(cs, 0, HSCX_EXIR);
                if (exval & 0x40) {
@@ -598,7 +643,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val)
                } else if (cs->debug & L1_DEB_HSCX)
                        debugl1(cs, "HSCX A EXIR %x", exval);
        }
-       if (val & 0x04) {
+       if (val & 0x04) {       // ICA
                exval = MemReadHSCX(cs, 0, HSCX_ISTA);
                if (cs->debug & L1_DEB_HSCX)
                        debugl1(cs, "HSCX A interrupt %x", exval);
@@ -659,12 +704,31 @@ Start_IPACPCI:
        memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0);
 }
 
+static void
+diva_irq_ipacx_pci(int intno, void *dev_id, struct pt_regs *regs)
+{
+       struct IsdnCardState *cs = dev_id;
+       u_char val;
+       u_char *cfg;
+
+       if (!cs) {
+               printk(KERN_WARNING "Diva: Spurious interrupt!\n");
+               return;
+       }
+       cfg = (u_char *) cs->hw.diva.pci_cfg;
+       val = *cfg;
+       if (!(val &PITA_INT0_STATUS)) return; // other shared IRQ
+  interrupt_ipacx(cs);      // handler for chip
+       *cfg = PITA_INT0_STATUS;  // Reset PLX interrupt
+}
+
 void
 release_io_diva(struct IsdnCardState *cs)
 {
        int bytecnt;
 
-       if (cs->subtyp == DIVA_IPAC_PCI) {
+       if ((cs->subtyp == DIVA_IPAC_PCI) || 
+           (cs->subtyp == DIVA_IPACX_PCI)   ) {
                u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg;
 
                *cfg = 0; /* disable INT0/1 */ 
@@ -711,6 +775,16 @@ reset_diva(struct IsdnCardState *cs)
                set_current_state(TASK_UNINTERRUPTIBLE);
                schedule_timeout((10*HZ)/1000);
                memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0);
+       } else if (cs->subtyp == DIVA_IPACX_PCI) {
+               unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
+                                       PITA_MISC_REG);
+               *ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout((10*HZ)/1000);
+               *ireg = PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout((10*HZ)/1000);
+    MemWriteISAC_IPACX(cs, IPACX_MASK, 0xff); // Interrupts off
        } else { /* DIVA 2.0 */
                cs->hw.diva.ctrl_reg = 0;        /* Reset On */
                byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
@@ -739,7 +813,10 @@ diva_led_handler(struct IsdnCardState *cs)
 {
        int blink = 0;
 
-       if ((cs->subtyp == DIVA_IPAC_ISA) || (cs->subtyp == DIVA_IPAC_PCI))
+//     if ((cs->subtyp == DIVA_IPAC_ISA) || (cs->subtyp == DIVA_IPAC_PCI))
+       if ((cs->subtyp == DIVA_IPAC_ISA) ||
+           (cs->subtyp == DIVA_IPAC_PCI) ||
+           (cs->subtyp == DIVA_IPACX_PCI)   )
                return;
        del_timer(&cs->hw.diva.tl);
        if (cs->hw.diva.status & DIVA_ASSIGN)
@@ -782,6 +859,12 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
                        release_io_diva(cs);
                        return(0);
                case CARD_INIT:
+                       if (cs->subtyp == DIVA_IPACX_PCI) {
+                               ireg = (unsigned int *)cs->hw.diva.pci_cfg;
+                               *ireg = PITA_INT0_ENABLE;
+                         init_ipacx(cs, 3); // init chip and enable interrupts
+        return (0);
+                       }
                        if (cs->subtyp == DIVA_IPAC_PCI) {
                                ireg = (unsigned int *)cs->hw.diva.pci_cfg;
                                *ireg = PITA_INT0_ENABLE;
@@ -818,7 +901,9 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
                        }
                        break;
        }
-       if ((cs->subtyp != DIVA_IPAC_ISA) && (cs->subtyp != DIVA_IPAC_PCI))
+       if ((cs->subtyp != DIVA_IPAC_ISA) && 
+           (cs->subtyp != DIVA_IPAC_PCI) &&
+           (cs->subtyp != DIVA_IPACX_PCI)   )
                diva_led_handler(cs);
        return(0);
 }
@@ -916,7 +1001,8 @@ setup_diva(struct IsdnCard *card)
                printk(KERN_WARNING "Diva: unable to config DIVA PCI\n");
                return (0);
 #endif /* CONFIG_PCI */
-               if (cs->subtyp == DIVA_IPAC_PCI) {
+               if ((cs->subtyp == DIVA_IPAC_PCI) ||
+                   (cs->subtyp == DIVA_IPACX_PCI)   ) {
                        cs->hw.diva.ctrl = 0;
                        cs->hw.diva.isac = 0;
                        cs->hw.diva.hscx = 0;
@@ -938,13 +1024,18 @@ setup_diva(struct IsdnCard *card)
                "Diva: %s card configured at %#lx IRQ %d\n",
                (cs->subtyp == DIVA_PCI) ? "PCI" :
                (cs->subtyp == DIVA_ISA) ? "ISA" : 
-               (cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" : "IPAC PCI",
+               (cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" :
+               (cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
                cs->hw.diva.cfg_reg, cs->irq);
-       if ((cs->subtyp == DIVA_IPAC_PCI) || (cs->subtyp == DIVA_PCI))
-               printk(KERN_INFO "Diva: %s PCI space at %#lx\n",
-                       (cs->subtyp == DIVA_PCI) ? "PCI" : "IPAC PCI",
+       if ((cs->subtyp == DIVA_IPAC_PCI)  || 
+           (cs->subtyp == DIVA_IPACX_PCI) || 
+           (cs->subtyp == DIVA_PCI)         )
+               printk(KERN_INFO "Diva: %s space at %#lx\n",
+                       (cs->subtyp == DIVA_PCI) ? "PCI" :
+                       (cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
                        cs->hw.diva.pci_cfg);
-       if (cs->subtyp != DIVA_IPAC_PCI) {
+       if ((cs->subtyp != DIVA_IPAC_PCI) &&
+           (cs->subtyp != DIVA_IPACX_PCI)   ) {
                if (check_region(cs->hw.diva.cfg_reg, bytecnt)) {
                        printk(KERN_WARNING
                               "HiSax: %s config port %lx-%lx already in use\n",
@@ -980,6 +1071,17 @@ setup_diva(struct IsdnCard *card)
                cs->irq_func = &diva_irq_ipac_pci;
                val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID);
                printk(KERN_INFO "Diva: IPAC version %x\n", val);
+       } else if (cs->subtyp == DIVA_IPACX_PCI) {
+               cs->readisac  = &MemReadISAC_IPACX;
+               cs->writeisac = &MemWriteISAC_IPACX;
+               cs->readisacfifo  = &MemReadISACfifo_IPACX;
+               cs->writeisacfifo = &MemWriteISACfifo_IPACX;
+               cs->BC_Read_Reg  = &MemReadHSCX_IPACX;
+               cs->BC_Write_Reg = &MemWriteHSCX_IPACX;
+               cs->BC_Send_Data = 0; // function located in ipacx module
+               cs->irq_func = &diva_irq_ipacx_pci;
+               printk(KERN_INFO "Diva: IPACX Design Id: %x\n", 
+            MemReadISAC_IPACX(cs, IPACX_ID) &0x3F);
        } else { /* DIVA 2.0 */
                cs->hw.diva.tl.function = (void *) diva_led_handler;
                cs->hw.diva.tl.data = (long) cs;
diff --git a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c
new file mode 100644 (file)
index 0000000..2d4d4a4
--- /dev/null
@@ -0,0 +1,1029 @@
+/* 
+ *
+ * IPACX specific routines
+ *
+ * Author       Joerg Petersohn
+ * Derived from hisax_isac.c, isac.c, hscx.c and others
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#define __NO_VERSION__
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include "hisax_if.h"
+#include "hisax.h"
+#include "isdnl1.h"
+#include "ipacx.h"
+
+#define DBUSY_TIMER_VALUE 80
+#define TIMER3_VALUE      7000
+#define MAX_DFRAME_LEN_L1 300
+#define B_FIFO_SIZE       64
+#define D_FIFO_SIZE       32
+
+
+// ipacx interrupt mask values    
+#define _MASK_IMASK     0x2E  // global mask
+#define _MASKB_IMASK    0x0B
+#define _MASKD_IMASK    0x03  // all on
+
+//----------------------------------------------------------
+// local function declarations
+//----------------------------------------------------------
+static void ph_command(struct IsdnCardState *cs, unsigned int command);
+static inline void cic_int(struct IsdnCardState *cs);
+static void dch_l2l1(struct PStack *st, int pr, void *arg);
+static void dbusy_timer_handler(struct IsdnCardState *cs);
+static void ipacx_new_ph(struct IsdnCardState *cs);
+static void dch_bh(struct IsdnCardState *cs);
+static void dch_sched_event(struct IsdnCardState *cs, int event);
+static void dch_empty_fifo(struct IsdnCardState *cs, int count);
+static void dch_fill_fifo(struct IsdnCardState *cs);
+static inline void dch_int(struct IsdnCardState *cs);
+static void __devinit dch_setstack(struct PStack *st, struct IsdnCardState *cs);
+static void __devinit dch_init(struct IsdnCardState *cs);
+static void bch_l2l1(struct PStack *st, int pr, void *arg);
+static void bch_sched_event(struct BCState *bcs, int event);
+static void bch_empty_fifo(struct BCState *bcs, int count);
+static void bch_fill_fifo(struct BCState *bcs);
+static void bch_int(struct IsdnCardState *cs, u_char hscx);
+static void bch_mode(struct BCState *bcs, int mode, int bc);
+static void bch_close_state(struct BCState *bcs);
+static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs);
+static int bch_setstack(struct PStack *st, struct BCState *bcs);
+static void __devinit bch_init(struct IsdnCardState *cs, int hscx);
+static void __init clear_pending_ints(struct IsdnCardState *cs);
+
+//----------------------------------------------------------
+// Issue Layer 1 command to chip
+//----------------------------------------------------------
+static void 
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+       if (cs->debug &L1_DEB_ISAC)
+               debugl1(cs, "ph_command (%#x) in (%#x)", command,
+                       cs->dc.isac.ph_state);
+//###################################  
+//     printk(KERN_INFO "ph_command (%#x)\n", command);
+//###################################  
+       cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E);
+}
+
+//----------------------------------------------------------
+// Transceiver interrupt handler
+//----------------------------------------------------------
+static inline void 
+cic_int(struct IsdnCardState *cs)
+{
+       u_char event;
+
+       event = cs->readisac(cs, IPACX_CIR0) >> 4;
+       if (cs->debug &L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event);
+//#########################################  
+//     printk(KERN_INFO "cic_int(%x)\n", event);
+//#########################################  
+  cs->dc.isac.ph_state = event;
+  dch_sched_event(cs, D_L1STATECHANGE);
+}
+
+//==========================================================
+// D channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Command entry point
+//----------------------------------------------------------
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
+{
+       struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+       struct sk_buff *skb = arg;
+  u_char cda1_cr, cda2_cr;
+
+       switch (pr) {
+               case (PH_DATA |REQUEST):
+                       if (cs->debug &DEB_DLOG_HEX)     LogFrame(cs, skb->data, skb->len);
+                       if (cs->debug &DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+                       if (cs->tx_skb) {
+                               skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG
+                               if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+                       } else {
+                               cs->tx_skb = skb;
+                               cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+                               if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+                               dch_fill_fifo(cs);
+                       }
+                       break;
+      
+               case (PH_PULL |INDICATION):
+                       if (cs->tx_skb) {
+                               if (cs->debug & L1_DEB_WARN)
+                                       debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+                               skb_queue_tail(&cs->sq, skb);
+                               break;
+                       }
+                       if (cs->debug & DEB_DLOG_HEX)     LogFrame(cs, skb->data, skb->len);
+                       if (cs->debug & DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+                       cs->tx_skb = skb;
+                       cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+                       if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+                       dch_fill_fifo(cs);
+                       break;
+      
+               case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG
+                       if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+                       if (!cs->tx_skb) {
+                               clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+                               st->l2.l1l2(st, PH_PULL | CONFIRM, NULL);
+                       } else
+                               set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+                       break;
+
+               case (HW_RESET | REQUEST):
+               case (HW_ENABLE | REQUEST):
+                       ph_command(cs, IPACX_CMD_TIM);
+                       break;
+
+               case (HW_INFO3 | REQUEST):
+                       ph_command(cs, IPACX_CMD_AR8);
+                       break;
+
+               case (HW_TESTLOOP | REQUEST):
+      cs->writeisac(cs, IPACX_CDA_TSDP10, 0x80); // Timeslot 0 is B1
+      cs->writeisac(cs, IPACX_CDA_TSDP11, 0x81); // Timeslot 0 is B1
+      cda1_cr = cs->readisac(cs, IPACX_CDA1_CR);
+      cda2_cr = cs->readisac(cs, IPACX_CDA2_CR);
+                       if ((long)arg &1) { // loop B1
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x0a); 
+      }
+      else {  // B1 off
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x0a); 
+      }
+                       if ((long)arg &2) { // loop B2
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x14); 
+      }
+      else {  // B2 off
+        cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x14); 
+      }
+                       break;
+
+               case (HW_DEACTIVATE | RESPONSE):
+                       skb_queue_purge(&cs->rq);
+                       skb_queue_purge(&cs->sq);
+                       if (cs->tx_skb) {
+                               dev_kfree_skb_any(cs->tx_skb);
+                               cs->tx_skb = NULL;
+                       }
+                       if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+                               del_timer(&cs->dbusytimer);
+                       break;
+
+               default:
+                       if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_l2l1 unknown %04x", pr);
+                       break;
+       }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+       struct PStack *st;
+       int     rbchd, stard;
+
+       if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+               rbchd = cs->readisac(cs, IPACX_RBCHD);
+               stard = cs->readisac(cs, IPACX_STARD);
+               if (cs->debug) 
+      debugl1(cs, "D-Channel Busy RBCHD %02x STARD %02x", rbchd, stard);
+               if (!(stard &0x40)) { // D-Channel Busy
+                       set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+      for (st = cs->stlist; st; st = st->next) {
+                               st->l2.l1l2(st, PH_PAUSE | INDICATION, NULL); // flow control on
+                       }
+               } else {
+                       // seems we lost an interrupt; reset transceiver */
+                       clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+                       if (cs->tx_skb) {
+                               dev_kfree_skb_any(cs->tx_skb);
+                               cs->tx_cnt = 0;
+                               cs->tx_skb = NULL;
+                       } else {
+                               printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
+                               debugl1(cs, "D-Channel Busy no skb");
+                       }
+                       cs->writeisac(cs, IPACX_CMDRD, 0x01); // Tx reset, generates XPR
+               }
+       }
+}
+
+//----------------------------------------------------------
+// L1 state machine intermediate layer to isdnl1 module
+//----------------------------------------------------------
+static void
+ipacx_new_ph(struct IsdnCardState *cs)
+{
+       switch (cs->dc.isac.ph_state) {
+               case (IPACX_IND_RES):
+                       ph_command(cs, IPACX_CMD_DI);
+                       l1_msg(cs, HW_RESET | INDICATION, NULL);
+                       break;
+      
+               case (IPACX_IND_DC):
+                       l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+                       break;
+      
+               case (IPACX_IND_DR):
+                       l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+                       break;
+      
+               case (IPACX_IND_PU):
+                       l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+                       break;
+
+               case (IPACX_IND_RSY):
+                       l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+                       break;
+
+               case (IPACX_IND_AR):
+                       l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+                       break;
+      
+               case (IPACX_IND_AI8):
+                       l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+                       break;
+      
+               case (IPACX_IND_AI10):
+                       l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+                       break;
+      
+               default:
+                       break;
+       }
+}
+
+//----------------------------------------------------------
+// bottom half handler for D channel
+//----------------------------------------------------------
+static void
+dch_bh(struct IsdnCardState *cs)
+{
+       struct PStack *st;
+       
+       if (!cs) return;
+  
+       if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+               if (cs->debug) debugl1(cs, "D-Channel Busy cleared");
+               for (st = cs->stlist; st; st = st->next) {
+                       st->l2.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+               }
+       }
+  
+       if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
+               DChannel_proc_rcv(cs);
+  }  
+  
+       if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
+               DChannel_proc_xmt(cs);
+  }  
+  
+       if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+    ipacx_new_ph(cs);
+  }  
+}
+
+//----------------------------------------------------------
+// proceed with bottom half handler dch_bh()
+//----------------------------------------------------------
+static void
+dch_sched_event(struct IsdnCardState *cs, int event)
+{
+       set_bit(event, &cs->event);
+       queue_task(&cs->tqueue, &tq_immediate);
+       mark_bh(IMMEDIATE_BH);
+}
+
+//----------------------------------------------------------
+// Fill buffer from receive FIFO
+//----------------------------------------------------------
+static void 
+dch_empty_fifo(struct IsdnCardState *cs, int count)
+{
+       long flags;
+       u_char *ptr;
+
+       if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+               debugl1(cs, "dch_empty_fifo()");
+
+  // message too large, remove
+       if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+               if (cs->debug &L1_DEB_WARN)
+                       debugl1(cs, "dch_empty_fifo() incoming message too large");
+         cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+               cs->rcvidx = 0;
+               return;
+       }
+  
+       ptr = cs->rcvbuf + cs->rcvidx;
+       cs->rcvidx += count;
+  
+       save_flags(flags);
+       cli();
+       cs->readisacfifo(cs, ptr, count);
+       cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+       restore_flags(flags);
+  
+       if (cs->debug &L1_DEB_ISAC_FIFO) {
+               char *t = cs->dlog;
+
+               t += sprintf(t, "dch_empty_fifo() cnt %d", count);
+               QuickHex(t, ptr, count);
+               debugl1(cs, cs->dlog);
+       }
+}
+
+//----------------------------------------------------------
+// Fill transmit FIFO
+//----------------------------------------------------------
+static void 
+dch_fill_fifo(struct IsdnCardState *cs)
+{
+       long flags;
+       int count;
+       u_char cmd, *ptr;
+
+       if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+               debugl1(cs, "dch_fill_fifo()");
+    
+       if (!cs->tx_skb) return;
+       count = cs->tx_skb->len;
+       if (count <= 0) return;
+
+       if (count > D_FIFO_SIZE) {
+               count = D_FIFO_SIZE;
+               cmd   = 0x08; // XTF
+       } else {
+               cmd   = 0x0A; // XTF | XME
+       }
+  
+       save_flags(flags);
+       cli();
+       ptr = cs->tx_skb->data;
+       skb_pull(cs->tx_skb, count);
+       cs->tx_cnt += count;
+       cs->writeisacfifo(cs, ptr, count);
+       cs->writeisac(cs, IPACX_CMDRD, cmd);
+  
+  // set timeout for transmission contol
+       if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+               debugl1(cs, "dch_fill_fifo dbusytimer running");
+               del_timer(&cs->dbusytimer);
+       }
+       init_timer(&cs->dbusytimer);
+       cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+       add_timer(&cs->dbusytimer);
+       restore_flags(flags);
+  
+       if (cs->debug &L1_DEB_ISAC_FIFO) {
+               char *t = cs->dlog;
+
+               t += sprintf(t, "dch_fill_fifo() cnt %d", count);
+               QuickHex(t, ptr, count);
+               debugl1(cs, cs->dlog);
+       }
+}
+
+//----------------------------------------------------------
+// D channel interrupt handler
+//----------------------------------------------------------
+static inline void 
+dch_int(struct IsdnCardState *cs)
+{
+       struct sk_buff *skb;
+       u_char istad, rstad;
+       long flags;
+       int count;
+
+       istad = cs->readisac(cs, IPACX_ISTAD);
+//##############################################  
+//     printk(KERN_WARNING "dch_int(istad=%02x)\n", istad);
+//##############################################  
+  
+       if (istad &0x80) {  // RME
+         rstad = cs->readisac(cs, IPACX_RSTAD);
+               if ((rstad &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+                       if (!(rstad &0x80))
+                               if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): invalid frame");
+                       if ((rstad &0x40))
+                               if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): RDO");
+                       if (!(rstad &0x20))
+                               if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "dch_int(): CRC error");
+           cs->writeisac(cs, IPACX_CMDRD, 0x80);  // RMC
+               } else {  // received frame ok
+                       count = cs->readisac(cs, IPACX_RBCLD);
+      if (count) count--; // RSTAB is last byte
+                       count &= D_FIFO_SIZE-1;
+                       if (count == 0) count = D_FIFO_SIZE;
+                       dch_empty_fifo(cs, count);
+                       save_flags(flags);
+                       cli();
+                       if ((count = cs->rcvidx) > 0) {
+             cs->rcvidx = 0;
+                               if (!(skb = dev_alloc_skb(count)))
+                                       printk(KERN_WARNING "HiSax dch_int(): receive out of memory\n");
+                               else {
+                                       memcpy(skb_put(skb, count), cs->rcvbuf, count);
+                                       skb_queue_tail(&cs->rq, skb);
+                               }
+                       }
+                       restore_flags(flags);
+    }
+         cs->rcvidx = 0;
+               dch_sched_event(cs, D_RCVBUFREADY);
+       }
+
+       if (istad &0x40) {  // RPF
+               dch_empty_fifo(cs, D_FIFO_SIZE);
+       }
+
+       if (istad &0x20) {  // RFO
+               if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): RFO");
+         cs->writeisac(cs, IPACX_CMDRD, 0x40); //RRES
+       }
+  
+  if (istad &0x10) {  // XPR
+               if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+                       del_timer(&cs->dbusytimer);
+               if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+                       dch_sched_event(cs, D_CLEARBUSY);
+    if (cs->tx_skb) {
+      if (cs->tx_skb->len) {
+        dch_fill_fifo(cs);
+        goto afterXPR;
+      }
+      else {
+        dev_kfree_skb_irq(cs->tx_skb);
+        cs->tx_skb = NULL;
+        cs->tx_cnt = 0;
+      }
+    }
+    if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+      cs->tx_cnt = 0;
+      dch_fill_fifo(cs);
+    } 
+    else {
+      dch_sched_event(cs, D_XMTBUFREADY);
+    }  
+  }  
+  afterXPR:
+
+       if (istad &0x0C) {  // XDU or XMR
+               if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): XDU");
+         if (cs->tx_skb) {
+           skb_push(cs->tx_skb, cs->tx_cnt); // retransmit
+           cs->tx_cnt = 0;
+                       dch_fill_fifo(cs);
+               } else {
+                       printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
+                       debugl1(cs, "ISAC XDU no skb");
+               }
+  }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+dch_setstack(struct PStack *st, struct IsdnCardState *cs)
+{
+       st->l1.l1hw = dch_l2l1;
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+dch_init(struct IsdnCardState *cs)
+{
+       printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n");
+
+       cs->tqueue.routine  = (void *)(void *) dch_bh;
+       cs->setstack_d      = dch_setstack;
+  
+       cs->dbusytimer.function = (void *) dbusy_timer_handler;
+       cs->dbusytimer.data = (long) cs;
+       init_timer(&cs->dbusytimer);
+
+  cs->writeisac(cs, IPACX_TR_CONF0, 0x00);  // clear LDD
+  cs->writeisac(cs, IPACX_TR_CONF2, 0x00);  // enable transmitter
+  cs->writeisac(cs, IPACX_MODED,    0xC9);  // transparent mode 0, RAC, stop/go
+  cs->writeisac(cs, IPACX_MON_CR,   0x00);  // disable monitor channel
+}
+
+
+//==========================================================
+// B channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Entry point for commands
+//----------------------------------------------------------
+static void
+bch_l2l1(struct PStack *st, int pr, void *arg)
+{
+       struct sk_buff *skb = arg;
+       long flags;
+
+       switch (pr) {
+               case (PH_DATA | REQUEST):
+                       save_flags(flags);
+                       cli();
+                       if (st->l1.bcs->tx_skb) {
+                               skb_queue_tail(&st->l1.bcs->squeue, skb);
+                               restore_flags(flags);
+                       } else {
+                               st->l1.bcs->tx_skb = skb;
+                               set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+                               st->l1.bcs->hw.hscx.count = 0;
+                               restore_flags(flags);
+        bch_fill_fifo(st->l1.bcs);
+                       }
+                       break;
+               case (PH_PULL | INDICATION):
+                       if (st->l1.bcs->tx_skb) {
+                               printk(KERN_WARNING "HiSax bch_l2l1(): this shouldn't happen\n");
+                               break;
+                       }
+                       set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+                       st->l1.bcs->tx_skb = skb;
+                       st->l1.bcs->hw.hscx.count = 0;
+      bch_fill_fifo(st->l1.bcs);
+                       break;
+               case (PH_PULL | REQUEST):
+                       if (!st->l1.bcs->tx_skb) {
+                               clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+                               st->l2.l1l2(st, PH_PULL | CONFIRM, NULL);
+                       } else
+                               set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+                       break;
+               case (PH_ACTIVATE | REQUEST):
+                       set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+                       bch_mode(st->l1.bcs, st->l1.mode, st->l1.bc);
+                       l1_msg_b(st, pr, arg);
+                       break;
+               case (PH_DEACTIVATE | REQUEST):
+                       l1_msg_b(st, pr, arg);
+                       break;
+               case (PH_DEACTIVATE | CONFIRM):
+                       clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+                       clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+                       bch_mode(st->l1.bcs, 0, st->l1.bc);
+                       st->l2.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+                       break;
+       }
+}
+
+//----------------------------------------------------------
+// proceed with bottom half handler BChannel_bh()
+//----------------------------------------------------------
+static void
+bch_sched_event(struct BCState *bcs, int event)
+{
+       bcs->event |= 1 << event;
+       queue_task(&bcs->tqueue, &tq_immediate);
+       mark_bh(IMMEDIATE_BH);
+}
+
+//----------------------------------------------------------
+// Read B channel fifo to receive buffer
+//----------------------------------------------------------
+static void
+bch_empty_fifo(struct BCState *bcs, int count)
+{
+       u_char *ptr, hscx;
+       struct IsdnCardState *cs;
+       long flags;
+       int cnt;
+
+       cs = bcs->cs;
+  hscx = bcs->hw.hscx.hscx;
+       if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+               debugl1(cs, "bch_empty_fifo()");
+
+  // message too large, remove
+       if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+               if (cs->debug &L1_DEB_WARN)
+                       debugl1(cs, "bch_empty_fifo() incoming packet too large");
+         cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+               bcs->hw.hscx.rcvidx = 0;
+               return;
+       }
+  
+  // Read data uninterruptible
+       save_flags(flags);
+       cli();
+       ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+       cnt = count;
+       while (cnt--) *ptr++ = cs->BC_Read_Reg(cs, hscx, IPACX_RFIFOB); 
+       cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+  
+       ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+       bcs->hw.hscx.rcvidx += count;
+       restore_flags(flags);
+  
+       if (cs->debug &L1_DEB_HSCX_FIFO) {
+               char *t = bcs->blog;
+
+               t += sprintf(t, "bch_empty_fifo() B-%d cnt %d", hscx, count);
+               QuickHex(t, ptr, count);
+               debugl1(cs, bcs->blog);
+       }
+}
+
+//----------------------------------------------------------
+// Fill buffer to transmit FIFO
+//----------------------------------------------------------
+static void
+bch_fill_fifo(struct BCState *bcs)
+{
+       struct IsdnCardState *cs;
+       int more, count, cnt;
+       u_char *ptr, *p, hscx;
+       long flags;
+
+       cs = bcs->cs;
+       if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+               debugl1(cs, "bch_fill_fifo()");
+
+       if (!bcs->tx_skb)           return;
+       if (bcs->tx_skb->len <= 0)  return;
+
+       hscx = bcs->hw.hscx.hscx;
+       more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+       if (bcs->tx_skb->len > B_FIFO_SIZE) {
+               more  = 1;
+               count = B_FIFO_SIZE;
+       } else {
+               count = bcs->tx_skb->len;
+       }  
+       cnt = count;
+    
+       save_flags(flags);
+       cli();
+       p = ptr = bcs->tx_skb->data;
+       skb_pull(bcs->tx_skb, count);
+       bcs->tx_cnt -= count;
+       bcs->hw.hscx.count += count;
+       while (cnt--) cs->BC_Write_Reg(cs, hscx, IPACX_XFIFOB, *p++); 
+       cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, (more ? 0x08 : 0x0a));
+       restore_flags(flags);
+  
+       if (cs->debug &L1_DEB_HSCX_FIFO) {
+               char *t = bcs->blog;
+
+               t += sprintf(t, "chb_fill_fifo() B-%d cnt %d", hscx, count);
+               QuickHex(t, ptr, count);
+               debugl1(cs, bcs->blog);
+       }
+}
+
+//----------------------------------------------------------
+// B channel interrupt handler
+//----------------------------------------------------------
+static void
+bch_int(struct IsdnCardState *cs, u_char hscx)
+{
+       u_char istab;
+       struct BCState *bcs;
+       struct sk_buff *skb;
+       int count;
+       u_char rstab;
+
+       bcs = cs->bcs + hscx;
+       istab = cs->BC_Read_Reg(cs, hscx, IPACX_ISTAB);
+//##############################################  
+//     printk(KERN_WARNING "bch_int(istab=%02x)\n", istab);
+//##############################################  
+       if (!test_bit(BC_FLG_INIT, &bcs->Flag)) return;
+
+       if (istab &0x80) {      // RME
+               rstab = cs->BC_Read_Reg(cs, hscx, IPACX_RSTAB);
+               if ((rstab &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+                       if (!(rstab &0x80))
+                               if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: invalid frame", hscx);
+                       if ((rstab &0x40) && (bcs->mode != L1_MODE_NULL))
+                               if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: RDO mode=%d", hscx, bcs->mode);
+                       if (!(rstab &0x20))
+                               if (cs->debug &L1_DEB_WARN) 
+          debugl1(cs, "bch_int() B-%d: CRC error", hscx);
+           cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80);  // RMC
+               } 
+    else {  // received frame ok
+                       count = cs->BC_Read_Reg(cs, hscx, IPACX_RBCLB) &(B_FIFO_SIZE-1);
+                       if (count == 0) count = B_FIFO_SIZE;
+                       bch_empty_fifo(bcs, count);
+                       if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+                               if (cs->debug &L1_DEB_HSCX_FIFO)
+                                       debugl1(cs, "bch_int Frame %d", count);
+                               if (!(skb = dev_alloc_skb(count)))
+                                       printk(KERN_WARNING "HiSax bch_int(): receive frame out of memory\n");
+                               else {
+                                       memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+                                       skb_queue_tail(&bcs->rqueue, skb);
+                               }
+                       }
+               }
+               bcs->hw.hscx.rcvidx = 0;
+               bch_sched_event(bcs, B_RCVBUFREADY);
+       }
+  
+       if (istab &0x40) {      // RPF
+               bch_empty_fifo(bcs, B_FIFO_SIZE);
+
+               if (bcs->mode == L1_MODE_TRANS) { // queue every chunk
+                       // receive transparent audio data
+                       if (!(skb = dev_alloc_skb(B_FIFO_SIZE)))
+                               printk(KERN_WARNING "HiSax bch_int(): receive transparent out of memory\n");
+                       else {
+                               memcpy(skb_put(skb, B_FIFO_SIZE), bcs->hw.hscx.rcvbuf, B_FIFO_SIZE);
+                               skb_queue_tail(&bcs->rqueue, skb);
+                       }
+                       bcs->hw.hscx.rcvidx = 0;
+                       bch_sched_event(bcs, B_RCVBUFREADY);
+               }
+       }
+  
+       if (istab &0x20) {      // RFO
+               if (cs->debug &L1_DEB_WARN) 
+      debugl1(cs, "bch_int() B-%d: RFO error", hscx);
+         cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x40);  // RRES
+       }
+
+       if (istab &0x10) {      // XPR
+               if (bcs->tx_skb) {
+                       if (bcs->tx_skb->len) {
+                   bch_fill_fifo(bcs);
+        goto afterXPR;
+      }
+      else {
+                               if (bcs->st->lli.l1writewakeup &&
+                                         (PACKET_NOACK != bcs->tx_skb->pkt_type)) {    
+                                       bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count);
+        }  
+                         dev_kfree_skb_irq(bcs->tx_skb);
+                         bcs->hw.hscx.count = 0; 
+                         bcs->tx_skb = NULL;
+      }
+    }    
+    if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+      bcs->hw.hscx.count = 0;
+      set_bit(BC_FLG_BUSY, &bcs->Flag);
+      bch_fill_fifo(bcs);
+    } else {
+      clear_bit(BC_FLG_BUSY, &bcs->Flag);
+      bch_sched_event(bcs, B_XMTBUFREADY);
+    }
+  }
+  afterXPR:
+
+       if (istab &0x04) {      // XDU
+    if (bcs->mode == L1_MODE_TRANS) {
+                       bch_fill_fifo(bcs);
+    }  
+    else {
+      if (bcs->tx_skb) {  // restart transmitting the whole frame
+        skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+        bcs->tx_cnt += bcs->hw.hscx.count;
+        bcs->hw.hscx.count = 0;
+      }
+           cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x01);  // XRES
+      if (cs->debug &L1_DEB_WARN)
+        debugl1(cs, "bch_int() B-%d XDU error", hscx);
+    }
+       }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_mode(struct BCState *bcs, int mode, int bc)
+{
+       struct IsdnCardState *cs = bcs->cs;
+       int hscx = bcs->hw.hscx.hscx;
+
+        bc = bc ? 1 : 0;  // in case bc is greater than 1
+       if (cs->debug & L1_DEB_HSCX)
+               debugl1(cs, "mode_bch() switch B-% mode %d chan %d", hscx, mode, bc);
+       bcs->mode = mode;
+       bcs->channel = bc;
+  
+  // map controller to according timeslot
+  if (!hscx)
+  {
+    cs->writeisac(cs, IPACX_BCHA_TSDP_BC1, 0x80 | bc);
+    cs->writeisac(cs, IPACX_BCHA_CR,       0x88); 
+  }
+  else
+  {
+    cs->writeisac(cs, IPACX_BCHB_TSDP_BC1, 0x80 | bc);
+    cs->writeisac(cs, IPACX_BCHB_CR,       0x88); 
+  }
+
+       switch (mode) {
+               case (L1_MODE_NULL):
+                   cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC0);  // rec off
+                   cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x30);  // std adj.
+                   cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, 0xFF);  // ints off
+                   cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+                   break;
+               case (L1_MODE_TRANS):
+                   cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0x88);  // ext transp mode
+                   cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x00);  // xxx00000
+                   cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+                   cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+                   break;
+               case (L1_MODE_HDLC):
+                   cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC8);  // transp mode 0
+                   cs->BC_Write_Reg(cs, hscx, IPACX_EXMB,  0x01);  // idle=hdlc flags crc enabled
+                   cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41);  // validate adjustments
+                   cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+                   break;
+       }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_close_state(struct BCState *bcs)
+{
+       bch_mode(bcs, 0, bcs->channel);
+       if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+               if (bcs->hw.hscx.rcvbuf) {
+                       kfree(bcs->hw.hscx.rcvbuf);
+                       bcs->hw.hscx.rcvbuf = NULL;
+               }
+               if (bcs->blog) {
+                       kfree(bcs->blog);
+                       bcs->blog = NULL;
+               }
+               skb_queue_purge(&bcs->rqueue);
+               skb_queue_purge(&bcs->squeue);
+               if (bcs->tx_skb) {
+                       dev_kfree_skb_any(bcs->tx_skb);
+                       bcs->tx_skb = NULL;
+                       clear_bit(BC_FLG_BUSY, &bcs->Flag);
+               }
+       }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_open_state(struct IsdnCardState *cs, struct BCState *bcs)
+{
+       if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+               if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+                       printk(KERN_WARNING
+                               "HiSax open_bchstate(): No memory for hscx.rcvbuf\n");
+                       clear_bit(BC_FLG_INIT, &bcs->Flag);
+                       return (1);
+               }
+               if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+                       printk(KERN_WARNING
+                               "HiSax open_bchstate: No memory for bcs->blog\n");
+                       clear_bit(BC_FLG_INIT, &bcs->Flag);
+                       kfree(bcs->hw.hscx.rcvbuf);
+                       bcs->hw.hscx.rcvbuf = NULL;
+                       return (2);
+               }
+               skb_queue_head_init(&bcs->rqueue);
+               skb_queue_head_init(&bcs->squeue);
+       }
+       bcs->tx_skb = NULL;
+       clear_bit(BC_FLG_BUSY, &bcs->Flag);
+       bcs->event = 0;
+       bcs->hw.hscx.rcvidx = 0;
+       bcs->tx_cnt = 0;
+       return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_setstack(struct PStack *st, struct BCState *bcs)
+{
+       bcs->channel = st->l1.bc;
+       if (bch_open_state(st->l1.hardware, bcs)) return (-1);
+       st->l1.bcs = bcs;
+       st->l1.l2l1 = bch_l2l1;
+       setstack_manager(st);
+       bcs->st = st;
+       setstack_l1_B(st);
+       return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void __devinit
+bch_init(struct IsdnCardState *cs, int hscx)
+{
+       cs->bcs[hscx].BC_SetStack   = bch_setstack;
+       cs->bcs[hscx].BC_Close      = bch_close_state;
+       cs->bcs[hscx].hw.hscx.hscx  = hscx;
+       cs->bcs[hscx].cs            = cs;
+       bch_mode(cs->bcs + hscx, 0, hscx);
+}
+
+
+//==========================================================
+// Shared functions
+//==========================================================
+
+//----------------------------------------------------------
+// Main interrupt handler
+//----------------------------------------------------------
+void 
+interrupt_ipacx(struct IsdnCardState *cs)
+{
+       u_char ista;
+  
+       while ((ista = cs->readisac(cs, IPACX_ISTA))) {
+//#################################################  
+//             printk(KERN_WARNING "interrupt_ipacx(ista=%02x)\n", ista);
+//#################################################  
+    if (ista &0x80) bch_int(cs, 0); // B channel interrupts
+    if (ista &0x40) bch_int(cs, 1);
+    
+    if (ista &0x01) dch_int(cs);    // D channel
+    if (ista &0x10) cic_int(cs);    // Layer 1 state
+  }  
+}
+
+//----------------------------------------------------------
+// Clears chip interrupt status
+//----------------------------------------------------------
+static void __init
+clear_pending_ints(struct IsdnCardState *cs)
+{
+       int ista;
+
+  // all interrupts off
+  cs->writeisac(cs, IPACX_MASK, 0xff);
+       cs->writeisac(cs, IPACX_MASKD, 0xff);
+       cs->BC_Write_Reg(cs, 0, IPACX_MASKB, 0xff);
+       cs->BC_Write_Reg(cs, 1, IPACX_MASKB, 0xff);
+  
+  ista = cs->readisac(cs, IPACX_ISTA); 
+  if (ista &0x80) cs->BC_Read_Reg(cs, 0, IPACX_ISTAB);
+  if (ista &0x40) cs->BC_Read_Reg(cs, 1, IPACX_ISTAB);
+  if (ista &0x10) cs->readisac(cs, IPACX_CIR0);
+  if (ista &0x01) cs->readisac(cs, IPACX_ISTAD); 
+}
+
+//----------------------------------------------------------
+// Does chip configuration work
+// Work to do depends on bit mask in part
+//----------------------------------------------------------
+void __init
+init_ipacx(struct IsdnCardState *cs, int part)
+{
+       if (part &1) {  // initialise chip
+//##################################################  
+//     printk(KERN_INFO "init_ipacx(%x)\n", part);
+//##################################################  
+               clear_pending_ints(cs);
+               bch_init(cs, 0);
+               bch_init(cs, 1);
+               dch_init(cs);
+       }
+       if (part &2) {  // reenable all interrupts and start chip
+               cs->BC_Write_Reg(cs, 0, IPACX_MASKB, _MASKB_IMASK);
+               cs->BC_Write_Reg(cs, 1, IPACX_MASKB, _MASKB_IMASK);
+               cs->writeisac(cs, IPACX_MASKD, _MASKD_IMASK);
+               cs->writeisac(cs, IPACX_MASK, _MASK_IMASK); // global mask register
+
+    // reset HDLC Transmitters/receivers
+               cs->writeisac(cs, IPACX_CMDRD, 0x41); 
+    cs->BC_Write_Reg(cs, 0, IPACX_CMDRB, 0x41);
+    cs->BC_Write_Reg(cs, 1, IPACX_CMDRB, 0x41);
+       ph_command(cs, IPACX_CMD_RES);
+       }
+}
+
+//----------------- end of file -----------------------
+
diff --git a/drivers/isdn/hisax/ipacx.h b/drivers/isdn/hisax/ipacx.h
new file mode 100644 (file)
index 0000000..fef4021
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ *
+ * IPACX specific defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec  */
+
+#ifndef INCLUDE_IPACX_H
+#define INCLUDE_IPACX_H
+
+/* D-channel registers   */
+#define IPACX_RFIFOD        0x00    /* RD       */
+#define IPACX_XFIFOD        0x00    /* WR       */
+#define IPACX_ISTAD         0x20    /* RD       */
+#define IPACX_MASKD         0x20    /* WR       */
+#define IPACX_STARD         0x21    /* RD       */
+#define IPACX_CMDRD         0x21    /* WR       */
+#define IPACX_MODED         0x22    /* RD/WR    */
+#define IPACX_EXMD1         0x23    /* RD/WR    */
+#define IPACX_TIMR1         0x24    /* RD/WR    */
+#define IPACX_SAP1          0x25    /* WR       */
+#define IPACX_SAP2          0x26    /* WR       */
+#define IPACX_RBCLD         0x26    /* RD       */
+#define IPACX_RBCHD         0x27    /* RD       */
+#define IPACX_TEI1          0x27    /* WR       */
+#define IPACX_TEI2          0x28    /* WR       */
+#define IPACX_RSTAD         0x28    /* RD       */
+#define IPACX_TMD           0x29    /* RD/WR    */
+#define IPACX_CIR0          0x2E    /* RD       */
+#define IPACX_CIX0          0x2E    /* WR       */
+#define IPACX_CIR1          0x2F    /* RD       */
+#define IPACX_CIX1          0x2F    /* WR       */
+
+/* Transceiver registers    */
+#define IPACX_TR_CONF0      0x30    /* RD/WR    */
+#define IPACX_TR_CONF1      0x31    /* RD/WR    */
+#define IPACX_TR_CONF2      0x32    /* RD/WR    */
+#define IPACX_TR_STA        0x33    /* RD       */
+#define IPACX_TR_CMD        0x34    /* RD/WR    */
+#define IPACX_SQRR1         0x35    /* RD       */
+#define IPACX_SQXR1         0x35    /* WR       */
+#define IPACX_SQRR2         0x36    /* RD       */
+#define IPACX_SQXR2         0x36    /* WR       */
+#define IPACX_SQRR3         0x37    /* RD       */
+#define IPACX_SQXR3         0x37    /* WR       */
+#define IPACX_ISTATR        0x38    /* RD       */
+#define IPACX_MASKTR        0x39    /* RD/WR    */
+#define IPACX_TR_MODE       0x3A    /* RD/WR    */
+#define IPACX_ACFG1         0x3C    /* RD/WR    */
+#define IPACX_ACFG2         0x3D    /* RD/WR    */
+#define IPACX_AOE           0x3E    /* RD/WR    */
+#define IPACX_ARX           0x3F    /* RD       */
+#define IPACX_ATX           0x3F    /* WR       */
+
+/* IOM: Timeslot, DPS, CDA  */
+#define IPACX_CDA10         0x40    /* RD/WR    */
+#define IPACX_CDA11         0x41    /* RD/WR    */
+#define IPACX_CDA20         0x42    /* RD/WR    */
+#define IPACX_CDA21         0x43    /* RD/WR    */
+#define IPACX_CDA_TSDP10    0x44    /* RD/WR    */
+#define IPACX_CDA_TSDP11    0x45    /* RD/WR    */
+#define IPACX_CDA_TSDP20    0x46    /* RD/WR    */
+#define IPACX_CDA_TSDP21    0x47    /* RD/WR    */
+#define IPACX_BCHA_TSDP_BC1 0x48    /* RD/WR    */
+#define IPACX_BCHA_TSDP_BC2 0x49    /* RD/WR    */
+#define IPACX_BCHB_TSDP_BC1 0x4A    /* RD/WR    */
+#define IPACX_BCHB_TSDP_BC2 0x4B    /* RD/WR    */
+#define IPACX_TR_TSDP_BC1   0x4C    /* RD/WR    */
+#define IPACX_TR_TSDP_BC2   0x4D    /* RD/WR    */
+#define IPACX_CDA1_CR       0x4E    /* RD/WR    */
+#define IPACX_CDA2_CR       0x4F    /* RD/WR    */
+
+/* IOM: Contol, Sync transfer, Monitor    */
+#define IPACX_TR_CR         0x50    /* RD/WR    */
+#define IPACX_TRC_CR        0x50    /* RD/WR    */
+#define IPACX_BCHA_CR       0x51    /* RD/WR    */
+#define IPACX_BCHB_CR       0x52    /* RD/WR    */
+#define IPACX_DCI_CR        0x53    /* RD/WR    */
+#define IPACX_DCIC_CR       0x53    /* RD/WR    */
+#define IPACX_MON_CR        0x54    /* RD/WR    */
+#define IPACX_SDS1_CR       0x55    /* RD/WR    */
+#define IPACX_SDS2_CR       0x56    /* RD/WR    */
+#define IPACX_IOM_CR        0x57    /* RD/WR    */
+#define IPACX_STI           0x58    /* RD       */
+#define IPACX_ASTI          0x58    /* WR       */
+#define IPACX_MSTI          0x59    /* RD/WR    */
+#define IPACX_SDS_CONF      0x5A    /* RD/WR    */
+#define IPACX_MCDA          0x5B    /* RD       */
+#define IPACX_MOR           0x5C    /* RD       */
+#define IPACX_MOX           0x5C    /* WR       */
+#define IPACX_MOSR          0x5D    /* RD       */
+#define IPACX_MOCR          0x5E    /* RD/WR    */
+#define IPACX_MSTA          0x5F    /* RD       */
+#define IPACX_MCONF         0x5F    /* WR       */
+
+/* Interrupt and general registers */
+#define IPACX_ISTA          0x60    /* RD       */
+#define IPACX_MASK          0x60    /* WR       */
+#define IPACX_AUXI          0x61    /* RD       */
+#define IPACX_AUXM          0x61    /* WR       */
+#define IPACX_MODE1         0x62    /* RD/WR    */
+#define IPACX_MODE2         0x63    /* RD/WR    */
+#define IPACX_ID            0x64    /* RD       */
+#define IPACX_SRES          0x64    /* WR       */
+#define IPACX_TIMR2         0x65    /* RD/WR    */
+
+/* B-channel registers */
+#define IPACX_OFF_B1        0x70
+#define IPACX_OFF_B2        0x80
+
+#define IPACX_ISTAB         0x00    /* RD       */
+#define IPACX_MASKB         0x00    /* WR       */
+#define IPACX_STARB         0x01    /* RD       */
+#define IPACX_CMDRB         0x01    /* WR       */
+#define IPACX_MODEB         0x02    /* RD/WR    */
+#define IPACX_EXMB          0x03    /* RD/WR    */
+#define IPACX_RAH1          0x05    /* WR       */
+#define IPACX_RAH2          0x06    /* WR       */
+#define IPACX_RBCLB         0x06    /* RD       */
+#define IPACX_RBCHB         0x07    /* RD       */
+#define IPACX_RAL1          0x07    /* WR       */
+#define IPACX_RAL2          0x08    /* WR       */
+#define IPACX_RSTAB         0x08    /* RD       */
+#define IPACX_TMB           0x09    /* RD/WR    */
+#define IPACX_RFIFOB        0x0A    /*- RD      */
+#define IPACX_XFIFOB        0x0A    /*- WR      */
+
+/* Layer 1 Commands */
+#define IPACX_CMD_TIM    0x0
+#define IPACX_CMD_RES    0x1
+#define IPACX_CMD_SSP    0x2
+#define IPACX_CMD_SCP    0x3
+#define IPACX_CMD_AR8    0x8
+#define IPACX_CMD_AR10   0x9
+#define IPACX_CMD_ARL    0xa
+#define IPACX_CMD_DI     0xf
+
+/* Layer 1 Indications */
+#define IPACX_IND_DR     0x0
+#define IPACX_IND_RES    0x1
+#define IPACX_IND_TMA    0x2
+#define IPACX_IND_SLD    0x3
+#define IPACX_IND_RSY    0x4
+#define IPACX_IND_DR6    0x5
+#define IPACX_IND_PU     0x7
+#define IPACX_IND_AR     0x8
+#define IPACX_IND_ARL    0xa
+#define IPACX_IND_CVR    0xb
+#define IPACX_IND_AI8    0xc
+#define IPACX_IND_AI10   0xd
+#define IPACX_IND_AIL    0xe
+#define IPACX_IND_DC     0xf
+
+extern void init_ipacx(struct IsdnCardState *cs, int part);
+extern void interrupt_ipacx(struct IsdnCardState *cs);
+
+#endif
index 5fd0a9198a2a9f89e02fd665776e8a41bd15b91f..6a45e0eb97e5b545f95c69b8ac565c39879d7e1e 100644 (file)
 #define PCI_DEVICE_ID_EICON_DIVA20PRO_U        0xe003
 #define PCI_DEVICE_ID_EICON_DIVA20_U   0xe004
 #define PCI_DEVICE_ID_EICON_DIVA201    0xe005
+#define PCI_DEVICE_ID_EICON_DIVA202    0xe00b
 #define PCI_DEVICE_ID_EICON_MAESTRA    0xe010
 #define PCI_DEVICE_ID_EICON_MAESTRAQ   0xe012
 #define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013