Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorDavid S. Miller <davem@davemloft.net>
Sun, 26 Jul 2009 17:01:25 +0000 (10:01 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 26 Jul 2009 17:01:25 +0000 (10:01 -0700)
63 files changed:
drivers/isdn/Kconfig
drivers/isdn/act2000/capi.c
drivers/isdn/act2000/module.c
drivers/isdn/hardware/eicon/message.c
drivers/isdn/hardware/eicon/os_4bri.c
drivers/isdn/hardware/mISDN/Kconfig
drivers/isdn/hardware/mISDN/Makefile
drivers/isdn/hardware/mISDN/avmfritz.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfcmulti.c
drivers/isdn/hardware/mISDN/hfcpci.c
drivers/isdn/hardware/mISDN/hfcsusb.c
drivers/isdn/hardware/mISDN/iohelper.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/ipac.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/isar.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/mISDNinfineon.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/mISDNipac.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/mISDNisar.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/netjet.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/netjet.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/speedfax.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/w6692.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/w6692.h [new file with mode: 0644]
drivers/isdn/hisax/Kconfig
drivers/isdn/hisax/Makefile
drivers/isdn/hisax/amd7930_fn.c
drivers/isdn/hisax/callc.c
drivers/isdn/hisax/hfc_pci.c
drivers/isdn/hisax/hfc_sx.c
drivers/isdn/hisax/icc.c
drivers/isdn/hisax/isac.c
drivers/isdn/hisax/isdnhdlc.h [deleted file]
drivers/isdn/hisax/isdnl1.c
drivers/isdn/hisax/isdnl2.c
drivers/isdn/hisax/isdnl3.c
drivers/isdn/hisax/l3_1tr6.c
drivers/isdn/hisax/l3dss1.c
drivers/isdn/hisax/l3ni1.c
drivers/isdn/hisax/q931.c
drivers/isdn/hisax/st5481.h
drivers/isdn/hisax/st5481_b.c
drivers/isdn/hisax/st5481_d.c
drivers/isdn/hisax/st5481_usb.c
drivers/isdn/hisax/tei.c
drivers/isdn/hisax/w6692.c
drivers/isdn/i4l/Kconfig
drivers/isdn/i4l/Makefile
drivers/isdn/i4l/isdnhdlc.c [moved from drivers/isdn/hisax/isdnhdlc.c with 54% similarity]
drivers/isdn/mISDN/hwchannel.c
drivers/isdn/mISDN/layer2.c
drivers/net/igb/e1000_82575.c
drivers/net/igb/e1000_82575.h
drivers/net/igb/e1000_defines.h
drivers/net/igb/e1000_hw.h
drivers/net/igb/e1000_mac.c
drivers/net/igb/e1000_mac.h
drivers/net/igb/e1000_phy.c
drivers/net/igb/e1000_regs.h
drivers/net/igb/igb_ethtool.c
drivers/net/igb/igb_main.c
drivers/net/igbvf/netdev.c
include/linux/isdn/hdlc.h [new file with mode: 0644]
include/linux/mISDNhw.h
include/linux/mISDNif.h

index 02bdca6..022a194 100644 (file)
@@ -21,8 +21,6 @@ menuconfig ISDN
 
 if ISDN
 
-source "drivers/isdn/mISDN/Kconfig"
-
 menuconfig ISDN_I4L
        tristate "Old ISDN4Linux (deprecated)"
        ---help---
@@ -41,9 +39,9 @@ menuconfig ISDN_I4L
          It is still available, though, for use with adapters that are not
          supported by the new CAPI subsystem yet.
 
-if ISDN_I4L
+source "drivers/isdn/mISDN/Kconfig"
+
 source "drivers/isdn/i4l/Kconfig"
-endif
 
 menuconfig ISDN_CAPI
        tristate "CAPI 2.0 subsystem"
index 946c38c..1f0a949 100644 (file)
@@ -78,7 +78,6 @@ static actcapi_msgdsc valid_msg[] = {
 #endif
        {{ 0x00, 0x00}, NULL},
 };
-#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc))
 #define num_valid_imsg 27 /* MANUFACTURER_IND */
 
 /*
@@ -1025,7 +1024,7 @@ actcapi_debug_msg(struct sk_buff *skb, int direction)
 #ifdef DEBUG_DUMP_SKB
        dump_skb(skb);
 #endif
-       for (i = 0; i < num_valid_msg; i++)
+       for (i = 0; i < ARRAY_SIZE(valid_msg); i++)
                if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) &&
                    (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) {
                        descr = valid_msg[i].description;
index 8325022..f774e12 100644 (file)
@@ -23,7 +23,6 @@ static unsigned short act2000_isa_ports[] =
         0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380,
         0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60,
 };
-#define ISA_NRPORTS (sizeof(act2000_isa_ports)/sizeof(unsigned short))
 
 static act2000_card *cards = (act2000_card *) NULL;
 
@@ -686,21 +685,21 @@ act2000_addcard(int bus, int port, int irq, char *id)
                 * This may result in more than one card detected.
                 */
                switch (bus) {
-                       case ACT2000_BUS_ISA:
-                               for (i = 0; i < ISA_NRPORTS; i++)
-                                       if (act2000_isa_detect(act2000_isa_ports[i])) {
-                                               printk(KERN_INFO
-                                                      "act2000: Detected ISA card at port 0x%x\n",
-                                                      act2000_isa_ports[i]);
-                                               act2000_alloccard(bus, act2000_isa_ports[i], irq, id);
-                                       }
-                               break;
-                       case ACT2000_BUS_MCA:
-                       case ACT2000_BUS_PCMCIA:
-                       default:
-                               printk(KERN_WARNING
-                                      "act2000: addcard: Invalid BUS type %d\n",
-                                      bus);
+               case ACT2000_BUS_ISA:
+                       for (i = 0; i < ARRAY_SIZE(act2000_isa_ports); i++)
+                               if (act2000_isa_detect(act2000_isa_ports[i])) {
+                                       printk(KERN_INFO "act2000: Detected "
+                                               "ISA card at port 0x%x\n",
+                                               act2000_isa_ports[i]);
+                                       act2000_alloccard(bus,
+                                               act2000_isa_ports[i], irq, id);
+                               }
+                       break;
+               case ACT2000_BUS_MCA:
+               case ACT2000_BUS_PCMCIA:
+               default:
+                       printk(KERN_WARNING
+                               "act2000: addcard: Invalid BUS type %d\n", bus);
                }
        }
        if (!cards)
index 31f91c1..27d5dd6 100644 (file)
@@ -551,9 +551,7 @@ word api_put(APPL   * appl, CAPI_MSG   * msg)
   dbug(1,dprintf("com=%x",msg->header.command));
 
   for(j=0;j<MAX_MSG_PARMS+1;j++) msg_parms[j].length = 0;
-  for(i=0, ret = _BAD_MSG;
-      i<(sizeof(ftable)/sizeof(struct _ftable));
-      i++) {
+  for(i=0, ret = _BAD_MSG; i < ARRAY_SIZE(ftable); i++) {
 
     if(ftable[i].command==msg->header.command) {
       /* break loop if the message is correct, otherwise continue scan  */
index c964b8d..cb7616c 100644 (file)
@@ -149,8 +149,7 @@ int diva_4bri_init_card(diva_os_xdi_adapter_t * a)
        diva_os_xdi_adapter_t *diva_current;
        diva_os_xdi_adapter_t *adapter_list[4];
        PISDN_ADAPTER Slave;
-       unsigned long bar_length[sizeof(_4bri_bar_length) /
-                                sizeof(_4bri_bar_length[0])];
+       unsigned long bar_length[ARRAY_SIZE(_4bri_bar_length)];
        int v2 = _4bri_is_rev_2_card(a->CardOrdinal);
        int tasks = _4bri_is_rev_2_bri_card(a->CardOrdinal) ? 1 : MQ_INSTANCE_COUNT;
        int factor = (tasks == 1) ? 1 : 2;
index 3024566..bde55d7 100644 (file)
@@ -39,3 +39,54 @@ config MISDN_HFCUSB
          Enable support for USB ISDN TAs with Cologne Chip AG's
          HFC-S USB ISDN Controller
 
+config MISDN_AVMFRITZ
+       tristate "Support for AVM FRITZ!CARD PCI"
+       depends on MISDN
+       depends on PCI
+       select MISDN_IPAC
+       help
+         Enable support for AVMs FRITZ!CARD PCI cards
+
+config MISDN_SPEEDFAX
+       tristate "Support for Sedlbauer Speedfax+"
+       depends on MISDN
+       depends on PCI
+       select MISDN_IPAC
+       select MISDN_ISAR
+       help
+         Enable support for Sedlbauer Speedfax+.
+
+config MISDN_INFINEON
+       tristate "Support for cards with Infineon chipset"
+       depends on MISDN
+       depends on PCI
+       select MISDN_IPAC
+       help
+         Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX
+         chip from Infineon (former manufacturer Siemens).
+
+config MISDN_W6692
+       tristate "Support for cards with Winbond 6692"
+       depends on MISDN
+       depends on PCI
+       help
+         Enable support for Winbond 6692 PCI chip based cards.
+
+config MISDN_NETJET
+       tristate "Support for NETJet cards"
+       depends on MISDN
+       depends on PCI
+       select MISDN_IPAC
+       select ISDN_HDLC
+       help
+         Enable support for Traverse Technologies NETJet PCI cards.
+
+
+config MISDN_IPAC
+       tristate
+       depends on MISDN
+
+config MISDN_ISAR
+       tristate
+       depends on MISDN
+
index b040352..2987d99 100644 (file)
@@ -6,3 +6,11 @@
 obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
 obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
 obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o
+obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o
+obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o
+obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o
+obj-$(CONFIG_MISDN_W6692) += w6692.o
+obj-$(CONFIG_MISDN_NETJET) += netjet.o
+# chip modules
+obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
+obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
new file mode 100644 (file)
index 0000000..81ac541
--- /dev/null
@@ -0,0 +1,1152 @@
+/*
+ * avm_fritz.c    low level stuff for AVM FRITZ!CARD PCI ISDN cards
+ *                Thanks to AVM, Berlin for informations
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <asm/unaligned.h>
+#include "ipac.h"
+
+
+#define AVMFRITZ_REV   "2.1"
+
+static int AVM_cnt;
+static int debug;
+
+enum {
+       AVM_FRITZ_PCI,
+       AVM_FRITZ_PCIV2,
+};
+
+#define HDLC_FIFO              0x0
+#define HDLC_STATUS            0x4
+#define CHIP_WINDOW            0x10
+
+#define CHIP_INDEX             0x4
+#define AVM_HDLC_1             0x00
+#define AVM_HDLC_2             0x01
+#define AVM_ISAC_FIFO          0x02
+#define AVM_ISAC_REG_LOW       0x04
+#define AVM_ISAC_REG_HIGH      0x06
+
+#define AVM_STATUS0_IRQ_ISAC   0x01
+#define AVM_STATUS0_IRQ_HDLC   0x02
+#define AVM_STATUS0_IRQ_TIMER  0x04
+#define AVM_STATUS0_IRQ_MASK   0x07
+
+#define AVM_STATUS0_RESET      0x01
+#define AVM_STATUS0_DIS_TIMER  0x02
+#define AVM_STATUS0_RES_TIMER  0x04
+#define AVM_STATUS0_ENA_IRQ    0x08
+#define AVM_STATUS0_TESTBIT    0x10
+
+#define AVM_STATUS1_INT_SEL    0x0f
+#define AVM_STATUS1_ENA_IOM    0x80
+
+#define HDLC_MODE_ITF_FLG      0x01
+#define HDLC_MODE_TRANS                0x02
+#define HDLC_MODE_CCR_7                0x04
+#define HDLC_MODE_CCR_16       0x08
+#define HDLC_MODE_TESTLOOP     0x80
+
+#define HDLC_INT_XPR           0x80
+#define HDLC_INT_XDU           0x40
+#define HDLC_INT_RPR           0x20
+#define HDLC_INT_MASK          0xE0
+
+#define HDLC_STAT_RME          0x01
+#define HDLC_STAT_RDO          0x10
+#define HDLC_STAT_CRCVFRRAB    0x0E
+#define HDLC_STAT_CRCVFR       0x06
+#define HDLC_STAT_RML_MASK     0x3f00
+
+#define HDLC_CMD_XRS           0x80
+#define HDLC_CMD_XME           0x01
+#define HDLC_CMD_RRS           0x20
+#define HDLC_CMD_XML_MASK      0x3f00
+#define HDLC_FIFO_SIZE         32
+
+/* Fritz PCI v2.0 */
+
+#define AVM_HDLC_FIFO_1                0x10
+#define AVM_HDLC_FIFO_2                0x18
+
+#define AVM_HDLC_STATUS_1      0x14
+#define AVM_HDLC_STATUS_2      0x1c
+
+#define AVM_ISACX_INDEX                0x04
+#define AVM_ISACX_DATA         0x08
+
+/* data struct */
+#define LOG_SIZE               63
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+       u8 fill;
+       u8 mode;
+       u8 xml;
+       u8 cmd;
+#else
+       u8 cmd;
+       u8 xml;
+       u8 mode;
+       u8 fill;
+#endif
+} __attribute__((packed));
+
+struct hdlc_hw {
+       union {
+               u32 ctrl;
+               struct hdlc_stat_reg sr;
+       } ctrl;
+       u32 stat;
+};
+
+struct fritzcard {
+       struct list_head        list;
+       struct pci_dev          *pdev;
+       char                    name[MISDN_MAX_IDLEN];
+       u8                      type;
+       u8                      ctrlreg;
+       u16                     irq;
+       u32                     irqcnt;
+       u32                     addr;
+       spinlock_t              lock; /* hw lock */
+       struct isac_hw          isac;
+       struct hdlc_hw          hdlc[2];
+       struct bchannel         bch[2];
+       char                    log[LOG_SIZE + 1];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct fritzcard *card)
+{
+       card->isac.dch.debug = debug;
+       card->bch[0].debug = debug;
+       card->bch[1].debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+       int ret;
+       struct fritzcard *card;
+
+       ret = param_set_uint(val, kp);
+       if (!ret) {
+               read_lock(&card_lock);
+               list_for_each_entry(card, &Cards, list)
+                       _set_debug(card);
+               read_unlock(&card_lock);
+       }
+       return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(AVMFRITZ_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "avmfritz debug mask");
+
+/* Interface functions */
+
+static u8
+ReadISAC_V1(void *p, u8 offset)
+{
+       struct fritzcard *fc = p;
+       u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+       outb(idx, fc->addr + CHIP_INDEX);
+       return inb(fc->addr + CHIP_WINDOW + (offset & 0xf));
+}
+
+static void
+WriteISAC_V1(void *p, u8 offset, u8 value)
+{
+       struct fritzcard *fc = p;
+       u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+       outb(idx, fc->addr + CHIP_INDEX);
+       outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf));
+}
+
+static void
+ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
+{
+       struct fritzcard *fc = p;
+
+       outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
+       insb(fc->addr + CHIP_WINDOW, data, size);
+}
+
+static void
+WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
+{
+       struct fritzcard *fc = p;
+
+       outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
+       outsb(fc->addr + CHIP_WINDOW, data, size);
+}
+
+static u8
+ReadISAC_V2(void *p, u8 offset)
+{
+       struct fritzcard *fc = p;
+
+       outl(offset, fc->addr + AVM_ISACX_INDEX);
+       return 0xff & inl(fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+WriteISAC_V2(void *p, u8 offset, u8 value)
+{
+       struct fritzcard *fc = p;
+
+       outl(offset, fc->addr + AVM_ISACX_INDEX);
+       outl(value, fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
+{
+       struct fritzcard *fc = p;
+       int i;
+
+       outl(off, fc->addr + AVM_ISACX_INDEX);
+       for (i = 0; i < size; i++)
+               data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
+{
+       struct fritzcard *fc = p;
+       int i;
+
+       outl(off, fc->addr + AVM_ISACX_INDEX);
+       for (i = 0; i < size; i++)
+               outl(data[i], fc->addr + AVM_ISACX_DATA);
+}
+
+static struct bchannel *
+Sel_BCS(struct fritzcard *fc, u32 channel)
+{
+       if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) &&
+               (fc->bch[0].nr & channel))
+               return &fc->bch[0];
+       else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) &&
+               (fc->bch[1].nr & channel))
+               return &fc->bch[1];
+       else
+               return NULL;
+}
+
+static inline void
+__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
+       u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1;
+
+       outl(idx, fc->addr + CHIP_INDEX);
+       outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS);
+}
+
+static inline void
+__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
+       outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
+               AVM_HDLC_STATUS_1));
+}
+
+void
+write_ctrl(struct bchannel *bch, int which) {
+       struct fritzcard *fc = bch->hw;
+       struct hdlc_hw *hdlc;
+
+       hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+       pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr,
+               which, hdlc->ctrl.ctrl);
+       switch (fc->type) {
+       case AVM_FRITZ_PCIV2:
+               __write_ctrl_pciv2(fc, hdlc, bch->nr);
+               break;
+       case AVM_FRITZ_PCI:
+               __write_ctrl_pci(fc, hdlc, bch->nr);
+               break;
+       }
+}
+
+
+static inline u32
+__read_status_pci(u_long addr, u32 channel)
+{
+       outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX);
+       return inl(addr + CHIP_WINDOW + HDLC_STATUS);
+}
+
+static inline u32
+__read_status_pciv2(u_long addr, u32 channel)
+{
+       return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
+               AVM_HDLC_STATUS_1));
+}
+
+
+static u32
+read_status(struct fritzcard *fc, u32 channel)
+{
+       switch (fc->type) {
+       case AVM_FRITZ_PCIV2:
+               return __read_status_pciv2(fc->addr, channel);
+       case AVM_FRITZ_PCI:
+               return __read_status_pci(fc->addr, channel);
+       }
+       /* dummy */
+       return 0;
+}
+
+static void
+enable_hwirq(struct fritzcard *fc)
+{
+       fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
+       outb(fc->ctrlreg, fc->addr + 2);
+}
+
+static void
+disable_hwirq(struct fritzcard *fc)
+{
+       fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ;
+       outb(fc->ctrlreg, fc->addr + 2);
+}
+
+static int
+modehdlc(struct bchannel *bch, int protocol)
+{
+       struct fritzcard *fc = bch->hw;
+       struct hdlc_hw *hdlc;
+
+       hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+       pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name,
+               '@' + bch->nr, bch->state, protocol, bch->nr);
+       hdlc->ctrl.ctrl = 0;
+       switch (protocol) {
+       case -1: /* used for init */
+               bch->state = -1;
+       case ISDN_P_NONE:
+               if (bch->state == ISDN_P_NONE)
+                       break;
+               hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+               hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+               write_ctrl(bch, 5);
+               bch->state = ISDN_P_NONE;
+               test_and_clear_bit(FLG_HDLC, &bch->Flags);
+               test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
+               break;
+       case ISDN_P_B_RAW:
+               bch->state = protocol;
+               hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+               hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+               write_ctrl(bch, 5);
+               hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
+               write_ctrl(bch, 1);
+               hdlc->ctrl.sr.cmd = 0;
+               test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
+               break;
+       case ISDN_P_B_HDLC:
+               bch->state = protocol;
+               hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
+               hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+               write_ctrl(bch, 5);
+               hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
+               write_ctrl(bch, 1);
+               hdlc->ctrl.sr.cmd = 0;
+               test_and_set_bit(FLG_HDLC, &bch->Flags);
+               break;
+       default:
+               pr_info("%s: protocol not known %x\n", fc->name, protocol);
+               return -ENOPROTOOPT;
+       }
+       return 0;
+}
+
+static void
+hdlc_empty_fifo(struct bchannel *bch, int count)
+{
+       u32 *ptr;
+       u8 *p;
+       u32  val, addr;
+       int cnt = 0;
+       struct fritzcard *fc = bch->hw;
+
+       pr_debug("%s: %s %d\n", fc->name, __func__, count);
+       if (!bch->rx_skb) {
+               bch->rx_skb = mI_alloc_skb(bch->maxlen, GFP_ATOMIC);
+               if (!bch->rx_skb) {
+                       pr_info("%s: B receive out of memory\n",
+                               fc->name);
+                       return;
+               }
+       }
+       if ((bch->rx_skb->len + count) > bch->maxlen) {
+               pr_debug("%s: overrun %d\n", fc->name,
+                       bch->rx_skb->len + count);
+               return;
+       }
+       p = skb_put(bch->rx_skb, count);
+       ptr = (u32 *)p;
+       if (AVM_FRITZ_PCIV2 == fc->type)
+               addr = fc->addr + (bch->nr == 2 ?
+                       AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
+       else {
+               addr = fc->addr + CHIP_WINDOW;
+               outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr);
+       }
+       while (cnt < count) {
+               val = le32_to_cpu(inl(addr));
+               put_unaligned(val, ptr);
+               ptr++;
+               cnt += 4;
+       }
+       if (debug & DEBUG_HW_BFIFO) {
+               snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ",
+                       bch->nr, fc->name, count);
+               print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+}
+
+static void
+hdlc_fill_fifo(struct bchannel *bch)
+{
+       struct fritzcard *fc = bch->hw;
+       struct hdlc_hw *hdlc;
+       int count, cnt = 0;
+       u8 *p;
+       u32 *ptr, val, addr;
+
+       hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+       if (!bch->tx_skb)
+               return;
+       count = bch->tx_skb->len - bch->tx_idx;
+       if (count <= 0)
+               return;
+       p = bch->tx_skb->data + bch->tx_idx;
+       hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+       if (count > HDLC_FIFO_SIZE) {
+               count = HDLC_FIFO_SIZE;
+       } else {
+               if (test_bit(FLG_HDLC, &bch->Flags))
+                       hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
+       }
+       pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count,
+               bch->tx_idx, bch->tx_skb->len);
+       ptr = (u32 *)p;
+       bch->tx_idx += count;
+       hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count);
+       if (AVM_FRITZ_PCIV2 == fc->type) {
+               __write_ctrl_pciv2(fc, hdlc, bch->nr);
+               addr = fc->addr + (bch->nr == 2 ?
+                       AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
+       } else {
+               __write_ctrl_pci(fc, hdlc, bch->nr);
+               addr = fc->addr + CHIP_WINDOW;
+       }
+       while (cnt < count) {
+               val = get_unaligned(ptr);
+               outl(cpu_to_le32(val), addr);
+               ptr++;
+               cnt += 4;
+       }
+       if (debug & DEBUG_HW_BFIFO) {
+               snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ",
+                       bch->nr, fc->name, count);
+               print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+}
+
+static void
+HDLC_irq_xpr(struct bchannel *bch)
+{
+       if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+               hdlc_fill_fifo(bch);
+       else {
+               if (bch->tx_skb) {
+                       /* send confirm, on trans, free on hdlc. */
+                       if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               confirm_Bsend(bch);
+                       dev_kfree_skb(bch->tx_skb);
+               }
+               if (get_next_bframe(bch))
+                       hdlc_fill_fifo(bch);
+       }
+}
+
+static void
+HDLC_irq(struct bchannel *bch, u32 stat)
+{
+       struct fritzcard *fc = bch->hw;
+       int             len;
+       struct hdlc_hw  *hdlc;
+
+       hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+       pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat);
+       if (stat & HDLC_INT_RPR) {
+               if (stat & HDLC_STAT_RDO) {
+                       hdlc->ctrl.sr.xml = 0;
+                       hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS;
+                       write_ctrl(bch, 1);
+                       hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+                       write_ctrl(bch, 1);
+                       if (bch->rx_skb)
+                               skb_trim(bch->rx_skb, 0);
+               } else {
+                       len = (stat & HDLC_STAT_RML_MASK) >> 8;
+                       if (!len)
+                               len = 32;
+                       hdlc_empty_fifo(bch, len);
+                       if (!bch->rx_skb)
+                               goto handle_tx;
+                       if ((stat & HDLC_STAT_RME) || test_bit(FLG_TRANSPARENT,
+                           &bch->Flags)) {
+                               if (((stat & HDLC_STAT_CRCVFRRAB) ==
+                                   HDLC_STAT_CRCVFR) ||
+                                   test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+                                       recv_Bchannel(bch, 0);
+                               } else {
+                                       pr_debug("%s: got invalid frame\n",
+                                               fc->name);
+                                       skb_trim(bch->rx_skb, 0);
+                               }
+                       }
+               }
+       }
+handle_tx:
+       if (stat & HDLC_INT_XDU) {
+               /* Here we lost an TX interrupt, so
+                * restart transmitting the whole frame on HDLC
+                * in transparent mode we send the next data
+                */
+               if (bch->tx_skb)
+                       pr_debug("%s: ch%d XDU len(%d) idx(%d) Flags(%lx)\n",
+                               fc->name, bch->nr, bch->tx_skb->len,
+                               bch->tx_idx, bch->Flags);
+               else
+                       pr_debug("%s: ch%d XDU no tx_skb Flags(%lx)\n",
+                               fc->name, bch->nr, bch->Flags);
+               if (bch->tx_skb && bch->tx_skb->len) {
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               bch->tx_idx = 0;
+               }
+               hdlc->ctrl.sr.xml = 0;
+               hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS;
+               write_ctrl(bch, 1);
+               hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+               HDLC_irq_xpr(bch);
+               return;
+       } else if (stat & HDLC_INT_XPR)
+               HDLC_irq_xpr(bch);
+}
+
+static inline void
+HDLC_irq_main(struct fritzcard *fc)
+{
+       u32 stat;
+       struct bchannel *bch;
+
+       stat = read_status(fc, 1);
+       if (stat & HDLC_INT_MASK) {
+               bch = Sel_BCS(fc, 1);
+               if (bch)
+                       HDLC_irq(bch, stat);
+               else
+                       pr_debug("%s: spurious ch1 IRQ\n", fc->name);
+       }
+       stat = read_status(fc, 2);
+       if (stat & HDLC_INT_MASK) {
+               bch = Sel_BCS(fc, 2);
+               if (bch)
+                       HDLC_irq(bch, stat);
+               else
+                       pr_debug("%s: spurious ch2 IRQ\n", fc->name);
+       }
+}
+
+static irqreturn_t
+avm_fritz_interrupt(int intno, void *dev_id)
+{
+       struct fritzcard *fc = dev_id;
+       u8 val;
+       u8 sval;
+
+       spin_lock(&fc->lock);
+       sval = inb(fc->addr + 2);
+       pr_debug("%s: irq stat0 %x\n", fc->name, sval);
+       if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
+               /* shared  IRQ from other HW */
+               spin_unlock(&fc->lock);
+               return IRQ_NONE;
+       }
+       fc->irqcnt++;
+
+       if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+               val = ReadISAC_V1(fc, ISAC_ISTA);
+               mISDNisac_irq(&fc->isac, val);
+       }
+       if (!(sval & AVM_STATUS0_IRQ_HDLC))
+               HDLC_irq_main(fc);
+       spin_unlock(&fc->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+avm_fritzv2_interrupt(int intno, void *dev_id)
+{
+       struct fritzcard *fc = dev_id;
+       u8 val;
+       u8 sval;
+
+       spin_lock(&fc->lock);
+       sval = inb(fc->addr + 2);
+       pr_debug("%s: irq stat0 %x\n", fc->name, sval);
+       if (!(sval & AVM_STATUS0_IRQ_MASK)) {
+               /* shared  IRQ from other HW */
+               spin_unlock(&fc->lock);
+               return IRQ_NONE;
+       }
+       fc->irqcnt++;
+
+       if (sval & AVM_STATUS0_IRQ_HDLC)
+               HDLC_irq_main(fc);
+       if (sval & AVM_STATUS0_IRQ_ISAC) {
+               val = ReadISAC_V2(fc, ISACX_ISTA);
+               mISDNisac_irq(&fc->isac, val);
+       }
+       if (sval & AVM_STATUS0_IRQ_TIMER) {
+               pr_debug("%s: timer irq\n", fc->name);
+               outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2);
+               udelay(1);
+               outb(fc->ctrlreg, fc->addr + 2);
+       }
+       spin_unlock(&fc->lock);
+       return IRQ_HANDLED;
+}
+
+static int
+avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct fritzcard *fc = bch->hw;
+       int ret = -EINVAL;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       u32 id;
+       u_long flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&fc->lock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hdlc_fill_fifo(bch);
+                       ret = 0;
+                       spin_unlock_irqrestore(&fc->lock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&fc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(&fc->lock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = modehdlc(bch, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(&fc->lock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               spin_lock_irqsave(&fc->lock, flags);
+               mISDN_clear_bchannel(bch);
+               modehdlc(bch, ISDN_P_NONE);
+               spin_unlock_irqrestore(&fc->lock, flags);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static void
+inithdlc(struct fritzcard *fc)
+{
+       modehdlc(&fc->bch[0], -1);
+       modehdlc(&fc->bch[1], -1);
+}
+
+void
+clear_pending_hdlc_ints(struct fritzcard *fc)
+{
+       u32 val;
+
+       val = read_status(fc, 1);
+       pr_debug("%s: HDLC 1 STA %x\n", fc->name, val);
+       val = read_status(fc, 2);
+       pr_debug("%s: HDLC 2 STA %x\n", fc->name, val);
+}
+
+static void
+reset_avm(struct fritzcard *fc)
+{
+       switch (fc->type) {
+       case AVM_FRITZ_PCI:
+               fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER;
+               break;
+       case AVM_FRITZ_PCIV2:
+               fc->ctrlreg = AVM_STATUS0_RESET;
+               break;
+       }
+       if (debug & DEBUG_HW)
+               pr_notice("%s: reset\n", fc->name);
+       disable_hwirq(fc);
+       mdelay(5);
+       switch (fc->type) {
+       case AVM_FRITZ_PCI:
+               fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
+               disable_hwirq(fc);
+               outb(AVM_STATUS1_ENA_IOM, fc->addr + 3);
+               break;
+       case AVM_FRITZ_PCIV2:
+               fc->ctrlreg = 0;
+               disable_hwirq(fc);
+               break;
+       }
+       mdelay(1);
+       if (debug & DEBUG_HW)
+               pr_notice("%s: S0/S1 %x/%x\n", fc->name,
+                       inb(fc->addr + 2), inb(fc->addr + 3));
+}
+
+static int
+init_card(struct fritzcard *fc)
+{
+       int             ret, cnt = 3;
+       u_long          flags;
+
+       reset_avm(fc); /* disable IRQ */
+       if (fc->type == AVM_FRITZ_PCIV2)
+               ret = request_irq(fc->irq, avm_fritzv2_interrupt,
+                       IRQF_SHARED, fc->name, fc);
+       else
+               ret = request_irq(fc->irq, avm_fritz_interrupt,
+                       IRQF_SHARED, fc->name, fc);
+       if (ret) {
+               pr_info("%s: couldn't get interrupt %d\n",
+                       fc->name, fc->irq);
+               return ret;
+       }
+       while (cnt--) {
+               spin_lock_irqsave(&fc->lock, flags);
+               ret = fc->isac.init(&fc->isac);
+               if (ret) {
+                       spin_unlock_irqrestore(&fc->lock, flags);
+                       pr_info("%s: ISAC init failed with %d\n",
+                               fc->name, ret);
+                       break;
+               }
+               clear_pending_hdlc_ints(fc);
+               inithdlc(fc);
+               enable_hwirq(fc);
+               /* RESET Receiver and Transmitter */
+               if (AVM_FRITZ_PCIV2 == fc->type) {
+                       WriteISAC_V2(fc, ISACX_MASK, 0);
+                       WriteISAC_V2(fc, ISACX_CMDRD, 0x41);
+               } else {
+                       WriteISAC_V1(fc, ISAC_MASK, 0);
+                       WriteISAC_V1(fc, ISAC_CMDR, 0x41);
+               }
+               spin_unlock_irqrestore(&fc->lock, flags);
+               /* Timeout 10ms */
+               msleep_interruptible(10);
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IRQ %d count %d\n", fc->name,
+                               fc->irq, fc->irqcnt);
+               if (!fc->irqcnt) {
+                       pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
+                               fc->name, fc->irq, 3 - cnt);
+                       reset_avm(fc);
+               } else
+                       return 0;
+       }
+       free_irq(fc->irq, fc);
+       return -EIO;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int ret = 0;
+       struct fritzcard *fc = bch->hw;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       /* Nothing implemented yet */
+       case MISDN_CTRL_FILL_EMPTY:
+       default:
+               pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct fritzcard *fc = bch->hw;
+       int ret = -EINVAL;
+       u_long flags;
+
+       pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       spin_lock_irqsave(&fc->lock, flags);
+                       mISDN_freebchannel(bch);
+                       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+                       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+                       modehdlc(bch, ISDN_P_NONE);
+                       spin_unlock_irqrestore(&fc->lock, flags);
+               }
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bch, arg);
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd);
+       }
+       return ret;
+}
+
+static int
+channel_ctrl(struct fritzcard  *fc, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_LOOP;
+               break;
+       case MISDN_CTRL_LOOP:
+               /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+               if (cq->channel < 0 || cq->channel > 3) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel);
+               break;
+       default:
+               pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+open_bchannel(struct fritzcard *fc, struct channel_req *rq)
+{
+       struct bchannel         *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &fc->bch[rq->adr.channel - 1];
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct fritzcard        *fc = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+
+       pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->protocol == ISDN_P_TE_S0)
+                       err = fc->isac.open(&fc->isac, rq);
+               else
+                       err = open_bchannel(fc, rq);
+               if (err)
+                       break;
+               if (!try_module_get(THIS_MODULE))
+                       pr_info("%s: cannot get module\n", fc->name);
+               break;
+       case CLOSE_CHANNEL:
+               pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id,
+                       __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(fc, arg);
+               break;
+       default:
+               pr_debug("%s: %s unknown command %x\n",
+                       fc->name, __func__, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+int
+setup_fritz(struct fritzcard *fc)
+{
+       u32 val, ver;
+
+       if (!request_region(fc->addr, 32, fc->name)) {
+               pr_info("%s: AVM config port %x-%x already in use\n",
+                       fc->name, fc->addr, fc->addr + 31);
+               return -EIO;
+       }
+       switch (fc->type) {
+       case AVM_FRITZ_PCI:
+               val = inl(fc->addr);
+               outl(AVM_HDLC_1, fc->addr + CHIP_INDEX);
+               ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24;
+               if (debug & DEBUG_HW) {
+                       pr_notice("%s: PCI stat %#x\n", fc->name, val);
+                       pr_notice("%s: PCI Class %X Rev %d\n", fc->name,
+                               val & 0xff, (val >> 8) & 0xff);
+                       pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
+               }
+               ASSIGN_FUNC(V1, ISAC, fc->isac);
+               fc->isac.type = IPAC_TYPE_ISAC;
+               break;
+       case AVM_FRITZ_PCIV2:
+               val = inl(fc->addr);
+               ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24;
+               if (debug & DEBUG_HW) {
+                       pr_notice("%s: PCI V2 stat %#x\n", fc->name, val);
+                       pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name,
+                               val & 0xff, (val>>8) & 0xff);
+                       pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
+               }
+               ASSIGN_FUNC(V2, ISAC, fc->isac);
+               fc->isac.type = IPAC_TYPE_ISACX;
+               break;
+       default:
+               release_region(fc->addr, 32);
+               pr_info("%s: AVM unknown type %d\n", fc->name, fc->type);
+               return -ENODEV;
+       }
+       pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name,
+               (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" :
+               "AVM Fritz!CARD PCIv2", fc->irq, fc->addr);
+       return 0;
+}
+
+static void
+release_card(struct fritzcard *card)
+{
+       u_long flags;
+
+       disable_hwirq(card);
+       spin_lock_irqsave(&card->lock, flags);
+       modehdlc(&card->bch[0], ISDN_P_NONE);
+       modehdlc(&card->bch[1], ISDN_P_NONE);
+       spin_unlock_irqrestore(&card->lock, flags);
+       card->isac.release(&card->isac);
+       free_irq(card->irq, card);
+       mISDN_freebchannel(&card->bch[1]);
+       mISDN_freebchannel(&card->bch[0]);
+       mISDN_unregister_device(&card->isac.dch.dev);
+       release_region(card->addr, 32);
+       pci_disable_device(card->pdev);
+       pci_set_drvdata(card->pdev, NULL);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       kfree(card);
+       AVM_cnt--;
+}
+
+static int __devinit
+setup_instance(struct fritzcard *card)
+{
+       int i, err;
+       u_long flags;
+
+       snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1);
+       write_lock_irqsave(&card_lock, flags);
+       list_add_tail(&card->list, &Cards);
+       write_unlock_irqrestore(&card_lock, flags);
+
+       _set_debug(card);
+       card->isac.name = card->name;
+       spin_lock_init(&card->lock);
+       card->isac.hwlock = &card->lock;
+       mISDNisac_init(&card->isac, card);
+
+       card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       card->isac.dch.dev.D.ctrl = avm_dctrl;
+       for (i = 0; i < 2; i++) {
+               card->bch[i].nr = i + 1;
+               set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+               mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+               card->bch[i].hw = card;
+               card->bch[i].ch.send = avm_l2l1B;
+               card->bch[i].ch.ctrl = avm_bctrl;
+               card->bch[i].ch.nr = i + 1;
+               list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels);
+       }
+       err = setup_fritz(card);
+       if (err)
+               goto error;
+       err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
+               card->name);
+       if (err)
+               goto error_reg;
+       err = init_card(card);
+       if (!err)  {
+               AVM_cnt++;
+               pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt);
+               return 0;
+       }
+       mISDN_unregister_device(&card->isac.dch.dev);
+error_reg:
+       release_region(card->addr, 32);
+error:
+       card->isac.release(&card->isac);
+       mISDN_freebchannel(&card->bch[1]);
+       mISDN_freebchannel(&card->bch[0]);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       kfree(card);
+       return err;
+}
+
+static int __devinit
+fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int err = -ENOMEM;
+       struct fritzcard *card;
+
+       card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL);
+       if (!card) {
+               pr_info("No kmem for fritzcard\n");
+               return err;
+       }
+       if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2)
+               card->type = AVM_FRITZ_PCIV2;
+       else
+               card->type = AVM_FRITZ_PCI;
+       card->pdev = pdev;
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+
+       pr_notice("mISDN: found adapter %s at %s\n",
+              (char *) ent->driver_data, pci_name(pdev));
+
+       card->addr = pci_resource_start(pdev, 1);
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_instance(card);
+       if (err)
+               pci_set_drvdata(pdev, NULL);
+       return err;
+}
+
+static void __devexit
+fritz_remove_pci(struct pci_dev *pdev)
+{
+       struct fritzcard *card = pci_get_drvdata(pdev);
+
+       if (card)
+               release_card(card);
+       else
+               if (debug)
+                       pr_info("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id fcpci_ids[] __devinitdata = {
+       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID,
+         0, 0, (unsigned long) "Fritz!Card PCI"},
+       { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
+         0, 0, (unsigned long) "Fritz!Card PCI v2" },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+static struct pci_driver fcpci_driver = {
+       .name = "fcpci",
+       .probe = fritzpci_probe,
+       .remove = __devexit_p(fritz_remove_pci),
+       .id_table = fcpci_ids,
+};
+
+static int __init AVM_init(void)
+{
+       int err;
+
+       pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV);
+       err = pci_register_driver(&fcpci_driver);
+       return err;
+}
+
+static void __exit AVM_cleanup(void)
+{
+       pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(AVM_init);
+module_exit(AVM_cleanup);
index e1dab30..faed794 100644 (file)
@@ -3416,22 +3416,8 @@ deactivate_bchannel(struct bchannel *bch)
        u_long                  flags;
 
        spin_lock_irqsave(&hc->lock, flags);
-       if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
-               dev_kfree_skb(bch->next_skb);
-               bch->next_skb = NULL;
-       }
-       if (bch->tx_skb) {
-               dev_kfree_skb(bch->tx_skb);
-               bch->tx_skb = NULL;
-       }
-       bch->tx_idx = 0;
-       if (bch->rx_skb) {
-               dev_kfree_skb(bch->rx_skb);
-               bch->rx_skb = NULL;
-       }
+       mISDN_clear_bchannel(bch);
        hc->chan[bch->slot].coeff_count = 0;
-       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
-       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
        hc->chan[bch->slot].rx_off = 0;
        hc->chan[bch->slot].conf = -1;
        mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0);
@@ -5384,9 +5370,10 @@ hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
            ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
            ent->device == PCI_DEVICE_ID_CCD_HFCE1)) {
                printk(KERN_ERR
-                   "Unknown HFC multiport controller (vendor:%x device:%x "
-                   "subvendor:%x subdevice:%x)\n", ent->vendor, ent->device,
-                   ent->subvendor, ent->subdevice);
+                   "Unknown HFC multiport controller (vendor:%04x device:%04x "
+                   "subvendor:%04x subdevice:%04x)\n", pdev->vendor,
+                   pdev->device, pdev->subsystem_vendor,
+                   pdev->subsystem_device);
                printk(KERN_ERR
                    "Please contact the driver maintainer for support.\n");
                return -ENODEV;
index 228ffbe..70e6b0e 100644 (file)
@@ -1522,22 +1522,8 @@ deactivate_bchannel(struct bchannel *bch)
        u_long          flags;
 
        spin_lock_irqsave(&hc->lock, flags);
-       if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
-               dev_kfree_skb(bch->next_skb);
-               bch->next_skb = NULL;
-       }
-       if (bch->tx_skb) {
-               dev_kfree_skb(bch->tx_skb);
-               bch->tx_skb = NULL;
-       }
-       bch->tx_idx = 0;
-       if (bch->rx_skb) {
-               dev_kfree_skb(bch->rx_skb);
-               bch->rx_skb = NULL;
-       }
+       mISDN_clear_bchannel(bch);
        mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
-       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
-       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
        spin_unlock_irqrestore(&hc->lock, flags);
 }
 
index 6b7704c..fc46a26 100644 (file)
@@ -1809,21 +1809,7 @@ deactivate_bchannel(struct bchannel *bch)
                    hw->name, __func__, bch->nr);
 
        spin_lock_irqsave(&hw->lock, flags);
-       if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
-               dev_kfree_skb(bch->next_skb);
-               bch->next_skb = NULL;
-       }
-       if (bch->tx_skb) {
-               dev_kfree_skb(bch->tx_skb);
-               bch->tx_skb = NULL;
-       }
-       bch->tx_idx = 0;
-       if (bch->rx_skb) {
-               dev_kfree_skb(bch->rx_skb);
-               bch->rx_skb = NULL;
-       }
-       clear_bit(FLG_ACTIVE, &bch->Flags);
-       clear_bit(FLG_TX_BUSY, &bch->Flags);
+       mISDN_clear_bchannel(bch);
        spin_unlock_irqrestore(&hw->lock, flags);
        hfcsusb_setup_bch(bch, ISDN_P_NONE);
        hfcsusb_stop_endpoint(hw, bch->nr);
diff --git a/drivers/isdn/hardware/mISDN/iohelper.h b/drivers/isdn/hardware/mISDN/iohelper.h
new file mode 100644 (file)
index 0000000..c16a217
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * iohelper.h
+ *             helper for define functions to access ISDN hardware
+ *              supported are memory mapped IO
+ *             indirect port IO (one port for address, one for data)
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _IOHELPER_H
+#define _IOHELPER_H
+
+typedef        u8      (read_reg_t)(void *, u8);
+typedef        void    (write_reg_t)(void *, u8, u8);
+typedef        void    (fifo_func_t)(void *, u8, u8 *, int);
+
+struct _ioport {
+       u32     port;
+       u32     ale;
+};
+
+#define IOFUNC_IO(name, hws, ap) \
+       static u8 Read##name##_IO(void *p, u8 off) {\
+               struct hws *hw = p;\
+               return inb(hw->ap.port + off);\
+       } \
+       static void Write##name##_IO(void *p, u8 off, u8 val) {\
+               struct hws *hw = p;\
+               outb(val, hw->ap.port + off);\
+       } \
+       static void ReadFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               insb(hw->ap.port + off, dp, size);\
+       } \
+       static void WriteFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               outsb(hw->ap.port + off, dp, size);\
+       }
+
+#define IOFUNC_IND(name, hws, ap) \
+       static u8 Read##name##_IND(void *p, u8 off) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               return inb(hw->ap.port);\
+       } \
+       static void Write##name##_IND(void *p, u8 off, u8 val) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               outb(val, hw->ap.port);\
+       } \
+       static void ReadFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               insb(hw->ap.port, dp, size);\
+       } \
+       static void WriteFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               outsb(hw->ap.port, dp, size);\
+       }
+
+#define IOFUNC_MEMIO(name, hws, typ, adr) \
+       static u8 Read##name##_MIO(void *p, u8 off) {\
+               struct hws *hw = p;\
+               return readb(((typ *)hw->adr) + off);\
+       } \
+       static void Write##name##_MIO(void *p, u8 off, u8 val) {\
+               struct hws *hw = p;\
+               writeb(val, ((typ *)hw->adr) + off);\
+       } \
+       static void ReadFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               while (size--)\
+                       *dp++ = readb(((typ *)hw->adr) + off);\
+       } \
+       static void WriteFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) {\
+               struct inf_hw *hw = p;\
+               while (size--)\
+                       writeb(*dp++, ((typ *)hw->adr) + off);\
+       }
+
+#define ASSIGN_FUNC(typ, name, dest)   do {\
+       dest.read_reg = &Read##name##_##typ;\
+       dest.write_reg = &Write##name##_##typ;\
+       dest.read_fifo = &ReadFiFo##name##_##typ;\
+       dest.write_fifo = &WriteFiFo##name##_##typ;\
+       } while (0)
+#define ASSIGN_FUNC_IPAC(typ, target)  do {\
+       ASSIGN_FUNC(typ, ISAC, target.isac);\
+       ASSIGN_FUNC(typ, IPAC, target);\
+       } while (0)
+
+#endif
\ No newline at end of file
diff --git a/drivers/isdn/hardware/mISDN/ipac.h b/drivers/isdn/hardware/mISDN/ipac.h
new file mode 100644 (file)
index 0000000..f9601d5
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ *
+ * ipac.h      Defines for the Infineon (former Siemens) ISDN
+ *             chip series
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "iohelper.h"
+
+struct isac_hw {
+       struct dchannel         dch;
+       u32                     type;
+       u32                     off;            /* offset to isac regs */
+       char                    *name;
+       spinlock_t              *hwlock;        /* lock HW acccess */
+       read_reg_t              *read_reg;
+       write_reg_t             *write_reg;
+       fifo_func_t             *read_fifo;
+       fifo_func_t             *write_fifo;
+       int                     (*monitor)(void *, u32, u8 *, int);
+       void                    (*release)(struct isac_hw *);
+       int                     (*init)(struct isac_hw *);
+       int                     (*ctrl)(struct isac_hw *, u32, u_long);
+       int                     (*open)(struct isac_hw *, struct channel_req *);
+       u8                      *mon_tx;
+       u8                      *mon_rx;
+       int                     mon_txp;
+       int                     mon_txc;
+       int                     mon_rxp;
+       struct arcofi_msg       *arcofi_list;
+       struct timer_list       arcofitimer;
+       wait_queue_head_t       arcofi_wait;
+       u8                      arcofi_bc;
+       u8                      arcofi_state;
+       u8                      mocr;
+       u8                      adf2;
+       u8                      state;
+};
+
+struct ipac_hw;
+
+struct hscx_hw {
+       struct bchannel         bch;
+       struct ipac_hw          *ip;
+       u8                      fifo_size;
+       u8                      off;    /* offset to ICA or ICB */
+       u8                      slot;
+       char                    log[64];
+};
+
+struct ipac_hw {
+       struct isac_hw          isac;
+       struct hscx_hw          hscx[2];
+       char                    *name;
+       void                    *hw;
+       spinlock_t              *hwlock;        /* lock HW acccess */
+       struct module           *owner;
+       u32                     type;
+       read_reg_t              *read_reg;
+       write_reg_t             *write_reg;
+       fifo_func_t             *read_fifo;
+       fifo_func_t             *write_fifo;
+       void                    (*release)(struct ipac_hw *);
+       int                     (*init)(struct ipac_hw *);
+       int                     (*ctrl)(struct ipac_hw *, u32, u_long);
+       u8                      conf;
+};
+
+#define IPAC_TYPE_ISAC         0x0010
+#define IPAC_TYPE_IPAC         0x0020
+#define IPAC_TYPE_ISACX                0x0040
+#define IPAC_TYPE_IPACX                0x0080
+#define IPAC_TYPE_HSCX         0x0100
+
+#define ISAC_USE_ARCOFI                0x1000
+
+/* Monitor functions */
+#define MONITOR_RX_0           0x1000
+#define MONITOR_RX_1           0x1001
+#define MONITOR_TX_0           0x2000
+#define MONITOR_TX_1           0x2001
+
+/* All registers original Siemens Spec  */
+/* IPAC/ISAC registers */
+#define ISAC_MASK              0x20
+#define ISAC_ISTA              0x20
+#define ISAC_STAR              0x21
+#define ISAC_CMDR              0x21
+#define ISAC_EXIR              0x24
+#define ISAC_ADF2              0x39
+#define ISAC_SPCR              0x30
+#define ISAC_ADF1              0x38
+#define ISAC_CIR0              0x31
+#define ISAC_CIX0              0x31
+#define ISAC_CIR1              0x33
+#define ISAC_CIX1              0x33
+#define ISAC_STCR              0x37
+#define ISAC_MODE              0x22
+#define ISAC_RSTA              0x27
+#define ISAC_RBCL              0x25
+#define ISAC_RBCH              0x2A
+#define ISAC_TIMR              0x23
+#define ISAC_SQXR              0x3b
+#define ISAC_SQRR              0x3b
+#define ISAC_MOSR              0x3a
+#define ISAC_MOCR              0x3a
+#define ISAC_MOR0              0x32
+#define ISAC_MOX0              0x32
+#define ISAC_MOR1              0x34
+#define ISAC_MOX1              0x34
+
+#define ISAC_RBCH_XAC          0x80
+
+#define IPAC_D_TIN2            0x01
+
+/* IPAC/HSCX */
+#define IPAC_ISTAB             0x20    /* RD   */
+#define IPAC_MASKB             0x20    /* WR   */
+#define IPAC_STARB             0x21    /* RD   */
+#define IPAC_CMDRB             0x21    /* WR   */
+#define IPAC_MODEB             0x22    /* R/W  */
+#define IPAC_EXIRB             0x24    /* RD   */
+#define IPAC_RBCLB             0x25    /* RD   */
+#define IPAC_RAH1              0x26    /* WR   */
+#define IPAC_RAH2              0x27    /* WR   */
+#define IPAC_RSTAB             0x27    /* RD   */
+#define IPAC_RAL1              0x28    /* R/W  */
+#define IPAC_RAL2              0x29    /* WR   */
+#define IPAC_RHCRB             0x29    /* RD   */
+#define IPAC_XBCL              0x2A    /* WR   */
+#define IPAC_CCR2              0x2C    /* R/W  */
+#define IPAC_RBCHB             0x2D    /* RD   */
+#define IPAC_XBCH              0x2D    /* WR   */
+#define HSCX_VSTR              0x2E    /* RD   */
+#define IPAC_RLCR              0x2E    /* WR   */
+#define IPAC_CCR1              0x2F    /* R/W  */
+#define IPAC_TSAX              0x30    /* WR   */
+#define IPAC_TSAR              0x31    /* WR   */
+#define IPAC_XCCR              0x32    /* WR   */
+#define IPAC_RCCR              0x33    /* WR   */
+
+/* IPAC_ISTAB/IPAC_MASKB bits */
+#define IPAC_B_XPR             0x10
+#define IPAC_B_RPF             0x40
+#define IPAC_B_RME             0x80
+#define IPAC_B_ON              0x2F
+
+/* IPAC_EXIRB bits */
+#define IPAC_B_RFS             0x04
+#define IPAC_B_RFO             0x10
+#define IPAC_B_XDU             0x40
+#define IPAC_B_XMR             0x80
+
+/* IPAC special registers */
+#define IPAC_CONF              0xC0    /* R/W  */
+#define IPAC_ISTA              0xC1    /* RD   */
+#define IPAC_MASK              0xC1    /* WR   */
+#define IPAC_ID                        0xC2    /* RD   */
+#define IPAC_ACFG              0xC3    /* R/W  */
+#define IPAC_AOE               0xC4    /* R/W  */
+#define IPAC_ARX               0xC5    /* RD   */
+#define IPAC_ATX               0xC5    /* WR   */
+#define IPAC_PITA1             0xC6    /* R/W  */
+#define IPAC_PITA2             0xC7    /* R/W  */
+#define IPAC_POTA1             0xC8    /* R/W  */
+#define IPAC_POTA2             0xC9    /* R/W  */
+#define IPAC_PCFG              0xCA    /* R/W  */
+#define IPAC_SCFG              0xCB    /* R/W  */
+#define IPAC_TIMR2             0xCC    /* R/W  */
+
+/* IPAC_ISTA/_MASK bits */
+#define IPAC__EXB              0x01
+#define IPAC__ICB              0x02
+#define IPAC__EXA              0x04
+#define IPAC__ICA              0x08
+#define IPAC__EXD              0x10
+#define IPAC__ICD              0x20
+#define IPAC__INT0             0x40
+#define IPAC__INT1             0x80
+#define IPAC__ON               0xC0
+
+/* HSCX ISTA/MASK bits */
+#define HSCX__EXB              0x01
+#define HSCX__EXA              0x02
+#define HSCX__ICA              0x04
+
+/* ISAC/ISACX/IPAC/IPACX L1 commands */
+#define ISAC_CMD_TIM           0x0
+#define ISAC_CMD_RS            0x1
+#define ISAC_CMD_SCZ           0x4
+#define ISAC_CMD_SSZ           0x2
+#define ISAC_CMD_AR8           0x8
+#define ISAC_CMD_AR10          0x9
+#define ISAC_CMD_ARL           0xA
+#define ISAC_CMD_DUI           0xF
+
+/* ISAC/ISACX/IPAC/IPACX L1 indications */
+#define ISAC_IND_RS            0x1
+#define ISAC_IND_PU            0x7
+#define ISAC_IND_DR            0x0
+#define ISAC_IND_SD            0x2
+#define ISAC_IND_DIS           0x3
+#define ISAC_IND_EI            0x6
+#define ISAC_IND_RSY           0x4
+#define ISAC_IND_ARD           0x8
+#define ISAC_IND_TI            0xA
+#define ISAC_IND_ATI           0xB
+#define ISAC_IND_AI8           0xC
+#define ISAC_IND_AI10          0xD
+#define ISAC_IND_DID           0xF
+
+/* the new ISACX / IPACX */
+/* D-channel registers   */
+#define ISACX_RFIFOD           0x00    /* RD   */
+#define ISACX_XFIFOD           0x00    /* WR   */
+#define ISACX_ISTAD            0x20    /* RD   */
+#define ISACX_MASKD            0x20    /* WR   */
+#define ISACX_STARD            0x21    /* RD   */
+#define ISACX_CMDRD            0x21    /* WR   */
+#define ISACX_MODED            0x22    /* R/W  */
+#define ISACX_EXMD1            0x23    /* R/W  */
+#define ISACX_TIMR1            0x24    /* R/W  */
+#define ISACX_SAP1             0x25    /* WR   */
+#define ISACX_SAP2             0x26    /* WR   */
+#define ISACX_RBCLD            0x26    /* RD   */
+#define ISACX_RBCHD            0x27    /* RD   */
+#define ISACX_TEI1             0x27    /* WR   */
+#define ISACX_TEI2             0x28    /* WR   */
+#define ISACX_RSTAD            0x28    /* RD   */
+#define ISACX_TMD              0x29    /* R/W  */
+#define ISACX_CIR0             0x2E    /* RD   */
+#define ISACX_CIX0             0x2E    /* WR   */
+#define ISACX_CIR1             0x2F    /* RD   */
+#define ISACX_CIX1             0x2F    /* WR   */
+
+/* Transceiver registers  */
+#define ISACX_TR_CONF0         0x30    /* R/W  */
+#define ISACX_TR_CONF1         0x31    /* R/W  */
+#define ISACX_TR_CONF2         0x32    /* R/W  */
+#define ISACX_TR_STA           0x33    /* RD   */
+#define ISACX_TR_CMD           0x34    /* R/W  */
+#define ISACX_SQRR1            0x35    /* RD   */
+#define ISACX_SQXR1            0x35    /* WR   */
+#define ISACX_SQRR2            0x36    /* RD   */
+#define ISACX_SQXR2            0x36    /* WR   */
+#define ISACX_SQRR3            0x37    /* RD   */
+#define ISACX_SQXR3            0x37    /* WR   */
+#define ISACX_ISTATR           0x38    /* RD   */
+#define ISACX_MASKTR           0x39    /* R/W  */
+#define ISACX_TR_MODE          0x3A    /* R/W  */
+#define ISACX_ACFG1            0x3C    /* R/W  */
+#define ISACX_ACFG2            0x3D    /* R/W  */
+#define ISACX_AOE              0x3E    /* R/W  */
+#define ISACX_ARX              0x3F    /* RD   */
+#define ISACX_ATX              0x3F    /* WR   */
+
+/* IOM: Timeslot, DPS, CDA  */
+#define ISACX_CDA10            0x40    /* R/W  */
+#define ISACX_CDA11            0x41    /* R/W  */
+#define ISACX_CDA20            0x42    /* R/W  */
+#define ISACX_CDA21            0x43    /* R/W  */
+#define ISACX_CDA_TSDP10       0x44    /* R/W  */
+#define ISACX_CDA_TSDP11       0x45    /* R/W  */
+#define ISACX_CDA_TSDP20       0x46    /* R/W  */
+#define ISACX_CDA_TSDP21       0x47    /* R/W  */
+#define ISACX_BCHA_TSDP_BC1    0x48    /* R/W  */
+#define ISACX_BCHA_TSDP_BC2    0x49    /* R/W  */
+#define ISACX_BCHB_TSDP_BC1    0x4A    /* R/W  */
+#define ISACX_BCHB_TSDP_BC2    0x4B    /* R/W  */
+#define ISACX_TR_TSDP_BC1      0x4C    /* R/W  */
+#define ISACX_TR_TSDP_BC2      0x4D    /* R/W  */
+#define ISACX_CDA1_CR          0x4E    /* R/W  */
+#define ISACX_CDA2_CR          0x4F    /* R/W  */
+
+/* IOM: Contol, Sync transfer, Monitor    */
+#define ISACX_TR_CR            0x50    /* R/W  */
+#define ISACX_TRC_CR           0x50    /* R/W  */
+#define ISACX_BCHA_CR          0x51    /* R/W  */
+#define ISACX_BCHB_CR          0x52    /* R/W  */
+#define ISACX_DCI_CR           0x53    /* R/W  */
+#define ISACX_DCIC_CR          0x53    /* R/W  */
+#define ISACX_MON_CR           0x54    /* R/W  */
+#define ISACX_SDS1_CR          0x55    /* R/W  */
+#define ISACX_SDS2_CR          0x56    /* R/W  */
+#define ISACX_IOM_CR           0x57    /* R/W  */
+#define ISACX_STI              0x58    /* RD   */
+#define ISACX_ASTI             0x58    /* WR   */
+#define ISACX_MSTI             0x59    /* R/W  */
+#define ISACX_SDS_CONF         0x5A    /* R/W  */
+#define ISACX_MCDA             0x5B    /* RD   */
+#define ISACX_MOR              0x5C    /* RD   */
+#define ISACX_MOX              0x5C    /* WR   */
+#define ISACX_MOSR             0x5D    /* RD   */
+#define ISACX_MOCR             0x5E    /* R/W  */
+#define ISACX_MSTA             0x5F    /* RD   */
+#define ISACX_MCONF            0x5F    /* WR   */
+
+/* Interrupt and general registers */
+#define ISACX_ISTA             0x60    /* RD   */
+#define ISACX_MASK             0x60    /* WR   */
+#define ISACX_AUXI             0x61    /* RD   */
+#define ISACX_AUXM             0x61    /* WR   */
+#define ISACX_MODE1            0x62    /* R/W  */
+#define ISACX_MODE2            0x63    /* R/W  */
+#define ISACX_ID               0x64    /* RD   */
+#define ISACX_SRES             0x64    /* WR   */
+#define ISACX_TIMR2            0x65    /* R/W  */
+
+/* Register Bits */
+/* ISACX/IPACX _ISTAD (R) and _MASKD (W) */
+#define ISACX_D_XDU            0x04
+#define ISACX_D_XMR            0x08
+#define ISACX_D_XPR            0x10
+#define ISACX_D_RFO            0x20
+#define ISACX_D_RPF            0x40
+#define ISACX_D_RME            0x80
+
+/* ISACX/IPACX _ISTA (R) and _MASK (W) */
+#define ISACX__ICD             0x01
+#define ISACX__MOS             0x02
+#define ISACX__TRAN            0x04
+#define ISACX__AUX             0x08
+#define ISACX__CIC             0x10
+#define ISACX__ST              0x20
+#define IPACX__ICB             0x40
+#define IPACX__ICA             0x80
+#define IPACX__ON              0x2C
+
+/* ISACX/IPACX _CMDRD (W) */
+#define ISACX_CMDRD_XRES       0x01
+#define ISACX_CMDRD_XME                0x02
+#define ISACX_CMDRD_XTF                0x08
+#define ISACX_CMDRD_STI                0x10
+#define ISACX_CMDRD_RRES       0x40
+#define ISACX_CMDRD_RMC                0x80
+
+/* ISACX/IPACX _RSTAD (R) */
+#define ISACX_RSTAD_TA         0x01
+#define ISACX_RSTAD_CR         0x02
+#define ISACX_RSTAD_SA0                0x04
+#define ISACX_RSTAD_SA1                0x08
+#define ISACX_RSTAD_RAB                0x10
+#define ISACX_RSTAD_CRC                0x20
+#define ISACX_RSTAD_RDO                0x40
+#define ISACX_RSTAD_VFR                0x80
+
+/* ISACX/IPACX _CIR0 (R) */
+#define ISACX_CIR0_BAS         0x01
+#define ISACX_CIR0_SG          0x08
+#define ISACX_CIR0_CIC1                0x08
+#define ISACX_CIR0_CIC0                0x08
+
+/* B-channel registers */
+#define IPACX_OFF_ICA          0x70
+#define IPACX_OFF_ICB          0x80
+
+/* ICA: IPACX_OFF_ICA + Reg ICB: IPACX_OFF_ICB + Reg */
+
+#define IPACX_ISTAB            0x00    /* RD   */
+#define IPACX_MASKB            0x00    /* WR   */
+#define IPACX_STARB            0x01    /* RD   */
+#define IPACX_CMDRB            0x01    /* WR   */
+#define IPACX_MODEB            0x02    /* R/W  */
+#define IPACX_EXMB             0x03    /* R/W  */
+#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    /* R/W  */
+#define IPACX_RFIFOB           0x0A    /* RD   */
+#define IPACX_XFIFOB           0x0A    /* WR   */
+
+/* IPACX_ISTAB / IPACX_MASKB bits */
+#define IPACX_B_XDU            0x04
+#define IPACX_B_XPR            0x10
+#define IPACX_B_RFO            0x20
+#define IPACX_B_RPF            0x40
+#define IPACX_B_RME            0x80
+
+#define IPACX_B_ON             0x0B
+
+extern int mISDNisac_init(struct isac_hw *, void *);
+extern irqreturn_t mISDNisac_irq(struct isac_hw *, u8);
+extern u32 mISDNipac_init(struct ipac_hw *, void *);
+extern irqreturn_t mISDNipac_irq(struct ipac_hw *, int);
diff --git a/drivers/isdn/hardware/mISDN/isar.h b/drivers/isdn/hardware/mISDN/isar.h
new file mode 100644 (file)
index 0000000..092351a
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ *
+ * isar.h   ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "iohelper.h"
+
+struct isar_hw;
+
+struct isar_ch {
+       struct bchannel         bch;
+       struct isar_hw          *is;
+       struct timer_list       ftimer;
+       u8                      nr;
+       u8                      dpath;
+       u8                      mml;
+       u8                      state;
+       u8                      cmd;
+       u8                      mod;
+       u8                      newcmd;
+       u8                      newmod;
+       u8                      try_mod;
+       u8                      conmsg[16];
+};
+
+struct isar_hw {
+       struct  isar_ch ch[2];
+       void            *hw;
+       spinlock_t      *hwlock;        /* lock HW acccess */
+       char            *name;
+       struct module   *owner;
+       read_reg_t      *read_reg;
+       write_reg_t     *write_reg;
+       fifo_func_t     *read_fifo;
+       fifo_func_t     *write_fifo;
+       int             (*ctrl)(void *, u32, u_long);
+       void            (*release)(struct isar_hw *);
+       int             (*init)(struct isar_hw *);
+       int             (*open)(struct isar_hw *, struct channel_req *);
+       int             (*firmware)(struct isar_hw *, const u8 *, int);
+       unsigned long   Flags;
+       int             version;
+       u8              bstat;
+       u8              iis;
+       u8              cmsb;
+       u8              clsb;
+       u8              buf[256];
+       u8              log[256];
+};
+
+#define ISAR_IRQMSK    0x04
+#define ISAR_IRQSTA    0x04
+#define ISAR_IRQBIT    0x75
+#define ISAR_CTRL_H    0x61
+#define ISAR_CTRL_L    0x60
+#define ISAR_IIS       0x58
+#define ISAR_IIA       0x58
+#define ISAR_HIS       0x50
+#define ISAR_HIA       0x50
+#define ISAR_MBOX      0x4c
+#define ISAR_WADR      0x4a
+#define ISAR_RADR      0x48
+
+#define ISAR_HIS_VNR           0x14
+#define ISAR_HIS_DKEY          0x02
+#define ISAR_HIS_FIRM          0x1e
+#define ISAR_HIS_STDSP         0x08
+#define ISAR_HIS_DIAG          0x05
+#define ISAR_HIS_P0CFG         0x3c
+#define ISAR_HIS_P12CFG                0x24
+#define ISAR_HIS_SARTCFG       0x25
+#define ISAR_HIS_PUMPCFG       0x26
+#define ISAR_HIS_PUMPCTRL      0x2a
+#define ISAR_HIS_IOM2CFG       0x27
+#define ISAR_HIS_IOM2REQ       0x07
+#define ISAR_HIS_IOM2CTRL      0x2b
+#define ISAR_HIS_BSTREQ                0x0c
+#define ISAR_HIS_PSTREQ                0x0e
+#define ISAR_HIS_SDATA         0x20
+#define ISAR_HIS_DPS1          0x40
+#define ISAR_HIS_DPS2          0x80
+#define SET_DPS(x)             ((x<<6) & 0xc0)
+
+#define ISAR_IIS_MSCMSD                0x3f
+#define ISAR_IIS_VNR           0x15
+#define ISAR_IIS_DKEY          0x03
+#define ISAR_IIS_FIRM          0x1f
+#define ISAR_IIS_STDSP         0x09
+#define ISAR_IIS_DIAG          0x25
+#define ISAR_IIS_GSTEV         0x00
+#define ISAR_IIS_BSTEV         0x28
+#define ISAR_IIS_BSTRSP                0x2c
+#define ISAR_IIS_PSTRSP                0x2e
+#define ISAR_IIS_PSTEV         0x2a
+#define ISAR_IIS_IOM2RSP       0x27
+#define ISAR_IIS_RDATA         0x20
+#define ISAR_IIS_INVMSG                0x3f
+
+#define ISAR_CTRL_SWVER        0x10
+#define ISAR_CTRL_STST 0x40
+
+#define ISAR_MSG_HWVER 0x20
+
+#define ISAR_DP1_USE   1
+#define ISAR_DP2_USE   2
+#define ISAR_RATE_REQ  3
+
+#define PMOD_DISABLE   0
+#define PMOD_FAX       1
+#define PMOD_DATAMODEM 2
+#define PMOD_HALFDUPLEX        3
+#define PMOD_V110      4
+#define PMOD_DTMF      5
+#define PMOD_DTMF_TRANS        6
+#define PMOD_BYPASS    7
+
+#define PCTRL_ORIG     0x80
+#define PV32P2_V23R    0x40
+#define PV32P2_V22A    0x20
+#define PV32P2_V22B    0x10
+#define PV32P2_V22C    0x08
+#define PV32P2_V21     0x02
+#define PV32P2_BEL     0x01
+
+/* LSB MSB in ISAR doc wrong !!! Arghhh */
+#define PV32P3_AMOD    0x80
+#define PV32P3_V32B    0x02
+#define PV32P3_V23B    0x01
+#define PV32P4_48      0x11
+#define PV32P5_48      0x05
+#define PV32P4_UT48    0x11
+#define PV32P5_UT48    0x0d
+#define PV32P4_96      0x11
+#define PV32P5_96      0x03
+#define PV32P4_UT96    0x11
+#define PV32P5_UT96    0x0f
+#define PV32P4_B96     0x91
+#define PV32P5_B96     0x0b
+#define PV32P4_UTB96   0xd1
+#define PV32P5_UTB96   0x0f
+#define PV32P4_120     0xb1
+#define PV32P5_120     0x09
+#define PV32P4_UT120   0xf1
+#define PV32P5_UT120   0x0f
+#define PV32P4_144     0x99
+#define PV32P5_144     0x09
+#define PV32P4_UT144   0xf9
+#define PV32P5_UT144   0x0f
+#define PV32P6_CTN     0x01
+#define PV32P6_ATN     0x02
+
+#define PFAXP2_CTN     0x01
+#define PFAXP2_ATN     0x04
+
+#define PSEV_10MS_TIMER        0x02
+#define PSEV_CON_ON    0x18
+#define PSEV_CON_OFF   0x19
+#define PSEV_V24_OFF   0x20
+#define PSEV_CTS_ON    0x21
+#define PSEV_CTS_OFF   0x22
+#define PSEV_DCD_ON    0x23
+#define PSEV_DCD_OFF   0x24
+#define PSEV_DSR_ON    0x25
+#define PSEV_DSR_OFF   0x26
+#define PSEV_REM_RET   0xcc
+#define PSEV_REM_REN   0xcd
+#define PSEV_GSTN_CLR  0xd4
+
+#define PSEV_RSP_READY 0xbc
+#define PSEV_LINE_TX_H 0xb3
+#define PSEV_LINE_TX_B 0xb2
+#define PSEV_LINE_RX_H 0xb1
+#define PSEV_LINE_RX_B 0xb0
+#define PSEV_RSP_CONN  0xb5
+#define PSEV_RSP_DISC  0xb7
+#define PSEV_RSP_FCERR 0xb9
+#define PSEV_RSP_SILDET        0xbe
+#define PSEV_RSP_SILOFF        0xab
+#define PSEV_FLAGS_DET 0xba
+
+#define PCTRL_CMD_TDTMF        0x5a
+
+#define PCTRL_CMD_FTH  0xa7
+#define PCTRL_CMD_FRH  0xa5
+#define PCTRL_CMD_FTM  0xa8
+#define PCTRL_CMD_FRM  0xa6
+#define PCTRL_CMD_SILON        0xac
+#define PCTRL_CMD_CONT 0xa2
+#define PCTRL_CMD_ESC  0xa4
+#define PCTRL_CMD_SILOFF 0xab
+#define PCTRL_CMD_HALT 0xa9
+
+#define PCTRL_LOC_RET  0xcf
+#define PCTRL_LOC_REN  0xce
+
+#define SMODE_DISABLE  0
+#define SMODE_V14      2
+#define SMODE_HDLC     3
+#define SMODE_BINARY   4
+#define SMODE_FSK_V14  5
+
+#define SCTRL_HDMC_BOTH        0x00
+#define SCTRL_HDMC_DTX 0x80
+#define SCTRL_HDMC_DRX 0x40
+#define S_P1_OVSP      0x40
+#define S_P1_SNP       0x20
+#define S_P1_EOP       0x10
+#define S_P1_EDP       0x08
+#define S_P1_NSB       0x04
+#define S_P1_CHS_8     0x03
+#define S_P1_CHS_7     0x02
+#define S_P1_CHS_6     0x01
+#define S_P1_CHS_5     0x00
+
+#define S_P2_BFT_DEF   0x10
+
+#define IOM_CTRL_ENA   0x80
+#define IOM_CTRL_NOPCM 0x00
+#define IOM_CTRL_ALAW  0x02
+#define IOM_CTRL_ULAW  0x04
+#define IOM_CTRL_RCV   0x01
+
+#define IOM_P1_TXD     0x10
+
+#define HDLC_FED       0x40
+#define HDLC_FSD       0x20
+#define HDLC_FST       0x20
+#define HDLC_ERROR     0x1c
+#define HDLC_ERR_FAD   0x10
+#define HDLC_ERR_RER   0x08
+#define HDLC_ERR_CER   0x04
+#define SART_NMD       0x01
+
+#define BSTAT_RDM0     0x1
+#define BSTAT_RDM1     0x2
+#define BSTAT_RDM2     0x4
+#define BSTAT_RDM3     0x8
+#define BSTEV_TBO      0x1f
+#define BSTEV_RBO      0x2f
+
+/* FAX State Machine */
+#define STFAX_NULL     0
+#define STFAX_READY    1
+#define STFAX_LINE     2
+#define STFAX_CONT     3
+#define STFAX_ACTIV    4
+#define STFAX_ESCAPE   5
+#define STFAX_SILDET   6
+
+extern u32 mISDNisar_init(struct isar_hw *, void *);
+extern void mISDNisar_irq(struct isar_hw *);
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
new file mode 100644 (file)
index 0000000..62441ba
--- /dev/null
@@ -0,0 +1,1178 @@
+/*
+ * mISDNinfineon.c
+ *             Support for cards based on following Infineon ISDN chipsets
+ *             - ISAC + HSCX
+ *             - IPAC and IPAC-X
+ *             - ISAC-SX + HSCX
+ *
+ * Supported cards:
+ *             - Dialogic Diva 2.0
+ *             - Dialogic Diva 2.0U
+ *             - Dialogic Diva 2.01
+ *             - Dialogic Diva 2.02
+ *             - Sedlbauer Speedwin
+ *             - HST Saphir3
+ *             - Develo (former ELSA) Microlink PCI (Quickstep 1000)
+ *             - Develo (former ELSA) Quickstep 3000
+ *             - Berkom Scitel BRIX Quadro
+ *             - Dr.Neuhaus (Sagem) Niccy
+ *
+ *
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+
+#define INFINEON_REV   "1.0"
+
+static int inf_cnt;
+static u32 debug;
+static u32 irqloops = 4;
+
+enum inf_types {
+       INF_NONE,
+       INF_DIVA20,
+       INF_DIVA20U,
+       INF_DIVA201,
+       INF_DIVA202,
+       INF_SPEEDWIN,
+       INF_SAPHIR3,
+       INF_QS1000,
+       INF_QS3000,
+       INF_NICCY,
+       INF_SCT_1,
+       INF_SCT_2,
+       INF_SCT_3,
+       INF_SCT_4,
+       INF_GAZEL_R685,
+       INF_GAZEL_R753
+};
+
+enum addr_mode {
+       AM_NONE = 0,
+       AM_IO,
+       AM_MEMIO,
+       AM_IND_IO,
+};
+
+struct inf_cinfo {
+       enum inf_types  typ;
+       const char      *full;
+       const char      *name;
+       enum addr_mode  cfg_mode;
+       enum addr_mode  addr_mode;
+       u8              cfg_bar;
+       u8              addr_bar;
+       void            *irqfunc;
+};
+
+struct _ioaddr {
+       enum addr_mode  mode;
+       union {
+               void __iomem    *p;
+               struct _ioport  io;
+       } a;
+};
+
+struct _iohandle {
+       enum addr_mode  mode;
+       resource_size_t size;
+       resource_size_t start;
+       void __iomem    *p;
+};
+
+struct inf_hw {
+       struct list_head        list;
+       struct pci_dev          *pdev;
+       const struct inf_cinfo  *ci;
+       char                    name[MISDN_MAX_IDLEN];
+       u32                     irq;
+       u32                     irqcnt;
+       struct _iohandle        cfg;
+       struct _iohandle        addr;
+       struct _ioaddr          isac;
+       struct _ioaddr          hscx;
+       spinlock_t              lock;   /* HW access lock */
+       struct ipac_hw          ipac;
+       struct inf_hw           *sc[3]; /* slave cards */
+};
+
+
+#define PCI_SUBVENDOR_HST_SAPHIR3       0x52
+#define PCI_SUBVENDOR_SEDLBAUER_PCI     0x53
+#define PCI_SUB_ID_SEDLBAUER            0x01
+
+static struct pci_device_id infineon_ids[] __devinitdata = {
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20},
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20U},
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA201},
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA202,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA202},
+       { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+         PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0,
+         INF_SPEEDWIN},
+       { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+         PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3},
+       { PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS1000},
+       { PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_QS3000,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS3000},
+       { PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_NICCY},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+         PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0,
+         INF_SCT_1},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R685,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R685},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R753,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_OLITEC,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+       { }
+};
+MODULE_DEVICE_TABLE(pci, infineon_ids);
+
+/* PCI interface specific defines */
+/* Diva 2.0/2.0U */
+#define DIVA_HSCX_PORT         0x00
+#define DIVA_HSCX_ALE          0x04
+#define DIVA_ISAC_PORT         0x08
+#define DIVA_ISAC_ALE          0x0C
+#define DIVA_PCI_CTRL           0x10
+
+/* DIVA_PCI_CTRL bits */
+#define DIVA_IRQ_BIT           0x01
+#define DIVA_RESET_BIT         0x08
+#define DIVA_EEPROM_CLK                0x40
+#define DIVA_LED_A             0x10
+#define DIVA_LED_B             0x20
+#define DIVA_IRQ_CLR           0x80
+
+/* Diva 2.01/2.02 */
+/* Siemens PITA */
+#define PITA_ICR_REG           0x00
+#define PITA_INT0_STATUS       0x02
+
+#define PITA_MISC_REG          0x1c
+#define PITA_PARA_SOFTRESET    0x01000000
+#define PITA_SER_SOFTRESET     0x02000000
+#define PITA_PARA_MPX_MODE     0x04000000
+#define PITA_INT0_ENABLE       0x00020000
+
+/* TIGER 100 Registers */
+#define TIGER_RESET_ADDR       0x00
+#define TIGER_EXTERN_RESET     0x01
+#define TIGER_AUX_CTRL         0x02
+#define TIGER_AUX_DATA         0x03
+#define TIGER_AUX_IRQMASK      0x05
+#define TIGER_AUX_STATUS       0x07
+
+/* Tiger AUX BITs */
+#define TIGER_IOMASK           0xdd    /* 1 and 5 are inputs */
+#define TIGER_IRQ_BIT          0x02
+
+#define TIGER_IPAC_ALE         0xC0
+#define TIGER_IPAC_PORT                0xC8
+
+/* ELSA (now Develo) PCI cards */
+#define ELSA_IRQ_ADDR          0x4c
+#define ELSA_IRQ_MASK          0x04
+#define QS1000_IRQ_OFF         0x01
+#define QS3000_IRQ_OFF         0x03
+#define QS1000_IRQ_ON          0x41
+#define QS3000_IRQ_ON          0x43
+
+/* Dr Neuhaus/Sagem Niccy */
+#define NICCY_ISAC_PORT                0x00
+#define NICCY_HSCX_PORT                0x01
+#define NICCY_ISAC_ALE         0x02
+#define NICCY_HSCX_ALE         0x03
+
+#define NICCY_IRQ_CTRL_REG     0x38
+#define NICCY_IRQ_ENABLE       0x001f00
+#define NICCY_IRQ_DISABLE      0xff0000
+#define NICCY_IRQ_BIT          0x800000
+
+
+/* Scitel PLX */
+#define SCT_PLX_IRQ_ADDR       0x4c
+#define SCT_PLX_RESET_ADDR     0x50
+#define SCT_PLX_IRQ_ENABLE     0x41
+#define SCT_PLX_RESET_BIT      0x04
+
+/* Gazel */
+#define        GAZEL_IPAC_DATA_PORT    0x04
+/* Gazel PLX */
+#define GAZEL_CNTRL            0x50
+#define GAZEL_RESET            0x04
+#define GAZEL_RESET_9050       0x40000000
+#define GAZEL_INCSR            0x4C
+#define GAZEL_ISAC_EN          0x08
+#define GAZEL_INT_ISAC         0x20
+#define GAZEL_HSCX_EN          0x01
+#define GAZEL_INT_HSCX         0x04
+#define GAZEL_PCI_EN           0x40
+#define GAZEL_IPAC_EN          0x03
+
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct inf_hw *card)
+{
+       card->ipac.isac.dch.debug = debug;
+       card->ipac.hscx[0].bch.debug = debug;
+       card->ipac.hscx[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+       int ret;
+       struct inf_hw *card;
+
+       ret = param_set_uint(val, kp);
+       if (!ret) {
+               read_lock(&card_lock);
+               list_for_each_entry(card, &Cards, list)
+                       _set_debug(card);
+               read_unlock(&card_lock);
+       }
+       return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(INFINEON_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "infineon debug mask");
+module_param(irqloops, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)");
+
+/* Interface functions */
+
+IOFUNC_IO(ISAC, inf_hw, isac.a.io)
+IOFUNC_IO(IPAC, inf_hw, hscx.a.io)
+IOFUNC_IND(ISAC, inf_hw, isac.a.io)
+IOFUNC_IND(IPAC, inf_hw, hscx.a.io)
+IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p)
+IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p)
+
+static irqreturn_t
+diva_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL);
+       if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva20x_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = readb(hw->cfg.p);
+       if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+tiger_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS);
+       if (val & TIGER_IRQ_BIT) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+elsa_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR);
+       if (!(val & ELSA_IRQ_MASK)) {
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+niccy_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u32 val;
+
+       spin_lock(&hw->lock);
+       val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+       if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+gazel_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       irqreturn_t ret;
+
+       spin_lock(&hw->lock);
+       ret = mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return ret;
+}
+
+static irqreturn_t
+ipac_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = hw->ipac.read_reg(hw, IPAC_ISTA);
+       if (!(val & 0x3f)) {
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static void
+enable_hwirq(struct inf_hw *hw)
+{
+       u16 w;
+       u32 val;
+
+       switch (hw->ci->typ) {
+       case INF_DIVA201:
+       case INF_DIVA202:
+               writel(PITA_INT0_ENABLE, hw->cfg.p);
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
+               break;
+       case INF_QS1000:
+               outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_QS3000:
+               outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_NICCY:
+               val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               val |= NICCY_IRQ_ENABLE;;
+               outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               break;
+       case INF_SCT_1:
+               w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               w |= SCT_PLX_IRQ_ENABLE;
+               outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               break;
+       case INF_GAZEL_R685:
+               outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN,
+                       (u32)hw->cfg.start + GAZEL_INCSR);
+               break;
+       case INF_GAZEL_R753:
+               outb(GAZEL_IPAC_EN + GAZEL_PCI_EN,
+                       (u32)hw->cfg.start + GAZEL_INCSR);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+disable_hwirq(struct inf_hw *hw)
+{
+       u16 w;
+       u32 val;
+
+       switch (hw->ci->typ) {
+       case INF_DIVA201:
+       case INF_DIVA202:
+               writel(0, hw->cfg.p);
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
+               break;
+       case INF_QS1000:
+               outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_QS3000:
+               outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_NICCY:
+               val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               val &= NICCY_IRQ_DISABLE;
+               outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               break;
+       case INF_SCT_1:
+               w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               w &= (~SCT_PLX_IRQ_ENABLE);
+               outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               break;
+       case INF_GAZEL_R685:
+       case INF_GAZEL_R753:
+               outb(0, (u32)hw->cfg.start + GAZEL_INCSR);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+ipac_chip_reset(struct inf_hw *hw)
+{
+       hw->ipac.write_reg(hw, IPAC_POTA2, 0x20);
+       mdelay(5);
+       hw->ipac.write_reg(hw, IPAC_POTA2, 0x00);
+       mdelay(5);
+       hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf);
+       hw->ipac.write_reg(hw, IPAC_MASK, 0xc0);
+}
+
+static void
+reset_inf(struct inf_hw *hw)
+{
+       u16 w;
+       u32 val;
+
+       if (debug & DEBUG_HW)
+               pr_notice("%s: resetting card\n", hw->name);
+       switch (hw->ci->typ) {
+       case INF_DIVA20:
+       case INF_DIVA20U:
+               outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL);
+               mdelay(10);
+               outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL);
+               mdelay(10);
+               /* Workaround PCI9060 */
+               outb(9, (u32)hw->cfg.start + 0x69);
+               outb(DIVA_RESET_BIT | DIVA_LED_A,
+                       (u32)hw->cfg.start + DIVA_PCI_CTRL);
+               break;
+       case INF_DIVA201:
+               writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
+                       hw->cfg.p + PITA_MISC_REG);
+               mdelay(1);
+               writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG);
+               mdelay(10);
+               break;
+       case INF_DIVA202:
+               writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
+                       hw->cfg.p + PITA_MISC_REG);
+               mdelay(1);
+               writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET,
+                       hw->cfg.p + PITA_MISC_REG);
+               mdelay(10);
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               ipac_chip_reset(hw);
+               hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
+               hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
+               hw->ipac.write_reg(hw, IPAC_PCFG, 0x12);
+               break;
+       case INF_QS1000:
+       case INF_QS3000:
+               ipac_chip_reset(hw);
+               hw->ipac.write_reg(hw, IPAC_ACFG, 0x00);
+               hw->ipac.write_reg(hw, IPAC_AOE, 0x3c);
+               hw->ipac.write_reg(hw, IPAC_ATX, 0xff);
+               break;
+       case INF_NICCY:
+               break;
+       case INF_SCT_1:
+               w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               w &= (~SCT_PLX_RESET_BIT);
+               outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               mdelay(10);
+               w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               w |= SCT_PLX_RESET_BIT;
+               outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               mdelay(10);
+               break;
+       case INF_GAZEL_R685:
+               val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
+               val |= (GAZEL_RESET_9050 + GAZEL_RESET);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
+               mdelay(4);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               mdelay(10);
+               hw->ipac.isac.adf2 = 0x87;
+               hw->ipac.hscx[0].slot = 0x1f;
+               hw->ipac.hscx[0].slot = 0x23;
+               break;
+       case INF_GAZEL_R753:
+               val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
+               val |= (GAZEL_RESET_9050 + GAZEL_RESET);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
+               mdelay(4);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               mdelay(10);
+               ipac_chip_reset(hw);
+               hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
+               hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
+               hw->ipac.conf = 0x01; /* IOM off */
+               break;
+       default:
+               return;
+       }
+       enable_hwirq(hw);
+}
+
+static int
+inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       case HW_RESET_REQ:
+               reset_inf(hw);
+               break;
+       default:
+               pr_info("%s: %s unknown command %x %lx\n",
+                       hw->name, __func__, cmd, arg);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int __devinit
+init_irq(struct inf_hw *hw)
+{
+       int     ret, cnt = 3;
+       u_long  flags;
+
+       if (!hw->ci->irqfunc)
+               return -EINVAL;
+       ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw);
+       if (ret) {
+               pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq);
+               return ret;
+       }
+       while (cnt--) {
+               spin_lock_irqsave(&hw->lock, flags);
+               reset_inf(hw);
+               ret = hw->ipac.init(&hw->ipac);
+               if (ret) {
+                       spin_unlock_irqrestore(&hw->lock, flags);
+                       pr_info("%s: ISAC init failed with %d\n",
+                               hw->name, ret);
+                       break;
+               }
+               spin_unlock_irqrestore(&hw->lock, flags);
+               msleep_interruptible(10);
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IRQ %d count %d\n", hw->name,
+                               hw->irq, hw->irqcnt);
+               if (!hw->irqcnt) {
+                       pr_info("%s: IRQ(%d) got no requests during init %d\n",
+                               hw->name, hw->irq, 3 - cnt);
+               } else
+                       return 0;
+       }
+       free_irq(hw->irq, hw);
+       return -EIO;
+}
+
+static void
+release_io(struct inf_hw *hw)
+{
+       if (hw->cfg.mode) {
+               if (hw->cfg.p) {
+                       release_mem_region(hw->cfg.start, hw->cfg.size);
+                       iounmap(hw->cfg.p);
+               } else
+                       release_region(hw->cfg.start, hw->cfg.size);
+               hw->cfg.mode = AM_NONE;
+       }
+       if (hw->addr.mode) {
+               if (hw->addr.p) {
+                       release_mem_region(hw->addr.start, hw->addr.size);
+                       iounmap(hw->addr.p);
+               } else
+                       release_region(hw->addr.start, hw->addr.size);
+               hw->addr.mode = AM_NONE;
+       }
+}
+
+static int __devinit
+setup_io(struct inf_hw *hw)
+{
+       int err = 0;
+
+       if (hw->ci->cfg_mode) {
+               hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar);
+               hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar);
+               if (hw->ci->cfg_mode == AM_MEMIO) {
+                       if (!request_mem_region(hw->cfg.start, hw->cfg.size,
+                           hw->name))
+                               err = -EBUSY;
+               } else {
+                       if (!request_region(hw->cfg.start, hw->cfg.size,
+                           hw->name))
+                               err = -EBUSY;
+               }
+               if (err) {
+                       pr_info("mISDN: %s config port %lx (%lu bytes)"
+                               "already in use\n", hw->name,
+                               (ulong)hw->cfg.start, (ulong)hw->cfg.size);
+                       return err;
+               }
+               if (hw->ci->cfg_mode == AM_MEMIO)
+                       hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size);
+               hw->cfg.mode = hw->ci->cfg_mode;
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n",
+                               hw->name, (ulong)hw->cfg.start,
+                               (ulong)hw->cfg.size, hw->ci->cfg_mode);
+
+       }
+       if (hw->ci->addr_mode) {
+               hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar);
+               hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar);
+               if (hw->ci->addr_mode == AM_MEMIO) {
+                       if (!request_mem_region(hw->addr.start, hw->addr.size,
+                           hw->name))
+                               err = -EBUSY;
+               } else {
+                       if (!request_region(hw->addr.start, hw->addr.size,
+                           hw->name))
+                               err = -EBUSY;
+               }
+               if (err) {
+                       pr_info("mISDN: %s address port %lx (%lu bytes)"
+                               "already in use\n", hw->name,
+                               (ulong)hw->addr.start, (ulong)hw->addr.size);
+                       return err;
+               }
+               if (hw->ci->addr_mode == AM_MEMIO)
+                       hw->addr.p = ioremap(hw->addr.start, hw->addr.size);
+               hw->addr.mode = hw->ci->addr_mode;
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n",
+                               hw->name, (ulong)hw->addr.start,
+                               (ulong)hw->addr.size, hw->ci->addr_mode);
+
+       }
+
+       switch (hw->ci->typ) {
+       case INF_DIVA20:
+       case INF_DIVA20U:
+               hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+               hw->isac.mode = hw->cfg.mode;
+               hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE;
+               hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT;
+               hw->hscx.mode = hw->cfg.mode;
+               hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE;
+               hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT;
+               break;
+       case INF_DIVA201:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.p = hw->addr.p;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.p = hw->addr.p;
+               break;
+       case INF_DIVA202:
+               hw->ipac.type = IPAC_TYPE_IPACX;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.p = hw->addr.p;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.p = hw->addr.p;
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->cfg.mode;
+               hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
+               hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
+               hw->hscx.mode = hw->cfg.mode;
+               hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
+               hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
+               outb(0xff, (ulong)hw->cfg.start);
+               mdelay(1);
+               outb(0x00, (ulong)hw->cfg.start);
+               mdelay(1);
+               outb(TIGER_IOMASK, (ulong)hw->cfg.start + TIGER_AUX_CTRL);
+               break;
+       case INF_QS1000:
+       case INF_QS3000:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start;
+               hw->isac.a.io.port = (u32)hw->addr.start + 1;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = (u32)hw->addr.start;
+               hw->hscx.a.io.port = (u32)hw->addr.start + 1;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_NICCY:
+               hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE;
+               hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE;
+               hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT;
+               break;
+       case INF_SCT_1:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_SCT_2:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start + 0x08;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_SCT_3:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start + 0x10;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_SCT_4:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start + 0x20;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_GAZEL_R685:
+               hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.io.port = (u32)hw->addr.start;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               break;
+       case INF_GAZEL_R753:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.io.ale = (u32)hw->addr.start;
+               hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               break;
+       default:
+               return -EINVAL;
+       }
+       switch (hw->isac.mode) {
+       case AM_MEMIO:
+               ASSIGN_FUNC_IPAC(MIO, hw->ipac);
+               break;
+       case AM_IND_IO:
+               ASSIGN_FUNC_IPAC(IND, hw->ipac);
+               break;
+       case AM_IO:
+               ASSIGN_FUNC_IPAC(IO, hw->ipac);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void
+release_card(struct inf_hw *card) {
+       ulong   flags;
+       int     i;
+
+       spin_lock_irqsave(&card->lock, flags);
+       disable_hwirq(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+       card->ipac.isac.release(&card->ipac.isac);
+       free_irq(card->irq, card);
+       mISDN_unregister_device(&card->ipac.isac.dch.dev);
+       release_io(card);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       switch (card->ci->typ) {
+       case INF_SCT_2:
+       case INF_SCT_3:
+       case INF_SCT_4:
+               break;
+       case INF_SCT_1:
+               for (i = 0; i < 3; i++) {
+                       if (card->sc[i])
+                               release_card(card->sc[i]);
+                       card->sc[i] = NULL;
+               }
+       default:
+               pci_disable_device(card->pdev);
+               pci_set_drvdata(card->pdev, NULL);
+               break;
+       }
+       kfree(card);
+       inf_cnt--;
+}
+
+static int __devinit
+setup_instance(struct inf_hw *card)
+{
+       int err;
+       ulong flags;
+
+       snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name,
+               inf_cnt + 1);
+       write_lock_irqsave(&card_lock, flags);
+       list_add_tail(&card->list, &Cards);
+       write_unlock_irqrestore(&card_lock, flags);
+
+       _set_debug(card);
+       card->ipac.isac.name = card->name;
+       card->ipac.name = card->name;
+       card->ipac.owner = THIS_MODULE;
+       spin_lock_init(&card->lock);
+       card->ipac.isac.hwlock = &card->lock;
+       card->ipac.hwlock = &card->lock;
+       card->ipac.ctrl = (void *)&inf_ctrl;
+
+       err = setup_io(card);
+       if (err)
+               goto error_setup;
+
+       card->ipac.isac.dch.dev.Bprotocols =
+               mISDNipac_init(&card->ipac, card);
+
+       if (card->ipac.isac.dch.dev.Bprotocols == 0)
+               goto error_setup;;
+
+       err = mISDN_register_device(&card->ipac.isac.dch.dev,
+               &card->pdev->dev, card->name);
+       if (err)
+               goto error;
+
+       err = init_irq(card);
+       if (!err)  {
+               inf_cnt++;
+               pr_notice("Infineon %d cards installed\n", inf_cnt);
+               return 0;
+       }
+       mISDN_unregister_device(&card->ipac.isac.dch.dev);
+error:
+       card->ipac.release(&card->ipac);
+error_setup:
+       release_io(card);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       return err;
+}
+
+static const struct inf_cinfo inf_card_info[] = {
+       {
+               INF_DIVA20,
+               "Dialogic Diva 2.0",
+               "diva20",
+               AM_IND_IO, AM_NONE, 2, 0,
+               &diva_irq
+       },
+       {
+               INF_DIVA20U,
+               "Dialogic Diva 2.0U",
+               "diva20U",
+               AM_IND_IO, AM_NONE, 2, 0,
+               &diva_irq
+       },
+       {
+               INF_DIVA201,
+               "Dialogic Diva 2.01",
+               "diva201",
+               AM_MEMIO, AM_MEMIO, 0, 1,
+               &diva20x_irq
+       },
+       {
+               INF_DIVA202,
+               "Dialogic Diva 2.02",
+               "diva202",
+               AM_MEMIO, AM_MEMIO, 0, 1,
+               &diva20x_irq
+       },
+       {
+               INF_SPEEDWIN,
+               "Sedlbauer SpeedWin PCI",
+               "speedwin",
+               AM_IND_IO, AM_NONE, 0, 0,
+               &tiger_irq
+       },
+       {
+               INF_SAPHIR3,
+               "HST Saphir 3",
+               "saphir",
+               AM_IND_IO, AM_NONE, 0, 0,
+               &tiger_irq
+       },
+       {
+               INF_QS1000,
+               "Develo Microlink PCI",
+               "qs1000",
+               AM_IO, AM_IND_IO, 1, 3,
+               &elsa_irq
+       },
+       {
+               INF_QS3000,
+               "Develo QuickStep 3000",
+               "qs3000",
+               AM_IO, AM_IND_IO, 1, 3,
+               &elsa_irq
+       },
+       {
+               INF_NICCY,
+               "Sagem NICCY",
+               "niccy",
+               AM_IO, AM_IND_IO, 0, 1,
+               &niccy_irq
+       },
+       {
+               INF_SCT_1,
+               "SciTel Quadro",
+               "p1_scitel",
+               AM_IO, AM_IND_IO, 1, 5,
+               &ipac_irq
+       },
+       {
+               INF_SCT_2,
+               "SciTel Quadro",
+               "p2_scitel",
+               AM_NONE, AM_IND_IO, 0, 4,
+               &ipac_irq
+       },
+       {
+               INF_SCT_3,
+               "SciTel Quadro",
+               "p3_scitel",
+               AM_NONE, AM_IND_IO, 0, 3,
+               &ipac_irq
+       },
+       {
+               INF_SCT_4,
+               "SciTel Quadro",
+               "p4_scitel",
+               AM_NONE, AM_IND_IO, 0, 2,
+               &ipac_irq
+       },
+       {
+               INF_GAZEL_R685,
+               "Gazel R685",
+               "gazel685",
+               AM_IO, AM_IO, 1, 2,
+               &gazel_irq
+       },
+       {
+               INF_GAZEL_R753,
+               "Gazel R753",
+               "gazel753",
+               AM_IO, AM_IND_IO, 1, 2,
+               &ipac_irq
+       },
+       {
+               INF_NONE,
+       }
+};
+
+static const struct inf_cinfo * __devinit
+get_card_info(enum inf_types typ)
+{
+       const struct inf_cinfo *ci = inf_card_info;
+
+       while (ci->typ != INF_NONE) {
+               if (ci->typ == typ)
+                       return ci;
+               ci++;
+       }
+       return NULL;
+}
+
+static int __devinit
+inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int err = -ENOMEM;
+       struct inf_hw *card;
+
+       card = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
+       if (!card) {
+               pr_info("No memory for Infineon ISDN card\n");
+               return err;
+       }
+       card->pdev = pdev;
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+       card->ci = get_card_info(ent->driver_data);
+       if (!card->ci) {
+               pr_info("mISDN: do not have informations about adapter at %s\n",
+                       pci_name(pdev));
+               kfree(card);
+               return -EINVAL;
+       } else
+               pr_notice("mISDN: found adapter %s at %s\n",
+                       card->ci->full, pci_name(pdev));
+
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_instance(card);
+       if (err) {
+               pci_disable_device(card->pdev);
+               kfree(card);
+               pci_set_drvdata(pdev, NULL);
+       } else if (ent->driver_data == INF_SCT_1) {
+               int i;
+               struct inf_hw *sc;
+
+               for (i = 1; i < 4; i++) {
+                       sc = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
+                       if (!sc) {
+                               release_card(card);
+                               return -ENOMEM;
+                       }
+                       sc->irq = card->irq;
+                       sc->pdev = card->pdev;
+                       sc->ci = card->ci + i;
+                       err = setup_instance(sc);
+                       if (err) {
+                               kfree(sc);
+                               release_card(card);
+                       } else
+                               card->sc[i - 1] = sc;
+               }
+       }
+       return err;
+}
+
+static void __devexit
+inf_remove(struct pci_dev *pdev)
+{
+       struct inf_hw   *card = pci_get_drvdata(pdev);
+
+       if (card)
+               release_card(card);
+       else
+               pr_debug("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_driver infineon_driver = {
+       .name = "ISDN Infineon pci",
+       .probe = inf_probe,
+       .remove = __devexit_p(inf_remove),
+       .id_table = infineon_ids,
+};
+
+static int __init
+infineon_init(void)
+{
+       int err;
+
+       pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV);
+       err = pci_register_driver(&infineon_driver);
+       return err;
+}
+
+static void __exit
+infineon_cleanup(void)
+{
+       pci_unregister_driver(&infineon_driver);
+}
+
+module_init(infineon_init);
+module_exit(infineon_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c
new file mode 100644 (file)
index 0000000..613ba04
--- /dev/null
@@ -0,0 +1,1655 @@
+/*
+ * isac.c   ISAC specific routines
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+
+
+#define DBUSY_TIMER_VALUE      80
+#define ARCOFI_USE             1
+
+#define ISAC_REV               "2.0"
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_VERSION(ISAC_REV);
+MODULE_LICENSE("GPL v2");
+
+#define ReadISAC(is, o)                (is->read_reg(is->dch.hw, o + is->off))
+#define        WriteISAC(is, o, v)     (is->write_reg(is->dch.hw, o + is->off, v))
+#define ReadHSCX(h, o)         (h->ip->read_reg(h->ip->hw, h->off + o))
+#define WriteHSCX(h, o, v)     (h->ip->write_reg(h->ip->hw, h->off + o, v))
+#define ReadIPAC(ip, o)                (ip->read_reg(ip->hw, o))
+#define WriteIPAC(ip, o, v)    (ip->write_reg(ip->hw, o, v))
+
+static inline void
+ph_command(struct isac_hw *isac, u8 command)
+{
+       pr_debug("%s: ph_command %x\n", isac->name, command);
+       if (isac->type & IPAC_TYPE_ISACX)
+               WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE);
+       else
+               WriteISAC(isac, ISAC_CIX0, (command << 2) | 3);
+}
+
+static void
+isac_ph_state_change(struct isac_hw *isac)
+{
+       switch (isac->state) {
+       case (ISAC_IND_RS):
+       case (ISAC_IND_EI):
+               ph_command(isac, ISAC_CMD_DUI);
+       }
+       schedule_event(&isac->dch, FLG_PHCHANGE);
+}
+
+static void
+isac_ph_state_bh(struct dchannel *dch)
+{
+       struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+
+       switch (isac->state) {
+       case ISAC_IND_RS:
+       case ISAC_IND_EI:
+               dch->state = 0;
+               l1_event(dch->l1, HW_RESET_IND);
+               break;
+       case ISAC_IND_DID:
+               dch->state = 3;
+               l1_event(dch->l1, HW_DEACT_CNF);
+               break;
+       case ISAC_IND_DR:
+               dch->state = 3;
+               l1_event(dch->l1, HW_DEACT_IND);
+               break;
+       case ISAC_IND_PU:
+               dch->state = 4;
+               l1_event(dch->l1, HW_POWERUP_IND);
+               break;
+       case ISAC_IND_RSY:
+               if (dch->state <= 5) {
+                       dch->state = 5;
+                       l1_event(dch->l1, ANYSIGNAL);
+               } else {
+                       dch->state = 8;
+                       l1_event(dch->l1, LOSTFRAMING);
+               }
+               break;
+       case ISAC_IND_ARD:
+               dch->state = 6;
+               l1_event(dch->l1, INFO2);
+               break;
+       case ISAC_IND_AI8:
+               dch->state = 7;
+               l1_event(dch->l1, INFO4_P8);
+               break;
+       case ISAC_IND_AI10:
+               dch->state = 7;
+               l1_event(dch->l1, INFO4_P10);
+               break;
+       }
+       pr_debug("%s: TE newstate %x\n", isac->name, dch->state);
+}
+
+void
+isac_empty_fifo(struct isac_hw *isac, int count)
+{
+       u8 *ptr;
+
+       pr_debug("%s: %s  %d\n", isac->name, __func__, count);
+
+       if (!isac->dch.rx_skb) {
+               isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC);
+               if (!isac->dch.rx_skb) {
+                       pr_info("%s: D receive out of memory\n", isac->name);
+                       WriteISAC(isac, ISAC_CMDR, 0x80);
+                       return;
+               }
+       }
+       if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) {
+               pr_debug("%s: %s overrun %d\n", isac->name, __func__,
+                           isac->dch.rx_skb->len + count);
+               WriteISAC(isac, ISAC_CMDR, 0x80);
+               return;
+       }
+       ptr = skb_put(isac->dch.rx_skb, count);
+       isac->read_fifo(isac->dch.hw, isac->off, ptr, count);
+       WriteISAC(isac, ISAC_CMDR, 0x80);
+       if (isac->dch.debug & DEBUG_HW_DFIFO) {
+               char    pfx[MISDN_MAX_IDLEN + 16];
+
+               snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ",
+                       isac->name, count);
+               print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static void
+isac_fill_fifo(struct isac_hw *isac)
+{
+       int count, more;
+       u8 *ptr;
+
+       if (!isac->dch.tx_skb)
+               return;
+       count = isac->dch.tx_skb->len - isac->dch.tx_idx;
+       if (count <= 0)
+               return;
+
+       more = 0;
+       if (count > 32) {
+               more = !0;
+               count = 32;
+       }
+       pr_debug("%s: %s  %d\n", isac->name, __func__, count);
+       ptr = isac->dch.tx_skb->data + isac->dch.tx_idx;
+       isac->dch.tx_idx += count;
+       isac->write_fifo(isac->dch.hw, isac->off, ptr, count);
+       WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa);
+       if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
+               pr_debug("%s: %s dbusytimer running\n", isac->name, __func__);
+               del_timer(&isac->dch.timer);
+       }
+       init_timer(&isac->dch.timer);
+       isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+       add_timer(&isac->dch.timer);
+       if (isac->dch.debug & DEBUG_HW_DFIFO) {
+               char    pfx[MISDN_MAX_IDLEN + 16];
+
+               snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ",
+                       isac->name, count);
+               print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static void
+isac_rme_irq(struct isac_hw *isac)
+{
+       u8 val, count;
+
+       val = ReadISAC(isac, ISAC_RSTA);
+       if ((val & 0x70) != 0x20) {
+               if (val & 0x40) {
+                       pr_debug("%s: ISAC RDO\n", isac->name);
+#ifdef ERROR_STATISTIC
+                       isac->dch.err_rx++;
+#endif
+               }
+               if (!(val & 0x20)) {
+                       pr_debug("%s: ISAC CRC error\n", isac->name);
+#ifdef ERROR_STATISTIC
+                       isac->dch.err_crc++;
+#endif
+               }
+               WriteISAC(isac, ISAC_CMDR, 0x80);
+               if (isac->dch.rx_skb)
+                       dev_kfree_skb(isac->dch.rx_skb);
+               isac->dch.rx_skb = NULL;
+       } else {
+               count = ReadISAC(isac, ISAC_RBCL) & 0x1f;
+               if (count == 0)
+                       count = 32;
+               isac_empty_fifo(isac, count);
+               recv_Dchannel(&isac->dch);
+       }
+}
+
+static void
+isac_xpr_irq(struct isac_hw *isac)
+{
+       if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
+               del_timer(&isac->dch.timer);
+       if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) {
+               isac_fill_fifo(isac);
+       } else {
+               if (isac->dch.tx_skb)
+                       dev_kfree_skb(isac->dch.tx_skb);
+               if (get_next_dframe(&isac->dch))
+                       isac_fill_fifo(isac);
+       }
+}
+
+static void
+isac_retransmit(struct isac_hw *isac)
+{
+       if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
+               del_timer(&isac->dch.timer);
+       if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) {
+               /* Restart frame */
+               isac->dch.tx_idx = 0;
+               isac_fill_fifo(isac);
+       } else if (isac->dch.tx_skb) { /* should not happen */
+               pr_info("%s: tx_skb exist but not busy\n", isac->name);
+               test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags);
+               isac->dch.tx_idx = 0;
+               isac_fill_fifo(isac);
+       } else {
+               pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name);
+               if (get_next_dframe(&isac->dch))
+                       isac_fill_fifo(isac);
+       }
+}
+
+static void
+isac_mos_irq(struct isac_hw *isac)
+{
+       u8 val;
+       int ret;
+
+       val = ReadISAC(isac, ISAC_MOSR);
+       pr_debug("%s: ISAC MOSR %02x\n", isac->name, val);
+#if ARCOFI_USE
+       if (val & 0x08) {
+               if (!isac->mon_rx) {
+                       isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
+                       if (!isac->mon_rx) {
+                               pr_info("%s: ISAC MON RX out of memory!\n",
+                                       isac->name);
+                               isac->mocr &= 0xf0;
+                               isac->mocr |= 0x0a;
+                               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                               goto afterMONR0;
+                       } else
+                               isac->mon_rxp = 0;
+               }
+               if (isac->mon_rxp >= MAX_MON_FRAME) {
+                       isac->mocr &= 0xf0;
+                       isac->mocr |= 0x0a;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mon_rxp = 0;
+                       pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
+                       goto afterMONR0;
+               }
+               isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0);
+               pr_debug("%s: ISAC MOR0 %02x\n", isac->name,
+                       isac->mon_rx[isac->mon_rxp - 1]);
+               if (isac->mon_rxp == 1) {
+                       isac->mocr |= 0x04;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               }
+       }
+afterMONR0:
+       if (val & 0x80) {
+               if (!isac->mon_rx) {
+                       isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
+                       if (!isac->mon_rx) {
+                               pr_info("%s: ISAC MON RX out of memory!\n",
+                                       isac->name);
+                               isac->mocr &= 0x0f;
+                               isac->mocr |= 0xa0;
+                               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                               goto afterMONR1;
+                       } else
+                               isac->mon_rxp = 0;
+               }
+               if (isac->mon_rxp >= MAX_MON_FRAME) {
+                       isac->mocr &= 0x0f;
+                       isac->mocr |= 0xa0;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mon_rxp = 0;
+                       pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
+                       goto afterMONR1;
+               }
+               isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1);
+               pr_debug("%s: ISAC MOR1 %02x\n", isac->name,
+                       isac->mon_rx[isac->mon_rxp - 1]);
+               isac->mocr |= 0x40;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+       }
+afterMONR1:
+       if (val & 0x04) {
+               isac->mocr &= 0xf0;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               isac->mocr |= 0x0a;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               if (isac->monitor) {
+                       ret = isac->monitor(isac->dch.hw, MONITOR_RX_0,
+                               isac->mon_rx, isac->mon_rxp);
+                       if (ret)
+                               kfree(isac->mon_rx);
+               } else {
+                       pr_info("%s: MONITOR 0 received %d but no user\n",
+                               isac->name, isac->mon_rxp);
+                       kfree(isac->mon_rx);
+               }
+               isac->mon_rx = NULL;
+               isac->mon_rxp = 0;
+       }
+       if (val & 0x40) {
+               isac->mocr &= 0x0f;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               isac->mocr |= 0xa0;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               if (isac->monitor) {
+                       ret = isac->monitor(isac->dch.hw, MONITOR_RX_1,
+                               isac->mon_rx, isac->mon_rxp);
+                       if (ret)
+                               kfree(isac->mon_rx);
+               } else {
+                       pr_info("%s: MONITOR 1 received %d but no user\n",
+                               isac->name, isac->mon_rxp);
+                       kfree(isac->mon_rx);
+               }
+               isac->mon_rx = NULL;
+               isac->mon_rxp = 0;
+       }
+       if (val & 0x02) {
+               if ((!isac->mon_tx) || (isac->mon_txc &&
+                       (isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {
+                       isac->mocr &= 0xf0;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mocr |= 0x0a;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                               if (isac->monitor)
+                                       ret = isac->monitor(isac->dch.hw,
+                                               MONITOR_TX_0, NULL, 0);
+                       }
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX0;
+               }
+               if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                       if (isac->monitor)
+                               ret = isac->monitor(isac->dch.hw,
+                                       MONITOR_TX_0, NULL, 0);
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX0;
+               }
+               WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]);
+               pr_debug("%s: ISAC %02x -> MOX0\n", isac->name,
+                       isac->mon_tx[isac->mon_txp - 1]);
+       }
+AfterMOX0:
+       if (val & 0x20) {
+               if ((!isac->mon_tx) || (isac->mon_txc &&
+                       (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {
+                       isac->mocr &= 0x0f;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mocr |= 0xa0;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                               if (isac->monitor)
+                                       ret = isac->monitor(isac->dch.hw,
+                                               MONITOR_TX_1, NULL, 0);
+                       }
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX1;
+               }
+               if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                       if (isac->monitor)
+                               ret = isac->monitor(isac->dch.hw,
+                                       MONITOR_TX_1, NULL, 0);
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX1;
+               }
+               WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
+               pr_debug("%s: ISAC %02x -> MOX1\n", isac->name,
+                       isac->mon_tx[isac->mon_txp - 1]);
+       }
+AfterMOX1:
+       val = 0; /* dummy to avoid warning */
+#endif
+}
+
+static void
+isac_cisq_irq(struct isac_hw *isac) {
+       u8 val;
+
+       val = ReadISAC(isac, ISAC_CIR0);
+       pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val);
+       if (val & 2) {
+               pr_debug("%s: ph_state change %x->%x\n", isac->name,
+                       isac->state, (val >> 2) & 0xf);
+               isac->state = (val >> 2) & 0xf;
+               isac_ph_state_change(isac);
+       }
+       if (val & 1) {
+               val = ReadISAC(isac, ISAC_CIR1);
+               pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val);
+       }
+}
+
+static void
+isacsx_cic_irq(struct isac_hw *isac)
+{
+       u8 val;
+
+       val = ReadISAC(isac, ISACX_CIR0);
+       pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
+       if (val & ISACX_CIR0_CIC0) {
+               pr_debug("%s: ph_state change %x->%x\n", isac->name,
+                       isac->state, val >> 4);
+               isac->state = val >> 4;
+               isac_ph_state_change(isac);
+       }
+}
+
+static void
+isacsx_rme_irq(struct isac_hw *isac)
+{
+       int count;
+       u8 val;
+
+       val = ReadISAC(isac, ISACX_RSTAD);
+       if ((val & (ISACX_RSTAD_VFR |
+                   ISACX_RSTAD_RDO |
+                   ISACX_RSTAD_CRC |
+                   ISACX_RSTAD_RAB))
+           != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) {
+               pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val);
+#ifdef ERROR_STATISTIC
+               if (val & ISACX_RSTAD_CRC)
+                       isac->dch.err_rx++;
+               else
+                       isac->dch.err_crc++;
+#endif
+               WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
+               if (isac->dch.rx_skb)
+                       dev_kfree_skb(isac->dch.rx_skb);
+               isac->dch.rx_skb = NULL;
+       } else {
+               count = ReadISAC(isac, ISACX_RBCLD) & 0x1f;
+               if (count == 0)
+                       count = 32;
+               isac_empty_fifo(isac, count);
+               if (isac->dch.rx_skb) {
+                       skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1);
+                       pr_debug("%s: dchannel received %d\n", isac->name,
+                               isac->dch.rx_skb->len);
+                       recv_Dchannel(&isac->dch);
+               }
+       }
+}
+
+irqreturn_t
+mISDNisac_irq(struct isac_hw *isac, u8 val)
+{
+       if (unlikely(!val))
+               return IRQ_NONE;
+       pr_debug("%s: ISAC interrupt %02x\n", isac->name, val);
+       if (isac->type & IPAC_TYPE_ISACX) {
+               if (val & ISACX__CIC)
+                       isacsx_cic_irq(isac);
+               if (val & ISACX__ICD) {
+                       val = ReadISAC(isac, ISACX_ISTAD);
+                       pr_debug("%s: ISTAD %02x\n", isac->name, val);
+                       if (val & ISACX_D_XDU) {
+                               pr_debug("%s: ISAC XDU\n", isac->name);
+#ifdef ERROR_STATISTIC
+                               isac->dch.err_tx++;
+#endif
+                               isac_retransmit(isac);
+                       }
+                       if (val & ISACX_D_XMR) {
+                               pr_debug("%s: ISAC XMR\n", isac->name);
+#ifdef ERROR_STATISTIC
+                               isac->dch.err_tx++;
+#endif
+                               isac_retransmit(isac);
+                       }
+                       if (val & ISACX_D_XPR)
+                               isac_xpr_irq(isac);
+                       if (val & ISACX_D_RFO) {
+                               pr_debug("%s: ISAC RFO\n", isac->name);
+                               WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
+                       }
+                       if (val & ISACX_D_RME)
+                               isacsx_rme_irq(isac);
+                       if (val & ISACX_D_RPF)
+                               isac_empty_fifo(isac, 0x20);
+               }
+       } else {
+               if (val & 0x80) /* RME */
+                       isac_rme_irq(isac);
+               if (val & 0x40) /* RPF */
+                       isac_empty_fifo(isac, 32);
+               if (val & 0x10) /* XPR */
+                       isac_xpr_irq(isac);
+               if (val & 0x04) /* CISQ */
+                       isac_cisq_irq(isac);
+               if (val & 0x20) /* RSC - never */
+                       pr_debug("%s: ISAC RSC interrupt\n", isac->name);
+               if (val & 0x02) /* SIN - never */
+                       pr_debug("%s: ISAC SIN interrupt\n", isac->name);
+               if (val & 0x01) {       /* EXI */
+                       val = ReadISAC(isac, ISAC_EXIR);
+                       pr_debug("%s: ISAC EXIR %02x\n", isac->name, val);
+                       if (val & 0x80) /* XMR */
+                               pr_debug("%s: ISAC XMR\n", isac->name);
+                       if (val & 0x40) { /* XDU */
+                               pr_debug("%s: ISAC XDU\n", isac->name);
+#ifdef ERROR_STATISTIC
+                               isac->dch.err_tx++;
+#endif
+                               isac_retransmit(isac);
+                       }
+                       if (val & 0x04) /* MOS */
+                               isac_mos_irq(isac);
+               }
+       }
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(mISDNisac_irq);
+
+static int
+isac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct isac_hw          *isac = container_of(dch, struct isac_hw, dch);
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       u32                     id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ret = dchannel_senddata(dch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       isac_fill_fifo(isac);
+                       ret = 0;
+                       spin_unlock_irqrestore(isac->hwlock, flags);
+                       queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(isac->hwlock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               ret = l1_event(dch->l1, hh->prim);
+               break;
+       case PH_DEACTIVATE_REQ:
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               ret = l1_event(dch->l1, hh->prim);
+               break;
+       }
+
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+isac_ctrl(struct isac_hw *isac, u32 cmd, u_long para)
+{
+       u8 tl = 0;
+       u_long flags;
+
+       switch (cmd) {
+       case HW_TESTLOOP:
+               spin_lock_irqsave(isac->hwlock, flags);
+               if (!(isac->type & IPAC_TYPE_ISACX)) {
+                       /* TODO: implement for IPAC_TYPE_ISACX */
+                       if (para & 1) /* B1 */
+                               tl |= 0x0c;
+                       else if (para & 2) /* B2 */
+                               tl |= 0x3;
+                       /* we only support IOM2 mode */
+                       WriteISAC(isac, ISAC_SPCR, tl);
+                       if (tl)
+                               WriteISAC(isac, ISAC_ADF1, 0x8);
+                       else
+                               WriteISAC(isac, ISAC_ADF1, 0x0);
+               }
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       default:
+               pr_debug("%s: %s unknown command %x %lx\n", isac->name,
+                       __func__, cmd, para);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+isac_l1cmd(struct dchannel *dch, u32 cmd)
+{
+       struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+       u_long flags;
+
+       pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state);
+       switch (cmd) {
+       case INFO3_P8:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ph_command(isac, ISAC_CMD_AR8);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case INFO3_P10:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ph_command(isac, ISAC_CMD_AR10);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case HW_RESET_REQ:
+               spin_lock_irqsave(isac->hwlock, flags);
+               if ((isac->state == ISAC_IND_EI) ||
+                   (isac->state == ISAC_IND_DR) ||
+                   (isac->state == ISAC_IND_RS))
+                       ph_command(isac, ISAC_CMD_TIM);
+               else
+                       ph_command(isac, ISAC_CMD_RS);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case HW_DEACT_REQ:
+               skb_queue_purge(&dch->squeue);
+               if (dch->tx_skb) {
+                       dev_kfree_skb(dch->tx_skb);
+                       dch->tx_skb = NULL;
+               }
+               dch->tx_idx = 0;
+               if (dch->rx_skb) {
+                       dev_kfree_skb(dch->rx_skb);
+                       dch->rx_skb = NULL;
+               }
+               test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                       del_timer(&dch->timer);
+               break;
+       case HW_POWERUP_REQ:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ph_command(isac, ISAC_CMD_TIM);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       default:
+               pr_debug("%s: %s unknown command %x\n", isac->name,
+                       __func__, cmd);
+               return -1;
+       }
+       return 0;
+}
+
+static void
+isac_release(struct isac_hw *isac)
+{
+       if (isac->type & IPAC_TYPE_ISACX)
+               WriteISAC(isac, ISACX_MASK, 0xff);
+       else
+               WriteISAC(isac, ISAC_MASK, 0xff);
+       if (isac->dch.timer.function != NULL) {
+               del_timer(&isac->dch.timer);
+               isac->dch.timer.function = NULL;
+       }
+       kfree(isac->mon_rx);
+       isac->mon_rx = NULL;
+       kfree(isac->mon_tx);
+       isac->mon_tx = NULL;
+       if (isac->dch.l1)
+               l1_event(isac->dch.l1, CLOSE_CHANNEL);
+       mISDN_freedchannel(&isac->dch);
+}
+
+static void
+dbusy_timer_handler(struct isac_hw *isac)
+{
+       int rbch, star;
+       u_long flags;
+
+       if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
+               spin_lock_irqsave(isac->hwlock, flags);
+               rbch = ReadISAC(isac, ISAC_RBCH);
+               star = ReadISAC(isac, ISAC_STAR);
+               pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
+                       isac->name, rbch, star);
+               if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */
+                       test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags);
+               else {
+                       /* discard frame; reset transceiver */
+                       test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags);
+                       if (isac->dch.tx_idx)
+                               isac->dch.tx_idx = 0;
+                       else
+                               pr_info("%s: ISAC D-Channel Busy no tx_idx\n",
+                                       isac->name);
+                       /* Transmitter reset */
+                       WriteISAC(isac, ISAC_CMDR, 0x01);
+               }
+               spin_unlock_irqrestore(isac->hwlock, flags);
+       }
+}
+
+static int
+open_dchannel(struct isac_hw *isac, struct channel_req *rq)
+{
+       pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__,
+               isac->dch.dev.id, __builtin_return_address(1));
+       if (rq->protocol != ISDN_P_TE_S0)
+               return -EINVAL;
+       if (rq->adr.channel == 1)
+               /* E-Channel not supported */
+               return -EINVAL;
+       rq->ch = &isac->dch.dev.D;
+       rq->ch->protocol = rq->protocol;
+       if (isac->dch.state == 7)
+               _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       return 0;
+}
+
+static const char *ISACVer[] =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+static int
+isac_init(struct isac_hw *isac)
+{
+       u8 val;
+       int err = 0;
+
+       if (!isac->dch.l1) {
+               err = create_l1(&isac->dch, isac_l1cmd);
+               if (err)
+                       return err;
+       }
+       isac->mon_tx = NULL;
+       isac->mon_rx = NULL;
+       isac->dch.timer.function = (void *) dbusy_timer_handler;
+       isac->dch.timer.data = (long)isac;
+       init_timer(&isac->dch.timer);
+       isac->mocr = 0xaa;
+       if (isac->type & IPAC_TYPE_ISACX) {
+               /* Disable all IRQ */
+               WriteISAC(isac, ISACX_MASK, 0xff);
+               val = ReadISAC(isac, ISACX_STARD);
+               pr_debug("%s: ISACX STARD %x\n", isac->name, val);
+               val = ReadISAC(isac, ISACX_ISTAD);
+               pr_debug("%s: ISACX ISTAD %x\n", isac->name, val);
+               val = ReadISAC(isac, ISACX_ISTA);
+               pr_debug("%s: ISACX ISTA %x\n", isac->name, val);
+               /* clear LDD */
+               WriteISAC(isac, ISACX_TR_CONF0, 0x00);
+               /* enable transmitter */
+               WriteISAC(isac, ISACX_TR_CONF2, 0x00);
+               /* transparent mode 0, RAC, stop/go */
+               WriteISAC(isac, ISACX_MODED, 0xc9);
+               /* all HDLC IRQ unmasked */
+               val = ReadISAC(isac, ISACX_ID);
+               if (isac->dch.debug & DEBUG_HW)
+                       pr_notice("%s: ISACX Design ID %x\n",
+                               isac->name, val & 0x3f);
+               val = ReadISAC(isac, ISACX_CIR0);
+               pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
+               isac->state = val >> 4;
+               isac_ph_state_change(isac);
+               ph_command(isac, ISAC_CMD_RS);
+               WriteISAC(isac, ISACX_MASK, IPACX__ON);
+               WriteISAC(isac, ISACX_MASKD, 0x00);
+       } else { /* old isac */
+               WriteISAC(isac, ISAC_MASK, 0xff);
+               val = ReadISAC(isac, ISAC_STAR);
+               pr_debug("%s: ISAC STAR %x\n", isac->name, val);
+               val = ReadISAC(isac, ISAC_MODE);
+               pr_debug("%s: ISAC MODE %x\n", isac->name, val);
+               val = ReadISAC(isac, ISAC_ADF2);
+               pr_debug("%s: ISAC ADF2 %x\n", isac->name, val);
+               val = ReadISAC(isac, ISAC_ISTA);
+               pr_debug("%s: ISAC ISTA %x\n", isac->name, val);
+               if (val & 0x01) {
+                       val = ReadISAC(isac, ISAC_EXIR);
+                       pr_debug("%s: ISAC EXIR %x\n", isac->name, val);
+               }
+               val = ReadISAC(isac, ISAC_RBCH);
+               if (isac->dch.debug & DEBUG_HW)
+                       pr_notice("%s: ISAC version (%x): %s\n", isac->name,
+                               val, ISACVer[(val >> 5) & 3]);
+               isac->type |= ((val >> 5) & 3);
+               if (!isac->adf2)
+                       isac->adf2 = 0x80;
+               if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */
+                       pr_info("%s: only support IOM2 mode but adf2=%02x\n",
+                               isac->name, isac->adf2);
+                       isac_release(isac);
+                       return -EINVAL;
+               }
+               WriteISAC(isac, ISAC_ADF2, isac->adf2);
+               WriteISAC(isac, ISAC_SQXR, 0x2f);
+               WriteISAC(isac, ISAC_SPCR, 0x00);
+               WriteISAC(isac, ISAC_STCR, 0x70);
+               WriteISAC(isac, ISAC_MODE, 0xc9);
+               WriteISAC(isac, ISAC_TIMR, 0x00);
+               WriteISAC(isac, ISAC_ADF1, 0x00);
+               val = ReadISAC(isac, ISAC_CIR0);
+               pr_debug("%s: ISAC CIR0 %x\n", isac->name, val);
+               isac->state = (val >> 2) & 0xf;
+               isac_ph_state_change(isac);
+               ph_command(isac, ISAC_CMD_RS);
+               WriteISAC(isac, ISAC_MASK, 0);
+       }
+       return err;
+}
+
+int
+mISDNisac_init(struct isac_hw *isac, void *hw)
+{
+       mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh);
+       isac->dch.hw = hw;
+       isac->dch.dev.D.send = isac_l1hw;
+       isac->init = isac_init;
+       isac->release = isac_release;
+       isac->ctrl = isac_ctrl;
+       isac->open = open_dchannel;
+       isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
+       isac->dch.dev.nrbchan = 2;
+       return 0;
+}
+EXPORT_SYMBOL(mISDNisac_init);
+
+static void
+waitforCEC(struct hscx_hw *hx)
+{
+       u8 starb, to = 50;
+
+       while (to) {
+               starb = ReadHSCX(hx, IPAC_STARB);
+               if (!(starb & 0x04))
+                       break;
+               udelay(1);
+               to--;
+       }
+       if (to < 50)
+               pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr,
+                       50 - to);
+       if (!to)
+               pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr);
+}
+
+
+static void
+waitforXFW(struct hscx_hw *hx)
+{
+       u8 starb, to = 50;
+
+       while (to) {
+               starb = ReadHSCX(hx, IPAC_STARB);
+               if ((starb & 0x44) == 0x40)
+                       break;
+               udelay(1);
+               to--;
+       }
+       if (to < 50)
+               pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr,
+                       50 - to);
+       if (!to)
+               pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr);
+}
+
+static void
+hscx_cmdr(struct hscx_hw *hx, u8 cmd)
+{
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               WriteHSCX(hx, IPACX_CMDRB, cmd);
+       else {
+               waitforCEC(hx);
+               WriteHSCX(hx, IPAC_CMDRB, cmd);
+       }
+}
+
+static void
+hscx_empty_fifo(struct hscx_hw *hscx, u8 count)
+{
+       u8 *p;
+
+       pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count);
+       if (!hscx->bch.rx_skb) {
+               hscx->bch.rx_skb = mI_alloc_skb(hscx->bch.maxlen, GFP_ATOMIC);
+               if (!hscx->bch.rx_skb) {
+                       pr_info("%s: B receive out of memory\n",
+                               hscx->ip->name);
+                       hscx_cmdr(hscx, 0x80); /* RMC */
+                       return;
+               }
+       }
+       if ((hscx->bch.rx_skb->len + count) > hscx->bch.maxlen) {
+               pr_debug("%s: overrun %d\n", hscx->ip->name,
+                       hscx->bch.rx_skb->len + count);
+               skb_trim(hscx->bch.rx_skb, 0);
+               hscx_cmdr(hscx, 0x80); /* RMC */
+               return;
+       }
+       p = skb_put(hscx->bch.rx_skb, count);
+
+       if (hscx->ip->type & IPAC_TYPE_IPACX)
+               hscx->ip->read_fifo(hscx->ip->hw,
+                       hscx->off + IPACX_RFIFOB, p, count);
+       else
+               hscx->ip->read_fifo(hscx->ip->hw,
+                       hscx->off, p, count);
+
+       hscx_cmdr(hscx, 0x80); /* RMC */
+
+       if (hscx->bch.debug & DEBUG_HW_BFIFO) {
+               snprintf(hscx->log, 64, "B%1d-recv %s %d ",
+                       hscx->bch.nr, hscx->ip->name, count);
+               print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+}
+
+static void
+hscx_fill_fifo(struct hscx_hw *hscx)
+{
+       int count, more;
+       u8 *p;
+
+       if (!hscx->bch.tx_skb)
+               return;
+       count = hscx->bch.tx_skb->len - hscx->bch.tx_idx;
+       if (count <= 0)
+               return;
+       p = hscx->bch.tx_skb->data + hscx->bch.tx_idx;
+
+       more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0;
+       if (count > hscx->fifo_size) {
+               count = hscx->fifo_size;
+               more = 1;
+       }
+       pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr, count,
+               hscx->bch.tx_idx, hscx->bch.tx_skb->len);
+       hscx->bch.tx_idx += count;
+
+       if (hscx->ip->type & IPAC_TYPE_IPACX)
+               hscx->ip->write_fifo(hscx->ip->hw,
+                       hscx->off + IPACX_XFIFOB, p, count);
+       else {
+               waitforXFW(hscx);
+               hscx->ip->write_fifo(hscx->ip->hw,
+                       hscx->off, p, count);
+       }
+       hscx_cmdr(hscx, more ? 0x08 : 0x0a);
+
+       if (hscx->bch.debug & DEBUG_HW_BFIFO) {
+               snprintf(hscx->log, 64, "B%1d-send %s %d ",
+                       hscx->bch.nr, hscx->ip->name, count);
+               print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+}
+
+static void
+hscx_xpr(struct hscx_hw *hx)
+{
+       if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len)
+               hscx_fill_fifo(hx);
+       else {
+               if (hx->bch.tx_skb) {
+                       /* send confirm, on trans, free on hdlc. */
+                       if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags))
+                               confirm_Bsend(&hx->bch);
+                       dev_kfree_skb(hx->bch.tx_skb);
+               }
+               if (get_next_bframe(&hx->bch))
+                       hscx_fill_fifo(hx);
+       }
+}
+
+static void
+ipac_rme(struct hscx_hw *hx)
+{
+       int count;
+       u8 rstab;
+
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               rstab = ReadHSCX(hx, IPACX_RSTAB);
+       else
+               rstab = ReadHSCX(hx, IPAC_RSTAB);
+       pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab);
+       if ((rstab & 0xf0) != 0xa0) {
+               /* !(VFR && !RDO && CRC && !RAB) */
+               if (!(rstab & 0x80)) {
+                       if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+                               pr_notice("%s: B%1d invalid frame\n",
+                                       hx->ip->name, hx->bch.nr);
+               }
+               if (rstab & 0x40) {
+                       if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+                               pr_notice("%s: B%1d RDO proto=%x\n",
+                                       hx->ip->name, hx->bch.nr,
+                                       hx->bch.state);
+               }
+               if (!(rstab & 0x20)) {
+                       if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+                               pr_notice("%s: B%1d CRC error\n",
+                                       hx->ip->name, hx->bch.nr);
+               }
+               hscx_cmdr(hx, 0x80); /* Do RMC */
+               return;
+       }
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               count = ReadHSCX(hx, IPACX_RBCLB);
+       else
+               count = ReadHSCX(hx, IPAC_RBCLB);
+       count &= (hx->fifo_size - 1);
+       if (count == 0)
+               count = hx->fifo_size;
+       hscx_empty_fifo(hx, count);
+       if (!hx->bch.rx_skb)
+               return;
+       if (hx->bch.rx_skb->len < 2) {
+               pr_debug("%s: B%1d frame to short %d\n",
+                       hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len);
+               skb_trim(hx->bch.rx_skb, 0);
+       } else {
+               skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1);
+               recv_Bchannel(&hx->bch, 0);
+       }
+}
+
+static void
+ipac_irq(struct hscx_hw *hx, u8 ista)
+{
+       u8 istab, m, exirb = 0;
+
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               istab = ReadHSCX(hx, IPACX_ISTAB);
+       else if (hx->ip->type & IPAC_TYPE_IPAC) {
+               istab = ReadHSCX(hx, IPAC_ISTAB);
+               m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB;
+               if (m & ista) {
+                       exirb = ReadHSCX(hx, IPAC_EXIRB);
+                       pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+                               hx->bch.nr, exirb);
+               }
+       } else if (hx->bch.nr & 2) { /* HSCX B */
+               if (ista & (HSCX__EXA | HSCX__ICA))
+                       ipac_irq(&hx->ip->hscx[0], ista);
+               if (ista & HSCX__EXB) {
+                       exirb = ReadHSCX(hx, IPAC_EXIRB);
+                       pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+                               hx->bch.nr, exirb);
+               }
+               istab = ista & 0xF8;
+       } else { /* HSCX A */
+               istab = ReadHSCX(hx, IPAC_ISTAB);
+               if (ista & HSCX__EXA) {
+                       exirb = ReadHSCX(hx, IPAC_EXIRB);
+                       pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+                               hx->bch.nr, exirb);
+               }
+               istab = istab & 0xF8;
+       }
+       if (exirb & IPAC_B_XDU)
+               istab |= IPACX_B_XDU;
+       if (exirb & IPAC_B_RFO)
+               istab |= IPACX_B_RFO;
+       pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab);
+
+       if (!test_bit(FLG_ACTIVE, &hx->bch.Flags))
+               return;
+
+       if (istab & IPACX_B_RME)
+               ipac_rme(hx);
+
+       if (istab & IPACX_B_RPF) {
+               hscx_empty_fifo(hx, hx->fifo_size);
+               if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
+                       /* receive transparent audio data */
+                       if (hx->bch.rx_skb)
+                               recv_Bchannel(&hx->bch, 0);
+               }
+       }
+
+       if (istab & IPACX_B_RFO) {
+               pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr);
+               hscx_cmdr(hx, 0x40);    /* RRES */
+       }
+
+       if (istab & IPACX_B_XPR)
+               hscx_xpr(hx);
+
+       if (istab & IPACX_B_XDU) {
+               if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
+                       hscx_fill_fifo(hx);
+                       return;
+               }
+               pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name,
+                       hx->bch.nr, hx->bch.tx_idx);
+               hx->bch.tx_idx = 0;
+               hscx_cmdr(hx, 0x01);    /* XRES */
+       }
+}
+
+irqreturn_t
+mISDNipac_irq(struct ipac_hw *ipac, int maxloop)
+{
+       int cnt = maxloop + 1;
+       u8 ista, istad;
+       struct isac_hw  *isac = &ipac->isac;
+
+       if (ipac->type & IPAC_TYPE_IPACX) {
+               ista = ReadIPAC(ipac, ISACX_ISTA);
+               while (ista && cnt--) {
+                       pr_debug("%s: ISTA %02x\n", ipac->name, ista);
+                       if (ista & IPACX__ICA)
+                               ipac_irq(&ipac->hscx[0], ista);
+                       if (ista & IPACX__ICB)
+                               ipac_irq(&ipac->hscx[1], ista);
+                       if (ista & (ISACX__ICD | ISACX__CIC))
+                               mISDNisac_irq(&ipac->isac, ista);
+                       ista = ReadIPAC(ipac, ISACX_ISTA);
+               }
+       } else if (ipac->type & IPAC_TYPE_IPAC) {
+               ista = ReadIPAC(ipac, IPAC_ISTA);
+               while (ista && cnt--) {
+                       pr_debug("%s: ISTA %02x\n", ipac->name, ista);
+                       if (ista & (IPAC__ICD | IPAC__EXD)) {
+                               istad = ReadISAC(isac, ISAC_ISTA);
+                               pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
+                               if (istad & IPAC_D_TIN2)
+                                       pr_debug("%s TIN2 irq\n", ipac->name);
+                               if (ista & IPAC__EXD)
+                                       istad |= 1; /* ISAC EXI */
+                               mISDNisac_irq(isac, istad);
+                       }
+                       if (ista & (IPAC__ICA | IPAC__EXA))
+                               ipac_irq(&ipac->hscx[0], ista);
+                       if (ista & (IPAC__ICB | IPAC__EXB))
+                               ipac_irq(&ipac->hscx[1], ista);
+                       ista = ReadIPAC(ipac, IPAC_ISTA);
+               }
+       } else if (ipac->type & IPAC_TYPE_HSCX) {
+               while (cnt) {
+                       ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off);
+                       pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista);
+                       if (ista)
+                               ipac_irq(&ipac->hscx[1], ista);
+                       istad = ReadISAC(isac, ISAC_ISTA);
+                       pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
+                       if (istad)
+                               mISDNisac_irq(isac, istad);
+                       if (0 == (ista | istad))
+                               break;
+                       cnt--;
+               }
+       }
+       if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */
+               return IRQ_NONE;
+       if (cnt < maxloop)
+               pr_debug("%s: %d irqloops cpu%d\n", ipac->name,
+                       maxloop - cnt, smp_processor_id());
+       if (maxloop && !cnt)
+               pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name,
+                       maxloop, smp_processor_id());
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(mISDNipac_irq);
+
+static int
+hscx_mode(struct hscx_hw *hscx, u32 bprotocol)
+{
+       pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name,
+               '@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr);
+       if (hscx->ip->type & IPAC_TYPE_IPACX) {
+               if (hscx->bch.nr & 1) { /* B1 and ICA */
+                       WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80);
+                       WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88);
+               } else { /* B2 and ICB */
+                       WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81);
+                       WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88);
+               }
+               switch (bprotocol) {
+               case ISDN_P_NONE: /* init */
+                       WriteHSCX(hscx, IPACX_MODEB, 0xC0);     /* rec off */
+                       WriteHSCX(hscx, IPACX_EXMB,  0x30);     /* std adj. */
+                       WriteHSCX(hscx, IPACX_MASKB, 0xFF);     /* ints off */
+                       hscx_cmdr(hscx, 0x41);
+                       test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_RAW:
+                       WriteHSCX(hscx, IPACX_MODEB, 0x88);     /* ex trans */
+                       WriteHSCX(hscx, IPACX_EXMB,  0x00);     /* trans */
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
+                       test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_HDLC:
+                       WriteHSCX(hscx, IPACX_MODEB, 0xC0);     /* trans */
+                       WriteHSCX(hscx, IPACX_EXMB,  0x00);     /* hdlc,crc */
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
+                       test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+                       break;
+               default:
+                       pr_info("%s: protocol not known %x\n", hscx->ip->name,
+                               bprotocol);
+                       return -ENOPROTOOPT;
+               }
+       } else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */
+               WriteHSCX(hscx, IPAC_CCR1, 0x82);
+               WriteHSCX(hscx, IPAC_CCR2, 0x30);
+               WriteHSCX(hscx, IPAC_XCCR, 0x07);
+               WriteHSCX(hscx, IPAC_RCCR, 0x07);
+               WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
+               WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
+               switch (bprotocol) {
+               case ISDN_P_NONE:
+                       WriteHSCX(hscx, IPAC_TSAX, 0x1F);
+                       WriteHSCX(hscx, IPAC_TSAR, 0x1F);
+                       WriteHSCX(hscx, IPAC_MODEB, 0x84);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x82);
+                       WriteHSCX(hscx, IPAC_MASKB, 0xFF);      /* ints off */
+                       test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_RAW:
+                       WriteHSCX(hscx, IPAC_MODEB, 0xe4);      /* ex trans */
+                       WriteHSCX(hscx, IPAC_CCR1, 0x82);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_HDLC:
+                       WriteHSCX(hscx, IPAC_MODEB, 0x8c);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x8a);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+                       break;
+               default:
+                       pr_info("%s: protocol not known %x\n", hscx->ip->name,
+                               bprotocol);
+                       return -ENOPROTOOPT;
+               }
+       } else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */
+               WriteHSCX(hscx, IPAC_CCR1, 0x85);
+               WriteHSCX(hscx, IPAC_CCR2, 0x30);
+               WriteHSCX(hscx, IPAC_XCCR, 0x07);
+               WriteHSCX(hscx, IPAC_RCCR, 0x07);
+               WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
+               WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
+               switch (bprotocol) {
+               case ISDN_P_NONE:
+                       WriteHSCX(hscx, IPAC_TSAX, 0x1F);
+                       WriteHSCX(hscx, IPAC_TSAR, 0x1F);
+                       WriteHSCX(hscx, IPAC_MODEB, 0x84);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x85);
+                       WriteHSCX(hscx, IPAC_MASKB, 0xFF);      /* ints off */
+                       test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_RAW:
+                       WriteHSCX(hscx, IPAC_MODEB, 0xe4);      /* ex trans */
+                       WriteHSCX(hscx, IPAC_CCR1, 0x85);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_HDLC:
+                       WriteHSCX(hscx, IPAC_MODEB, 0x8c);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x8d);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+                       break;
+               default:
+                       pr_info("%s: protocol not known %x\n", hscx->ip->name,
+                               bprotocol);
+                       return -ENOPROTOOPT;
+               }
+       } else
+               return -EINVAL;
+       hscx->bch.state = bprotocol;
+       return 0;
+}
+
+static int
+hscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct hscx_hw  *hx = container_of(bch, struct hscx_hw, bch);
+       int ret = -EINVAL;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       u32 id;
+       u_long flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(hx->ip->hwlock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       ret = 0;
+                       hscx_fill_fifo(hx);
+                       spin_unlock_irqrestore(hx->ip->hwlock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(hx->ip->hwlock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = hscx_mode(hx, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               spin_lock_irqsave(hx->ip->hwlock, flags);
+               mISDN_clear_bchannel(bch);
+               hscx_mode(hx, ISDN_P_NONE);
+               spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x,%x)\n",
+                       hx->ip->name, __func__, hh->prim, hh->id);
+               ret = -EINVAL;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       /* Nothing implemented yet */
+       case MISDN_CTRL_FILL_EMPTY:
+       default:
+               pr_info("%s: unknown Op %x\n", __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct hscx_hw  *hx = container_of(bch, struct hscx_hw, bch);
+       int ret = -EINVAL;
+       u_long flags;
+
+       pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       spin_lock_irqsave(hx->ip->hwlock, flags);
+                       mISDN_freebchannel(bch);
+                       hscx_mode(hx, ISDN_P_NONE);
+                       spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               } else {
+                       skb_queue_purge(&bch->rqueue);
+                       bch->rcount = 0;
+               }
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(hx->ip->owner);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bch, arg);
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x)\n",
+                       hx->ip->name, __func__, cmd);
+       }
+       return ret;
+}
+
+static void
+free_ipac(struct ipac_hw *ipac)
+{
+       isac_release(&ipac->isac);
+}
+
+static const char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+
+
+static void
+hscx_init(struct hscx_hw *hx)
+{
+       u8 val;
+
+       WriteHSCX(hx, IPAC_RAH2, 0xFF);
+       WriteHSCX(hx, IPAC_XBCH, 0x00);
+       WriteHSCX(hx, IPAC_RLCR, 0x00);
+
+       if (hx->ip->type & IPAC_TYPE_HSCX) {
+               WriteHSCX(hx, IPAC_CCR1, 0x85);
+               val = ReadHSCX(hx, HSCX_VSTR);
+               pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val);
+               if (hx->bch.debug & DEBUG_HW)
+                       pr_notice("%s: HSCX version %s\n", hx->ip->name,
+                               HSCXVer[val & 0x0f]);
+       } else
+               WriteHSCX(hx, IPAC_CCR1, 0x82);
+       WriteHSCX(hx, IPAC_CCR2, 0x30);
+       WriteHSCX(hx, IPAC_XCCR, 0x07);
+       WriteHSCX(hx, IPAC_RCCR, 0x07);
+}
+
+static int
+ipac_init(struct ipac_hw *ipac)
+{
+       u8 val;
+
+       if (ipac->type & IPAC_TYPE_HSCX) {
+               hscx_init(&ipac->hscx[0]);
+               hscx_init(&ipac->hscx[1]);
+               val = ReadIPAC(ipac, IPAC_ID);
+       } else if (ipac->type & IPAC_TYPE_IPAC) {
+               hscx_init(&ipac->hscx[0]);
+               hscx_init(&ipac->hscx[1]);
+               WriteIPAC(ipac, IPAC_MASK, IPAC__ON);
+               val = ReadIPAC(ipac, IPAC_CONF);
+               /* conf is default 0, but can be overwritten by card setup */
+               pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name,
+                       val, ipac->conf);
+               WriteIPAC(ipac, IPAC_CONF, ipac->conf);
+               val = ReadIPAC(ipac, IPAC_ID);
+               if (ipac->hscx[0].bch.debug & DEBUG_HW)
+                       pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val);
+       }
+       /* nothing special for IPACX to do here */
+       return isac_init(&ipac->isac);
+}
+
+static int
+open_bchannel(struct ipac_hw *ipac, struct channel_req *rq)
+{
+       struct bchannel         *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &ipac->hscx[rq->adr.channel - 1].bch;
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       return 0;
+}
+
+static int
+channel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_LOOP;
+               break;
+       case MISDN_CTRL_LOOP:
+               /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+               if (cq->channel < 0 || cq->channel > 3) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel);
+               break;
+       default:
+               pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel *dch = container_of(dev, struct dchannel, dev);
+       struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+       struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac);
+       struct channel_req *rq;
+       int err = 0;
+
+       pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->protocol == ISDN_P_TE_S0)
+                       err = open_dchannel(isac, rq);
+               else
+                       err = open_bchannel(ipac, rq);
+               if (err)
+                       break;
+               if (!try_module_get(ipac->owner))
+                       pr_info("%s: cannot get module\n", ipac->name);
+               break;
+       case CLOSE_CHANNEL:
+               pr_debug("%s: dev(%d) close from %p\n", ipac->name,
+                       dch->dev.id, __builtin_return_address(0));
+               module_put(ipac->owner);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(ipac, arg);
+               break;
+       default:
+               pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+u32
+mISDNipac_init(struct ipac_hw *ipac, void *hw)
+{
+       u32 ret;
+       u8 i;
+
+       ipac->hw = hw;
+       if (ipac->isac.dch.debug & DEBUG_HW)
+               pr_notice("%s: ipac type %x\n", ipac->name, ipac->type);
+       if (ipac->type & IPAC_TYPE_HSCX) {
+               ipac->isac.type = IPAC_TYPE_ISAC;
+               ipac->hscx[0].off = 0;
+               ipac->hscx[1].off = 0x40;
+               ipac->hscx[0].fifo_size = 32;
+               ipac->hscx[1].fifo_size = 32;
+       } else if (ipac->type & IPAC_TYPE_IPAC) {
+               ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC;
+               ipac->hscx[0].off = 0;
+               ipac->hscx[1].off = 0x40;
+               ipac->hscx[0].fifo_size = 64;
+               ipac->hscx[1].fifo_size = 64;
+       } else if (ipac->type & IPAC_TYPE_IPACX) {
+               ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX;
+               ipac->hscx[0].off = IPACX_OFF_ICA;
+               ipac->hscx[1].off = IPACX_OFF_ICB;
+               ipac->hscx[0].fifo_size = 64;
+               ipac->hscx[1].fifo_size = 64;
+       } else
+               return 0;
+
+       mISDNisac_init(&ipac->isac, hw);
+
+       ipac->isac.dch.dev.D.ctrl = ipac_dctrl;
+
+       for (i = 0; i < 2; i++) {
+               ipac->hscx[i].bch.nr = i + 1;
+               set_channelmap(i + 1, ipac->isac.dch.dev.channelmap);
+               list_add(&ipac->hscx[i].bch.ch.list,
+                       &ipac->isac.dch.dev.bchannels);
+               mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM);
+               ipac->hscx[i].bch.ch.nr = i + 1;
+               ipac->hscx[i].bch.ch.send = &hscx_l2l1;
+               ipac->hscx[i].bch.ch.ctrl = hscx_bctrl;
+               ipac->hscx[i].bch.hw = hw;
+               ipac->hscx[i].ip = ipac;
+               /* default values for IOM time slots
+                * can be overwriten by card */
+               ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03;
+       }
+
+       ipac->init = ipac_init;
+       ipac->release = free_ipac;
+
+       ret =   (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       return ret;
+}
+EXPORT_SYMBOL(mISDNipac_init);
+
+static int __init
+isac_mod_init(void)
+{
+       pr_notice("mISDNipac module version %s\n", ISAC_REV);
+       return 0;
+}
+
+static void __exit
+isac_mod_cleanup(void)
+{
+       pr_notice("mISDNipac module unloaded\n");
+}
+module_init(isac_mod_init);
+module_exit(isac_mod_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c
new file mode 100644 (file)
index 0000000..de352a1
--- /dev/null
@@ -0,0 +1,1726 @@
+/*
+ * mISDNisar.c   ISAR (Siemens PSB 7110) specific functions
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* define this to enable static debug messages, if you kernel supports
+ * dynamic debugging, you should use debugfs for this
+ */
+/* #define DEBUG */
+
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/mISDNhw.h>
+#include "isar.h"
+
+#define ISAR_REV       "2.1"
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(ISAR_REV);
+
+#define DEBUG_HW_FIRMWARE_FIFO 0x10000
+
+static const u8 faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146";
+static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121,
+                                       122, 145, 146};
+#define FAXMODCNT 13
+
+static void isar_setup(struct isar_hw *);
+
+static inline int
+waitforHIA(struct isar_hw *isar, int timeout)
+{
+       int t = timeout;
+       u8 val = isar->read_reg(isar->hw, ISAR_HIA);
+
+       while ((val & 1) && t) {
+               udelay(1);
+               t--;
+               val = isar->read_reg(isar->hw, ISAR_HIA);
+       }
+       pr_debug("%s: HIA after %dus\n", isar->name, timeout - t);
+       return timeout;
+}
+
+/*
+ * send msg to ISAR mailbox
+ * if msg is NULL use isar->buf
+ */
+static int
+send_mbox(struct isar_hw *isar, u8 his, u8 creg, u8 len, u8 *msg)
+{
+       if (!waitforHIA(isar, 1000))
+               return 0;
+       pr_debug("send_mbox(%02x,%02x,%d)\n", his, creg, len);
+       isar->write_reg(isar->hw, ISAR_CTRL_H, creg);
+       isar->write_reg(isar->hw, ISAR_CTRL_L, len);
+       isar->write_reg(isar->hw, ISAR_WADR, 0);
+       if (!msg)
+               msg = isar->buf;
+       if (msg && len) {
+               isar->write_fifo(isar->hw, ISAR_MBOX, msg, len);
+               if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) {
+                       int l = 0;
+
+                       while (l < (int)len) {
+                               hex_dump_to_buffer(msg + l, len - l, 32, 1,
+                                       isar->log, 256, 1);
+                               pr_debug("%s: %s %02x: %s\n", isar->name,
+                                       __func__, l, isar->log);
+                               l += 32;
+                       }
+               }
+       }
+       isar->write_reg(isar->hw, ISAR_HIS, his);
+       waitforHIA(isar, 1000);
+       return 1;
+}
+
+/*
+ * receive message from ISAR mailbox
+ * if msg is NULL use isar->buf
+ */
+static void
+rcv_mbox(struct isar_hw *isar, u8 *msg)
+{
+       if (!msg)
+               msg = isar->buf;
+       isar->write_reg(isar->hw, ISAR_RADR, 0);
+       if (msg && isar->clsb) {
+               isar->read_fifo(isar->hw, ISAR_MBOX, msg, isar->clsb);
+               if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) {
+                       int l = 0;
+
+                       while (l < (int)isar->clsb) {
+                               hex_dump_to_buffer(msg + l, isar->clsb - l, 32,
+                                       1, isar->log, 256, 1);
+                               pr_debug("%s: %s %02x: %s\n", isar->name,
+                                       __func__, l, isar->log);
+                               l += 32;
+                       }
+               }
+       }
+       isar->write_reg(isar->hw, ISAR_IIA, 0);
+}
+
+static inline void
+get_irq_infos(struct isar_hw *isar)
+{
+       isar->iis = isar->read_reg(isar->hw, ISAR_IIS);
+       isar->cmsb = isar->read_reg(isar->hw, ISAR_CTRL_H);
+       isar->clsb = isar->read_reg(isar->hw, ISAR_CTRL_L);
+       pr_debug("%s: rcv_mbox(%02x,%02x,%d)\n", isar->name,
+               isar->iis, isar->cmsb, isar->clsb);
+}
+
+/*
+ * poll answer message from ISAR mailbox
+ * should be used only with ISAR IRQs disabled before DSP was started
+ *
+ */
+static int
+poll_mbox(struct isar_hw *isar, int maxdelay)
+{
+       int t = maxdelay;
+       u8 irq;
+
+       irq = isar->read_reg(isar->hw, ISAR_IRQBIT);
+       while (t && !(irq & ISAR_IRQSTA)) {
+               udelay(1);
+               t--;
+       }
+       if (t)  {
+               get_irq_infos(isar);
+               rcv_mbox(isar, NULL);
+       }
+       pr_debug("%s: pulled %d bytes after %d us\n",
+               isar->name, isar->clsb, maxdelay - t);
+       return t;
+}
+
+static int
+ISARVersion(struct isar_hw *isar)
+{
+       int ver;
+
+       /* disable ISAR IRQ */
+       isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+       isar->buf[0] = ISAR_MSG_HWVER;
+       isar->buf[1] = 0;
+       isar->buf[2] = 1;
+       if (!send_mbox(isar, ISAR_HIS_VNR, 0, 3, NULL))
+               return -1;
+       if (!poll_mbox(isar, 1000))
+               return -2;
+       if (isar->iis == ISAR_IIS_VNR) {
+               if (isar->clsb == 1) {
+                       ver = isar->buf[0] & 0xf;
+                       return ver;
+               }
+               return -3;
+       }
+       return -4;
+}
+
+static int
+load_firmware(struct isar_hw *isar, const u8 *buf, int size)
+{
+       u32     saved_debug = isar->ch[0].bch.debug;
+       int     ret, cnt;
+       u8      nom, noc;
+       u16     left, val, *sp = (u16 *)buf;
+       u8      *mp;
+       u_long  flags;
+
+       struct {
+               u16 sadr;
+               u16 len;
+               u16 d_key;
+       } blk_head;
+
+       if (1 != isar->version) {
+               pr_err("%s: ISAR wrong version %d firmware download aborted\n",
+                       isar->name, isar->version);
+               return -EINVAL;
+       }
+       if (!(saved_debug & DEBUG_HW_FIRMWARE_FIFO))
+               isar->ch[0].bch.debug &= ~DEBUG_HW_BFIFO;
+       pr_debug("%s: load firmware %d words (%d bytes)\n",
+               isar->name, size/2, size);
+       cnt = 0;
+       size /= 2;
+       /* disable ISAR IRQ */
+       spin_lock_irqsave(isar->hwlock, flags);
+       isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+       spin_unlock_irqrestore(isar->hwlock, flags);
+       while (cnt < size) {
+               blk_head.sadr = le16_to_cpu(*sp++);
+               blk_head.len = le16_to_cpu(*sp++);
+               blk_head.d_key = le16_to_cpu(*sp++);
+               cnt += 3;
+               pr_debug("ISAR firmware block (%#x,%d,%#x)\n",
+                       blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+               left = blk_head.len;
+               if (cnt + left > size) {
+                       pr_info("%s: firmware error have %d need %d words\n",
+                               isar->name, size, cnt + left);
+                       ret = -EINVAL;
+                       goto reterrflg;
+               }
+               spin_lock_irqsave(isar->hwlock, flags);
+               if (!send_mbox(isar, ISAR_HIS_DKEY, blk_head.d_key & 0xff,
+                   0, NULL)) {
+                       pr_info("ISAR send_mbox dkey failed\n");
+                       ret = -ETIME;
+                       goto reterror;
+               }
+               if (!poll_mbox(isar, 1000)) {
+                       pr_warning("ISAR poll_mbox dkey failed\n");
+                       ret = -ETIME;
+                       goto reterror;
+               }
+               spin_unlock_irqrestore(isar->hwlock, flags);
+               if ((isar->iis != ISAR_IIS_DKEY) || isar->cmsb || isar->clsb) {
+                       pr_info("ISAR wrong dkey response (%x,%x,%x)\n",
+                               isar->iis, isar->cmsb, isar->clsb);
+                       ret = 1;
+                       goto reterrflg;
+               }
+               while (left > 0) {
+                       if (left > 126)
+                               noc = 126;
+                       else
+                               noc = left;
+                       nom = (2 * noc) + 3;
+                       mp  = isar->buf;
+                       /* the ISAR is big endian */
+                       *mp++ = blk_head.sadr >> 8;
+                       *mp++ = blk_head.sadr & 0xFF;
+                       left -= noc;
+                       cnt += noc;
+                       *mp++ = noc;
+                       pr_debug("%s: load %3d words at %04x\n", isar->name,
+                               noc, blk_head.sadr);
+                       blk_head.sadr += noc;
+                       while (noc) {
+                               val = le16_to_cpu(*sp++);
+                               *mp++ = val >> 8;
+                               *mp++ = val & 0xFF;;
+                               noc--;
+                       }
+                       spin_lock_irqsave(isar->hwlock, flags);
+                       if (!send_mbox(isar, ISAR_HIS_FIRM, 0, nom, NULL)) {
+                               pr_info("ISAR send_mbox prog failed\n");
+                               ret = -ETIME;
+                               goto reterror;
+                       }
+                       if (!poll_mbox(isar, 1000)) {
+                               pr_info("ISAR poll_mbox prog failed\n");
+                               ret = -ETIME;
+                               goto reterror;
+                       }
+                       spin_unlock_irqrestore(isar->hwlock, flags);
+                       if ((isar->iis != ISAR_IIS_FIRM) ||
+                           isar->cmsb || isar->clsb) {
+                               pr_info("ISAR wrong prog response (%x,%x,%x)\n",
+                                       isar->iis, isar->cmsb, isar->clsb);
+                               ret = -EIO;
+                               goto reterrflg;
+                       }
+               }
+               pr_debug("%s: ISAR firmware block %d words loaded\n",
+                       isar->name, blk_head.len);
+       }
+       isar->ch[0].bch.debug = saved_debug;
+       /* 10ms delay */
+       cnt = 10;
+       while (cnt--)
+               mdelay(1);
+       isar->buf[0] = 0xff;
+       isar->buf[1] = 0xfe;
+       isar->bstat = 0;
+       spin_lock_irqsave(isar->hwlock, flags);
+       if (!send_mbox(isar, ISAR_HIS_STDSP, 0, 2, NULL)) {
+               pr_info("ISAR send_mbox start dsp failed\n");
+               ret = -ETIME;
+               goto reterror;
+       }
+       if (!poll_mbox(isar, 1000)) {
+               pr_info("ISAR poll_mbox start dsp failed\n");
+               ret = -ETIME;
+               goto reterror;
+       }
+       if ((isar->iis != ISAR_IIS_STDSP) || isar->cmsb || isar->clsb) {
+               pr_info("ISAR wrong start dsp response (%x,%x,%x)\n",
+                       isar->iis, isar->cmsb, isar->clsb);
+               ret = -EIO;
+               goto reterror;
+       } else
+               pr_debug("%s: ISAR start dsp success\n", isar->name);
+
+       /* NORMAL mode entered */
+       /* Enable IRQs of ISAR */
+       isar->write_reg(isar->hw, ISAR_IRQBIT, ISAR_IRQSTA);
+       spin_unlock_irqrestore(isar->hwlock, flags);
+       cnt = 1000; /* max 1s */
+       while ((!isar->bstat) && cnt) {
+               mdelay(1);
+               cnt--;
+       }
+       if (!cnt) {
+               pr_info("ISAR no general status event received\n");
+               ret = -ETIME;
+               goto reterrflg;
+       } else
+               pr_debug("%s: ISAR general status event %x\n",
+                       isar->name, isar->bstat);
+       /* 10ms delay */
+       cnt = 10;
+       while (cnt--)
+               mdelay(1);
+       isar->iis = 0;
+       spin_lock_irqsave(isar->hwlock, flags);
+       if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+               pr_info("ISAR send_mbox self tst failed\n");
+               ret = -ETIME;
+               goto reterror;
+       }
+       spin_unlock_irqrestore(isar->hwlock, flags);
+       cnt = 10000; /* max 100 ms */
+       while ((isar->iis != ISAR_IIS_DIAG) && cnt) {
+               udelay(10);
+               cnt--;
+       }
+       mdelay(1);
+       if (!cnt) {
+               pr_info("ISAR no self tst response\n");
+               ret = -ETIME;
+               goto reterrflg;
+       }
+       if ((isar->cmsb == ISAR_CTRL_STST) && (isar->clsb == 1)
+           && (isar->buf[0] == 0))
+               pr_debug("%s: ISAR selftest OK\n", isar->name);
+       else {
+               pr_info("ISAR selftest not OK %x/%x/%x\n",
+                       isar->cmsb, isar->clsb, isar->buf[0]);
+               ret = -EIO;
+               goto reterrflg;
+       }
+       spin_lock_irqsave(isar->hwlock, flags);
+       isar->iis = 0;
+       if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+               pr_info("ISAR RQST SVN failed\n");
+               ret = -ETIME;
+               goto reterror;
+       }
+       spin_unlock_irqrestore(isar->hwlock, flags);
+       cnt = 30000; /* max 300 ms */
+       while ((isar->iis != ISAR_IIS_DIAG) && cnt) {
+               udelay(10);
+               cnt--;
+       }
+       mdelay(1);
+       if (!cnt) {
+               pr_info("ISAR no SVN response\n");
+               ret = -ETIME;
+               goto reterrflg;
+       } else {
+               if ((isar->cmsb == ISAR_CTRL_SWVER) && (isar->clsb == 1)) {
+                       pr_notice("%s: ISAR software version %#x\n",
+                               isar->name, isar->buf[0]);
+               } else {
+                       pr_info("%s: ISAR wrong swver response (%x,%x)"
+                               " cnt(%d)\n", isar->name, isar->cmsb,
+                               isar->clsb, cnt);
+                       ret = -EIO;
+                       goto reterrflg;
+               }
+       }
+       spin_lock_irqsave(isar->hwlock, flags);
+       isar_setup(isar);
+       spin_unlock_irqrestore(isar->hwlock, flags);
+       ret = 0;
+reterrflg:
+       spin_lock_irqsave(isar->hwlock, flags);
+reterror:
+       isar->ch[0].bch.debug = saved_debug;
+       if (ret)
+               /* disable ISAR IRQ */
+               isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+       spin_unlock_irqrestore(isar->hwlock, flags);
+       return ret;
+}
+
+static inline void
+deliver_status(struct isar_ch *ch, int status)
+{
+       pr_debug("%s: HL->LL FAXIND %x\n", ch->is->name, status);
+       _queue_data(&ch->bch.ch, PH_CONTROL_IND, status, 0, NULL, GFP_ATOMIC);
+}
+
+static inline void
+isar_rcv_frame(struct isar_ch *ch)
+{
+       u8              *ptr;
+
+       if (!ch->is->clsb) {
+               pr_debug("%s; ISAR zero len frame\n", ch->is->name);
+               ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+               return;
+       }
+       switch (ch->bch.state) {
+       case ISDN_P_NONE:
+               pr_debug("%s: ISAR protocol 0 spurious IIS_RDATA %x/%x/%x\n",
+                       ch->is->name, ch->is->iis, ch->is->cmsb, ch->is->clsb);
+               ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+               break;
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_MODEM_ASYNC:
+               if (!ch->bch.rx_skb) {
+                       ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+                                               GFP_ATOMIC);
+                       if (unlikely(!ch->bch.rx_skb)) {
+                               pr_info("%s: B receive out of memory\n",
+                                       ch->is->name);
+                               ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                               break;
+                       }
+               }
+               rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb));
+               recv_Bchannel(&ch->bch, 0);
+               break;
+       case ISDN_P_B_HDLC:
+               if (!ch->bch.rx_skb) {
+                       ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+                                               GFP_ATOMIC);
+                       if (unlikely(!ch->bch.rx_skb)) {
+                               pr_info("%s: B receive out of memory\n",
+                                       ch->is->name);
+                               ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                               break;
+                       }
+               }
+               if ((ch->bch.rx_skb->len + ch->is->clsb) >
+                   (ch->bch.maxlen + 2)) {
+                       pr_debug("%s: incoming packet too large\n",
+                               ch->is->name);
+                       ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                       skb_trim(ch->bch.rx_skb, 0);
+                       break;
+               }
+               if (ch->is->cmsb & HDLC_ERROR) {
+                       pr_debug("%s: ISAR frame error %x len %d\n",
+                               ch->is->name, ch->is->cmsb, ch->is->clsb);
+#ifdef ERROR_STATISTIC
+                       if (ch->is->cmsb & HDLC_ERR_RER)
+                               ch->bch.err_inv++;
+                       if (ch->is->cmsb & HDLC_ERR_CER)
+                               ch->bch.err_crc++;
+#endif
+                       skb_trim(ch->bch.rx_skb, 0);
+                       ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                       break;
+               }
+               if (ch->is->cmsb & HDLC_FSD)
+                       skb_trim(ch->bch.rx_skb, 0);
+               ptr = skb_put(ch->bch.rx_skb, ch->is->clsb);
+               rcv_mbox(ch->is, ptr);
+               if (ch->is->cmsb & HDLC_FED) {
+                       if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */
+                               pr_debug("%s: ISAR frame to short %d\n",
+                                       ch->is->name, ch->bch.rx_skb->len);
+                               skb_trim(ch->bch.rx_skb, 0);
+                               break;
+                       }
+                       skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2);
+                       recv_Bchannel(&ch->bch, 0);
+               }
+               break;
+       case ISDN_P_B_T30_FAX:
+               if (ch->state != STFAX_ACTIV) {
+                       pr_debug("%s: isar_rcv_frame: not ACTIV\n",
+                               ch->is->name);
+                       ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                       if (ch->bch.rx_skb)
+                               skb_trim(ch->bch.rx_skb, 0);
+                       break;
+               }
+               if (!ch->bch.rx_skb) {
+                       ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+                                               GFP_ATOMIC);
+                       if (unlikely(!ch->bch.rx_skb)) {
+                               pr_info("%s: B receive out of memory\n",
+                                       __func__);
+                               ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                               break;
+                       }
+               }
+               if (ch->cmd == PCTRL_CMD_FRM) {
+                       rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb));
+                       pr_debug("%s: isar_rcv_frame: %d\n",
+                               ch->is->name, ch->bch.rx_skb->len);
+                       if (ch->is->cmsb & SART_NMD) { /* ABORT */
+                               pr_debug("%s: isar_rcv_frame: no more data\n",
+                                       ch->is->name);
+                               ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                               send_mbox(ch->is, SET_DPS(ch->dpath) |
+                                       ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+                                       0, NULL);
+                               ch->state = STFAX_ESCAPE;
+                               /* set_skb_flag(skb, DF_NOMOREDATA); */
+                       }
+                       recv_Bchannel(&ch->bch, 0);
+                       if (ch->is->cmsb & SART_NMD)
+                               deliver_status(ch, HW_MOD_NOCARR);
+                       break;
+               }
+               if (ch->cmd != PCTRL_CMD_FRH) {
+                       pr_debug("%s: isar_rcv_frame: unknown fax mode %x\n",
+                               ch->is->name, ch->cmd);
+                       ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                       if (ch->bch.rx_skb)
+                               skb_trim(ch->bch.rx_skb, 0);
+                       break;
+               }
+               /* PCTRL_CMD_FRH */
+               if ((ch->bch.rx_skb->len + ch->is->clsb) >
+                   (ch->bch.maxlen + 2)) {
+                       pr_info("%s: %s incoming packet too large\n",
+                               ch->is->name, __func__);
+                       ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                       skb_trim(ch->bch.rx_skb, 0);
+                       break;
+               }  else if (ch->is->cmsb & HDLC_ERROR) {
+                       pr_info("%s: ISAR frame error %x len %d\n",
+                               ch->is->name, ch->is->cmsb, ch->is->clsb);
+                       skb_trim(ch->bch.rx_skb, 0);
+                       ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                       break;
+               }
+               if (ch->is->cmsb & HDLC_FSD)
+                       skb_trim(ch->bch.rx_skb, 0);
+               ptr = skb_put(ch->bch.rx_skb, ch->is->clsb);
+               rcv_mbox(ch->is, ptr);
+               if (ch->is->cmsb & HDLC_FED) {
+                       if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */
+                               pr_info("%s: ISAR frame to short %d\n",
+                                       ch->is->name, ch->bch.rx_skb->len);
+                               skb_trim(ch->bch.rx_skb, 0);
+                               break;
+                       }
+                       skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2);
+                       recv_Bchannel(&ch->bch, 0);
+               }
+               if (ch->is->cmsb & SART_NMD) { /* ABORT */
+                       pr_debug("%s: isar_rcv_frame: no more data\n",
+                               ch->is->name);
+                       ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+                       if (ch->bch.rx_skb)
+                               skb_trim(ch->bch.rx_skb, 0);
+                       send_mbox(ch->is, SET_DPS(ch->dpath) |
+                               ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+                       ch->state = STFAX_ESCAPE;
+                       deliver_status(ch, HW_MOD_NOCARR);
+               }
+               break;
+       default:
+               pr_info("isar_rcv_frame protocol (%x)error\n", ch->bch.state);
+               ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+               break;
+       }
+}
+
+static void
+isar_fill_fifo(struct isar_ch *ch)
+{
+       int count;
+       u8 msb;
+       u8 *ptr;
+
+       pr_debug("%s: ch%d  tx_skb %p tx_idx %d\n",
+               ch->is->name, ch->bch.nr, ch->bch.tx_skb, ch->bch.tx_idx);
+       if (!ch->bch.tx_skb)
+               return;
+       count = ch->bch.tx_skb->len - ch->bch.tx_idx;
+       if (count <= 0)
+               return;
+       if (!(ch->is->bstat &
+               (ch->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+               return;
+       if (count > ch->mml) {
+               msb = 0;
+               count = ch->mml;
+       } else {
+               msb = HDLC_FED;
+       }
+       ptr = ch->bch.tx_skb->data + ch->bch.tx_idx;
+       if (!ch->bch.tx_idx) {
+               pr_debug("%s: frame start\n", ch->is->name);
+               if ((ch->bch.state == ISDN_P_B_T30_FAX) &&
+                       (ch->cmd == PCTRL_CMD_FTH)) {
+                       if (count > 1) {
+                               if ((ptr[0] == 0xff) && (ptr[1] == 0x13)) {
+                                       /* last frame */
+                                       test_and_set_bit(FLG_LASTDATA,
+                                               &ch->bch.Flags);
+                                       pr_debug("%s: set LASTDATA\n",
+                                               ch->is->name);
+                                       if (msb == HDLC_FED)
+                                               test_and_set_bit(FLG_DLEETX,
+                                                       &ch->bch.Flags);
+                               }
+                       }
+               }
+               msb |= HDLC_FST;
+       }
+       ch->bch.tx_idx += count;
+       switch (ch->bch.state) {
+       case ISDN_P_NONE:
+               pr_info("%s: wrong protocol 0\n", __func__);
+               break;
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_MODEM_ASYNC:
+               send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+                       0, count, ptr);
+               break;
+       case ISDN_P_B_HDLC:
+               send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+                       msb, count, ptr);
+               break;
+       case ISDN_P_B_T30_FAX:
+               if (ch->state != STFAX_ACTIV)
+                       pr_debug("%s: not ACTIV\n", ch->is->name);
+               else if (ch->cmd == PCTRL_CMD_FTH)
+                       send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+                               msb, count, ptr);
+               else if (ch->cmd == PCTRL_CMD_FTM)
+                       send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+                               0, count, ptr);
+               else
+                       pr_debug("%s: not FTH/FTM\n", ch->is->name);
+               break;
+       default:
+               pr_info("%s: protocol(%x) error\n",
+                       __func__, ch->bch.state);
+               break;
+       }
+}
+
+static inline struct isar_ch *
+sel_bch_isar(struct isar_hw *isar, u8 dpath)
+{
+       struct isar_ch  *base = &isar->ch[0];
+
+       if ((!dpath) || (dpath > 2))
+               return NULL;
+       if (base->dpath == dpath)
+               return base;
+       base++;
+       if (base->dpath == dpath)
+               return base;
+       return NULL;
+}
+
+static void
+send_next(struct isar_ch *ch)
+{
+       pr_debug("%s: %s ch%d tx_skb %p tx_idx %d\n",
+               ch->is->name, __func__, ch->bch.nr,
+               ch->bch.tx_skb, ch->bch.tx_idx);
+       if (ch->bch.state == ISDN_P_B_T30_FAX) {
+               if (ch->cmd == PCTRL_CMD_FTH) {
+                       if (test_bit(FLG_LASTDATA, &ch->bch.Flags)) {
+                               pr_debug("set NMD_DATA\n");
+                               test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags);
+                       }
+               } else if (ch->cmd == PCTRL_CMD_FTM) {
+                       if (test_bit(FLG_DLEETX, &ch->bch.Flags)) {
+                               test_and_set_bit(FLG_LASTDATA, &ch->bch.Flags);
+                               test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags);
+                       }
+               }
+       }
+       if (ch->bch.tx_skb) {
+               /* send confirm, on trans, free on hdlc. */
+               if (test_bit(FLG_TRANSPARENT, &ch->bch.Flags))
+                       confirm_Bsend(&ch->bch);
+               dev_kfree_skb(ch->bch.tx_skb);
+       }
+       if (get_next_bframe(&ch->bch))
+               isar_fill_fifo(ch);
+       else {
+               if (test_and_clear_bit(FLG_DLEETX, &ch->bch.Flags)) {
+                       if (test_and_clear_bit(FLG_LASTDATA,
+                           &ch->bch.Flags)) {
+                               if (test_and_clear_bit(FLG_NMD_DATA,
+                                   &ch->bch.Flags)) {
+                                       u8 zd = 0;
+                                       send_mbox(ch->is, SET_DPS(ch->dpath) |
+                                               ISAR_HIS_SDATA, 0x01, 1, &zd);
+                               }
+                               test_and_set_bit(FLG_LL_OK, &ch->bch.Flags);
+                       } else {
+                               deliver_status(ch, HW_MOD_CONNECT);
+                       }
+               }
+       }
+}
+
+static void
+check_send(struct isar_hw *isar, u8 rdm)
+{
+       struct isar_ch  *ch;
+
+       pr_debug("%s: rdm %x\n", isar->name, rdm);
+       if (rdm & BSTAT_RDM1) {
+               ch = sel_bch_isar(isar, 1);
+               if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) {
+                       if (ch->bch.tx_skb && (ch->bch.tx_skb->len >
+                           ch->bch.tx_idx))
+                               isar_fill_fifo(ch);
+                       else
+                               send_next(ch);
+               }
+       }
+       if (rdm & BSTAT_RDM2) {
+               ch = sel_bch_isar(isar, 2);
+               if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) {
+                       if (ch->bch.tx_skb && (ch->bch.tx_skb->len >
+                           ch->bch.tx_idx))
+                               isar_fill_fifo(ch);
+                       else
+                               send_next(ch);
+               }
+       }
+}
+
+const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
+                       "300", "600", "1200", "2400", "4800", "7200",
+                       "9600nt", "9600t", "12000", "14400", "WRONG"};
+const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
+                       "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"};
+
+static void
+isar_pump_status_rsp(struct isar_ch *ch) {
+       u8 ril = ch->is->buf[0];
+       u8 rim;
+
+       if (!test_and_clear_bit(ISAR_RATE_REQ, &ch->is->Flags))
+               return;
+       if (ril > 14) {
+               pr_info("%s: wrong pstrsp ril=%d\n", ch->is->name, ril);
+               ril = 15;
+       }
+       switch (ch->is->buf[1]) {
+       case 0:
+               rim = 0;
+               break;
+       case 0x20:
+               rim = 2;
+               break;
+       case 0x40:
+               rim = 3;
+               break;
+       case 0x41:
+               rim = 4;
+               break;
+       case 0x51:
+               rim = 5;
+               break;
+       case 0x61:
+               rim = 6;
+               break;
+       case 0x71:
+               rim = 7;
+               break;
+       case 0x82:
+               rim = 8;
+               break;
+       case 0x92:
+               rim = 9;
+               break;
+       case 0xa2:
+               rim = 10;
+               break;
+       default:
+               rim = 1;
+               break;
+       }
+       sprintf(ch->conmsg, "%s %s", dmril[ril], dmrim[rim]);
+       pr_debug("%s: pump strsp %s\n", ch->is->name, ch->conmsg);
+}
+
+static void
+isar_pump_statev_modem(struct isar_ch *ch, u8 devt) {
+       u8 dps = SET_DPS(ch->dpath);
+
+       switch (devt) {
+       case PSEV_10MS_TIMER:
+               pr_debug("%s: pump stev TIMER\n", ch->is->name);
+               break;
+       case PSEV_CON_ON:
+               pr_debug("%s: pump stev CONNECT\n", ch->is->name);
+               deliver_status(ch, HW_MOD_CONNECT);
+               break;
+       case PSEV_CON_OFF:
+               pr_debug("%s: pump stev NO CONNECT\n", ch->is->name);
+               send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+               deliver_status(ch, HW_MOD_NOCARR);
+               break;
+       case PSEV_V24_OFF:
+               pr_debug("%s: pump stev V24 OFF\n", ch->is->name);
+               break;
+       case PSEV_CTS_ON:
+               pr_debug("%s: pump stev CTS ON\n", ch->is->name);
+               break;
+       case PSEV_CTS_OFF:
+               pr_debug("%s pump stev CTS OFF\n", ch->is->name);
+               break;
+       case PSEV_DCD_ON:
+               pr_debug("%s: pump stev CARRIER ON\n", ch->is->name);
+               test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags);
+               send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+               break;
+       case PSEV_DCD_OFF:
+               pr_debug("%s: pump stev CARRIER OFF\n", ch->is->name);
+               break;
+       case PSEV_DSR_ON:
+               pr_debug("%s: pump stev DSR ON\n", ch->is->name);
+               break;
+       case PSEV_DSR_OFF:
+               pr_debug("%s: pump stev DSR_OFF\n", ch->is->name);
+               break;
+       case PSEV_REM_RET:
+               pr_debug("%s: pump stev REMOTE RETRAIN\n", ch->is->name);
+               break;
+       case PSEV_REM_REN:
+               pr_debug("%s: pump stev REMOTE RENEGOTIATE\n", ch->is->name);
+               break;
+       case PSEV_GSTN_CLR:
+               pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name);
+               break;
+       default:
+               pr_info("u%s: nknown pump stev %x\n", ch->is->name, devt);
+               break;
+       }
+}
+
+static void
+isar_pump_statev_fax(struct isar_ch *ch, u8 devt) {
+       u8 dps = SET_DPS(ch->dpath);
+       u8 p1;
+
+       switch (devt) {
+       case PSEV_10MS_TIMER:
+               pr_debug("%s: pump stev TIMER\n", ch->is->name);
+               break;
+       case PSEV_RSP_READY:
+               pr_debug("%s: pump stev RSP_READY\n", ch->is->name);
+               ch->state = STFAX_READY;
+               deliver_status(ch, HW_MOD_READY);
+#ifdef AUTOCON
+               if (test_bit(BC_FLG_ORIG, &ch->bch.Flags))
+                       isar_pump_cmd(bch, HW_MOD_FRH, 3);
+               else
+                       isar_pump_cmd(bch, HW_MOD_FTH, 3);
+#endif
+               break;
+       case PSEV_LINE_TX_H:
+               if (ch->state == STFAX_LINE) {
+                       pr_debug("%s: pump stev LINE_TX_H\n", ch->is->name);
+                       ch->state = STFAX_CONT;
+                       send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                               PCTRL_CMD_CONT, 0, NULL);
+               } else {
+                       pr_debug("%s: pump stev LINE_TX_H wrong st %x\n",
+                               ch->is->name, ch->state);
+               }
+               break;
+       case PSEV_LINE_RX_H:
+               if (ch->state == STFAX_LINE) {
+                       pr_debug("%s: pump stev LINE_RX_H\n", ch->is->name);
+                       ch->state = STFAX_CONT;
+                       send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                               PCTRL_CMD_CONT, 0, NULL);
+               } else {
+                       pr_debug("%s: pump stev LINE_RX_H wrong st %x\n",
+                               ch->is->name, ch->state);
+               }
+               break;
+       case PSEV_LINE_TX_B:
+               if (ch->state == STFAX_LINE) {
+                       pr_debug("%s: pump stev LINE_TX_B\n", ch->is->name);
+                       ch->state = STFAX_CONT;
+                       send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                               PCTRL_CMD_CONT, 0, NULL);
+               } else {
+                       pr_debug("%s: pump stev LINE_TX_B wrong st %x\n",
+                               ch->is->name, ch->state);
+               }
+               break;
+       case PSEV_LINE_RX_B:
+               if (ch->state == STFAX_LINE) {
+                       pr_debug("%s: pump stev LINE_RX_B\n", ch->is->name);
+                       ch->state = STFAX_CONT;
+                       send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                               PCTRL_CMD_CONT, 0, NULL);
+               } else {
+                       pr_debug("%s: pump stev LINE_RX_B wrong st %x\n",
+                               ch->is->name, ch->state);
+               }
+               break;
+       case PSEV_RSP_CONN:
+               if (ch->state == STFAX_CONT) {
+                       pr_debug("%s: pump stev RSP_CONN\n", ch->is->name);
+                       ch->state = STFAX_ACTIV;
+                       test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags);
+                       send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+                       if (ch->cmd == PCTRL_CMD_FTH) {
+                               int delay = (ch->mod == 3) ? 1000 : 200;
+                               /* 1s (200 ms) Flags before data */
+                               if (test_and_set_bit(FLG_FTI_RUN,
+                                   &ch->bch.Flags))
+                                       del_timer(&ch->ftimer);
+                               ch->ftimer.expires =
+                                       jiffies + ((delay * HZ)/1000);
+                               test_and_set_bit(FLG_LL_CONN,
+                                       &ch->bch.Flags);
+                               add_timer(&ch->ftimer);
+                       } else {
+                               deliver_status(ch, HW_MOD_CONNECT);
+                       }
+               } else {
+                       pr_debug("%s: pump stev RSP_CONN wrong st %x\n",
+                               ch->is->name, ch->state);
+               }
+               break;
+       case PSEV_FLAGS_DET:
+               pr_debug("%s: pump stev FLAGS_DET\n", ch->is->name);
+               break;
+       case PSEV_RSP_DISC:
+               pr_debug("%s: pump stev RSP_DISC state(%d)\n",
+                       ch->is->name, ch->state);
+               if (ch->state == STFAX_ESCAPE) {
+                       p1 = 5;
+                       switch (ch->newcmd) {
+                       case 0:
+                               ch->state = STFAX_READY;
+                               break;
+                       case PCTRL_CMD_FTM:
+                               p1 = 2;
+                       case PCTRL_CMD_FTH:
+                               send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                                       PCTRL_CMD_SILON, 1, &p1);
+                               ch->state = STFAX_SILDET;
+                               break;
+                       case PCTRL_CMD_FRH:
+                       case PCTRL_CMD_FRM:
+                               ch->mod = ch->newmod;
+                               p1 = ch->newmod;
+                               ch->newmod = 0;
+                               ch->cmd = ch->newcmd;
+                               ch->newcmd = 0;
+                               send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                                       ch->cmd, 1, &p1);
+                               ch->state = STFAX_LINE;
+                               ch->try_mod = 3;
+                               break;
+                       default:
+                               pr_debug("%s: RSP_DISC unknown newcmd %x\n",
+                                       ch->is->name, ch->newcmd);
+                               break;
+                       }
+               } else if (ch->state == STFAX_ACTIV) {
+                       if (test_and_clear_bit(FLG_LL_OK, &ch->bch.Flags))
+                               deliver_status(ch, HW_MOD_OK);
+                       else if (ch->cmd == PCTRL_CMD_FRM)
+                               deliver_status(ch, HW_MOD_NOCARR);
+                       else
+                               deliver_status(ch, HW_MOD_FCERROR);
+                       ch->state = STFAX_READY;
+               } else if (ch->state != STFAX_SILDET) {
+                       /* ignore in STFAX_SILDET */
+                       ch->state = STFAX_READY;
+                       deliver_status(ch, HW_MOD_FCERROR);
+               }
+               break;
+       case PSEV_RSP_SILDET:
+               pr_debug("%s: pump stev RSP_SILDET\n", ch->is->name);
+               if (ch->state == STFAX_SILDET) {
+                       ch->mod = ch->newmod;
+                       p1 = ch->newmod;
+                       ch->newmod = 0;
+                       ch->cmd = ch->newcmd;
+                       ch->newcmd = 0;
+                       send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                               ch->cmd, 1, &p1);
+                       ch->state = STFAX_LINE;
+                       ch->try_mod = 3;
+               }
+               break;
+       case PSEV_RSP_SILOFF:
+               pr_debug("%s: pump stev RSP_SILOFF\n", ch->is->name);
+               break;
+       case PSEV_RSP_FCERR:
+               if (ch->state == STFAX_LINE) {
+                       pr_debug("%s: pump stev RSP_FCERR try %d\n",
+                               ch->is->name, ch->try_mod);
+                       if (ch->try_mod--) {
+                               send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+                                       ch->cmd, 1, &ch->mod);
+                               break;
+                       }
+               }
+               pr_debug("%s: pump stev RSP_FCERR\n", ch->is->name);
+               ch->state = STFAX_ESCAPE;
+               send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+                       0, NULL);
+               deliver_status(ch, HW_MOD_FCERROR);
+               break;
+       default:
+               break;
+       }
+}
+
+void
+mISDNisar_irq(struct isar_hw *isar)
+{
+       struct isar_ch *ch;
+
+       get_irq_infos(isar);
+       switch (isar->iis & ISAR_IIS_MSCMSD) {
+       case ISAR_IIS_RDATA:
+               ch = sel_bch_isar(isar, isar->iis >> 6);
+               if (ch)
+                       isar_rcv_frame(ch);
+               else {
+                       pr_debug("%s: ISAR spurious IIS_RDATA %x/%x/%x\n",
+                               isar->name, isar->iis, isar->cmsb,
+                               isar->clsb);
+                       isar->write_reg(isar->hw, ISAR_IIA, 0);
+               }
+               break;
+       case ISAR_IIS_GSTEV:
+               isar->write_reg(isar->hw, ISAR_IIA, 0);
+               isar->bstat |= isar->cmsb;
+               check_send(isar, isar->cmsb);
+               break;
+       case ISAR_IIS_BSTEV:
+#ifdef ERROR_STATISTIC
+               ch = sel_bch_isar(isar, isar->iis >> 6);
+               if (ch) {
+                       if (isar->cmsb == BSTEV_TBO)
+                               ch->bch.err_tx++;
+                       if (isar->cmsb == BSTEV_RBO)
+                               ch->bch.err_rdo++;
+               }
+#endif
+               pr_debug("%s: Buffer STEV dpath%d msb(%x)\n",
+                       isar->name, isar->iis>>6, isar->cmsb);
+               isar->write_reg(isar->hw, ISAR_IIA, 0);
+               break;
+       case ISAR_IIS_PSTEV:
+               ch = sel_bch_isar(isar, isar->iis >> 6);
+               if (ch) {
+                       rcv_mbox(isar, NULL);
+                       if (ch->bch.state == ISDN_P_B_MODEM_ASYNC)
+                               isar_pump_statev_modem(ch, isar->cmsb);
+                       else if (ch->bch.state == ISDN_P_B_T30_FAX)
+                               isar_pump_statev_fax(ch, isar->cmsb);
+                       else if (ch->bch.state == ISDN_P_B_RAW) {
+                               int     tt;
+                               tt = isar->cmsb | 0x30;
+                               if (tt == 0x3e)
+                                       tt = '*';
+                               else if (tt == 0x3f)
+                                       tt = '#';
+                               else if (tt > '9')
+                                       tt += 7;
+                               tt |= DTMF_TONE_VAL;
+                               _queue_data(&ch->bch.ch, PH_CONTROL_IND,
+                                       MISDN_ID_ANY, sizeof(tt), &tt,
+                                       GFP_ATOMIC);
+                       } else
+                               pr_debug("%s: ISAR IIS_PSTEV pm %d sta %x\n",
+                                       isar->name, ch->bch.state,
+                                       isar->cmsb);
+               } else {
+                       pr_debug("%s: ISAR spurious IIS_PSTEV %x/%x/%x\n",
+                               isar->name, isar->iis, isar->cmsb,
+                               isar->clsb);
+                       isar->write_reg(isar->hw, ISAR_IIA, 0);
+               }
+               break;
+       case ISAR_IIS_PSTRSP:
+               ch = sel_bch_isar(isar, isar->iis >> 6);
+               if (ch) {
+                       rcv_mbox(isar, NULL);
+                       isar_pump_status_rsp(ch);
+               } else {
+                       pr_debug("%s: ISAR spurious IIS_PSTRSP %x/%x/%x\n",
+                               isar->name, isar->iis, isar->cmsb,
+                               isar->clsb);
+                       isar->write_reg(isar->hw, ISAR_IIA, 0);
+               }
+               break;
+       case ISAR_IIS_DIAG:
+       case ISAR_IIS_BSTRSP:
+       case ISAR_IIS_IOM2RSP:
+               rcv_mbox(isar, NULL);
+               break;
+       case ISAR_IIS_INVMSG:
+               rcv_mbox(isar, NULL);
+               pr_debug("%s: invalid msg his:%x\n", isar->name, isar->cmsb);
+               break;
+       default:
+               rcv_mbox(isar, NULL);
+               pr_debug("%s: unhandled msg iis(%x) ctrl(%x/%x)\n",
+                       isar->name, isar->iis, isar->cmsb, isar->clsb);
+               break;
+       }
+}
+EXPORT_SYMBOL(mISDNisar_irq);
+
+static void
+ftimer_handler(unsigned long data)
+{
+       struct isar_ch *ch = (struct isar_ch *)data;
+
+       pr_debug("%s: ftimer flags %lx\n", ch->is->name, ch->bch.Flags);
+       test_and_clear_bit(FLG_FTI_RUN, &ch->bch.Flags);
+       if (test_and_clear_bit(FLG_LL_CONN, &ch->bch.Flags))
+               deliver_status(ch, HW_MOD_CONNECT);
+}
+
+static void
+setup_pump(struct isar_ch *ch) {
+       u8 dps = SET_DPS(ch->dpath);
+       u8 ctrl, param[6];
+
+       switch (ch->bch.state) {
+       case ISDN_P_NONE:
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+               send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
+               break;
+       case ISDN_P_B_L2DTMF:
+               if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) {
+                       param[0] = 5; /* TOA 5 db */
+                       send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG,
+                               PMOD_DTMF_TRANS, 1, param);
+               } else {
+                       param[0] = 40; /* REL -46 dbm */
+                       send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG,
+                               PMOD_DTMF, 1, param);
+               }
+       case ISDN_P_B_MODEM_ASYNC:
+               ctrl = PMOD_DATAMODEM;
+               if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) {
+                       ctrl |= PCTRL_ORIG;
+                       param[5] = PV32P6_CTN;
+               } else {
+                       param[5] = PV32P6_ATN;
+               }
+               param[0] = 6; /* 6 db */
+               param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B |
+                       PV32P2_V22C | PV32P2_V21 | PV32P2_BEL;
+               param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B;
+               param[3] = PV32P4_UT144;
+               param[4] = PV32P5_UT144;
+               send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
+               break;
+       case ISDN_P_B_T30_FAX:
+               ctrl = PMOD_FAX;
+               if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) {
+                       ctrl |= PCTRL_ORIG;
+                       param[1] = PFAXP2_CTN;
+               } else {
+                       param[1] = PFAXP2_ATN;
+               }
+               param[0] = 6; /* 6 db */
+               send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
+               ch->state = STFAX_NULL;
+               ch->newcmd = 0;
+               ch->newmod = 0;
+               test_and_set_bit(FLG_FTI_RUN, &ch->bch.Flags);
+               break;
+       }
+       udelay(1000);
+       send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+       udelay(1000);
+}
+
+static void
+setup_sart(struct isar_ch *ch) {
+       u8 dps = SET_DPS(ch->dpath);
+       u8 ctrl, param[2] = {0, 0};
+
+       switch (ch->bch.state) {
+       case ISDN_P_NONE:
+               send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE,
+                       0, NULL);
+               break;
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_L2DTMF:
+               send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_BINARY,
+                       2, param);
+               break;
+       case ISDN_P_B_HDLC:
+       case ISDN_P_B_T30_FAX:
+               send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_HDLC,
+                       1, param);
+               break;
+       case ISDN_P_B_MODEM_ASYNC:
+               ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
+               param[0] = S_P1_CHS_8;
+               param[1] = S_P2_BFT_DEF;
+               send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, ctrl, 2, param);
+               break;
+       }
+       udelay(1000);
+       send_mbox(ch->is, dps | ISAR_HIS_BSTREQ, 0, 0, NULL);
+       udelay(1000);
+}
+
+static void
+setup_iom2(struct isar_ch *ch) {
+       u8 dps = SET_DPS(ch->dpath);
+       u8 cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD, 0, 0, 0, 0};
+
+       if (ch->bch.nr == 2) {
+               msg[1] = 1;
+               msg[3] = 1;
+       }
+       switch (ch->bch.state) {
+       case ISDN_P_NONE:
+               cmsb = 0;
+               /* dummy slot */
+               msg[1] = ch->dpath + 2;
+               msg[3] = ch->dpath + 2;
+               break;
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+               break;
+       case ISDN_P_B_MODEM_ASYNC:
+       case ISDN_P_B_T30_FAX:
+               cmsb |= IOM_CTRL_RCV;
+       case ISDN_P_B_L2DTMF:
+               if (test_bit(FLG_DTMFSEND, &ch->bch.Flags))
+                       cmsb |= IOM_CTRL_RCV;
+               cmsb |= IOM_CTRL_ALAW;
+               break;
+       }
+       send_mbox(ch->is, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg);
+       udelay(1000);
+       send_mbox(ch->is, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL);
+       udelay(1000);
+}
+
+static int
+modeisar(struct isar_ch *ch, u32 bprotocol)
+{
+       /* Here we are selecting the best datapath for requested protocol */
+       if (ch->bch.state == ISDN_P_NONE) { /* New Setup */
+               switch (bprotocol) {
+               case ISDN_P_NONE: /* init */
+                       if (!ch->dpath)
+                               /* no init for dpath 0 */
+                               return 0;
+                       test_and_clear_bit(FLG_HDLC, &ch->bch.Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT, &ch->bch.Flags);
+                       break;
+               case ISDN_P_B_RAW:
+               case ISDN_P_B_HDLC:
+                       /* best is datapath 2 */
+                       if (!test_and_set_bit(ISAR_DP2_USE, &ch->is->Flags))
+                               ch->dpath = 2;
+                       else if (!test_and_set_bit(ISAR_DP1_USE,
+                           &ch->is->Flags))
+                               ch->dpath = 1;
+                       else {
+                               pr_info("modeisar both pathes in use\n");
+                               return -EBUSY;
+                       }
+                       if (bprotocol == ISDN_P_B_HDLC)
+                               test_and_set_bit(FLG_HDLC, &ch->bch.Flags);
+                       else
+                               test_and_set_bit(FLG_TRANSPARENT,
+                                       &ch->bch.Flags);
+                       break;
+               case ISDN_P_B_MODEM_ASYNC:
+               case ISDN_P_B_T30_FAX:
+               case ISDN_P_B_L2DTMF:
+                       /* only datapath 1 */
+                       if (!test_and_set_bit(ISAR_DP1_USE, &ch->is->Flags))
+                               ch->dpath = 1;
+                       else {
+                               pr_info("%s: ISAR modeisar analog functions"
+                                       "only with DP1\n", ch->is->name);
+                               return -EBUSY;
+                       }
+                       break;
+               default:
+                       pr_info("%s: protocol not known %x\n", ch->is->name,
+                               bprotocol);
+                       return -ENOPROTOOPT;
+               }
+       }
+       pr_debug("%s: ISAR ch%d dp%d protocol %x->%x\n", ch->is->name,
+               ch->bch.nr, ch->dpath, ch->bch.state, bprotocol);
+       ch->bch.state = bprotocol;
+       setup_pump(ch);
+       setup_iom2(ch);
+       setup_sart(ch);
+       if (ch->bch.state == ISDN_P_NONE) {
+               /* Clear resources */
+               if (ch->dpath == 1)
+                       test_and_clear_bit(ISAR_DP1_USE, &ch->is->Flags);
+               else if (ch->dpath == 2)
+                       test_and_clear_bit(ISAR_DP2_USE, &ch->is->Flags);
+               ch->dpath = 0;
+               ch->is->ctrl(ch->is->hw, HW_DEACT_IND, ch->bch.nr);
+       } else
+               ch->is->ctrl(ch->is->hw, HW_ACTIVATE_IND, ch->bch.nr);
+       return 0;
+}
+
+static void
+isar_pump_cmd(struct isar_ch *ch, u32 cmd, u8 para)
+{
+       u8 dps = SET_DPS(ch->dpath);
+       u8 ctrl = 0, nom = 0, p1 = 0;
+
+       pr_debug("%s: isar_pump_cmd %x/%x state(%x)\n",
+               ch->is->name, cmd, para, ch->bch.state);
+       switch (cmd) {
+       case HW_MOD_FTM:
+               if (ch->state == STFAX_READY) {
+                       p1 = para;
+                       ctrl = PCTRL_CMD_FTM;
+                       nom = 1;
+                       ch->state = STFAX_LINE;
+                       ch->cmd = ctrl;
+                       ch->mod = para;
+                       ch->newmod = 0;
+                       ch->newcmd = 0;
+                       ch->try_mod = 3;
+               } else if ((ch->state == STFAX_ACTIV) &&
+                   (ch->cmd == PCTRL_CMD_FTM) && (ch->mod == para))
+                       deliver_status(ch, HW_MOD_CONNECT);
+               else {
+                       ch->newmod = para;
+                       ch->newcmd = PCTRL_CMD_FTM;
+                       nom = 0;
+                       ctrl = PCTRL_CMD_ESC;
+                       ch->state = STFAX_ESCAPE;
+               }
+               break;
+       case HW_MOD_FTH:
+               if (ch->state == STFAX_READY) {
+                       p1 = para;
+                       ctrl = PCTRL_CMD_FTH;
+                       nom = 1;
+                       ch->state = STFAX_LINE;
+                       ch->cmd = ctrl;
+                       ch->mod = para;
+                       ch->newmod = 0;
+                       ch->newcmd = 0;
+                       ch->try_mod = 3;
+               } else if ((ch->state == STFAX_ACTIV) &&
+                   (ch->cmd == PCTRL_CMD_FTH) && (ch->mod == para))
+                               deliver_status(ch, HW_MOD_CONNECT);
+               else {
+                       ch->newmod = para;
+                       ch->newcmd = PCTRL_CMD_FTH;
+                       nom = 0;
+                       ctrl = PCTRL_CMD_ESC;
+                       ch->state = STFAX_ESCAPE;
+               }
+               break;
+       case HW_MOD_FRM:
+               if (ch->state == STFAX_READY) {
+                       p1 = para;
+                       ctrl = PCTRL_CMD_FRM;
+                       nom = 1;
+                       ch->state = STFAX_LINE;
+                       ch->cmd = ctrl;
+                       ch->mod = para;
+                       ch->newmod = 0;
+                       ch->newcmd = 0;
+                       ch->try_mod = 3;
+               } else if ((ch->state == STFAX_ACTIV) &&
+                   (ch->cmd == PCTRL_CMD_FRM) && (ch->mod == para))
+                       deliver_status(ch, HW_MOD_CONNECT);
+               else {
+                       ch->newmod = para;
+                       ch->newcmd = PCTRL_CMD_FRM;
+                       nom = 0;
+                       ctrl = PCTRL_CMD_ESC;
+                       ch->state = STFAX_ESCAPE;
+               }
+               break;
+       case HW_MOD_FRH:
+               if (ch->state == STFAX_READY) {
+                       p1 = para;
+                       ctrl = PCTRL_CMD_FRH;
+                       nom = 1;
+                       ch->state = STFAX_LINE;
+                       ch->cmd = ctrl;
+                       ch->mod = para;
+                       ch->newmod = 0;
+                       ch->newcmd = 0;
+                       ch->try_mod = 3;
+               } else if ((ch->state == STFAX_ACTIV) &&
+                   (ch->cmd == PCTRL_CMD_FRH) && (ch->mod == para))
+                       deliver_status(ch, HW_MOD_CONNECT);
+               else {
+                       ch->newmod = para;
+                       ch->newcmd = PCTRL_CMD_FRH;
+                       nom = 0;
+                       ctrl = PCTRL_CMD_ESC;
+                       ch->state = STFAX_ESCAPE;
+               }
+               break;
+       case PCTRL_CMD_TDTMF:
+               p1 = para;
+               nom = 1;
+               ctrl = PCTRL_CMD_TDTMF;
+               break;
+       }
+       if (ctrl)
+               send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
+}
+
+static void
+isar_setup(struct isar_hw *isar)
+{
+       u8 msg;
+       int i;
+
+       /* Dpath 1, 2 */
+       msg = 61;
+       for (i = 0; i < 2; i++) {
+               /* Buffer Config */
+               send_mbox(isar, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+                       ISAR_HIS_P12CFG, 4, 1, &msg);
+               isar->ch[i].mml = msg;
+               isar->ch[i].bch.state = 0;
+               isar->ch[i].dpath = i + 1;
+               modeisar(&isar->ch[i], ISDN_P_NONE);
+       }
+}
+
+static int
+isar_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct isar_ch *ich = container_of(bch, struct isar_ch, bch);
+       int ret = -EINVAL;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       u32 id, *val;
+       u_long flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(ich->is->hwlock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       ret = 0;
+                       isar_fill_fifo(ich);
+                       spin_unlock_irqrestore(ich->is->hwlock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(ich->is->hwlock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(ich->is->hwlock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = modeisar(ich, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(ich->is->hwlock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               spin_lock_irqsave(ich->is->hwlock, flags);
+               mISDN_clear_bchannel(bch);
+               modeisar(ich, ISDN_P_NONE);
+               spin_unlock_irqrestore(ich->is->hwlock, flags);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       case PH_CONTROL_REQ:
+               val = (u32 *)skb->data;
+               pr_debug("%s: PH_CONTROL | REQUEST %x/%x\n", ich->is->name,
+                       hh->id, *val);
+               if ((hh->id == 0) && ((*val & ~DTMF_TONE_MASK) ==
+                   DTMF_TONE_VAL)) {
+                       if (bch->state == ISDN_P_B_L2DTMF) {
+                               char tt = *val & DTMF_TONE_MASK;
+
+                               if (tt == '*')
+                                       tt = 0x1e;
+                               else if (tt == '#')
+                                       tt = 0x1f;
+                               else if (tt > '9')
+                                       tt -= 7;
+                               tt &= 0x1f;
+                               spin_lock_irqsave(ich->is->hwlock, flags);
+                               isar_pump_cmd(ich, PCTRL_CMD_TDTMF, tt);
+                               spin_unlock_irqrestore(ich->is->hwlock, flags);
+                       } else {
+                               pr_info("%s: DTMF send wrong protocol %x\n",
+                                       __func__, bch->state);
+                               return -EINVAL;
+                       }
+               } else if ((hh->id == HW_MOD_FRM) || (hh->id == HW_MOD_FRH) ||
+                   (hh->id == HW_MOD_FTM) || (hh->id == HW_MOD_FTH)) {
+                       for (id = 0; id < FAXMODCNT; id++)
+                               if (faxmodulation[id] == *val)
+                                       break;
+                       if ((FAXMODCNT > id) &&
+                           test_bit(FLG_INITIALIZED, &bch->Flags)) {
+                               pr_debug("%s: isar: new mod\n", ich->is->name);
+                               isar_pump_cmd(ich, hh->id, *val);
+                               ret = 0;
+                       } else {
+                               pr_info("%s: wrong modulation\n",
+                                       ich->is->name);
+                               ret = -EINVAL;
+                       }
+               } else if (hh->id == HW_MOD_LASTDATA)
+                       test_and_set_bit(FLG_DLEETX, &bch->Flags);
+               else {
+                       pr_info("%s: unknown PH_CONTROL_REQ %x\n",
+                               ich->is->name, hh->id);
+                       ret = -EINVAL;
+               }
+       default:
+               pr_info("%s: %s unknown prim(%x,%x)\n",
+                       ich->is->name, __func__, hh->prim, hh->id);
+               ret = -EINVAL;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       /* Nothing implemented yet */
+       case MISDN_CTRL_FILL_EMPTY:
+       default:
+               pr_info("%s: unknown Op %x\n", __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct isar_ch *ich = container_of(bch, struct isar_ch, bch);
+       int ret = -EINVAL;
+       u_long flags;
+
+       pr_debug("%s: %s cmd:%x %p\n", ich->is->name, __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       spin_lock_irqsave(ich->is->hwlock, flags);
+                       mISDN_freebchannel(bch);
+                       modeisar(ich, ISDN_P_NONE);
+                       spin_unlock_irqrestore(ich->is->hwlock, flags);
+               } else {
+                       skb_queue_purge(&bch->rqueue);
+                       bch->rcount = 0;
+               }
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(ich->is->owner);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bch, arg);
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x)\n",
+                       ich->is->name, __func__, cmd);
+       }
+       return ret;
+}
+
+static void
+free_isar(struct isar_hw *isar)
+{
+       modeisar(&isar->ch[0], ISDN_P_NONE);
+       modeisar(&isar->ch[1], ISDN_P_NONE);
+       del_timer(&isar->ch[0].ftimer);
+       del_timer(&isar->ch[1].ftimer);
+       test_and_clear_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags);
+       test_and_clear_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags);
+}
+
+static int
+init_isar(struct isar_hw *isar)
+{
+       int     cnt = 3;
+
+       while (cnt--) {
+               isar->version = ISARVersion(isar);
+               if (isar->ch[0].bch.debug & DEBUG_HW)
+                       pr_notice("%s: Testing version %d (%d time)\n",
+                               isar->name, isar->version, 3 - cnt);
+               if (isar->version == 1)
+                       break;
+               isar->ctrl(isar->hw, HW_RESET_REQ, 0);
+       }
+       if (isar->version != 1)
+               return -EINVAL;
+       isar->ch[0].ftimer.function = &ftimer_handler;
+       isar->ch[0].ftimer.data = (long)&isar->ch[0];
+       init_timer(&isar->ch[0].ftimer);
+       test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags);
+       isar->ch[1].ftimer.function = &ftimer_handler;
+       isar->ch[1].ftimer.data = (long)&isar->ch[1];
+       init_timer(&isar->ch[1].ftimer);
+       test_and_set_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags);
+       return 0;
+}
+
+static int
+isar_open(struct isar_hw *isar, struct channel_req *rq)
+{
+       struct bchannel         *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &isar->ch[rq->adr.channel - 1].bch;
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       return 0;
+}
+
+u32
+mISDNisar_init(struct isar_hw *isar, void *hw)
+{
+       u32 ret, i;
+
+       isar->hw = hw;
+       for (i = 0; i < 2; i++) {
+               isar->ch[i].bch.nr = i + 1;
+               mISDN_initbchannel(&isar->ch[i].bch, MAX_DATA_MEM);
+               isar->ch[i].bch.ch.nr = i + 1;
+               isar->ch[i].bch.ch.send = &isar_l2l1;
+               isar->ch[i].bch.ch.ctrl = isar_bctrl;
+               isar->ch[i].bch.hw = hw;
+               isar->ch[i].is = isar;
+       }
+
+       isar->init = &init_isar;
+       isar->release = &free_isar;
+       isar->firmware = &load_firmware;
+       isar->open = &isar_open;
+
+       ret =   (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK));
+
+       return ret;
+}
+EXPORT_SYMBOL(mISDNisar_init);
+
+static int isar_mod_init(void)
+{
+       pr_notice("mISDN: ISAR driver Rev. %s\n", ISAR_REV);
+       return 0;
+}
+
+static void isar_mod_cleanup(void)
+{
+       pr_notice("mISDN: ISAR module unloaded\n");
+}
+module_init(isar_mod_init);
+module_exit(isar_mod_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c
new file mode 100644 (file)
index 0000000..6c1b164
--- /dev/null
@@ -0,0 +1,1156 @@
+/*
+ * NETJet mISDN driver
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+#include "iohelper.h"
+#include "netjet.h"
+#include <linux/isdn/hdlc.h>
+
+#define NETJET_REV     "2.0"
+
+enum nj_types {
+       NETJET_S_TJ300,
+       NETJET_S_TJ320,
+       ENTERNOW__TJ320,
+};
+
+struct tiger_dma {
+       size_t          size;
+       u32             *start;
+       int             idx;
+       u32             dmastart;
+       u32             dmairq;
+       u32             dmaend;
+       u32             dmacur;
+};
+
+struct tiger_hw;
+
+struct tiger_ch {
+       struct bchannel         bch;
+       struct tiger_hw         *nj;
+       int                     idx;
+       int                     free;
+       int                     lastrx;
+       u16                     rxstate;
+       u16                     txstate;
+       struct isdnhdlc_vars    hsend;
+       struct isdnhdlc_vars    hrecv;
+       u8                      *hsbuf;
+       u8                      *hrbuf;
+};
+
+#define TX_INIT                0x0001
+#define TX_IDLE                0x0002
+#define TX_RUN         0x0004
+#define TX_UNDERRUN    0x0100
+#define RX_OVERRUN     0x0100
+
+#define LOG_SIZE       64
+
+struct tiger_hw {
+       struct list_head        list;
+       struct pci_dev          *pdev;
+       char                    name[MISDN_MAX_IDLEN];
+       enum nj_types           typ;
+       int                     irq;
+       u32                     irqcnt;
+       u32                     base;
+       size_t                  base_s;
+       dma_addr_t              dma;
+       void                    *dma_p;
+       spinlock_t              lock;   /* lock HW */
+       struct isac_hw          isac;
+       struct tiger_dma        send;
+       struct tiger_dma        recv;
+       struct tiger_ch         bc[2];
+       u8                      ctrlreg;
+       u8                      dmactrl;
+       u8                      auxd;
+       u8                      last_is0;
+       u8                      irqmask0;
+       char                    log[LOG_SIZE];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+static u32 debug;
+static int nj_cnt;
+
+static void
+_set_debug(struct tiger_hw *card)
+{
+       card->isac.dch.debug = debug;
+       card->bc[0].bch.debug = debug;
+       card->bc[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+       int ret;
+       struct tiger_hw *card;
+
+       ret = param_set_uint(val, kp);
+       if (!ret) {
+               read_lock(&card_lock);
+               list_for_each_entry(card, &Cards, list)
+                       _set_debug(card);
+               read_unlock(&card_lock);
+       }
+       return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(NETJET_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Netjet debug mask");
+
+static void
+nj_disable_hwirq(struct tiger_hw *card)
+{
+       outb(0, card->base + NJ_IRQMASK0);
+       outb(0, card->base + NJ_IRQMASK1);
+}
+
+
+static u8
+ReadISAC_nj(void *p, u8 offset)
+{
+       struct tiger_hw *card = p;
+       u8 ret;
+
+       card->auxd &= 0xfc;
+       card->auxd |= (offset >> 4) & 3;
+       outb(card->auxd, card->base + NJ_AUXDATA);
+       ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
+       return ret;
+}
+
+static void
+WriteISAC_nj(void *p, u8 offset, u8 value)
+{
+       struct tiger_hw *card = p;
+
+       card->auxd &= 0xfc;
+       card->auxd |= (offset >> 4) & 3;
+       outb(card->auxd, card->base + NJ_AUXDATA);
+       outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
+}
+
+static void
+ReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
+{
+       struct tiger_hw *card = p;
+
+       card->auxd &= 0xfc;
+       outb(card->auxd, card->base + NJ_AUXDATA);
+       insb(card->base + NJ_ISAC_OFF, data, size);
+}
+
+static void
+WriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
+{
+       struct tiger_hw *card = p;
+
+       card->auxd &= 0xfc;
+       outb(card->auxd, card->base + NJ_AUXDATA);
+       outsb(card->base + NJ_ISAC_OFF, data, size);
+}
+
+static void
+fill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill)
+{
+       struct tiger_hw *card = bc->bch.hw;
+       u32 mask = 0xff, val;
+
+       pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name,
+               bc->bch.nr, fill, cnt, idx, card->send.idx);
+       if (bc->bch.nr & 2) {
+               fill  <<= 8;
+               mask <<= 8;
+       }
+       mask ^= 0xffffffff;
+       while (cnt--) {
+               val = card->send.start[idx];
+               val &= mask;
+               val |= fill;
+               card->send.start[idx++] = val;
+               if (idx >= card->send.size)
+                       idx = 0;
+       }
+}
+
+static int
+mode_tiger(struct tiger_ch *bc, u32 protocol)
+{
+       struct tiger_hw *card = bc->bch.hw;
+
+       pr_debug("%s: B%1d protocol %x-->%x\n", card->name,
+               bc->bch.nr, bc->bch.state, protocol);
+       switch (protocol) {
+       case ISDN_P_NONE:
+               if (bc->bch.state == ISDN_P_NONE)
+                       break;
+               fill_mem(bc, 0, card->send.size, 0xff);
+               bc->bch.state = protocol;
+               /* only stop dma and interrupts if both channels NULL */
+               if ((card->bc[0].bch.state == ISDN_P_NONE) &&
+                   (card->bc[1].bch.state == ISDN_P_NONE)) {
+                       card->dmactrl = 0;
+                       outb(card->dmactrl, card->base + NJ_DMACTRL);
+                       outb(0, card->base + NJ_IRQMASK0);
+               }
+               test_and_clear_bit(FLG_HDLC, &bc->bch.Flags);
+               test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags);
+               bc->txstate = 0;
+               bc->rxstate = 0;
+               bc->lastrx = -1;
+               break;
+       case ISDN_P_B_RAW:
+               test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags);
+               bc->bch.state = protocol;
+               bc->idx = 0;
+               bc->free = card->send.size/2;
+               bc->rxstate = 0;
+               bc->txstate = TX_INIT | TX_IDLE;
+               bc->lastrx = -1;
+               if (!card->dmactrl) {
+                       card->dmactrl = 1;
+                       outb(card->dmactrl, card->base + NJ_DMACTRL);
+                       outb(0x0f, card->base + NJ_IRQMASK0);
+               }
+               break;
+       case ISDN_P_B_HDLC:
+               test_and_set_bit(FLG_HDLC, &bc->bch.Flags);
+               bc->bch.state = protocol;
+               bc->idx = 0;
+               bc->free = card->send.size/2;
+               bc->rxstate = 0;
+               bc->txstate = TX_INIT | TX_IDLE;
+               isdnhdlc_rcv_init(&bc->hrecv, 0);
+               isdnhdlc_out_init(&bc->hsend, 0);
+               bc->lastrx = -1;
+               if (!card->dmactrl) {
+                       card->dmactrl = 1;
+                       outb(card->dmactrl, card->base + NJ_DMACTRL);
+                       outb(0x0f, card->base + NJ_IRQMASK0);
+               }
+               break;
+       default:
+               pr_info("%s: %s protocol %x not handled\n", card->name,
+                       __func__, protocol);
+               return -ENOPROTOOPT;
+       }
+       card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR);
+       card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR);
+       card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+       card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
+       pr_debug("%s: %s ctrl %x irq  %02x/%02x idx %d/%d\n",
+               card->name, __func__,
+               inb(card->base + NJ_DMACTRL),
+               inb(card->base + NJ_IRQMASK0),
+               inb(card->base + NJ_IRQSTAT0),
+               card->send.idx,
+               card->recv.idx);
+       return 0;
+}
+
+static void
+nj_reset(struct tiger_hw *card)
+{
+       outb(0xff, card->base + NJ_CTRL); /* Reset On */
+       mdelay(1);
+
+       /* now edge triggered for TJ320 GE 13/07/00 */
+       /* see comment in IRQ function */
+       if (card->typ == NETJET_S_TJ320) /* TJ320 */
+               card->ctrlreg = 0x40;  /* Reset Off and status read clear */
+       else
+               card->ctrlreg = 0x00;  /* Reset Off and status read clear */
+       outb(card->ctrlreg, card->base + NJ_CTRL);
+       mdelay(10);
+
+       /* configure AUX pins (all output except ISAC IRQ pin) */
+       card->auxd = 0;
+       card->dmactrl = 0;
+       outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL);
+       outb(NJ_ISACIRQ,  card->base + NJ_IRQMASK1);
+       outb(card->auxd, card->base + NJ_AUXDATA);
+}
+
+static int
+inittiger(struct tiger_hw *card)
+{
+       int i;
+
+       card->dma_p = pci_alloc_consistent(card->pdev, NJ_DMA_SIZE,
+                       &card->dma);
+       if (!card->dma_p) {
+               pr_info("%s: No DMA memory\n", card->name);
+               return -ENOMEM;
+       }
+       if ((u64)card->dma > 0xffffffff) {
+               pr_info("%s: DMA outside 32 bit\n", card->name);
+               return -ENOMEM;
+       }
+       for (i = 0; i < 2; i++) {
+               card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_KERNEL);
+               if (!card->bc[i].hsbuf) {
+                       pr_info("%s: no B%d send buffer\n", card->name, i + 1);
+                       return -ENOMEM;
+               }
+               card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_KERNEL);
+               if (!card->bc[i].hrbuf) {
+                       pr_info("%s: no B%d recv buffer\n", card->name, i + 1);
+                       return -ENOMEM;
+               }
+       }
+       memset(card->dma_p, 0xff, NJ_DMA_SIZE);
+
+       card->send.start = card->dma_p;
+       card->send.dmastart = (u32)card->dma;
+       card->send.dmaend = card->send.dmastart +
+               (4 * (NJ_DMA_TXSIZE - 1));
+       card->send.dmairq = card->send.dmastart +
+               (4 * ((NJ_DMA_TXSIZE / 2) - 1));
+       card->send.size = NJ_DMA_TXSIZE;
+
+       if (debug & DEBUG_HW)
+               pr_notice("%s: send buffer phy %#x - %#x - %#x  virt %p"
+                       " size %zu u32\n", card->name,
+                       card->send.dmastart, card->send.dmairq,
+                       card->send.dmaend, card->send.start, card->send.size);
+
+       outl(card->send.dmastart, card->base + NJ_DMA_READ_START);
+       outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ);
+       outl(card->send.dmaend, card->base + NJ_DMA_READ_END);
+
+       card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2);
+       card->recv.dmastart = (u32)card->dma  + (NJ_DMA_SIZE / 2);
+       card->recv.dmaend = card->recv.dmastart +
+               (4 * (NJ_DMA_RXSIZE - 1));
+       card->recv.dmairq = card->recv.dmastart +
+               (4 * ((NJ_DMA_RXSIZE / 2) - 1));
+       card->recv.size = NJ_DMA_RXSIZE;
+
+       if (debug & DEBUG_HW)
+               pr_notice("%s: recv buffer phy %#x - %#x - %#x  virt %p"
+                       " size %zu u32\n", card->name,
+                       card->recv.dmastart, card->recv.dmairq,
+                       card->recv.dmaend, card->recv.start, card->recv.size);
+
+       outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START);
+       outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ);
+       outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END);
+       return 0;
+}
+
+static void
+read_dma(struct tiger_ch *bc, u32 idx, int cnt)
+{
+       struct tiger_hw *card = bc->bch.hw;
+       int i, stat;
+       u32 val;
+       u8 *p, *pn;
+
+       if (bc->lastrx == idx) {
+               bc->rxstate |= RX_OVERRUN;
+               pr_info("%s: B%1d overrun at idx %d\n", card->name,
+                       bc->bch.nr, idx);
+       }
+       bc->lastrx = idx;
+       if (!bc->bch.rx_skb) {
+               bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen, GFP_ATOMIC);
+               if (!bc->bch.rx_skb) {
+                       pr_info("%s: B%1d receive out of memory\n",
+                               card->name, bc->bch.nr);
+                       return;
+               }
+       }
+
+       if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) {
+               if ((bc->bch.rx_skb->len + cnt) > bc->bch.maxlen) {
+                       pr_debug("%s: B%1d overrun %d\n", card->name,
+                               bc->bch.nr, bc->bch.rx_skb->len + cnt);
+                       skb_trim(bc->bch.rx_skb, 0);
+                       return;
+               }
+               p = skb_put(bc->bch.rx_skb, cnt);
+       } else
+               p = bc->hrbuf;
+
+       for (i = 0; i < cnt; i++) {
+               val = card->recv.start[idx++];
+               if (bc->bch.nr & 2)
+                       val >>= 8;
+               if (idx >= card->recv.size)
+                       idx = 0;
+               p[i] = val & 0xff;
+       }
+       pn = bc->hrbuf;
+next_frame:
+       if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+               stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i,
+                       bc->bch.rx_skb->data, bc->bch.maxlen);
+               if (stat > 0) /* valid frame received */ 
+                       p = skb_put(bc->bch.rx_skb, stat);
+               else if (stat == -HDLC_CRC_ERROR)
+                       pr_info("%s: B%1d receive frame CRC error\n",
+                               card->name, bc->bch.nr);
+               else if (stat == -HDLC_FRAMING_ERROR)
+                       pr_info("%s: B%1d receive framing error\n",
+                               card->name, bc->bch.nr);
+               else if (stat == -HDLC_LENGTH_ERROR)
+                       pr_info("%s: B%1d receive frame too long (> %d)\n",
+                               card->name, bc->bch.nr, bc->bch.maxlen);
+       } else
+               stat = cnt;     
+
+       if (stat > 0) {
+               if (debug & DEBUG_HW_BFIFO) {
+                       snprintf(card->log, LOG_SIZE, "B%1d-recv %s %d ",
+                               bc->bch.nr, card->name, stat);
+                       print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET,
+                               p, stat);
+               }
+               recv_Bchannel(&bc->bch, 0);
+       }
+       if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+               pn += i;
+               cnt -= i;
+               if (!bc->bch.rx_skb) {
+                       bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen,
+                               GFP_ATOMIC);
+                       if (!bc->bch.rx_skb) {
+                               pr_info("%s: B%1d receive out of memory\n",
+                                       card->name, bc->bch.nr);
+                               return;
+                       }
+               }
+               if (cnt > 0)
+                       goto next_frame;
+       }
+}
+
+static void
+recv_tiger(struct tiger_hw *card, u8 irq_stat)
+{
+       u32 idx;
+       int cnt = card->recv.size / 2;
+
+       /* Note receive is via the WRITE DMA channel */
+       card->last_is0 &= ~NJ_IRQM0_WR_MASK;
+       card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK);
+
+       if (irq_stat & NJ_IRQM0_WR_END)
+               idx = cnt - 1;
+       else
+               idx = card->recv.size - 1;
+
+       if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags))
+               read_dma(&card->bc[0], idx, cnt);
+       if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags))
+               read_dma(&card->bc[1], idx, cnt);
+}
+
+/* sync with current DMA address at start or after exception */
+static void
+resync(struct tiger_ch *bc, struct tiger_hw *card)
+{
+       card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
+       card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+       if (bc->free > card->send.size / 2)
+               bc->free = card->send.size / 2;
+       /* currently we simple sync to the next complete free area
+        * this hast the advantage that we have always maximum time to
+        * handle TX irq
+        */
+       if (card->send.idx < ((card->send.size / 2) - 1))
+               bc->idx = (card->recv.size / 2) - 1;
+       else
+               bc->idx = card->recv.size - 1;
+       bc->txstate = TX_RUN;
+       pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name,
+               __func__, bc->bch.nr, bc->free, bc->idx, card->send.idx);
+}
+
+static int bc_next_frame(struct tiger_ch *);
+
+static void
+fill_hdlc_flag(struct tiger_ch *bc)
+{
+       struct tiger_hw *card = bc->bch.hw;
+       int count, i;
+       u32 m, v;
+       u8  *p;
+
+       if (bc->free == 0)
+               return;
+       pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name,
+               __func__, bc->bch.nr, bc->free, bc->txstate,
+               bc->idx, card->send.idx);
+       if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
+               resync(bc, card);
+       count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i,
+                       bc->hsbuf, bc->free);
+       pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name,
+                       bc->bch.nr, count);
+       bc->free -= count;
+       p = bc->hsbuf;
+       m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
+       for (i = 0; i < count; i++) {
+               if (bc->idx >= card->send.size)
+                       bc->idx = 0;
+               v = card->send.start[bc->idx];
+               v &= m;
+               v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
+               card->send.start[bc->idx++] = v;
+       }
+       if (debug & DEBUG_HW_BFIFO) {
+               snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
+                       bc->bch.nr, card->name, count);
+               print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+}
+
+static void
+fill_dma(struct tiger_ch *bc)
+{
+       struct tiger_hw *card = bc->bch.hw;
+       int count, i;
+       u32 m, v;
+       u8  *p;
+
+       if (bc->free == 0)
+               return;
+       count = bc->bch.tx_skb->len - bc->bch.tx_idx;
+       if (count <= 0)
+               return;
+       pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n", card->name,
+               __func__, bc->bch.nr, count, bc->free, bc->bch.tx_idx,
+               bc->bch.tx_skb->len, bc->txstate, bc->idx, card->send.idx);
+       if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
+               resync(bc, card);
+       p = bc->bch.tx_skb->data + bc->bch.tx_idx;
+       if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+               count = isdnhdlc_encode(&bc->hsend, p, count, &i,
+                       bc->hsbuf, bc->free);
+               pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name,
+                       bc->bch.nr, i, count);
+               bc->bch.tx_idx += i;
+               bc->free -= count;
+               p = bc->hsbuf;
+       } else {
+               if (count > bc->free)
+                       count = bc->free;
+               bc->bch.tx_idx += count;
+               bc->free -= count;
+       }
+       m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
+       for (i = 0; i < count; i++) {
+               if (bc->idx >= card->send.size)
+                       bc->idx = 0;
+               v = card->send.start[bc->idx];
+               v &= m;
+               v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
+               card->send.start[bc->idx++] = v;
+       }
+       if (debug & DEBUG_HW_BFIFO) {
+               snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
+                       bc->bch.nr, card->name, count);
+               print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+       if (bc->free)
+               bc_next_frame(bc);
+}
+
+
+static int
+bc_next_frame(struct tiger_ch *bc)
+{
+       if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len)
+               fill_dma(bc);
+       else {
+               if (bc->bch.tx_skb) {
+                       /* send confirm, on trans, free on hdlc. */
+                       if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags))
+                               confirm_Bsend(&bc->bch);
+                       dev_kfree_skb(bc->bch.tx_skb);
+               }
+               if (get_next_bframe(&bc->bch))
+                       fill_dma(bc);
+               else
+                       return 0;
+       }
+       return 1;
+}
+
+static void
+send_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc)
+{
+       int ret;
+
+       bc->free += card->send.size / 2;
+       if (bc->free >= card->send.size) {
+               if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) {
+                       pr_info("%s: B%1d TX underrun state %x\n", card->name,
+                               bc->bch.nr, bc->txstate);
+                       bc->txstate |= TX_UNDERRUN;
+               }
+               bc->free = card->send.size;
+       }
+       ret = bc_next_frame(bc);
+       if (!ret) {
+               if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+                       fill_hdlc_flag(bc);
+                       return;
+               }
+               pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name,
+                       bc->bch.nr, bc->free, bc->idx, card->send.idx);
+               if (!(bc->txstate & (TX_IDLE | TX_INIT))) {
+                       fill_mem(bc, bc->idx, bc->free, 0xff);
+                       if (bc->free == card->send.size)
+                               bc->txstate |= TX_IDLE;
+               }
+       }
+}
+
+static void
+send_tiger(struct tiger_hw *card, u8 irq_stat)
+{
+       int i;
+
+       /* Note send is via the READ DMA channel */
+       if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) {
+               pr_info("%s: tiger warn write double dma %x/%x\n",
+                       card->name, irq_stat, card->last_is0);
+               return;
+       } else {
+               card->last_is0 &= ~NJ_IRQM0_RD_MASK;
+               card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK);
+       }
+       for (i = 0; i < 2; i++) {
+               if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags))
+                       send_tiger_bc(card, &card->bc[i]);
+       }
+}
+
+static irqreturn_t
+nj_irq(int intno, void *dev_id)
+{
+       struct tiger_hw *card = dev_id;
+       u8 val, s1val, s0val;
+
+       spin_lock(&card->lock);
+       s0val = inb(card->base | NJ_IRQSTAT0);
+       s1val = inb(card->base | NJ_IRQSTAT1);
+       if ((s1val & NJ_ISACIRQ) && (s0val == 0)) {
+               /* shared IRQ */
+               spin_unlock(&card->lock);
+               return IRQ_NONE;
+       }
+       pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val);
+       card->irqcnt++;
+       if (!(s1val & NJ_ISACIRQ)) {
+               val = ReadISAC_nj(card, ISAC_ISTA);
+               if (val)
+                       mISDNisac_irq(&card->isac, val);
+       }
+
+       if (s0val)
+               /* write to clear */
+               outb(s0val, card->base | NJ_IRQSTAT0);
+       else
+               goto end;
+       s1val = s0val;
+       /* set bits in sval to indicate which page is free */
+       card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR);
+       card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
+       if (card->recv.dmacur < card->recv.dmairq)
+               s0val = 0x08;   /* the 2nd write area is free */
+       else
+               s0val = 0x04;   /* the 1st write area is free */
+
+       card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
+       card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+       if (card->send.dmacur < card->send.dmairq)
+               s0val |= 0x02;  /* the 2nd read area is free */
+       else
+               s0val |= 0x01;  /* the 1st read area is free */
+
+       pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name,
+               s1val, s0val, card->last_is0,
+               card->recv.idx, card->send.idx);
+       /* test if we have a DMA interrupt */
+       if (s0val != card->last_is0) {
+               if ((s0val & NJ_IRQM0_RD_MASK) !=
+                   (card->last_is0 & NJ_IRQM0_RD_MASK))
+                       /* got a write dma int */
+                       send_tiger(card, s0val);
+               if ((s0val & NJ_IRQM0_WR_MASK) !=
+                   (card->last_is0 & NJ_IRQM0_WR_MASK))
+                       /* got a read dma int */
+                       recv_tiger(card, s0val);
+       }
+end:
+       spin_unlock(&card->lock);
+       return IRQ_HANDLED;
+}
+
+static int
+nj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       int ret = -EINVAL;
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
+       struct tiger_hw *card = bch->hw;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       u32 id;
+       u_long flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       fill_dma(bc);
+                       ret = 0;
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&card->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = mode_tiger(bc, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(&card->lock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               mISDN_clear_bchannel(bch);
+               mode_tiger(bc, ISDN_P_NONE);
+               spin_unlock_irqrestore(&card->lock, flags);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq)
+{
+       int ret = 0;
+       struct tiger_hw *card  = bc->bch.hw;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       /* Nothing implemented yet */
+       case MISDN_CTRL_FILL_EMPTY:
+       default:
+               pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
+       struct tiger_hw *card  = bch->hw;
+       int ret = -EINVAL;
+       u_long flags;
+
+       pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       spin_lock_irqsave(&card->lock, flags);
+                       mISDN_freebchannel(bch);
+                       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+                       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+                       mode_tiger(bc, ISDN_P_NONE);
+                       spin_unlock_irqrestore(&card->lock, flags);
+               }
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bc, arg);
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd);
+       }
+       return ret;
+}
+
+static int
+channel_ctrl(struct tiger_hw *card, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_LOOP;
+               break;
+       case MISDN_CTRL_LOOP:
+               /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+               if (cq->channel < 0 || cq->channel > 3) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel);
+               break;
+       default:
+               pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+open_bchannel(struct tiger_hw *card, struct channel_req *rq)
+{
+       struct bchannel *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &card->bc[rq->adr.channel - 1].bch;
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+nj_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct tiger_hw *card = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+
+       pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->protocol == ISDN_P_TE_S0)
+                       err = card->isac.open(&card->isac, rq);
+               else
+                       err = open_bchannel(card, rq);
+               if (err)
+                       break;
+               if (!try_module_get(THIS_MODULE))
+                       pr_info("%s: cannot get module\n", card->name);
+               break;
+       case CLOSE_CHANNEL:
+               pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id,
+                       __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(card, arg);
+               break;
+       default:
+               pr_debug("%s: %s unknown command %x\n",
+                       card->name, __func__, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+static int
+nj_init_card(struct tiger_hw *card)
+{
+       u_long flags;
+       int ret;
+
+       spin_lock_irqsave(&card->lock, flags);
+       nj_disable_hwirq(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+
+       card->irq = card->pdev->irq;
+       if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) {
+               pr_info("%s: couldn't get interrupt %d\n",
+                       card->name, card->irq);
+               card->irq = -1;
+               return -EIO;
+       }
+
+       spin_lock_irqsave(&card->lock, flags);
+       nj_reset(card);
+       ret = card->isac.init(&card->isac);
+       if (ret)
+               goto error;
+       ret = inittiger(card);
+       if (ret)
+               goto error;
+       mode_tiger(&card->bc[0], ISDN_P_NONE);
+       mode_tiger(&card->bc[1], ISDN_P_NONE);
+error:
+       spin_unlock_irqrestore(&card->lock, flags);
+       return ret;
+}
+
+
+static void
+nj_release(struct tiger_hw *card)
+{
+       u_long flags;
+       int i;
+
+       if (card->base_s) {
+               spin_lock_irqsave(&card->lock, flags);
+               nj_disable_hwirq(card);
+               mode_tiger(&card->bc[0], ISDN_P_NONE);
+               mode_tiger(&card->bc[1], ISDN_P_NONE);
+               card->isac.release(&card->isac);
+               spin_unlock_irqrestore(&card->lock, flags);
+               release_region(card->base, card->base_s);
+               card->base_s = 0;
+       }
+       if (card->irq > 0)
+               free_irq(card->irq, card);
+       if (card->isac.dch.dev.dev.class)
+               mISDN_unregister_device(&card->isac.dch.dev);
+       
+       for (i = 0; i < 2; i++) {
+               mISDN_freebchannel(&card->bc[i].bch);
+               kfree(card->bc[i].hsbuf);
+               kfree(card->bc[i].hrbuf);
+       }
+       if (card->dma_p)
+               pci_free_consistent(card->pdev, NJ_DMA_SIZE,
+                       card->dma_p, card->dma);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       pci_clear_master(card->pdev);
+       pci_disable_device(card->pdev);
+       pci_set_drvdata(card->pdev, NULL);
+       kfree(card);
+}
+
+
+static int
+nj_setup(struct tiger_hw *card)
+{
+       card->base = pci_resource_start(card->pdev, 0);
+       card->base_s = pci_resource_len(card->pdev, 0);
+       if (!request_region(card->base, card->base_s, card->name)) {
+               pr_info("%s: NETjet config port %#x-%#x already in use\n",
+                       card->name, card->base,
+                       (u32)(card->base + card->base_s - 1));
+               card->base_s = 0;
+               return -EIO;
+       }
+       ASSIGN_FUNC(nj, ISAC, card->isac);
+       return 0;
+}
+
+
+static int __devinit
+setup_instance(struct tiger_hw *card)
+{
+       int i, err;
+       u_long flags;
+
+       snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1);
+       write_lock_irqsave(&card_lock, flags);
+       list_add_tail(&card->list, &Cards);
+       write_unlock_irqrestore(&card_lock, flags);
+
+       _set_debug(card);
+       card->isac.name = card->name;
+       spin_lock_init(&card->lock);
+       card->isac.hwlock = &card->lock;
+       mISDNisac_init(&card->isac, card);
+
+       card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       card->isac.dch.dev.D.ctrl = nj_dctrl;
+       for (i = 0; i < 2; i++) {
+               card->bc[i].bch.nr = i + 1;
+               set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+               mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);
+               card->bc[i].bch.hw = card;
+               card->bc[i].bch.ch.send = nj_l2l1B;
+               card->bc[i].bch.ch.ctrl = nj_bctrl;
+               card->bc[i].bch.ch.nr = i + 1;
+               list_add(&card->bc[i].bch.ch.list,
+                       &card->isac.dch.dev.bchannels);
+               card->bc[i].bch.hw = card;
+       }
+       err = nj_setup(card);
+       if (err)
+               goto error;
+       err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
+               card->name);
+       if (err)
+               goto error;
+       err = nj_init_card(card);
+       if (!err)  {
+               nj_cnt++;
+               pr_notice("Netjet %d cards installed\n", nj_cnt);
+               return 0;
+       }
+error:
+       nj_release(card);
+       return err;
+}
+
+static int __devinit
+nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int err = -ENOMEM;
+       int cfg;
+       struct tiger_hw *card;
+
+       if (pdev->subsystem_vendor == 0x8086 &&
+           pdev->subsystem_device == 0x0003) {
+               pr_notice("Netjet: Digium X100P/X101P not handled\n");
+               return -ENODEV;
+       }
+
+       if (pdev->subsystem_vendor == 0x55 &&
+           pdev->subsystem_device == 0x02) {
+               pr_notice("Netjet: Enter!Now not handled yet\n");
+               return -ENODEV;
+       }
+
+       card = kzalloc(sizeof(struct tiger_hw), GFP_ATOMIC);
+       if (!card) {
+               pr_info("No kmem for Netjet\n");
+               return err;
+       }
+
+       card->pdev = pdev;
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+
+       printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n",
+               pci_name(pdev));
+
+       pci_set_master(pdev);
+
+       /* the TJ300 and TJ320 must be detected, the IRQ handling is different
+        * unfortunately the chips use the same device ID, but the TJ320 has
+        * the bit20 in status PCI cfg register set
+        */
+       pci_read_config_dword(pdev, 0x04, &cfg);
+       if (cfg & 0x00100000)
+               card->typ = NETJET_S_TJ320;
+       else
+               card->typ = NETJET_S_TJ300;
+
+       card->base = pci_resource_start(pdev, 0);
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_instance(card);
+       if (err)
+               pci_set_drvdata(pdev, NULL);
+
+       return err;
+}
+
+
+static void __devexit nj_remove(struct pci_dev *pdev)
+{
+       struct tiger_hw *card = pci_get_drvdata(pdev);
+
+       if (card)
+               nj_release(card);
+       else
+               pr_info("%s drvdata already removed\n", __func__);
+}
+
+/* We cannot select cards with PCI_SUB... IDs, since here are cards with
+ * SUB IDs set to PCI_ANY_ID, so we need to match all and reject
+ * known other cards which not work with this driver - see probe function */
+static struct pci_device_id nj_pci_ids[] __devinitdata = {
+       { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { }
+};
+MODULE_DEVICE_TABLE(pci, nj_pci_ids);
+
+static struct pci_driver nj_driver = {
+       .name = "netjet",
+       .probe = nj_probe,
+       .remove = __devexit_p(nj_remove),
+       .id_table = nj_pci_ids,
+};
+
+static int __init nj_init(void)
+{
+       int err;
+
+       pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV);
+       err = pci_register_driver(&nj_driver);
+       return err;
+}
+
+static void __exit nj_cleanup(void)
+{
+       pci_unregister_driver(&nj_driver);
+}
+
+module_init(nj_init);
+module_exit(nj_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/netjet.h b/drivers/isdn/hardware/mISDN/netjet.h
new file mode 100644 (file)
index 0000000..d061ff9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * NETjet common header file
+ *
+ * Author      Karsten Keil
+ *              based on work of Matt Henderson and Daniel Potts,
+ *              Traverse Technologies P/L www.traverse.com.au
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define NJ_CTRL                        0x00
+#define NJ_DMACTRL             0x01
+#define NJ_AUXCTRL             0x02
+#define NJ_AUXDATA             0x03
+#define NJ_IRQMASK0            0x04
+#define NJ_IRQMASK1            0x05
+#define NJ_IRQSTAT0            0x06
+#define NJ_IRQSTAT1            0x07
+#define NJ_DMA_READ_START      0x08
+#define NJ_DMA_READ_IRQ                0x0c
+#define NJ_DMA_READ_END                0x10
+#define NJ_DMA_READ_ADR                0x14
+#define NJ_DMA_WRITE_START     0x18
+#define NJ_DMA_WRITE_IRQ       0x1c
+#define NJ_DMA_WRITE_END       0x20
+#define NJ_DMA_WRITE_ADR       0x24
+#define NJ_PULSE_CNT           0x28
+
+#define NJ_ISAC_OFF            0xc0
+#define NJ_ISACIRQ             0x10
+
+#define NJ_IRQM0_RD_MASK       0x03
+#define NJ_IRQM0_RD_IRQ                0x01
+#define NJ_IRQM0_RD_END                0x02
+#define NJ_IRQM0_WR_MASK       0x0c
+#define NJ_IRQM0_WR_IRQ                0x04
+#define NJ_IRQM0_WR_END                0x08
+
+/* one page here is no need to be smaller */
+#define NJ_DMA_SIZE            4096
+/* 2 * 64 byte is a compromise between IRQ count and latency */
+#define NJ_DMA_RXSIZE          128  /* 2 * 64 */
+#define NJ_DMA_TXSIZE          128  /* 2 * 64 */
+
diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c
new file mode 100644 (file)
index 0000000..ff3a4e2
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * speedfax.c  low level stuff for Sedlbauer Speedfax+ cards
+ *             based on the ISAR DSP
+ *             Thanks to Sedlbauer AG for informations and HW
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <linux/firmware.h>
+#include "ipac.h"
+#include "isar.h"
+
+#define SPEEDFAX_REV   "2.0"
+
+#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51
+#define PCI_SUBVENDOR_SPEEDFAX_PCI     0x54
+#define PCI_SUB_ID_SEDLBAUER           0x01
+
+#define SFAX_PCI_ADDR          0xc8
+#define SFAX_PCI_ISAC          0xd0
+#define SFAX_PCI_ISAR          0xe0
+
+/* TIGER 100 Registers */
+
+#define TIGER_RESET_ADDR       0x00
+#define TIGER_EXTERN_RESET_ON  0x01
+#define TIGER_EXTERN_RESET_OFF 0x00
+#define TIGER_AUX_CTRL         0x02
+#define TIGER_AUX_DATA         0x03
+#define TIGER_AUX_IRQMASK      0x05
+#define TIGER_AUX_STATUS       0x07
+
+/* Tiger AUX BITs */
+#define SFAX_AUX_IOMASK                0xdd    /* 1 and 5 are inputs */
+#define SFAX_ISAR_RESET_BIT_OFF 0x00
+#define SFAX_ISAR_RESET_BIT_ON 0x01
+#define SFAX_TIGER_IRQ_BIT     0x02
+#define SFAX_LED1_BIT          0x08
+#define SFAX_LED2_BIT          0x10
+
+#define SFAX_PCI_RESET_ON      (SFAX_ISAR_RESET_BIT_ON)
+#define SFAX_PCI_RESET_OFF     (SFAX_LED1_BIT | SFAX_LED2_BIT)
+
+static int sfax_cnt;
+static u32 debug;
+static u32 irqloops = 4;
+
+struct sfax_hw {
+       struct list_head        list;
+       struct pci_dev          *pdev;
+       char                    name[MISDN_MAX_IDLEN];
+       u32                     irq;
+       u32                     irqcnt;
+       u32                     cfg;
+       struct _ioport          p_isac;
+       struct _ioport          p_isar;
+       u8                      aux_data;
+       spinlock_t              lock;   /* HW access lock */
+       struct isac_hw          isac;
+       struct isar_hw          isar;
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct sfax_hw *card)
+{
+       card->isac.dch.debug = debug;
+       card->isar.ch[0].bch.debug = debug;
+       card->isar.ch[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+       int ret;
+       struct sfax_hw *card;
+
+       ret = param_set_uint(val, kp);
+       if (!ret) {
+               read_lock(&card_lock);
+               list_for_each_entry(card, &Cards, list)
+                       _set_debug(card);
+               read_unlock(&card_lock);
+       }
+       return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(SPEEDFAX_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Speedfax debug mask");
+module_param(irqloops, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)");
+
+IOFUNC_IND(ISAC, sfax_hw, p_isac)
+IOFUNC_IND(ISAR, sfax_hw, p_isar)
+
+static irqreturn_t
+speedfax_irq(int intno, void *dev_id)
+{
+       struct sfax_hw  *sf = dev_id;
+       u8 val;
+       int cnt = irqloops;
+
+       spin_lock(&sf->lock);
+       val = inb(sf->cfg + TIGER_AUX_STATUS);
+       if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */
+               spin_unlock(&sf->lock);
+               return IRQ_NONE; /* shared */
+       }
+       sf->irqcnt++;
+       val = ReadISAR_IND(sf, ISAR_IRQBIT);
+Start_ISAR:
+       if (val & ISAR_IRQSTA)
+               mISDNisar_irq(&sf->isar);
+       val = ReadISAC_IND(sf, ISAC_ISTA);
+       if (val)
+               mISDNisac_irq(&sf->isac, val);
+       val = ReadISAR_IND(sf, ISAR_IRQBIT);
+       if ((val & ISAR_IRQSTA) && cnt--)
+               goto Start_ISAR;
+       if (cnt < irqloops)
+               pr_debug("%s: %d irqloops cpu%d\n", sf->name,
+                       irqloops - cnt, smp_processor_id());
+       if (irqloops && !cnt)
+               pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name,
+                       irqloops, smp_processor_id());
+       spin_unlock(&sf->lock);
+       return IRQ_HANDLED;
+}
+
+static void
+enable_hwirq(struct sfax_hw *sf)
+{
+       WriteISAC_IND(sf, ISAC_MASK, 0);
+       WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK);
+       outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK);
+}
+
+static void
+disable_hwirq(struct sfax_hw *sf)
+{
+       WriteISAC_IND(sf, ISAC_MASK, 0xFF);
+       WriteISAR_IND(sf, ISAR_IRQBIT, 0);
+       outb(0, sf->cfg + TIGER_AUX_IRQMASK);
+}
+
+static void
+reset_speedfax(struct sfax_hw *sf)
+{
+
+       pr_debug("%s: resetting card\n", sf->name);
+       outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR);
+       outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA);
+       mdelay(1);
+       outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR);
+       sf->aux_data = SFAX_PCI_RESET_OFF;
+       outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+       mdelay(1);
+}
+
+static int
+sfax_ctrl(struct sfax_hw  *sf, u32 cmd, u_long arg)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       case HW_RESET_REQ:
+               reset_speedfax(sf);
+               break;
+       case HW_ACTIVATE_IND:
+               if (arg & 1)
+                       sf->aux_data &= ~SFAX_LED1_BIT;
+               if (arg & 2)
+                       sf->aux_data &= ~SFAX_LED2_BIT;
+               outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+               break;
+       case HW_DEACT_IND:
+               if (arg & 1)
+                       sf->aux_data |= SFAX_LED1_BIT;
+               if (arg & 2)
+                       sf->aux_data |= SFAX_LED2_BIT;
+               outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+               break;
+       default:
+               pr_info("%s: %s unknown command %x %lx\n",
+                       sf->name, __func__, cmd, arg);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+channel_ctrl(struct sfax_hw  *sf, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_LOOP;
+               break;
+       case MISDN_CTRL_LOOP:
+               /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+               if (cq->channel < 0 || cq->channel > 3) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel);
+               break;
+       default:
+               pr_info("%s: unknown Op %x\n", sf->name, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct sfax_hw          *sf = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+
+       pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->protocol == ISDN_P_TE_S0)
+                       err = sf->isac.open(&sf->isac, rq);
+               else
+                       err = sf->isar.open(&sf->isar, rq);
+               if (err)
+                       break;
+               if (!try_module_get(THIS_MODULE))
+                       pr_info("%s: cannot get module\n", sf->name);
+               break;
+       case CLOSE_CHANNEL:
+               pr_debug("%s: dev(%d) close from %p\n", sf->name,
+                       dch->dev.id, __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(sf, arg);
+               break;
+       default:
+               pr_debug("%s: unknown command %x\n", sf->name, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+static int __devinit
+init_card(struct sfax_hw *sf)
+{
+       int     ret, cnt = 3;
+       u_long  flags;
+
+       ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf);
+       if (ret) {
+               pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq);
+               return ret;
+       }
+       while (cnt--) {
+               spin_lock_irqsave(&sf->lock, flags);
+               ret = sf->isac.init(&sf->isac);
+               if (ret) {
+                       spin_unlock_irqrestore(&sf->lock, flags);
+                       pr_info("%s: ISAC init failed with %d\n",
+                               sf->name, ret);
+                       break;
+               }
+               enable_hwirq(sf);
+               /* RESET Receiver and Transmitter */
+               WriteISAC_IND(sf, ISAC_CMDR, 0x41);
+               spin_unlock_irqrestore(&sf->lock, flags);
+               msleep_interruptible(10);
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IRQ %d count %d\n", sf->name,
+                               sf->irq, sf->irqcnt);
+               if (!sf->irqcnt) {
+                       pr_info("%s: IRQ(%d) got no requests during init %d\n",
+                              sf->name, sf->irq, 3 - cnt);
+               } else
+                       return 0;
+       }
+       free_irq(sf->irq, sf);
+       return -EIO;
+}
+
+
+static int __devinit
+setup_speedfax(struct sfax_hw *sf)
+{
+       u_long flags;
+
+       if (!request_region(sf->cfg, 256, sf->name)) {
+               pr_info("mISDN: %s config port %x-%x already in use\n",
+                      sf->name, sf->cfg, sf->cfg + 255);
+               return -EIO;
+       }
+       outb(0xff, sf->cfg);
+       outb(0, sf->cfg);
+       outb(0xdd, sf->cfg + TIGER_AUX_CTRL);
+       outb(0, sf->cfg + TIGER_AUX_IRQMASK);
+
+       sf->isac.type = IPAC_TYPE_ISAC;
+       sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR;
+       sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC;
+       sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR;
+       sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR;
+       ASSIGN_FUNC(IND, ISAC, sf->isac);
+       ASSIGN_FUNC(IND, ISAR, sf->isar);
+       spin_lock_irqsave(&sf->lock, flags);
+       reset_speedfax(sf);
+       disable_hwirq(sf);
+       spin_unlock_irqrestore(&sf->lock, flags);
+       return 0;
+}
+
+static void
+release_card(struct sfax_hw *card) {
+       u_long  flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+       disable_hwirq(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+       card->isac.release(&card->isac);
+       free_irq(card->irq, card);
+       card->isar.release(&card->isar);
+       mISDN_unregister_device(&card->isac.dch.dev);
+       release_region(card->cfg, 256);
+       pci_disable_device(card->pdev);
+       pci_set_drvdata(card->pdev, NULL);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       kfree(card);
+       sfax_cnt--;
+}
+
+static int __devinit
+setup_instance(struct sfax_hw *card)
+{
+       const struct firmware *firmware;
+       int i, err;
+       u_long flags;
+
+       snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1);
+       write_lock_irqsave(&card_lock, flags);
+       list_add_tail(&card->list, &Cards);
+       write_unlock_irqrestore(&card_lock, flags);
+       _set_debug(card);
+       spin_lock_init(&card->lock);
+       card->isac.hwlock = &card->lock;
+       card->isar.hwlock = &card->lock;
+       card->isar.ctrl = (void *)&sfax_ctrl;
+       card->isac.name = card->name;
+       card->isar.name = card->name;
+       card->isar.owner = THIS_MODULE;
+
+       err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev);
+       if (err < 0) {
+               pr_info("%s: firmware request failed %d\n",
+                       card->name, err);
+               goto error_fw;
+       }
+       if (debug & DEBUG_HW)
+               pr_notice("%s: got firmware %zu bytes\n",
+                       card->name, firmware->size);
+
+       mISDNisac_init(&card->isac, card);
+
+       card->isac.dch.dev.D.ctrl = sfax_dctrl;
+       card->isac.dch.dev.Bprotocols =
+               mISDNisar_init(&card->isar, card);
+       for (i = 0; i < 2; i++) {
+               set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+               list_add(&card->isar.ch[i].bch.ch.list,
+                       &card->isac.dch.dev.bchannels);
+       }
+
+       err = setup_speedfax(card);
+       if (err)
+               goto error_setup;
+       err = card->isar.init(&card->isar);
+       if (err)
+               goto error;
+       err = mISDN_register_device(&card->isac.dch.dev,
+               &card->pdev->dev, card->name);
+       if (err)
+               goto error;
+       err = init_card(card);
+       if (err)
+               goto error_init;
+       err = card->isar.firmware(&card->isar, firmware->data, firmware->size);
+       if (!err)  {
+               release_firmware(firmware);
+               sfax_cnt++;
+               pr_notice("SpeedFax %d cards installed\n", sfax_cnt);
+               return 0;
+       }
+       disable_hwirq(card);
+       free_irq(card->irq, card);
+error_init:
+       mISDN_unregister_device(&card->isac.dch.dev);
+error:
+       release_region(card->cfg, 256);
+error_setup:
+       card->isac.release(&card->isac);
+       card->isar.release(&card->isar);
+       release_firmware(firmware);
+error_fw:
+       pci_disable_device(card->pdev);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       kfree(card);
+       return err;
+}
+
+static int __devinit
+sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int err = -ENOMEM;
+       struct sfax_hw *card = kzalloc(sizeof(struct sfax_hw), GFP_KERNEL);
+
+       if (!card) {
+               pr_info("No memory for Speedfax+ PCI\n");
+               return err;
+       }
+       card->pdev = pdev;
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+
+       pr_notice("mISDN: Speedfax found adapter %s at %s\n",
+               (char *)ent->driver_data, pci_name(pdev));
+
+       card->cfg = pci_resource_start(pdev, 0);
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_instance(card);
+       if (err)
+               pci_set_drvdata(pdev, NULL);
+       return err;
+}
+
+static void __devexit
+sfax_remove_pci(struct pci_dev *pdev)
+{
+       struct sfax_hw  *card = pci_get_drvdata(pdev);
+
+       if (card)
+               release_card(card);
+       else
+               pr_debug("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id sfaxpci_ids[] __devinitdata = {
+       { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+         PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER,
+         0, 0, (unsigned long) "Pyramid Speedfax + PCI"
+       },
+       { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+         PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER,
+         0, 0, (unsigned long) "Sedlbauer Speedfax + PCI"
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, sfaxpci_ids);
+
+static struct pci_driver sfaxpci_driver = {
+       .name = "speedfax+ pci",
+       .probe = sfaxpci_probe,
+       .remove = __devexit_p(sfax_remove_pci),
+       .id_table = sfaxpci_ids,
+};
+
+static int __init
+Speedfax_init(void)
+{
+       int err;
+
+       pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n",
+               SPEEDFAX_REV);
+       err = pci_register_driver(&sfaxpci_driver);
+       return err;
+}
+
+static void __exit
+Speedfax_cleanup(void)
+{
+       pci_unregister_driver(&sfaxpci_driver);
+}
+
+module_init(Speedfax_init);
+module_exit(Speedfax_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
new file mode 100644 (file)
index 0000000..1b9008f
--- /dev/null
@@ -0,0 +1,1440 @@
+/*
+ * w6692.c     mISDN driver for Winbond w6692 based cards
+ *
+ * Author      Karsten Keil <kkeil@suse.de>
+ *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include "w6692.h"
+
+#define W6692_REV      "2.0"
+
+#define DBUSY_TIMER_VALUE      80
+
+enum {
+       W6692_ASUS,
+       W6692_WINBOND,
+       W6692_USR
+};
+
+/* private data in the PCI devices list */
+struct w6692map {
+       u_int   subtype;
+       char    *name;
+};
+
+static const struct w6692map  w6692_map[] =
+{
+       {W6692_ASUS, "Dynalink/AsusCom IS64PH"},
+       {W6692_WINBOND, "Winbond W6692"},
+       {W6692_USR, "USR W6692"}
+};
+
+#ifndef PCI_VENDOR_ID_USR
+#define PCI_VENDOR_ID_USR      0x16ec
+#define PCI_DEVICE_ID_USR_6692 0x3409
+#endif
+
+struct w6692_ch {
+       struct bchannel         bch;
+       u32                     addr;
+       struct timer_list       timer;
+       u8                      b_mode;
+};
+
+struct w6692_hw {
+       struct list_head        list;
+       struct pci_dev          *pdev;
+       char                    name[MISDN_MAX_IDLEN];
+       u32                     irq;
+       u32                     irqcnt;
+       u32                     addr;
+       u32                     fmask;  /* feature mask - bit set per card nr */
+       int                     subtype;
+       spinlock_t              lock;   /* hw lock */
+       u8                      imask;
+       u8                      pctl;
+       u8                      xaddr;
+       u8                      xdata;
+       u8                      state;
+       struct w6692_ch         bc[2];
+       struct dchannel         dch;
+       char                    log[64];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static int w6692_cnt;
+static int debug;
+static u32 led;
+static u32 pots;
+
+static void
+_set_debug(struct w6692_hw *card)
+{
+       card->dch.debug = debug;
+       card->bc[0].bch.debug = debug;
+       card->bc[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+       int ret;
+       struct w6692_hw *card;
+
+       ret = param_set_uint(val, kp);
+       if (!ret) {
+               read_lock(&card_lock);
+               list_for_each_entry(card, &Cards, list)
+                       _set_debug(card);
+               read_unlock(&card_lock);
+       }
+       return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(W6692_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "W6692 debug mask");
+module_param(led, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)");
+module_param(pots, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)");
+
+static inline u8
+ReadW6692(struct w6692_hw *card, u8 offset)
+{
+       return inb(card->addr + offset);
+}
+
+static inline void
+WriteW6692(struct w6692_hw *card, u8 offset, u8 value)
+{
+       outb(value, card->addr + offset);
+}
+
+static inline u8
+ReadW6692B(struct w6692_ch *bc, u8 offset)
+{
+       return inb(bc->addr + offset);
+}
+
+static inline void
+WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value)
+{
+       outb(value, bc->addr + offset);
+}
+
+static void
+enable_hwirq(struct w6692_hw *card)
+{
+       WriteW6692(card, W_IMASK, card->imask);
+}
+
+static void
+disable_hwirq(struct w6692_hw *card)
+{
+       WriteW6692(card, W_IMASK, 0xff);
+}
+
+static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"};
+
+static void
+W6692Version(struct w6692_hw *card)
+{
+       int val;
+
+       val = ReadW6692(card, W_D_RBCH);
+       pr_notice("%s: Winbond W6692 version: %s\n", card->name,
+               W6692Ver[(val >> 6) & 3]);
+}
+
+static void
+w6692_led_handler(struct w6692_hw *card, int on)
+{
+       if ((!(card->fmask & led)) || card->subtype == W6692_USR)
+               return;
+       if (on) {
+               card->xdata &= 0xfb;    /*  LED ON */
+               WriteW6692(card, W_XDATA, card->xdata);
+       } else {
+               card->xdata |= 0x04;    /*  LED OFF */
+               WriteW6692(card, W_XDATA, card->xdata);
+       }
+}
+
+static void
+ph_command(struct w6692_hw *card, u8 cmd)
+{
+       pr_debug("%s: ph_command %x\n", card->name, cmd);
+       WriteW6692(card, W_CIX, cmd);
+}
+
+static void
+W6692_new_ph(struct w6692_hw *card)
+{
+       if (card->state == W_L1CMD_RST)
+               ph_command(card, W_L1CMD_DRC);
+       schedule_event(&card->dch, FLG_PHCHANGE);
+}
+
+static void
+W6692_ph_bh(struct dchannel *dch)
+{
+       struct w6692_hw *card = dch->hw;
+
+       switch (card->state) {
+       case W_L1CMD_RST:
+               dch->state = 0;
+               l1_event(dch->l1, HW_RESET_IND);
+               break;
+       case W_L1IND_CD:
+               dch->state = 3;
+               l1_event(dch->l1, HW_DEACT_CNF);
+               break;
+       case W_L1IND_DRD:
+               dch->state = 3;
+               l1_event(dch->l1, HW_DEACT_IND);
+               break;
+       case W_L1IND_CE:
+               dch->state = 4;
+               l1_event(dch->l1, HW_POWERUP_IND);
+               break;
+       case W_L1IND_LD:
+               if (dch->state <= 5) {
+                       dch->state = 5;
+                       l1_event(dch->l1, ANYSIGNAL);
+               } else {
+                       dch->state = 8;
+                       l1_event(dch->l1, LOSTFRAMING);
+               }
+               break;
+       case W_L1IND_ARD:
+               dch->state = 6;
+               l1_event(dch->l1, INFO2);
+               break;
+       case W_L1IND_AI8:
+               dch->state = 7;
+               l1_event(dch->l1, INFO4_P8);
+               break;
+       case W_L1IND_AI10:
+               dch->state = 7;
+               l1_event(dch->l1, INFO4_P10);
+               break;
+       default:
+               pr_debug("%s: TE unknown state %02x dch state %02x\n",
+                       card->name, card->state, dch->state);
+               break;
+       }
+       pr_debug("%s: TE newstate %02x\n", card->name, dch->state);
+}
+
+static void
+W6692_empty_Dfifo(struct w6692_hw *card, int count)
+{
+       struct dchannel *dch = &card->dch;
+       u8 *ptr;
+
+       pr_debug("%s: empty_Dfifo %d\n", card->name, count);
+       if (!dch->rx_skb) {
+               dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC);
+               if (!dch->rx_skb) {
+                       pr_info("%s: D receive out of memory\n", card->name);
+                       WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+                       return;
+               }
+       }
+       if ((dch->rx_skb->len + count) >= dch->maxlen) {
+               pr_debug("%s: empty_Dfifo overrun %d\n", card->name,
+                       dch->rx_skb->len + count);
+               WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+               return;
+       }
+       ptr = skb_put(dch->rx_skb, count);
+       insb(card->addr + W_D_RFIFO, ptr, count);
+       WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+       if (debug & DEBUG_HW_DFIFO) {
+               snprintf(card->log, 63, "D-recv %s %d ",
+                       card->name, count);
+               print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static void
+W6692_fill_Dfifo(struct w6692_hw *card)
+{
+       struct dchannel *dch = &card->dch;
+       int count;
+       u8 *ptr;
+       u8 cmd = W_D_CMDR_XMS;
+
+       pr_debug("%s: fill_Dfifo\n", card->name);
+       if (!dch->tx_skb)
+               return;
+       count = dch->tx_skb->len - dch->tx_idx;
+       if (count <= 0)
+               return;
+       if (count > W_D_FIFO_THRESH)
+               count = W_D_FIFO_THRESH;
+       else
+               cmd |= W_D_CMDR_XME;
+       ptr = dch->tx_skb->data + dch->tx_idx;
+       dch->tx_idx += count;
+       outsb(card->addr + W_D_XFIFO, ptr, count);
+       WriteW6692(card, W_D_CMDR, cmd);
+       if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) {
+               pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name);
+               del_timer(&dch->timer);
+       }
+       init_timer(&dch->timer);
+       dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+       add_timer(&dch->timer);
+       if (debug & DEBUG_HW_DFIFO) {
+               snprintf(card->log, 63, "D-send %s %d ",
+                       card->name, count);
+               print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static void
+d_retransmit(struct w6692_hw *card)
+{
+       struct dchannel *dch = &card->dch;
+
+       if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+               del_timer(&dch->timer);
+#ifdef FIXME
+       if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+               dchannel_sched_event(dch, D_CLEARBUSY);
+#endif
+       if (test_bit(FLG_TX_BUSY, &dch->Flags)) {
+               /* Restart frame */
+               dch->tx_idx = 0;
+               W6692_fill_Dfifo(card);
+       } else if (dch->tx_skb) { /* should not happen */
+               pr_info("%s: %s without TX_BUSY\n", card->name, __func__);
+               test_and_set_bit(FLG_TX_BUSY, &dch->Flags);
+               dch->tx_idx = 0;
+               W6692_fill_Dfifo(card);
+       } else {
+               pr_info("%s: XDU no TX_BUSY\n", card->name);
+               if (get_next_dframe(dch))
+                       W6692_fill_Dfifo(card);
+       }
+}
+
+static void
+handle_rxD(struct w6692_hw *card) {
+       u8      stat;
+       int     count;
+
+       stat = ReadW6692(card, W_D_RSTA);
+       if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
+               if (stat & W_D_RSTA_RDOV) {
+                       pr_debug("%s: D-channel RDOV\n", card->name);
+#ifdef ERROR_STATISTIC
+                       card->dch.err_rx++;
+#endif
+               }
+               if (stat & W_D_RSTA_CRCE) {
+                       pr_debug("%s: D-channel CRC error\n", card->name);
+#ifdef ERROR_STATISTIC
+                       card->dch.err_crc++;
+#endif
+               }
+               if (stat & W_D_RSTA_RMB) {
+                       pr_debug("%s: D-channel ABORT\n", card->name);
+#ifdef ERROR_STATISTIC
+                       card->dch.err_rx++;
+#endif
+               }
+               if (card->dch.rx_skb)
+                       dev_kfree_skb(card->dch.rx_skb);
+               card->dch.rx_skb = NULL;
+               WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
+       } else {
+               count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
+               if (count == 0)
+                       count = W_D_FIFO_THRESH;
+               W6692_empty_Dfifo(card, count);
+               recv_Dchannel(&card->dch);
+       }
+}
+
+static void
+handle_txD(struct w6692_hw *card) {
+       if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags))
+               del_timer(&card->dch.timer);
+       if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) {
+               W6692_fill_Dfifo(card);
+       } else {
+               if (card->dch.tx_skb)
+                       dev_kfree_skb(card->dch.tx_skb);
+               if (get_next_dframe(&card->dch))
+                       W6692_fill_Dfifo(card);
+       }
+}
+
+static void
+handle_statusD(struct w6692_hw *card)
+{
+       struct dchannel *dch = &card->dch;
+       u8 exval, v1, cir;
+
+       exval = ReadW6692(card, W_D_EXIR);
+
+       pr_debug("%s: D_EXIR %02x\n", card->name, exval);
+       if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {
+               /* Transmit underrun/collision */
+               pr_debug("%s: D-channel underrun/collision\n", card->name);
+#ifdef ERROR_STATISTIC
+               dch->err_tx++;
+#endif
+               d_retransmit(card);
+       }
+       if (exval & W_D_EXI_RDOV) {     /* RDOV */
+               pr_debug("%s: D-channel RDOV\n", card->name);
+               WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);
+       }
+       if (exval & W_D_EXI_TIN2)       /* TIN2 - never */
+               pr_debug("%s: spurious TIN2 interrupt\n", card->name);
+       if (exval & W_D_EXI_MOC) {      /* MOC - not supported */
+               v1 = ReadW6692(card, W_MOSR);
+               pr_debug("%s: spurious MOC interrupt MOSR %02x\n",
+                       card->name, v1);
+       }
+       if (exval & W_D_EXI_ISC) {      /* ISC - Level1 change */
+               cir = ReadW6692(card, W_CIR);
+               pr_debug("%s: ISC CIR %02X\n", card->name, cir);
+               if (cir & W_CIR_ICC) {
+                       v1 = cir & W_CIR_COD_MASK;
+                       pr_debug("%s: ph_state_change %x -> %x\n", card->name,
+                               dch->state, v1);
+                       card->state = v1;
+                       if (card->fmask & led) {
+                               switch (v1) {
+                               case W_L1IND_AI8:
+                               case W_L1IND_AI10:
+                                       w6692_led_handler(card, 1);
+                                       break;
+                               default:
+                                       w6692_led_handler(card, 0);
+                                       break;
+                               }
+                       }
+                       W6692_new_ph(card);
+               }
+               if (cir & W_CIR_SCC) {
+                       v1 = ReadW6692(card, W_SQR);
+                       pr_debug("%s: SCC SQR %02X\n", card->name, v1);
+               }
+       }
+       if (exval & W_D_EXI_WEXP)
+               pr_debug("%s: spurious WEXP interrupt!\n", card->name);
+       if (exval & W_D_EXI_TEXP)
+               pr_debug("%s: spurious TEXP interrupt!\n", card->name);
+}
+
+static void
+W6692_empty_Bfifo(struct w6692_ch *wch, int count)
+{
+       struct w6692_hw *card = wch->bch.hw;
+       u8 *ptr;
+
+       pr_debug("%s: empty_Bfifo %d\n", card->name, count);
+       if (unlikely(wch->bch.state == ISDN_P_NONE)) {
+               pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name);
+               WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+               if (wch->bch.rx_skb)
+                       skb_trim(wch->bch.rx_skb, 0);
+               return;
+       }
+       if (!wch->bch.rx_skb) {
+               wch->bch.rx_skb = mI_alloc_skb(wch->bch.maxlen, GFP_ATOMIC);
+               if (unlikely(!wch->bch.rx_skb)) {
+                       pr_info("%s: B receive out of memory\n", card->name);
+                       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+                               W_B_CMDR_RACT);
+                       return;
+               }
+       }
+       if (wch->bch.rx_skb->len + count > wch->bch.maxlen) {
+               pr_debug("%s: empty_Bfifo incoming packet too large\n",
+                       card->name);
+               WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+               skb_trim(wch->bch.rx_skb, 0);
+               return;
+       }
+       ptr = skb_put(wch->bch.rx_skb, count);
+       insb(wch->addr + W_B_RFIFO, ptr, count);
+       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+       if (debug & DEBUG_HW_DFIFO) {
+               snprintf(card->log, 63, "B%1d-recv %s %d ",
+                       wch->bch.nr, card->name, count);
+               print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static void
+W6692_fill_Bfifo(struct w6692_ch *wch)
+{
+       struct w6692_hw *card = wch->bch.hw;
+       int count;
+       u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;
+
+       pr_debug("%s: fill Bfifo\n", card->name);
+       if (!wch->bch.tx_skb)
+               return;
+       count = wch->bch.tx_skb->len - wch->bch.tx_idx;
+       if (count <= 0)
+               return;
+       ptr = wch->bch.tx_skb->data + wch->bch.tx_idx;
+       if (count > W_B_FIFO_THRESH)
+               count = W_B_FIFO_THRESH;
+       else if (test_bit(FLG_HDLC, &wch->bch.Flags))
+               cmd |= W_B_CMDR_XME;
+
+       pr_debug("%s: fill Bfifo%d/%d\n", card->name,
+                       count, wch->bch.tx_idx);
+       wch->bch.tx_idx += count;
+       outsb(wch->addr + W_B_XFIFO, ptr, count);
+       WriteW6692B(wch, W_B_CMDR, cmd);
+       if (debug & DEBUG_HW_DFIFO) {
+               snprintf(card->log, 63, "B%1d-send %s %d ",
+                       wch->bch.nr, card->name, count);
+               print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static int
+setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb)
+{
+       struct w6692_hw *card = wch->bch.hw;
+       u16 *vol = (u16 *)skb->data;
+       u8 val;
+
+       if ((!(card->fmask & pots)) ||
+           !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+               return -ENODEV;
+       if (skb->len < 2)
+               return -EINVAL;
+       if (*vol > 7)
+               return -EINVAL;
+       val = *vol & 7;
+       val = 7 - val;
+       if (mic) {
+               val <<= 3;
+               card->xaddr &= 0xc7;
+       } else {
+               card->xaddr &= 0xf8;
+       }
+       card->xaddr |= val;
+       WriteW6692(card, W_XADDR, card->xaddr);
+       return 0;
+}
+
+static int
+enable_pots(struct w6692_ch *wch)
+{
+       struct w6692_hw *card = wch->bch.hw;
+
+       if ((!(card->fmask & pots)) ||
+           !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+               return -ENODEV;
+       wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;
+       WriteW6692B(wch, W_B_MODE, wch->b_mode);
+       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+       card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0);
+       WriteW6692(card, W_PCTL, card->pctl);
+       return 0;
+}
+
+static int
+disable_pots(struct w6692_ch *wch)
+{
+       struct w6692_hw *card = wch->bch.hw;
+
+       if (!(card->fmask & pots))
+               return -ENODEV;
+       wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);
+       WriteW6692B(wch, W_B_MODE, wch->b_mode);
+       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+               W_B_CMDR_XRST);
+       return 0;
+}
+
+static int
+w6692_mode(struct w6692_ch *wch, u32 pr)
+{
+       struct w6692_hw *card;
+
+       card = wch->bch.hw;
+       pr_debug("%s: B%d protocol %x-->%x\n", card->name,
+               wch->bch.nr, wch->bch.state, pr);
+       switch (pr) {
+       case ISDN_P_NONE:
+               if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM))
+                       disable_pots(wch);
+               wch->b_mode = 0;
+               mISDN_clear_bchannel(&wch->bch);
+               WriteW6692B(wch, W_B_MODE, wch->b_mode);
+               WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+               test_and_clear_bit(FLG_HDLC, &wch->bch.Flags);
+               test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags);
+               break;
+       case ISDN_P_B_RAW:
+               wch->b_mode = W_B_MODE_MMS;
+               WriteW6692B(wch, W_B_MODE, wch->b_mode);
+               WriteW6692B(wch, W_B_EXIM, 0);
+               WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+                       W_B_CMDR_XRST);
+               test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags);
+               break;
+       case ISDN_P_B_HDLC:
+               wch->b_mode = W_B_MODE_ITF;
+               WriteW6692B(wch, W_B_MODE, wch->b_mode);
+               WriteW6692B(wch, W_B_ADM1, 0xff);
+               WriteW6692B(wch, W_B_ADM2, 0xff);
+               WriteW6692B(wch, W_B_EXIM, 0);
+               WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+                       W_B_CMDR_XRST);
+               test_and_set_bit(FLG_HDLC, &wch->bch.Flags);
+               break;
+       default:
+               pr_info("%s: protocol %x not known\n", card->name, pr);
+               return -ENOPROTOOPT;
+       }
+       wch->bch.state = pr;
+       return 0;
+}
+
+static void
+send_next(struct w6692_ch *wch)
+{
+       if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len)
+               W6692_fill_Bfifo(wch);
+       else {
+               if (wch->bch.tx_skb) {
+                       /* send confirm, on trans, free on hdlc. */
+                       if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+                               confirm_Bsend(&wch->bch);
+                       dev_kfree_skb(wch->bch.tx_skb);
+               }
+               if (get_next_bframe(&wch->bch))
+                       W6692_fill_Bfifo(wch);
+       }
+}
+
+static void
+W6692B_interrupt(struct w6692_hw *card, int ch)
+{
+       struct w6692_ch *wch = &card->bc[ch];
+       int             count;
+       u8              stat, star = 0;
+
+       stat = ReadW6692B(wch, W_B_EXIR);
+       pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat);
+       if (stat & W_B_EXI_RME) {
+               star = ReadW6692B(wch, W_B_STAR);
+               if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
+                       if ((star & W_B_STAR_RDOV) &&
+                           test_bit(FLG_ACTIVE, &wch->bch.Flags)) {
+                               pr_debug("%s: B%d RDOV proto=%x\n", card->name,
+                                       wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+                               wch->bch.err_rdo++;
+#endif
+                       }
+                       if (test_bit(FLG_HDLC, &wch->bch.Flags)) {
+                               if (star & W_B_STAR_CRCE) {
+                                       pr_debug("%s: B%d CRC error\n",
+                                               card->name, wch->bch.nr);
+#ifdef ERROR_STATISTIC
+                                       wch->bch.err_crc++;
+#endif
+                               }
+                               if (star & W_B_STAR_RMB) {
+                                       pr_debug("%s: B%d message abort\n",
+                                               card->name, wch->bch.nr);
+#ifdef ERROR_STATISTIC
+                                       wch->bch.err_inv++;
+#endif
+                               }
+                       }
+                       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+                               W_B_CMDR_RRST | W_B_CMDR_RACT);
+                       if (wch->bch.rx_skb)
+                               skb_trim(wch->bch.rx_skb, 0);
+               } else {
+                       count = ReadW6692B(wch, W_B_RBCL) &
+                               (W_B_FIFO_THRESH - 1);
+                       if (count == 0)
+                               count = W_B_FIFO_THRESH;
+                       W6692_empty_Bfifo(wch, count);
+                       recv_Bchannel(&wch->bch, 0);
+               }
+       }
+       if (stat & W_B_EXI_RMR) {
+               if (!(stat & W_B_EXI_RME))
+                       star = ReadW6692B(wch, W_B_STAR);
+               if (star & W_B_STAR_RDOV) {
+                       pr_debug("%s: B%d RDOV proto=%x\n", card->name,
+                               wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+                       wch->bch.err_rdo++;
+#endif
+                       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+                               W_B_CMDR_RRST | W_B_CMDR_RACT);
+               } else {
+                       W6692_empty_Bfifo(wch, W_B_FIFO_THRESH);
+                       if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags) &&
+                           wch->bch.rx_skb && (wch->bch.rx_skb->len > 0))
+                               recv_Bchannel(&wch->bch, 0);
+               }
+       }
+       if (stat & W_B_EXI_RDOV) {
+               /* only if it is not handled yet */
+               if (!(star & W_B_STAR_RDOV)) {
+                       pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name,
+                               wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+                       wch->bch.err_rdo++;
+#endif
+                       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+                               W_B_CMDR_RRST | W_B_CMDR_RACT);
+               }
+       }
+       if (stat & W_B_EXI_XFR) {
+               if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {
+                       star = ReadW6692B(wch, W_B_STAR);
+                       pr_debug("%s: B%d star %02x\n", card->name,
+                               wch->bch.nr, star);
+               }
+               if (star & W_B_STAR_XDOW) {
+                       pr_debug("%s: B%d XDOW proto=%x\n", card->name,
+                               wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+                       wch->bch.err_xdu++;
+#endif
+                       WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST |
+                               W_B_CMDR_RACT);
+                       /* resend */
+                       if (wch->bch.tx_skb) {
+                               if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+                                       wch->bch.tx_idx = 0;
+                       }
+               }
+               send_next(wch);
+               if (stat & W_B_EXI_XDUN)
+                       return; /* handle XDOW only once */
+       }
+       if (stat & W_B_EXI_XDUN) {
+               pr_debug("%s: B%d XDUN proto=%x\n", card->name,
+                       wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+               wch->bch.err_xdu++;
+#endif
+               WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+               /* resend */
+               if (wch->bch.tx_skb) {
+                       if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+                               wch->bch.tx_idx = 0;
+               }
+               send_next(wch);
+       }
+}
+
+static irqreturn_t
+w6692_irq(int intno, void *dev_id)
+{
+       struct w6692_hw *card = dev_id;
+       u8              ista;
+
+       spin_lock(&card->lock);
+       ista = ReadW6692(card, W_ISTA);
+       if ((ista | card->imask) == card->imask) {
+               /* possible a shared  IRQ reqest */
+               spin_unlock(&card->lock);
+               return IRQ_NONE;
+       }
+       card->irqcnt++;
+       pr_debug("%s: ista %02x\n", card->name, ista);
+       ista &= ~card->imask;
+       if (ista & W_INT_B1_EXI)
+               W6692B_interrupt(card, 0);
+       if (ista & W_INT_B2_EXI)
+               W6692B_interrupt(card, 1);
+       if (ista & W_INT_D_RME)
+               handle_rxD(card);
+       if (ista & W_INT_D_RMR)
+               W6692_empty_Dfifo(card, W_D_FIFO_THRESH);
+       if (ista & W_INT_D_XFR)
+               handle_txD(card);
+       if (ista & W_INT_D_EXI)
+               handle_statusD(card);
+       if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */
+               pr_debug("%s: W6692 spurious XINT!\n", card->name);
+/* End IRQ Handler */
+       spin_unlock(&card->lock);
+       return IRQ_HANDLED;
+}
+
+static void
+dbusy_timer_handler(struct dchannel *dch)
+{
+       struct w6692_hw *card = dch->hw;
+       int             rbch, star;
+       u_long          flags;
+
+       if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) {
+               spin_lock_irqsave(&card->lock, flags);
+               rbch = ReadW6692(card, W_D_RBCH);
+               star = ReadW6692(card, W_D_STAR);
+               pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
+                       card->name, rbch, star);
+               if (star & W_D_STAR_XBZ)        /* D-Channel Busy */
+                       test_and_set_bit(FLG_L1_BUSY, &dch->Flags);
+               else {
+                       /* discard frame; reset transceiver */
+                       test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags);
+                       if (dch->tx_idx)
+                               dch->tx_idx = 0;
+                       else
+                               pr_info("%s: W6692 D-Channel Busy no tx_idx\n",
+                                       card->name);
+                       /* Transmitter reset */
+                       WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST);
+               }
+               spin_unlock_irqrestore(&card->lock, flags);
+       }
+}
+
+void initW6692(struct w6692_hw *card)
+{
+       u8      val;
+
+       card->dch.timer.function = (void *)dbusy_timer_handler;
+       card->dch.timer.data = (u_long)&card->dch;
+       init_timer(&card->dch.timer);
+       w6692_mode(&card->bc[0], ISDN_P_NONE);
+       w6692_mode(&card->bc[1], ISDN_P_NONE);
+       WriteW6692(card, W_D_CTL, 0x00);
+       disable_hwirq(card);
+       WriteW6692(card, W_D_SAM, 0xff);
+       WriteW6692(card, W_D_TAM, 0xff);
+       WriteW6692(card, W_D_MODE, W_D_MODE_RACT);
+       card->state = W_L1CMD_RST;
+       ph_command(card, W_L1CMD_RST);
+       ph_command(card, W_L1CMD_ECK);
+       /* enable all IRQ but extern */
+       card->imask = 0x18;
+       WriteW6692(card, W_D_EXIM, 0x00);
+       WriteW6692B(&card->bc[0], W_B_EXIM, 0);
+       WriteW6692B(&card->bc[1], W_B_EXIM, 0);
+       /* Reset D-chan receiver and transmitter */
+       WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
+       /* Reset B-chan receiver and transmitter */
+       WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+       WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+       /* enable peripheral */
+       if (card->subtype == W6692_USR) {
+               /* seems that USR implemented some power control features
+                * Pin 79 is connected to the oscilator circuit so we
+                * have to handle it here
+                */
+               card->pctl = 0x80;
+               card->xdata = 0;
+               WriteW6692(card, W_PCTL, card->pctl);
+               WriteW6692(card, W_XDATA, card->xdata);
+       } else {
+               card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 |
+                       W_PCTL_OE1 | W_PCTL_OE0;
+               card->xaddr = 0x00;/* all sw off */
+               if (card->fmask & pots)
+                       card->xdata |= 0x06;    /*  POWER UP/ LED OFF / ALAW */
+               if (card->fmask & led)
+                       card->xdata |= 0x04;    /* LED OFF */
+               if ((card->fmask & pots) || (card->fmask & led)) {
+                       WriteW6692(card, W_PCTL, card->pctl);
+                       WriteW6692(card, W_XADDR, card->xaddr);
+                       WriteW6692(card, W_XDATA, card->xdata);
+                       val = ReadW6692(card, W_XADDR);
+                       if (debug & DEBUG_HW)
+                               pr_notice("%s: W_XADDR=%02x\n",
+                                       card->name, val);
+               }
+       }
+}
+
+static void
+reset_w6692(struct w6692_hw *card)
+{
+       WriteW6692(card, W_D_CTL, W_D_CTL_SRST);
+       mdelay(10);
+       WriteW6692(card, W_D_CTL, 0);
+}
+
+static int
+init_card(struct w6692_hw *card)
+{
+       int     cnt = 3;
+       u_long  flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+       disable_hwirq(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+       if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) {
+               pr_info("%s: couldn't get interrupt %d\n", card->name,
+                       card->irq);
+               return -EIO;
+       }
+       while (cnt--) {
+               spin_lock_irqsave(&card->lock, flags);
+               initW6692(card);
+               enable_hwirq(card);
+               spin_unlock_irqrestore(&card->lock, flags);
+               /* Timeout 10ms */
+               msleep_interruptible(10);
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IRQ %d count %d\n", card->name,
+                               card->irq, card->irqcnt);
+               if (!card->irqcnt) {
+                       pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
+                               card->name, card->irq, 3 - cnt);
+                       reset_w6692(card);
+               } else
+                       return 0;
+       }
+       free_irq(card->irq, card);
+       return -EIO;
+}
+
+static int
+w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);
+       struct w6692_hw *card = bch->hw;
+       int ret = -EINVAL;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       u32 id;
+       u_long flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       ret = 0;
+                       W6692_fill_Bfifo(bc);
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&card->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = w6692_mode(bc, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(&card->lock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               mISDN_clear_bchannel(bch);
+               w6692_mode(bc, ISDN_P_NONE);
+               spin_unlock_irqrestore(&card->lock, flags);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x,%x)\n",
+                       card->name, __func__, hh->prim, hh->id);
+               ret = -EINVAL;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       /* Nothing implemented yet */
+       case MISDN_CTRL_FILL_EMPTY:
+       default:
+               pr_info("%s: unknown Op %x\n", __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+open_bchannel(struct w6692_hw *card, struct channel_req *rq)
+{
+       struct bchannel *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &card->bc[rq->adr.channel - 1].bch;
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       return 0;
+}
+
+static int
+channel_ctrl(struct w6692_hw *card, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       default:
+               pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);
+       struct w6692_hw *card = bch->hw;
+       int ret = -EINVAL;
+       u_long flags;
+
+       pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       spin_lock_irqsave(&card->lock, flags);
+                       mISDN_freebchannel(bch);
+                       w6692_mode(bc, ISDN_P_NONE);
+                       spin_unlock_irqrestore(&card->lock, flags);
+               } else {
+                       skb_queue_purge(&bch->rqueue);
+                       bch->rcount = 0;
+               }
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bch, arg);
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x)\n",
+                       card->name, __func__, cmd);
+       }
+       return ret;
+}
+
+static int
+w6692_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct w6692_hw         *card = container_of(dch, struct w6692_hw, dch);
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       u32                     id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               ret = dchannel_senddata(dch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       W6692_fill_Dfifo(card);
+                       ret = 0;
+                       spin_unlock_irqrestore(&card->lock, flags);
+                       queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&card->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               ret = l1_event(dch->l1, hh->prim);
+               break;
+       case PH_DEACTIVATE_REQ:
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               ret = l1_event(dch->l1, hh->prim);
+               break;
+       }
+
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+w6692_l1callback(struct dchannel *dch, u32 cmd)
+{
+       struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
+       u_long flags;
+
+       pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state);
+       switch (cmd) {
+       case INFO3_P8:
+               spin_lock_irqsave(&card->lock, flags);
+               ph_command(card, W_L1CMD_AR8);
+               spin_unlock_irqrestore(&card->lock, flags);
+               break;
+       case INFO3_P10:
+               spin_lock_irqsave(&card->lock, flags);
+               ph_command(card, W_L1CMD_AR10);
+               spin_unlock_irqrestore(&card->lock, flags);
+               break;
+       case HW_RESET_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               if (card->state != W_L1IND_DRD)
+                       ph_command(card, W_L1CMD_RST);
+               ph_command(card, W_L1CMD_ECK);
+               spin_unlock_irqrestore(&card->lock, flags);
+               break;
+       case HW_DEACT_REQ:
+               skb_queue_purge(&dch->squeue);
+               if (dch->tx_skb) {
+                       dev_kfree_skb(dch->tx_skb);
+                       dch->tx_skb = NULL;
+               }
+               dch->tx_idx = 0;
+               if (dch->rx_skb) {
+                       dev_kfree_skb(dch->rx_skb);
+                       dch->rx_skb = NULL;
+               }
+               test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                       del_timer(&dch->timer);
+               break;
+       case HW_POWERUP_REQ:
+               spin_lock_irqsave(&card->lock, flags);
+               ph_command(card, W_L1CMD_ECK);
+               spin_unlock_irqrestore(&card->lock, flags);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       default:
+               pr_debug("%s: %s unknown command %x\n", card->name,
+                       __func__, cmd);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+open_dchannel(struct w6692_hw *card, struct channel_req *rq)
+{
+       pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__,
+               card->dch.dev.id, __builtin_return_address(1));
+       if (rq->protocol != ISDN_P_TE_S0)
+               return -EINVAL;
+       if (rq->adr.channel == 1)
+               /* E-Channel not supported */
+               return -EINVAL;
+       rq->ch = &card->dch.dev.D;
+       rq->ch->protocol = rq->protocol;
+       if (card->dch.state == 7)
+               _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       return 0;
+}
+
+static int
+w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel *dch = container_of(dev, struct dchannel, dev);
+       struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
+       struct channel_req *rq;
+       int err = 0;
+
+       pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->protocol == ISDN_P_TE_S0)
+                       err = open_dchannel(card, rq);
+               else
+                       err = open_bchannel(card, rq);
+               if (err)
+                       break;
+               if (!try_module_get(THIS_MODULE))
+                       pr_info("%s: cannot get module\n", card->name);
+               break;
+       case CLOSE_CHANNEL:
+               pr_debug("%s: dev(%d) close from %p\n", card->name,
+                       dch->dev.id, __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(card, arg);
+               break;
+       default:
+               pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+int
+setup_w6692(struct w6692_hw *card)
+{
+       u32     val;
+
+       if (!request_region(card->addr, 256, card->name)) {
+               pr_info("%s: config port %x-%x already in use\n", card->name,
+                      card->addr, card->addr + 255);
+               return -EIO;
+       }
+       W6692Version(card);
+       card->bc[0].addr = card->addr;
+       card->bc[1].addr = card->addr + 0x40;
+       val = ReadW6692(card, W_ISTA);
+       if (debug & DEBUG_HW)
+               pr_notice("%s ISTA=%02x\n", card->name, val);
+       val = ReadW6692(card, W_IMASK);
+       if (debug & DEBUG_HW)
+               pr_notice("%s IMASK=%02x\n", card->name, val);
+       val = ReadW6692(card, W_D_EXIR);
+       if (debug & DEBUG_HW)
+               pr_notice("%s D_EXIR=%02x\n", card->name, val);
+       val = ReadW6692(card, W_D_EXIM);
+       if (debug & DEBUG_HW)
+               pr_notice("%s D_EXIM=%02x\n", card->name, val);
+       val = ReadW6692(card, W_D_RSTA);
+       if (debug & DEBUG_HW)
+               pr_notice("%s D_RSTA=%02x\n", card->name, val);
+       return 0;
+}
+
+static void
+release_card(struct w6692_hw *card)
+{
+       u_long  flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+       disable_hwirq(card);
+       w6692_mode(&card->bc[0], ISDN_P_NONE);
+       w6692_mode(&card->bc[1], ISDN_P_NONE);
+       if ((card->fmask & led) || card->subtype == W6692_USR) {
+               card->xdata |= 0x04;    /*  LED OFF */
+               WriteW6692(card, W_XDATA, card->xdata);
+       }
+       spin_unlock_irqrestore(&card->lock, flags);
+       free_irq(card->irq, card);
+       l1_event(card->dch.l1, CLOSE_CHANNEL);
+       mISDN_unregister_device(&card->dch.dev);
+       release_region(card->addr, 256);
+       mISDN_freebchannel(&card->bc[1].bch);
+       mISDN_freebchannel(&card->bc[0].bch);
+       mISDN_freedchannel(&card->dch);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       pci_disable_device(card->pdev);
+       pci_set_drvdata(card->pdev, NULL);
+       kfree(card);
+}
+
+static int
+setup_instance(struct w6692_hw *card)
+{
+       int             i, err;
+       u_long          flags;
+
+       snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1);
+       write_lock_irqsave(&card_lock, flags);
+       list_add_tail(&card->list, &Cards);
+       write_unlock_irqrestore(&card_lock, flags);
+       card->fmask = (1 << w6692_cnt);
+       _set_debug(card);
+       spin_lock_init(&card->lock);
+       mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh);
+       card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
+       card->dch.dev.D.send = w6692_l2l1D;
+       card->dch.dev.D.ctrl = w6692_dctrl;
+       card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       card->dch.hw = card;
+       card->dch.dev.nrbchan = 2;
+       for (i = 0; i < 2; i++) {
+               mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);
+               card->bc[i].bch.hw = card;
+               card->bc[i].bch.nr = i + 1;
+               card->bc[i].bch.ch.nr = i + 1;
+               card->bc[i].bch.ch.send = w6692_l2l1B;
+               card->bc[i].bch.ch.ctrl = w6692_bctrl;
+               set_channelmap(i + 1, card->dch.dev.channelmap);
+               list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels);
+       }
+       err = setup_w6692(card);
+       if (err)
+               goto error_setup;
+       err = mISDN_register_device(&card->dch.dev, &card->pdev->dev,
+               card->name);
+       if (err)
+               goto error_reg;
+       err = init_card(card);
+       if (err)
+               goto error_init;
+       err = create_l1(&card->dch, w6692_l1callback);
+       if (!err) {
+               w6692_cnt++;
+               pr_notice("W6692 %d cards installed\n", w6692_cnt);
+               return 0;
+       }
+
+       free_irq(card->irq, card);
+error_init:
+       mISDN_unregister_device(&card->dch.dev);
+error_reg:
+       release_region(card->addr, 256);
+error_setup:
+       mISDN_freebchannel(&card->bc[1].bch);
+       mISDN_freebchannel(&card->bc[0].bch);
+       mISDN_freedchannel(&card->dch);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       kfree(card);
+       return err;
+}
+
+static int __devinit
+w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int             err = -ENOMEM;
+       struct w6692_hw *card;
+       struct w6692map *m = (struct w6692map *)ent->driver_data;
+
+       card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL);
+       if (!card) {
+               pr_info("No kmem for w6692 card\n");
+               return err;
+       }
+       card->pdev = pdev;
+       card->subtype = m->subtype;
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+
+       printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
+              m->name, pci_name(pdev));
+
+       card->addr = pci_resource_start(pdev, 1);
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_instance(card);
+       if (err)
+               pci_set_drvdata(pdev, NULL);
+       return err;
+}
+
+static void __devexit
+w6692_remove_pci(struct pci_dev *pdev)
+{
+       struct w6692_hw *card = pci_get_drvdata(pdev);
+
+       if (card)
+               release_card(card);
+       else
+               if (debug)
+                       pr_notice("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id w6692_ids[] = {
+       { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]},
+       { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
+         PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0,
+         (ulong)&w6692_map[2]},
+       { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]},
+       { }
+};
+MODULE_DEVICE_TABLE(pci, w6692_ids);
+
+static struct pci_driver w6692_driver = {
+       .name =  "w6692",
+       .probe = w6692_probe,
+       .remove = __devexit_p(w6692_remove_pci),
+       .id_table = w6692_ids,
+};
+
+static int __init w6692_init(void)
+{
+       int err;
+
+       pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV);
+
+       err = pci_register_driver(&w6692_driver);
+       return err;
+}
+
+static void __exit w6692_cleanup(void)
+{
+       pci_unregister_driver(&w6692_driver);
+}
+
+module_init(w6692_init);
+module_exit(w6692_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/w6692.h b/drivers/isdn/hardware/mISDN/w6692.h
new file mode 100644 (file)
index 0000000..f956977
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Winbond W6692 specific defines
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Specifications of W6692 registers */
+
+#define W_D_RFIFO      0x00    /* R */
+#define W_D_XFIFO      0x04    /* W */
+#define W_D_CMDR       0x08    /* W */
+#define W_D_MODE       0x0c    /* R/W */
+#define W_D_TIMR       0x10    /* R/W */
+#define W_ISTA         0x14    /* R_clr */
+#define W_IMASK                0x18    /* R/W */
+#define W_D_EXIR       0x1c    /* R_clr */
+#define W_D_EXIM       0x20    /* R/W */
+#define W_D_STAR       0x24    /* R */
+#define W_D_RSTA       0x28    /* R */
+#define W_D_SAM                0x2c    /* R/W */
+#define W_D_SAP1       0x30    /* R/W */
+#define W_D_SAP2       0x34    /* R/W */
+#define W_D_TAM                0x38    /* R/W */
+#define W_D_TEI1       0x3c    /* R/W */
+#define W_D_TEI2       0x40    /* R/W */
+#define W_D_RBCH       0x44    /* R */
+#define W_D_RBCL       0x48    /* R */
+#define W_TIMR2                0x4c    /* W */
+#define W_L1_RC                0x50    /* R/W */
+#define W_D_CTL                0x54    /* R/W */
+#define W_CIR          0x58    /* R */
+#define W_CIX          0x5c    /* W */
+#define W_SQR          0x60    /* R */
+#define W_SQX          0x64    /* W */
+#define W_PCTL         0x68    /* R/W */
+#define W_MOR          0x6c    /* R */
+#define W_MOX          0x70    /* R/W */
+#define W_MOSR         0x74    /* R_clr */
+#define W_MOCR         0x78    /* R/W */
+#define W_GCR          0x7c    /* R/W */
+
+#define        W_B_RFIFO       0x80    /* R */
+#define        W_B_XFIFO       0x84    /* W */
+#define        W_B_CMDR        0x88    /* W */
+#define        W_B_MODE        0x8c    /* R/W */
+#define        W_B_EXIR        0x90    /* R_clr */
+#define        W_B_EXIM        0x94    /* R/W */
+#define        W_B_STAR        0x98    /* R */
+#define        W_B_ADM1        0x9c    /* R/W */
+#define        W_B_ADM2        0xa0    /* R/W */
+#define        W_B_ADR1        0xa4    /* R/W */
+#define        W_B_ADR2        0xa8    /* R/W */
+#define        W_B_RBCL        0xac    /* R */
+#define        W_B_RBCH        0xb0    /* R */
+
+#define W_XADDR                0xf4    /* R/W */
+#define W_XDATA                0xf8    /* R/W */
+#define W_EPCTL                0xfc    /* W */
+
+/* W6692 register bits */
+
+#define        W_D_CMDR_XRST   0x01
+#define        W_D_CMDR_XME    0x02
+#define        W_D_CMDR_XMS    0x08
+#define        W_D_CMDR_STT    0x10
+#define        W_D_CMDR_RRST   0x40
+#define        W_D_CMDR_RACK   0x80
+
+#define        W_D_MODE_RLP    0x01
+#define        W_D_MODE_DLP    0x02
+#define        W_D_MODE_MFD    0x04
+#define        W_D_MODE_TEE    0x08
+#define        W_D_MODE_TMS    0x10
+#define        W_D_MODE_RACT   0x40
+#define        W_D_MODE_MMS    0x80
+
+#define W_INT_B2_EXI   0x01
+#define W_INT_B1_EXI   0x02
+#define W_INT_D_EXI    0x04
+#define W_INT_XINT0    0x08
+#define W_INT_XINT1    0x10
+#define W_INT_D_XFR    0x20
+#define W_INT_D_RME    0x40
+#define W_INT_D_RMR    0x80
+
+#define W_D_EXI_WEXP   0x01
+#define W_D_EXI_TEXP   0x02
+#define W_D_EXI_ISC    0x04
+#define W_D_EXI_MOC    0x08
+#define W_D_EXI_TIN2   0x10
+#define W_D_EXI_XCOL   0x20
+#define W_D_EXI_XDUN   0x40
+#define W_D_EXI_RDOV   0x80
+
+#define        W_D_STAR_DRDY   0x10
+#define        W_D_STAR_XBZ    0x20
+#define        W_D_STAR_XDOW   0x80
+
+#define W_D_RSTA_RMB   0x10
+#define W_D_RSTA_CRCE  0x20
+#define W_D_RSTA_RDOV  0x40
+
+#define W_D_CTL_SRST   0x20
+
+#define W_CIR_SCC      0x80
+#define W_CIR_ICC      0x40
+#define W_CIR_COD_MASK 0x0f
+
+#define W_PCTL_PCX     0x01
+#define W_PCTL_XMODE   0x02
+#define W_PCTL_OE0     0x04
+#define W_PCTL_OE1     0x08
+#define W_PCTL_OE2     0x10
+#define W_PCTL_OE3     0x20
+#define W_PCTL_OE4     0x40
+#define W_PCTL_OE5     0x80
+
+#define        W_B_CMDR_XRST   0x01
+#define        W_B_CMDR_XME    0x02
+#define        W_B_CMDR_XMS    0x04
+#define        W_B_CMDR_RACT   0x20
+#define        W_B_CMDR_RRST   0x40
+#define        W_B_CMDR_RACK   0x80
+
+#define        W_B_MODE_FTS0   0x01
+#define        W_B_MODE_FTS1   0x02
+#define        W_B_MODE_SW56   0x04
+#define        W_B_MODE_BSW0   0x08
+#define        W_B_MODE_BSW1   0x10
+#define        W_B_MODE_EPCM   0x20
+#define        W_B_MODE_ITF    0x40
+#define        W_B_MODE_MMS    0x80
+
+#define        W_B_EXI_XDUN    0x01
+#define        W_B_EXI_XFR     0x02
+#define        W_B_EXI_RDOV    0x10
+#define        W_B_EXI_RME     0x20
+#define        W_B_EXI_RMR     0x40
+
+#define        W_B_STAR_XBZ    0x01
+#define        W_B_STAR_XDOW   0x04
+#define        W_B_STAR_RMB    0x10
+#define        W_B_STAR_CRCE   0x20
+#define        W_B_STAR_RDOV   0x40
+
+#define        W_B_RBCH_LOV    0x20
+
+/* W6692 Layer1 commands */
+
+#define        W_L1CMD_ECK     0x00
+#define W_L1CMD_RST    0x01
+#define W_L1CMD_SCP    0x04
+#define W_L1CMD_SSP    0x02
+#define W_L1CMD_AR8    0x08
+#define W_L1CMD_AR10   0x09
+#define W_L1CMD_EAL    0x0a
+#define W_L1CMD_DRC    0x0f
+
+/* W6692 Layer1 indications */
+
+#define W_L1IND_CE     0x07
+#define W_L1IND_DRD    0x00
+#define W_L1IND_LD     0x04
+#define W_L1IND_ARD    0x08
+#define W_L1IND_TI     0x0a
+#define W_L1IND_ATI    0x0b
+#define W_L1IND_AI8    0x0c
+#define W_L1IND_AI10   0x0d
+#define W_L1IND_CD     0x0f
+
+/* FIFO thresholds */
+#define W_D_FIFO_THRESH        64
+#define W_B_FIFO_THRESH        64
index 7832d8b..3464ebc 100644 (file)
@@ -391,6 +391,7 @@ comment "HiSax sub driver modules"
 config HISAX_ST5481
        tristate "ST5481 USB ISDN modem (EXPERIMENTAL)"
        depends on USB && EXPERIMENTAL
+       select ISDN_HDLC
        select CRC_CCITT
        select BITREVERSE
        help
@@ -418,11 +419,6 @@ config HISAX_FRITZ_PCIPNP
          (the latter also needs you to select "ISA Plug and Play support"
          from the menu "Plug and Play configuration")
 
-config HISAX_HDLC
-       bool
-       depends on HISAX_ST5481
-       default y
-
 config HISAX_AVM_A1_PCMCIA
        bool
        depends on HISAX_AVM_A1_CS
index c7a3794..ab638b0 100644 (file)
@@ -16,10 +16,6 @@ obj-$(CONFIG_HISAX_HFCUSB)           += hfc_usb.o
 obj-$(CONFIG_HISAX_HFC4S8S)            += hfc4s8s_l1.o
 obj-$(CONFIG_HISAX_FRITZ_PCIPNP)        += hisax_isac.o hisax_fcpcipnp.o
 
-ifdef CONFIG_HISAX_HDLC
-obj-$(CONFIG_ISDN_DRV_HISAX)           += isdnhdlc.o
-endif
-
 # Multipart objects.
 
 hisax_st5481-y                                 := st5481_init.o st5481_usb.o st5481_d.o \
index 341faf5..bf526a7 100644 (file)
@@ -238,8 +238,6 @@ Amd7930_bh(struct work_struct *work)
                container_of(work, struct IsdnCardState, tqueue);
         struct PStack *stptr;
 
-       if (!cs)
-               return;
        if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
                 if (cs->debug)
                        debugl1(cs, "Amd7930: bh, D-Channel Busy cleared");
index 025a20d..475b1a0 100644 (file)
@@ -833,8 +833,6 @@ static struct FsmNode fnlist[] __initdata =
 };
 /* *INDENT-ON* */
 
-#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
-
 int __init
 CallcNew(void)
 {
@@ -842,7 +840,7 @@ CallcNew(void)
        callcfsm.event_count = EVENT_COUNT;
        callcfsm.strEvent = strEvent;
        callcfsm.strState = strState;
-       return FsmNew(&callcfsm, fnlist, FNCOUNT);
+       return FsmNew(&callcfsm, fnlist, ARRAY_SIZE(fnlist));
 }
 
 void
index 3d337d9..d110a77 100644 (file)
@@ -1506,8 +1506,6 @@ hfcpci_bh(struct work_struct *work)
        u_long  flags;
 //      struct PStack *stptr;
 
-       if (!cs)
-               return;
        if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
                if (!cs->hw.hfcpci.nt_mode)
                        switch (cs->dc.hfcpci.ph_state) {
index d92e8d6..419f87c 100644 (file)
@@ -1255,8 +1255,6 @@ hfcsx_bh(struct work_struct *work)
                container_of(work, struct IsdnCardState, tqueue);
        u_long flags;
 
-       if (!cs)
-               return;
        if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
                if (!cs->hw.hfcsx.nt_mode)
                        switch (cs->dc.hfcsx.ph_state) {
index 682cac3..9aba646 100644 (file)
@@ -83,8 +83,6 @@ icc_bh(struct work_struct *work)
                container_of(work, struct IsdnCardState, tqueue);
        struct PStack *stptr;
        
-       if (!cs)
-               return;
        if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
                if (cs->debug)
                        debugl1(cs, "D-Channel Busy cleared");
index 07b1673..a19354d 100644 (file)
@@ -86,8 +86,6 @@ isac_bh(struct work_struct *work)
                container_of(work, struct IsdnCardState, tqueue);
        struct PStack *stptr;
        
-       if (!cs)
-               return;
        if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
                if (cs->debug)
                        debugl1(cs, "D-Channel Busy cleared");
diff --git a/drivers/isdn/hisax/isdnhdlc.h b/drivers/isdn/hisax/isdnhdlc.h
deleted file mode 100644 (file)
index cf0a95a..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * isdnhdlc.h  --  General purpose ISDN HDLC decoder.
- *
- * Implementation of a HDLC decoder/encoder in software.
- * Neccessary because some ISDN devices don't have HDLC
- * controllers. Also included: a bit reversal table.
- *
- *Copyright (C) 2002    Wolfgang Mües      <wolfgang@iksw-muees.de>
- *             2001    Frode Isaksen      <fisaksen@bewan.com>
- *              2001   Kai Germaschewski  <kai.germaschewski@gmx.de>
- *
- *      This program is free software; you can redistribute it and/or modify
- *      it under the terms of the GNU General Public License as published by
- *      the Free Software Foundation; either version 2 of the License, or
- *      (at your option) any later version.
- *
- *      This program is distributed in the hope that it will be useful,
- *      but WITHOUT ANY WARRANTY; without even the implied warranty of
- *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *      GNU General Public License for more details.
- *
- *      You should have received a copy of the GNU General Public License
- *      along with this program; if not, write to the Free Software
- *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __ISDNHDLC_H__
-#define __ISDNHDLC_H__
-
-struct isdnhdlc_vars {
-       int bit_shift;
-       int hdlc_bits1;
-       int data_bits;
-       int ffbit_shift;        // encoding only
-       int state;
-       int dstpos;
-
-       unsigned short crc;
-
-       unsigned char cbin;
-       unsigned char shift_reg;
-       unsigned char ffvalue;
-
-       unsigned int data_received:1;   // set if transferring data
-       unsigned int dchannel:1;        // set if D channel (send idle instead of flags)
-       unsigned int do_adapt56:1;      // set if 56K adaptation
-       unsigned int do_closing:1;      // set if in closing phase (need to send CRC + flag
-};
-
-
-/*
-  The return value from isdnhdlc_decode is
-  the frame length, 0 if no complete frame was decoded,
-  or a negative error number
-*/
-#define HDLC_FRAMING_ERROR     1
-#define HDLC_CRC_ERROR         2
-#define HDLC_LENGTH_ERROR      3
-
-extern void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56);
-
-extern int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src, int slen,int *count,
-                           unsigned char *dst, int dsize);
-
-extern void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc,int is_d_channel,int do_adapt56);
-
-extern int isdnhdlc_encode (struct isdnhdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count,
-                           unsigned char *dst,int dsize);
-
-#endif /* __ISDNHDLC_H__ */
index 317f16f..9ce6abe 100644 (file)
@@ -647,8 +647,6 @@ static struct FsmNode L1SFnList[] __initdata =
        {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
 };
 
-#define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode))
-
 #ifdef HISAX_UINTERFACE
 static void
 l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
@@ -706,8 +704,6 @@ static struct FsmNode L1UFnList[] __initdata =
        {ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact},
 };
 
-#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode))
-
 #endif
 
 static void
@@ -754,8 +750,6 @@ static struct FsmNode L1BFnList[] __initdata =
        {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact},
 };
 
-#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
-
 int __init 
 Isdnl1New(void)
 {
@@ -765,7 +759,7 @@ Isdnl1New(void)
        l1fsm_s.event_count = L1_EVENT_COUNT;
        l1fsm_s.strEvent = strL1Event;
        l1fsm_s.strState = strL1SState;
-       retval = FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT);
+       retval = FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
        if (retval)
                return retval;
 
@@ -773,7 +767,7 @@ Isdnl1New(void)
        l1fsm_b.event_count = L1_EVENT_COUNT;
        l1fsm_b.strEvent = strL1Event;
        l1fsm_b.strState = strL1BState;
-       retval = FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
+       retval = FsmNew(&l1fsm_b, L1BFnList, ARRAY_SIZE(L1BFnList));
        if (retval) {
                FsmFree(&l1fsm_s);
                return retval;
@@ -783,7 +777,7 @@ Isdnl1New(void)
        l1fsm_u.event_count = L1_EVENT_COUNT;
        l1fsm_u.strEvent = strL1Event;
        l1fsm_u.strState = strL1UState;
-       retval = FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT);
+       retval = FsmNew(&l1fsm_u, L1UFnList, ARRAY_SIZE(L1UFnList));
        if (retval) {
                FsmFree(&l1fsm_s);
                FsmFree(&l1fsm_b);
index 3446f24..7b9496a 100644 (file)
@@ -1623,8 +1623,6 @@ static struct FsmNode L2FnList[] __initdata =
        {ST_L2_8, EV_L1_DEACTIVATE, l2_persistent_da},
 };
 
-#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
-
 static void
 isdnl2_l1l2(struct PStack *st, int pr, void *arg)
 {
@@ -1836,7 +1834,7 @@ Isdnl2New(void)
        l2fsm.event_count = L2_EVENT_COUNT;
        l2fsm.strEvent = strL2Event;
        l2fsm.strState = strL2State;
-       return FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+       return FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList));
 }
 
 void
index 935f233..0676602 100644 (file)
@@ -543,8 +543,6 @@ static struct FsmNode L3FnList[] __initdata =
 };
 /* *INDENT-ON* */
 
-#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode))
-
 void
 l3_msg(struct PStack *st, int pr, void *arg)
 {
@@ -587,7 +585,7 @@ Isdnl3New(void)
        l3fsm.event_count = L3_EVENT_COUNT;
        l3fsm.strEvent = strL3Event;
        l3fsm.strState = strL3State;
-       return FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+       return FsmNew(&l3fsm, L3FnList, ARRAY_SIZE(L3FnList));
 }
 
 void
index c5c36ee..b0554f8 100644 (file)
@@ -698,9 +698,6 @@ static struct stateentry downstl[] =
         CC_T308_2, l3_1tr6_t308_2},
 };
 
-#define DOWNSTL_LEN \
-       (sizeof(downstl) / sizeof(struct stateentry))
-
 static struct stateentry datastln1[] =
 {
        {SBIT(0),
@@ -735,9 +732,6 @@ static struct stateentry datastln1[] =
         MT_N1_REL_ACK, l3_1tr6_rel_ack}
 };
 
-#define DATASTLN1_LEN \
-       (sizeof(datastln1) / sizeof(struct stateentry))
-
 static struct stateentry manstatelist[] =
 {
         {SBIT(2),
@@ -746,8 +740,6 @@ static struct stateentry manstatelist[] =
          DL_RELEASE | INDICATION, l3_1tr6_dl_release},
 };
  
-#define MANSLLEN \
-        (sizeof(manstatelist) / sizeof(struct stateentry))
 /* *INDENT-ON* */
 
 static void
@@ -840,11 +832,11 @@ up1tr6(struct PStack *st, int pr, void *arg)
                                mt = MT_N1_INVALID;
                        }
                }
-               for (i = 0; i < DATASTLN1_LEN; i++)
+               for (i = 0; i < ARRAY_SIZE(datastln1); i++)
                        if ((mt == datastln1[i].primitive) &&
                            ((1 << proc->state) & datastln1[i].state))
                                break;
-               if (i == DATASTLN1_LEN) {
+               if (i == ARRAY_SIZE(datastln1)) {
                        dev_kfree_skb(skb);
                        if (st->l3.debug & L3_DEB_STATE) {
                                sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
@@ -892,11 +884,11 @@ down1tr6(struct PStack *st, int pr, void *arg)
                proc = arg;
        }
 
-       for (i = 0; i < DOWNSTL_LEN; i++)
+       for (i = 0; i < ARRAY_SIZE(downstl); i++)
                if ((pr == downstl[i].primitive) &&
                    ((1 << proc->state) & downstl[i].state))
                        break;
-       if (i == DOWNSTL_LEN) {
+       if (i == ARRAY_SIZE(downstl)) {
                if (st->l3.debug & L3_DEB_STATE) {
                        sprintf(tmp, "down1tr6 state %d prim %d unhandled",
                                proc->state, pr);
@@ -922,11 +914,11 @@ man1tr6(struct PStack *st, int pr, void *arg)
                 printk(KERN_ERR "HiSax man1tr6 without proc pr=%04x\n", pr);
                 return;
         }
-        for (i = 0; i < MANSLLEN; i++)
+        for (i = 0; i < ARRAY_SIZE(manstatelist); i++)
                 if ((pr == manstatelist[i].primitive) &&
                     ((1 << proc->state) & manstatelist[i].state))
                         break;
-        if (i == MANSLLEN) {
+        if (i == ARRAY_SIZE(manstatelist)) {
                 if (st->l3.debug & L3_DEB_STATE) {
                         l3_debug(st, "cr %d man1tr6 state %d prim %d unhandled",
                                 proc->callref & 0x7f, proc->state, pr);
index 99feae8..a12fa4d 100644 (file)
@@ -2820,9 +2820,6 @@ static struct stateentry downstatelist[] =
         CC_T309, l3dss1_dl_release},
 };
 
-#define DOWNSLLEN \
-       (sizeof(downstatelist) / sizeof(struct stateentry))
-
 static struct stateentry datastatelist[] =
 {
        {ALL_STATES,
@@ -2875,9 +2872,6 @@ static struct stateentry datastatelist[] =
         MT_RESUME_REJECT, l3dss1_resume_rej},
 };
 
-#define DATASLLEN \
-       (sizeof(datastatelist) / sizeof(struct stateentry))
-
 static struct stateentry globalmes_list[] =
 {
        {ALL_STATES,
@@ -2888,8 +2882,6 @@ static struct stateentry globalmes_list[] =
         MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
 */
 };
-#define GLOBALM_LEN \
-       (sizeof(globalmes_list) / sizeof(struct stateentry))
 
 static struct stateentry manstatelist[] =
 {
@@ -2903,8 +2895,6 @@ static struct stateentry manstatelist[] =
          DL_RELEASE | INDICATION, l3dss1_dl_release},
 };
 
-#define MANSLLEN \
-        (sizeof(manstatelist) / sizeof(struct stateentry))
 /* *INDENT-ON* */
 
 
@@ -2918,11 +2908,11 @@ global_handler(struct PStack *st, int mt, struct sk_buff *skb)
        struct l3_process *proc = st->l3.global;
 
        proc->callref = skb->data[2]; /* cr flag */
-       for (i = 0; i < GLOBALM_LEN; i++)
+       for (i = 0; i < ARRAY_SIZE(globalmes_list); i++)
                if ((mt == globalmes_list[i].primitive) &&
                    ((1 << proc->state) & globalmes_list[i].state))
                        break;
-       if (i == GLOBALM_LEN) {
+       if (i == ARRAY_SIZE(globalmes_list)) {
                if (st->l3.debug & L3_DEB_STATE) {
                        l3_debug(st, "dss1 global state %d mt %x unhandled",
                                proc->state, mt);
@@ -3097,11 +3087,11 @@ dss1up(struct PStack *st, int pr, void *arg)
        }
        if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) 
          l3dss1_deliver_display(proc, pr, p); /* Display IE included */
-       for (i = 0; i < DATASLLEN; i++)
+       for (i = 0; i < ARRAY_SIZE(datastatelist); i++)
                if ((mt == datastatelist[i].primitive) &&
                    ((1 << proc->state) & datastatelist[i].state))
                        break;
-       if (i == DATASLLEN) {
+       if (i == ARRAY_SIZE(datastatelist)) {
                if (st->l3.debug & L3_DEB_STATE) {
                        l3_debug(st, "dss1up%sstate %d mt %#x unhandled",
                                (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
@@ -3156,11 +3146,11 @@ dss1down(struct PStack *st, int pr, void *arg)
                return;
        }  
 
-       for (i = 0; i < DOWNSLLEN; i++)
+       for (i = 0; i < ARRAY_SIZE(downstatelist); i++)
                if ((pr == downstatelist[i].primitive) &&
                    ((1 << proc->state) & downstatelist[i].state))
                        break;
-       if (i == DOWNSLLEN) {
+       if (i == ARRAY_SIZE(downstatelist)) {
                if (st->l3.debug & L3_DEB_STATE) {
                        l3_debug(st, "dss1down state %d prim %#x unhandled",
                                proc->state, pr);
@@ -3184,11 +3174,11 @@ dss1man(struct PStack *st, int pr, void *arg)
                 printk(KERN_ERR "HiSax dss1man without proc pr=%04x\n", pr);
                 return;
         }
-        for (i = 0; i < MANSLLEN; i++)
+        for (i = 0; i < ARRAY_SIZE(manstatelist); i++)
                 if ((pr == manstatelist[i].primitive) &&
                     ((1 << proc->state) & manstatelist[i].state))
                         break;
-        if (i == MANSLLEN) {
+        if (i == ARRAY_SIZE(manstatelist)) {
                 if (st->l3.debug & L3_DEB_STATE) {
                         l3_debug(st, "cr %d dss1man state %d prim %#x unhandled",
                                 proc->callref & 0x7f, proc->state, pr);
index f7041d5..4622d43 100644 (file)
@@ -2755,9 +2755,6 @@ static struct stateentry downstatelist[] =
         CC_TSPID, l3ni1_spid_tout },
 };
 
-#define DOWNSLLEN \
-       (sizeof(downstatelist) / sizeof(struct stateentry))
-
 static struct stateentry datastatelist[] =
 {
        {ALL_STATES,
@@ -2810,9 +2807,6 @@ static struct stateentry datastatelist[] =
         MT_RESUME_REJECT, l3ni1_resume_rej},
 };
 
-#define DATASLLEN \
-       (sizeof(datastatelist) / sizeof(struct stateentry))
-
 static struct stateentry globalmes_list[] =
 {
        {ALL_STATES,
@@ -2825,8 +2819,6 @@ static struct stateentry globalmes_list[] =
        { SBIT( 0 ), MT_DL_ESTABLISHED, l3ni1_spid_send },
        { SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ), MT_INFORMATION, l3ni1_spid_epid },
 };
-#define GLOBALM_LEN \
-       (sizeof(globalmes_list) / sizeof(struct stateentry))
 
 static struct stateentry manstatelist[] =
 {
@@ -2840,8 +2832,6 @@ static struct stateentry manstatelist[] =
          DL_RELEASE | INDICATION, l3ni1_dl_release},
 };
 
-#define MANSLLEN \
-        (sizeof(manstatelist) / sizeof(struct stateentry))
 /* *INDENT-ON* */
 
 
@@ -2858,11 +2848,11 @@ global_handler(struct PStack *st, int mt, struct sk_buff *skb)
                proc->callref = skb->data[2]; /* cr flag */
        else
                proc->callref = 0;
-       for (i = 0; i < GLOBALM_LEN; i++)
+       for (i = 0; i < ARRAY_SIZE(globalmes_list); i++)
                if ((mt == globalmes_list[i].primitive) &&
                    ((1 << proc->state) & globalmes_list[i].state))
                        break;
-       if (i == GLOBALM_LEN) {
+       if (i == ARRAY_SIZE(globalmes_list)) {
                if (st->l3.debug & L3_DEB_STATE) {
                        l3_debug(st, "ni1 global state %d mt %x unhandled",
                                proc->state, mt);
@@ -3049,11 +3039,11 @@ ni1up(struct PStack *st, int pr, void *arg)
        }
        if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL) 
          l3ni1_deliver_display(proc, pr, p); /* Display IE included */
-       for (i = 0; i < DATASLLEN; i++)
+       for (i = 0; i < ARRAY_SIZE(datastatelist); i++)
                if ((mt == datastatelist[i].primitive) &&
                    ((1 << proc->state) & datastatelist[i].state))
                        break;
-       if (i == DATASLLEN) {
+       if (i == ARRAY_SIZE(datastatelist)) {
                if (st->l3.debug & L3_DEB_STATE) {
                        l3_debug(st, "ni1up%sstate %d mt %#x unhandled",
                                (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
@@ -3108,11 +3098,11 @@ ni1down(struct PStack *st, int pr, void *arg)
                return;
        }  
 
-       for (i = 0; i < DOWNSLLEN; i++)
+       for (i = 0; i < ARRAY_SIZE(downstatelist); i++)
                if ((pr == downstatelist[i].primitive) &&
                    ((1 << proc->state) & downstatelist[i].state))
                        break;
-       if (i == DOWNSLLEN) {
+       if (i == ARRAY_SIZE(downstatelist)) {
                if (st->l3.debug & L3_DEB_STATE) {
                        l3_debug(st, "ni1down state %d prim %#x unhandled",
                                proc->state, pr);
@@ -3136,11 +3126,11 @@ ni1man(struct PStack *st, int pr, void *arg)
                 printk(KERN_ERR "HiSax ni1man without proc pr=%04x\n", pr);
                 return;
         }
-        for (i = 0; i < MANSLLEN; i++)
+        for (i = 0; i < ARRAY_SIZE(manstatelist); i++)
                 if ((pr == manstatelist[i].primitive) &&
                     ((1 << proc->state) & manstatelist[i].state))
                         break;
-        if (i == MANSLLEN) {
+        if (i == ARRAY_SIZE(manstatelist)) {
                 if (st->l3.debug & L3_DEB_STATE) {
                         l3_debug(st, "cr %d ni1man state %d prim %#x unhandled",
                                 proc->callref & 0x7f, proc->state, pr);
index aacbf0d..8b853d5 100644 (file)
@@ -140,7 +140,7 @@ struct MessageType {
        }
 };
 
-#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+#define MTSIZE ARRAY_SIZE(mtlist)
 
 static
 struct MessageType mt_n0[] =
@@ -157,7 +157,7 @@ struct MessageType mt_n0[] =
        {MT_N0_CLO_ACK, "CLOse ACKnowledge"}
 };
 
-#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType))
+#define MT_N0_LEN ARRAY_SIZE(mt_n0)
 
 static
 struct MessageType mt_n1[] =
@@ -194,7 +194,7 @@ struct MessageType mt_n1[] =
        {MT_N1_STAT, "STATus"}
 };
 
-#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
+#define MT_N1_LEN ARRAY_SIZE(mt_n1)
 
 
 static int
@@ -438,7 +438,7 @@ struct CauseValue {
        },
 };
 
-#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+#define CVSIZE ARRAY_SIZE(cvlist)
 
 static
 int
@@ -516,7 +516,7 @@ struct MessageType cause_1tr6[] =
        {CAUSE_UserInfoDiscarded, "User Info Discarded"}
 };
 
-static int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
+static int cause_1tr6_len = ARRAY_SIZE(cause_1tr6);
 
 static int
 prcause_1tr6(char *dest, u_char * p)
@@ -865,7 +865,7 @@ struct DTag { /* Display tags */
        { 0x96, "Redirection name" },
        { 0x9e, "Text" },
 };
-#define DTAGSIZE sizeof(dtaglist)/sizeof(struct DTag)
+#define DTAGSIZE ARRAY_SIZE(dtaglist)
 
 static int
 disptext_ni1(char *dest, u_char * p)
@@ -1074,7 +1074,7 @@ struct InformationElement {
 };
 
 
-#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+#define IESIZE ARRAY_SIZE(ielist)
 
 static
 struct InformationElement ielist_ni1[] = {
@@ -1102,7 +1102,7 @@ struct InformationElement ielist_ni1[] = {
 };
 
 
-#define IESIZE_NI1 sizeof(ielist_ni1)/sizeof(struct InformationElement)
+#define IESIZE_NI1 ARRAY_SIZE(ielist_ni1)
 
 static
 struct InformationElement ielist_ni1_cs5[] = {
@@ -1110,14 +1110,14 @@ struct InformationElement ielist_ni1_cs5[] = {
        { 0x2a, "Display text", disptext_ni1 },
 };
 
-#define IESIZE_NI1_CS5 sizeof(ielist_ni1_cs5)/sizeof(struct InformationElement)
+#define IESIZE_NI1_CS5 ARRAY_SIZE(ielist_ni1_cs5)
 
 static
 struct InformationElement ielist_ni1_cs6[] = {
        { 0x7b, "Call appearance", general_ni1 },
 };
 
-#define IESIZE_NI1_CS6 sizeof(ielist_ni1_cs6)/sizeof(struct InformationElement)
+#define IESIZE_NI1_CS6 ARRAY_SIZE(ielist_ni1_cs6)
 
 static struct InformationElement we_0[] =
 {
@@ -1133,7 +1133,7 @@ static struct InformationElement we_0[] =
        {WE0_userInfo, "User Info", general}
 };
 
-#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement))
+#define WE_0_LEN ARRAY_SIZE(we_0)
 
 static struct InformationElement we_6[] =
 {
@@ -1145,7 +1145,7 @@ static struct InformationElement we_6[] =
        {WE6_statusCalled, "Status Called", general},
        {WE6_addTransAttr, "Additional Transmission Attributes", general}
 };
-#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement))
+#define WE_6_LEN ARRAY_SIZE(we_6)
 
 int
 QuickHex(char *txt, u_char * p, int cnt)
index cff7a63..64f78a8 100644 (file)
@@ -226,7 +226,7 @@ printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__,  __func__ , ## arg)
 #define INFO(format, arg...) \
 printk(KERN_INFO "%s:%s: " format "\n" , __FILE__,  __func__ , ## arg)
 
-#include "isdnhdlc.h"
+#include <linux/isdn/hdlc.h>
 #include "fsm.h"
 #include "hisax_if.h"
 #include <linux/skbuff.h>
index 0074b60..95b1cdd 100644 (file)
@@ -218,7 +218,10 @@ static void st5481B_mode(struct st5481_bcs *bcs, int mode)
        if (bcs->mode != L1_MODE_NULL) {
                // Open the B channel
                if (bcs->mode != L1_MODE_TRANS) {
-                       isdnhdlc_out_init(&b_out->hdlc_state, 0, bcs->mode == L1_MODE_HDLC_56K);
+                       u32 features = HDLC_BITREVERSE;
+                       if (bcs->mode == L1_MODE_HDLC_56K)
+                               features |= HDLC_56KBIT;
+                       isdnhdlc_out_init(&b_out->hdlc_state, features);
                }
                st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2, NULL, NULL);
        
index 077991c..39e8e49 100644 (file)
@@ -417,7 +417,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg)
 
        DBG(2,"len=%d",skb->len);
 
-       isdnhdlc_out_init(&d_out->hdlc_state, 1, 0);
+       isdnhdlc_out_init(&d_out->hdlc_state, HDLC_DCHANNEL | HDLC_BITREVERSE);
 
        if (test_and_set_bit(buf_nr, &d_out->busy)) {
                WARNING("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
index 2b3a055..10d41c5 100644 (file)
@@ -637,10 +637,13 @@ void st5481_in_mode(struct st5481_in *in, int mode)
        usb_unlink_urb(in->urb[1]);
 
        if (in->mode != L1_MODE_NULL) {
-               if (in->mode != L1_MODE_TRANS)
-                       isdnhdlc_rcv_init(&in->hdlc_state,
-                               in->mode == L1_MODE_HDLC_56K);
-               
+               if (in->mode != L1_MODE_TRANS) {
+                       u32 features = HDLC_BITREVERSE;
+
+                       if (in->mode == L1_MODE_HDLC_56K)
+                               features |= HDLC_56KBIT;
+                       isdnhdlc_rcv_init(&in->hdlc_state, features);
+               }
                st5481_usb_pipe_reset(in->adapter, in->ep, NULL, NULL);
                st5481_usb_device_ctrl_msg(in->adapter, in->counter,
                                           in->packet_size,
index ceb0df9..6e65424 100644 (file)
@@ -447,8 +447,6 @@ static struct FsmNode TeiFnList[] __initdata =
        {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
 };
 
-#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode))
-
 int __init
 TeiNew(void)
 {
@@ -456,7 +454,7 @@ TeiNew(void)
        teifsm.event_count = TEI_EVENT_COUNT;
        teifsm.strEvent = strTeiEvent;
        teifsm.strState = strTeiState;
-       return FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
+       return FsmNew(&teifsm, TeiFnList, ARRAY_SIZE(TeiFnList));
 }
 
 void
index bb1c8dd..c4d862c 100644 (file)
@@ -105,8 +105,6 @@ W6692_bh(struct work_struct *work)
                container_of(work, struct IsdnCardState, tqueue);
        struct PStack *stptr;
 
-       if (!cs)
-               return;
        if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
                if (cs->debug)
                        debugl1(cs, "D-Channel Busy cleared");
index ed3510f..dd744ff 100644 (file)
@@ -2,6 +2,8 @@
 # Old ISDN4Linux config
 #
 
+if ISDN_I4L
+
 config ISDN_PPP
        bool "Support synchronous PPP"
        depends on INET
@@ -135,3 +137,12 @@ source "drivers/isdn/act2000/Kconfig"
 source "drivers/isdn/hysdn/Kconfig"
 
 endmenu
+# end ISDN_I4L
+endif
+
+config ISDN_HDLC
+       tristate 
+       depends on HISAX_ST5481
+       select CRC_CCITT
+       select BITREVERSE
+
index 49a06c0..cb9d3bb 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_ISDN_I4L)         += isdn.o
 obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
+obj-$(CONFIG_ISDN_HDLC)                += isdnhdlc.o
 
 # Multipart objects.
 
similarity index 54%
rename from drivers/isdn/hisax/isdnhdlc.c
rename to drivers/isdn/i4l/isdnhdlc.c
index c69a77a..c989aa3 100644 (file)
@@ -1,29 +1,32 @@
 /*
  * isdnhdlc.c  --  General purpose ISDN HDLC decoder.
  *
- *Copyright (C) 2002   Wolfgang Mües      <wolfgang@iksw-muees.de>
- *             2001    Frode Isaksen      <fisaksen@bewan.com>
- *              2001   Kai Germaschewski  <kai.germaschewski@gmx.de>
+ * Copyright (C)
+ *     2009    Karsten Keil            <keil@b1-systems.de>
+ *     2002    Wolfgang Mües          <wolfgang@iksw-muees.de>
+ *     2001    Frode Isaksen           <fisaksen@bewan.com>
+ *      2001   Kai Germaschewski       <kai.germaschewski@gmx.de>
  *
- *      This program is free software; you can redistribute it and/or modify
- *      it under the terms of the GNU General Public License as published by
- *      the Free Software Foundation; either version 2 of the License, or
- *      (at your option) any later version.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
  *
- *      This program is distributed in the hope that it will be useful,
- *      but WITHOUT ANY WARRANTY; without even the implied warranty of
- *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *      GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
  *
- *      You should have received a copy of the GNU General Public License
- *      along with this program; if not, write to the Free Software
- *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/crc-ccitt.h>
-#include "isdnhdlc.h"
+#include <linux/isdn/hdlc.h>
+#include <linux/bitrev.h>
 
 /*-------------------------------------------------------------------*/
 
@@ -36,44 +39,32 @@ MODULE_LICENSE("GPL");
 /*-------------------------------------------------------------------*/
 
 enum {
-       HDLC_FAST_IDLE,HDLC_GET_FLAG_B0,HDLC_GETFLAG_B1A6,HDLC_GETFLAG_B7,
-       HDLC_GET_DATA,HDLC_FAST_FLAG
+       HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7,
+       HDLC_GET_DATA, HDLC_FAST_FLAG
 };
 
 enum {
-       HDLC_SEND_DATA,HDLC_SEND_CRC1,HDLC_SEND_FAST_FLAG,
-       HDLC_SEND_FIRST_FLAG,HDLC_SEND_CRC2,HDLC_SEND_CLOSING_FLAG,
-       HDLC_SEND_IDLE1,HDLC_SEND_FAST_IDLE,HDLC_SENDFLAG_B0,
-       HDLC_SENDFLAG_B1A6,HDLC_SENDFLAG_B7,STOPPED
+       HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG,
+       HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG,
+       HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0,
+       HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE
 };
 
-void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56)
+void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features)
 {
-       hdlc->bit_shift = 0;
-       hdlc->hdlc_bits1 = 0;
-       hdlc->data_bits = 0;
-       hdlc->ffbit_shift = 0;
-       hdlc->data_received = 0;
+       memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
        hdlc->state = HDLC_GET_DATA;
-       hdlc->do_adapt56 = do_adapt56;
-       hdlc->dchannel = 0;
-       hdlc->crc = 0;
-       hdlc->cbin = 0;
-       hdlc->shift_reg = 0;
-       hdlc->ffvalue = 0;
-       hdlc->dstpos = 0;
+       if (features & HDLC_56KBIT)
+               hdlc->do_adapt56 = 1;
+       if (features & HDLC_BITREVERSE)
+               hdlc->do_bitreverse = 1;
 }
+EXPORT_SYMBOL(isdnhdlc_out_init);
 
-void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc, int is_d_channel, int do_adapt56)
+void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features)
 {
-       hdlc->bit_shift = 0;
-       hdlc->hdlc_bits1 = 0;
-       hdlc->data_bits = 0;
-       hdlc->ffbit_shift = 0;
-       hdlc->data_received = 0;
-       hdlc->do_closing = 0;
-       hdlc->ffvalue = 0;
-       if (is_d_channel) {
+       memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
+       if (features & HDLC_DCHANNEL) {
                hdlc->dchannel = 1;
                hdlc->state = HDLC_SEND_FIRST_FLAG;
        } else {
@@ -82,16 +73,32 @@ void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc, int is_d_channel, int do_ada
                hdlc->ffvalue = 0x7e;
        }
        hdlc->cbin = 0x7e;
-       hdlc->bit_shift = 0;
-       if(do_adapt56){
+       if (features & HDLC_56KBIT) {
                hdlc->do_adapt56 = 1;
-               hdlc->data_bits = 0;
                hdlc->state = HDLC_SENDFLAG_B0;
-       } else {
-               hdlc->do_adapt56 = 0;
+       } else
                hdlc->data_bits = 8;
+       if (features & HDLC_BITREVERSE)
+               hdlc->do_bitreverse = 1;
+}
+EXPORT_SYMBOL(isdnhdlc_rcv_init);
+
+static int
+check_frame(struct isdnhdlc_vars *hdlc)
+{
+       int status;
+
+       if (hdlc->dstpos < 2)   /* too small - framing error */
+               status = -HDLC_FRAMING_ERROR;
+       else if (hdlc->crc != 0xf0b8)   /* crc error */
+               status = -HDLC_CRC_ERROR;
+       else {
+               /* remove CRC */
+               hdlc->dstpos -= 2;
+               /* good frame */
+               status = hdlc->dstpos;
        }
-       hdlc->shift_reg = 0;
+       return status;
 }
 
 /*
@@ -121,40 +128,67 @@ void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc, int is_d_channel, int do_ada
   returns - number of decoded bytes in the destination buffer and status
   flag.
  */
-int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
-                    int slen, int *count, unsigned char *dst, int dsize)
+int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen,
+       int *count, u8 *dst, int dsize)
 {
-       int status=0;
+       int status = 0;
 
-       static const unsigned char fast_flag[]={
-               0x00,0x00,0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f
+       static const unsigned char fast_flag[] = {
+               0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f
        };
 
-       static const unsigned char fast_flag_value[]={
-               0x00,0x7e,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f
+       static const unsigned char fast_flag_value[] = {
+               0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f
        };
 
-       static const unsigned char fast_abort[]={
-               0x00,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff
+       static const unsigned char fast_abort[] = {
+               0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
        };
 
+#define handle_fast_flag(h) \
+       do {\
+               if (h->cbin == fast_flag[h->bit_shift]) {\
+                       h->ffvalue = fast_flag_value[h->bit_shift];\
+                       h->state = HDLC_FAST_FLAG;\
+                       h->ffbit_shift = h->bit_shift;\
+                       h->bit_shift = 1;\
+               } else {\
+                       h->state = HDLC_GET_DATA;\
+                       h->data_received = 0;\
+               } \
+       } while (0)
+
+#define handle_abort(h) \
+       do {\
+               h->shift_reg = fast_abort[h->ffbit_shift - 1];\
+               h->hdlc_bits1 = h->ffbit_shift - 2;\
+               if (h->hdlc_bits1 < 0)\
+                       h->hdlc_bits1 = 0;\
+               h->data_bits = h->ffbit_shift - 1;\
+               h->state = HDLC_GET_DATA;\
+               h->data_received = 0;\
+       } while (0)
+
        *count = slen;
 
-       while(slen > 0){
-               if(hdlc->bit_shift==0){
-                       hdlc->cbin = *src++;
+       while (slen > 0) {
+               if (hdlc->bit_shift == 0) {
+                       /* the code is for bitreverse streams */
+                       if (hdlc->do_bitreverse == 0)
+                               hdlc->cbin = bitrev8(*src++);
+                       else
+                               hdlc->cbin = *src++;
                        slen--;
                        hdlc->bit_shift = 8;
-                       if(hdlc->do_adapt56){
-                               hdlc->bit_shift --;
-                       }
+                       if (hdlc->do_adapt56)
+                               hdlc->bit_shift--;
                }
 
-               switch(hdlc->state){
+               switch (hdlc->state) {
                case STOPPED:
                        return 0;
                case HDLC_FAST_IDLE:
-                       if(hdlc->cbin == 0xff){
+                       if (hdlc->cbin == 0xff) {
                                hdlc->bit_shift = 0;
                                break;
                        }
@@ -163,32 +197,30 @@ int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
                        hdlc->bit_shift = 8;
                        break;
                case HDLC_GET_FLAG_B0:
-                       if(!(hdlc->cbin & 0x80)) {
+                       if (!(hdlc->cbin & 0x80)) {
                                hdlc->state = HDLC_GETFLAG_B1A6;
                                hdlc->hdlc_bits1 = 0;
                        } else {
-                               if(!hdlc->do_adapt56){
-                                       if(++hdlc->hdlc_bits1 >=8 ) if(hdlc->bit_shift==1)
+                               if ((!hdlc->do_adapt56) &&
+                                   (++hdlc->hdlc_bits1 >= 8) &&
+                                   (hdlc->bit_shift == 1))
                                                hdlc->state = HDLC_FAST_IDLE;
-                               }
                        }
-                       hdlc->cbin<<=1;
-                       hdlc->bit_shift --;
+                       hdlc->cbin <<= 1;
+                       hdlc->bit_shift--;
                        break;
                case HDLC_GETFLAG_B1A6:
-                       if(hdlc->cbin & 0x80){
+                       if (hdlc->cbin & 0x80) {
                                hdlc->hdlc_bits1++;
-                               if(hdlc->hdlc_bits1==6){
+                               if (hdlc->hdlc_bits1 == 6)
                                        hdlc->state = HDLC_GETFLAG_B7;
-                               }
-                       } else {
+                       } else
                                hdlc->hdlc_bits1 = 0;
-                       }
-                       hdlc->cbin<<=1;
-                       hdlc->bit_shift --;
+                       hdlc->cbin <<= 1;
+                       hdlc->bit_shift--;
                        break;
                case HDLC_GETFLAG_B7:
-                       if(hdlc->cbin & 0x80) {
+                       if (hdlc->cbin & 0x80) {
                                hdlc->state = HDLC_GET_FLAG_B0;
                        } else {
                                hdlc->state = HDLC_GET_DATA;
@@ -198,74 +230,55 @@ int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
                                hdlc->data_bits = 0;
                                hdlc->data_received = 0;
                        }
-                       hdlc->cbin<<=1;
-                       hdlc->bit_shift --;
+                       hdlc->cbin <<= 1;
+                       hdlc->bit_shift--;
                        break;
                case HDLC_GET_DATA:
-                       if(hdlc->cbin & 0x80){
+                       if (hdlc->cbin & 0x80) {
                                hdlc->hdlc_bits1++;
-                               switch(hdlc->hdlc_bits1){
+                               switch (hdlc->hdlc_bits1) {
                                case 6:
                                        break;
                                case 7:
-                                       if(hdlc->data_received) {
-                                               // bad frame
+                                       if (hdlc->data_received)
+                                               /* bad frame */
                                                status = -HDLC_FRAMING_ERROR;
-                                       }
-                                       if(!hdlc->do_adapt56){
-                                               if(hdlc->cbin==fast_abort[hdlc->bit_shift+1]){
-                                                       hdlc->state = HDLC_FAST_IDLE;
-                                                       hdlc->bit_shift=1;
+                                       if (!hdlc->do_adapt56) {
+                                               if (hdlc->cbin == fast_abort
+                                                   [hdlc->bit_shift + 1]) {
+                                                       hdlc->state =
+                                                               HDLC_FAST_IDLE;
+                                                       hdlc->bit_shift = 1;
                                                        break;
                                                }
-                                       } else {
+                                       } else
                                                hdlc->state = HDLC_GET_FLAG_B0;
-                                       }
                                        break;
                                default:
-                                       hdlc->shift_reg>>=1;
+                                       hdlc->shift_reg >>= 1;
                                        hdlc->shift_reg |= 0x80;
                                        hdlc->data_bits++;
                                        break;
                                }
                        } else {
-                               switch(hdlc->hdlc_bits1){
+                               switch (hdlc->hdlc_bits1) {
                                case 5:
                                        break;
                                case 6:
-                                       if(hdlc->data_received){
-                                               if (hdlc->dstpos < 2) {
-                                                       status = -HDLC_FRAMING_ERROR;
-                                               } else if (hdlc->crc != 0xf0b8){
-                                                       // crc error
-                                                       status = -HDLC_CRC_ERROR;
-                                               } else {
-                                                       // remove CRC
-                                                       hdlc->dstpos -= 2;
-                                                       // good frame
-                                                       status = hdlc->dstpos;
-                                               }
-                                       }
+                                       if (hdlc->data_received)
+                                               status = check_frame(hdlc);
                                        hdlc->crc = 0xffff;
                                        hdlc->shift_reg = 0;
                                        hdlc->data_bits = 0;
-                                       if(!hdlc->do_adapt56){
-                                               if(hdlc->cbin==fast_flag[hdlc->bit_shift]){
-                                                       hdlc->ffvalue = fast_flag_value[hdlc->bit_shift];
-                                                       hdlc->state = HDLC_FAST_FLAG;
-                                                       hdlc->ffbit_shift = hdlc->bit_shift;
-                                                       hdlc->bit_shift = 1;
-                                               } else {
-                                                       hdlc->state = HDLC_GET_DATA;
-                                                       hdlc->data_received = 0;
-                                               }
-                                       } else {
+                                       if (!hdlc->do_adapt56)
+                                               handle_fast_flag(hdlc);
+                                       else {
                                                hdlc->state = HDLC_GET_DATA;
                                                hdlc->data_received = 0;
                                        }
                                        break;
                                default:
-                                       hdlc->shift_reg>>=1;
+                                       hdlc->shift_reg >>= 1;
                                        hdlc->data_bits++;
                                        break;
                                }
@@ -278,16 +291,17 @@ int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
                                hdlc->bit_shift--;
                                return status;
                        }
-                       if(hdlc->data_bits==8){
+                       if (hdlc->data_bits == 8) {
                                hdlc->data_bits = 0;
                                hdlc->data_received = 1;
-                               hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+                               hdlc->crc = crc_ccitt_byte(hdlc->crc,
+                                               hdlc->shift_reg);
 
-                               // good byte received
-                               if (hdlc->dstpos < dsize) {
+                               /* good byte received */
+                               if (hdlc->dstpos < dsize)
                                        dst[hdlc->dstpos++] = hdlc->shift_reg;
-                               else {
-                                       // frame too long
+                               else {
+                                       /* frame too long */
                                        status = -HDLC_LENGTH_ERROR;
                                        hdlc->dstpos = 0;
                                }
@@ -296,24 +310,18 @@ int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
                        hdlc->bit_shift--;
                        break;
                case HDLC_FAST_FLAG:
-                       if(hdlc->cbin==hdlc->ffvalue){
+                       if (hdlc->cbin == hdlc->ffvalue) {
                                hdlc->bit_shift = 0;
                                break;
                        } else {
-                               if(hdlc->cbin == 0xff){
+                               if (hdlc->cbin == 0xff) {
                                        hdlc->state = HDLC_FAST_IDLE;
-                                       hdlc->bit_shift=0;
-                               } else if(hdlc->ffbit_shift==8){
+                                       hdlc->bit_shift = 0;
+                               } else if (hdlc->ffbit_shift == 8) {
                                        hdlc->state = HDLC_GETFLAG_B7;
                                        break;
-                               } else {
-                                       hdlc->shift_reg = fast_abort[hdlc->ffbit_shift-1];
-                                       hdlc->hdlc_bits1 = hdlc->ffbit_shift-2;
-                                       if(hdlc->hdlc_bits1<0)hdlc->hdlc_bits1 = 0;
-                                       hdlc->data_bits = hdlc->ffbit_shift-1;
-                                       hdlc->state = HDLC_GET_DATA;
-                                       hdlc->data_received = 0;
-                               }
+                               } else
+                                       handle_abort(hdlc);
                        }
                        break;
                default:
@@ -323,7 +331,7 @@ int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
        *count -= slen;
        return 0;
 }
-
+EXPORT_SYMBOL(isdnhdlc_decode);
 /*
   isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
 
@@ -343,59 +351,70 @@ int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
   dsize - destination buffer size
   returns - number of encoded bytes in the destination buffer
 */
-int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
-               unsigned short slen, int *count,
-               unsigned char *dst, int dsize)
+int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen,
+       int *count, u8 *dst, int dsize)
 {
        static const unsigned char xfast_flag_value[] = {
-               0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e
+               0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e
        };
 
        int len = 0;
 
        *count = slen;
 
+       /* special handling for one byte frames */
+       if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG))
+               hdlc->state = HDLC_SENDFLAG_ONE;
        while (dsize > 0) {
-               if(hdlc->bit_shift==0){
-                       if(slen && !hdlc->do_closing){
+               if (hdlc->bit_shift == 0) {
+                       if (slen && !hdlc->do_closing) {
                                hdlc->shift_reg = *src++;
                                slen--;
                                if (slen == 0)
-                                       hdlc->do_closing = 1;  /* closing sequence, CRC + flag(s) */
+                                       /* closing sequence, CRC + flag(s) */
+                                       hdlc->do_closing = 1;
                                hdlc->bit_shift = 8;
                        } else {
-                               if(hdlc->state == HDLC_SEND_DATA){
-                                       if(hdlc->data_received){
+                               if (hdlc->state == HDLC_SEND_DATA) {
+                                       if (hdlc->data_received) {
                                                hdlc->state = HDLC_SEND_CRC1;
                                                hdlc->crc ^= 0xffff;
                                                hdlc->bit_shift = 8;
-                                               hdlc->shift_reg = hdlc->crc & 0xff;
-                                       } else if(!hdlc->do_adapt56){
-                                               hdlc->state = HDLC_SEND_FAST_FLAG;
-                                       } else {
-                                               hdlc->state = HDLC_SENDFLAG_B0;
-                                       }
+                                               hdlc->shift_reg =
+                                                       hdlc->crc & 0xff;
+                                       } else if (!hdlc->do_adapt56)
+                                               hdlc->state =
+                                                       HDLC_SEND_FAST_FLAG;
+                                       else
+                                               hdlc->state =
+                                                       HDLC_SENDFLAG_B0;
                                }
 
                        }
                }
 
-               switch(hdlc->state){
+               switch (hdlc->state) {
                case STOPPED:
                        while (dsize--)
                                *dst++ = 0xff;
-
                        return dsize;
                case HDLC_SEND_FAST_FLAG:
                        hdlc->do_closing = 0;
-                       if(slen == 0){
-                               *dst++ = hdlc->ffvalue;
+                       if (slen == 0) {
+                               /* the code is for bitreverse streams */
+                               if (hdlc->do_bitreverse == 0)
+                                       *dst++ = bitrev8(hdlc->ffvalue);
+                               else
+                                       *dst++ = hdlc->ffvalue;
                                len++;
                                dsize--;
                                break;
                        }
-                       if(hdlc->bit_shift==8){
-                               hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits);
+                       /* fall through */
+               case HDLC_SENDFLAG_ONE:
+                       if (hdlc->bit_shift == 8) {
+                               hdlc->cbin = hdlc->ffvalue >>
+                                       (8 - hdlc->data_bits);
                                hdlc->state = HDLC_SEND_DATA;
                                hdlc->crc = 0xffff;
                                hdlc->hdlc_bits1 = 0;
@@ -413,17 +432,17 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                        hdlc->cbin <<= 1;
                        hdlc->data_bits++;
                        hdlc->cbin++;
-                       if(++hdlc->hdlc_bits1 == 6)
+                       if (++hdlc->hdlc_bits1 == 6)
                                hdlc->state = HDLC_SENDFLAG_B7;
                        break;
                case HDLC_SENDFLAG_B7:
                        hdlc->cbin <<= 1;
                        hdlc->data_bits++;
-                       if(slen == 0){
+                       if (slen == 0) {
                                hdlc->state = HDLC_SENDFLAG_B0;
                                break;
                        }
-                       if(hdlc->bit_shift==8){
+                       if (hdlc->bit_shift == 8) {
                                hdlc->state = HDLC_SEND_DATA;
                                hdlc->crc = 0xffff;
                                hdlc->hdlc_bits1 = 0;
@@ -432,7 +451,7 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                        break;
                case HDLC_SEND_FIRST_FLAG:
                        hdlc->data_received = 1;
-                       if(hdlc->data_bits==8){
+                       if (hdlc->data_bits == 8) {
                                hdlc->state = HDLC_SEND_DATA;
                                hdlc->crc = 0xffff;
                                hdlc->hdlc_bits1 = 0;
@@ -440,11 +459,11 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                        }
                        hdlc->cbin <<= 1;
                        hdlc->data_bits++;
-                       if(hdlc->shift_reg & 0x01)
+                       if (hdlc->shift_reg & 0x01)
                                hdlc->cbin++;
                        hdlc->shift_reg >>= 1;
                        hdlc->bit_shift--;
-                       if(hdlc->bit_shift==0){
+                       if (hdlc->bit_shift == 0) {
                                hdlc->state = HDLC_SEND_DATA;
                                hdlc->crc = 0xffff;
                                hdlc->hdlc_bits1 = 0;
@@ -453,14 +472,14 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                case HDLC_SEND_DATA:
                        hdlc->cbin <<= 1;
                        hdlc->data_bits++;
-                       if(hdlc->hdlc_bits1 == 5){
+                       if (hdlc->hdlc_bits1 == 5) {
                                hdlc->hdlc_bits1 = 0;
                                break;
                        }
-                       if(hdlc->bit_shift==8){
-                               hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
-                       }
-                       if(hdlc->shift_reg & 0x01){
+                       if (hdlc->bit_shift == 8)
+                               hdlc->crc = crc_ccitt_byte(hdlc->crc,
+                                       hdlc->shift_reg);
+                       if (hdlc->shift_reg & 0x01) {
                                hdlc->hdlc_bits1++;
                                hdlc->cbin++;
                                hdlc->shift_reg >>= 1;
@@ -474,11 +493,11 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                case HDLC_SEND_CRC1:
                        hdlc->cbin <<= 1;
                        hdlc->data_bits++;
-                       if(hdlc->hdlc_bits1 == 5){
+                       if (hdlc->hdlc_bits1 == 5) {
                                hdlc->hdlc_bits1 = 0;
                                break;
                        }
-                       if(hdlc->shift_reg & 0x01){
+                       if (hdlc->shift_reg & 0x01) {
                                hdlc->hdlc_bits1++;
                                hdlc->cbin++;
                                hdlc->shift_reg >>= 1;
@@ -488,7 +507,7 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                                hdlc->shift_reg >>= 1;
                                hdlc->bit_shift--;
                        }
-                       if(hdlc->bit_shift==0){
+                       if (hdlc->bit_shift == 0) {
                                hdlc->shift_reg = (hdlc->crc >> 8);
                                hdlc->state = HDLC_SEND_CRC2;
                                hdlc->bit_shift = 8;
@@ -497,11 +516,11 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                case HDLC_SEND_CRC2:
                        hdlc->cbin <<= 1;
                        hdlc->data_bits++;
-                       if(hdlc->hdlc_bits1 == 5){
+                       if (hdlc->hdlc_bits1 == 5) {
                                hdlc->hdlc_bits1 = 0;
                                break;
                        }
-                       if(hdlc->shift_reg & 0x01){
+                       if (hdlc->shift_reg & 0x01) {
                                hdlc->hdlc_bits1++;
                                hdlc->cbin++;
                                hdlc->shift_reg >>= 1;
@@ -511,7 +530,7 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                                hdlc->shift_reg >>= 1;
                                hdlc->bit_shift--;
                        }
-                       if(hdlc->bit_shift==0){
+                       if (hdlc->bit_shift == 0) {
                                hdlc->shift_reg = 0x7e;
                                hdlc->state = HDLC_SEND_CLOSING_FLAG;
                                hdlc->bit_shift = 8;
@@ -520,33 +539,36 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                case HDLC_SEND_CLOSING_FLAG:
                        hdlc->cbin <<= 1;
                        hdlc->data_bits++;
-                       if(hdlc->hdlc_bits1 == 5){
+                       if (hdlc->hdlc_bits1 == 5) {
                                hdlc->hdlc_bits1 = 0;
                                break;
                        }
-                       if(hdlc->shift_reg & 0x01){
+                       if (hdlc->shift_reg & 0x01)
                                hdlc->cbin++;
-                       }
                        hdlc->shift_reg >>= 1;
                        hdlc->bit_shift--;
-                       if(hdlc->bit_shift==0){
-                               hdlc->ffvalue = xfast_flag_value[hdlc->data_bits];
-                               if(hdlc->dchannel){
+                       if (hdlc->bit_shift == 0) {
+                               hdlc->ffvalue =
+                                       xfast_flag_value[hdlc->data_bits];
+                               if (hdlc->dchannel) {
                                        hdlc->ffvalue = 0x7e;
                                        hdlc->state = HDLC_SEND_IDLE1;
                                        hdlc->bit_shift = 8-hdlc->data_bits;
-                                       if(hdlc->bit_shift==0)
-                                               hdlc->state = HDLC_SEND_FAST_IDLE;
+                                       if (hdlc->bit_shift == 0)
+                                               hdlc->state =
+                                                       HDLC_SEND_FAST_IDLE;
                                } else {
-                                       if(!hdlc->do_adapt56){
-                                               hdlc->state = HDLC_SEND_FAST_FLAG;
+                                       if (!hdlc->do_adapt56) {
+                                               hdlc->state =
+                                                       HDLC_SEND_FAST_FLAG;
                                                hdlc->data_received = 0;
                                        } else {
                                                hdlc->state = HDLC_SENDFLAG_B0;
                                                hdlc->data_received = 0;
                                        }
-                                       // Finished with this frame, send flags
-                                       if (dsize > 1) dsize = 1;
+                                       /* Finished this frame, send flags */
+                                       if (dsize > 1)
+                                               dsize = 1;
                                }
                        }
                        break;
@@ -556,7 +578,7 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                        hdlc->cbin++;
                        hdlc->data_bits++;
                        hdlc->bit_shift--;
-                       if(hdlc->bit_shift==0){
+                       if (hdlc->bit_shift == 0) {
                                hdlc->state = HDLC_SEND_FAST_IDLE;
                                hdlc->bit_shift = 0;
                        }
@@ -565,12 +587,17 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                        hdlc->do_closing = 0;
                        hdlc->cbin = 0xff;
                        hdlc->data_bits = 8;
-                       if(hdlc->bit_shift == 8){
+                       if (hdlc->bit_shift == 8) {
                                hdlc->cbin = 0x7e;
                                hdlc->state = HDLC_SEND_FIRST_FLAG;
                        } else {
-                               *dst++ = hdlc->cbin;
-                               hdlc->bit_shift = hdlc->data_bits = 0;
+                               /* the code is for bitreverse streams */
+                               if (hdlc->do_bitreverse == 0)
+                                       *dst++ = bitrev8(hdlc->cbin);
+                               else
+                                       *dst++ = hdlc->cbin;
+                               hdlc->bit_shift = 0;
+                               hdlc->data_bits = 0;
                                len++;
                                dsize = 0;
                        }
@@ -578,15 +605,19 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
                default:
                        break;
                }
-               if(hdlc->do_adapt56){
-                       if(hdlc->data_bits==7){
+               if (hdlc->do_adapt56) {
+                       if (hdlc->data_bits == 7) {
                                hdlc->cbin <<= 1;
                                hdlc->cbin++;
                                hdlc->data_bits++;
                        }
                }
-               if(hdlc->data_bits==8){
-                       *dst++ = hdlc->cbin;
+               if (hdlc->data_bits == 8) {
+                       /* the code is for bitreverse streams */
+                       if (hdlc->do_bitreverse == 0)
+                               *dst++ = bitrev8(hdlc->cbin);
+                       else
+                               *dst++ = hdlc->cbin;
                        hdlc->data_bits = 0;
                        len++;
                        dsize--;
@@ -596,8 +627,4 @@ int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
 
        return len;
 }
-
-EXPORT_SYMBOL(isdnhdlc_rcv_init);
-EXPORT_SYMBOL(isdnhdlc_decode);
-EXPORT_SYMBOL(isdnhdlc_out_init);
 EXPORT_SYMBOL(isdnhdlc_encode);
index 0481a0c..e8049be 100644 (file)
@@ -114,13 +114,14 @@ mISDN_freedchannel(struct dchannel *ch)
 }
 EXPORT_SYMBOL(mISDN_freedchannel);
 
-int
-mISDN_freebchannel(struct bchannel *ch)
+void
+mISDN_clear_bchannel(struct bchannel *ch)
 {
        if (ch->tx_skb) {
                dev_kfree_skb(ch->tx_skb);
                ch->tx_skb = NULL;
        }
+       ch->tx_idx = 0;
        if (ch->rx_skb) {
                dev_kfree_skb(ch->rx_skb);
                ch->rx_skb = NULL;
@@ -129,6 +130,16 @@ mISDN_freebchannel(struct bchannel *ch)
                dev_kfree_skb(ch->next_skb);
                ch->next_skb = NULL;
        }
+       test_and_clear_bit(FLG_TX_BUSY, &ch->Flags);
+       test_and_clear_bit(FLG_TX_NEXT, &ch->Flags);
+       test_and_clear_bit(FLG_ACTIVE, &ch->Flags);
+}
+EXPORT_SYMBOL(mISDN_clear_bchannel);
+
+int
+mISDN_freebchannel(struct bchannel *ch)
+{
+       mISDN_clear_bchannel(ch);
        skb_queue_purge(&ch->rqueue);
        ch->rcount = 0;
        flush_scheduled_work();
index 9c2589e..e17f004 100644 (file)
@@ -1832,8 +1832,6 @@ static struct FsmNode L2FnList[] =
        {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
 };
 
-#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
-
 static int
 ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb)
 {
index ac28dd5..6158c0f 100644 (file)
@@ -53,7 +53,7 @@ static s32  igb_setup_fiber_serdes_link_82575(struct e1000_hw *);
 static s32  igb_write_phy_reg_sgmii_82575(struct e1000_hw *, u32, u16);
 static void igb_clear_hw_cntrs_82575(struct e1000_hw *);
 static s32  igb_acquire_swfw_sync_82575(struct e1000_hw *, u16);
-static s32  igb_configure_pcs_link_82575(struct e1000_hw *);
+static void igb_configure_pcs_link_82575(struct e1000_hw *);
 static s32  igb_get_pcs_speed_and_duplex_82575(struct e1000_hw *, u16 *,
                                                 u16 *);
 static s32  igb_get_phy_id_82575(struct e1000_hw *);
@@ -61,6 +61,7 @@ static void igb_release_swfw_sync_82575(struct e1000_hw *, u16);
 static bool igb_sgmii_active_82575(struct e1000_hw *);
 static s32  igb_reset_init_script_82575(struct e1000_hw *);
 static s32  igb_read_mac_addr_82575(struct e1000_hw *);
+static s32  igb_set_pcie_completion_timeout(struct e1000_hw *hw);
 
 static s32 igb_get_invariants_82575(struct e1000_hw *hw)
 {
@@ -84,6 +85,7 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
        case E1000_DEV_ID_82576_FIBER:
        case E1000_DEV_ID_82576_SERDES:
        case E1000_DEV_ID_82576_QUAD_COPPER:
+       case E1000_DEV_ID_82576_SERDES_QUAD:
                mac->type = e1000_82576;
                break;
        default:
@@ -170,6 +172,10 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
                size = 14;
        nvm->word_size = 1 << size;
 
+       /* if 82576 then initialize mailbox parameters */
+       if (mac->type == e1000_82576)
+               igb_init_mbx_params_pf(hw);
+
        /* setup PHY parameters */
        if (phy->media_type != e1000_media_type_copper) {
                phy->type = e1000_phy_none;
@@ -219,10 +225,6 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
                return -E1000_ERR_PHY;
        }
 
-       /* if 82576 then initialize mailbox parameters */
-       if (mac->type == e1000_82576)
-               igb_init_mbx_params_pf(hw);
-
        return 0;
 }
 
@@ -763,98 +765,6 @@ static s32 igb_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, u16 *speed,
        return 0;
 }
 
-/**
- *  igb_init_rx_addrs_82575 - Initialize receive address's
- *  @hw: pointer to the HW structure
- *  @rar_count: receive address registers
- *
- *  Setups the receive address registers by setting the base receive address
- *  register to the devices MAC address and clearing all the other receive
- *  address registers to 0.
- **/
-static void igb_init_rx_addrs_82575(struct e1000_hw *hw, u16 rar_count)
-{
-       u32 i;
-       u8 addr[6] = {0,0,0,0,0,0};
-       /*
-        * This function is essentially the same as that of
-        * e1000_init_rx_addrs_generic. However it also takes care
-        * of the special case where the register offset of the
-        * second set of RARs begins elsewhere. This is implicitly taken care by
-        * function e1000_rar_set_generic.
-        */
-
-       hw_dbg("e1000_init_rx_addrs_82575");
-
-       /* Setup the receive address */
-       hw_dbg("Programming MAC Address into RAR[0]\n");
-       hw->mac.ops.rar_set(hw, hw->mac.addr, 0);
-
-       /* Zero out the other (rar_entry_count - 1) receive addresses */
-       hw_dbg("Clearing RAR[1-%u]\n", rar_count-1);
-       for (i = 1; i < rar_count; i++)
-           hw->mac.ops.rar_set(hw, addr, i);
-}
-
-/**
- *  igb_update_mc_addr_list - Update Multicast addresses
- *  @hw: pointer to the HW structure
- *  @mc_addr_list: array of multicast addresses to program
- *  @mc_addr_count: number of multicast addresses to program
- *  @rar_used_count: the first RAR register free to program
- *  @rar_count: total number of supported Receive Address Registers
- *
- *  Updates the Receive Address Registers and Multicast Table Array.
- *  The caller must have a packed mc_addr_list of multicast addresses.
- *  The parameter rar_count will usually be hw->mac.rar_entry_count
- *  unless there are workarounds that change this.
- **/
-void igb_update_mc_addr_list(struct e1000_hw *hw,
-                             u8 *mc_addr_list, u32 mc_addr_count,
-                             u32 rar_used_count, u32 rar_count)
-{
-       u32 hash_value;
-       u32 i;
-       u8 addr[6] = {0,0,0,0,0,0};
-       /*
-        * This function is essentially the same as that of 
-        * igb_update_mc_addr_list_generic. However it also takes care 
-        * of the special case where the register offset of the 
-        * second set of RARs begins elsewhere. This is implicitly taken care by 
-        * function e1000_rar_set_generic.
-        */
-
-       /*
-        * Load the first set of multicast addresses into the exact
-        * filters (RAR).  If there are not enough to fill the RAR
-        * array, clear the filters.
-        */
-       for (i = rar_used_count; i < rar_count; i++) {
-               if (mc_addr_count) {
-                       igb_rar_set(hw, mc_addr_list, i);
-                       mc_addr_count--;
-                       mc_addr_list += ETH_ALEN;
-               } else {
-                       igb_rar_set(hw, addr, i);
-               }
-       }
-
-       /* Clear the old settings from the MTA */
-       hw_dbg("Clearing MTA\n");
-       for (i = 0; i < hw->mac.mta_reg_count; i++) {
-               array_wr32(E1000_MTA, i, 0);
-               wrfl();
-       }
-
-       /* Load any remaining multicast addresses into the hash table. */
-       for (; mc_addr_count > 0; mc_addr_count--) {
-               hash_value = igb_hash_mc_addr(hw, mc_addr_list);
-               hw_dbg("Hash value = 0x%03X\n", hash_value);
-               igb_mta_set(hw, hash_value);
-               mc_addr_list += ETH_ALEN;
-       }
-}
-
 /**
  *  igb_shutdown_fiber_serdes_link_82575 - Remove link during power down
  *  @hw: pointer to the HW structure
@@ -866,9 +776,7 @@ void igb_shutdown_fiber_serdes_link_82575(struct e1000_hw *hw)
 {
        u32 reg;
 
-       if (hw->mac.type != e1000_82576 ||
-           (hw->phy.media_type != e1000_media_type_fiber &&
-            hw->phy.media_type != e1000_media_type_internal_serdes))
+       if (hw->phy.media_type != e1000_media_type_internal_serdes)
                return;
 
        /* if the management interface is not enabled, then power down */
@@ -911,6 +819,12 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
        if (ret_val)
                hw_dbg("PCI-E Master disable polling has failed.\n");
 
+       /* set the completion timeout for interface */
+       ret_val = igb_set_pcie_completion_timeout(hw);
+       if (ret_val) {
+               hw_dbg("PCI-E Set completion timeout has failed.\n");
+       }
+
        hw_dbg("Masking off all interrupts\n");
        wr32(E1000_IMC, 0xffffffff);
 
@@ -943,7 +857,8 @@ static s32 igb_reset_hw_82575(struct e1000_hw *hw)
        wr32(E1000_IMC, 0xffffffff);
        icr = rd32(E1000_ICR);
 
-       igb_check_alt_mac_addr(hw);
+       /* Install any alternate MAC address into RAR0 */
+       ret_val = igb_check_alt_mac_addr(hw);
 
        return ret_val;
 }
@@ -972,7 +887,8 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
        igb_clear_vfta(hw);
 
        /* Setup the receive address */
-       igb_init_rx_addrs_82575(hw, rar_count);
+       igb_init_rx_addrs(hw, rar_count);
+
        /* Zero out the Multicast HASH table */
        hw_dbg("Zeroing the MTA\n");
        for (i = 0; i < mac->mta_reg_count; i++)
@@ -1002,7 +918,7 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
  **/
 static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
 {
-       u32 ctrl, led_ctrl;
+       u32 ctrl;
        s32  ret_val;
        bool link;
 
@@ -1017,11 +933,6 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
                break;
        case e1000_phy_igp_3:
                ret_val = igb_copper_link_setup_igp(hw);
-               /* Setup activity LED */
-               led_ctrl = rd32(E1000_LEDCTL);
-               led_ctrl &= IGP_ACTIVITY_LED_MASK;
-               led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
-               wr32(E1000_LEDCTL, led_ctrl);
                break;
        default:
                ret_val = -E1000_ERR_PHY;
@@ -1052,9 +963,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
                }
        }
 
-       ret_val = igb_configure_pcs_link_82575(hw);
-       if (ret_val)
-               goto out;
+       igb_configure_pcs_link_82575(hw);
 
        /*
         * Check link status. Wait up to 100 microseconds for link to become
@@ -1163,14 +1072,14 @@ static s32 igb_setup_fiber_serdes_link_82575(struct e1000_hw *hw)
  *  independent interface (sgmii) is being used.  Configures the link
  *  for auto-negotiation or forces speed/duplex.
  **/
-static s32 igb_configure_pcs_link_82575(struct e1000_hw *hw)
+static void igb_configure_pcs_link_82575(struct e1000_hw *hw)
 {
        struct e1000_mac_info *mac = &hw->mac;
        u32 reg = 0;
 
        if (hw->phy.media_type != e1000_media_type_copper ||
            !(igb_sgmii_active_82575(hw)))
-               goto out;
+               return;
 
        /* For SGMII, we need to issue a PCS autoneg restart */
        reg = rd32(E1000_PCS_LCTL);
@@ -1213,9 +1122,6 @@ static s32 igb_configure_pcs_link_82575(struct e1000_hw *hw)
                       reg);
        }
        wr32(E1000_PCS_LCTL, reg);
-
-out:
-       return 0;
 }
 
 /**
@@ -1229,10 +1135,6 @@ out:
 static bool igb_sgmii_active_82575(struct e1000_hw *hw)
 {
        struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575;
-
-       if (hw->mac.type != e1000_82575 && hw->mac.type != e1000_82576)
-               return false;
-
        return dev_spec->sgmii_active;
 }
 
@@ -1423,6 +1325,57 @@ void igb_rx_fifo_flush_82575(struct e1000_hw *hw)
        rd32(E1000_MPC);
 }
 
+/**
+ *  igb_set_pcie_completion_timeout - set pci-e completion timeout
+ *  @hw: pointer to the HW structure
+ *
+ *  The defaults for 82575 and 82576 should be in the range of 50us to 50ms,
+ *  however the hardware default for these parts is 500us to 1ms which is less
+ *  than the 10ms recommended by the pci-e spec.  To address this we need to
+ *  increase the value to either 10ms to 200ms for capability version 1 config,
+ *  or 16ms to 55ms for version 2.
+ **/
+static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw)
+{
+       u32 gcr = rd32(E1000_GCR);
+       s32 ret_val = 0;
+       u16 pcie_devctl2;
+
+       /* only take action if timeout value is defaulted to 0 */
+       if (gcr & E1000_GCR_CMPL_TMOUT_MASK)
+               goto out;
+
+       /*
+        * if capababilities version is type 1 we can write the
+        * timeout of 10ms to 200ms through the GCR register
+        */
+       if (!(gcr & E1000_GCR_CAP_VER2)) {
+               gcr |= E1000_GCR_CMPL_TMOUT_10ms;
+               goto out;
+       }
+
+       /*
+        * for version 2 capabilities we need to write the config space
+        * directly in order to set the completion timeout value for
+        * 16ms to 55ms
+        */
+       ret_val = igb_read_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2,
+                                       &pcie_devctl2);
+       if (ret_val)
+               goto out;
+
+       pcie_devctl2 |= PCIE_DEVICE_CONTROL2_16ms;
+
+       ret_val = igb_write_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2,
+                                        &pcie_devctl2);
+out:
+       /* disable completion timeout resend */
+       gcr &= ~E1000_GCR_CMPL_TMOUT_RESEND;
+
+       wr32(E1000_GCR, gcr);
+       return ret_val;
+}
+
 /**
  *  igb_vmdq_set_loopback_pf - enable or disable vmdq loopback
  *  @hw: pointer to the hardware struct
index 0f16aba..8a1e659 100644 (file)
 #ifndef _E1000_82575_H_
 #define _E1000_82575_H_
 
-void igb_update_mc_addr_list(struct e1000_hw*, u8*, u32, u32, u32);
 extern void igb_shutdown_fiber_serdes_link_82575(struct e1000_hw *hw);
 extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw);
 
+#define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \
+                                     (ID_LED_DEF1_DEF2 <<  8) | \
+                                     (ID_LED_DEF1_DEF2 <<  4) | \
+                                     (ID_LED_OFF1_ON2))
+
 #define E1000_RAR_ENTRIES_82575   16
 #define E1000_RAR_ENTRIES_82576   24
 
index 3bda3db..c858293 100644 (file)
 /* Flow Control */
 #define E1000_FCRTL_XONE 0x80000000     /* Enable XON frame transmission */
 
+/* PCI Express Control */
+#define E1000_GCR_CMPL_TMOUT_MASK       0x0000F000
+#define E1000_GCR_CMPL_TMOUT_10ms       0x00001000
+#define E1000_GCR_CMPL_TMOUT_RESEND     0x00010000
+#define E1000_GCR_CAP_VER2              0x00040000
+
 /* PHY Control Register */
 #define MII_CR_FULL_DUPLEX      0x0100  /* FDX =1, half duplex =0 */
 #define MII_CR_RESTART_AUTO_NEG 0x0200  /* Restart auto negotiation */
 
 /* PCI/PCI-X/PCI-EX Config space */
 #define PCIE_LINK_STATUS             0x12
+#define PCIE_DEVICE_CONTROL2         0x28
 
 #define PCIE_LINK_WIDTH_MASK         0x3F0
 #define PCIE_LINK_WIDTH_SHIFT        4
+#define PCIE_DEVICE_CONTROL2_16ms    0x0005
 
 #define PHY_REVISION_MASK      0xFFFFFFF0
 #define MAX_PHY_REG_ADDRESS    0x1F  /* 5 bit address bus (0-0x1F) */
index 68aac20..119869b 100644 (file)
@@ -42,6 +42,7 @@ struct e1000_hw;
 #define E1000_DEV_ID_82576_SERDES             0x10E7
 #define E1000_DEV_ID_82576_QUAD_COPPER        0x10E8
 #define E1000_DEV_ID_82576_NS                 0x150A
+#define E1000_DEV_ID_82576_SERDES_QUAD        0x150D
 #define E1000_DEV_ID_82575EB_COPPER           0x10A7
 #define E1000_DEV_ID_82575EB_FIBER_SERDES     0x10A9
 #define E1000_DEV_ID_82575GB_QUAD_COPPER      0x10D6
@@ -61,8 +62,7 @@ enum e1000_mac_type {
 enum e1000_media_type {
        e1000_media_type_unknown = 0,
        e1000_media_type_copper = 1,
-       e1000_media_type_fiber = 2,
-       e1000_media_type_internal_serdes = 3,
+       e1000_media_type_internal_serdes = 2,
        e1000_num_media_types
 };
 
@@ -137,7 +137,7 @@ enum e1000_rev_polarity {
        e1000_rev_polarity_undefined = 0xFF
 };
 
-enum e1000_fc_type {
+enum e1000_fc_mode {
        e1000_fc_none = 0,
        e1000_fc_rx_pause,
        e1000_fc_tx_pause,
@@ -339,6 +339,10 @@ struct e1000_mac_info {
        u16 ifs_ratio;
        u16 ifs_step_size;
        u16 mta_reg_count;
+
+       /* Maximum size of the MTA register table in all supported adapters */
+       #define MAX_MTA_REG 128
+       u32 mta_shadow[MAX_MTA_REG];
        u16 rar_entry_count;
 
        u8  forced_speed_duplex;
@@ -425,8 +429,8 @@ struct e1000_fc_info {
        u16 pause_time;     /* Flow control pause timer */
        bool send_xon;      /* Flow control send XON */
        bool strict_ieee;   /* Strict IEEE mode */
-       enum e1000_fc_type type; /* Type of flow control */
-       enum e1000_fc_type original_type;
+       enum e1000_fc_mode current_mode; /* Type of flow control */
+       enum e1000_fc_mode requested_mode;
 };
 
 struct e1000_mbx_operations {
@@ -495,5 +499,7 @@ extern char *igb_get_hw_dev_name(struct e1000_hw *hw);
 #else
 #define hw_dbg(format, arg...)
 #endif
-
 #endif
+/* These functions must be implemented by drivers */
+s32  igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
+s32  igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
index 472f3f1..a0231cd 100644 (file)
 static s32 igb_set_default_fc(struct e1000_hw *hw);
 static s32 igb_set_fc_watermarks(struct e1000_hw *hw);
 
-static s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
-{
-       struct igb_adapter *adapter = hw->back;
-       u16 cap_offset;
-
-       cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
-       if (!cap_offset)
-               return -E1000_ERR_CONFIG;
-
-       pci_read_config_word(adapter->pdev, cap_offset + reg, value);
-
-       return 0;
-}
-
 /**
  *  igb_get_bus_info_pcie - Get PCIe bus information
  *  @hw: pointer to the HW structure
@@ -117,6 +103,31 @@ static void igb_write_vfta(struct e1000_hw *hw, u32 offset, u32 value)
        wrfl();
 }
 
+/**
+ *  igb_init_rx_addrs - Initialize receive address's
+ *  @hw: pointer to the HW structure
+ *  @rar_count: receive address registers
+ *
+ *  Setups the receive address registers by setting the base receive address
+ *  register to the devices MAC address and clearing all the other receive
+ *  address registers to 0.
+ **/
+void igb_init_rx_addrs(struct e1000_hw *hw, u16 rar_count)
+{
+       u32 i;
+       u8 mac_addr[ETH_ALEN] = {0};
+
+       /* Setup the receive address */
+       hw_dbg("Programming MAC Address into RAR[0]\n");
+
+       hw->mac.ops.rar_set(hw, hw->mac.addr, 0);
+
+       /* Zero out the other (rar_entry_count - 1) receive addresses */
+       hw_dbg("Clearing RAR[1-%u]\n", rar_count-1);
+       for (i = 1; i < rar_count; i++)
+               hw->mac.ops.rar_set(hw, mac_addr, i);
+}
+
 /**
  *  igb_vfta_set - enable or disable vlan in VLAN filter table
  *  @hw: pointer to the HW structure
@@ -274,6 +285,41 @@ void igb_mta_set(struct e1000_hw *hw, u32 hash_value)
        wrfl();
 }
 
+/**
+ *  igb_update_mc_addr_list - Update Multicast addresses
+ *  @hw: pointer to the HW structure
+ *  @mc_addr_list: array of multicast addresses to program
+ *  @mc_addr_count: number of multicast addresses to program
+ *
+ *  Updates entire Multicast Table Array.
+ *  The caller must have a packed mc_addr_list of multicast addresses.
+ **/
+void igb_update_mc_addr_list(struct e1000_hw *hw,
+                             u8 *mc_addr_list, u32 mc_addr_count)
+{
+       u32 hash_value, hash_bit, hash_reg;
+       int i;
+
+       /* clear mta_shadow */
+       memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow));
+
+       /* update mta_shadow from mc_addr_list */
+       for (i = 0; (u32) i < mc_addr_count; i++) {
+               hash_value = igb_hash_mc_addr(hw, mc_addr_list);
+
+               hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1);
+               hash_bit = hash_value & 0x1F;
+
+               hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit);
+               mc_addr_list += (ETH_ALEN);
+       }
+
+       /* replace the entire MTA table */
+       for (i = hw->mac.mta_reg_count - 1; i >= 0; i--)
+               array_wr32(E1000_MTA, i, hw->mac.mta_shadow[i]);
+       wrfl();
+}
+
 /**
  *  igb_hash_mc_addr - Generate a multicast hash value
  *  @hw: pointer to the HW structure
@@ -490,18 +536,24 @@ s32 igb_setup_link(struct e1000_hw *hw)
        if (igb_check_reset_block(hw))
                goto out;
 
-       ret_val = igb_set_default_fc(hw);
-       if (ret_val)
-               goto out;
+       /*
+        * If requested flow control is set to default, set flow control
+        * based on the EEPROM flow control settings.
+        */
+       if (hw->fc.requested_mode == e1000_fc_default) {
+               ret_val = igb_set_default_fc(hw);
+               if (ret_val)
+                       goto out;
+       }
 
        /*
         * We want to save off the original Flow Control configuration just
         * in case we get disconnected and then reconnected into a different
         * hub or switch with different Flow Control capabilities.
         */
-       hw->fc.original_type = hw->fc.type;
+       hw->fc.current_mode = hw->fc.requested_mode;
 
-       hw_dbg("After fix-ups FlowControl is now = %x\n", hw->fc.type);
+       hw_dbg("After fix-ups FlowControl is now = %x\n", hw->fc.current_mode);
 
        /* Call the necessary media_type subroutine to configure the link. */
        ret_val = hw->mac.ops.setup_physical_interface(hw);
@@ -568,7 +620,7 @@ static s32 igb_set_fc_watermarks(struct e1000_hw *hw)
         * ability to transmit pause frames is not enabled, then these
         * registers will be set to 0.
         */
-       if (hw->fc.type & e1000_fc_tx_pause) {
+       if (hw->fc.current_mode & e1000_fc_tx_pause) {
                /*
                 * We need to set up the Receive Threshold high and low water
                 * marks as well as (optionally) enabling the transmission of
@@ -615,12 +667,12 @@ static s32 igb_set_default_fc(struct e1000_hw *hw)
        }
 
        if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == 0)
-               hw->fc.type = e1000_fc_none;
+               hw->fc.requested_mode = e1000_fc_none;
        else if ((nvm_data & NVM_WORD0F_PAUSE_MASK) ==
                 NVM_WORD0F_ASM_DIR)
-               hw->fc.type = e1000_fc_tx_pause;
+               hw->fc.requested_mode = e1000_fc_tx_pause;
        else
-               hw->fc.type = e1000_fc_full;
+               hw->fc.requested_mode = e1000_fc_full;
 
 out:
        return ret_val;
@@ -650,7 +702,7 @@ s32 igb_force_mac_fc(struct e1000_hw *hw)
         * receive flow control.
         *
         * The "Case" statement below enables/disable flow control
-        * according to the "hw->fc.type" parameter.
+        * according to the "hw->fc.current_mode" parameter.
         *
         * The possible values of the "fc" parameter are:
         *      0:  Flow control is completely disabled
@@ -661,9 +713,9 @@ s32 igb_force_mac_fc(struct e1000_hw *hw)
         *      3:  Both Rx and TX flow control (symmetric) is enabled.
         *  other:  No other values should be possible at this point.
         */
-       hw_dbg("hw->fc.type = %u\n", hw->fc.type);
+       hw_dbg("hw->fc.current_mode = %u\n", hw->fc.current_mode);
 
-       switch (hw->fc.type) {
+       switch (hw->fc.current_mode) {
        case e1000_fc_none:
                ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE));
                break;
@@ -713,8 +765,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
         * configuration of the MAC to match the "fc" parameter.
         */
        if (mac->autoneg_failed) {
-               if (hw->phy.media_type == e1000_media_type_fiber ||
-                   hw->phy.media_type == e1000_media_type_internal_serdes)
+               if (hw->phy.media_type == e1000_media_type_internal_serdes)
                        ret_val = igb_force_mac_fc(hw);
        } else {
                if (hw->phy.media_type == e1000_media_type_copper)
@@ -812,11 +863,11 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                         * ONLY. Hence, we must now check to see if we need to
                         * turn OFF  the TRANSMISSION of PAUSE frames.
                         */
-                       if (hw->fc.original_type == e1000_fc_full) {
-                               hw->fc.type = e1000_fc_full;
+                       if (hw->fc.requested_mode == e1000_fc_full) {
+                               hw->fc.current_mode = e1000_fc_full;
                                hw_dbg("Flow Control = FULL.\r\n");
                        } else {
-                               hw->fc.type = e1000_fc_rx_pause;
+                               hw->fc.current_mode = e1000_fc_rx_pause;
                                hw_dbg("Flow Control = "
                                       "RX PAUSE frames only.\r\n");
                        }
@@ -833,7 +884,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                          (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
                          (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
                          (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
-                       hw->fc.type = e1000_fc_tx_pause;
+                       hw->fc.current_mode = e1000_fc_tx_pause;
                        hw_dbg("Flow Control = TX PAUSE frames only.\r\n");
                }
                /*
@@ -848,7 +899,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                         (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
                         !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
                         (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
-                       hw->fc.type = e1000_fc_rx_pause;
+                       hw->fc.current_mode = e1000_fc_rx_pause;
                        hw_dbg("Flow Control = RX PAUSE frames only.\r\n");
                }
                /*
@@ -872,13 +923,13 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                 * be asked to delay transmission of packets than asking
                 * our link partner to pause transmission of frames.
                 */
-               else if ((hw->fc.original_type == e1000_fc_none ||
-                         hw->fc.original_type == e1000_fc_tx_pause) ||
+               else if ((hw->fc.requested_mode == e1000_fc_none ||
+                         hw->fc.requested_mode == e1000_fc_tx_pause) ||
                         hw->fc.strict_ieee) {
-                       hw->fc.type = e1000_fc_none;
+                       hw->fc.current_mode = e1000_fc_none;
                        hw_dbg("Flow Control = NONE.\r\n");
                } else {
-                       hw->fc.type = e1000_fc_rx_pause;
+                       hw->fc.current_mode = e1000_fc_rx_pause;
                        hw_dbg("Flow Control = RX PAUSE frames only.\r\n");
                }
 
@@ -894,7 +945,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw)
                }
 
                if (duplex == HALF_DUPLEX)
-                       hw->fc.type = e1000_fc_none;
+                       hw->fc.current_mode = e1000_fc_none;
 
                /*
                 * Now we call a subroutine to actually force the MAC
@@ -1065,9 +1116,17 @@ static s32 igb_valid_led_default(struct e1000_hw *hw, u16 *data)
                goto out;
        }
 
-       if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF)
-               *data = ID_LED_DEFAULT;
-
+       if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) {
+               switch(hw->phy.media_type) {
+               case e1000_media_type_internal_serdes:
+                       *data = ID_LED_DEFAULT_82575_SERDES;
+                       break;
+               case e1000_media_type_copper:
+               default:
+                       *data = ID_LED_DEFAULT;
+                       break;
+               }
+       }
 out:
        return ret_val;
 }
@@ -1161,22 +1220,16 @@ s32 igb_blink_led(struct e1000_hw *hw)
        u32 ledctl_blink = 0;
        u32 i;
 
-       if (hw->phy.media_type == e1000_media_type_fiber) {
-               /* always blink LED0 for PCI-E fiber */
-               ledctl_blink = E1000_LEDCTL_LED0_BLINK |
-                    (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT);
-       } else {
-               /*
-                * set the blink bit for each LED that's "on" (0x0E)
-                * in ledctl_mode2
-                */
-               ledctl_blink = hw->mac.ledctl_mode2;
-               for (i = 0; i < 4; i++)
-                       if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) ==
-                           E1000_LEDCTL_MODE_LED_ON)
-                               ledctl_blink |= (E1000_LEDCTL_LED0_BLINK <<
-                                                (i * 8));
-       }
+       /*
+        * set the blink bit for each LED that's "on" (0x0E)
+        * in ledctl_mode2
+        */
+       ledctl_blink = hw->mac.ledctl_mode2;
+       for (i = 0; i < 4; i++)
+               if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) ==
+                   E1000_LEDCTL_MODE_LED_ON)
+                       ledctl_blink |= (E1000_LEDCTL_LED0_BLINK <<
+                                        (i * 8));
 
        wr32(E1000_LEDCTL, ledctl_blink);
 
@@ -1191,15 +1244,7 @@ s32 igb_blink_led(struct e1000_hw *hw)
  **/
 s32 igb_led_off(struct e1000_hw *hw)
 {
-       u32 ctrl;
-
        switch (hw->phy.media_type) {
-       case e1000_media_type_fiber:
-               ctrl = rd32(E1000_CTRL);
-               ctrl |= E1000_CTRL_SWDPIN0;
-               ctrl |= E1000_CTRL_SWDPIO0;
-               wr32(E1000_CTRL, ctrl);
-               break;
        case e1000_media_type_copper:
                wr32(E1000_LEDCTL, hw->mac.ledctl_mode1);
                break;
index 1d690b4..7518af8 100644 (file)
@@ -51,6 +51,8 @@ s32  igb_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
                                       u16 *duplex);
 s32  igb_id_led_init(struct e1000_hw *hw);
 s32  igb_led_off(struct e1000_hw *hw);
+void igb_update_mc_addr_list(struct e1000_hw *hw,
+                            u8 *mc_addr_list, u32 mc_addr_count);
 s32  igb_setup_link(struct e1000_hw *hw);
 s32  igb_validate_mdi_setting(struct e1000_hw *hw);
 s32  igb_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg,
@@ -60,6 +62,7 @@ void igb_clear_hw_cntrs_base(struct e1000_hw *hw);
 void igb_clear_vfta(struct e1000_hw *hw);
 s32  igb_vfta_set(struct e1000_hw *hw, u32 vid, bool add);
 void igb_config_collision_dist(struct e1000_hw *hw);
+void igb_init_rx_addrs(struct e1000_hw *hw, u16 rar_count);
 void igb_mta_set(struct e1000_hw *hw, u32 hash_value);
 void igb_put_hw_semaphore(struct e1000_hw *hw);
 void igb_rar_set(struct e1000_hw *hw, u8 *addr, u32 index);
index f50fac2..c1f4da6 100644 (file)
@@ -735,7 +735,7 @@ static s32 igb_phy_setup_autoneg(struct e1000_hw *hw)
         *  other:  No software override.  The flow control configuration
         *          in the EEPROM is used.
         */
-       switch (hw->fc.type) {
+       switch (hw->fc.current_mode) {
        case e1000_fc_none:
                /*
                 * Flow control (RX & TX) is completely disabled by a
@@ -992,7 +992,7 @@ static void igb_phy_force_speed_duplex_setup(struct e1000_hw *hw,
        u32 ctrl;
 
        /* Turn off flow control when forcing speed/duplex */
-       hw->fc.type = e1000_fc_none;
+       hw->fc.current_mode = e1000_fc_none;
 
        /* Force speed/duplex on the mac */
        ctrl = rd32(E1000_CTRL);
index 6e59245..345d144 100644 (file)
@@ -305,6 +305,7 @@ enum {
 #define E1000_CCMCTL      0x05B48 /* CCM Control Register */
 #define E1000_GIOCTL      0x05B44 /* GIO Analog Control Register */
 #define E1000_SCCTL       0x05B4C /* PCIc PLL Configuration Register */
+#define E1000_GCR         0x05B00 /* PCI-Ex Control */
 #define E1000_FACTPS    0x05B30 /* Function Active and Power State to MNG */
 #define E1000_SWSM      0x05B50 /* SW Semaphore */
 #define E1000_FWSM      0x05B54 /* FW Semaphore */
index 9598ac0..114ccab 100644 (file)
@@ -168,8 +168,7 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
                ecmd->duplex = -1;
        }
 
-       ecmd->autoneg = ((hw->phy.media_type == e1000_media_type_fiber) ||
-                        hw->mac.autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+       ecmd->autoneg = hw->mac.autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
        return 0;
 }
 
@@ -191,23 +190,20 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
 
        if (ecmd->autoneg == AUTONEG_ENABLE) {
                hw->mac.autoneg = 1;
-               if (hw->phy.media_type == e1000_media_type_fiber)
-                       hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full |
-                                                    ADVERTISED_FIBRE |
-                                                    ADVERTISED_Autoneg;
-               else
-                       hw->phy.autoneg_advertised = ecmd->advertising |
-                                                    ADVERTISED_TP |
-                                                    ADVERTISED_Autoneg;
+               hw->phy.autoneg_advertised = ecmd->advertising |
+                                            ADVERTISED_TP |
+                                            ADVERTISED_Autoneg;
                ecmd->advertising = hw->phy.autoneg_advertised;
-       } else
+               if (adapter->fc_autoneg)
+                       hw->fc.requested_mode = e1000_fc_default;
+       } else {
                if (igb_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex)) {
                        clear_bit(__IGB_RESETTING, &adapter->state);
                        return -EINVAL;
                }
+       }
 
        /* reset the link */
-
        if (netif_running(adapter->netdev)) {
                igb_down(adapter);
                igb_up(adapter);
@@ -227,11 +223,11 @@ static void igb_get_pauseparam(struct net_device *netdev,
        pause->autoneg =
                (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
 
-       if (hw->fc.type == e1000_fc_rx_pause)
+       if (hw->fc.current_mode == e1000_fc_rx_pause)
                pause->rx_pause = 1;
-       else if (hw->fc.type == e1000_fc_tx_pause)
+       else if (hw->fc.current_mode == e1000_fc_tx_pause)
                pause->tx_pause = 1;
-       else if (hw->fc.type == e1000_fc_full) {
+       else if (hw->fc.current_mode == e1000_fc_full) {
                pause->rx_pause = 1;
                pause->tx_pause = 1;
        }
@@ -249,26 +245,28 @@ static int igb_set_pauseparam(struct net_device *netdev,
        while (test_and_set_bit(__IGB_RESETTING, &adapter->state))
                msleep(1);
 
-       if (pause->rx_pause && pause->tx_pause)
-               hw->fc.type = e1000_fc_full;
-       else if (pause->rx_pause && !pause->tx_pause)
-               hw->fc.type = e1000_fc_rx_pause;
-       else if (!pause->rx_pause && pause->tx_pause)
-               hw->fc.type = e1000_fc_tx_pause;
-       else if (!pause->rx_pause && !pause->tx_pause)
-               hw->fc.type = e1000_fc_none;
-
-       hw->fc.original_type = hw->fc.type;
-
        if (adapter->fc_autoneg == AUTONEG_ENABLE) {
+               hw->fc.requested_mode = e1000_fc_default;
                if (netif_running(adapter->netdev)) {
                        igb_down(adapter);
                        igb_up(adapter);
                } else
                        igb_reset(adapter);
-       } else
-               retval = ((hw->phy.media_type == e1000_media_type_fiber) ?
-                         igb_setup_link(hw) : igb_force_mac_fc(hw));
+       } else {
+               if (pause->rx_pause && pause->tx_pause)
+                       hw->fc.requested_mode = e1000_fc_full;
+               else if (pause->rx_pause && !pause->tx_pause)
+                       hw->fc.requested_mode = e1000_fc_rx_pause;
+               else if (!pause->rx_pause && pause->tx_pause)
+                       hw->fc.requested_mode = e1000_fc_tx_pause;
+               else if (!pause->rx_pause && !pause->tx_pause)
+                       hw->fc.requested_mode = e1000_fc_none;
+
+               hw->fc.current_mode = hw->fc.requested_mode;
+
+               retval = ((hw->phy.media_type == e1000_media_type_copper) ?
+                         igb_force_mac_fc(hw) : igb_setup_link(hw));
+       }
 
        clear_bit(__IGB_RESETTING, &adapter->state);
        return retval;
@@ -1483,8 +1481,7 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter)
        struct e1000_hw *hw = &adapter->hw;
        u32 reg;
 
-       if (hw->phy.media_type == e1000_media_type_fiber ||
-           hw->phy.media_type == e1000_media_type_internal_serdes) {
+       if (hw->phy.media_type == e1000_media_type_internal_serdes) {
                reg = rd32(E1000_RCTL);
                reg |= E1000_RCTL_LBM_TCVR;
                wr32(E1000_RCTL, reg);
@@ -1843,7 +1840,6 @@ static void igb_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 static int igb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
-       struct e1000_hw *hw = &adapter->hw;
 
        if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
                return -EOPNOTSUPP;
@@ -1852,11 +1848,6 @@ static int igb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
            !device_can_wakeup(&adapter->pdev->dev))
                return wol->wolopts ? -EOPNOTSUPP : 0;
 
-       switch (hw->device_id) {
-       default:
-               break;
-       }
-
        /* these settings will always override what we currently have */
        adapter->wol = 0;
 
index adb09d3..fb32735 100644 (file)
@@ -65,6 +65,7 @@ static struct pci_device_id igb_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_FIBER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES), board_82575 },
+       { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES_QUAD), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), board_82575 },
@@ -127,7 +128,7 @@ static void igb_restore_vlan(struct igb_adapter *);
 static void igb_ping_all_vfs(struct igb_adapter *);
 static void igb_msg_task(struct igb_adapter *);
 static int igb_rcv_msg_from_vf(struct igb_adapter *, u32);
-static void igb_set_mc_list_pools(struct igb_adapter *, int, u16);
+static inline void igb_set_rah_pool(struct e1000_hw *, int , int);
 static void igb_vmm_control(struct igb_adapter *);
 static int igb_set_vf_mac(struct igb_adapter *adapter, int, unsigned char *);
 static void igb_restore_vf_multicasts(struct igb_adapter *adapter);
@@ -1129,7 +1130,7 @@ void igb_reset(struct igb_adapter *adapter)
        }
        fc->pause_time = 0xFFFF;
        fc->send_xon = 1;
-       fc->type = fc->original_type;
+       fc->current_mode = fc->requested_mode;
 
        /* disable receive for all VFs and wait one second */
        if (adapter->vfs_allocated_count) {
@@ -1426,8 +1427,8 @@ static int __devinit igb_probe(struct pci_dev *pdev,
        hw->mac.autoneg = true;
        hw->phy.autoneg_advertised = 0x2f;
 
-       hw->fc.original_type = e1000_fc_default;
-       hw->fc.type = e1000_fc_default;
+       hw->fc.requested_mode = e1000_fc_default;
+       hw->fc.current_mode = e1000_fc_default;
 
        adapter->itr_setting = IGB_DEFAULT_ITR;
        adapter->itr = IGB_START_ITR;
@@ -2535,7 +2536,6 @@ static void igb_set_multi(struct net_device *netdev)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
-       struct e1000_mac_info *mac = &hw->mac;
        struct dev_mc_list *mc_ptr;
        u8  *mta_list = NULL;
        u32 rctl;
@@ -2558,13 +2558,18 @@ static void igb_set_multi(struct net_device *netdev)
        }
        wr32(E1000_RCTL, rctl);
 
-       if (netdev->mc_count) {
-               mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC);
-               if (!mta_list) {
-                       dev_err(&adapter->pdev->dev,
-                               "failed to allocate multicast filter list\n");
-                       return;
-               }
+       if (!netdev->mc_count) {
+               /* nothing to program, so clear mc list */
+               igb_update_mc_addr_list(hw, NULL, 0);
+               igb_restore_vf_multicasts(adapter);
+               return;
+       }
+
+       mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC);
+       if (!mta_list) {
+               dev_err(&adapter->pdev->dev,
+                       "failed to allocate multicast filter list\n");
+               return;
        }
 
        /* The shared function expects a packed array of only addresses. */
@@ -2576,14 +2581,9 @@ static void igb_set_multi(struct net_device *netdev)
                memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN);
                mc_ptr = mc_ptr->next;
        }
-       igb_update_mc_addr_list(hw, mta_list, i,
-                               adapter->vfs_allocated_count + 1,
-                               mac->rar_entry_count);
-
-       igb_set_mc_list_pools(adapter, i, mac->rar_entry_count);
-       igb_restore_vf_multicasts(adapter);
-
+       igb_update_mc_addr_list(hw, mta_list, i);
        kfree(mta_list);
+       igb_restore_vf_multicasts(adapter);
 }
 
 /* Need to wait a few seconds after link up to get diagnostic information from
@@ -2618,10 +2618,6 @@ static bool igb_has_link(struct igb_adapter *adapter)
                        link_active = true;
                }
                break;
-       case e1000_media_type_fiber:
-               ret_val = hw->mac.ops.check_for_link(hw);
-               link_active = !!(rd32(E1000_STATUS) & E1000_STATUS_LU);
-               break;
        case e1000_media_type_internal_serdes:
                ret_val = hw->mac.ops.check_for_link(hw);
                link_active = hw->mac.serdes_has_link;
@@ -4542,6 +4538,20 @@ static inline void igb_rx_checksum_adv(struct igb_adapter *adapter,
        adapter->hw_csum_good++;
 }
 
+static inline u16 igb_get_hlen(struct igb_adapter *adapter,
+                               union e1000_adv_rx_desc *rx_desc)
+{
+       /* HW will not DMA in data larger than the given buffer, even if it
+        * parses the (NFS, of course) header to be larger.  In that case, it
+        * fills the header buffer and spills the rest into the page.
+        */
+       u16 hlen = (le16_to_cpu(rx_desc->wb.lower.lo_dword.hdr_info) &
+                  E1000_RXDADV_HDRBUFLEN_MASK) >> E1000_RXDADV_HDRBUFLEN_SHIFT;
+       if (hlen > adapter->rx_ps_hdr_size)
+               hlen = adapter->rx_ps_hdr_size;
+       return hlen;
+}
+
 static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
                                 int *work_done, int budget)
 {
@@ -4556,7 +4566,8 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
        int cleaned_count = 0;
        unsigned int total_bytes = 0, total_packets = 0;
        unsigned int i;
-       u32 length, hlen, staterr;
+       u32 staterr;
+       u16 length;
 
        i = rx_ring->next_to_clean;
        buffer_info = &rx_ring->buffer_info[i];
@@ -4593,17 +4604,8 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
                        goto send_up;
                }
 
-               /* HW will not DMA in data larger than the given buffer, even
-                * if it parses the (NFS, of course) header to be larger.  In
-                * that case, it fills the header buffer and spills the rest
-                * into the page.
-                */
-               hlen = (le16_to_cpu(rx_desc->wb.lower.lo_dword.hdr_info) &
-                 E1000_RXDADV_HDRBUFLEN_MASK) >> E1000_RXDADV_HDRBUFLEN_SHIFT;
-               if (hlen > adapter->rx_ps_hdr_size)
-                       hlen = adapter->rx_ps_hdr_size;
-
-               if (!skb_shinfo(skb)->nr_frags) {
+               if (buffer_info->dma) {
+                       u16 hlen = igb_get_hlen(adapter, rx_desc);
                        pci_unmap_single(pdev, buffer_info->dma,
                                         adapter->rx_ps_hdr_size,
                                         PCI_DMA_FROMDEVICE);
@@ -5033,6 +5035,34 @@ static int igb_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
        }
 }
 
+s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
+{
+       struct igb_adapter *adapter = hw->back;
+       u16 cap_offset;
+
+       cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+       if (!cap_offset)
+               return -E1000_ERR_CONFIG;
+
+       pci_read_config_word(adapter->pdev, cap_offset + reg, value);
+
+       return 0;
+}
+
+s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
+{
+       struct igb_adapter *adapter = hw->back;
+       u16 cap_offset;
+
+       cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+       if (!cap_offset)
+               return -E1000_ERR_CONFIG;
+
+       pci_write_config_word(adapter->pdev, cap_offset + reg, *value);
+
+       return 0;
+}
+
 static void igb_vlan_rx_register(struct net_device *netdev,
                                 struct vlan_group *grp)
 {
@@ -5136,14 +5166,6 @@ int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx)
 
        mac->autoneg = 0;
 
-       /* Fiber NICs only allow 1000 gbps Full duplex */
-       if ((adapter->hw.phy.media_type == e1000_media_type_fiber) &&
-               spddplx != (SPEED_1000 + DUPLEX_FULL)) {
-               dev_err(&adapter->pdev->dev,
-                       "Unsupported Speed/Duplex configuration\n");
-               return -EINVAL;
-       }
-
        switch (spddplx) {
        case SPEED_10 + DUPLEX_HALF:
                mac->forced_speed_duplex = ADVERTISE_10_HALF;
@@ -5452,19 +5474,6 @@ static void igb_io_resume(struct pci_dev *pdev)
        igb_get_hw_control(adapter);
 }
 
-static void igb_set_mc_list_pools(struct igb_adapter *adapter,
-                                 int entry_count, u16 total_rar_filters)
-{
-       struct e1000_hw *hw = &adapter->hw;
-       int i = adapter->vfs_allocated_count + 1;
-
-       if ((i + entry_count) < total_rar_filters)
-               total_rar_filters = i + entry_count;
-
-       for (; i < total_rar_filters; i++)
-               igb_set_rah_pool(hw, adapter->vfs_allocated_count, i);
-}
-
 static int igb_set_vf_mac(struct igb_adapter *adapter,
                           int vf, unsigned char *mac_addr)
 {
index 2bc9d63..926c31b 100644 (file)
@@ -149,7 +149,6 @@ static void igbvf_alloc_rx_buffers(struct igbvf_ring *rx_ring,
                bufsz = adapter->rx_ps_hdr_size;
        else
                bufsz = adapter->rx_buffer_len;
-       bufsz += NET_IP_ALIGN;
 
        while (cleaned_count--) {
                rx_desc = IGBVF_RX_DESC_ADV(*rx_ring, i);
@@ -173,7 +172,7 @@ static void igbvf_alloc_rx_buffers(struct igbvf_ring *rx_ring,
                }
 
                if (!buffer_info->skb) {
-                       skb = netdev_alloc_skb(netdev, bufsz);
+                       skb = netdev_alloc_skb(netdev, bufsz + NET_IP_ALIGN);
                        if (!skb) {
                                adapter->alloc_rx_buff_failed++;
                                goto no_buffers;
@@ -286,7 +285,7 @@ static bool igbvf_clean_rx_irq(struct igbvf_adapter *adapter,
 
                if (!skb_shinfo(skb)->nr_frags) {
                        pci_unmap_single(pdev, buffer_info->dma,
-                                        adapter->rx_ps_hdr_size + NET_IP_ALIGN,
+                                        adapter->rx_ps_hdr_size,
                                         PCI_DMA_FROMDEVICE);
                        skb_put(skb, hlen);
                }
diff --git a/include/linux/isdn/hdlc.h b/include/linux/isdn/hdlc.h
new file mode 100644 (file)
index 0000000..4b3ecc4
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * hdlc.h  --  General purpose ISDN HDLC decoder.
+ *
+ * Implementation of a HDLC decoder/encoder in software.
+ * Neccessary because some ISDN devices don't have HDLC
+ * controllers.
+ *
+ * Copyright (C)
+ *     2009    Karsten Keil            <keil@b1-systems.de>
+ *     2002    Wolfgang Mües          <wolfgang@iksw-muees.de>
+ *     2001    Frode Isaksen           <fisaksen@bewan.com>
+ *     2001    Kai Germaschewski       <kai.germaschewski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ISDNHDLC_H__
+#define __ISDNHDLC_H__
+
+struct isdnhdlc_vars {
+       int bit_shift;
+       int hdlc_bits1;
+       int data_bits;
+       int ffbit_shift;        /* encoding only */
+       int state;
+       int dstpos;
+
+       u16 crc;
+
+       u8 cbin;
+       u8 shift_reg;
+       u8 ffvalue;
+
+       /* set if transferring data */
+       u32 data_received:1;
+       /* set if D channel (send idle instead of flags) */
+       u32 dchannel:1;
+       /* set if 56K adaptation */
+       u32 do_adapt56:1;
+       /* set if in closing phase (need to send CRC + flag) */
+       u32 do_closing:1;
+       /* set if data is bitreverse */
+       u32 do_bitreverse:1;
+};
+
+/* Feature Flags */
+#define HDLC_56KBIT    0x01
+#define HDLC_DCHANNEL  0x02
+#define HDLC_BITREVERSE        0x04
+
+/*
+  The return value from isdnhdlc_decode is
+  the frame length, 0 if no complete frame was decoded,
+  or a negative error number
+*/
+#define HDLC_FRAMING_ERROR     1
+#define HDLC_CRC_ERROR         2
+#define HDLC_LENGTH_ERROR      3
+
+extern void    isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features);
+
+extern int     isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src,
+                       int slen, int *count, u8 *dst, int dsize);
+
+extern void    isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features);
+
+extern int     isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src,
+                       u16 slen, int *count, u8 *dst, int dsize);
+
+#endif /* __ISDNHDLC_H__ */
index 7f9831d..4af8414 100644 (file)
@@ -168,6 +168,7 @@ struct bchannel {
 extern int     mISDN_initdchannel(struct dchannel *, int, void *);
 extern int     mISDN_initbchannel(struct bchannel *, int);
 extern int     mISDN_freedchannel(struct dchannel *);
+extern void    mISDN_clear_bchannel(struct bchannel *);
 extern int     mISDN_freebchannel(struct bchannel *);
 extern void    queue_ch_frame(struct mISDNchannel *, u_int,
                        int, struct sk_buff *);
index 45100b3..536ca12 100644 (file)
@@ -37,7 +37,7 @@
  */
 #define        MISDN_MAJOR_VERSION     1
 #define        MISDN_MINOR_VERSION     1
-#define MISDN_RELEASE          20
+#define MISDN_RELEASE          21
 
 /* primitives for information exchange
  * generell format
 #define HFC_VOL_CHANGE_RX      0x2602
 #define HFC_SPL_LOOP_ON                0x2603
 #define HFC_SPL_LOOP_OFF       0x2604
+/* for T30 FAX and analog modem */
+#define HW_MOD_FRM             0x4000
+#define HW_MOD_FRH             0x4001
+#define HW_MOD_FTM             0x4002
+#define HW_MOD_FTH             0x4003
+#define HW_MOD_FTS             0x4004
+#define HW_MOD_CONNECT         0x4010
+#define HW_MOD_OK              0x4011
+#define HW_MOD_NOCARR          0x4012
+#define HW_MOD_FCERROR         0x4013
+#define HW_MOD_READY           0x4014
+#define HW_MOD_LASTDATA                0x4015
 
 /* DSP_TONE_PATT_ON parameter */
 #define TONE_OFF                       0x0000
 #define ISDN_P_B_L2DTMF                0x24
 #define ISDN_P_B_L2DSP         0x25
 #define ISDN_P_B_L2DSPHDLC     0x26
+#define ISDN_P_B_T30_FAX       0x27
+#define ISDN_P_B_MODEM_ASYNC   0x28
 
 #define OPTION_L2_PMX          1
 #define OPTION_L2_PTP          2