]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 27 Jul 2008 03:23:44 +0000 (20:23 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 27 Jul 2008 03:23:44 +0000 (20:23 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (39 commits)
  [PATCH] fix RLIM_NOFILE handling
  [PATCH] get rid of corner case in dup3() entirely
  [PATCH] remove remaining namei_{32,64}.h crap
  [PATCH] get rid of indirect users of namei.h
  [PATCH] get rid of __user_path_lookup_open
  [PATCH] f_count may wrap around
  [PATCH] dup3 fix
  [PATCH] don't pass nameidata to __ncp_lookup_validate()
  [PATCH] don't pass nameidata to gfs2_lookupi()
  [PATCH] new (local) helper: user_path_parent()
  [PATCH] sanitize __user_walk_fd() et.al.
  [PATCH] preparation to __user_walk_fd cleanup
  [PATCH] kill nameidata passing to permission(), rename to inode_permission()
  [PATCH] take noexec checks to very few callers that care
  Re: [PATCH 3/6] vfs: open_exec cleanup
  [patch 4/4] vfs: immutable inode checking cleanup
  [patch 3/4] fat: dont call notify_change
  [patch 2/4] vfs: utimes cleanup
  [patch 1/4] vfs: utimes: move owner check into inode_change_ok()
  [PATCH] vfs: use kstrdup() and check failing allocation
  ...

118 files changed:
Documentation/isdn/README.mISDN [new file with mode: 0644]
drivers/firmware/memmap.c
drivers/isdn/Kconfig
drivers/isdn/Makefile
drivers/isdn/hardware/Makefile
drivers/isdn/hardware/mISDN/Kconfig [new file with mode: 0644]
drivers/isdn/hardware/mISDN/Makefile [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfc_multi.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfc_pci.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfcmulti.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/hfcpci.c [new file with mode: 0644]
drivers/isdn/mISDN/Kconfig [new file with mode: 0644]
drivers/isdn/mISDN/Makefile [new file with mode: 0644]
drivers/isdn/mISDN/core.c [new file with mode: 0644]
drivers/isdn/mISDN/core.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_audio.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_biquad.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_blowfish.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_cmx.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_core.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_dtmf.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_ecdis.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_hwec.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_hwec.h [new file with mode: 0644]
drivers/isdn/mISDN/dsp_pipeline.c [new file with mode: 0644]
drivers/isdn/mISDN/dsp_tones.c [new file with mode: 0644]
drivers/isdn/mISDN/fsm.c [new file with mode: 0644]
drivers/isdn/mISDN/fsm.h [new file with mode: 0644]
drivers/isdn/mISDN/hwchannel.c [new file with mode: 0644]
drivers/isdn/mISDN/l1oip.h [new file with mode: 0644]
drivers/isdn/mISDN/l1oip_codec.c [new file with mode: 0644]
drivers/isdn/mISDN/l1oip_core.c [new file with mode: 0644]
drivers/isdn/mISDN/layer1.c [new file with mode: 0644]
drivers/isdn/mISDN/layer1.h [new file with mode: 0644]
drivers/isdn/mISDN/layer2.c [new file with mode: 0644]
drivers/isdn/mISDN/layer2.h [new file with mode: 0644]
drivers/isdn/mISDN/socket.c [new file with mode: 0644]
drivers/isdn/mISDN/stack.c [new file with mode: 0644]
drivers/isdn/mISDN/tei.c [new file with mode: 0644]
drivers/isdn/mISDN/timerdev.c [new file with mode: 0644]
drivers/net/bnx2x_main.c
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
fs/proc/base.c
include/asm-generic/gpio.h
include/linux/hugetlb.h
include/linux/mISDNdsp.h [new file with mode: 0644]
include/linux/mISDNhw.h [new file with mode: 0644]
include/linux/mISDNif.h [new file with mode: 0644]
include/linux/pci_ids.h
include/linux/rtnetlink.h
include/linux/slab.h
include/linux/socket.h
include/net/request_sock.h
mm/util.c
net/appletalk/ddp.c
net/bridge/netfilter/ebtable_filter.c
net/bridge/netfilter/ebtable_nat.c
net/core/datagram.c
net/core/dev.c
net/core/request_sock.c
net/core/skbuff.c
net/core/stream.c
net/core/user_dma.c
net/dccp/dccp.h
net/dccp/input.c
net/dccp/ipv4.c
net/dccp/ipv6.c
net/dccp/proto.c
net/dccp/timer.c
net/ipv4/af_inet.c
net/ipv4/devinet.c
net/ipv4/inet_connection_sock.c
net/ipv4/inet_fragment.c
net/ipv4/inet_hashtables.c
net/ipv4/inet_timewait_sock.c
net/ipv4/ip_fragment.c
net/ipv4/ip_output.c
net/ipv4/netfilter/arptable_filter.c
net/ipv4/netfilter/iptable_security.c
net/ipv4/route.c
net/ipv4/syncookies.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_timer.c
net/ipv6/addrconf.c
net/ipv6/af_inet6.c
net/ipv6/inet6_connection_sock.c
net/ipv6/inet6_hashtables.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_output.c
net/ipv6/mip6.c
net/ipv6/netfilter/ip6table_security.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/reassembly.c
net/ipv6/syncookies.c
net/ipv6/tcp_ipv6.c
net/key/af_key.c
net/netfilter/nf_conntrack_extend.c
net/netlink/af_netlink.c
net/packet/af_packet.c
net/rxrpc/af_rxrpc.c
net/sched/act_api.c
net/sched/act_police.c
net/sched/cls_u32.c
net/sched/sch_cbq.c
net/sched/sch_generic.c
net/sched/sch_htb.c
net/sched/sch_sfq.c
net/sctp/associola.c
net/unix/af_unix.c
net/xfrm/xfrm_algo.c
net/xfrm/xfrm_ipcomp.c
net/xfrm/xfrm_state.c
security/selinux/hooks.c

diff --git a/Documentation/isdn/README.mISDN b/Documentation/isdn/README.mISDN
new file mode 100644 (file)
index 0000000..cd8bf92
--- /dev/null
@@ -0,0 +1,6 @@
+mISDN is a new modular ISDN driver, in the long term it should replace
+the old I4L driver architecture for passiv ISDN cards.
+It was designed to allow a broad range of applications and interfaces
+but only have the basic function in kernel, the interface to the user
+space is based on sockets with a own address family AF_ISDN.
+
index e23399c7f7735ad8677d7f011821cc5d3fa56717..001622eb86f962d28f6b6842981817ddf7f14132 100644 (file)
@@ -153,12 +153,14 @@ int __init firmware_map_add_early(resource_size_t start, resource_size_t end,
 
 static ssize_t start_show(struct firmware_map_entry *entry, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->start);
+       return snprintf(buf, PAGE_SIZE, "0x%llx\n",
+               (unsigned long long)entry->start);
 }
 
 static ssize_t end_show(struct firmware_map_entry *entry, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "0x%llx\n", entry->end);
+       return snprintf(buf, PAGE_SIZE, "0x%llx\n",
+               (unsigned long long)entry->end);
 }
 
 static ssize_t type_show(struct firmware_map_entry *entry, char *buf)
index 66f946aa30b3fb0830b8a858e0ef91d9a47cfd73..3d113c6e4a703242e32c0d9f57cc09be29516690 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 menuconfig ISDN
-       tristate "ISDN support"
+       bool "ISDN support"
        depends on NET
        depends on !S390
        ---help---
@@ -21,6 +21,8 @@ menuconfig ISDN
 
 if ISDN
 
+source "drivers/isdn/mISDN/Kconfig"
+
 menuconfig ISDN_I4L
        tristate "Old ISDN4Linux (deprecated)"
        ---help---
index 988142c30a6d374c4db55b0d041bc30932ed0db6..8380a4568d11dda17aa5c0b92839a8ab334e5dbf 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_ISDN_I4L)                 += i4l/
 obj-$(CONFIG_ISDN_CAPI)                        += capi/
+obj-$(CONFIG_MISDN)                    += mISDN/
 obj-$(CONFIG_ISDN_CAPI)                        += hardware/
 obj-$(CONFIG_ISDN_DIVERSION)           += divert/
 obj-$(CONFIG_ISDN_DRV_HISAX)           += hisax/
index 11c8a183948c60cddf556602b60c1c032bd2dc09..a5d8fce4c4c496acec316ddea08431a4ec274ebb 100644 (file)
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_CAPI_AVM)         += avm/
 obj-$(CONFIG_CAPI_EICON)       += eicon/
+obj-$(CONFIG_MISDN)            += mISDN/
diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
new file mode 100644 (file)
index 0000000..1479348
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Hardware for mISDN
+#
+comment "mISDN hardware drivers"
+
+config MISDN_HFCPCI
+       tristate "Support for HFC PCI cards"
+       depends on MISDN
+       depends on PCI
+       help
+         Enable support for cards with Cologne Chip AG's
+          HFC PCI chip.
+
+config MISDN_HFCMULTI
+       tristate "Support for HFC multiport cards (HFC-4S/8S/E1)"
+       depends on PCI
+       depends on MISDN
+       help
+         Enable support for cards with Cologne Chip AG's HFC multiport
+         chip. There are three types of chips that are quite similar,
+         but the interface is different:
+          * HFC-4S (4 S/T interfaces on one chip)
+          * HFC-8S (8 S/T interfaces on one chip)
+          * HFC-E1 (E1 interface for 2Mbit ISDN)
+
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
new file mode 100644 (file)
index 0000000..1e7ca53
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the modular ISDN hardware drivers
+#
+#
+
+obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
+obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
new file mode 100644 (file)
index 0000000..a33d87a
--- /dev/null
@@ -0,0 +1,1204 @@
+/*
+ * see notice in hfc_multi.c
+ */
+
+extern void ztdummy_extern_interrupt(void);
+extern void ztdummy_register_interrupt(void);
+extern int ztdummy_unregister_interrupt(void);
+
+#define DEBUG_HFCMULTI_FIFO    0x00010000
+#define        DEBUG_HFCMULTI_CRC      0x00020000
+#define        DEBUG_HFCMULTI_INIT     0x00040000
+#define        DEBUG_HFCMULTI_PLXSD    0x00080000
+#define        DEBUG_HFCMULTI_MODE     0x00100000
+#define        DEBUG_HFCMULTI_MSG      0x00200000
+#define        DEBUG_HFCMULTI_STATE    0x00400000
+#define        DEBUG_HFCMULTI_SYNC     0x01000000
+#define        DEBUG_HFCMULTI_DTMF     0x02000000
+#define        DEBUG_HFCMULTI_LOCK     0x80000000
+
+#define        PCI_ENA_REGIO   0x01
+#define        PCI_ENA_MEMIO   0x02
+
+/*
+ * NOTE: some registers are assigned multiple times due to different modes
+ *       also registers are assigned differen for HFC-4s/8s and HFC-E1
+ */
+
+/*
+#define MAX_FRAME_SIZE 2048
+*/
+
+struct hfc_chan {
+       struct dchannel *dch;   /* link if channel is a D-channel */
+       struct bchannel *bch;   /* link if channel is a B-channel */
+       int             port;   /* the interface port this */
+                               /* channel is associated with */
+       int             nt_timer; /* -1 if off, 0 if elapsed, >0 if running */
+       int             los, ais, slip_tx, slip_rx, rdi; /* current alarms */
+       int             jitter;
+       u_long          cfg;    /* port configuration */
+       int             sync;   /* sync state (used by E1) */
+       u_int           protocol; /* current protocol */
+       int             slot_tx; /* current pcm slot */
+       int             bank_tx; /* current pcm bank */
+       int             slot_rx;
+       int             bank_rx;
+       int             conf;   /* conference setting of TX slot */
+       int             txpending;      /* if there is currently data in */
+                                       /* the FIFO 0=no, 1=yes, 2=splloop */
+       int             rx_off; /* set to turn fifo receive off */
+       int             coeff_count; /* curren coeff block */
+       s32             *coeff; /* memory pointer to 8 coeff blocks */
+};
+
+
+struct hfcm_hw {
+       u_char  r_ctrl;
+       u_char  r_irq_ctrl;
+       u_char  r_cirm;
+       u_char  r_ram_sz;
+       u_char  r_pcm_md0;
+       u_char  r_irqmsk_misc;
+       u_char  r_dtmf;
+       u_char  r_st_sync;
+       u_char  r_sci_msk;
+       u_char  r_tx0, r_tx1;
+       u_char  a_st_ctrl0[8];
+       timer_t timer;
+};
+
+
+/* for each stack these flags are used (cfg) */
+#define        HFC_CFG_NONCAP_TX       1 /* S/T TX interface has less capacity */
+#define        HFC_CFG_DIS_ECHANNEL    2 /* disable E-channel processing */
+#define        HFC_CFG_REG_ECHANNEL    3 /* register E-channel */
+#define        HFC_CFG_OPTICAL         4 /* the E1 interface is optical */
+#define        HFC_CFG_REPORT_LOS      5 /* the card should report loss of signal */
+#define        HFC_CFG_REPORT_AIS      6 /* the card should report alarm ind. sign. */
+#define        HFC_CFG_REPORT_SLIP     7 /* the card should report bit slips */
+#define        HFC_CFG_REPORT_RDI      8 /* the card should report remote alarm */
+#define        HFC_CFG_DTMF            9 /* enable DTMF-detection */
+#define        HFC_CFG_CRC4            10 /* disable CRC-4 Multiframe mode, */
+                                       /* use double frame instead. */
+
+#define        HFC_CHIP_EXRAM_128      0 /* external ram 128k */
+#define        HFC_CHIP_EXRAM_512      1 /* external ram 256k */
+#define        HFC_CHIP_REVISION0      2 /* old fifo handling */
+#define        HFC_CHIP_PCM_SLAVE      3 /* PCM is slave */
+#define        HFC_CHIP_PCM_MASTER     4 /* PCM is master */
+#define        HFC_CHIP_RX_SYNC        5 /* disable pll sync for pcm */
+#define        HFC_CHIP_DTMF           6 /* DTMF decoding is enabled */
+#define        HFC_CHIP_ULAW           7 /* ULAW mode */
+#define        HFC_CHIP_CLOCK2         8 /* double clock mode */
+#define        HFC_CHIP_E1CLOCK_GET    9 /* always get clock from E1 interface */
+#define        HFC_CHIP_E1CLOCK_PUT    10 /* always put clock from E1 interface */
+#define        HFC_CHIP_WATCHDOG       11 /* whether we should send signals */
+                                       /* to the watchdog */
+#define        HFC_CHIP_B410P          12 /* whether we have a b410p with echocan in */
+                                       /* hw */
+#define        HFC_CHIP_PLXSD          13 /* whether we have a Speech-Design PLX */
+
+#define HFC_IO_MODE_PCIMEM     0x00 /* normal memory mapped IO */
+#define HFC_IO_MODE_REGIO      0x01 /* PCI io access */
+#define HFC_IO_MODE_PLXSD      0x02 /* access HFC via PLX9030 */
+
+/* table entry in the PCI devices list */
+struct hm_map {
+       char *vendor_name;
+       char *card_name;
+       int type;
+       int ports;
+       int clock2;
+       int leds;
+       int opticalsupport;
+       int dip_type;
+       int io_mode;
+};
+
+struct hfc_multi {
+       struct list_head        list;
+       struct hm_map   *mtyp;
+       int             id;
+       int             pcm;    /* id of pcm bus */
+       int             type;
+       int             ports;
+
+       u_int           irq;    /* irq used by card */
+       u_int           irqcnt;
+       struct pci_dev  *pci_dev;
+       int             io_mode; /* selects mode */
+#ifdef HFC_REGISTER_DEBUG
+       void            (*HFC_outb)(struct hfc_multi *hc, u_char reg,
+                               u_char val, const char *function, int line);
+       void            (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+                               u_char val, const char *function, int line);
+       u_char          (*HFC_inb)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       u_char          (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       u_short         (*HFC_inw)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       u_short         (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg,
+                               const char *function, int line);
+       void            (*HFC_wait)(struct hfc_multi *hc,
+                               const char *function, int line);
+       void            (*HFC_wait_nodebug)(struct hfc_multi *hc,
+                               const char *function, int line);
+#else
+       void            (*HFC_outb)(struct hfc_multi *hc, u_char reg,
+                               u_char val);
+       void            (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+                               u_char val);
+       u_char          (*HFC_inb)(struct hfc_multi *hc, u_char reg);
+       u_char          (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg);
+       u_short         (*HFC_inw)(struct hfc_multi *hc, u_char reg);
+       u_short         (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg);
+       void            (*HFC_wait)(struct hfc_multi *hc);
+       void            (*HFC_wait_nodebug)(struct hfc_multi *hc);
+#endif
+       void            (*read_fifo)(struct hfc_multi *hc, u_char *data,
+                               int len);
+       void            (*write_fifo)(struct hfc_multi *hc, u_char *data,
+                               int len);
+       u_long          pci_origmembase, plx_origmembase, dsp_origmembase;
+       u_char          *pci_membase; /* PCI memory (MUST BE BYTE POINTER) */
+       u_char          *plx_membase; /* PLX memory */
+       u_char          *dsp_membase; /* DSP on PLX */
+       u_long          pci_iobase; /* PCI IO */
+       struct hfcm_hw  hw;     /* remember data of write-only-registers */
+
+       u_long          chip;   /* chip configuration */
+       int             masterclk; /* port that provides master clock -1=off */
+       int             dtmf;   /* flag that dtmf is currently in process */
+       int             Flen;   /* F-buffer size */
+       int             Zlen;   /* Z-buffer size (must be int for calculation)*/
+       int             max_trans; /* maximum transparent fifo fill */
+       int             Zmin;   /* Z-buffer offset */
+       int             DTMFbase; /* base address of DTMF coefficients */
+
+       u_int           slots;  /* number of PCM slots */
+       u_int           leds;   /* type of leds */
+       u_int           ledcount; /* used to animate leds */
+       u_long          ledstate; /* save last state of leds */
+       int             opticalsupport; /* has the e1 board */
+                                       /* an optical Interface */
+       int             dslot;  /* channel # of d-channel (E1) default 16 */
+
+       u_long          wdcount;        /* every 500 ms we need to */
+                                       /* send the watchdog a signal */
+       u_char          wdbyte; /* watchdog toggle byte */
+       u_int           activity[8];    /* if there is any action on this */
+                                       /* port (will be cleared after */
+                                       /* showing led-states) */
+       int             e1_state; /* keep track of last state */
+       int             e1_getclock; /* if sync is retrieved from interface */
+       int             syncronized; /* keep track of existing sync interface */
+       int             e1_resync; /* resync jobs */
+
+       spinlock_t      lock;   /* the lock */
+
+       /*
+        * the channel index is counted from 0, regardless where the channel
+        * is located on the hfc-channel.
+        * the bch->channel is equvalent to the hfc-channel
+        */
+       struct hfc_chan chan[32];
+       u_char          created[8]; /* what port is created */
+       signed char     slot_owner[256]; /* owner channel of slot */
+};
+
+/* PLX GPIOs */
+#define        PLX_GPIO4_DIR_BIT       13
+#define        PLX_GPIO4_BIT           14
+#define        PLX_GPIO5_DIR_BIT       16
+#define        PLX_GPIO5_BIT           17
+#define        PLX_GPIO6_DIR_BIT       19
+#define        PLX_GPIO6_BIT           20
+#define        PLX_GPIO7_DIR_BIT       22
+#define        PLX_GPIO7_BIT           23
+#define PLX_GPIO8_DIR_BIT      25
+#define PLX_GPIO8_BIT          26
+
+#define        PLX_GPIO4               (1 << PLX_GPIO4_BIT)
+#define        PLX_GPIO5               (1 << PLX_GPIO5_BIT)
+#define        PLX_GPIO6               (1 << PLX_GPIO6_BIT)
+#define        PLX_GPIO7               (1 << PLX_GPIO7_BIT)
+#define PLX_GPIO8              (1 << PLX_GPIO8_BIT)
+
+#define        PLX_GPIO4_DIR           (1 << PLX_GPIO4_DIR_BIT)
+#define        PLX_GPIO5_DIR           (1 << PLX_GPIO5_DIR_BIT)
+#define        PLX_GPIO6_DIR           (1 << PLX_GPIO6_DIR_BIT)
+#define        PLX_GPIO7_DIR           (1 << PLX_GPIO7_DIR_BIT)
+#define PLX_GPIO8_DIR          (1 << PLX_GPIO8_DIR_BIT)
+
+#define        PLX_TERM_ON                     PLX_GPIO7
+#define        PLX_SLAVE_EN_N          PLX_GPIO5
+#define        PLX_MASTER_EN           PLX_GPIO6
+#define        PLX_SYNC_O_EN           PLX_GPIO4
+#define PLX_DSP_RES_N          PLX_GPIO8
+/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */
+#define PLX_GPIOC_INIT         (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \
+                       | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N)
+
+/* PLX Interrupt Control/STATUS */
+#define PLX_INTCSR_LINTI1_ENABLE 0x01
+#define PLX_INTCSR_LINTI1_STATUS 0x04
+#define PLX_INTCSR_LINTI2_ENABLE 0x08
+#define PLX_INTCSR_LINTI2_STATUS 0x20
+#define PLX_INTCSR_PCIINT_ENABLE 0x40
+
+/* PLX Registers */
+#define PLX_INTCSR 0x4c
+#define PLX_CNTRL  0x50
+#define PLX_GPIOC  0x54
+
+
+/*
+ * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1
+ */
+
+/* write only registers */
+#define R_CIRM                 0x00
+#define R_CTRL                 0x01
+#define R_BRG_PCM_CFG          0x02
+#define R_RAM_ADDR0            0x08
+#define R_RAM_ADDR1            0x09
+#define R_RAM_ADDR2            0x0A
+#define R_FIRST_FIFO           0x0B
+#define R_RAM_SZ               0x0C
+#define R_FIFO_MD              0x0D
+#define R_INC_RES_FIFO         0x0E
+#define R_FSM_IDX              0x0F
+#define R_FIFO                 0x0F
+#define R_SLOT                 0x10
+#define R_IRQMSK_MISC          0x11
+#define R_SCI_MSK              0x12
+#define R_IRQ_CTRL             0x13
+#define R_PCM_MD0              0x14
+#define R_PCM_MD1              0x15
+#define R_PCM_MD2              0x15
+#define R_SH0H                 0x15
+#define R_SH1H                 0x15
+#define R_SH0L                 0x15
+#define R_SH1L                 0x15
+#define R_SL_SEL0              0x15
+#define R_SL_SEL1              0x15
+#define R_SL_SEL2              0x15
+#define R_SL_SEL3              0x15
+#define R_SL_SEL4              0x15
+#define R_SL_SEL5              0x15
+#define R_SL_SEL6              0x15
+#define R_SL_SEL7              0x15
+#define R_ST_SEL               0x16
+#define R_ST_SYNC              0x17
+#define R_CONF_EN              0x18
+#define R_TI_WD                        0x1A
+#define R_BERT_WD_MD           0x1B
+#define R_DTMF                 0x1C
+#define R_DTMF_N               0x1D
+#define R_E1_WR_STA            0x20
+#define R_E1_RD_STA            0x20
+#define R_LOS0                 0x22
+#define R_LOS1                 0x23
+#define R_RX0                  0x24
+#define R_RX_FR0               0x25
+#define R_RX_FR1               0x26
+#define R_TX0                  0x28
+#define R_TX1                  0x29
+#define R_TX_FR0               0x2C
+
+#define R_TX_FR1               0x2D
+#define R_TX_FR2               0x2E
+#define R_JATT_ATT             0x2F /* undocumented */
+#define A_ST_RD_STATE          0x30
+#define A_ST_WR_STATE          0x30
+#define R_RX_OFF               0x30
+#define A_ST_CTRL0             0x31
+#define R_SYNC_OUT             0x31
+#define A_ST_CTRL1             0x32
+#define A_ST_CTRL2             0x33
+#define A_ST_SQ_WR             0x34
+#define R_TX_OFF               0x34
+#define R_SYNC_CTRL            0x35
+#define A_ST_CLK_DLY           0x37
+#define R_PWM0                 0x38
+#define R_PWM1                 0x39
+#define A_ST_B1_TX             0x3C
+#define A_ST_B2_TX             0x3D
+#define A_ST_D_TX              0x3E
+#define R_GPIO_OUT0            0x40
+#define R_GPIO_OUT1            0x41
+#define R_GPIO_EN0             0x42
+#define R_GPIO_EN1             0x43
+#define R_GPIO_SEL             0x44
+#define R_BRG_CTRL             0x45
+#define R_PWM_MD               0x46
+#define R_BRG_MD               0x47
+#define R_BRG_TIM0             0x48
+#define R_BRG_TIM1             0x49
+#define R_BRG_TIM2             0x4A
+#define R_BRG_TIM3             0x4B
+#define R_BRG_TIM_SEL01                0x4C
+#define R_BRG_TIM_SEL23                0x4D
+#define R_BRG_TIM_SEL45                0x4E
+#define R_BRG_TIM_SEL67                0x4F
+#define A_SL_CFG               0xD0
+#define A_CONF                 0xD1
+#define A_CH_MSK               0xF4
+#define A_CON_HDLC             0xFA
+#define A_SUBCH_CFG            0xFB
+#define A_CHANNEL              0xFC
+#define A_FIFO_SEQ             0xFD
+#define A_IRQ_MSK              0xFF
+
+/* read only registers */
+#define A_Z12                  0x04
+#define A_Z1L                  0x04
+#define A_Z1                   0x04
+#define A_Z1H                  0x05
+#define A_Z2L                  0x06
+#define A_Z2                   0x06
+#define A_Z2H                  0x07
+#define A_F1                   0x0C
+#define A_F12                  0x0C
+#define A_F2                   0x0D
+#define R_IRQ_OVIEW            0x10
+#define R_IRQ_MISC             0x11
+#define R_IRQ_STATECH          0x12
+#define R_CONF_OFLOW           0x14
+#define R_RAM_USE              0x15
+#define R_CHIP_ID              0x16
+#define R_BERT_STA             0x17
+#define R_F0_CNTL              0x18
+#define R_F0_CNTH              0x19
+#define R_BERT_EC              0x1A
+#define R_BERT_ECL             0x1A
+#define R_BERT_ECH             0x1B
+#define R_STATUS               0x1C
+#define R_CHIP_RV              0x1F
+#define R_STATE                        0x20
+#define R_SYNC_STA             0x24
+#define R_RX_SL0_0             0x25
+#define R_RX_SL0_1             0x26
+#define R_RX_SL0_2             0x27
+#define R_JATT_DIR             0x2b /* undocumented */
+#define R_SLIP                 0x2c
+#define A_ST_RD_STA            0x30
+#define R_FAS_EC               0x30
+#define R_FAS_ECL              0x30
+#define R_FAS_ECH              0x31
+#define R_VIO_EC               0x32
+#define R_VIO_ECL              0x32
+#define R_VIO_ECH              0x33
+#define A_ST_SQ_RD             0x34
+#define R_CRC_EC               0x34
+#define R_CRC_ECL              0x34
+#define R_CRC_ECH              0x35
+#define R_E_EC                 0x36
+#define R_E_ECL                        0x36
+#define R_E_ECH                        0x37
+#define R_SA6_SA13_EC          0x38
+#define R_SA6_SA13_ECL         0x38
+#define R_SA6_SA13_ECH         0x39
+#define R_SA6_SA23_EC          0x3A
+#define R_SA6_SA23_ECL         0x3A
+#define R_SA6_SA23_ECH         0x3B
+#define A_ST_B1_RX             0x3C
+#define A_ST_B2_RX             0x3D
+#define A_ST_D_RX              0x3E
+#define A_ST_E_RX              0x3F
+#define R_GPIO_IN0             0x40
+#define R_GPIO_IN1             0x41
+#define R_GPI_IN0              0x44
+#define R_GPI_IN1              0x45
+#define R_GPI_IN2              0x46
+#define R_GPI_IN3              0x47
+#define R_INT_DATA             0x88
+#define R_IRQ_FIFO_BL0         0xC8
+#define R_IRQ_FIFO_BL1         0xC9
+#define R_IRQ_FIFO_BL2         0xCA
+#define R_IRQ_FIFO_BL3         0xCB
+#define R_IRQ_FIFO_BL4         0xCC
+#define R_IRQ_FIFO_BL5         0xCD
+#define R_IRQ_FIFO_BL6         0xCE
+#define R_IRQ_FIFO_BL7         0xCF
+
+/* read and write registers */
+#define A_FIFO_DATA0           0x80
+#define A_FIFO_DATA1           0x80
+#define A_FIFO_DATA2           0x80
+#define A_FIFO_DATA0_NOINC     0x84
+#define A_FIFO_DATA1_NOINC     0x84
+#define A_FIFO_DATA2_NOINC     0x84
+#define R_RAM_DATA             0xC0
+
+
+/*
+ * BIT SETTING FOR HFC-4S/8S AND HFC-E1
+ */
+
+/* chapter 2: universal bus interface */
+/* R_CIRM */
+#define V_IRQ_SEL              0x01
+#define V_SRES                 0x08
+#define V_HFCRES               0x10
+#define V_PCMRES               0x20
+#define V_STRES                        0x40
+#define V_ETRES                        0x40
+#define V_RLD_EPR              0x80
+/* R_CTRL */
+#define V_FIFO_LPRIO           0x02
+#define V_SLOW_RD              0x04
+#define V_EXT_RAM              0x08
+#define V_CLK_OFF              0x20
+#define V_ST_CLK               0x40
+/* R_RAM_ADDR0 */
+#define V_RAM_ADDR2            0x01
+#define V_ADDR_RES             0x40
+#define V_ADDR_INC             0x80
+/* R_RAM_SZ */
+#define V_RAM_SZ               0x01
+#define V_PWM0_16KHZ           0x10
+#define V_PWM1_16KHZ           0x20
+#define V_FZ_MD                        0x80
+/* R_CHIP_ID */
+#define V_PNP_IRQ              0x01
+#define V_CHIP_ID              0x10
+
+/* chapter 3: data flow */
+/* R_FIRST_FIFO */
+#define V_FIRST_FIRO_DIR       0x01
+#define V_FIRST_FIFO_NUM       0x02
+/* R_FIFO_MD */
+#define V_FIFO_MD              0x01
+#define V_CSM_MD               0x04
+#define V_FSM_MD               0x08
+#define V_FIFO_SZ              0x10
+/* R_FIFO */
+#define V_FIFO_DIR             0x01
+#define V_FIFO_NUM             0x02
+#define V_REV                  0x80
+/* R_SLOT */
+#define V_SL_DIR               0x01
+#define V_SL_NUM               0x02
+/* A_SL_CFG */
+#define V_CH_DIR               0x01
+#define V_CH_SEL               0x02
+#define V_ROUTING              0x40
+/* A_CON_HDLC */
+#define V_IFF                  0x01
+#define V_HDLC_TRP             0x02
+#define V_TRP_IRQ              0x04
+#define V_DATA_FLOW            0x20
+/* A_SUBCH_CFG */
+#define V_BIT_CNT              0x01
+#define V_START_BIT            0x08
+#define V_LOOP_FIFO            0x40
+#define V_INV_DATA             0x80
+/* A_CHANNEL */
+#define V_CH_DIR0              0x01
+#define V_CH_NUM0              0x02
+/* A_FIFO_SEQ */
+#define V_NEXT_FIFO_DIR                0x01
+#define V_NEXT_FIFO_NUM                0x02
+#define V_SEQ_END              0x40
+
+/* chapter 4: FIFO handling and HDLC controller */
+/* R_INC_RES_FIFO */
+#define V_INC_F                        0x01
+#define V_RES_F                        0x02
+#define V_RES_LOST             0x04
+
+/* chapter 5: S/T interface */
+/* R_SCI_MSK */
+#define V_SCI_MSK_ST0          0x01
+#define V_SCI_MSK_ST1          0x02
+#define V_SCI_MSK_ST2          0x04
+#define V_SCI_MSK_ST3          0x08
+#define V_SCI_MSK_ST4          0x10
+#define V_SCI_MSK_ST5          0x20
+#define V_SCI_MSK_ST6          0x40
+#define V_SCI_MSK_ST7          0x80
+/* R_ST_SEL */
+#define V_ST_SEL               0x01
+#define V_MULT_ST              0x08
+/* R_ST_SYNC */
+#define V_SYNC_SEL             0x01
+#define V_AUTO_SYNC            0x08
+/* A_ST_WR_STA */
+#define V_ST_SET_STA           0x01
+#define V_ST_LD_STA            0x10
+#define V_ST_ACT               0x20
+#define V_SET_G2_G3            0x80
+/* A_ST_CTRL0 */
+#define V_B1_EN                        0x01
+#define V_B2_EN                        0x02
+#define V_ST_MD                        0x04
+#define V_D_PRIO               0x08
+#define V_SQ_EN                        0x10
+#define V_96KHZ                        0x20
+#define V_TX_LI                        0x40
+#define V_ST_STOP              0x80
+/* A_ST_CTRL1 */
+#define V_G2_G3_EN             0x01
+#define V_D_HI                 0x04
+#define V_E_IGNO               0x08
+#define V_E_LO                 0x10
+#define V_B12_SWAP             0x80
+/* A_ST_CTRL2 */
+#define V_B1_RX_EN             0x01
+#define V_B2_RX_EN             0x02
+#define V_ST_TRIS              0x40
+/* A_ST_CLK_DLY */
+#define V_ST_CK_DLY            0x01
+#define V_ST_SMPL              0x10
+/* A_ST_D_TX */
+#define V_ST_D_TX              0x40
+/* R_IRQ_STATECH */
+#define V_SCI_ST0              0x01
+#define V_SCI_ST1              0x02
+#define V_SCI_ST2              0x04
+#define V_SCI_ST3              0x08
+#define V_SCI_ST4              0x10
+#define V_SCI_ST5              0x20
+#define V_SCI_ST6              0x40
+#define V_SCI_ST7              0x80
+/* A_ST_RD_STA */
+#define V_ST_STA               0x01
+#define V_FR_SYNC_ST           0x10
+#define V_TI2_EXP              0x20
+#define V_INFO0                        0x40
+#define V_G2_G3                        0x80
+/* A_ST_SQ_RD */
+#define V_ST_SQ                        0x01
+#define V_MF_RX_RDY            0x10
+#define V_MF_TX_RDY            0x80
+/* A_ST_D_RX */
+#define V_ST_D_RX              0x40
+/* A_ST_E_RX */
+#define V_ST_E_RX              0x40
+
+/* chapter 5: E1 interface */
+/* R_E1_WR_STA */
+/* R_E1_RD_STA */
+#define V_E1_SET_STA           0x01
+#define V_E1_LD_STA            0x10
+/* R_RX0 */
+#define V_RX_CODE              0x01
+#define V_RX_FBAUD             0x04
+#define V_RX_CMI               0x08
+#define V_RX_INV_CMI           0x10
+#define V_RX_INV_CLK           0x20
+#define V_RX_INV_DATA          0x40
+#define V_AIS_ITU              0x80
+/* R_RX_FR0 */
+#define V_NO_INSYNC            0x01
+#define V_AUTO_RESYNC          0x02
+#define V_AUTO_RECO            0x04
+#define V_SWORD_COND           0x08
+#define V_SYNC_LOSS            0x10
+#define V_XCRC_SYNC            0x20
+#define V_MF_RESYNC            0x40
+#define V_RESYNC               0x80
+/* R_RX_FR1 */
+#define V_RX_MF                        0x01
+#define V_RX_MF_SYNC           0x02
+#define V_RX_SL0_RAM           0x04
+#define V_ERR_SIM              0x20
+#define V_RES_NMF              0x40
+/* R_TX0 */
+#define V_TX_CODE              0x01
+#define V_TX_FBAUD             0x04
+#define V_TX_CMI_CODE          0x08
+#define V_TX_INV_CMI_CODE      0x10
+#define V_TX_INV_CLK           0x20
+#define V_TX_INV_DATA          0x40
+#define V_OUT_EN               0x80
+/* R_TX1 */
+#define V_INV_CLK              0x01
+#define V_EXCHG_DATA_LI                0x02
+#define V_AIS_OUT              0x04
+#define V_ATX                  0x20
+#define V_NTRI                 0x40
+#define V_AUTO_ERR_RES         0x80
+/* R_TX_FR0 */
+#define V_TRP_FAS              0x01
+#define V_TRP_NFAS             0x02
+#define V_TRP_RAL              0x04
+#define V_TRP_SA               0x08
+/* R_TX_FR1 */
+#define V_TX_FAS               0x01
+#define V_TX_NFAS              0x02
+#define V_TX_RAL               0x04
+#define V_TX_SA                        0x08
+/* R_TX_FR2 */
+#define V_TX_MF                        0x01
+#define V_TRP_SL0              0x02
+#define V_TX_SL0_RAM           0x04
+#define V_TX_E                 0x10
+#define V_NEG_E                        0x20
+#define V_XS12_ON              0x40
+#define V_XS15_ON              0x80
+/* R_RX_OFF */
+#define V_RX_SZ                        0x01
+#define V_RX_INIT              0x04
+/* R_SYNC_OUT */
+#define V_SYNC_E1_RX           0x01
+#define V_IPATS0               0x20
+#define V_IPATS1               0x40
+#define V_IPATS2               0x80
+/* R_TX_OFF */
+#define V_TX_SZ                        0x01
+#define V_TX_INIT              0x04
+/* R_SYNC_CTRL */
+#define V_EXT_CLK_SYNC         0x01
+#define V_SYNC_OFFS            0x02
+#define V_PCM_SYNC             0x04
+#define V_NEG_CLK              0x08
+#define V_HCLK                 0x10
+/*
+#define V_JATT_AUTO_DEL                0x20
+#define V_JATT_AUTO            0x40
+*/
+#define V_JATT_OFF             0x80
+/* R_STATE */
+#define V_E1_STA               0x01
+#define V_ALT_FR_RX            0x40
+#define V_ALT_FR_TX            0x80
+/* R_SYNC_STA */
+#define V_RX_STA               0x01
+#define V_FR_SYNC_E1           0x04
+#define V_SIG_LOS              0x08
+#define V_MFA_STA              0x10
+#define V_AIS                  0x40
+#define V_NO_MF_SYNC           0x80
+/* R_RX_SL0_0 */
+#define V_SI_FAS               0x01
+#define V_SI_NFAS              0x02
+#define V_A                    0x04
+#define V_CRC_OK               0x08
+#define V_TX_E1                        0x10
+#define V_TX_E2                        0x20
+#define V_RX_E1                        0x40
+#define V_RX_E2                        0x80
+/* R_SLIP */
+#define V_SLIP_RX              0x01
+#define V_FOSLIP_RX            0x08
+#define V_SLIP_TX              0x10
+#define V_FOSLIP_TX            0x80
+
+/* chapter 6: PCM interface */
+/* R_PCM_MD0 */
+#define V_PCM_MD               0x01
+#define V_C4_POL               0x02
+#define V_F0_NEG               0x04
+#define V_F0_LEN               0x08
+#define V_PCM_ADDR             0x10
+/* R_SL_SEL0 */
+#define V_SL_SEL0              0x01
+#define V_SH_SEL0              0x80
+/* R_SL_SEL1 */
+#define V_SL_SEL1              0x01
+#define V_SH_SEL1              0x80
+/* R_SL_SEL2 */
+#define V_SL_SEL2              0x01
+#define V_SH_SEL2              0x80
+/* R_SL_SEL3 */
+#define V_SL_SEL3              0x01
+#define V_SH_SEL3              0x80
+/* R_SL_SEL4 */
+#define V_SL_SEL4              0x01
+#define V_SH_SEL4              0x80
+/* R_SL_SEL5 */
+#define V_SL_SEL5              0x01
+#define V_SH_SEL5              0x80
+/* R_SL_SEL6 */
+#define V_SL_SEL6              0x01
+#define V_SH_SEL6              0x80
+/* R_SL_SEL7 */
+#define V_SL_SEL7              0x01
+#define V_SH_SEL7              0x80
+/* R_PCM_MD1 */
+#define V_ODEC_CON             0x01
+#define V_PLL_ADJ              0x04
+#define V_PCM_DR               0x10
+#define V_PCM_LOOP             0x40
+/* R_PCM_MD2 */
+#define V_SYNC_PLL             0x02
+#define V_SYNC_SRC             0x04
+#define V_SYNC_OUT             0x08
+#define V_ICR_FR_TIME          0x40
+#define V_EN_PLL               0x80
+
+/* chapter 7: pulse width modulation */
+/* R_PWM_MD */
+#define V_EXT_IRQ_EN           0x08
+#define V_PWM0_MD              0x10
+#define V_PWM1_MD              0x40
+
+/* chapter 8: multiparty audio conferences */
+/* R_CONF_EN */
+#define V_CONF_EN              0x01
+#define V_ULAW                 0x80
+/* A_CONF */
+#define V_CONF_NUM             0x01
+#define V_NOISE_SUPPR          0x08
+#define V_ATT_LEV              0x20
+#define V_CONF_SL              0x80
+/* R_CONF_OFLOW */
+#define V_CONF_OFLOW0          0x01
+#define V_CONF_OFLOW1          0x02
+#define V_CONF_OFLOW2          0x04
+#define V_CONF_OFLOW3          0x08
+#define V_CONF_OFLOW4          0x10
+#define V_CONF_OFLOW5          0x20
+#define V_CONF_OFLOW6          0x40
+#define V_CONF_OFLOW7          0x80
+
+/* chapter 9: DTMF contoller */
+/* R_DTMF0 */
+#define V_DTMF_EN              0x01
+#define V_HARM_SEL             0x02
+#define V_DTMF_RX_CH           0x04
+#define V_DTMF_STOP            0x08
+#define V_CHBL_SEL             0x10
+#define V_RST_DTMF             0x40
+#define V_ULAW_SEL             0x80
+
+/* chapter 10: BERT */
+/* R_BERT_WD_MD */
+#define V_PAT_SEQ              0x01
+#define V_BERT_ERR             0x08
+#define V_AUTO_WD_RES          0x20
+#define V_WD_RES               0x80
+/* R_BERT_STA */
+#define V_BERT_SYNC_SRC                0x01
+#define V_BERT_SYNC            0x10
+#define V_BERT_INV_DATA                0x20
+
+/* chapter 11: auxiliary interface */
+/* R_BRG_PCM_CFG */
+#define V_BRG_EN               0x01
+#define V_BRG_MD               0x02
+#define V_PCM_CLK              0x20
+#define V_ADDR_WRDLY           0x40
+/* R_BRG_CTRL */
+#define V_BRG_CS               0x01
+#define V_BRG_ADDR             0x08
+#define V_BRG_CS_SRC           0x80
+/* R_BRG_MD */
+#define V_BRG_MD0              0x01
+#define V_BRG_MD1              0x02
+#define V_BRG_MD2              0x04
+#define V_BRG_MD3              0x08
+#define V_BRG_MD4              0x10
+#define V_BRG_MD5              0x20
+#define V_BRG_MD6              0x40
+#define V_BRG_MD7              0x80
+/* R_BRG_TIM0 */
+#define V_BRG_TIM0_IDLE                0x01
+#define V_BRG_TIM0_CLK         0x10
+/* R_BRG_TIM1 */
+#define V_BRG_TIM1_IDLE                0x01
+#define V_BRG_TIM1_CLK         0x10
+/* R_BRG_TIM2 */
+#define V_BRG_TIM2_IDLE                0x01
+#define V_BRG_TIM2_CLK         0x10
+/* R_BRG_TIM3 */
+#define V_BRG_TIM3_IDLE                0x01
+#define V_BRG_TIM3_CLK         0x10
+/* R_BRG_TIM_SEL01 */
+#define V_BRG_WR_SEL0          0x01
+#define V_BRG_RD_SEL0          0x04
+#define V_BRG_WR_SEL1          0x10
+#define V_BRG_RD_SEL1          0x40
+/* R_BRG_TIM_SEL23 */
+#define V_BRG_WR_SEL2          0x01
+#define V_BRG_RD_SEL2          0x04
+#define V_BRG_WR_SEL3          0x10
+#define V_BRG_RD_SEL3          0x40
+/* R_BRG_TIM_SEL45 */
+#define V_BRG_WR_SEL4          0x01
+#define V_BRG_RD_SEL4          0x04
+#define V_BRG_WR_SEL5          0x10
+#define V_BRG_RD_SEL5          0x40
+/* R_BRG_TIM_SEL67 */
+#define V_BRG_WR_SEL6          0x01
+#define V_BRG_RD_SEL6          0x04
+#define V_BRG_WR_SEL7          0x10
+#define V_BRG_RD_SEL7          0x40
+
+/* chapter 12: clock, reset, interrupt, timer and watchdog */
+/* R_IRQMSK_MISC */
+#define V_STA_IRQMSK           0x01
+#define V_TI_IRQMSK            0x02
+#define V_PROC_IRQMSK          0x04
+#define V_DTMF_IRQMSK          0x08
+#define V_IRQ1S_MSK            0x10
+#define V_SA6_IRQMSK           0x20
+#define V_RX_EOMF_MSK          0x40
+#define V_TX_EOMF_MSK          0x80
+/* R_IRQ_CTRL */
+#define V_FIFO_IRQ             0x01
+#define V_GLOB_IRQ_EN          0x08
+#define V_IRQ_POL              0x10
+/* R_TI_WD */
+#define V_EV_TS                        0x01
+#define V_WD_TS                        0x10
+/* A_IRQ_MSK */
+#define V_IRQ                  0x01
+#define V_BERT_EN              0x02
+#define V_MIX_IRQ              0x04
+/* R_IRQ_OVIEW */
+#define V_IRQ_FIFO_BL0         0x01
+#define V_IRQ_FIFO_BL1         0x02
+#define V_IRQ_FIFO_BL2         0x04
+#define V_IRQ_FIFO_BL3         0x08
+#define V_IRQ_FIFO_BL4         0x10
+#define V_IRQ_FIFO_BL5         0x20
+#define V_IRQ_FIFO_BL6         0x40
+#define V_IRQ_FIFO_BL7         0x80
+/* R_IRQ_MISC */
+#define V_STA_IRQ              0x01
+#define V_TI_IRQ               0x02
+#define V_IRQ_PROC             0x04
+#define V_DTMF_IRQ             0x08
+#define V_IRQ1S                        0x10
+#define V_SA6_IRQ              0x20
+#define V_RX_EOMF              0x40
+#define V_TX_EOMF              0x80
+/* R_STATUS */
+#define V_BUSY                 0x01
+#define V_PROC                 0x02
+#define V_DTMF_STA             0x04
+#define V_LOST_STA             0x08
+#define V_SYNC_IN              0x10
+#define V_EXT_IRQSTA           0x20
+#define V_MISC_IRQSTA          0x40
+#define V_FR_IRQSTA            0x80
+/* R_IRQ_FIFO_BL0 */
+#define V_IRQ_FIFO0_TX         0x01
+#define V_IRQ_FIFO0_RX         0x02
+#define V_IRQ_FIFO1_TX         0x04
+#define V_IRQ_FIFO1_RX         0x08
+#define V_IRQ_FIFO2_TX         0x10
+#define V_IRQ_FIFO2_RX         0x20
+#define V_IRQ_FIFO3_TX         0x40
+#define V_IRQ_FIFO3_RX         0x80
+/* R_IRQ_FIFO_BL1 */
+#define V_IRQ_FIFO4_TX         0x01
+#define V_IRQ_FIFO4_RX         0x02
+#define V_IRQ_FIFO5_TX         0x04
+#define V_IRQ_FIFO5_RX         0x08
+#define V_IRQ_FIFO6_TX         0x10
+#define V_IRQ_FIFO6_RX         0x20
+#define V_IRQ_FIFO7_TX         0x40
+#define V_IRQ_FIFO7_RX         0x80
+/* R_IRQ_FIFO_BL2 */
+#define V_IRQ_FIFO8_TX         0x01
+#define V_IRQ_FIFO8_RX         0x02
+#define V_IRQ_FIFO9_TX         0x04
+#define V_IRQ_FIFO9_RX         0x08
+#define V_IRQ_FIFO10_TX                0x10
+#define V_IRQ_FIFO10_RX                0x20
+#define V_IRQ_FIFO11_TX                0x40
+#define V_IRQ_FIFO11_RX                0x80
+/* R_IRQ_FIFO_BL3 */
+#define V_IRQ_FIFO12_TX                0x01
+#define V_IRQ_FIFO12_RX                0x02
+#define V_IRQ_FIFO13_TX                0x04
+#define V_IRQ_FIFO13_RX                0x08
+#define V_IRQ_FIFO14_TX                0x10
+#define V_IRQ_FIFO14_RX                0x20
+#define V_IRQ_FIFO15_TX                0x40
+#define V_IRQ_FIFO15_RX                0x80
+/* R_IRQ_FIFO_BL4 */
+#define V_IRQ_FIFO16_TX                0x01
+#define V_IRQ_FIFO16_RX                0x02
+#define V_IRQ_FIFO17_TX                0x04
+#define V_IRQ_FIFO17_RX                0x08
+#define V_IRQ_FIFO18_TX                0x10
+#define V_IRQ_FIFO18_RX                0x20
+#define V_IRQ_FIFO19_TX                0x40
+#define V_IRQ_FIFO19_RX                0x80
+/* R_IRQ_FIFO_BL5 */
+#define V_IRQ_FIFO20_TX                0x01
+#define V_IRQ_FIFO20_RX                0x02
+#define V_IRQ_FIFO21_TX                0x04
+#define V_IRQ_FIFO21_RX                0x08
+#define V_IRQ_FIFO22_TX                0x10
+#define V_IRQ_FIFO22_RX                0x20
+#define V_IRQ_FIFO23_TX                0x40
+#define V_IRQ_FIFO23_RX                0x80
+/* R_IRQ_FIFO_BL6 */
+#define V_IRQ_FIFO24_TX                0x01
+#define V_IRQ_FIFO24_RX                0x02
+#define V_IRQ_FIFO25_TX                0x04
+#define V_IRQ_FIFO25_RX                0x08
+#define V_IRQ_FIFO26_TX                0x10
+#define V_IRQ_FIFO26_RX                0x20
+#define V_IRQ_FIFO27_TX                0x40
+#define V_IRQ_FIFO27_RX                0x80
+/* R_IRQ_FIFO_BL7 */
+#define V_IRQ_FIFO28_TX                0x01
+#define V_IRQ_FIFO28_RX                0x02
+#define V_IRQ_FIFO29_TX                0x04
+#define V_IRQ_FIFO29_RX                0x08
+#define V_IRQ_FIFO30_TX                0x10
+#define V_IRQ_FIFO30_RX                0x20
+#define V_IRQ_FIFO31_TX                0x40
+#define V_IRQ_FIFO31_RX                0x80
+
+/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */
+/* R_GPIO_OUT0 */
+#define V_GPIO_OUT0            0x01
+#define V_GPIO_OUT1            0x02
+#define V_GPIO_OUT2            0x04
+#define V_GPIO_OUT3            0x08
+#define V_GPIO_OUT4            0x10
+#define V_GPIO_OUT5            0x20
+#define V_GPIO_OUT6            0x40
+#define V_GPIO_OUT7            0x80
+/* R_GPIO_OUT1 */
+#define V_GPIO_OUT8            0x01
+#define V_GPIO_OUT9            0x02
+#define V_GPIO_OUT10           0x04
+#define V_GPIO_OUT11           0x08
+#define V_GPIO_OUT12           0x10
+#define V_GPIO_OUT13           0x20
+#define V_GPIO_OUT14           0x40
+#define V_GPIO_OUT15           0x80
+/* R_GPIO_EN0 */
+#define V_GPIO_EN0             0x01
+#define V_GPIO_EN1             0x02
+#define V_GPIO_EN2             0x04
+#define V_GPIO_EN3             0x08
+#define V_GPIO_EN4             0x10
+#define V_GPIO_EN5             0x20
+#define V_GPIO_EN6             0x40
+#define V_GPIO_EN7             0x80
+/* R_GPIO_EN1 */
+#define V_GPIO_EN8             0x01
+#define V_GPIO_EN9             0x02
+#define V_GPIO_EN10            0x04
+#define V_GPIO_EN11            0x08
+#define V_GPIO_EN12            0x10
+#define V_GPIO_EN13            0x20
+#define V_GPIO_EN14            0x40
+#define V_GPIO_EN15            0x80
+/* R_GPIO_SEL */
+#define V_GPIO_SEL0            0x01
+#define V_GPIO_SEL1            0x02
+#define V_GPIO_SEL2            0x04
+#define V_GPIO_SEL3            0x08
+#define V_GPIO_SEL4            0x10
+#define V_GPIO_SEL5            0x20
+#define V_GPIO_SEL6            0x40
+#define V_GPIO_SEL7            0x80
+/* R_GPIO_IN0 */
+#define V_GPIO_IN0             0x01
+#define V_GPIO_IN1             0x02
+#define V_GPIO_IN2             0x04
+#define V_GPIO_IN3             0x08
+#define V_GPIO_IN4             0x10
+#define V_GPIO_IN5             0x20
+#define V_GPIO_IN6             0x40
+#define V_GPIO_IN7             0x80
+/* R_GPIO_IN1 */
+#define V_GPIO_IN8             0x01
+#define V_GPIO_IN9             0x02
+#define V_GPIO_IN10            0x04
+#define V_GPIO_IN11            0x08
+#define V_GPIO_IN12            0x10
+#define V_GPIO_IN13            0x20
+#define V_GPIO_IN14            0x40
+#define V_GPIO_IN15            0x80
+/* R_GPI_IN0 */
+#define V_GPI_IN0              0x01
+#define V_GPI_IN1              0x02
+#define V_GPI_IN2              0x04
+#define V_GPI_IN3              0x08
+#define V_GPI_IN4              0x10
+#define V_GPI_IN5              0x20
+#define V_GPI_IN6              0x40
+#define V_GPI_IN7              0x80
+/* R_GPI_IN1 */
+#define V_GPI_IN8              0x01
+#define V_GPI_IN9              0x02
+#define V_GPI_IN10             0x04
+#define V_GPI_IN11             0x08
+#define V_GPI_IN12             0x10
+#define V_GPI_IN13             0x20
+#define V_GPI_IN14             0x40
+#define V_GPI_IN15             0x80
+/* R_GPI_IN2 */
+#define V_GPI_IN16             0x01
+#define V_GPI_IN17             0x02
+#define V_GPI_IN18             0x04
+#define V_GPI_IN19             0x08
+#define V_GPI_IN20             0x10
+#define V_GPI_IN21             0x20
+#define V_GPI_IN22             0x40
+#define V_GPI_IN23             0x80
+/* R_GPI_IN3 */
+#define V_GPI_IN24             0x01
+#define V_GPI_IN25             0x02
+#define V_GPI_IN26             0x04
+#define V_GPI_IN27             0x08
+#define V_GPI_IN28             0x10
+#define V_GPI_IN29             0x20
+#define V_GPI_IN30             0x40
+#define V_GPI_IN31             0x80
+
+/* map of all registers, used for debugging */
+
+#ifdef HFC_REGISTER_DEBUG
+struct hfc_register_names {
+       char *name;
+       u_char reg;
+} hfc_register_names[] = {
+       /* write registers */
+       {"R_CIRM",              0x00},
+       {"R_CTRL",              0x01},
+       {"R_BRG_PCM_CFG ",      0x02},
+       {"R_RAM_ADDR0",         0x08},
+       {"R_RAM_ADDR1",         0x09},
+       {"R_RAM_ADDR2",         0x0A},
+       {"R_FIRST_FIFO",        0x0B},
+       {"R_RAM_SZ",            0x0C},
+       {"R_FIFO_MD",           0x0D},
+       {"R_INC_RES_FIFO",      0x0E},
+       {"R_FIFO / R_FSM_IDX",  0x0F},
+       {"R_SLOT",              0x10},
+       {"R_IRQMSK_MISC",       0x11},
+       {"R_SCI_MSK",           0x12},
+       {"R_IRQ_CTRL",          0x13},
+       {"R_PCM_MD0",           0x14},
+       {"R_0x15",              0x15},
+       {"R_ST_SEL",            0x16},
+       {"R_ST_SYNC",           0x17},
+       {"R_CONF_EN",           0x18},
+       {"R_TI_WD",             0x1A},
+       {"R_BERT_WD_MD",        0x1B},
+       {"R_DTMF",              0x1C},
+       {"R_DTMF_N",            0x1D},
+       {"R_E1_XX_STA",         0x20},
+       {"R_LOS0",              0x22},
+       {"R_LOS1",              0x23},
+       {"R_RX0",               0x24},
+       {"R_RX_FR0",            0x25},
+       {"R_RX_FR1",            0x26},
+       {"R_TX0",               0x28},
+       {"R_TX1",               0x29},
+       {"R_TX_FR0",            0x2C},
+       {"R_TX_FR1",            0x2D},
+       {"R_TX_FR2",            0x2E},
+       {"R_JATT_ATT",          0x2F},
+       {"A_ST_xx_STA/R_RX_OFF", 0x30},
+       {"A_ST_CTRL0/R_SYNC_OUT", 0x31},
+       {"A_ST_CTRL1",          0x32},
+       {"A_ST_CTRL2",          0x33},
+       {"A_ST_SQ_WR",          0x34},
+       {"R_TX_OFF",            0x34},
+       {"R_SYNC_CTRL",         0x35},
+       {"A_ST_CLK_DLY",        0x37},
+       {"R_PWM0",              0x38},
+       {"R_PWM1",              0x39},
+       {"A_ST_B1_TX",          0x3C},
+       {"A_ST_B2_TX",          0x3D},
+       {"A_ST_D_TX",           0x3E},
+       {"R_GPIO_OUT0",         0x40},
+       {"R_GPIO_OUT1",         0x41},
+       {"R_GPIO_EN0",          0x42},
+       {"R_GPIO_EN1",          0x43},
+       {"R_GPIO_SEL",          0x44},
+       {"R_BRG_CTRL",          0x45},
+       {"R_PWM_MD",            0x46},
+       {"R_BRG_MD",            0x47},
+       {"R_BRG_TIM0",          0x48},
+       {"R_BRG_TIM1",          0x49},
+       {"R_BRG_TIM2",          0x4A},
+       {"R_BRG_TIM3",          0x4B},
+       {"R_BRG_TIM_SEL01",     0x4C},
+       {"R_BRG_TIM_SEL23",     0x4D},
+       {"R_BRG_TIM_SEL45",     0x4E},
+       {"R_BRG_TIM_SEL67",     0x4F},
+       {"A_FIFO_DATA0-2",      0x80},
+       {"A_FIFO_DATA0-2_NOINC", 0x84},
+       {"R_RAM_DATA",          0xC0},
+       {"A_SL_CFG",            0xD0},
+       {"A_CONF",              0xD1},
+       {"A_CH_MSK",            0xF4},
+       {"A_CON_HDLC",          0xFA},
+       {"A_SUBCH_CFG",         0xFB},
+       {"A_CHANNEL",           0xFC},
+       {"A_FIFO_SEQ",          0xFD},
+       {"A_IRQ_MSK",           0xFF},
+       {NULL, 0},
+
+       /* read registers */
+       {"A_Z1",                0x04},
+       {"A_Z1H",               0x05},
+       {"A_Z2",                0x06},
+       {"A_Z2H",               0x07},
+       {"A_F1",                0x0C},
+       {"A_F2",                0x0D},
+       {"R_IRQ_OVIEW",         0x10},
+       {"R_IRQ_MISC",          0x11},
+       {"R_IRQ_STATECH",       0x12},
+       {"R_CONF_OFLOW",        0x14},
+       {"R_RAM_USE",           0x15},
+       {"R_CHIP_ID",           0x16},
+       {"R_BERT_STA",          0x17},
+       {"R_F0_CNTL",           0x18},
+       {"R_F0_CNTH",           0x19},
+       {"R_BERT_ECL",          0x1A},
+       {"R_BERT_ECH",          0x1B},
+       {"R_STATUS",            0x1C},
+       {"R_CHIP_RV",           0x1F},
+       {"R_STATE",             0x20},
+       {"R_SYNC_STA",          0x24},
+       {"R_RX_SL0_0",          0x25},
+       {"R_RX_SL0_1",          0x26},
+       {"R_RX_SL0_2",          0x27},
+       {"R_JATT_DIR",          0x2b},
+       {"R_SLIP",              0x2c},
+       {"A_ST_RD_STA",         0x30},
+       {"R_FAS_ECL",           0x30},
+       {"R_FAS_ECH",           0x31},
+       {"R_VIO_ECL",           0x32},
+       {"R_VIO_ECH",           0x33},
+       {"R_CRC_ECL / A_ST_SQ_RD", 0x34},
+       {"R_CRC_ECH",           0x35},
+       {"R_E_ECL",             0x36},
+       {"R_E_ECH",             0x37},
+       {"R_SA6_SA13_ECL",      0x38},
+       {"R_SA6_SA13_ECH",      0x39},
+       {"R_SA6_SA23_ECL",      0x3A},
+       {"R_SA6_SA23_ECH",      0x3B},
+       {"A_ST_B1_RX",          0x3C},
+       {"A_ST_B2_RX",          0x3D},
+       {"A_ST_D_RX",           0x3E},
+       {"A_ST_E_RX",           0x3F},
+       {"R_GPIO_IN0",          0x40},
+       {"R_GPIO_IN1",          0x41},
+       {"R_GPI_IN0",           0x44},
+       {"R_GPI_IN1",           0x45},
+       {"R_GPI_IN2",           0x46},
+       {"R_GPI_IN3",           0x47},
+       {"A_FIFO_DATA0-2",      0x80},
+       {"A_FIFO_DATA0-2_NOINC", 0x84},
+       {"R_INT_DATA",          0x88},
+       {"R_RAM_DATA",          0xC0},
+       {"R_IRQ_FIFO_BL0",      0xC8},
+       {"R_IRQ_FIFO_BL1",      0xC9},
+       {"R_IRQ_FIFO_BL2",      0xCA},
+       {"R_IRQ_FIFO_BL3",      0xCB},
+       {"R_IRQ_FIFO_BL4",      0xCC},
+       {"R_IRQ_FIFO_BL5",      0xCD},
+       {"R_IRQ_FIFO_BL6",      0xCE},
+       {"R_IRQ_FIFO_BL7",      0xCF},
+};
+#endif /* HFC_REGISTER_DEBUG */
+
diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h
new file mode 100644 (file)
index 0000000..fd2c9be
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ *  specific defines for CCD's HFC 2BDS0 PCI chips
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * thresholds for transparent B-channel mode
+ * change mask and threshold simultaneously
+ */
+#define HFCPCI_BTRANS_THRESHOLD 128
+#define HFCPCI_BTRANS_MAX      256
+#define HFCPCI_BTRANS_THRESMASK 0x00
+
+/* defines for PCI config */
+#define PCI_ENA_MEMIO          0x02
+#define PCI_ENA_MASTER         0x04
+
+/* GCI/IOM bus monitor registers */
+#define HCFPCI_C_I             0x08
+#define HFCPCI_TRxR            0x0C
+#define HFCPCI_MON1_D          0x28
+#define HFCPCI_MON2_D          0x2C
+
+/* GCI/IOM bus timeslot registers */
+#define HFCPCI_B1_SSL          0x80
+#define HFCPCI_B2_SSL          0x84
+#define HFCPCI_AUX1_SSL                0x88
+#define HFCPCI_AUX2_SSL                0x8C
+#define HFCPCI_B1_RSL          0x90
+#define HFCPCI_B2_RSL          0x94
+#define HFCPCI_AUX1_RSL                0x98
+#define HFCPCI_AUX2_RSL                0x9C
+
+/* GCI/IOM bus data registers */
+#define HFCPCI_B1_D            0xA0
+#define HFCPCI_B2_D            0xA4
+#define HFCPCI_AUX1_D          0xA8
+#define HFCPCI_AUX2_D          0xAC
+
+/* GCI/IOM bus configuration registers */
+#define HFCPCI_MST_EMOD                0xB4
+#define HFCPCI_MST_MODE                0xB8
+#define HFCPCI_CONNECT                 0xBC
+
+
+/* Interrupt and status registers */
+#define HFCPCI_FIFO_EN         0x44
+#define HFCPCI_TRM             0x48
+#define HFCPCI_B_MODE          0x4C
+#define HFCPCI_CHIP_ID         0x58
+#define HFCPCI_CIRM            0x60
+#define HFCPCI_CTMT            0x64
+#define HFCPCI_INT_M1          0x68
+#define HFCPCI_INT_M2          0x6C
+#define HFCPCI_INT_S1          0x78
+#define HFCPCI_INT_S2          0x7C
+#define HFCPCI_STATUS          0x70
+
+/* S/T section registers */
+#define HFCPCI_STATES          0xC0
+#define HFCPCI_SCTRL           0xC4
+#define HFCPCI_SCTRL_E         0xC8
+#define HFCPCI_SCTRL_R         0xCC
+#define HFCPCI_SQ              0xD0
+#define HFCPCI_CLKDEL          0xDC
+#define HFCPCI_B1_REC          0xF0
+#define HFCPCI_B1_SEND         0xF0
+#define HFCPCI_B2_REC          0xF4
+#define HFCPCI_B2_SEND         0xF4
+#define HFCPCI_D_REC           0xF8
+#define HFCPCI_D_SEND          0xF8
+#define HFCPCI_E_REC           0xFC
+
+
+/* bits in status register (READ) */
+#define HFCPCI_PCI_PROC                0x02
+#define HFCPCI_NBUSY           0x04
+#define HFCPCI_TIMER_ELAP      0x10
+#define HFCPCI_STATINT         0x20
+#define HFCPCI_FRAMEINT                0x40
+#define HFCPCI_ANYINT          0x80
+
+/* bits in CTMT (Write) */
+#define HFCPCI_CLTIMER         0x80
+#define HFCPCI_TIM3_125                0x04
+#define HFCPCI_TIM25           0x10
+#define HFCPCI_TIM50           0x14
+#define HFCPCI_TIM400          0x18
+#define HFCPCI_TIM800          0x1C
+#define HFCPCI_AUTO_TIMER      0x20
+#define HFCPCI_TRANSB2         0x02
+#define HFCPCI_TRANSB1         0x01
+
+/* bits in CIRM (Write) */
+#define HFCPCI_AUX_MSK         0x07
+#define HFCPCI_RESET           0x08
+#define HFCPCI_B1_REV          0x40
+#define HFCPCI_B2_REV          0x80
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCPCI_INTS_B1TRANS    0x01
+#define HFCPCI_INTS_B2TRANS    0x02
+#define HFCPCI_INTS_DTRANS     0x04
+#define HFCPCI_INTS_B1REC      0x08
+#define HFCPCI_INTS_B2REC      0x10
+#define HFCPCI_INTS_DREC       0x20
+#define HFCPCI_INTS_L1STATE    0x40
+#define HFCPCI_INTS_TIMER      0x80
+
+/* bits in INT_M2 */
+#define HFCPCI_PROC_TRANS      0x01
+#define HFCPCI_GCI_I_CHG       0x02
+#define HFCPCI_GCI_MON_REC     0x04
+#define HFCPCI_IRQ_ENABLE      0x08
+#define HFCPCI_PMESEL          0x80
+
+/* bits in STATES */
+#define HFCPCI_STATE_MSK       0x0F
+#define HFCPCI_LOAD_STATE      0x10
+#define HFCPCI_ACTIVATE                0x20
+#define HFCPCI_DO_ACTION       0x40
+#define HFCPCI_NT_G2_G3                0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCPCI_MASTER          0x01
+#define HFCPCI_SLAVE           0x00
+#define HFCPCI_F0IO_POSITIV    0x02
+#define HFCPCI_F0_NEGATIV      0x04
+#define HFCPCI_F0_2C4          0x08
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define SCTRL_B1_ENA           0x01
+#define SCTRL_B2_ENA           0x02
+#define SCTRL_MODE_TE          0x00
+#define SCTRL_MODE_NT          0x04
+#define SCTRL_LOW_PRIO         0x08
+#define SCTRL_SQ_ENA           0x10
+#define SCTRL_TEST             0x20
+#define SCTRL_NONE_CAP         0x40
+#define SCTRL_PWR_DOWN         0x80
+
+/* bits in SCTRL_E  */
+#define HFCPCI_AUTO_AWAKE      0x01
+#define HFCPCI_DBIT_1          0x04
+#define HFCPCI_IGNORE_COL      0x08
+#define HFCPCI_CHG_B1_B2       0x80
+
+/* bits in FIFO_EN register */
+#define HFCPCI_FIFOEN_B1       0x03
+#define HFCPCI_FIFOEN_B2       0x0C
+#define HFCPCI_FIFOEN_DTX      0x10
+#define HFCPCI_FIFOEN_B1TX     0x01
+#define HFCPCI_FIFOEN_B1RX     0x02
+#define HFCPCI_FIFOEN_B2TX     0x04
+#define HFCPCI_FIFOEN_B2RX     0x08
+
+
+/* definitions of fifo memory area */
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL    0x200
+#define B_FIFO_SIZE  (0x2000 - B_SUB_VAL)
+#define D_FIFO_SIZE  512
+#define D_FREG_MASK  0xF
+
+struct zt {
+       unsigned short z1;  /* Z1 pointer 16 Bit */
+       unsigned short z2;  /* Z2 pointer 16 Bit */
+};
+
+struct dfifo {
+       u_char data[D_FIFO_SIZE]; /* FIFO data space */
+       u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */
+       u_char f1, f2; /* f pointers */
+       u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */
+       /* mask index with D_FREG_MASK for access */
+       struct zt za[MAX_D_FRAMES+1];
+       u_char fill3[0x4000-0x2100]; /* align 16K */
+};
+
+struct bzfifo {
+       struct zt       za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */
+       u_char          f1, f2; /* f pointers */
+       u_char          fill[0x2100-0x2082]; /* alignment */
+};
+
+
+union fifo_area {
+       struct {
+               struct dfifo d_tx; /* D-send channel */
+               struct dfifo d_rx; /* D-receive channel */
+       } d_chan;
+       struct {
+               u_char          fill1[0x200];
+               u_char          txdat_b1[B_FIFO_SIZE];
+               struct bzfifo   txbz_b1;
+               struct bzfifo   txbz_b2;
+               u_char          txdat_b2[B_FIFO_SIZE];
+               u_char          fill2[D_FIFO_SIZE];
+               u_char          rxdat_b1[B_FIFO_SIZE];
+               struct bzfifo   rxbz_b1;
+               struct bzfifo   rxbz_b2;
+               u_char rxdat_b2[B_FIFO_SIZE];
+       } b_chans;
+       u_char fill[32768];
+};
+
+#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io)+b))
+#define Read_hfc(a, b) (readb((a->hw.pci_io)+b))
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
new file mode 100644 (file)
index 0000000..2649ea5
--- /dev/null
@@ -0,0 +1,5320 @@
+/*
+ * hfcmulti.c  low level driver for hfc-4s/hfc-8s/hfc-e1 based cards
+ *
+ * Author      Andreas Eversberg (jolly@eversberg.eu)
+ * ported to mqueue mechanism:
+ *             Peter Sprenger (sprengermoving-bytes.de)
+ *
+ * inspired by existing hfc-pci driver:
+ * Copyright 1999  by Werner Cornelius (werner@isdn-development.de)
+ * Copyright 2008  by Karsten Keil (kkeil@suse.de)
+ * Copyright 2008  by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Thanks to Cologne Chip AG for this great controller!
+ */
+
+/*
+ * module parameters:
+ * type:
+ *     By default (0), the card is automatically detected.
+ *     Or use the following combinations:
+ *     Bit 0-7   = 0x00001 = HFC-E1 (1 port)
+ * or  Bit 0-7   = 0x00004 = HFC-4S (4 ports)
+ * or  Bit 0-7   = 0x00008 = HFC-8S (8 ports)
+ *     Bit 8     = 0x00100 = uLaw (instead of aLaw)
+ *     Bit 9     = 0x00200 = Disable DTMF detect on all B-channels via hardware
+ *     Bit 10    = spare
+ *     Bit 11    = 0x00800 = Force PCM bus into slave mode. (otherwhise auto)
+ * or   Bit 12    = 0x01000 = Force PCM bus into master mode. (otherwhise auto)
+ *     Bit 13    = spare
+ *     Bit 14    = 0x04000 = Use external ram (128K)
+ *     Bit 15    = 0x08000 = Use external ram (512K)
+ *     Bit 16    = 0x10000 = Use 64 timeslots instead of 32
+ * or  Bit 17    = 0x20000 = Use 128 timeslots instead of anything else
+ *     Bit 18    = spare
+ *     Bit 19    = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog)
+ * (all other bits are reserved and shall be 0)
+ *     example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM
+ *              bus (PCM master)
+ *
+ * port: (optional or required for all ports on all installed cards)
+ *     HFC-4S/HFC-8S only bits:
+ *     Bit 0     = 0x001 = Use master clock for this S/T interface
+ *                         (ony once per chip).
+ *     Bit 1     = 0x002 = transmitter line setup (non capacitive mode)
+ *                         Don't use this unless you know what you are doing!
+ *     Bit 2     = 0x004 = Disable E-channel. (No E-channel processing)
+ *     example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock
+ *              received from port 1
+ *
+ *     HFC-E1 only bits:
+ *     Bit 0     = 0x0001 = interface: 0=copper, 1=optical
+ *     Bit 1     = 0x0002 = reserved (later for 32 B-channels transparent mode)
+ *     Bit 2     = 0x0004 = Report LOS
+ *     Bit 3     = 0x0008 = Report AIS
+ *     Bit 4     = 0x0010 = Report SLIP
+ *     Bit 5     = 0x0020 = Report RDI
+ *     Bit 8     = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame
+ *                          mode instead.
+ *     Bit 9     = 0x0200 = Force get clock from interface, even in NT mode.
+ * or  Bit 10    = 0x0400 = Force put clock to interface, even in TE mode.
+ *     Bit 11    = 0x0800 = Use direct RX clock for PCM sync rather than PLL.
+ *                          (E1 only)
+ *     Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0
+ *                          for default.
+ * (all other bits are reserved and shall be 0)
+ *
+ * debug:
+ *     NOTE: only one debug value must be given for all cards
+ *     enable debugging (see hfc_multi.h for debug options)
+ *
+ * poll:
+ *     NOTE: only one poll value must be given for all cards
+ *     Give the number of samples for each fifo process.
+ *     By default 128 is used. Decrease to reduce delay, increase to
+ *     reduce cpu load. If unsure, don't mess with it!
+ *     Valid is 8, 16, 32, 64, 128, 256.
+ *
+ * pcm:
+ *     NOTE: only one pcm value must be given for every card.
+ *     The PCM bus id tells the mISDNdsp module about the connected PCM bus.
+ *     By default (0), the PCM bus id is 100 for the card that is PCM master.
+ *     If multiple cards are PCM master (because they are not interconnected),
+ *     each card with PCM master will have increasing PCM id.
+ *     All PCM busses with the same ID are expected to be connected and have
+ *     common time slots slots.
+ *     Only one chip of the PCM bus must be master, the others slave.
+ *     -1 means no support of PCM bus not even.
+ *     Omit this value, if all cards are interconnected or none is connected.
+ *     If unsure, don't give this parameter.
+ *
+ * dslot:
+ *     NOTE: only one poll value must be given for every card.
+ *     Also this value must be given for non-E1 cards. If omitted, the E1
+ *     card has D-channel on time slot 16, which is default.
+ *     If 1..15 or 17..31, an alternate time slot is used for D-channel.
+ *     In this case, the application must be able to handle this.
+ *     If -1 is given, the D-channel is disabled and all 31 slots can be used
+ *     for B-channel. (only for specific applications)
+ *     If you don't know how to use it, you don't need it!
+ *
+ * iomode:
+ *     NOTE: only one mode value must be given for every card.
+ *     -> See hfc_multi.h for HFC_IO_MODE_* values
+ *     By default, the IO mode is pci memory IO (MEMIO).
+ *     Some cards requre specific IO mode, so it cannot be changed.
+ *     It may be usefull to set IO mode to register io (REGIO) to solve
+ *     PCI bridge problems.
+ *     If unsure, don't give this parameter.
+ *
+ * clockdelay_nt:
+ *     NOTE: only one clockdelay_nt value must be given once for all cards.
+ *     Give the value of the clock control register (A_ST_CLK_DLY)
+ *     of the S/T interfaces in NT mode.
+ *     This register is needed for the TBR3 certification, so don't change it.
+ *
+ * clockdelay_te:
+ *     NOTE: only one clockdelay_te value must be given once
+ *     Give the value of the clock control register (A_ST_CLK_DLY)
+ *     of the S/T interfaces in TE mode.
+ *     This register is needed for the TBR3 certification, so don't change it.
+ */
+
+/*
+ * debug register access (never use this, it will flood your system log)
+ * #define HFC_REGISTER_DEBUG
+ */
+
+static const char *hfcmulti_revision = "2.00";
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+
+/*
+#define IRQCOUNT_DEBUG
+#define IRQ_DEBUG
+*/
+
+#include "hfc_multi.h"
+#ifdef ECHOPREP
+#include "gaintab.h"
+#endif
+
+#define        MAX_CARDS       8
+#define        MAX_PORTS       (8 * MAX_CARDS)
+
+static LIST_HEAD(HFClist);
+static spinlock_t HFClock; /* global hfc list lock */
+
+static void ph_state_change(struct dchannel *);
+static void (*hfc_interrupt)(void);
+static void (*register_interrupt)(void);
+static int (*unregister_interrupt)(void);
+static int interrupt_registered;
+
+static struct hfc_multi *syncmaster;
+int plxsd_master; /* if we have a master card (yet) */
+static spinlock_t plx_lock; /* may not acquire other lock inside */
+EXPORT_SYMBOL(plx_lock);
+
+#define        TYP_E1          1
+#define        TYP_4S          4
+#define TYP_8S         8
+
+static int poll_timer = 6;     /* default = 128 samples = 16ms */
+/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */
+static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30  };
+#define        CLKDEL_TE       0x0f    /* CLKDEL in TE mode */
+#define        CLKDEL_NT       0x6c    /* CLKDEL in NT mode
+                                  (0x60 MUST be included!) */
+static u_char silence =        0xff;   /* silence by LAW */
+
+#define        DIP_4S  0x1             /* DIP Switches for Beronet 1S/2S/4S cards */
+#define        DIP_8S  0x2             /* DIP Switches for Beronet 8S+ cards */
+#define        DIP_E1  0x3             /* DIP Switches for Beronet E1 cards */
+
+/*
+ * module stuff
+ */
+
+static uint    type[MAX_CARDS];
+static uint    pcm[MAX_CARDS];
+static uint    dslot[MAX_CARDS];
+static uint    iomode[MAX_CARDS];
+static uint    port[MAX_PORTS];
+static uint    debug;
+static uint    poll;
+static uint    timer;
+static uint    clockdelay_te = CLKDEL_TE;
+static uint    clockdelay_nt = CLKDEL_NT;
+
+static int     HFC_cnt, Port_cnt, PCM_cnt = 99;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(timer, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR);
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+
+#ifdef HFC_REGISTER_DEBUG
+#define HFC_outb(hc, reg, val) \
+       (hc->HFC_outb(hc, reg, val, __func__, __LINE__))
+#define HFC_outb_nodebug(hc, reg, val) \
+       (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__))
+#define HFC_inb(hc, reg) \
+       (hc->HFC_inb(hc, reg, __func__, __LINE__))
+#define HFC_inb_nodebug(hc, reg) \
+       (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_inw(hc, reg) \
+       (hc->HFC_inw(hc, reg, __func__, __LINE__))
+#define HFC_inw_nodebug(hc, reg) \
+       (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_wait(hc) \
+       (hc->HFC_wait(hc, __func__, __LINE__))
+#define HFC_wait_nodebug(hc) \
+       (hc->HFC_wait_nodebug(hc, __func__, __LINE__))
+#else
+#define HFC_outb(hc, reg, val)         (hc->HFC_outb(hc, reg, val))
+#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val))
+#define HFC_inb(hc, reg)               (hc->HFC_inb(hc, reg))
+#define HFC_inb_nodebug(hc, reg)       (hc->HFC_inb_nodebug(hc, reg))
+#define HFC_inw(hc, reg)               (hc->HFC_inw(hc, reg))
+#define HFC_inw_nodebug(hc, reg)       (hc->HFC_inw_nodebug(hc, reg))
+#define HFC_wait(hc)                   (hc->HFC_wait(hc))
+#define HFC_wait_nodebug(hc)           (hc->HFC_wait_nodebug(hc))
+#endif
+
+/* HFC_IO_MODE_PCIMEM */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val,
+               const char *function, int line)
+#else
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+       writeb(val, (hc->pci_membase)+reg);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       return readb((hc->pci_membase)+reg);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       return readw((hc->pci_membase)+reg);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_pcimem(struct hfc_multi *hc)
+#endif
+{
+       while (readb((hc->pci_membase)+R_STATUS) & V_BUSY);
+}
+
+/* HFC_IO_MODE_REGIO */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val,
+       const char *function, int line)
+#else
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+       outb(reg, (hc->pci_iobase)+4);
+       outb(val, hc->pci_iobase);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       outb(reg, (hc->pci_iobase)+4);
+       return inb(hc->pci_iobase);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+       outb(reg, (hc->pci_iobase)+4);
+       return inw(hc->pci_iobase);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_regio(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_regio(struct hfc_multi *hc)
+#endif
+{
+       outb(R_STATUS, (hc->pci_iobase)+4);
+       while (inb(hc->pci_iobase) & V_BUSY);
+}
+
+#ifdef HFC_REGISTER_DEBUG
+static void
+HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val,
+               const char *function, int line)
+{
+       char regname[256] = "", bits[9] = "xxxxxxxx";
+       int i;
+
+       i = -1;
+       while (hfc_register_names[++i].name) {
+               if (hfc_register_names[i].reg == reg)
+                       strcat(regname, hfc_register_names[i].name);
+       }
+       if (regname[0] == '\0')
+               strcpy(regname, "register");
+
+       bits[7] = '0'+(!!(val&1));
+       bits[6] = '0'+(!!(val&2));
+       bits[5] = '0'+(!!(val&4));
+       bits[4] = '0'+(!!(val&8));
+       bits[3] = '0'+(!!(val&16));
+       bits[2] = '0'+(!!(val&32));
+       bits[1] = '0'+(!!(val&64));
+       bits[0] = '0'+(!!(val&128));
+       printk(KERN_DEBUG
+           "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n",
+           hc->id, reg, regname, val, bits, function, line);
+       HFC_outb_nodebug(hc, reg, val);
+}
+static u_char
+HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+       char regname[256] = "", bits[9] = "xxxxxxxx";
+       u_char val = HFC_inb_nodebug(hc, reg);
+       int i;
+
+       i = 0;
+       while (hfc_register_names[i++].name)
+               ;
+       while (hfc_register_names[++i].name) {
+               if (hfc_register_names[i].reg == reg)
+                       strcat(regname, hfc_register_names[i].name);
+       }
+       if (regname[0] == '\0')
+               strcpy(regname, "register");
+
+       bits[7] = '0'+(!!(val&1));
+       bits[6] = '0'+(!!(val&2));
+       bits[5] = '0'+(!!(val&4));
+       bits[4] = '0'+(!!(val&8));
+       bits[3] = '0'+(!!(val&16));
+       bits[2] = '0'+(!!(val&32));
+       bits[1] = '0'+(!!(val&64));
+       bits[0] = '0'+(!!(val&128));
+       printk(KERN_DEBUG
+           "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n",
+           hc->id, reg, regname, val, bits, function, line);
+       return val;
+}
+static u_short
+HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+       char regname[256] = "";
+       u_short val = HFC_inw_nodebug(hc, reg);
+       int i;
+
+       i = 0;
+       while (hfc_register_names[i++].name)
+               ;
+       while (hfc_register_names[++i].name) {
+               if (hfc_register_names[i].reg == reg)
+                       strcat(regname, hfc_register_names[i].name);
+       }
+       if (regname[0] == '\0')
+               strcpy(regname, "register");
+
+       printk(KERN_DEBUG
+           "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n",
+           hc->id, reg, regname, val, function, line);
+       return val;
+}
+static void
+HFC_wait_debug(struct hfc_multi *hc, const char *function, int line)
+{
+       printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n",
+           hc->id, function, line);
+       HFC_wait_nodebug(hc);
+}
+#endif
+
+/* write fifo data (REGIO) */
+void
+write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+       outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+       while (len>>2) {
+               outl(*(u32 *)data, hc->pci_iobase);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               outw(*(u16 *)data, hc->pci_iobase);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               outb(*data, hc->pci_iobase);
+               data++;
+               len--;
+       }
+}
+/* write fifo data (PCIMEM) */
+void
+write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+       while (len>>2) {
+               writel(*(u32 *)data, (hc->pci_membase)+A_FIFO_DATA0);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               writew(*(u16 *)data, (hc->pci_membase)+A_FIFO_DATA0);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               writeb(*data, (hc->pci_membase)+A_FIFO_DATA0);
+               data++;
+               len--;
+       }
+}
+/* read fifo data (REGIO) */
+void
+read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+       outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+       while (len>>2) {
+               *(u32 *)data = inl(hc->pci_iobase);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               *(u16 *)data = inw(hc->pci_iobase);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               *data = inb(hc->pci_iobase);
+               data++;
+               len--;
+       }
+}
+
+/* read fifo data (PCIMEM) */
+void
+read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+       while (len>>2) {
+               *(u32 *)data =
+                       readl((hc->pci_membase)+A_FIFO_DATA0);
+               data += 4;
+               len -= 4;
+       }
+       while (len>>1) {
+               *(u16 *)data =
+                       readw((hc->pci_membase)+A_FIFO_DATA0);
+               data += 2;
+               len -= 2;
+       }
+       while (len) {
+               *data = readb((hc->pci_membase)+A_FIFO_DATA0);
+               data++;
+               len--;
+       }
+}
+
+
+static void
+enable_hwirq(struct hfc_multi *hc)
+{
+       hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN;
+       HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+static void
+disable_hwirq(struct hfc_multi *hc)
+{
+       hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN);
+       HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+#define        NUM_EC 2
+#define        MAX_TDM_CHAN 32
+
+
+inline void
+enablepcibridge(struct hfc_multi *c)
+{
+       HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */
+}
+
+inline void
+disablepcibridge(struct hfc_multi *c)
+{
+       HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */
+}
+
+inline unsigned char
+readpcibridge(struct hfc_multi *hc, unsigned char address)
+{
+       unsigned short cipv;
+       unsigned char data;
+
+       if (!hc->pci_iobase)
+               return 0;
+
+       /* slow down a PCI read access by 1 PCI clock cycle */
+       HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/
+
+       if (address == 0)
+               cipv = 0x4000;
+       else
+               cipv = 0x5800;
+
+       /* select local bridge port address by writing to CIP port */
+       /* data = HFC_inb(c, cipv); * was _io before */
+       outw(cipv, hc->pci_iobase + 4);
+       data = inb(hc->pci_iobase);
+
+       /* restore R_CTRL for normal PCI read cycle speed */
+       HFC_outb(hc, R_CTRL, 0x0); /* was _io before */
+
+       return data;
+}
+
+inline void
+writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data)
+{
+       unsigned short cipv;
+       unsigned int datav;
+
+       if (!hc->pci_iobase)
+               return;
+
+       if (address == 0)
+               cipv = 0x4000;
+       else
+               cipv = 0x5800;
+
+       /* select local bridge port address by writing to CIP port */
+       outw(cipv, hc->pci_iobase + 4);
+       /* define a 32 bit dword with 4 identical bytes for write sequence */
+       datav = data | ((__u32) data << 8) | ((__u32) data << 16) |
+           ((__u32) data << 24);
+
+       /*
+        * write this 32 bit dword to the bridge data port
+        * this will initiate a write sequence of up to 4 writes to the same
+        * address on the local bus interface the number of write accesses
+        * is undefined but >=1 and depends on the next PCI transaction
+        * during write sequence on the local bus
+        */
+       outl(datav, hc->pci_iobase);
+}
+
+inline void
+cpld_set_reg(struct hfc_multi *hc, unsigned char reg)
+{
+       /* Do data pin read low byte */
+       HFC_outb(hc, R_GPIO_OUT1, reg);
+}
+
+inline void
+cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val)
+{
+       cpld_set_reg(hc, reg);
+
+       enablepcibridge(hc);
+       writepcibridge(hc, 1, val);
+       disablepcibridge(hc);
+
+       return;
+}
+
+inline unsigned char
+cpld_read_reg(struct hfc_multi *hc, unsigned char reg)
+{
+       unsigned char bytein;
+
+       cpld_set_reg(hc, reg);
+
+       /* Do data pin read low byte */
+       HFC_outb(hc, R_GPIO_OUT1, reg);
+
+       enablepcibridge(hc);
+       bytein = readpcibridge(hc, 1);
+       disablepcibridge(hc);
+
+       return bytein;
+}
+
+inline void
+vpm_write_address(struct hfc_multi *hc, unsigned short addr)
+{
+       cpld_write_reg(hc, 0, 0xff & addr);
+       cpld_write_reg(hc, 1, 0x01 & (addr >> 8));
+}
+
+inline unsigned short
+vpm_read_address(struct hfc_multi *c)
+{
+       unsigned short addr;
+       unsigned short highbit;
+
+       addr = cpld_read_reg(c, 0);
+       highbit = cpld_read_reg(c, 1);
+
+       addr = addr | (highbit << 8);
+
+       return addr & 0x1ff;
+}
+
+inline unsigned char
+vpm_in(struct hfc_multi *c, int which, unsigned short addr)
+{
+       unsigned char res;
+
+       vpm_write_address(c, addr);
+
+       if (!which)
+               cpld_set_reg(c, 2);
+       else
+               cpld_set_reg(c, 3);
+
+       enablepcibridge(c);
+       res = readpcibridge(c, 1);
+       disablepcibridge(c);
+
+       cpld_set_reg(c, 0);
+
+       return res;
+}
+
+inline void
+vpm_out(struct hfc_multi *c, int which, unsigned short addr,
+    unsigned char data)
+{
+       vpm_write_address(c, addr);
+
+       enablepcibridge(c);
+
+       if (!which)
+               cpld_set_reg(c, 2);
+       else
+               cpld_set_reg(c, 3);
+
+       writepcibridge(c, 1, data);
+
+       cpld_set_reg(c, 0);
+
+       disablepcibridge(c);
+
+       {
+       unsigned char regin;
+       regin = vpm_in(c, which, addr);
+       if (regin != data)
+               printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back "
+                       "0x%x\n", data, addr, regin);
+       }
+
+}
+
+
+void
+vpm_init(struct hfc_multi *wc)
+{
+       unsigned char reg;
+       unsigned int mask;
+       unsigned int i, x, y;
+       unsigned int ver;
+
+       for (x = 0; x < NUM_EC; x++) {
+               /* Setup GPIO's */
+               if (!x) {
+                       ver = vpm_in(wc, x, 0x1a0);
+                       printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver);
+               }
+
+               for (y = 0; y < 4; y++) {
+                       vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
+                       vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
+                       vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
+               }
+
+               /* Setup TDM path - sets fsync and tdm_clk as inputs */
+               reg = vpm_in(wc, x, 0x1a3); /* misc_con */
+               vpm_out(wc, x, 0x1a3, reg & ~2);
+
+               /* Setup Echo length (256 taps) */
+               vpm_out(wc, x, 0x022, 1);
+               vpm_out(wc, x, 0x023, 0xff);
+
+               /* Setup timeslots */
+               vpm_out(wc, x, 0x02f, 0x00);
+               mask = 0x02020202 << (x * 4);
+
+               /* Setup the tdm channel masks for all chips */
+               for (i = 0; i < 4; i++)
+                       vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff);
+
+               /* Setup convergence rate */
+               printk(KERN_DEBUG "VPM: A-law mode\n");
+               reg = 0x00 | 0x10 | 0x01;
+               vpm_out(wc, x, 0x20, reg);
+               printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg);
+               /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */
+
+               vpm_out(wc, x, 0x24, 0x02);
+               reg = vpm_in(wc, x, 0x24);
+               printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg);
+
+               /* Initialize echo cans */
+               for (i = 0; i < MAX_TDM_CHAN; i++) {
+                       if (mask & (0x00000001 << i))
+                               vpm_out(wc, x, i, 0x00);
+               }
+
+               /*
+                * ARM arch at least disallows a udelay of
+                * more than 2ms... it gives a fake "__bad_udelay"
+                * reference at link-time.
+                * long delays in kernel code are pretty sucky anyway
+                * for now work around it using 5 x 2ms instead of 1 x 10ms
+                */
+
+               udelay(2000);
+               udelay(2000);
+               udelay(2000);
+               udelay(2000);
+               udelay(2000);
+
+               /* Put in bypass mode */
+               for (i = 0; i < MAX_TDM_CHAN; i++) {
+                       if (mask & (0x00000001 << i))
+                               vpm_out(wc, x, i, 0x01);
+               }
+
+               /* Enable bypass */
+               for (i = 0; i < MAX_TDM_CHAN; i++) {
+                       if (mask & (0x00000001 << i))
+                               vpm_out(wc, x, 0x78 + i, 0x01);
+               }
+
+       }
+}
+
+void
+vpm_check(struct hfc_multi *hctmp)
+{
+       unsigned char gpi2;
+
+       gpi2 = HFC_inb(hctmp, R_GPI_IN2);
+
+       if ((gpi2 & 0x3) != 0x3)
+               printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2);
+}
+
+
+/*
+ * Interface to enable/disable the HW Echocan
+ *
+ * these functions are called within a spin_lock_irqsave on
+ * the channel instance lock, so we are not disturbed by irqs
+ *
+ * we can later easily change the interface to make  other
+ * things configurable, for now we configure the taps
+ *
+ */
+
+void
+vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
+{
+       unsigned int timeslot;
+       unsigned int unit;
+       struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+       int txadj = -4;
+       struct sk_buff *skb;
+#endif
+       if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+               return;
+
+       if (!bch)
+               return;
+
+#ifdef TXADJ
+       skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+               sizeof(int), &txadj, GFP_ATOMIC);
+       if (skb)
+               recv_Bchannel_skb(bch, skb);
+#endif
+
+       timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+       unit = ch % 4;
+
+       printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n",
+           taps, timeslot);
+
+       vpm_out(hc, unit, timeslot, 0x7e);
+}
+
+void
+vpm_echocan_off(struct hfc_multi *hc, int ch)
+{
+       unsigned int timeslot;
+       unsigned int unit;
+       struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+       int txadj = 0;
+       struct sk_buff *skb;
+#endif
+
+       if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+               return;
+
+       if (!bch)
+               return;
+
+#ifdef TXADJ
+       skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+               sizeof(int), &txadj, GFP_ATOMIC);
+       if (skb)
+               recv_Bchannel_skb(bch, skb);
+#endif
+
+       timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+       unit = ch % 4;
+
+       printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n",
+           timeslot);
+       /* FILLME */
+       vpm_out(hc, unit, timeslot, 0x01);
+}
+
+
+/*
+ * Speech Design resync feature
+ * NOTE: This is called sometimes outside interrupt handler.
+ * We must lock irqsave, so no other interrupt (other card) will occurr!
+ * Also multiple interrupts may nest, so must lock each access (lists, card)!
+ */
+static inline void
+hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
+{
+       struct hfc_multi *hc, *next, *pcmmaster = 0;
+       u_int *plx_acc_32, pv;
+       u_long flags;
+
+       spin_lock_irqsave(&HFClock, flags);
+       spin_lock(&plx_lock); /* must be locked inside other locks */
+
+       if (debug & DEBUG_HFCMULTI_PLXSD)
+               printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n",
+                       __func__, syncmaster);
+
+       /* select new master */
+       if (newmaster) {
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "using provided controller\n");
+       } else {
+               list_for_each_entry_safe(hc, next, &HFClist, list) {
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                               if (hc->syncronized) {
+                                       newmaster = hc;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* Disable sync of all cards */
+       list_for_each_entry_safe(hc, next, &HFClist, list) {
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+                       pv = readl(plx_acc_32);
+                       pv &= ~PLX_SYNC_O_EN;
+                       writel(pv, plx_acc_32);
+                       if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+                               pcmmaster = hc;
+                               if (hc->type == 1) {
+                                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                                               printk(KERN_DEBUG
+                                                       "Schedule SYNC_I\n");
+                                       hc->e1_resync |= 1; /* get SYNC_I */
+                               }
+                       }
+               }
+       }
+
+       if (newmaster) {
+               hc = newmaster;
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "id=%d (0x%p) = syncronized with "
+                               "interface.\n", hc->id, hc);
+               /* Enable new sync master */
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               pv = readl(plx_acc_32);
+               pv |= PLX_SYNC_O_EN;
+               writel(pv, plx_acc_32);
+               /* switch to jatt PLL, if not disabled by RX_SYNC */
+               if (hc->type == 1 && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "Schedule jatt PLL\n");
+                       hc->e1_resync |= 2; /* switch to jatt */
+               }
+       } else {
+               if (pcmmaster) {
+                       hc = pcmmaster;
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG
+                                       "id=%d (0x%p) = PCM master syncronized "
+                                       "with QUARTZ\n", hc->id, hc);
+                       if (hc->type == 1) {
+                               /* Use the crystal clock for the PCM
+                                  master card */
+                               if (debug & DEBUG_HFCMULTI_PLXSD)
+                                       printk(KERN_DEBUG
+                                           "Schedule QUARTZ for HFC-E1\n");
+                               hc->e1_resync |= 4; /* switch quartz */
+                       } else {
+                               if (debug & DEBUG_HFCMULTI_PLXSD)
+                                       printk(KERN_DEBUG
+                                           "QUARTZ is automatically "
+                                           "enabled by HFC-%dS\n", hc->type);
+                       }
+                       plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+                       pv = readl(plx_acc_32);
+                       pv |= PLX_SYNC_O_EN;
+                       writel(pv, plx_acc_32);
+               } else
+                       if (!rm)
+                               printk(KERN_ERR "%s no pcm master, this MUST "
+                                       "not happen!\n", __func__);
+       }
+       syncmaster = newmaster;
+
+       spin_unlock(&plx_lock);
+       spin_unlock_irqrestore(&HFClock, flags);
+}
+
+/* This must be called AND hc must be locked irqsave!!! */
+inline void
+plxsd_checksync(struct hfc_multi *hc, int rm)
+{
+       if (hc->syncronized) {
+               if (syncmaster == NULL) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_WARNING "%s: GOT sync on card %d"
+                                       " (id=%d)\n", __func__, hc->id + 1,
+                                       hc->id);
+                       hfcmulti_resync(hc, hc, rm);
+               }
+       } else {
+               if (syncmaster == hc) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_WARNING "%s: LOST sync on card %d"
+                                       " (id=%d)\n", __func__, hc->id + 1,
+                                       hc->id);
+                       hfcmulti_resync(hc, NULL, rm);
+               }
+       }
+}
+
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcmulti(struct hfc_multi *hc)
+{
+       u_int   *plx_acc_32, pv;
+       u_long  plx_flags;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+
+       /* soft reset also masks all interrupts */
+       hc->hw.r_cirm |= V_SRES;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(1000);
+       hc->hw.r_cirm &= ~V_SRES;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(1000); /* instead of 'wait' that may cause locking */
+
+       /* release Speech Design card, if PLX was initialized */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) {
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "%s: release PLXSD card %d\n",
+                           __func__, hc->id + 1);
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               writel(PLX_GPIOC_INIT, plx_acc_32);
+               pv = readl(plx_acc_32);
+               /* Termination off */
+               pv &= ~PLX_TERM_ON;
+               /* Disconnect the PCM */
+               pv |= PLX_SLAVE_EN_N;
+               pv &= ~PLX_MASTER_EN;
+               pv &= ~PLX_SYNC_O_EN;
+               /* Put the DSP in Reset */
+               pv &= ~PLX_DSP_RES_N;
+               writel(pv, plx_acc_32);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: PCM off: PLX_GPIO=%x\n",
+                               __func__, pv);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       /* disable memory mapped ports / io ports */
+       test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */
+       pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
+       if (hc->pci_membase)
+               iounmap((void *)hc->pci_membase);
+       if (hc->plx_membase)
+               iounmap((void *)hc->plx_membase);
+       if (hc->pci_iobase)
+               release_region(hc->pci_iobase, 8);
+
+       if (hc->pci_dev) {
+               pci_disable_device(hc->pci_dev);
+               pci_set_drvdata(hc->pci_dev, NULL);
+       }
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done\n", __func__);
+}
+
+/*
+ * function called to reset the HFC chip. A complete software reset of chip
+ * and fifos is done. All configuration of the chip is done.
+ */
+
+static int
+init_chip(struct hfc_multi *hc)
+{
+       u_long                  flags, val, val2 = 0, rev;
+       int                     i, err = 0;
+       u_char                  r_conf_en, rval;
+       u_int                   *plx_acc_32, pv;
+       u_long                  plx_flags, hfc_flags;
+       int                     plx_count;
+       struct hfc_multi        *pos, *next, *plx_last_hc;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       /* reset all registers */
+       memset(&hc->hw, 0, sizeof(struct hfcm_hw));
+
+       /* revision check */
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+       val = HFC_inb(hc, R_CHIP_ID)>>4;
+       if (val != 0x8 && val != 0xc && val != 0xe) {
+               printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val);
+               err = -EIO;
+               goto out;
+       }
+       rev = HFC_inb(hc, R_CHIP_RV);
+       printk(KERN_INFO
+           "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n",
+           val, rev, (rev == 0) ? " (old FIFO handling)" : "");
+       if (rev == 0) {
+               test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip);
+               printk(KERN_WARNING
+                   "HFC_multi: NOTE: Your chip is revision 0, "
+                   "ask Cologne Chip for update. Newer chips "
+                   "have a better FIFO handling. Old chips "
+                   "still work but may have slightly lower "
+                   "HDLC transmit performance.\n");
+       }
+       if (rev > 1) {
+               printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't "
+                   "consider chip revision = %ld. The chip / "
+                   "bridge may not work.\n", rev);
+       }
+
+       /* set s-ram size */
+       hc->Flen = 0x10;
+       hc->Zmin = 0x80;
+       hc->Zlen = 384;
+       hc->DTMFbase = 0x1000;
+       if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n",
+                           __func__);
+               hc->hw.r_ctrl |= V_EXT_RAM;
+               hc->hw.r_ram_sz = 1;
+               hc->Flen = 0x20;
+               hc->Zmin = 0xc0;
+               hc->Zlen = 1856;
+               hc->DTMFbase = 0x2000;
+       }
+       if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n",
+                           __func__);
+               hc->hw.r_ctrl |= V_EXT_RAM;
+               hc->hw.r_ram_sz = 2;
+               hc->Flen = 0x20;
+               hc->Zmin = 0xc0;
+               hc->Zlen = 8000;
+               hc->DTMFbase = 0x2000;
+       }
+       hc->max_trans = poll << 1;
+       if (hc->max_trans > hc->Zlen)
+               hc->max_trans = hc->Zlen;
+
+       /* Speech Design PLX bridge */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_PLXSD)
+                       printk(KERN_DEBUG "%s: initializing PLXSD card %d\n",
+                           __func__, hc->id + 1);
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               writel(PLX_GPIOC_INIT, plx_acc_32);
+               pv = readl(plx_acc_32);
+               /* The first and the last cards are terminating the PCM bus */
+               pv |= PLX_TERM_ON; /* hc is currently the last */
+               /* Disconnect the PCM */
+               pv |= PLX_SLAVE_EN_N;
+               pv &= ~PLX_MASTER_EN;
+               pv &= ~PLX_SYNC_O_EN;
+               /* Put the DSP in Reset */
+               pv &= ~PLX_DSP_RES_N;
+               writel(pv, plx_acc_32);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: slave/term: PLX_GPIO=%x\n",
+                               __func__, pv);
+               /*
+                * If we are the 3rd PLXSD card or higher, we must turn
+                * termination of last PLXSD card off.
+                */
+               spin_lock_irqsave(&HFClock, hfc_flags);
+               plx_count = 0;
+               plx_last_hc = NULL;
+               list_for_each_entry_safe(pos, next, &HFClist, list) {
+                       if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) {
+                               plx_count++;
+                               if (pos != hc)
+                                       plx_last_hc = pos;
+                       }
+               }
+               if (plx_count >= 3) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "%s: card %d is between, so "
+                                       "we disable termination\n",
+                                   __func__, plx_last_hc->id + 1);
+                       spin_lock_irqsave(&plx_lock, plx_flags);
+                       plx_acc_32 = (u_int *)(plx_last_hc->plx_membase
+                                       + PLX_GPIOC);
+                       pv = readl(plx_acc_32);
+                       pv &= ~PLX_TERM_ON;
+                       writel(pv, plx_acc_32);
+                       spin_unlock_irqrestore(&plx_lock, plx_flags);
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                           printk(KERN_WARNING "%s: term off: PLX_GPIO=%x\n",
+                                       __func__, pv);
+               }
+               spin_unlock_irqrestore(&HFClock, hfc_flags);
+               hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */
+       }
+
+       /* we only want the real Z2 read-pointer for revision > 0 */
+       if (!test_bit(HFC_CHIP_REVISION0, &hc->chip))
+               hc->hw.r_ram_sz |= V_FZ_MD;
+
+       /* select pcm mode */
+       if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: setting PCM into slave mode\n",
+                           __func__);
+       } else
+       if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: setting PCM into master mode\n",
+                           __func__);
+               hc->hw.r_pcm_md0 |= V_PCM_MD;
+       } else {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: performing PCM auto detect\n",
+                           __func__);
+       }
+
+       /* soft reset */
+       HFC_outb(hc, R_CTRL, hc->hw.r_ctrl);
+       HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
+       HFC_outb(hc, R_FIFO_MD, 0);
+       hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(100);
+       hc->hw.r_cirm = 0;
+       HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+       udelay(100);
+       HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
+
+       /* Speech Design PLX bridge pcm and sync mode */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               pv = readl(plx_acc_32);
+               /* Connect PCM */
+               if (hc->hw.r_pcm_md0 & V_PCM_MD) {
+                       pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+                       pv |= PLX_SYNC_O_EN;
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_WARNING "%s: master: PLX_GPIO=%x\n",
+                                       __func__, pv);
+               } else {
+                       pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N);
+                       pv &= ~PLX_SYNC_O_EN;
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_WARNING "%s: slave: PLX_GPIO=%x\n",
+                                       __func__, pv);
+               }
+               writel(pv, plx_acc_32);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       /* PCM setup */
+       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90);
+       if (hc->slots == 32)
+               HFC_outb(hc, R_PCM_MD1, 0x00);
+       if (hc->slots == 64)
+               HFC_outb(hc, R_PCM_MD1, 0x10);
+       if (hc->slots == 128)
+               HFC_outb(hc, R_PCM_MD1, 0x20);
+       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0);
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+               HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */
+       else
+               HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */
+       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+       for (i = 0; i < 256; i++) {
+               HFC_outb_nodebug(hc, R_SLOT, i);
+               HFC_outb_nodebug(hc, A_SL_CFG, 0);
+               HFC_outb_nodebug(hc, A_CONF, 0);
+               hc->slot_owner[i] = -1;
+       }
+
+       /* set clock speed */
+       if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: setting double clock\n", __func__);
+               HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+       }
+
+       /* B410P GPIO */
+       if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+               printk(KERN_NOTICE "Setting GPIOs\n");
+               HFC_outb(hc, R_GPIO_SEL, 0x30);
+               HFC_outb(hc, R_GPIO_EN1, 0x3);
+               udelay(1000);
+               printk(KERN_NOTICE "calling vpm_init\n");
+               vpm_init(hc);
+       }
+
+       /* check if R_F0_CNT counts (8 kHz frame count) */
+       val = HFC_inb(hc, R_F0_CNTL);
+       val += HFC_inb(hc, R_F0_CNTH) << 8;
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG
+                   "HFC_multi F0_CNT %ld after reset\n", val);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout((HZ/100)?:1); /* Timeout minimum 10ms */
+       spin_lock_irqsave(&hc->lock, flags);
+       val2 = HFC_inb(hc, R_F0_CNTL);
+       val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG
+                       "HFC_multi F0_CNT %ld after 10 ms (1st try)\n",
+                   val2);
+       if (val2 >= val+8) { /* 1 ms */
+               /* it counts, so we keep the pcm mode */
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+                       printk(KERN_INFO "controller is PCM bus MASTER\n");
+               else
+               if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip))
+                       printk(KERN_INFO "controller is PCM bus SLAVE\n");
+               else {
+                       test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+                       printk(KERN_INFO "controller is PCM bus SLAVE "
+                               "(auto detected)\n");
+               }
+       } else {
+               /* does not count */
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+controller_fail:
+                       printk(KERN_ERR "HFC_multi ERROR, getting no 125us "
+                           "pulse. Seems that controller fails.\n");
+                       err = -EIO;
+                       goto out;
+               }
+               if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+                       printk(KERN_INFO "controller is PCM bus SLAVE "
+                               "(ignoring missing PCM clock)\n");
+               } else {
+                       /* only one pcm master */
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+                               && plxsd_master) {
+                               printk(KERN_ERR "HFC_multi ERROR, no clock "
+                                   "on another Speech Design card found. "
+                                   "Please be sure to connect PCM cable.\n");
+                               err = -EIO;
+                               goto out;
+                       }
+                       /* retry with master clock */
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                               spin_lock_irqsave(&plx_lock, plx_flags);
+                               plx_acc_32 = (u_int *)(hc->plx_membase +
+                                       PLX_GPIOC);
+                               pv = readl(plx_acc_32);
+                               pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+                               pv |= PLX_SYNC_O_EN;
+                               writel(pv, plx_acc_32);
+                               spin_unlock_irqrestore(&plx_lock, plx_flags);
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                   printk(KERN_WARNING "%s: master: PLX_GPIO"
+                                       "=%x\n", __func__, pv);
+                       }
+                       hc->hw.r_pcm_md0 |= V_PCM_MD;
+                       HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout((HZ/100)?:1); /* Timeout min. 10ms */
+                       spin_lock_irqsave(&hc->lock, flags);
+                       val2 = HFC_inb(hc, R_F0_CNTL);
+                       val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG "HFC_multi F0_CNT %ld after "
+                                       "10 ms (2nd try)\n", val2);
+                       if (val2 >= val+8) { /* 1 ms */
+                               test_and_set_bit(HFC_CHIP_PCM_MASTER,
+                                       &hc->chip);
+                               printk(KERN_INFO "controller is PCM bus MASTER "
+                                       "(auto detected)\n");
+                       } else
+                               goto controller_fail;
+               }
+       }
+
+       /* Release the DSP Reset */
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+                       plxsd_master = 1;
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+               pv = readl(plx_acc_32);
+               pv |=  PLX_DSP_RES_N;
+               writel(pv, plx_acc_32);
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: reset off: PLX_GPIO=%x\n",
+                               __func__, pv);
+       }
+
+       /* pcm id */
+       if (hc->pcm)
+               printk(KERN_INFO "controller has given PCM BUS ID %d\n",
+                       hc->pcm);
+       else {
+               if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)
+                || test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       PCM_cnt++; /* SD has proprietary bridging */
+               }
+               hc->pcm = PCM_cnt;
+               printk(KERN_INFO "controller has PCM BUS ID %d "
+                       "(auto selected)\n", hc->pcm);
+       }
+
+       /* set up timer */
+       HFC_outb(hc, R_TI_WD, poll_timer);
+       hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
+
+       /*
+        * set up 125us interrupt, only if function pointer is available
+        * and module parameter timer is set
+        */
+       if (timer && hfc_interrupt && register_interrupt) {
+               /* only one chip should use this interrupt */
+               timer = 0;
+               interrupt_registered = 1;
+               hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK;
+               /* deactivate other interrupts in ztdummy */
+               register_interrupt();
+       }
+
+       /* set E1 state machine IRQ */
+       if (hc->type == 1)
+               hc->hw.r_irqmsk_misc |= V_STA_IRQMSK;
+
+       /* set DTMF detection */
+       if (test_bit(HFC_CHIP_DTMF, &hc->chip)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: enabling DTMF detection "
+                           "for all B-channel\n", __func__);
+               hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP;
+               if (test_bit(HFC_CHIP_ULAW, &hc->chip))
+                       hc->hw.r_dtmf |= V_ULAW_SEL;
+               HFC_outb(hc, R_DTMF_N, 102 - 1);
+               hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK;
+       }
+
+       /* conference engine */
+       if (test_bit(HFC_CHIP_ULAW, &hc->chip))
+               r_conf_en = V_CONF_EN | V_ULAW;
+       else
+               r_conf_en = V_CONF_EN;
+       HFC_outb(hc, R_CONF_EN, r_conf_en);
+
+       /* setting leds */
+       switch (hc->leds) {
+       case 1: /* HFC-E1 OEM */
+               if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+                       HFC_outb(hc, R_GPIO_SEL, 0x32);
+               else
+                       HFC_outb(hc, R_GPIO_SEL, 0x30);
+
+               HFC_outb(hc, R_GPIO_EN1, 0x0f);
+               HFC_outb(hc, R_GPIO_OUT1, 0x00);
+
+               HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+               break;
+
+       case 2: /* HFC-4S OEM */
+       case 3:
+               HFC_outb(hc, R_GPIO_SEL, 0xf0);
+               HFC_outb(hc, R_GPIO_EN1, 0xff);
+               HFC_outb(hc, R_GPIO_OUT1, 0x00);
+               break;
+       }
+
+       /* set master clock */
+       if (hc->masterclk >= 0) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: setting ST master clock "
+                           "to port %d (0..%d)\n",
+                           __func__, hc->masterclk, hc->ports-1);
+               hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC;
+               HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync);
+       }
+
+       /* setting misc irq */
+       HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc);
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n",
+                   hc->hw.r_irqmsk_misc);
+
+       /* RAM access test */
+       HFC_outb(hc, R_RAM_ADDR0, 0);
+       HFC_outb(hc, R_RAM_ADDR1, 0);
+       HFC_outb(hc, R_RAM_ADDR2, 0);
+       for (i = 0; i < 256; i++) {
+               HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+               HFC_outb_nodebug(hc, R_RAM_DATA, ((i*3)&0xff));
+       }
+       for (i = 0; i < 256; i++) {
+               HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+               HFC_inb_nodebug(hc, R_RAM_DATA);
+               rval = HFC_inb_nodebug(hc, R_INT_DATA);
+               if (rval != ((i * 3) & 0xff)) {
+                       printk(KERN_DEBUG
+                           "addr:%x val:%x should:%x\n", i, rval,
+                           (i * 3) & 0xff);
+                       err++;
+               }
+       }
+       if (err) {
+               printk(KERN_DEBUG "aborting - %d RAM access errors\n", err);
+               err = -EIO;
+               goto out;
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done\n", __func__);
+out:
+       spin_unlock_irqrestore(&hc->lock, flags);
+       return err;
+}
+
+
+/*
+ * control the watchdog
+ */
+static void
+hfcmulti_watchdog(struct hfc_multi *hc)
+{
+       hc->wdcount++;
+
+       if (hc->wdcount > 10) {
+               hc->wdcount = 0;
+               hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ?
+                   V_GPIO_OUT3 : V_GPIO_OUT2;
+
+       /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */
+               HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+               HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte);
+       }
+}
+
+
+
+/*
+ * output leds
+ */
+static void
+hfcmulti_leds(struct hfc_multi *hc)
+{
+       unsigned long lled;
+       unsigned long leddw;
+       int i, state, active, leds;
+       struct dchannel *dch;
+       int led[4];
+
+       hc->ledcount += poll;
+       if (hc->ledcount > 4096) {
+               hc->ledcount -= 4096;
+               hc->ledstate = 0xAFFEAFFE;
+       }
+
+       switch (hc->leds) {
+       case 1: /* HFC-E1 OEM */
+               /* 2 red blinking: NT mode deactivate
+                * 2 red steady:   TE mode deactivate
+                * left green:     L1 active
+                * left red:       frame sync, but no L1
+                * right green:    L2 active
+                */
+               if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */
+                       if (hc->chan[hc->dslot].dch->dev.D.protocol
+                               != ISDN_P_NT_E1) {
+                               led[0] = 1;
+                               led[1] = 1;
+                       } else if (hc->ledcount>>11) {
+                               led[0] = 1;
+                               led[1] = 1;
+                       } else {
+                               led[0] = 0;
+                               led[1] = 0;
+                       }
+                       led[2] = 0;
+                       led[3] = 0;
+               } else { /* with frame sync */
+                       /* TODO make it work */
+                       led[0] = 0;
+                       led[1] = 0;
+                       led[2] = 0;
+                       led[3] = 1;
+               }
+               leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF;
+                       /* leds are inverted */
+               if (leds != (int)hc->ledstate) {
+                       HFC_outb_nodebug(hc, R_GPIO_OUT1, leds);
+                       hc->ledstate = leds;
+               }
+               break;
+
+       case 2: /* HFC-4S OEM */
+               /* red blinking = PH_DEACTIVATE NT Mode
+                * red steady   = PH_DEACTIVATE TE Mode
+                * green steady = PH_ACTIVATE
+                */
+               for (i = 0; i < 4; i++) {
+                       state = 0;
+                       active = -1;
+                       dch = hc->chan[(i << 2) | 2].dch;
+                       if (dch) {
+                               state = dch->state;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                       }
+                       if (state) {
+                               if (state == active) {
+                                       led[i] = 1; /* led green */
+                               } else
+                                       if (dch->dev.D.protocol == ISDN_P_TE_S0)
+                                               /* TE mode: led red */
+                                               led[i] = 2;
+                                       else
+                                               if (hc->ledcount>>11)
+                                                       /* led red */
+                                                       led[i] = 2;
+                                               else
+                                                       /* led off */
+                                                       led[i] = 0;
+                       } else
+                               led[i] = 0; /* led off */
+               }
+               if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+                       leds = 0;
+                       for (i = 0; i < 4; i++) {
+                               if (led[i] == 1) {
+                                       /*green*/
+                                       leds |= (0x2 << (i * 2));
+                               } else if (led[i] == 2) {
+                                       /*red*/
+                                       leds |= (0x1 << (i * 2));
+                               }
+                       }
+                       if (leds != (int)hc->ledstate) {
+                               vpm_out(hc, 0, 0x1a8 + 3, leds);
+                               hc->ledstate = leds;
+                       }
+               } else {
+                       leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) |
+                           ((led[0] > 0) << 2) | ((led[2] > 0) << 3) |
+                           ((led[3] & 1) << 4) | ((led[1] & 1) << 5) |
+                           ((led[0] & 1) << 6) | ((led[2] & 1) << 7);
+                       if (leds != (int)hc->ledstate) {
+                               HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F);
+                               HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4);
+                               hc->ledstate = leds;
+                       }
+               }
+               break;
+
+       case 3: /* HFC 1S/2S Beronet */
+               /* red blinking = PH_DEACTIVATE NT Mode
+                * red steady   = PH_DEACTIVATE TE Mode
+                * green steady = PH_ACTIVATE
+                */
+               for (i = 0; i < 2; i++) {
+                       state = 0;
+                       active = -1;
+                       dch = hc->chan[(i << 2) | 2].dch;
+                       if (dch) {
+                               state = dch->state;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                       }
+                       if (state) {
+                               if (state == active) {
+                                       led[i] = 1; /* led green */
+                               } else
+                                       if (dch->dev.D.protocol == ISDN_P_TE_S0)
+                                               /* TE mode: led red */
+                                               led[i] = 2;
+                                       else
+                                               if (hc->ledcount >> 11)
+                                                       /* led red */
+                                                       led[i] = 2;
+                                               else
+                                                       /* led off */
+                                                       led[i] = 0;
+                       } else
+                               led[i] = 0; /* led off */
+               }
+
+
+               leds = (led[0] > 0) | ((led[1] > 0)<<1) | ((led[0]&1)<<2)
+                       | ((led[1]&1)<<3);
+               if (leds != (int)hc->ledstate) {
+                       HFC_outb_nodebug(hc, R_GPIO_EN1,
+                           ((led[0] > 0) << 2) | ((led[1] > 0) << 3));
+                       HFC_outb_nodebug(hc, R_GPIO_OUT1,
+                           ((led[0] & 1) << 2) | ((led[1] & 1) << 3));
+                       hc->ledstate = leds;
+               }
+               break;
+       case 8: /* HFC 8S+ Beronet */
+               lled = 0;
+
+               for (i = 0; i < 8; i++) {
+                       state = 0;
+                       active = -1;
+                       dch = hc->chan[(i << 2) | 2].dch;
+                       if (dch) {
+                               state = dch->state;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                       }
+                       if (state) {
+                               if (state == active) {
+                                       lled |= 0 << i;
+                               } else
+                                       if (hc->ledcount >> 11)
+                                               lled |= 0 << i;
+                                       else
+                                               lled |= 1 << i;
+                       } else
+                               lled |= 1 << i;
+               }
+               leddw = lled << 24 | lled << 16 | lled << 8 | lled;
+               if (leddw != hc->ledstate) {
+                       /* HFC_outb(hc, R_BRG_PCM_CFG, 1);
+                       HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */
+                       /* was _io before */
+                       HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+                       outw(0x4000, hc->pci_iobase + 4);
+                       outl(leddw, hc->pci_iobase);
+                       HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+                       hc->ledstate = leddw;
+               }
+               break;
+       }
+}
+/*
+ * read dtmf coefficients
+ */
+
+static void
+hfcmulti_dtmf(struct hfc_multi *hc)
+{
+       s32             *coeff;
+       u_int           mantissa;
+       int             co, ch;
+       struct bchannel *bch = NULL;
+       u8              exponent;
+       int             dtmf = 0;
+       int             addr;
+       u16             w_float;
+       struct sk_buff  *skb;
+       struct mISDNhead *hh;
+
+       if (debug & DEBUG_HFCMULTI_DTMF)
+               printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__);
+       for (ch = 0; ch <= 31; ch++) {
+               /* only process enabled B-channels */
+               bch = hc->chan[ch].bch;
+               if (!bch)
+                       continue;
+               if (!hc->created[hc->chan[ch].port])
+                       continue;
+               if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                       continue;
+               if (debug & DEBUG_HFCMULTI_DTMF)
+                       printk(KERN_DEBUG "%s: dtmf channel %d:",
+                               __func__, ch);
+               coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]);
+               dtmf = 1;
+               for (co = 0; co < 8; co++) {
+                       /* read W(n-1) coefficient */
+                       addr = hc->DTMFbase + ((co<<7) | (ch<<2));
+                       HFC_outb_nodebug(hc, R_RAM_ADDR0, addr);
+                       HFC_outb_nodebug(hc, R_RAM_ADDR1, addr>>8);
+                       HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr>>16)
+                               | V_ADDR_INC);
+                       w_float = HFC_inb_nodebug(hc, R_RAM_DATA);
+                       w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+                       if (debug & DEBUG_HFCMULTI_DTMF)
+                               printk(" %04x", w_float);
+
+                       /* decode float (see chip doc) */
+                       mantissa = w_float & 0x0fff;
+                       if (w_float & 0x8000)
+                               mantissa |= 0xfffff000;
+                       exponent = (w_float>>12) & 0x7;
+                       if (exponent) {
+                               mantissa ^= 0x1000;
+                               mantissa <<= (exponent-1);
+                       }
+
+                       /* store coefficient */
+                       coeff[co<<1] = mantissa;
+
+                       /* read W(n) coefficient */
+                       w_float = HFC_inb_nodebug(hc, R_RAM_DATA);
+                       w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+                       if (debug & DEBUG_HFCMULTI_DTMF)
+                               printk(" %04x", w_float);
+
+                       /* decode float (see chip doc) */
+                       mantissa = w_float & 0x0fff;
+                       if (w_float & 0x8000)
+                               mantissa |= 0xfffff000;
+                       exponent = (w_float>>12) & 0x7;
+                       if (exponent) {
+                               mantissa ^= 0x1000;
+                               mantissa <<= (exponent-1);
+                       }
+
+                       /* store coefficient */
+                       coeff[(co<<1)|1] = mantissa;
+               }
+               if (debug & DEBUG_HFCMULTI_DTMF)
+                       printk("%s: DTMF ready %08x %08x %08x %08x "
+                           "%08x %08x %08x %08x\n", __func__,
+                           coeff[0], coeff[1], coeff[2], coeff[3],
+                           coeff[4], coeff[5], coeff[6], coeff[7]);
+               hc->chan[ch].coeff_count++;
+               if (hc->chan[ch].coeff_count == 8) {
+                       hc->chan[ch].coeff_count = 0;
+                       skb = mI_alloc_skb(512, GFP_ATOMIC);
+                       if (!skb) {
+                               printk(KERN_WARNING "%s: No memory for skb\n",
+                                   __func__);
+                               continue;
+                       }
+                       hh = mISDN_HEAD_P(skb);
+                       hh->prim = PH_CONTROL_IND;
+                       hh->id = DTMF_HFC_COEF;
+                       memcpy(skb_put(skb, 512), hc->chan[ch].coeff, 512);
+                       recv_Bchannel_skb(bch, skb);
+               }
+       }
+
+       /* restart DTMF processing */
+       hc->dtmf = dtmf;
+       if (dtmf)
+               HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
+}
+
+
+/*
+ * fill fifo as much as possible
+ */
+
+static void
+hfcmulti_tx(struct hfc_multi *hc, int ch)
+{
+       int i, ii, temp, len = 0;
+       int Zspace, z1, z2; /* must be int for calculation */
+       int Fspace, f1, f2;
+       u_char *d;
+       int *txpending, slot_tx;
+       struct  bchannel *bch;
+       struct  dchannel *dch;
+       struct  sk_buff **sp = NULL;
+       int *idxp;
+
+       bch = hc->chan[ch].bch;
+       dch = hc->chan[ch].dch;
+       if ((!dch) && (!bch))
+               return;
+
+       txpending = &hc->chan[ch].txpending;
+       slot_tx = hc->chan[ch].slot_tx;
+       if (dch) {
+               if (!test_bit(FLG_ACTIVE, &dch->Flags))
+                       return;
+               sp = &dch->tx_skb;
+               idxp = &dch->tx_idx;
+       } else {
+               if (!test_bit(FLG_ACTIVE, &bch->Flags))
+                       return;
+               sp = &bch->tx_skb;
+               idxp = &bch->tx_idx;
+       }
+       if (*sp)
+               len = (*sp)->len;
+
+       if ((!len) && *txpending != 1)
+               return; /* no data */
+
+       if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+           (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+           (hc->chan[ch].slot_rx < 0) &&
+           (hc->chan[ch].slot_tx < 0))
+               HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1));
+       else
+               HFC_outb_nodebug(hc, R_FIFO, ch << 1);
+       HFC_wait_nodebug(hc);
+
+       if (*txpending == 2) {
+               /* reset fifo */
+               HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait_nodebug(hc);
+               HFC_outb(hc, A_SUBCH_CFG, 0);
+               *txpending = 1;
+       }
+next_frame:
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               f1 = HFC_inb_nodebug(hc, A_F1);
+               f2 = HFC_inb_nodebug(hc, A_F2);
+               while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) {
+                       if (debug & DEBUG_HFCMULTI_FIFO)
+                               printk(KERN_DEBUG
+                                   "%s(card %d): reread f2 because %d!=%d\n",
+                                   __func__, hc->id + 1, temp, f2);
+                       f2 = temp; /* repeat until F2 is equal */
+               }
+               Fspace = f2 - f1 - 1;
+               if (Fspace < 0)
+                       Fspace += hc->Flen;
+               /*
+                * Old FIFO handling doesn't give us the current Z2 read
+                * pointer, so we cannot send the next frame before the fifo
+                * is empty. It makes no difference except for a slightly
+                * lower performance.
+                */
+               if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) {
+                       if (f1 != f2)
+                               Fspace = 0;
+                       else
+                               Fspace = 1;
+               }
+               /* one frame only for ST D-channels, to allow resending */
+               if (hc->type != 1 && dch) {
+                       if (f1 != f2)
+                               Fspace = 0;
+               }
+               /* F-counter full condition */
+               if (Fspace == 0)
+                       return;
+       }
+       z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin;
+       z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+       while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) {
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG "%s(card %d): reread z2 because "
+                               "%d!=%d\n", __func__, hc->id + 1, temp, z2);
+               z2 = temp; /* repeat unti Z2 is equal */
+       }
+       Zspace = z2 - z1;
+       if (Zspace <= 0)
+               Zspace += hc->Zlen;
+       Zspace -= 4; /* keep not too full, so pointers will not overrun */
+       /* fill transparent data only to maxinum transparent load (minus 4) */
+       if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+               Zspace = Zspace - hc->Zlen + hc->max_trans;
+       if (Zspace <= 0) /* no space of 4 bytes */
+               return;
+
+       /* if no data */
+       if (!len) {
+               if (z1 == z2) { /* empty */
+                       /* if done with FIFO audio data during PCM connection */
+                       if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) &&
+                           *txpending && slot_tx >= 0) {
+                               if (debug & DEBUG_HFCMULTI_MODE)
+                                       printk(KERN_DEBUG
+                                           "%s: reconnecting PCM due to no "
+                                           "more FIFO data: channel %d "
+                                           "slot_tx %d\n",
+                                           __func__, ch, slot_tx);
+                               /* connect slot */
+                               HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+                                   V_HDLC_TRP | V_IFF);
+                               HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+                               HFC_wait_nodebug(hc);
+                               HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+                                   V_HDLC_TRP | V_IFF);
+                               HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+                               HFC_wait_nodebug(hc);
+                       }
+                       *txpending = 0;
+               }
+               return; /* no data */
+       }
+
+       /* if audio data and connected slot */
+       if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending)
+               && slot_tx >= 0) {
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: disconnecting PCM due to "
+                           "FIFO data: channel %d slot_tx %d\n",
+                           __func__, ch, slot_tx);
+               /* disconnect slot */
+               HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+               HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+               HFC_wait_nodebug(hc);
+               HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+               HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+               HFC_wait_nodebug(hc);
+       }
+       *txpending = 1;
+
+       /* show activity */
+       hc->activity[hc->chan[ch].port] = 1;
+
+       /* fill fifo to what we have left */
+       ii = len;
+       if (dch || test_bit(FLG_HDLC, &bch->Flags))
+               temp = 1;
+       else
+               temp = 0;
+       i = *idxp;
+       d = (*sp)->data + i;
+       if (ii - i > Zspace)
+               ii = Zspace + i;
+       if (debug & DEBUG_HFCMULTI_FIFO)
+               printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space "
+                   "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n",
+                       __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i,
+                       temp ? "HDLC":"TRANS");
+
+
+       /* Have to prep the audio data */
+       hc->write_fifo(hc, d, ii - i);
+       *idxp = ii;
+
+       /* if not all data has been written */
+       if (ii != len) {
+               /* NOTE: fifo is started by the calling function */
+               return;
+       }
+
+       /* if all data has been written, terminate frame */
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               /* increment f-counter */
+               HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+               HFC_wait_nodebug(hc);
+       }
+
+       /* send confirm, since get_net_bframe will not do it with trans */
+       if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+               confirm_Bsend(bch);
+
+       /* check for next frame */
+       dev_kfree_skb(*sp);
+       if (bch && get_next_bframe(bch)) { /* hdlc is confirmed here */
+               len = (*sp)->len;
+               goto next_frame;
+       }
+       if (dch && get_next_dframe(dch)) {
+               len = (*sp)->len;
+               goto next_frame;
+       }
+
+       /*
+        * now we have no more data, so in case of transparent,
+        * we set the last byte in fifo to 'silence' in case we will get
+        * no more data at all. this prevents sending an undefined value.
+        */
+       if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+               HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+}
+
+
+/* NOTE: only called if E1 card is in active state */
+static void
+hfcmulti_rx(struct hfc_multi *hc, int ch)
+{
+       int temp;
+       int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */
+       int f1 = 0, f2 = 0; /* = 0, to make GCC happy */
+       int again = 0;
+       struct  bchannel *bch;
+       struct  dchannel *dch;
+       struct sk_buff  *skb, **sp = NULL;
+       int     maxlen;
+
+       bch = hc->chan[ch].bch;
+       dch = hc->chan[ch].dch;
+       if ((!dch) && (!bch))
+               return;
+       if (dch) {
+               if (!test_bit(FLG_ACTIVE, &dch->Flags))
+                       return;
+               sp = &dch->rx_skb;
+               maxlen = dch->maxlen;
+       } else {
+               if (!test_bit(FLG_ACTIVE, &bch->Flags))
+                       return;
+               sp = &bch->rx_skb;
+               maxlen = bch->maxlen;
+       }
+next_frame:
+       /* on first AND before getting next valid frame, R_FIFO must be written
+          to. */
+       if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+           (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+           (hc->chan[ch].slot_rx < 0) &&
+           (hc->chan[ch].slot_tx < 0))
+               HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch<<1) | 1);
+       else
+               HFC_outb_nodebug(hc, R_FIFO, (ch<<1)|1);
+       HFC_wait_nodebug(hc);
+
+       /* ignore if rx is off BUT change fifo (above) to start pending TX */
+       if (hc->chan[ch].rx_off)
+               return;
+
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               f1 = HFC_inb_nodebug(hc, A_F1);
+               while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) {
+                       if (debug & DEBUG_HFCMULTI_FIFO)
+                               printk(KERN_DEBUG
+                                   "%s(card %d): reread f1 because %d!=%d\n",
+                                   __func__, hc->id + 1, temp, f1);
+                       f1 = temp; /* repeat until F1 is equal */
+               }
+               f2 = HFC_inb_nodebug(hc, A_F2);
+       }
+       z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin;
+       while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) {
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG "%s(card %d): reread z2 because "
+                               "%d!=%d\n", __func__, hc->id + 1, temp, z2);
+               z1 = temp; /* repeat until Z1 is equal */
+       }
+       z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+       Zsize = z1 - z2;
+       if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2)
+               /* complete hdlc frame */
+               Zsize++;
+       if (Zsize < 0)
+               Zsize += hc->Zlen;
+       /* if buffer is empty */
+       if (Zsize <= 0)
+               return;
+
+       if (*sp == NULL) {
+               *sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC);
+               if (*sp == NULL) {
+                       printk(KERN_DEBUG "%s: No mem for rx_skb\n",
+                           __func__);
+                       return;
+               }
+       }
+       /* show activity */
+       hc->activity[hc->chan[ch].port] = 1;
+
+       /* empty fifo with what we have */
+       if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d "
+                           "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) "
+                           "got=%d (again %d)\n", __func__, hc->id + 1, ch,
+                           Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE",
+                           f1, f2, Zsize + (*sp)->len, again);
+               /* HDLC */
+               if ((Zsize + (*sp)->len) > (maxlen + 3)) {
+                       if (debug & DEBUG_HFCMULTI_FIFO)
+                               printk(KERN_DEBUG
+                                   "%s(card %d): hdlc-frame too large.\n",
+                                   __func__, hc->id + 1);
+                       skb_trim(*sp, 0);
+                       HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait_nodebug(hc);
+                       return;
+               }
+
+               hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+
+               if (f1 != f2) {
+                       /* increment Z2,F2-counter */
+                       HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+                       HFC_wait_nodebug(hc);
+                       /* check size */
+                       if ((*sp)->len < 4) {
+                               if (debug & DEBUG_HFCMULTI_FIFO)
+                                       printk(KERN_DEBUG
+                                           "%s(card %d): Frame below minimum "
+                                           "size\n", __func__, hc->id + 1);
+                               skb_trim(*sp, 0);
+                               goto next_frame;
+                       }
+                       /* there is at least one complete frame, check crc */
+                       if ((*sp)->data[(*sp)->len - 1]) {
+                               if (debug & DEBUG_HFCMULTI_CRC)
+                                       printk(KERN_DEBUG
+                                           "%s: CRC-error\n", __func__);
+                               skb_trim(*sp, 0);
+                               goto next_frame;
+                       }
+                       skb_trim(*sp, (*sp)->len - 3);
+                       if ((*sp)->len < MISDN_COPY_SIZE) {
+                               skb = *sp;
+                               *sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+                               if (*sp) {
+                                       memcpy(skb_put(*sp, skb->len),
+                                           skb->data, skb->len);
+                                       skb_trim(skb, 0);
+                               } else {
+                                       printk(KERN_DEBUG "%s: No mem\n",
+                                           __func__);
+                                       *sp = skb;
+                                       skb = NULL;
+                               }
+                       } else {
+                               skb = NULL;
+                       }
+                       if (debug & DEBUG_HFCMULTI_FIFO) {
+                               printk(KERN_DEBUG "%s(card %d):",
+                                       __func__, hc->id + 1);
+                               temp = 0;
+                               while (temp < (*sp)->len)
+                                       printk(" %02x", (*sp)->data[temp++]);
+                               printk("\n");
+                       }
+                       if (dch)
+                               recv_Dchannel(dch);
+                       else
+                               recv_Bchannel(bch);
+                       *sp = skb;
+                       again++;
+                       goto next_frame;
+               }
+               /* there is an incomplete frame */
+       } else {
+               /* transparent */
+               if (Zsize > skb_tailroom(*sp))
+                       Zsize = skb_tailroom(*sp);
+               hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+               if (((*sp)->len) < MISDN_COPY_SIZE) {
+                       skb = *sp;
+                       *sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+                       if (*sp) {
+                               memcpy(skb_put(*sp, skb->len),
+                                   skb->data, skb->len);
+                               skb_trim(skb, 0);
+                       } else {
+                               printk(KERN_DEBUG "%s: No mem\n", __func__);
+                               *sp = skb;
+                               skb = NULL;
+                       }
+               } else {
+                       skb = NULL;
+               }
+               if (debug & DEBUG_HFCMULTI_FIFO)
+                       printk(KERN_DEBUG
+                           "%s(card %d): fifo(%d) reading %d bytes "
+                           "(z1=%04x, z2=%04x) TRANS\n",
+                               __func__, hc->id + 1, ch, Zsize, z1, z2);
+               /* only bch is transparent */
+               recv_Bchannel(bch);
+               *sp = skb;
+       }
+}
+
+
+/*
+ * Interrupt handler
+ */
+static void
+signal_state_up(struct dchannel *dch, int info, char *msg)
+{
+       struct sk_buff  *skb;
+       int             id, data = info;
+
+       if (debug & DEBUG_HFCMULTI_STATE)
+               printk(KERN_DEBUG "%s: %s\n", __func__, msg);
+
+       id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */
+
+       skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data,
+               GFP_ATOMIC);
+       if (!skb)
+               return;
+       recv_Dchannel_skb(dch, skb);
+}
+
+static inline void
+handle_timer_irq(struct hfc_multi *hc)
+{
+       int             ch, temp;
+       struct dchannel *dch;
+       u_long          flags;
+
+       /* process queued resync jobs */
+       if (hc->e1_resync) {
+               /* lock, so e1_resync gets not changed */
+               spin_lock_irqsave(&HFClock, flags);
+               if (hc->e1_resync & 1) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "Enable SYNC_I\n");
+                       HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC);
+                       /* disable JATT, if RX_SYNC is set */
+                       if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+                               HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+               }
+               if (hc->e1_resync & 2) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG "Enable jatt PLL\n");
+                       HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+               }
+               if (hc->e1_resync & 4) {
+                       if (debug & DEBUG_HFCMULTI_PLXSD)
+                               printk(KERN_DEBUG
+                                   "Enable QUARTZ for HFC-E1\n");
+                       /* set jatt to quartz */
+                       HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC
+                               | V_JATT_OFF);
+                       /* switch to JATT, in case it is not already */
+                       HFC_outb(hc, R_SYNC_OUT, 0);
+               }
+               hc->e1_resync = 0;
+               spin_unlock_irqrestore(&HFClock, flags);
+       }
+
+       if (hc->type != 1 || hc->e1_state == 1)
+               for (ch = 0; ch <= 31; ch++) {
+                       if (hc->created[hc->chan[ch].port]) {
+                               hfcmulti_tx(hc, ch);
+                               /* fifo is started when switching to rx-fifo */
+                               hfcmulti_rx(hc, ch);
+                               if (hc->chan[ch].dch &&
+                                   hc->chan[ch].nt_timer > -1) {
+                                       dch = hc->chan[ch].dch;
+                                       if (!(--hc->chan[ch].nt_timer)) {
+                                               schedule_event(dch,
+                                                   FLG_PHCHANGE);
+                                               if (debug &
+                                                   DEBUG_HFCMULTI_STATE)
+                                                       printk(KERN_DEBUG
+                                                           "%s: nt_timer at "
+                                                           "state %x\n",
+                                                           __func__,
+                                                           dch->state);
+                                       }
+                               }
+                       }
+               }
+       if (hc->type == 1 && hc->created[0]) {
+               dch = hc->chan[hc->dslot].dch;
+               if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) {
+                       /* LOS */
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS;
+                       if (!temp && hc->chan[hc->dslot].los)
+                               signal_state_up(dch, L1_SIGNAL_LOS_ON,
+                                   "LOS detected");
+                       if (temp && !hc->chan[hc->dslot].los)
+                               signal_state_up(dch, L1_SIGNAL_LOS_OFF,
+                                   "LOS gone");
+                       hc->chan[hc->dslot].los = temp;
+               }
+               if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) {
+                       /* AIS */
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS;
+                       if (!temp && hc->chan[hc->dslot].ais)
+                               signal_state_up(dch, L1_SIGNAL_AIS_ON,
+                                   "AIS detected");
+                       if (temp && !hc->chan[hc->dslot].ais)
+                               signal_state_up(dch, L1_SIGNAL_AIS_OFF,
+                                   "AIS gone");
+                       hc->chan[hc->dslot].ais = temp;
+               }
+               if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) {
+                       /* SLIP */
+                       temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX;
+                       if (!temp && hc->chan[hc->dslot].slip_rx)
+                               signal_state_up(dch, L1_SIGNAL_SLIP_RX,
+                                   " bit SLIP detected RX");
+                       hc->chan[hc->dslot].slip_rx = temp;
+                       temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX;
+                       if (!temp && hc->chan[hc->dslot].slip_tx)
+                               signal_state_up(dch, L1_SIGNAL_SLIP_TX,
+                                   " bit SLIP detected TX");
+                       hc->chan[hc->dslot].slip_tx = temp;
+               }
+               if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) {
+                       /* RDI */
+                       temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A;
+                       if (!temp && hc->chan[hc->dslot].rdi)
+                               signal_state_up(dch, L1_SIGNAL_RDI_ON,
+                                   "RDI detected");
+                       if (temp && !hc->chan[hc->dslot].rdi)
+                               signal_state_up(dch, L1_SIGNAL_RDI_OFF,
+                                   "RDI gone");
+                       hc->chan[hc->dslot].rdi = temp;
+               }
+               temp = HFC_inb_nodebug(hc, R_JATT_DIR);
+               switch (hc->chan[hc->dslot].sync) {
+               case 0:
+                       if ((temp & 0x60) == 0x60) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 now "
+                                           "in clock sync\n",
+                                           __func__, hc->id);
+                               HFC_outb(hc, R_RX_OFF,
+                                   hc->chan[hc->dslot].jitter | V_RX_INIT);
+                               HFC_outb(hc, R_TX_OFF,
+                                   hc->chan[hc->dslot].jitter | V_RX_INIT);
+                               hc->chan[hc->dslot].sync = 1;
+                               goto check_framesync;
+                       }
+                       break;
+               case 1:
+                       if ((temp & 0x60) != 0x60) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 "
+                                           "lost clock sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 0;
+                               break;
+                       }
+check_framesync:
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+                       if (temp == 0x27) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 "
+                                           "now in frame sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 2;
+                       }
+                       break;
+               case 2:
+                       if ((temp & 0x60) != 0x60) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 lost "
+                                           "clock & frame sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 0;
+                               break;
+                       }
+                       temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+                       if (temp != 0x27) {
+                               if (debug & DEBUG_HFCMULTI_SYNC)
+                                       printk(KERN_DEBUG
+                                           "%s: (id=%d) E1 "
+                                           "lost frame sync\n",
+                                           __func__, hc->id);
+                               hc->chan[hc->dslot].sync = 1;
+                       }
+                       break;
+               }
+       }
+
+       if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+               hfcmulti_watchdog(hc);
+
+       if (hc->leds)
+               hfcmulti_leds(hc);
+}
+
+static void
+ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech)
+{
+       struct dchannel *dch;
+       int             ch;
+       int             active;
+       u_char          st_status, temp;
+
+       /* state machine */
+       for (ch = 0; ch <= 31; ch++) {
+               if (hc->chan[ch].dch) {
+                       dch = hc->chan[ch].dch;
+                       if (r_irq_statech & 1) {
+                               HFC_outb_nodebug(hc, R_ST_SEL,
+                                       hc->chan[ch].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               /* undocumented: status changes during read */
+                               st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE);
+                               while (st_status != (temp =
+                                       HFC_inb_nodebug(hc, A_ST_RD_STATE))) {
+                                       if (debug & DEBUG_HFCMULTI_STATE)
+                                               printk(KERN_DEBUG "%s: reread "
+                                                   "STATE because %d!=%d\n",
+                                                   __func__, temp,
+                                                   st_status);
+                                       st_status = temp; /* repeat */
+                               }
+
+                               /* Speech Design TE-sync indication */
+                               if (test_bit(HFC_CHIP_PLXSD, &hc->chip) &&
+                                       dch->dev.D.protocol == ISDN_P_TE_S0) {
+                                       if (st_status & V_FR_SYNC_ST)
+                                               hc->syncronized |=
+                                                   (1 << hc->chan[ch].port);
+                                       else
+                                               hc->syncronized &=
+                                                  ~(1 << hc->chan[ch].port);
+                               }
+                               dch->state = st_status & 0x0f;
+                               if (dch->dev.D.protocol == ISDN_P_NT_S0)
+                                       active = 3;
+                               else
+                                       active = 7;
+                               if (dch->state == active) {
+                                       HFC_outb_nodebug(hc, R_FIFO,
+                                               (ch << 1) | 1);
+                                       HFC_wait_nodebug(hc);
+                                       HFC_outb_nodebug(hc,
+                                               R_INC_RES_FIFO, V_RES_F);
+                                       HFC_wait_nodebug(hc);
+                                       dch->tx_idx = 0;
+                               }
+                               schedule_event(dch, FLG_PHCHANGE);
+                               if (debug & DEBUG_HFCMULTI_STATE)
+                                       printk(KERN_DEBUG
+                                           "%s: S/T newstate %x port %d\n",
+                                           __func__, dch->state,
+                                           hc->chan[ch].port);
+                       }
+                       r_irq_statech >>= 1;
+               }
+       }
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+               plxsd_checksync(hc, 0);
+}
+
+static void
+fifo_irq(struct hfc_multi *hc, int block)
+{
+       int     ch, j;
+       struct dchannel *dch;
+       struct bchannel *bch;
+       u_char r_irq_fifo_bl;
+
+       r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block);
+       j = 0;
+       while (j < 8) {
+               ch = (block << 2) + (j >> 1);
+               dch = hc->chan[ch].dch;
+               bch = hc->chan[ch].bch;
+               if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) {
+                       j += 2;
+                       continue;
+               }
+               if (dch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &dch->Flags)) {
+                       hfcmulti_tx(hc, ch);
+                       /* start fifo */
+                       HFC_outb_nodebug(hc, R_FIFO, 0);
+                       HFC_wait_nodebug(hc);
+               }
+               if (bch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       hfcmulti_tx(hc, ch);
+                       /* start fifo */
+                       HFC_outb_nodebug(hc, R_FIFO, 0);
+                       HFC_wait_nodebug(hc);
+               }
+               j++;
+               if (dch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &dch->Flags)) {
+                       hfcmulti_rx(hc, ch);
+               }
+               if (bch && (r_irq_fifo_bl & (1 << j)) &&
+                   test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       hfcmulti_rx(hc, ch);
+               }
+               j++;
+       }
+}
+
+#ifdef IRQ_DEBUG
+int irqsem;
+#endif
+static irqreturn_t
+hfcmulti_interrupt(int intno, void *dev_id)
+{
+#ifdef IRQCOUNT_DEBUG
+       static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0,
+           iq5 = 0, iq6 = 0, iqcnt = 0;
+#endif
+       static int              count;
+       struct hfc_multi        *hc = dev_id;
+       struct dchannel         *dch;
+       u_char                  r_irq_statech, status, r_irq_misc, r_irq_oview;
+       int                     i;
+       u_short                 *plx_acc, wval;
+       u_char                  e1_syncsta, temp;
+       u_long                  flags;
+
+       if (!hc) {
+               printk(KERN_ERR "HFC-multi: Spurious interrupt!\n");
+               return IRQ_NONE;
+       }
+
+       spin_lock(&hc->lock);
+
+#ifdef IRQ_DEBUG
+       if (irqsem)
+               printk(KERN_ERR "irq for card %d during irq from "
+               "card %d, this is no bug.\n", hc->id + 1, irqsem);
+       irqsem = hc->id + 1;
+#endif
+
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, flags);
+               plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR);
+               wval = readw(plx_acc);
+               spin_unlock_irqrestore(&plx_lock, flags);
+               if (!(wval & PLX_INTCSR_LINTI1_STATUS))
+                       goto irq_notforus;
+       }
+
+       status = HFC_inb_nodebug(hc, R_STATUS);
+       r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH);
+#ifdef IRQCOUNT_DEBUG
+       if (r_irq_statech)
+               iq1++;
+       if (status & V_DTMF_STA)
+               iq2++;
+       if (status & V_LOST_STA)
+               iq3++;
+       if (status & V_EXT_IRQSTA)
+               iq4++;
+       if (status & V_MISC_IRQSTA)
+               iq5++;
+       if (status & V_FR_IRQSTA)
+               iq6++;
+       if (iqcnt++ > 5000) {
+               printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n",
+                   iq1, iq2, iq3, iq4, iq5, iq6);
+               iqcnt = 0;
+       }
+#endif
+       if (!r_irq_statech &&
+           !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA |
+           V_MISC_IRQSTA | V_FR_IRQSTA))) {
+               /* irq is not for us */
+               goto irq_notforus;
+       }
+       hc->irqcnt++;
+       if (r_irq_statech) {
+               if (hc->type != 1)
+                       ph_state_irq(hc, r_irq_statech);
+       }
+       if (status & V_EXT_IRQSTA)
+               ; /* external IRQ */
+       if (status & V_LOST_STA) {
+               /* LOST IRQ */
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */
+       }
+       if (status & V_MISC_IRQSTA) {
+               /* misc IRQ */
+               r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC);
+               if (r_irq_misc & V_STA_IRQ) {
+                       if (hc->type == 1) {
+                               /* state machine */
+                               dch = hc->chan[hc->dslot].dch;
+                               e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA);
+                               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+                                && hc->e1_getclock) {
+                                       if (e1_syncsta & V_FR_SYNC_E1)
+                                               hc->syncronized = 1;
+                                       else
+                                               hc->syncronized = 0;
+                               }
+                               /* undocumented: status changes during read */
+                               dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA);
+                               while (dch->state != (temp =
+                                       HFC_inb_nodebug(hc, R_E1_RD_STA))) {
+                                       if (debug & DEBUG_HFCMULTI_STATE)
+                                               printk(KERN_DEBUG "%s: reread "
+                                                   "STATE because %d!=%d\n",
+                                                   __func__, temp,
+                                                   dch->state);
+                                       dch->state = temp; /* repeat */
+                               }
+                               dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA)
+                                       & 0x7;
+                               schedule_event(dch, FLG_PHCHANGE);
+                               if (debug & DEBUG_HFCMULTI_STATE)
+                                       printk(KERN_DEBUG
+                                           "%s: E1 (id=%d) newstate %x\n",
+                                           __func__, hc->id, dch->state);
+                               if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+                                       plxsd_checksync(hc, 0);
+                       }
+               }
+               if (r_irq_misc & V_TI_IRQ)
+                       handle_timer_irq(hc);
+
+               if (r_irq_misc & V_DTMF_IRQ) {
+                       /* -> DTMF IRQ */
+                       hfcmulti_dtmf(hc);
+               }
+               /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable  */
+               if (r_irq_misc & V_IRQ_PROC) {
+                       /* IRQ every 125us */
+                       count++;
+                       /* generate 1kHz signal */
+                       if (count == 8) {
+                               if (hfc_interrupt)
+                                       hfc_interrupt();
+                               count = 0;
+                       }
+               }
+
+       }
+       if (status & V_FR_IRQSTA) {
+               /* FIFO IRQ */
+               r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW);
+               for (i = 0; i < 8; i++) {
+                       if (r_irq_oview & (1 << i))
+                               fifo_irq(hc, i);
+               }
+       }
+
+#ifdef IRQ_DEBUG
+       irqsem = 0;
+#endif
+       spin_unlock(&hc->lock);
+       return IRQ_HANDLED;
+
+irq_notforus:
+#ifdef IRQ_DEBUG
+       irqsem = 0;
+#endif
+       spin_unlock(&hc->lock);
+       return IRQ_NONE;
+}
+
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+
+static void
+hfcmulti_dbusy_timer(struct hfc_multi *hc)
+{
+}
+
+
+/*
+ * activate/deactivate hardware for selected channels and mode
+ *
+ * configure B-channel with the given protocol
+ * ch eqals to the HFC-channel (0-31)
+ * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31
+ * for S/T, 1-31 for E1)
+ * the hdlc interrupts will be set/unset
+ */
+static int
+mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx,
+    int bank_tx, int slot_rx, int bank_rx)
+{
+       int flow_tx = 0, flow_rx = 0, routing = 0;
+       int oslot_tx, oslot_rx;
+       int conf;
+
+       if (ch < 0 || ch > 31)
+               return EINVAL;
+       oslot_tx = hc->chan[ch].slot_tx;
+       oslot_rx = hc->chan[ch].slot_rx;
+       conf = hc->chan[ch].conf;
+
+       if (debug & DEBUG_HFCMULTI_MODE)
+               printk(KERN_DEBUG
+                   "%s: card %d channel %d protocol %x slot old=%d new=%d "
+                   "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n",
+                   __func__, hc->id, ch, protocol, oslot_tx, slot_tx,
+                   bank_tx, oslot_rx, slot_rx, bank_rx);
+
+       if (oslot_tx >= 0 && slot_tx != oslot_tx) {
+               /* remove from slot */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: remove from slot %d (TX)\n",
+                           __func__, oslot_tx);
+               if (hc->slot_owner[oslot_tx<<1] == ch) {
+                       HFC_outb(hc, R_SLOT, oslot_tx << 1);
+                       HFC_outb(hc, A_SL_CFG, 0);
+                       HFC_outb(hc, A_CONF, 0);
+                       hc->slot_owner[oslot_tx<<1] = -1;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_MODE)
+                               printk(KERN_DEBUG
+                                   "%s: we are not owner of this tx slot "
+                                   "anymore, channel %d is.\n",
+                                   __func__, hc->slot_owner[oslot_tx<<1]);
+               }
+       }
+
+       if (oslot_rx >= 0 && slot_rx != oslot_rx) {
+               /* remove from slot */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG
+                           "%s: remove from slot %d (RX)\n",
+                           __func__, oslot_rx);
+               if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) {
+                       HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR);
+                       HFC_outb(hc, A_SL_CFG, 0);
+                       hc->slot_owner[(oslot_rx << 1) | 1] = -1;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_MODE)
+                               printk(KERN_DEBUG
+                                   "%s: we are not owner of this rx slot "
+                                   "anymore, channel %d is.\n",
+                                   __func__,
+                                   hc->slot_owner[(oslot_rx << 1) | 1]);
+               }
+       }
+
+       if (slot_tx < 0) {
+               flow_tx = 0x80; /* FIFO->ST */
+               /* disable pcm slot */
+               hc->chan[ch].slot_tx = -1;
+               hc->chan[ch].bank_tx = 0;
+       } else {
+               /* set pcm slot */
+               if (hc->chan[ch].txpending)
+                       flow_tx = 0x80; /* FIFO->ST */
+               else
+                       flow_tx = 0xc0; /* PCM->ST */
+               /* put on slot */
+               routing = bank_tx ? 0xc0 : 0x80;
+               if (conf >= 0 || bank_tx > 1)
+                       routing = 0x40; /* loop */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: put channel %d to slot %d bank"
+                           " %d flow %02x routing %02x conf %d (TX)\n",
+                           __func__, ch, slot_tx, bank_tx,
+                           flow_tx, routing, conf);
+               HFC_outb(hc, R_SLOT, slot_tx << 1);
+               HFC_outb(hc, A_SL_CFG, (ch<<1) | routing);
+               HFC_outb(hc, A_CONF, (conf < 0) ? 0 : (conf | V_CONF_SL));
+               hc->slot_owner[slot_tx << 1] = ch;
+               hc->chan[ch].slot_tx = slot_tx;
+               hc->chan[ch].bank_tx = bank_tx;
+       }
+       if (slot_rx < 0) {
+               /* disable pcm slot */
+               flow_rx = 0x80; /* ST->FIFO */
+               hc->chan[ch].slot_rx = -1;
+               hc->chan[ch].bank_rx = 0;
+       } else {
+               /* set pcm slot */
+               if (hc->chan[ch].txpending)
+                       flow_rx = 0x80; /* ST->FIFO */
+               else
+                       flow_rx = 0xc0; /* ST->(FIFO,PCM) */
+               /* put on slot */
+               routing = bank_rx?0x80:0xc0; /* reversed */
+               if (conf >= 0 || bank_rx > 1)
+                       routing = 0x40; /* loop */
+               if (debug & DEBUG_HFCMULTI_MODE)
+                       printk(KERN_DEBUG "%s: put channel %d to slot %d bank"
+                           " %d flow %02x routing %02x conf %d (RX)\n",
+                           __func__, ch, slot_rx, bank_rx,
+                           flow_rx, routing, conf);
+               HFC_outb(hc, R_SLOT, (slot_rx<<1) | V_SL_DIR);
+               HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing);
+               hc->slot_owner[(slot_rx<<1)|1] = ch;
+               hc->chan[ch].slot_rx = slot_rx;
+               hc->chan[ch].bank_rx = bank_rx;
+       }
+
+       switch (protocol) {
+       case (ISDN_P_NONE):
+               /* disable TX fifo */
+               HFC_outb(hc, R_FIFO, ch << 1);
+               HFC_wait(hc);
+               HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF);
+               HFC_outb(hc, A_SUBCH_CFG, 0);
+               HFC_outb(hc, A_IRQ_MSK, 0);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               /* disable RX fifo */
+               HFC_outb(hc, R_FIFO, (ch<<1)|1);
+               HFC_wait(hc);
+               HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00);
+               HFC_outb(hc, A_SUBCH_CFG, 0);
+               HFC_outb(hc, A_IRQ_MSK, 0);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               if (hc->chan[ch].bch && hc->type != 1) {
+                       hc->hw.a_st_ctrl0[hc->chan[ch].port] &=
+                           ((ch & 0x3) == 0)? ~V_B1_EN: ~V_B2_EN;
+                       HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_CTRL0,
+                           hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+               }
+               if (hc->chan[ch].bch) {
+                       test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT,
+                           &hc->chan[ch].bch->Flags);
+               }
+               break;
+       case (ISDN_P_B_RAW): /* B-channel */
+
+               if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+                   (hc->chan[ch].slot_rx < 0) &&
+                   (hc->chan[ch].slot_tx < 0)) {
+
+                       printk(KERN_DEBUG
+                           "Setting B-channel %d to echo cancelable "
+                           "state on PCM slot %d\n", ch,
+                           ((ch / 4) * 8) + ((ch % 4) * 4) + 1);
+                       printk(KERN_DEBUG
+                           "Enabling pass through for channel\n");
+                       vpm_out(hc, ch, ((ch / 4) * 8) +
+                           ((ch % 4) * 4) + 1, 0x01);
+                       /* rx path */
+                       /* S/T -> PCM */
+                       HFC_outb(hc, R_FIFO, (ch << 1));
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+                           ((ch % 4) * 4) + 1) << 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1));
+
+                       /* PCM -> FIFO */
+                       HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+                       HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) +
+                           ((ch % 4) * 4) + 1) << 1) | 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1);
+
+                       /* tx path */
+                       /* PCM -> S/T */
+                       HFC_outb(hc, R_FIFO, (ch << 1) | 1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) +
+                           ((ch % 4) * 4)) << 1) | 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1);
+
+                       /* FIFO -> PCM */
+                       HFC_outb(hc, R_FIFO, 0x20 | (ch << 1));
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+                       /* tx silence */
+                       HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+                       HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+                           ((ch % 4) * 4)) << 1);
+                       HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1));
+               } else {
+                       /* enable TX fifo */
+                       HFC_outb(hc, R_FIFO, ch << 1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 |
+                           V_HDLC_TRP | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+                       /* tx silence */
+                       HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+                       /* enable RX fifo */
+                       HFC_outb(hc, R_FIFO, (ch<<1)|1);
+                       HFC_wait(hc);
+                       HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+                       HFC_outb(hc, A_IRQ_MSK, 0);
+                       HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait(hc);
+               }
+               if (hc->type != 1) {
+                       hc->hw.a_st_ctrl0[hc->chan[ch].port] |=
+                           ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN;
+                       HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_CTRL0,
+                           hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+               }
+               if (hc->chan[ch].bch)
+                       test_and_set_bit(FLG_TRANSPARENT,
+                           &hc->chan[ch].bch->Flags);
+               break;
+       case (ISDN_P_B_HDLC): /* B-channel */
+       case (ISDN_P_TE_S0): /* D-channel */
+       case (ISDN_P_NT_S0):
+       case (ISDN_P_TE_E1):
+       case (ISDN_P_NT_E1):
+               /* enable TX fifo */
+               HFC_outb(hc, R_FIFO, ch<<1);
+               HFC_wait(hc);
+               if (hc->type == 1 || hc->chan[ch].bch) {
+                       /* E1 or B-channel */
+                       HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04);
+                       HFC_outb(hc, A_SUBCH_CFG, 0);
+               } else {
+                       /* D-Channel without HDLC fill flags */
+                       HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF);
+                       HFC_outb(hc, A_SUBCH_CFG, 2);
+               }
+               HFC_outb(hc, A_IRQ_MSK, V_IRQ);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               /* enable RX fifo */
+               HFC_outb(hc, R_FIFO, (ch<<1)|1);
+               HFC_wait(hc);
+               HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04);
+               if (hc->type == 1 || hc->chan[ch].bch)
+                       HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */
+               else
+                       HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */
+               HFC_outb(hc, A_IRQ_MSK, V_IRQ);
+               HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+               HFC_wait(hc);
+               if (hc->chan[ch].bch) {
+                       test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+                       if (hc->type != 1) {
+                               hc->hw.a_st_ctrl0[hc->chan[ch].port] |=
+                                 ((ch&0x3) == 0) ? V_B1_EN : V_B2_EN;
+                               HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               HFC_outb(hc, A_ST_CTRL0,
+                                 hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+                       }
+               }
+               break;
+       default:
+               printk(KERN_DEBUG "%s: protocol not known %x\n",
+                   __func__, protocol);
+               hc->chan[ch].protocol = ISDN_P_NONE;
+               return -ENOPROTOOPT;
+       }
+       hc->chan[ch].protocol = protocol;
+       return 0;
+}
+
+
+/*
+ * connect/disconnect PCM
+ */
+
+static void
+hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx,
+    int slot_rx, int bank_rx)
+{
+       if (slot_rx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) {
+               /* disable PCM */
+               mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0);
+               return;
+       }
+
+       /* enable pcm */
+       mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx,
+               slot_rx, bank_rx);
+}
+
+/*
+ * set/disable conference
+ */
+
+static void
+hfcmulti_conf(struct hfc_multi *hc, int ch, int num)
+{
+       if (num >= 0 && num <= 7)
+               hc->chan[ch].conf = num;
+       else
+               hc->chan[ch].conf = -1;
+       mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx,
+           hc->chan[ch].bank_tx, hc->chan[ch].slot_rx,
+           hc->chan[ch].bank_rx);
+}
+
+
+/*
+ * set/disable sample loop
+ */
+
+/* NOTE: this function is experimental and therefore disabled */
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfcm_l1callback(struct dchannel *dch, u_int cmd)
+{
+       struct hfc_multi        *hc = dch->hw;
+       u_long  flags;
+
+       switch (cmd) {
+       case INFO3_P8:
+       case INFO3_P10:
+               break;
+       case HW_RESET_REQ:
+               /* start activation */
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->type == 1) {
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: HW_RESET_REQ no BRI\n",
+                                   __func__);
+               } else {
+                       HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */
+                       udelay(6); /* wait at least 5,21us */
+                       HFC_outb(hc, A_ST_WR_STATE, 3);
+                       HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT*3));
+                               /* activate */
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               l1_event(dch->l1, HW_POWERUP_IND);
+               break;
+       case HW_DEACT_REQ:
+               /* start deactivation */
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->type == 1) {
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: HW_DEACT_REQ no BRI\n",
+                                   __func__);
+               } else {
+                       HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2);
+                               /* deactivate */
+                       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                               hc->syncronized &=
+                                  ~(1 << hc->chan[dch->slot].port);
+                               plxsd_checksync(hc, 0);
+                       }
+               }
+               skb_queue_purge(&dch->squeue);
+               if (dch->tx_skb) {
+                       dev_kfree_skb(dch->tx_skb);
+                       dch->tx_skb = NULL;
+               }
+               dch->tx_idx = 0;
+               if (dch->rx_skb) {
+                       dev_kfree_skb(dch->rx_skb);
+                       dch->rx_skb = NULL;
+               }
+               test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                       del_timer(&dch->timer);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case HW_POWERUP_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->type == 1) {
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: HW_POWERUP_REQ no BRI\n",
+                                   __func__);
+               } else {
+                       HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+                       /* undocumented: delay after R_ST_SEL */
+                       udelay(1);
+                       HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */
+                       udelay(6); /* wait at least 5,21us */
+                       HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Layer2 -> Layer 1 Transfer
+ */
+
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_multi        *hc = dch->hw;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (skb->len < 1)
+                       break;
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = dchannel_senddata(dch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcmulti_tx(hc, dch->slot);
+                       ret = 0;
+                       /* start fifo */
+                       HFC_outb(hc, R_FIFO, 0);
+                       HFC_wait(hc);
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+                       spin_lock_irqsave(&hc->lock, flags);
+                       ret = 0;
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: PH_ACTIVATE port %d (0..%d)\n",
+                                   __func__, hc->chan[dch->slot].port,
+                                   hc->ports-1);
+                       /* start activation */
+                       if (hc->type == 1) {
+                               ph_state_change(dch);
+                               if (debug & DEBUG_HFCMULTI_STATE)
+                                       printk(KERN_DEBUG
+                                           "%s: E1 report state %x \n",
+                                           __func__, dch->state);
+                       } else {
+                               HFC_outb(hc, R_ST_SEL,
+                                   hc->chan[dch->slot].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1);
+                                   /* G1 */
+                               udelay(6); /* wait at least 5,21us */
+                               HFC_outb(hc, A_ST_WR_STATE, 1);
+                               HFC_outb(hc, A_ST_WR_STATE, 1 |
+                                   (V_ST_ACT*3)); /* activate */
+                               dch->state = 1;
+                       }
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               } else
+                       ret = l1_event(dch->l1, hh->prim);
+               break;
+       case PH_DEACTIVATE_REQ:
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+                       spin_lock_irqsave(&hc->lock, flags);
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG
+                                   "%s: PH_DEACTIVATE port %d (0..%d)\n",
+                                   __func__, hc->chan[dch->slot].port,
+                                   hc->ports-1);
+                       /* start deactivation */
+                       if (hc->type == 1) {
+                               if (debug & DEBUG_HFCMULTI_MSG)
+                                       printk(KERN_DEBUG
+                                           "%s: PH_DEACTIVATE no BRI\n",
+                                           __func__);
+                       } else {
+                               HFC_outb(hc, R_ST_SEL,
+                                   hc->chan[dch->slot].port);
+                               /* undocumented: delay after R_ST_SEL */
+                               udelay(1);
+                               HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2);
+                                   /* deactivate */
+                               dch->state = 1;
+                       }
+                       skb_queue_purge(&dch->squeue);
+                       if (dch->tx_skb) {
+                               dev_kfree_skb(dch->tx_skb);
+                               dch->tx_skb = NULL;
+                       }
+                       dch->tx_idx = 0;
+                       if (dch->rx_skb) {
+                               dev_kfree_skb(dch->rx_skb);
+                               dch->rx_skb = NULL;
+                       }
+                       test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+                       if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                               del_timer(&dch->timer);
+#ifdef FIXME
+                       if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+                               dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+                       ret = 0;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               } else
+                       ret = l1_event(dch->l1, hh->prim);
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+       struct hfc_multi        *hc = bch->hw;
+       u_long                  flags;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+               dev_kfree_skb(bch->next_skb);
+               bch->next_skb = NULL;
+       }
+       if (bch->tx_skb) {
+               dev_kfree_skb(bch->tx_skb);
+               bch->tx_skb = NULL;
+       }
+       bch->tx_idx = 0;
+       if (bch->rx_skb) {
+               dev_kfree_skb(bch->rx_skb);
+               bch->rx_skb = NULL;
+       }
+       hc->chan[bch->slot].coeff_count = 0;
+       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+       hc->chan[bch->slot].rx_off = 0;
+       hc->chan[bch->slot].conf = -1;
+       mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0);
+       spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_multi        *hc = bch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (!skb->len)
+                       break;
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcmulti_tx(hc, bch->slot);
+                       ret = 0;
+                       /* start fifo */
+                       HFC_outb_nodebug(hc, R_FIFO, 0);
+                       HFC_wait_nodebug(hc);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+                       } else
+                               spin_unlock_irqrestore(&hc->lock, flags);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n",
+                               __func__, bch->slot);
+               spin_lock_irqsave(&hc->lock, flags);
+               /* activate B-channel if not already activated */
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) {
+                       hc->chan[bch->slot].txpending = 0;
+                       ret = mode_hfcmulti(hc, bch->slot,
+                               ch->protocol,
+                               hc->chan[bch->slot].slot_tx,
+                               hc->chan[bch->slot].bank_tx,
+                               hc->chan[bch->slot].slot_rx,
+                               hc->chan[bch->slot].bank_rx);
+                       if (!ret) {
+                               if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf
+                                       && test_bit(HFC_CHIP_DTMF, &hc->chip)) {
+                                       /* start decoder */
+                                       hc->dtmf = 1;
+                                       if (debug & DEBUG_HFCMULTI_DTMF)
+                                               printk(KERN_DEBUG
+                                                   "%s: start dtmf decoder\n",
+                                                       __func__);
+                                       HFC_outb(hc, R_DTMF, hc->hw.r_dtmf |
+                                           V_RST_DTMF);
+                               }
+                       }
+               } else
+                       ret = 0;
+               spin_unlock_irqrestore(&hc->lock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+                               GFP_KERNEL);
+               break;
+       case PH_CONTROL_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               switch (hh->id) {
+               case HFC_SPL_LOOP_ON: /* set sample loop */
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG
+                           "%s: HFC_SPL_LOOP_ON (len = %d)\n",
+                           __func__, skb->len);
+                       ret = 0;
+                       break;
+               case HFC_SPL_LOOP_OFF: /* set silence */
+                       if (debug & DEBUG_HFCMULTI_MSG)
+                               printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n",
+                                   __func__);
+                       ret = 0;
+                       break;
+               default:
+                       printk(KERN_ERR
+                            "%s: unknown PH_CONTROL_REQ info %x\n",
+                            __func__, hh->id);
+                       ret = -EINVAL;
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case PH_DEACTIVATE_REQ:
+               deactivate_bchannel(bch); /* locked there */
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+                       GFP_KERNEL);
+               ret = 0;
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+/*
+ * bchannel control function
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int                     ret = 0;
+       struct dsp_features     *features =
+               (struct dsp_features *)(*((u_long *)&cq->p1));
+       struct hfc_multi        *hc = bch->hw;
+       int                     slot_tx;
+       int                     bank_tx;
+       int                     slot_rx;
+       int                     bank_rx;
+       int                     num;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP
+                       | MISDN_CTRL_RX_OFF;
+               break;
+       case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */
+               hc->chan[bch->slot].rx_off = !!cq->p1;
+               if (!hc->chan[bch->slot].rx_off) {
+                       /* reset fifo on rx on */
+                       HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1);
+                       HFC_wait_nodebug(hc);
+                       HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+                       HFC_wait_nodebug(hc);
+               }
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n",
+                           __func__, bch->nr, hc->chan[bch->slot].rx_off);
+               break;
+       case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+                           __func__);
+               /* create confirm */
+               features->hfc_id = hc->id;
+               if (test_bit(HFC_CHIP_DTMF, &hc->chip))
+                       features->hfc_dtmf = 1;
+               features->hfc_loops = 0;
+               if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+                       features->hfc_echocanhw = 1;
+               } else {
+                       features->pcm_id = hc->pcm;
+                       features->pcm_slots = hc->slots;
+                       features->pcm_banks = 2;
+               }
+               break;
+       case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */
+               slot_tx = cq->p1 & 0xff;
+               bank_tx = cq->p1 >> 8;
+               slot_rx = cq->p2 & 0xff;
+               bank_rx = cq->p2 >> 8;
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG
+                           "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+                           "slot %d bank %d (RX)\n",
+                           __func__, slot_tx, bank_tx,
+                           slot_rx, bank_rx);
+               if (slot_tx < hc->slots && bank_tx <= 2 &&
+                   slot_rx < hc->slots && bank_rx <= 2)
+                       hfcmulti_pcm(hc, bch->slot,
+                           slot_tx, bank_tx, slot_rx, bank_rx);
+               else {
+                       printk(KERN_WARNING
+                           "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+                           "slot %d bank %d (RX) out of range\n",
+                           __func__, slot_tx, bank_tx,
+                           slot_rx, bank_rx);
+                       ret = -EINVAL;
+               }
+               break;
+       case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_PCM_DISC\n",
+                           __func__);
+               hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0);
+               break;
+       case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */
+               num = cq->p1 & 0xff;
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n",
+                           __func__, num);
+               if (num <= 7)
+                       hfcmulti_conf(hc, bch->slot, num);
+               else {
+                       printk(KERN_WARNING
+                           "%s: HW_CONF_JOIN conf %d out of range\n",
+                           __func__, num);
+                       ret = -EINVAL;
+               }
+               break;
+       case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__);
+               hfcmulti_conf(hc, bch->slot, -1);
+               break;
+       case MISDN_CTRL_HFC_ECHOCAN_ON:
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__);
+               if (test_bit(HFC_CHIP_B410P, &hc->chip))
+                       vpm_echocan_on(hc, bch->slot, cq->p1);
+               else
+                       ret = -EINVAL;
+               break;
+
+       case MISDN_CTRL_HFC_ECHOCAN_OFF:
+               if (debug & DEBUG_HFCMULTI_MSG)
+                       printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n",
+                               __func__);
+               if (test_bit(HFC_CHIP_B410P, &hc->chip))
+                       vpm_echocan_off(hc, bch->slot);
+               else
+                       ret = -EINVAL;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_multi        *hc = bch->hw;
+       int                     err = -EINVAL;
+       u_long  flags;
+
+       if (bch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags))
+                       deactivate_bchannel(bch); /* locked there */
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               err = 0;
+               break;
+       case CONTROL_CHANNEL:
+               spin_lock_irqsave(&hc->lock, flags);
+               err = channel_bctrl(bch, arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown prim(%x)\n",
+                       __func__, cmd);
+       }
+       return err;
+}
+
+/*
+ * handle D-channel events
+ *
+ * handle state change event
+ */
+static void
+ph_state_change(struct dchannel *dch)
+{
+       struct hfc_multi *hc = dch->hw;
+       int ch, i;
+
+       if (!dch) {
+               printk(KERN_WARNING "%s: ERROR given dch is NULL\n",
+                   __func__);
+               return;
+       }
+       ch = dch->slot;
+
+       if (hc->type == 1) {
+               if (dch->dev.D.protocol == ISDN_P_TE_E1) {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG
+                                   "%s: E1 TE (id=%d) newstate %x\n",
+                                   __func__, hc->id, dch->state);
+               } else {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG
+                                   "%s: E1 NT (id=%d) newstate %x\n",
+                                   __func__, hc->id, dch->state);
+               }
+               switch (dch->state) {
+               case (1):
+                       if (hc->e1_state != 1) {
+                           for (i = 1; i <= 31; i++) {
+                               /* reset fifos on e1 activation */
+                               HFC_outb_nodebug(hc, R_FIFO, (i << 1) | 1);
+                               HFC_wait_nodebug(hc);
+                               HFC_outb_nodebug(hc,
+                                       R_INC_RES_FIFO, V_RES_F);
+                               HFC_wait_nodebug(hc);
+                           }
+                       }
+                       test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+                       _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+                           MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                       break;
+
+               default:
+                       if (hc->e1_state != 1)
+                               return;
+                       test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+                       _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+                           MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+               }
+               hc->e1_state = dch->state;
+       } else {
+               if (dch->dev.D.protocol == ISDN_P_TE_S0) {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG
+                                   "%s: S/T TE newstate %x\n",
+                                   __func__, dch->state);
+                       switch (dch->state) {
+                       case (0):
+                               l1_event(dch->l1, HW_RESET_IND);
+                               break;
+                       case (3):
+                               l1_event(dch->l1, HW_DEACT_IND);
+                               break;
+                       case (5):
+                       case (8):
+                               l1_event(dch->l1, ANYSIGNAL);
+                               break;
+                       case (6):
+                               l1_event(dch->l1, INFO2);
+                               break;
+                       case (7):
+                               l1_event(dch->l1, INFO4_P8);
+                               break;
+                       }
+               } else {
+                       if (debug & DEBUG_HFCMULTI_STATE)
+                               printk(KERN_DEBUG "%s: S/T NT newstate %x\n",
+                                   __func__, dch->state);
+                       switch (dch->state) {
+                       case (2):
+                               if (hc->chan[ch].nt_timer == 0) {
+                                       hc->chan[ch].nt_timer = -1;
+                                       HFC_outb(hc, R_ST_SEL,
+                                           hc->chan[ch].port);
+                                       /* undocumented: delay after R_ST_SEL */
+                                       udelay(1);
+                                       HFC_outb(hc, A_ST_WR_STATE, 4 |
+                                           V_ST_LD_STA); /* G4 */
+                                       udelay(6); /* wait at least 5,21us */
+                                       HFC_outb(hc, A_ST_WR_STATE, 4);
+                                       dch->state = 4;
+                               } else {
+                                       /* one extra count for the next event */
+                                       hc->chan[ch].nt_timer =
+                                           nt_t1_count[poll_timer] + 1;
+                                       HFC_outb(hc, R_ST_SEL,
+                                           hc->chan[ch].port);
+                                       /* undocumented: delay after R_ST_SEL */
+                                       udelay(1);
+                                       /* allow G2 -> G3 transition */
+                                       HFC_outb(hc, A_ST_WR_STATE, 2 |
+                                           V_SET_G2_G3);
+                               }
+                               break;
+                       case (1):
+                               hc->chan[ch].nt_timer = -1;
+                               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+                               _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+                                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                               break;
+                       case (4):
+                               hc->chan[ch].nt_timer = -1;
+                               break;
+                       case (3):
+                               hc->chan[ch].nt_timer = -1;
+                               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+                               _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+                                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                               break;
+                       }
+               }
+       }
+}
+
+/*
+ * called for card mode init message
+ */
+
+static void
+hfcmulti_initmode(struct dchannel *dch)
+{
+       struct hfc_multi *hc = dch->hw;
+       u_char          a_st_wr_state, r_e1_wr_sta;
+       int             i, pt;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+
+       if (hc->type == 1) {
+               hc->chan[hc->dslot].slot_tx = -1;
+               hc->chan[hc->dslot].slot_rx = -1;
+               hc->chan[hc->dslot].conf = -1;
+               if (hc->dslot) {
+                       mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol,
+                               -1, 0, -1, 0);
+                       dch->timer.function = (void *) hfcmulti_dbusy_timer;
+                       dch->timer.data = (long) dch;
+                       init_timer(&dch->timer);
+               }
+               for (i = 1; i <= 31; i++) {
+                       if (i == hc->dslot)
+                               continue;
+                       hc->chan[i].slot_tx = -1;
+                       hc->chan[i].slot_rx = -1;
+                       hc->chan[i].conf = -1;
+                       mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0);
+               }
+               /* E1 */
+               if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) {
+                       HFC_outb(hc, R_LOS0, 255); /* 2 ms */
+                       HFC_outb(hc, R_LOS1, 255); /* 512 ms */
+               }
+               if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dslot].cfg)) {
+                       HFC_outb(hc, R_RX0, 0);
+                       hc->hw.r_tx0 = 0 | V_OUT_EN;
+               } else {
+                       HFC_outb(hc, R_RX0, 1);
+                       hc->hw.r_tx0 = 1 | V_OUT_EN;
+               }
+               hc->hw.r_tx1 = V_ATX | V_NTRI;
+               HFC_outb(hc, R_TX0, hc->hw.r_tx0);
+               HFC_outb(hc, R_TX1, hc->hw.r_tx1);
+               HFC_outb(hc, R_TX_FR0, 0x00);
+               HFC_outb(hc, R_TX_FR1, 0xf8);
+
+               if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+                       HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E);
+
+               HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0);
+
+               if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+                       HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC);
+
+               if (dch->dev.D.protocol == ISDN_P_NT_E1) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG "%s: E1 port is NT-mode\n",
+                                   __func__);
+                       r_e1_wr_sta = 0; /* G0 */
+                       hc->e1_getclock = 0;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG "%s: E1 port is TE-mode\n",
+                                   __func__);
+                       r_e1_wr_sta = 0; /* F0 */
+                       hc->e1_getclock = 1;
+               }
+               if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+                       HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+               else
+                       HFC_outb(hc, R_SYNC_OUT, 0);
+               if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip))
+                       hc->e1_getclock = 1;
+               if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip))
+                       hc->e1_getclock = 0;
+               if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+                       /* SLAVE (clock master) */
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: E1 port is clock master "
+                                   "(clock from PCM)\n", __func__);
+                       HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC);
+               } else {
+                       if (hc->e1_getclock) {
+                               /* MASTER (clock slave) */
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                       printk(KERN_DEBUG
+                                           "%s: E1 port is clock slave "
+                                           "(clock to PCM)\n", __func__);
+                               HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+                       } else {
+                               /* MASTER (clock master) */
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                       printk(KERN_DEBUG "%s: E1 port is "
+                                           "clock master "
+                                           "(clock from QUARTZ)\n",
+                                           __func__);
+                               HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC |
+                                   V_PCM_SYNC | V_JATT_OFF);
+                               HFC_outb(hc, R_SYNC_OUT, 0);
+                       }
+               }
+               HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */
+               HFC_outb(hc, R_PWM_MD, V_PWM0_MD);
+               HFC_outb(hc, R_PWM0, 0x50);
+               HFC_outb(hc, R_PWM1, 0xff);
+               /* state machine setup */
+               HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA);
+               udelay(6); /* wait at least 5,21us */
+               HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta);
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized = 0;
+                       plxsd_checksync(hc, 0);
+               }
+       } else {
+               i = dch->slot;
+               hc->chan[i].slot_tx = -1;
+               hc->chan[i].slot_rx = -1;
+               hc->chan[i].conf = -1;
+               mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0);
+               dch->timer.function = (void *)hfcmulti_dbusy_timer;
+               dch->timer.data = (long) dch;
+               init_timer(&dch->timer);
+               hc->chan[i - 2].slot_tx = -1;
+               hc->chan[i - 2].slot_rx = -1;
+               hc->chan[i - 2].conf = -1;
+               mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0);
+               hc->chan[i - 1].slot_tx = -1;
+               hc->chan[i - 1].slot_rx = -1;
+               hc->chan[i - 1].conf = -1;
+               mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0);
+               /* ST */
+               pt = hc->chan[i].port;
+               /* select interface */
+               HFC_outb(hc, R_ST_SEL, pt);
+               /* undocumented: delay after R_ST_SEL */
+               udelay(1);
+               if (dch->dev.D.protocol == ISDN_P_NT_S0) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: ST port %d is NT-mode\n",
+                                   __func__, pt);
+                       /* clock delay */
+                       HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt);
+                       a_st_wr_state = 1; /* G1 */
+                       hc->hw.a_st_ctrl0[pt] = V_ST_MD;
+               } else {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: ST port %d is TE-mode\n",
+                                   __func__, pt);
+                       /* clock delay */
+                       HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te);
+                       a_st_wr_state = 2; /* F2 */
+                       hc->hw.a_st_ctrl0[pt] = 0;
+               }
+               if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg))
+                       hc->hw.a_st_ctrl0[pt] |= V_TX_LI;
+               /* line setup */
+               HFC_outb(hc, A_ST_CTRL0,  hc->hw.a_st_ctrl0[pt]);
+               /* disable E-channel */
+               if ((dch->dev.D.protocol == ISDN_P_NT_S0) ||
+                   test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg))
+                       HFC_outb(hc, A_ST_CTRL1, V_E_IGNO);
+               else
+                       HFC_outb(hc, A_ST_CTRL1, 0);
+               /* enable B-channel receive */
+               HFC_outb(hc, A_ST_CTRL2,  V_B1_RX_EN | V_B2_RX_EN);
+               /* state machine setup */
+               HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA);
+               udelay(6); /* wait at least 5,21us */
+               HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state);
+               hc->hw.r_sci_msk |= 1 << pt;
+               /* state machine interrupts */
+               HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk);
+               /* unset sync on port */
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized &=
+                          ~(1 << hc->chan[dch->slot].port);
+                       plxsd_checksync(hc, 0);
+               }
+       }
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk("%s: done\n", __func__);
+}
+
+
+static int
+open_dchannel(struct hfc_multi *hc, struct dchannel *dch,
+    struct channel_req *rq)
+{
+       int     err = 0;
+       u_long  flags;
+
+       if (debug & DEBUG_HW_OPEN)
+               printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+                   dch->dev.id, __builtin_return_address(0));
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+           (dch->dev.D.protocol != rq->protocol)) {
+           if (debug & DEBUG_HFCMULTI_MODE)
+               printk(KERN_WARNING "%s: change protocol %x to %x\n",
+                   __func__, dch->dev.D.protocol, rq->protocol);
+       }
+       if ((dch->dev.D.protocol == ISDN_P_TE_S0)
+        && (rq->protocol != ISDN_P_TE_S0))
+               l1_event(dch->l1, CLOSE_CHANNEL);
+       if (dch->dev.D.protocol != rq->protocol) {
+               if (rq->protocol == ISDN_P_TE_S0) {
+                       err = create_l1(dch, hfcm_l1callback);
+                       if (err)
+                               return err;
+               }
+               dch->dev.D.protocol = rq->protocol;
+               spin_lock_irqsave(&hc->lock, flags);
+               hfcmulti_initmode(dch);
+               spin_unlock_irqrestore(&hc->lock, flags);
+       }
+
+       if (((rq->protocol == ISDN_P_NT_S0) && (dch->state == 3)) ||
+           ((rq->protocol == ISDN_P_TE_S0) && (dch->state == 7)) ||
+           ((rq->protocol == ISDN_P_NT_E1) && (dch->state == 1)) ||
+           ((rq->protocol == ISDN_P_TE_E1) && (dch->state == 1))) {
+               _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       }
+       rq->ch = &dch->dev.D;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+open_bchannel(struct hfc_multi *hc, struct dchannel *dch,
+    struct channel_req *rq)
+{
+       struct bchannel *bch;
+       int             ch;
+
+       if (!test_bit(rq->adr.channel, &dch->dev.channelmap[0]))
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if (hc->type == 1)
+               ch = rq->adr.channel;
+       else
+               ch = (rq->adr.channel - 1) + (dch->slot - 2);
+       bch = hc->chan[ch].bch;
+       if (!bch) {
+               printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+                   __func__, ch);
+               return -EINVAL;
+       }
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       bch->ch.protocol = rq->protocol;
+       hc->chan[ch].rx_off = 0;
+       rq->ch = &bch->ch;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_multi        *hc = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+       u_long                  flags;
+
+       if (dch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               switch (rq->protocol) {
+               case ISDN_P_TE_S0:
+               case ISDN_P_NT_S0:
+                       if (hc->type == 1) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq); /* locked there */
+                       break;
+               case ISDN_P_TE_E1:
+               case ISDN_P_NT_E1:
+                       if (hc->type != 1) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq); /* locked there */
+                       break;
+               default:
+                       spin_lock_irqsave(&hc->lock, flags);
+                       err = open_bchannel(hc, dch, rq);
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+                           __func__, dch->dev.id,
+                           __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               spin_lock_irqsave(&hc->lock, flags);
+               err = channel_dctrl(dch, arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               err = -EINVAL;
+       }
+       return err;
+}
+
+/*
+ * initialize the card
+ */
+
+/*
+ * start timer irq, wait some time and check if we have interrupts.
+ * if not, reset chip and try again.
+ */
+static int
+init_card(struct hfc_multi *hc)
+{
+       int     err = -EIO;
+       u_long  flags;
+       u_short *plx_acc;
+       u_long  plx_flags;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered\n", __func__);
+
+       spin_lock_irqsave(&hc->lock, flags);
+       /* set interrupts but leave global interrupt disabled */
+       hc->hw.r_irq_ctrl = V_FIFO_IRQ;
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+
+       if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, IRQF_SHARED,
+           "HFC-multi", hc)) {
+               printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n",
+                   hc->pci_dev->irq);
+               return -EIO;
+       }
+       hc->irq = hc->pci_dev->irq;
+
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+               writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE),
+                       plx_acc); /* enable PCI & LINT1 irq */
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+                   __func__, hc->irq, hc->irqcnt);
+       err = init_chip(hc);
+       if (err)
+               goto error;
+       /*
+        * Finally enable IRQ output
+        * this is only allowed, if an IRQ routine is allready
+        * established for this HFC, so don't do that earlier
+        */
+       spin_lock_irqsave(&hc->lock, flags);
+       enable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       /* printk(KERN_DEBUG "no master irq set!!!\n"); */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
+       /* turn IRQ off until chip is completely initialized */
+       spin_lock_irqsave(&hc->lock, flags);
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+                   __func__, hc->irq, hc->irqcnt);
+       if (hc->irqcnt) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: done\n", __func__);
+
+               return 0;
+       }
+       if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+               printk(KERN_INFO "ignoring missing interrupts\n");
+               return 0;
+       }
+
+       printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n",
+               hc->irq);
+
+       err = -EIO;
+
+error:
+       if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+               spin_lock_irqsave(&plx_lock, plx_flags);
+               plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+               writew(0x00, plx_acc); /*disable IRQs*/
+               spin_unlock_irqrestore(&plx_lock, plx_flags);
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: free irq %d\n", __func__, hc->irq);
+       if (hc->irq) {
+               free_irq(hc->irq, hc);
+               hc->irq = 0;
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err);
+       return err;
+}
+
+/*
+ * find pci device and set it up
+ */
+
+static int
+setup_pci(struct hfc_multi *hc, struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       struct hm_map   *m = (struct hm_map *)ent->driver_data;
+
+       printk(KERN_INFO
+           "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n",
+           m->vendor_name, m->card_name, m->clock2 ? "double" : "normal");
+
+       hc->pci_dev = pdev;
+       if (m->clock2)
+               test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
+
+       if (ent->device == 0xB410) {
+               test_and_set_bit(HFC_CHIP_B410P, &hc->chip);
+               test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+               test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+               hc->slots = 32;
+       }
+
+       if (hc->pci_dev->irq <= 0) {
+               printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n");
+               return -EIO;
+       }
+       if (pci_enable_device(hc->pci_dev)) {
+               printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n");
+               return -EIO;
+       }
+       hc->leds = m->leds;
+       hc->ledstate = 0xAFFEAFFE;
+       hc->opticalsupport = m->opticalsupport;
+
+       /* set memory access methods */
+       if (m->io_mode) /* use mode from card config */
+               hc->io_mode = m->io_mode;
+       switch (hc->io_mode) {
+       case HFC_IO_MODE_PLXSD:
+               test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip);
+               hc->slots = 128; /* required */
+               /* fall through */
+       case HFC_IO_MODE_PCIMEM:
+               hc->HFC_outb = HFC_outb_pcimem;
+               hc->HFC_inb = HFC_inb_pcimem;
+               hc->HFC_inw = HFC_inw_pcimem;
+               hc->HFC_wait = HFC_wait_pcimem;
+               hc->read_fifo = read_fifo_pcimem;
+               hc->write_fifo = write_fifo_pcimem;
+               break;
+       case HFC_IO_MODE_REGIO:
+               hc->HFC_outb = HFC_outb_regio;
+               hc->HFC_inb = HFC_inb_regio;
+               hc->HFC_inw = HFC_inw_regio;
+               hc->HFC_wait = HFC_wait_regio;
+               hc->read_fifo = read_fifo_regio;
+               hc->write_fifo = write_fifo_regio;
+               break;
+       default:
+               printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+               pci_disable_device(hc->pci_dev);
+               return -EIO;
+       }
+       hc->HFC_outb_nodebug = hc->HFC_outb;
+       hc->HFC_inb_nodebug = hc->HFC_inb;
+       hc->HFC_inw_nodebug = hc->HFC_inw;
+       hc->HFC_wait_nodebug = hc->HFC_wait;
+#ifdef HFC_REGISTER_DEBUG
+       hc->HFC_outb = HFC_outb_debug;
+       hc->HFC_inb = HFC_inb_debug;
+       hc->HFC_inw = HFC_inw_debug;
+       hc->HFC_wait = HFC_wait_debug;
+#endif
+       hc->pci_iobase = 0;
+       hc->pci_membase = NULL;
+       hc->plx_membase = NULL;
+
+       switch (hc->io_mode) {
+       case HFC_IO_MODE_PLXSD:
+               hc->plx_origmembase =  hc->pci_dev->resource[0].start;
+               /* MEMBASE 1 is PLX PCI Bridge */
+
+               if (!hc->plx_origmembase) {
+                       printk(KERN_WARNING
+                         "HFC-multi: No IO-Memory for PCI PLX bridge found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               hc->plx_membase = ioremap(hc->plx_origmembase, 0x80);
+               if (!hc->plx_membase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: failed to remap plx address space. "
+                           "(internal error)\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+               printk(KERN_INFO
+                   "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n",
+                   (u_long)hc->plx_membase, hc->plx_origmembase);
+
+               hc->pci_origmembase =  hc->pci_dev->resource[2].start;
+                   /* MEMBASE 1 is PLX PCI Bridge */
+               if (!hc->pci_origmembase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: No IO-Memory for PCI card found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               hc->pci_membase = ioremap(hc->pci_origmembase, 0x400);
+               if (!hc->pci_membase) {
+                       printk(KERN_WARNING "HFC-multi: failed to remap io "
+                           "address space. (internal error)\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               printk(KERN_INFO
+                   "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d "
+                   "leds-type %d\n",
+                   hc->id, (u_long)hc->pci_membase, hc->pci_origmembase,
+                   hc->pci_dev->irq, HZ, hc->leds);
+               pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+               break;
+       case HFC_IO_MODE_PCIMEM:
+               hc->pci_origmembase = hc->pci_dev->resource[1].start;
+               if (!hc->pci_origmembase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: No IO-Memory for PCI card found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               hc->pci_membase = ioremap(hc->pci_origmembase, 256);
+               if (!hc->pci_membase) {
+                       printk(KERN_WARNING
+                           "HFC-multi: failed to remap io address space. "
+                           "(internal error)\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+               printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d "
+                   "HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase,
+                   hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds);
+               pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+               break;
+       case HFC_IO_MODE_REGIO:
+               hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start;
+               if (!hc->pci_iobase) {
+                       printk(KERN_WARNING
+                               "HFC-multi: No IO for PCI card found\n");
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               if (!request_region(hc->pci_iobase, 8, "hfcmulti")) {
+                       printk(KERN_WARNING "HFC-multi: failed to request "
+                           "address space at 0x%08lx (internal error)\n",
+                           hc->pci_iobase);
+                       pci_disable_device(hc->pci_dev);
+                       return -EIO;
+               }
+
+               printk(KERN_INFO
+                   "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n",
+                   m->vendor_name, m->card_name, (u_int) hc->pci_iobase,
+                   hc->pci_dev->irq, HZ, hc->leds);
+               pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO);
+               break;
+       default:
+               printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+               pci_disable_device(hc->pci_dev);
+               return -EIO;
+       }
+
+       pci_set_drvdata(hc->pci_dev, hc);
+
+       /* At this point the needed PCI config is done */
+       /* fifos are still not enabled */
+       return 0;
+}
+
+
+/*
+ * remove port
+ */
+
+static void
+release_port(struct hfc_multi *hc, struct dchannel *dch)
+{
+       int     pt, ci, i = 0;
+       u_long  flags;
+       struct bchannel *pb;
+
+       ci = dch->slot;
+       pt = hc->chan[ci].port;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: entered for port %d\n",
+                       __func__, pt + 1);
+
+       if (pt >= hc->ports) {
+               printk(KERN_WARNING "%s: ERROR port out of range (%d).\n",
+                    __func__, pt + 1);
+               return;
+       }
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: releasing port=%d\n",
+                   __func__, pt + 1);
+
+       if (dch->dev.D.protocol == ISDN_P_TE_S0)
+               l1_event(dch->l1, CLOSE_CHANNEL);
+
+       hc->chan[ci].dch = NULL;
+
+       if (hc->created[pt]) {
+               hc->created[pt] = 0;
+               mISDN_unregister_device(&dch->dev);
+       }
+
+       spin_lock_irqsave(&hc->lock, flags);
+
+       if (dch->timer.function) {
+               del_timer(&dch->timer);
+               dch->timer.function = NULL;
+       }
+
+       if (hc->type == 1) { /* E1 */
+               /* remove sync */
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized = 0;
+                       plxsd_checksync(hc, 1);
+               }
+               /* free channels */
+               for (i = 0; i <= 31; i++) {
+                       if (hc->chan[i].bch) {
+                               if (debug & DEBUG_HFCMULTI_INIT)
+                                       printk(KERN_DEBUG
+                                           "%s: free port %d channel %d\n",
+                                           __func__, hc->chan[i].port+1, i);
+                               pb = hc->chan[i].bch;
+                               hc->chan[i].bch = NULL;
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               mISDN_freebchannel(pb);
+                               kfree(pb);
+                               kfree(hc->chan[i].coeff);
+                               spin_lock_irqsave(&hc->lock, flags);
+                       }
+               }
+       } else {
+               /* remove sync */
+               if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+                       hc->syncronized &=
+                          ~(1 << hc->chan[ci].port);
+                       plxsd_checksync(hc, 1);
+               }
+               /* free channels */
+               if (hc->chan[ci - 2].bch) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: free port %d channel %d\n",
+                                   __func__, hc->chan[ci - 2].port+1,
+                                   ci - 2);
+                       pb = hc->chan[ci - 2].bch;
+                       hc->chan[ci - 2].bch = NULL;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       mISDN_freebchannel(pb);
+                       kfree(pb);
+                       kfree(hc->chan[ci - 2].coeff);
+                       spin_lock_irqsave(&hc->lock, flags);
+               }
+               if (hc->chan[ci - 1].bch) {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: free port %d channel %d\n",
+                                   __func__, hc->chan[ci - 1].port+1,
+                                   ci - 1);
+                       pb = hc->chan[ci - 1].bch;
+                       hc->chan[ci - 1].bch = NULL;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       mISDN_freebchannel(pb);
+                       kfree(pb);
+                       kfree(hc->chan[ci - 1].coeff);
+                       spin_lock_irqsave(&hc->lock, flags);
+               }
+       }
+
+       spin_unlock_irqrestore(&hc->lock, flags);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: free port %d channel D\n", __func__, pt);
+       mISDN_freedchannel(dch);
+       kfree(dch);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: done!\n", __func__);
+}
+
+static void
+release_card(struct hfc_multi *hc)
+{
+       u_long  flags;
+       int     ch;
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: release card (%d) entered\n",
+                   __func__, hc->id);
+
+       spin_lock_irqsave(&hc->lock, flags);
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+
+       udelay(1000);
+
+       /* dimm leds */
+       if (hc->leds)
+               hfcmulti_leds(hc);
+
+       /* disable D-channels & B-channels */
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: disable all channels (d and b)\n",
+                   __func__);
+       for (ch = 0; ch <= 31; ch++) {
+               if (hc->chan[ch].dch)
+                       release_port(hc, hc->chan[ch].dch);
+       }
+
+       /* release hardware & irq */
+       if (hc->irq) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_WARNING "%s: free irq %d\n",
+                           __func__, hc->irq);
+               free_irq(hc->irq, hc);
+               hc->irq = 0;
+
+       }
+       release_io_hfcmulti(hc);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: remove instance from list\n",
+                    __func__);
+       list_del(&hc->list);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: delete instance\n", __func__);
+       if (hc == syncmaster)
+               syncmaster = NULL;
+       kfree(hc);
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_WARNING "%s: card successfully removed\n",
+                   __func__);
+}
+
+static int
+init_e1_port(struct hfc_multi *hc, struct hm_map *m)
+{
+       struct dchannel *dch;
+       struct bchannel *bch;
+       int             ch, ret = 0;
+       char            name[MISDN_MAX_IDLEN];
+
+       dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+       if (!dch)
+               return -ENOMEM;
+       dch->debug = debug;
+       mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+       dch->hw = hc;
+       dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+       dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       dch->dev.D.send = handle_dmsg;
+       dch->dev.D.ctrl = hfcm_dctrl;
+       dch->dev.nrbchan = (hc->dslot)?30:31;
+       dch->slot = hc->dslot;
+       hc->chan[hc->dslot].dch = dch;
+       hc->chan[hc->dslot].port = 0;
+       hc->chan[hc->dslot].nt_timer = -1;
+       for (ch = 1; ch <= 31; ch++) {
+               if (ch == hc->dslot) /* skip dchannel */
+                       continue;
+               bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+               if (!bch) {
+                       printk(KERN_ERR "%s: no memory for bchannel\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL);
+               if (!hc->chan[ch].coeff) {
+                       printk(KERN_ERR "%s: no memory for coeffs\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               bch->nr = ch;
+               bch->slot = ch;
+               bch->debug = debug;
+               mISDN_initbchannel(bch, MAX_DATA_MEM);
+               bch->hw = hc;
+               bch->ch.send = handle_bmsg;
+               bch->ch.ctrl = hfcm_bctrl;
+               bch->ch.nr = ch;
+               list_add(&bch->ch.list, &dch->dev.bchannels);
+               hc->chan[ch].bch = bch;
+               hc->chan[ch].port = 0;
+               test_and_set_bit(bch->nr, &dch->dev.channelmap[0]);
+       }
+       /* set optical line type */
+       if (port[Port_cnt] & 0x001) {
+               if (!m->opticalsupport)  {
+                       printk(KERN_INFO
+                           "This board has no optical "
+                           "support\n");
+               } else {
+                       if (debug & DEBUG_HFCMULTI_INIT)
+                               printk(KERN_DEBUG
+                                   "%s: PORT set optical "
+                                   "interfacs: card(%d) "
+                                   "port(%d)\n",
+                                   __func__,
+                                   HFC_cnt + 1, 1);
+                       test_and_set_bit(HFC_CFG_OPTICAL,
+                           &hc->chan[hc->dslot].cfg);
+               }
+       }
+       /* set LOS report */
+       if (port[Port_cnt] & 0x004) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT set "
+                           "LOS report: card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_LOS,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set AIS report */
+       if (port[Port_cnt] & 0x008) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT set "
+                           "AIS report: card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_AIS,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set SLIP report */
+       if (port[Port_cnt] & 0x010) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PORT set SLIP report: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_SLIP,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set RDI report */
+       if (port[Port_cnt] & 0x020) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PORT set RDI report: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_REPORT_RDI,
+                   &hc->chan[hc->dslot].cfg);
+       }
+       /* set CRC-4 Mode */
+       if (!(port[Port_cnt] & 0x100)) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT turn on CRC4 report:"
+                               " card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CFG_CRC4,
+                   &hc->chan[hc->dslot].cfg);
+       } else {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT turn off CRC4"
+                               " report: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+       }
+       /* set forced clock */
+       if (port[Port_cnt] & 0x0200) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT force getting clock from "
+                               "E1: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip);
+       } else
+       if (port[Port_cnt] & 0x0400) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT force putting clock to "
+                               "E1: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip);
+       }
+       /* set JATT PLL */
+       if (port[Port_cnt] & 0x0800) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG "%s: PORT disable JATT PLL on "
+                               "E1: card(%d) port(%d)\n",
+                               __func__, HFC_cnt + 1, 1);
+               test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip);
+       }
+       /* set elastic jitter buffer */
+       if (port[Port_cnt] & 0x3000) {
+               hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3;
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PORT set elastic "
+                           "buffer to %d: card(%d) port(%d)\n",
+                           __func__, hc->chan[hc->dslot].jitter,
+                           HFC_cnt + 1, 1);
+       } else
+               hc->chan[hc->dslot].jitter = 2; /* default */
+       snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1);
+       ret = mISDN_register_device(&dch->dev, name);
+       if (ret)
+               goto free_chan;
+       hc->created[0] = 1;
+       return ret;
+free_chan:
+       release_port(hc, dch);
+       return ret;
+}
+
+static int
+init_multi_port(struct hfc_multi *hc, int pt)
+{
+       struct dchannel *dch;
+       struct bchannel *bch;
+       int             ch, i, ret = 0;
+       char            name[MISDN_MAX_IDLEN];
+
+       dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+       if (!dch)
+               return -ENOMEM;
+       dch->debug = debug;
+       mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+       dch->hw = hc;
+       dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+       dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       dch->dev.D.send = handle_dmsg;
+       dch->dev.D.ctrl = hfcm_dctrl;
+       dch->dev.nrbchan = 2;
+       i = pt << 2;
+       dch->slot = i + 2;
+       hc->chan[i + 2].dch = dch;
+       hc->chan[i + 2].port = pt;
+       hc->chan[i + 2].nt_timer = -1;
+       for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+               bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+               if (!bch) {
+                       printk(KERN_ERR "%s: no memory for bchannel\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL);
+               if (!hc->chan[i + ch].coeff) {
+                       printk(KERN_ERR "%s: no memory for coeffs\n",
+                           __func__);
+                       ret = -ENOMEM;
+                       goto free_chan;
+               }
+               bch->nr = ch + 1;
+               bch->slot = i + ch;
+               bch->debug = debug;
+               mISDN_initbchannel(bch, MAX_DATA_MEM);
+               bch->hw = hc;
+               bch->ch.send = handle_bmsg;
+               bch->ch.ctrl = hfcm_bctrl;
+               bch->ch.nr = ch + 1;
+               list_add(&bch->ch.list, &dch->dev.bchannels);
+               hc->chan[i + ch].bch = bch;
+               hc->chan[i + ch].port = pt;
+               test_and_set_bit(bch->nr, &dch->dev.channelmap[0]);
+       }
+       /* set master clock */
+       if (port[Port_cnt] & 0x001) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PROTOCOL set master clock: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, pt + 1);
+               if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+                       printk(KERN_ERR "Error: Master clock "
+                           "for port(%d) of card(%d) is only"
+                           " possible with TE-mode\n",
+                           pt + 1, HFC_cnt + 1);
+                       ret = -EINVAL;
+                       goto free_chan;
+               }
+               if (hc->masterclk >= 0) {
+                       printk(KERN_ERR "Error: Master clock "
+                           "for port(%d) of card(%d) already "
+                           "defined for port(%d)\n",
+                           pt + 1, HFC_cnt + 1, hc->masterclk+1);
+                       ret = -EINVAL;
+                       goto free_chan;
+               }
+               hc->masterclk = pt;
+       }
+       /* set transmitter line to non capacitive */
+       if (port[Port_cnt] & 0x002) {
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PROTOCOL set non capacitive "
+                           "transmitter: card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, pt + 1);
+               test_and_set_bit(HFC_CFG_NONCAP_TX,
+                   &hc->chan[i + 2].cfg);
+       }
+       /* disable E-channel */
+       if (port[Port_cnt] & 0x004) {
+       if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: PROTOCOL disable E-channel: "
+                           "card(%d) port(%d)\n",
+                           __func__, HFC_cnt + 1, pt + 1);
+               test_and_set_bit(HFC_CFG_DIS_ECHANNEL,
+                   &hc->chan[i + 2].cfg);
+       }
+       snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d",
+               hc->type, HFC_cnt + 1, pt + 1);
+       ret = mISDN_register_device(&dch->dev, name);
+       if (ret)
+               goto free_chan;
+       hc->created[pt] = 1;
+       return ret;
+free_chan:
+       release_port(hc, dch);
+       return ret;
+}
+
+static int
+hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct hm_map   *m = (struct hm_map *)ent->driver_data;
+       int             ret_err = 0;
+       int             pt;
+       struct hfc_multi        *hc;
+       u_long          flags;
+       u_char          dips = 0, pmj = 0; /* dip settings, port mode Jumpers */
+
+       if (HFC_cnt >= MAX_CARDS) {
+               printk(KERN_ERR "too many cards (max=%d).\n",
+                       MAX_CARDS);
+               return -EINVAL;
+       }
+       if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) {
+               printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but "
+                   "type[%d] %d was supplied as module parameter\n",
+                   m->vendor_name, m->card_name, m->type, HFC_cnt,
+                   type[HFC_cnt] & 0xff);
+               printk(KERN_WARNING "HFC-MULTI: Load module without parameters "
+                       "first, to see cards and their types.");
+               return -EINVAL;
+       }
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n",
+                   __func__, m->vendor_name, m->card_name, m->type,
+                   type[HFC_cnt]);
+
+       /* allocate card+fifo structure */
+       hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL);
+       if (!hc) {
+               printk(KERN_ERR "No kmem for HFC-Multi card\n");
+               return -ENOMEM;
+       }
+       spin_lock_init(&hc->lock);
+       hc->mtyp = m;
+       hc->type =  m->type;
+       hc->ports = m->ports;
+       hc->id = HFC_cnt;
+       hc->pcm = pcm[HFC_cnt];
+       hc->io_mode = iomode[HFC_cnt];
+       if (dslot[HFC_cnt] < 0) {
+               hc->dslot = 0;
+               printk(KERN_INFO "HFC-E1 card has disabled D-channel, but "
+                       "31 B-channels\n");
+       } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) {
+               hc->dslot = dslot[HFC_cnt];
+               printk(KERN_INFO "HFC-E1 card has alternating D-channel on "
+                       "time slot %d\n", dslot[HFC_cnt]);
+       } else
+               hc->dslot = 16;
+
+       /* set chip specific features */
+       hc->masterclk = -1;
+       if (type[HFC_cnt] & 0x100) {
+               test_and_set_bit(HFC_CHIP_ULAW, &hc->chip);
+               silence = 0xff; /* ulaw silence */
+       } else
+               silence = 0x2a; /* alaw silence */
+       if (!(type[HFC_cnt] & 0x200))
+               test_and_set_bit(HFC_CHIP_DTMF, &hc->chip);
+
+       if (type[HFC_cnt] & 0x800)
+               test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+       if (type[HFC_cnt] & 0x1000) {
+               test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+               test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+       }
+       if (type[HFC_cnt] & 0x4000)
+               test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip);
+       if (type[HFC_cnt] & 0x8000)
+               test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip);
+       hc->slots = 32;
+       if (type[HFC_cnt] & 0x10000)
+               hc->slots = 64;
+       if (type[HFC_cnt] & 0x20000)
+               hc->slots = 128;
+       if (type[HFC_cnt] & 0x80000) {
+               test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip);
+               hc->wdcount = 0;
+               hc->wdbyte = V_GPIO_OUT2;
+               printk(KERN_NOTICE "Watchdog enabled\n");
+       }
+
+       /* setup pci, hc->slots may change due to PLXSD */
+       ret_err = setup_pci(hc, pdev, ent);
+       if (ret_err) {
+               if (hc == syncmaster)
+                       syncmaster = NULL;
+               kfree(hc);
+               return ret_err;
+       }
+
+       /* crate channels */
+       for (pt = 0; pt < hc->ports; pt++) {
+               if (Port_cnt >= MAX_PORTS) {
+                       printk(KERN_ERR "too many ports (max=%d).\n",
+                               MAX_PORTS);
+                       ret_err = -EINVAL;
+                       goto free_card;
+               }
+               if (hc->type == 1)
+                       ret_err = init_e1_port(hc, m);
+               else
+                       ret_err = init_multi_port(hc, pt);
+               if (debug & DEBUG_HFCMULTI_INIT)
+                       printk(KERN_DEBUG
+                           "%s: Registering D-channel, card(%d) port(%d)"
+                           "result %d\n",
+                           __func__, HFC_cnt + 1, pt, ret_err);
+
+               if (ret_err) {
+                       while (pt) { /* release already registered ports */
+                               pt--;
+                               release_port(hc, hc->chan[(pt << 2) + 2].dch);
+                       }
+                       goto free_card;
+               }
+               Port_cnt++;
+       }
+
+       /* disp switches */
+       switch (m->dip_type) {
+       case DIP_4S:
+               /*
+                * get DIP Setting for beroNet 1S/2S/4S cards
+                *  check if Port Jumper config matches
+                * module param 'protocol'
+                * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) +
+                * GPI 19/23 (R_GPI_IN2))
+                */
+               dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) |
+                       ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) |
+                       (~HFC_inb(hc, R_GPI_IN2) & 0x08);
+
+               /* Port mode (TE/NT) jumpers */
+               pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4)  & 0xf);
+
+               if (test_bit(HFC_CHIP_B410P, &hc->chip))
+                       pmj = ~pmj & 0xf;
+
+               printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n",
+                       m->vendor_name, m->card_name, dips, pmj);
+               break;
+       case DIP_8S:
+               /*
+                * get DIP Setting for beroNet 8S0+ cards
+                *
+                * enable PCI auxbridge function
+                */
+               HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+               /* prepare access to auxport */
+               outw(0x4000, hc->pci_iobase + 4);
+               /*
+                * some dummy reads are required to
+                * read valid DIP switch data
+                */
+               dips = inb(hc->pci_iobase);
+               dips = inb(hc->pci_iobase);
+               dips = inb(hc->pci_iobase);
+               dips = ~inb(hc->pci_iobase) & 0x3F;
+               outw(0x0, hc->pci_iobase + 4);
+               /* disable PCI auxbridge function */
+               HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+               printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+                   m->vendor_name, m->card_name, dips);
+               break;
+       case DIP_E1:
+               /*
+                * get DIP Setting for beroNet E1 cards
+                * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0)
+                */
+               dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0)>>4;
+               printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+                   m->vendor_name, m->card_name, dips);
+               break;
+       }
+
+       /* add to list */
+       spin_lock_irqsave(&HFClock, flags);
+       list_add_tail(&hc->list, &HFClist);
+       spin_unlock_irqrestore(&HFClock, flags);
+
+       /* initialize hardware */
+       ret_err = init_card(hc);
+       if (ret_err) {
+               printk(KERN_ERR "init card returns %d\n", ret_err);
+               release_card(hc);
+               return ret_err;
+       }
+
+       /* start IRQ and return */
+       spin_lock_irqsave(&hc->lock, flags);
+       enable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       return 0;
+
+free_card:
+       release_io_hfcmulti(hc);
+       if (hc == syncmaster)
+               syncmaster = NULL;
+       kfree(hc);
+       return ret_err;
+}
+
+static void __devexit hfc_remove_pci(struct pci_dev *pdev)
+{
+       struct hfc_multi        *card = pci_get_drvdata(pdev);
+       u_long                  flags;
+
+       if (debug)
+               printk(KERN_INFO "removing hfc_multi card vendor:%x "
+                   "device:%x subvendor:%x subdevice:%x\n",
+                   pdev->vendor, pdev->device,
+                   pdev->subsystem_vendor, pdev->subsystem_device);
+
+       if (card) {
+               spin_lock_irqsave(&HFClock, flags);
+               release_card(card);
+               spin_unlock_irqrestore(&HFClock, flags);
+       }  else {
+               if (debug)
+                       printk(KERN_WARNING "%s: drvdata allready removed\n",
+                           __func__);
+       }
+}
+
+#define        VENDOR_CCD      "Cologne Chip AG"
+#define        VENDOR_BN       "beroNet GmbH"
+#define        VENDOR_DIG      "Digium Inc."
+#define VENDOR_JH      "Junghanns.NET GmbH"
+#define VENDOR_PRIM    "PrimuX"
+
+static const struct hm_map hfcm_map[] = {
+/*0*/  {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0},
+/*1*/  {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S},
+/*2*/  {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0},
+/*3*/  {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0},
+/*4*/  {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0},
+/*5*/  {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0},
+/*6*/  {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, 0, 0},
+/*7*/  {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0},
+/*8*/  {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO},
+/*9*/  {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0},
+/*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0},
+/*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0},
+
+/*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0},
+/*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S,
+               HFC_IO_MODE_REGIO},
+/*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0},
+/*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0},
+
+/*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0},
+/*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+/*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+
+/*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0},
+/*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+
+/*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0},
+/*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0},
+/*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0},
+
+/*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0,
+               HFC_IO_MODE_PLXSD},
+/*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0,
+               HFC_IO_MODE_PLXSD},
+/*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0},
+/*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0},
+/*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0},
+};
+
+#undef H
+#define H(x)   ((unsigned long)&hfcm_map[x])
+static struct pci_device_id hfmultipci_ids[] __devinitdata = {
+
+       /* Cards with HFC-4S Chip */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */
+       { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S,
+               PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */
+
+       /* Cards with HFC-8S Chip */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+       PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)},
+           /* IOB8ST Recording */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST  */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST  */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */
+
+
+       /* Cards with HFC-E1 Chip */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */
+
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */
+
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+               PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_ANY_ID, PCI_ANY_ID,
+               0, 0, 0},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_ANY_ID, PCI_ANY_ID,
+               0, 0, 0},
+       { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_ANY_ID, PCI_ANY_ID,
+               0, 0, 0},
+       {0, }
+};
+#undef H
+
+MODULE_DEVICE_TABLE(pci, hfmultipci_ids);
+
+static int
+hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct hm_map   *m = (struct hm_map *)ent->driver_data;
+       int             ret;
+
+       if (m == NULL) {
+               if (ent->vendor == PCI_VENDOR_ID_CCD)
+                       if (ent->device == PCI_DEVICE_ID_CCD_HFC4S ||
+                           ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
+                           ent->device == PCI_DEVICE_ID_CCD_HFCE1)
+                               printk(KERN_ERR
+                                   "unknown HFC multiport controller "
+                                   "(vendor:%x device:%x subvendor:%x "
+                                   "subdevice:%x) Please contact the "
+                                   "driver maintainer for support.\n",
+                                   ent->vendor, ent->device,
+                                   ent->subvendor, ent->subdevice);
+               return -ENODEV;
+       }
+       ret = hfcmulti_init(pdev, ent);
+       if (ret)
+               return ret;
+       HFC_cnt++;
+       printk(KERN_INFO "%d devices registered\n", HFC_cnt);
+       return 0;
+}
+
+static struct pci_driver hfcmultipci_driver = {
+       .name           = "hfc_multi",
+       .probe          = hfcmulti_probe,
+       .remove         = __devexit_p(hfc_remove_pci),
+       .id_table       = hfmultipci_ids,
+};
+
+static void __exit
+HFCmulti_cleanup(void)
+{
+       struct hfc_multi *card, *next;
+
+       /* unload interrupt function symbol */
+       if (hfc_interrupt)
+               symbol_put(ztdummy_extern_interrupt);
+       if (register_interrupt)
+               symbol_put(ztdummy_register_interrupt);
+       if (unregister_interrupt) {
+               if (interrupt_registered) {
+                       interrupt_registered = 0;
+                       unregister_interrupt();
+               }
+               symbol_put(ztdummy_unregister_interrupt);
+       }
+
+       list_for_each_entry_safe(card, next, &HFClist, list)
+               release_card(card);
+       /* get rid of all devices of this driver */
+       pci_unregister_driver(&hfcmultipci_driver);
+}
+
+static int __init
+HFCmulti_init(void)
+{
+       int err;
+
+#ifdef IRQ_DEBUG
+       printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__);
+#endif
+
+       spin_lock_init(&HFClock);
+       spin_lock_init(&plx_lock);
+
+       if (debug & DEBUG_HFCMULTI_INIT)
+               printk(KERN_DEBUG "%s: init entered\n", __func__);
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+       hfc_interrupt = symbol_get(ztdummy_extern_interrupt);
+       register_interrupt = symbol_get(ztdummy_register_interrupt);
+       unregister_interrupt = symbol_get(ztdummy_unregister_interrupt);
+       printk(KERN_INFO "mISDN: HFC-multi driver %s\n",
+           hfcmulti_revision);
+
+       switch (poll) {
+       case 0:
+               poll_timer = 6;
+               poll = 128;
+               break;
+               /*
+                * wenn dieses break nochmal verschwindet,
+                * gibt es heisse ohren :-)
+                * "without the break you will get hot ears ???"
+                */
+       case 8:
+               poll_timer = 2;
+               break;
+       case 16:
+               poll_timer = 3;
+               break;
+       case 32:
+               poll_timer = 4;
+               break;
+       case 64:
+               poll_timer = 5;
+               break;
+       case 128:
+               poll_timer = 6;
+               break;
+       case 256:
+               poll_timer = 7;
+               break;
+       default:
+               printk(KERN_ERR
+                   "%s: Wrong poll value (%d).\n", __func__, poll);
+               err = -EINVAL;
+               return err;
+
+       }
+
+       err = pci_register_driver(&hfcmultipci_driver);
+       if (err < 0) {
+               printk(KERN_ERR "error registering pci driver: %x\n", err);
+               if (hfc_interrupt)
+                       symbol_put(ztdummy_extern_interrupt);
+               if (register_interrupt)
+                       symbol_put(ztdummy_register_interrupt);
+               if (unregister_interrupt) {
+                       if (interrupt_registered) {
+                               interrupt_registered = 0;
+                               unregister_interrupt();
+                       }
+                       symbol_put(ztdummy_unregister_interrupt);
+               }
+               return err;
+       }
+       return 0;
+}
+
+
+module_init(HFCmulti_init);
+module_exit(HFCmulti_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
new file mode 100644 (file)
index 0000000..9179685
--- /dev/null
@@ -0,0 +1,2256 @@
+/*
+ *
+ * hfcpci.c     low level driver for CCD's hfc-pci based cards
+ *
+ * Author     Werner Cornelius (werner@isdn4linux.de)
+ *            based on existing driver for CCD hfc ISA cards
+ *            type approval valid for HFC-S PCI A based card
+ *
+ * Copyright 1999  by Werner Cornelius (werner@isdn-development.de)
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+
+#include "hfc_pci.h"
+
+static const char *hfcpci_revision = "2.0";
+
+#define MAX_CARDS      8
+static int HFC_cnt;
+static uint debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, 0);
+
+static LIST_HEAD(HFClist);
+DEFINE_RWLOCK(HFClock);
+
+enum {
+       HFC_CCD_2BD0,
+       HFC_CCD_B000,
+       HFC_CCD_B006,
+       HFC_CCD_B007,
+       HFC_CCD_B008,
+       HFC_CCD_B009,
+       HFC_CCD_B00A,
+       HFC_CCD_B00B,
+       HFC_CCD_B00C,
+       HFC_CCD_B100,
+       HFC_CCD_B700,
+       HFC_CCD_B701,
+       HFC_ASUS_0675,
+       HFC_BERKOM_A1T,
+       HFC_BERKOM_TCONCEPT,
+       HFC_ANIGMA_MC145575,
+       HFC_ZOLTRIX_2BD0,
+       HFC_DIGI_DF_M_IOM2_E,
+       HFC_DIGI_DF_M_E,
+       HFC_DIGI_DF_M_IOM2_A,
+       HFC_DIGI_DF_M_A,
+       HFC_ABOCOM_2BD1,
+       HFC_SITECOM_DC105V2,
+};
+
+struct hfcPCI_hw {
+       unsigned char           cirm;
+       unsigned char           ctmt;
+       unsigned char           clkdel;
+       unsigned char           states;
+       unsigned char           conn;
+       unsigned char           mst_m;
+       unsigned char           int_m1;
+       unsigned char           int_m2;
+       unsigned char           sctrl;
+       unsigned char           sctrl_r;
+       unsigned char           sctrl_e;
+       unsigned char           trm;
+       unsigned char           fifo_en;
+       unsigned char           bswapped;
+       unsigned char           protocol;
+       int                     nt_timer;
+       unsigned char           *pci_io; /* start of PCI IO memory */
+       dma_addr_t              dmahandle;
+       void                    *fifos; /* FIFO memory */
+       int                     last_bfifo_cnt[2];
+           /* marker saving last b-fifo frame count */
+       struct timer_list       timer;
+};
+
+#define        HFC_CFG_MASTER          1
+#define HFC_CFG_SLAVE          2
+#define        HFC_CFG_PCM             3
+#define HFC_CFG_2HFC           4
+#define HFC_CFG_SLAVEHFC       5
+#define HFC_CFG_NEG_F0         6
+#define HFC_CFG_SW_DD_DU       7
+
+#define FLG_HFC_TIMER_T1       16
+#define FLG_HFC_TIMER_T3       17
+
+#define NT_T1_COUNT    1120    /* number of 3.125ms interrupts (3.5s) */
+#define NT_T3_COUNT    31      /* number of 3.125ms interrupts (97 ms) */
+#define CLKDEL_TE      0x0e    /* CLKDEL in TE mode */
+#define CLKDEL_NT      0x6c    /* CLKDEL in NT mode */
+
+
+struct hfc_pci {
+       struct list_head        list;
+       u_char                  subtype;
+       u_char                  chanlimit;
+       u_char                  initdone;
+       u_long                  cfg;
+       u_int                   irq;
+       u_int                   irqcnt;
+       struct pci_dev          *pdev;
+       struct hfcPCI_hw        hw;
+       spinlock_t              lock;   /* card lock */
+       struct dchannel         dch;
+       struct bchannel         bch[2];
+};
+
+/* Interface functions */
+static void
+enable_hwirq(struct hfc_pci *hc)
+{
+       hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE;
+       Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+static void
+disable_hwirq(struct hfc_pci *hc)
+{
+       hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE);
+       Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcpci(struct hfc_pci *hc)
+{
+       /* disable memory mapped ports + busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND, 0);
+       del_timer(&hc->hw.timer);
+       pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos, hc->hw.dmahandle);
+       iounmap((void *)hc->hw.pci_io);
+}
+
+/*
+ * set mode (NT or TE)
+ */
+static void
+hfcpci_setmode(struct hfc_pci *hc)
+{
+       if (hc->hw.protocol == ISDN_P_NT_S0) {
+               hc->hw.clkdel = CLKDEL_NT;      /* ST-Bit delay for NT-Mode */
+               hc->hw.sctrl |= SCTRL_MODE_NT;  /* NT-MODE */
+               hc->hw.states = 1;              /* G1 */
+       } else {
+               hc->hw.clkdel = CLKDEL_TE;      /* ST-Bit delay for TE-Mode */
+               hc->hw.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */
+               hc->hw.states = 2;              /* F2 */
+       }
+       Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel);
+       Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states);
+       udelay(10);
+       Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */
+       Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
+}
+
+/*
+ * function called to reset the HFC PCI chip. A complete software reset of chip
+ * and fifos is done.
+ */
+static void
+reset_hfcpci(struct hfc_pci *hc)
+{
+       u_char  val;
+       int     cnt = 0;
+
+       printk(KERN_DEBUG "reset_hfcpci: entered\n");
+       val = Read_hfc(hc, HFCPCI_CHIP_ID);
+       printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val);
+       /* enable memory mapped ports, disable busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+       disable_hwirq(hc);
+       /* enable memory ports + busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND,
+           PCI_ENA_MEMIO + PCI_ENA_MASTER);
+       val = Read_hfc(hc, HFCPCI_STATUS);
+       printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val);
+       hc->hw.cirm = HFCPCI_RESET;     /* Reset On */
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       mdelay(10);                     /* Timeout 10ms */
+       hc->hw.cirm = 0;                /* Reset Off */
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+       val = Read_hfc(hc, HFCPCI_STATUS);
+       printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val);
+       while (cnt < 50000) { /* max 50000 us */
+               udelay(5);
+               cnt += 5;
+               val = Read_hfc(hc, HFCPCI_STATUS);
+               if (!(val & 2))
+                       break;
+       }
+       printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt);
+
+       hc->hw.fifo_en = 0x30;  /* only D fifos enabled */
+
+       hc->hw.bswapped = 0;    /* no exchange */
+       hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
+       hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */
+       hc->hw.sctrl = 0x40;    /* set tx_lo mode, error in datasheet ! */
+       hc->hw.sctrl_r = 0;
+       hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE;     /* S/T Auto awake */
+       hc->hw.mst_m = 0;
+       if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+               hc->hw.mst_m |= HFCPCI_MASTER;  /* HFC Master Mode */
+       if (test_bit(HFC_CFG_NEG_F0, &hc->cfg))
+               hc->hw.mst_m |= HFCPCI_F0_NEGATIV;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+       Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
+       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+
+       hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+           HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+
+       /* Clear already pending ints */
+       if (Read_hfc(hc, HFCPCI_INT_S1));
+
+       /* set NT/TE mode */
+       hfcpci_setmode(hc);
+
+       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+       Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+
+       /*
+        * Init GCI/IOM2 in master mode
+        * Slots 0 and 1 are set for B-chan 1 and 2
+        * D- and monitor/CI channel are not enabled
+        * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC
+        * STIO2 is used as data input, B1+B2 from IOM->ST
+        * ST B-channel send disabled -> continous 1s
+        * The IOM slots are always enabled
+        */
+       if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
+               /* set data flow directions: connect B1,B2: HFC to/from PCM */
+               hc->hw.conn = 0x09;
+       } else {
+               hc->hw.conn = 0x36;     /* set data flow directions */
+               if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
+                       Write_hfc(hc, HFCPCI_B1_SSL, 0xC0);
+                       Write_hfc(hc, HFCPCI_B2_SSL, 0xC1);
+                       Write_hfc(hc, HFCPCI_B1_RSL, 0xC0);
+                       Write_hfc(hc, HFCPCI_B2_RSL, 0xC1);
+               } else {
+                       Write_hfc(hc, HFCPCI_B1_SSL, 0x80);
+                       Write_hfc(hc, HFCPCI_B2_SSL, 0x81);
+                       Write_hfc(hc, HFCPCI_B1_RSL, 0x80);
+                       Write_hfc(hc, HFCPCI_B2_RSL, 0x81);
+               }
+       }
+       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+       val = Read_hfc(hc, HFCPCI_INT_S2);
+}
+
+/*
+ * Timer function called when kernel timer expires
+ */
+static void
+hfcpci_Timer(struct hfc_pci *hc)
+{
+       hc->hw.timer.expires = jiffies + 75;
+       /* WD RESET */
+/*
+ *     WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80);
+ *     add_timer(&hc->hw.timer);
+ */
+}
+
+
+/*
+ * select a b-channel entry matching and active
+ */
+static struct bchannel *
+Sel_BCS(struct hfc_pci *hc, int channel)
+{
+       if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) &&
+               (hc->bch[0].nr & channel))
+               return &hc->bch[0];
+       else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) &&
+               (hc->bch[1].nr & channel))
+               return &hc->bch[1];
+       else
+               return NULL;
+}
+
+/*
+ * clear the desired B-channel rx fifo
+ */
+static void
+hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo)
+{
+       u_char          fifo_state;
+       struct bzfifo   *bzr;
+
+       if (fifo) {
+               bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX;
+       } else {
+               bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX;
+       }
+       if (fifo_state)
+               hc->hw.fifo_en ^= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       hc->hw.last_bfifo_cnt[fifo] = 0;
+       bzr->f1 = MAX_B_FRAMES;
+       bzr->f2 = bzr->f1;      /* init F pointers to remain constant */
+       bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+       bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16(
+           le16_to_cpu(bzr->za[MAX_B_FRAMES].z1));
+       if (fifo_state)
+               hc->hw.fifo_en |= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+}
+
+/*
+ * clear the desired B-channel tx fifo
+ */
+static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo)
+{
+       u_char          fifo_state;
+       struct bzfifo   *bzt;
+
+       if (fifo) {
+               bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX;
+       } else {
+               bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1;
+               fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX;
+       }
+       if (fifo_state)
+               hc->hw.fifo_en ^= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) "
+                   "z1(%x) z2(%x) state(%x)\n",
+                   fifo, bzt->f1, bzt->f2,
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z2),
+                   fifo_state);
+       bzt->f2 = MAX_B_FRAMES;
+       bzt->f1 = bzt->f2;      /* init F pointers to remain constant */
+       bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+       bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(
+           le16_to_cpu(bzt->za[MAX_B_FRAMES].z1 - 1));
+       if (fifo_state)
+               hc->hw.fifo_en |= fifo_state;
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n",
+                   fifo, bzt->f1, bzt->f2,
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+                   le16_to_cpu(bzt->za[MAX_B_FRAMES].z2));
+}
+
+/*
+ * read a complete B-frame out of the buffer
+ */
+static void
+hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz,
+    u_char *bdata, int count)
+{
+       u_char          *ptr, *ptr1, new_f2;
+       int             total, maxlen, new_z2;
+       struct zt       *zp;
+
+       if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+               printk(KERN_DEBUG "hfcpci_empty_fifo\n");
+       zp = &bz->za[bz->f2];   /* point to Z-Regs */
+       new_z2 = le16_to_cpu(zp->z2) + count;   /* new position in fifo */
+       if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+               new_z2 -= B_FIFO_SIZE;  /* buffer wrap */
+       new_f2 = (bz->f2 + 1) & MAX_B_FRAMES;
+       if ((count > MAX_DATA_SIZE + 3) || (count < 4) ||
+           (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) {
+               if (bch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet "
+                           "invalid length %d or crc\n", count);
+#ifdef ERROR_STATISTIC
+               bch->err_inv++;
+#endif
+               bz->za[new_f2].z2 = cpu_to_le16(new_z2);
+               bz->f2 = new_f2;        /* next buffer */
+       } else {
+               bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC);
+               if (!bch->rx_skb) {
+                       printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+                       return;
+               }
+               total = count;
+               count -= 3;
+               ptr = skb_put(bch->rx_skb, count);
+
+               if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL)
+                       maxlen = count;         /* complete transfer */
+               else
+                       maxlen = B_FIFO_SIZE + B_SUB_VAL -
+                           le16_to_cpu(zp->z2);        /* maximum */
+
+               ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL);
+                   /* start of data */
+               memcpy(ptr, ptr1, maxlen);      /* copy data */
+               count -= maxlen;
+
+               if (count) {    /* rest remaining */
+                       ptr += maxlen;
+                       ptr1 = bdata;   /* start of buffer */
+                       memcpy(ptr, ptr1, count);       /* rest */
+               }
+               bz->za[new_f2].z2 = cpu_to_le16(new_z2);
+               bz->f2 = new_f2;        /* next buffer */
+               recv_Bchannel(bch);
+       }
+}
+
+/*
+ * D-channel receive procedure
+ */
+static int
+receive_dmsg(struct hfc_pci *hc)
+{
+       struct dchannel *dch = &hc->dch;
+       int             maxlen;
+       int             rcnt, total;
+       int             count = 5;
+       u_char          *ptr, *ptr1;
+       struct dfifo    *df;
+       struct zt       *zp;
+
+       df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx;
+       while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) {
+               zp = &df->za[df->f2 & D_FREG_MASK];
+               rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+               if (rcnt < 0)
+                       rcnt += D_FIFO_SIZE;
+               rcnt++;
+               if (dch->debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n",
+                               df->f1, df->f2,
+                               le16_to_cpu(zp->z1),
+                               le16_to_cpu(zp->z2),
+                               rcnt);
+
+               if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) ||
+                   (df->data[le16_to_cpu(zp->z1)])) {
+                       if (dch->debug & DEBUG_HW)
+                               printk(KERN_DEBUG
+                                   "empty_fifo hfcpci paket inv. len "
+                                   "%d or crc %d\n",
+                                   rcnt,
+                                   df->data[le16_to_cpu(zp->z1)]);
+#ifdef ERROR_STATISTIC
+                       cs->err_rx++;
+#endif
+                       df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) |
+                           (MAX_D_FRAMES + 1); /* next buffer */
+                       df->za[df->f2 & D_FREG_MASK].z2 =
+                           cpu_to_le16((zp->z2 + rcnt) & (D_FIFO_SIZE - 1));
+               } else {
+                       dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC);
+                       if (!dch->rx_skb) {
+                               printk(KERN_WARNING
+                                   "HFC-PCI: D receive out of memory\n");
+                               break;
+                       }
+                       total = rcnt;
+                       rcnt -= 3;
+                       ptr = skb_put(dch->rx_skb, rcnt);
+
+                       if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE)
+                               maxlen = rcnt;  /* complete transfer */
+                       else
+                               maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2);
+                                   /* maximum */
+
+                       ptr1 = df->data + le16_to_cpu(zp->z2);
+                           /* start of data */
+                       memcpy(ptr, ptr1, maxlen);      /* copy data */
+                       rcnt -= maxlen;
+
+                       if (rcnt) {     /* rest remaining */
+                               ptr += maxlen;
+                               ptr1 = df->data;        /* start of buffer */
+                               memcpy(ptr, ptr1, rcnt);        /* rest */
+                       }
+                       df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) |
+                           (MAX_D_FRAMES + 1); /* next buffer */
+                       df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((
+                           le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1));
+                       recv_Dchannel(dch);
+               }
+       }
+       return 1;
+}
+
+/*
+ * check for transparent receive data and read max one threshold size if avail
+ */
+int
+hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata)
+{
+       unsigned short  *z1r, *z2r;
+       int             new_z2, fcnt, maxlen;
+       u_char          *ptr, *ptr1;
+
+       z1r = &bz->za[MAX_B_FRAMES].z1;         /* pointer to z reg */
+       z2r = z1r + 1;
+
+       fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r);
+       if (!fcnt)
+               return 0;       /* no data avail */
+
+       if (fcnt <= 0)
+               fcnt += B_FIFO_SIZE;    /* bytes actually buffered */
+       if (fcnt > HFCPCI_BTRANS_THRESHOLD)
+               fcnt = HFCPCI_BTRANS_THRESHOLD;         /* limit size */
+
+       new_z2 = le16_to_cpu(*z2r) + fcnt;      /* new position in fifo */
+       if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+               new_z2 -= B_FIFO_SIZE;  /* buffer wrap */
+
+       bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC);
+       if (bch->rx_skb) {
+               ptr = skb_put(bch->rx_skb, fcnt);
+               if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
+                       maxlen = fcnt;  /* complete transfer */
+               else
+                       maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r);
+                           /* maximum */
+
+               ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL);
+                   /* start of data */
+               memcpy(ptr, ptr1, maxlen);      /* copy data */
+               fcnt -= maxlen;
+
+               if (fcnt) {     /* rest remaining */
+                       ptr += maxlen;
+                       ptr1 = bdata;   /* start of buffer */
+                       memcpy(ptr, ptr1, fcnt);        /* rest */
+               }
+               recv_Bchannel(bch);
+       } else
+               printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+
+       *z2r = cpu_to_le16(new_z2);             /* new position */
+       return 1;
+}
+
+/*
+ * B-channel main receive routine
+ */
+void
+main_rec_hfcpci(struct bchannel *bch)
+{
+       struct hfc_pci  *hc = bch->hw;
+       int             rcnt, real_fifo;
+       int             receive, count = 5;
+       struct bzfifo   *bz;
+       u_char          *bdata;
+       struct zt       *zp;
+
+
+       if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2;
+               real_fifo = 1;
+       } else {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1;
+               real_fifo = 0;
+       }
+Begin:
+       count--;
+       if (bz->f1 != bz->f2) {
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n",
+                           bch->nr, bz->f1, bz->f2);
+               zp = &bz->za[bz->f2];
+
+               rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+               if (rcnt < 0)
+                       rcnt += B_FIFO_SIZE;
+               rcnt++;
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n",
+                           bch->nr, le16_to_cpu(zp->z1),
+                           le16_to_cpu(zp->z2), rcnt);
+               hfcpci_empty_bfifo(bch, bz, bdata, rcnt);
+               rcnt = bz->f1 - bz->f2;
+               if (rcnt < 0)
+                       rcnt += MAX_B_FRAMES + 1;
+               if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) {
+                       rcnt = 0;
+                       hfcpci_clear_fifo_rx(hc, real_fifo);
+               }
+               hc->hw.last_bfifo_cnt[real_fifo] = rcnt;
+               if (rcnt > 1)
+                       receive = 1;
+               else
+                       receive = 0;
+       } else if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+               receive = hfcpci_empty_fifo_trans(bch, bz, bdata);
+       else
+               receive = 0;
+       if (count && receive)
+               goto Begin;
+
+}
+
+/*
+ * D-channel send routine
+ */
+static void
+hfcpci_fill_dfifo(struct hfc_pci *hc)
+{
+       struct dchannel *dch = &hc->dch;
+       int             fcnt;
+       int             count, new_z1, maxlen;
+       struct dfifo    *df;
+       u_char          *src, *dst, new_f1;
+
+       if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO))
+               printk(KERN_DEBUG "%s\n", __func__);
+
+       if (!dch->tx_skb)
+               return;
+       count = dch->tx_skb->len - dch->tx_idx;
+       if (count <= 0)
+               return;
+       df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx;
+
+       if (dch->debug & DEBUG_HW_DFIFO)
+               printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__,
+                   df->f1, df->f2,
+                   le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1));
+       fcnt = df->f1 - df->f2; /* frame count actually buffered */
+       if (fcnt < 0)
+               fcnt += (MAX_D_FRAMES + 1);     /* if wrap around */
+       if (fcnt > (MAX_D_FRAMES - 1)) {
+               if (dch->debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci_fill_Dfifo more as 14 frames\n");
+#ifdef ERROR_STATISTIC
+               cs->err_tx++;
+#endif
+               return;
+       }
+       /* now determine free bytes in FIFO buffer */
+       maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) -
+           le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1;
+       if (maxlen <= 0)
+               maxlen += D_FIFO_SIZE;  /* count now contains available bytes */
+
+       if (dch->debug & DEBUG_HW_DCHANNEL)
+               printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n",
+                       count, maxlen);
+       if (count > maxlen) {
+               if (dch->debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n");
+               return;
+       }
+       new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) &
+           (D_FIFO_SIZE - 1);
+       new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1);
+       src = dch->tx_skb->data + dch->tx_idx;  /* source pointer */
+       dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
+       maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
+           /* end fifo */
+       if (maxlen > count)
+               maxlen = count; /* limit size */
+       memcpy(dst, src, maxlen);       /* first copy */
+
+       count -= maxlen;        /* remaining bytes */
+       if (count) {
+               dst = df->data; /* start of buffer */
+               src += maxlen;  /* new position */
+               memcpy(dst, src, count);
+       }
+       df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1);
+           /* for next buffer */
+       df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1);
+           /* new pos actual buffer */
+       df->f1 = new_f1;        /* next frame */
+       dch->tx_idx = dch->tx_skb->len;
+}
+
+/*
+ * B-channel send routine
+ */
+static void
+hfcpci_fill_fifo(struct bchannel *bch)
+{
+       struct hfc_pci  *hc = bch->hw;
+       int             maxlen, fcnt;
+       int             count, new_z1;
+       struct bzfifo   *bz;
+       u_char          *bdata;
+       u_char          new_f1, *src, *dst;
+       unsigned short  *z1t, *z2t;
+
+       if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+               printk(KERN_DEBUG "%s\n", __func__);
+       if ((!bch->tx_skb) || bch->tx_skb->len <= 0)
+               return;
+       count = bch->tx_skb->len - bch->tx_idx;
+       if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2;
+       } else {
+               bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1;
+               bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1;
+       }
+
+       if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+               z1t = &bz->za[MAX_B_FRAMES].z1;
+               z2t = z1t + 1;
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) "
+                           "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count,
+                           le16_to_cpu(*z1t), le16_to_cpu(*z2t));
+               fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t);
+               if (fcnt <= 0)
+                       fcnt += B_FIFO_SIZE;
+                           /* fcnt contains available bytes in fifo */
+               fcnt = B_FIFO_SIZE - fcnt;
+                   /* remaining bytes to send (bytes in fifo) */
+next_t_frame:
+               count = bch->tx_skb->len - bch->tx_idx;
+               /* maximum fill shall be HFCPCI_BTRANS_MAX */
+               if (count > HFCPCI_BTRANS_MAX - fcnt)
+                       count = HFCPCI_BTRANS_MAX - fcnt;
+               if (count <= 0)
+                       return;
+               /* data is suitable for fifo */
+               new_z1 = le16_to_cpu(*z1t) + count;
+                   /* new buffer Position */
+               if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+                       new_z1 -= B_FIFO_SIZE;  /* buffer wrap */
+               src = bch->tx_skb->data + bch->tx_idx;
+                   /* source pointer */
+               dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL);
+               maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t);
+                   /* end of fifo */
+               if (bch->debug & DEBUG_HW_BFIFO)
+                       printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) "
+                           "maxl(%d) nz1(%x) dst(%p)\n",
+                           fcnt, maxlen, new_z1, dst);
+               fcnt += count;
+               bch->tx_idx += count;
+               if (maxlen > count)
+                       maxlen = count;         /* limit size */
+               memcpy(dst, src, maxlen);       /* first copy */
+               count -= maxlen;        /* remaining bytes */
+               if (count) {
+                       dst = bdata;    /* start of buffer */
+                       src += maxlen;  /* new position */
+                       memcpy(dst, src, count);
+               }
+               *z1t = cpu_to_le16(new_z1);     /* now send data */
+               if (bch->tx_idx < bch->tx_skb->len)
+                       return;
+               /* send confirm, on trans, free on hdlc. */
+               if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+                       confirm_Bsend(bch);
+               dev_kfree_skb(bch->tx_skb);
+               if (get_next_bframe(bch))
+                       goto next_t_frame;
+               return;
+       }
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n",
+                   __func__, bch->nr, bz->f1, bz->f2,
+                   bz->za[bz->f1].z1);
+       fcnt = bz->f1 - bz->f2; /* frame count actually buffered */
+       if (fcnt < 0)
+               fcnt += (MAX_B_FRAMES + 1);     /* if wrap around */
+       if (fcnt > (MAX_B_FRAMES - 1)) {
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG
+                           "hfcpci_fill_Bfifo more as 14 frames\n");
+               return;
+       }
+       /* now determine free bytes in FIFO buffer */
+       maxlen = le16_to_cpu(bz->za[bz->f2].z2) -
+           le16_to_cpu(bz->za[bz->f1].z1) - 1;
+       if (maxlen <= 0)
+               maxlen += B_FIFO_SIZE;  /* count now contains available bytes */
+
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n",
+                       bch->nr, count, maxlen);
+
+       if (maxlen < count) {
+               if (bch->debug & DEBUG_HW_BCHANNEL)
+                       printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n");
+               return;
+       }
+       new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count;
+           /* new buffer Position */
+       if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+               new_z1 -= B_FIFO_SIZE;  /* buffer wrap */
+
+       new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES);
+       src = bch->tx_skb->data + bch->tx_idx;  /* source pointer */
+       dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL);
+       maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1);
+           /* end fifo */
+       if (maxlen > count)
+               maxlen = count; /* limit size */
+       memcpy(dst, src, maxlen);       /* first copy */
+
+       count -= maxlen;        /* remaining bytes */
+       if (count) {
+               dst = bdata;    /* start of buffer */
+               src += maxlen;  /* new position */
+               memcpy(dst, src, count);
+       }
+       bz->za[new_f1].z1 = cpu_to_le16(new_z1);        /* for next buffer */
+       bz->f1 = new_f1;        /* next frame */
+       dev_kfree_skb(bch->tx_skb);
+       get_next_bframe(bch);
+}
+
+
+
+/*
+ * handle L1 state changes TE
+ */
+
+static void
+ph_state_te(struct dchannel *dch)
+{
+       if (dch->debug)
+               printk(KERN_DEBUG "%s: TE newstate %x\n",
+                       __func__, dch->state);
+       switch (dch->state) {
+       case 0:
+               l1_event(dch->l1, HW_RESET_IND);
+               break;
+       case 3:
+               l1_event(dch->l1, HW_DEACT_IND);
+               break;
+       case 5:
+       case 8:
+               l1_event(dch->l1, ANYSIGNAL);
+               break;
+       case 6:
+               l1_event(dch->l1, INFO2);
+               break;
+       case 7:
+               l1_event(dch->l1, INFO4_P8);
+               break;
+       }
+}
+
+/*
+ * handle L1 state changes NT
+ */
+
+static void
+handle_nt_timer3(struct dchannel *dch) {
+       struct hfc_pci  *hc = dch->hw;
+
+       test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+       hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       hc->hw.nt_timer = 0;
+       test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+       if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+               hc->hw.mst_m |= HFCPCI_MASTER;
+       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+       _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+           MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+}
+
+static void
+ph_state_nt(struct dchannel *dch)
+{
+       struct hfc_pci  *hc = dch->hw;
+
+       if (dch->debug)
+               printk(KERN_DEBUG "%s: NT newstate %x\n",
+                       __func__, dch->state);
+       switch (dch->state) {
+       case 2:
+               if (hc->hw.nt_timer < 0) {
+                       hc->hw.nt_timer = 0;
+                       test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+                       test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+                       hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+                       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+                       /* Clear already pending ints */
+                       if (Read_hfc(hc, HFCPCI_INT_S1));
+                       Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
+                       udelay(10);
+                       Write_hfc(hc, HFCPCI_STATES, 4);
+                       dch->state = 4;
+               } else if (hc->hw.nt_timer == 0) {
+                       hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+                       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+                       hc->hw.nt_timer = NT_T1_COUNT;
+                       hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+                       hc->hw.ctmt |= HFCPCI_TIM3_125;
+                       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+                               HFCPCI_CLTIMER);
+                       test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+                       test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+                       /* allow G2 -> G3 transition */
+                       Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+               } else {
+                       Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+               }
+               break;
+       case 1:
+               hc->hw.nt_timer = 0;
+               test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+               test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+               hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+               Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               hc->hw.mst_m &= ~HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+               break;
+       case 4:
+               hc->hw.nt_timer = 0;
+               test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+               test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+               hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+               Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+               break;
+       case 3:
+               if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) {
+                       if (!test_and_clear_bit(FLG_L2_ACTIVATED,
+                           &dch->Flags)) {
+                               handle_nt_timer3(dch);
+                               break;
+                       }
+                       test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+                       hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+                       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+                       hc->hw.nt_timer = NT_T3_COUNT;
+                       hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+                       hc->hw.ctmt |= HFCPCI_TIM3_125;
+                       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+                               HFCPCI_CLTIMER);
+               }
+               break;
+       }
+}
+
+static void
+ph_state(struct dchannel *dch)
+{
+       struct hfc_pci  *hc = dch->hw;
+
+       if (hc->hw.protocol == ISDN_P_NT_S0) {
+               if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) &&
+                   hc->hw.nt_timer < 0)
+                       handle_nt_timer3(dch);
+               else
+                       ph_state_nt(dch);
+       } else
+               ph_state_te(dch);
+}
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfc_l1callback(struct dchannel *dch, u_int cmd)
+{
+       struct hfc_pci          *hc = dch->hw;
+
+       switch (cmd) {
+       case INFO3_P8:
+       case INFO3_P10:
+               if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+                       hc->hw.mst_m |= HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               break;
+       case HW_RESET_REQ:
+               Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3);
+               /* HFC ST 3 */
+               udelay(6);
+               Write_hfc(hc, HFCPCI_STATES, 3);        /* HFC ST 2 */
+               if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+                       hc->hw.mst_m |= HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE |
+                  HFCPCI_DO_ACTION);
+               l1_event(dch->l1, HW_POWERUP_IND);
+               break;
+       case HW_DEACT_REQ:
+               hc->hw.mst_m &= ~HFCPCI_MASTER;
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               skb_queue_purge(&dch->squeue);
+               if (dch->tx_skb) {
+                       dev_kfree_skb(dch->tx_skb);
+                       dch->tx_skb = NULL;
+               }
+               dch->tx_idx = 0;
+               if (dch->rx_skb) {
+                       dev_kfree_skb(dch->rx_skb);
+                       dch->rx_skb = NULL;
+               }
+               test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                       del_timer(&dch->timer);
+               break;
+       case HW_POWERUP_REQ:
+               Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Interrupt handler
+ */
+static inline void
+tx_birq(struct bchannel *bch)
+{
+       if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+               hfcpci_fill_fifo(bch);
+       else {
+               if (bch->tx_skb)
+                       dev_kfree_skb(bch->tx_skb);
+               if (get_next_bframe(bch))
+                       hfcpci_fill_fifo(bch);
+       }
+}
+
+static inline void
+tx_dirq(struct dchannel *dch)
+{
+       if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len)
+               hfcpci_fill_dfifo(dch->hw);
+       else {
+               if (dch->tx_skb)
+                       dev_kfree_skb(dch->tx_skb);
+               if (get_next_dframe(dch))
+                       hfcpci_fill_dfifo(dch->hw);
+       }
+}
+
+static irqreturn_t
+hfcpci_int(int intno, void *dev_id)
+{
+       struct hfc_pci  *hc = dev_id;
+       u_char          exval;
+       struct bchannel *bch;
+       u_char          val, stat;
+
+       spin_lock(&hc->lock);
+       if (!(hc->hw.int_m2 & 0x08)) {
+               spin_unlock(&hc->lock);
+               return IRQ_NONE; /* not initialised */
+       }
+       stat = Read_hfc(hc, HFCPCI_STATUS);
+       if (HFCPCI_ANYINT & stat) {
+               val = Read_hfc(hc, HFCPCI_INT_S1);
+               if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG
+                           "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val);
+       } else {
+               /* shared */
+               spin_unlock(&hc->lock);
+               return IRQ_NONE;
+       }
+       hc->irqcnt++;
+
+       if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+               printk(KERN_DEBUG "HFC-PCI irq %x\n", val);
+       val &= hc->hw.int_m1;
+       if (val & 0x40) {       /* state machine irq */
+               exval = Read_hfc(hc, HFCPCI_STATES) & 0xf;
+               if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+                       printk(KERN_DEBUG "ph_state chg %d->%d\n",
+                               hc->dch.state, exval);
+               hc->dch.state = exval;
+               schedule_event(&hc->dch, FLG_PHCHANGE);
+               val &= ~0x40;
+       }
+       if (val & 0x80) {       /* timer irq */
+               if (hc->hw.protocol == ISDN_P_NT_S0) {
+                       if ((--hc->hw.nt_timer) < 0)
+                               schedule_event(&hc->dch, FLG_PHCHANGE);
+               }
+               val &= ~0x80;
+               Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
+       }
+       if (val & 0x08) {
+               bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+               if (bch)
+                       main_rec_hfcpci(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n");
+       }
+       if (val & 0x10) {
+               bch = Sel_BCS(hc, 2);
+               if (bch)
+                       main_rec_hfcpci(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n");
+       }
+       if (val & 0x01) {
+               bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+               if (bch)
+                       tx_birq(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n");
+       }
+       if (val & 0x02) {
+               bch = Sel_BCS(hc, 2);
+               if (bch)
+                       tx_birq(bch);
+               else if (hc->dch.debug)
+                       printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n");
+       }
+       if (val & 0x20)
+               receive_dmsg(hc);
+       if (val & 0x04) {       /* dframe transmitted */
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags))
+                       del_timer(&hc->dch.timer);
+               tx_dirq(&hc->dch);
+       }
+       spin_unlock(&hc->lock);
+       return IRQ_HANDLED;
+}
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+static void
+hfcpci_dbusy_timer(struct hfc_pci *hc)
+{
+}
+
+/*
+ * activate/deactivate hardware for selected channels and mode
+ */
+static int
+mode_hfcpci(struct bchannel *bch, int bc, int protocol)
+{
+       struct hfc_pci  *hc = bch->hw;
+       int             fifo2;
+       u_char          rx_slot = 0, tx_slot = 0, pcm_mode;
+
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n",
+                   bch->state, protocol, bch->nr, bc);
+
+       fifo2 = bc;
+       pcm_mode = (bc>>24) & 0xff;
+       if (pcm_mode) { /* PCM SLOT USE */
+               if (!test_bit(HFC_CFG_PCM, &hc->cfg))
+                       printk(KERN_WARNING
+                           "%s: pcm channel id without HFC_CFG_PCM\n",
+                           __func__);
+               rx_slot = (bc>>8) & 0xff;
+               tx_slot = (bc>>16) & 0xff;
+               bc = bc & 0xff;
+       } else if (test_bit(HFC_CFG_PCM, &hc->cfg) &&
+           (protocol > ISDN_P_NONE))
+               printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n",
+                   __func__);
+       if (hc->chanlimit > 1) {
+               hc->hw.bswapped = 0;    /* B1 and B2 normal mode */
+               hc->hw.sctrl_e &= ~0x80;
+       } else {
+               if (bc & 2) {
+                       if (protocol != ISDN_P_NONE) {
+                               hc->hw.bswapped = 1; /* B1 and B2 exchanged */
+                               hc->hw.sctrl_e |= 0x80;
+                       } else {
+                               hc->hw.bswapped = 0; /* B1 and B2 normal mode */
+                               hc->hw.sctrl_e &= ~0x80;
+                       }
+                       fifo2 = 1;
+               } else {
+                       hc->hw.bswapped = 0;    /* B1 and B2 normal mode */
+                       hc->hw.sctrl_e &= ~0x80;
+               }
+       }
+       switch (protocol) {
+       case (-1): /* used for init */
+               bch->state = -1;
+               bch->nr = bc;
+       case (ISDN_P_NONE):
+               if (bch->state == ISDN_P_NONE)
+                       return 0;
+               if (bc & 2) {
+                       hc->hw.sctrl &= ~SCTRL_B2_ENA;
+                       hc->hw.sctrl_r &= ~SCTRL_B2_ENA;
+               } else {
+                       hc->hw.sctrl &= ~SCTRL_B1_ENA;
+                       hc->hw.sctrl_r &= ~SCTRL_B1_ENA;
+               }
+               if (fifo2 & 2) {
+                       hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2;
+                       hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS +
+                               HFCPCI_INTS_B2REC);
+               } else {
+                       hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1;
+                       hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS +
+                               HFCPCI_INTS_B1REC);
+               }
+#ifdef REVERSE_BITORDER
+               if (bch->nr & 2)
+                       hc->hw.cirm &= 0x7f;
+               else
+                       hc->hw.cirm &= 0xbf;
+#endif
+               bch->state = ISDN_P_NONE;
+               bch->nr = bc;
+               test_and_clear_bit(FLG_HDLC, &bch->Flags);
+               test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
+               break;
+       case (ISDN_P_B_RAW):
+               bch->state = protocol;
+               bch->nr = bc;
+               hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
+               hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
+               if (bc & 2) {
+                       hc->hw.sctrl |= SCTRL_B2_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x80;
+#endif
+               } else {
+                       hc->hw.sctrl |= SCTRL_B1_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x40;
+#endif
+               }
+               if (fifo2 & 2) {
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
+                           HFCPCI_INTS_B2REC);
+                       hc->hw.ctmt |= 2;
+                       hc->hw.conn &= ~0x18;
+               } else {
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
+                           HFCPCI_INTS_B1REC);
+                       hc->hw.ctmt |= 1;
+                       hc->hw.conn &= ~0x03;
+               }
+               test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
+               break;
+       case (ISDN_P_B_HDLC):
+               bch->state = protocol;
+               bch->nr = bc;
+               hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
+               hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
+               if (bc & 2) {
+                       hc->hw.sctrl |= SCTRL_B2_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+               } else {
+                       hc->hw.sctrl |= SCTRL_B1_ENA;
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+               }
+               if (fifo2 & 2) {
+                       hc->hw.last_bfifo_cnt[1] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS +
+                           HFCPCI_INTS_B2REC);
+                       hc->hw.ctmt &= ~2;
+                       hc->hw.conn &= ~0x18;
+               } else {
+                       hc->hw.last_bfifo_cnt[0] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
+                       hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS +
+                           HFCPCI_INTS_B1REC);
+                       hc->hw.ctmt &= ~1;
+                       hc->hw.conn &= ~0x03;
+               }
+               test_and_set_bit(FLG_HDLC, &bch->Flags);
+               break;
+       default:
+               printk(KERN_DEBUG "prot not known %x\n", protocol);
+               return -ENOPROTOOPT;
+       }
+       if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
+               if ((protocol == ISDN_P_NONE) ||
+                       (protocol == -1)) {     /* init case */
+                       rx_slot = 0;
+                       tx_slot = 0;
+               } else {
+                       if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
+                               rx_slot |= 0xC0;
+                               tx_slot |= 0xC0;
+                       } else {
+                               rx_slot |= 0x80;
+                               tx_slot |= 0x80;
+                       }
+               }
+               if (bc & 2) {
+                       hc->hw.conn &= 0xc7;
+                       hc->hw.conn |= 0x08;
+                       printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n",
+                               __func__, tx_slot);
+                       printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n",
+                               __func__, rx_slot);
+                       Write_hfc(hc, HFCPCI_B2_SSL, tx_slot);
+                       Write_hfc(hc, HFCPCI_B2_RSL, rx_slot);
+               } else {
+                       hc->hw.conn &= 0xf8;
+                       hc->hw.conn |= 0x01;
+                       printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n",
+                               __func__, tx_slot);
+                       printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n",
+                               __func__, rx_slot);
+                       Write_hfc(hc, HFCPCI_B1_SSL, tx_slot);
+                       Write_hfc(hc, HFCPCI_B1_RSL, rx_slot);
+               }
+       }
+       Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
+       Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+#ifdef REVERSE_BITORDER
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+#endif
+       return 0;
+}
+
+static int
+set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan)
+{
+       struct hfc_pci  *hc = bch->hw;
+
+       if (bch->debug & DEBUG_HW_BCHANNEL)
+               printk(KERN_DEBUG
+                   "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n",
+                   bch->state, protocol, bch->nr, chan);
+       if (bch->nr != chan) {
+               printk(KERN_DEBUG
+                   "HFCPCI rxtest wrong channel parameter %x/%x\n",
+                   bch->nr, chan);
+               return -EINVAL;
+       }
+       switch (protocol) {
+       case (ISDN_P_B_RAW):
+               bch->state = protocol;
+               hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0);
+               if (chan & 2) {
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
+                       hc->hw.ctmt |= 2;
+                       hc->hw.conn &= ~0x18;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x80;
+#endif
+               } else {
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
+                       hc->hw.ctmt |= 1;
+                       hc->hw.conn &= ~0x03;
+#ifdef REVERSE_BITORDER
+                       hc->hw.cirm |= 0x40;
+#endif
+               }
+               break;
+       case (ISDN_P_B_HDLC):
+               bch->state = protocol;
+               hfcpci_clear_fifo_rx(hc, (chan & 2)?1:0);
+               if (chan & 2) {
+                       hc->hw.sctrl_r |= SCTRL_B2_ENA;
+                       hc->hw.last_bfifo_cnt[1] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
+                       hc->hw.ctmt &= ~2;
+                       hc->hw.conn &= ~0x18;
+               } else {
+                       hc->hw.sctrl_r |= SCTRL_B1_ENA;
+                       hc->hw.last_bfifo_cnt[0] = 0;
+                       hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
+                       hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
+                       hc->hw.ctmt &= ~1;
+                       hc->hw.conn &= ~0x03;
+               }
+               break;
+       default:
+               printk(KERN_DEBUG "prot not known %x\n", protocol);
+               return -ENOPROTOOPT;
+       }
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+       Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
+       Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
+       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+#ifdef REVERSE_BITORDER
+       Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
+#endif
+       return 0;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+       struct hfc_pci  *hc = bch->hw;
+       u_long          flags;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+               dev_kfree_skb(bch->next_skb);
+               bch->next_skb = NULL;
+       }
+       if (bch->tx_skb) {
+               dev_kfree_skb(bch->tx_skb);
+               bch->tx_skb = NULL;
+       }
+       bch->tx_idx = 0;
+       if (bch->rx_skb) {
+               dev_kfree_skb(bch->rx_skb);
+               bch->rx_skb = NULL;
+       }
+       mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+       test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+       spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+/*
+ * Layer 1 B-channel hardware access
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int                     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+static int
+hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_pci  *hc = bch->hw;
+       int             ret = -EINVAL;
+       u_long          flags;
+
+       if (bch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg);
+       switch (cmd) {
+       case HW_TESTRX_RAW:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case HW_TESTRX_HDLC:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case HW_TESTRX_OFF:
+               spin_lock_irqsave(&hc->lock, flags);
+               mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               ret = 0;
+               break;
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags))
+                       deactivate_bchannel(bch);
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bch, arg);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown prim(%x)\n",
+                       __func__, cmd);
+       }
+       return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Dchannel data
+ */
+static int
+hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_pci          *hc = dch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = dchannel_senddata(dch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcpci_fill_dfifo(dch->hw);
+                       ret = 0;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->hw.protocol == ISDN_P_NT_S0) {
+                       ret = 0;
+                       if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+                               hc->hw.mst_m |= HFCPCI_MASTER;
+                       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+                       if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+                                   MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+                               break;
+                       }
+                       test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags);
+                       Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE |
+                           HFCPCI_DO_ACTION | 1);
+               } else
+                       ret = l1_event(dch->l1, hh->prim);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       case PH_DEACTIVATE_REQ:
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               spin_lock_irqsave(&hc->lock, flags);
+               if (hc->hw.protocol == ISDN_P_NT_S0) {
+                       /* prepare deactivation */
+                       Write_hfc(hc, HFCPCI_STATES, 0x40);
+                       skb_queue_purge(&dch->squeue);
+                       if (dch->tx_skb) {
+                               dev_kfree_skb(dch->tx_skb);
+                               dch->tx_skb = NULL;
+                       }
+                       dch->tx_idx = 0;
+                       if (dch->rx_skb) {
+                               dev_kfree_skb(dch->rx_skb);
+                               dch->rx_skb = NULL;
+                       }
+                       test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+                       if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                               del_timer(&dch->timer);
+#ifdef FIXME
+                       if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+                               dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+                       hc->hw.mst_m &= ~HFCPCI_MASTER;
+                       Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+                       ret = 0;
+               } else {
+                       ret = l1_event(dch->l1, hh->prim);
+               }
+               spin_unlock_irqrestore(&hc->lock, flags);
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Bchannel data
+ */
+static int
+hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct hfc_pci          *hc = bch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       unsigned int            id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       hfcpci_fill_fifo(bch);
+                       ret = 0;
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(&hc->lock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(&hc->lock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = mode_hfcpci(bch, bch->nr, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(&hc->lock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               deactivate_bchannel(bch);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+/*
+ * called for card init message
+ */
+
+void
+inithfcpci(struct hfc_pci *hc)
+{
+       printk(KERN_DEBUG "inithfcpci: entered\n");
+       hc->dch.timer.function = (void *) hfcpci_dbusy_timer;
+       hc->dch.timer.data = (long) &hc->dch;
+       init_timer(&hc->dch.timer);
+       hc->chanlimit = 2;
+       mode_hfcpci(&hc->bch[0], 1, -1);
+       mode_hfcpci(&hc->bch[1], 2, -1);
+}
+
+
+static int
+init_card(struct hfc_pci *hc)
+{
+       int     cnt = 3;
+       u_long  flags;
+
+       printk(KERN_DEBUG "init_card: entered\n");
+
+
+       spin_lock_irqsave(&hc->lock, flags);
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) {
+               printk(KERN_WARNING
+                   "mISDN: couldn't get interrupt %d\n", hc->irq);
+               return -EIO;
+       }
+       spin_lock_irqsave(&hc->lock, flags);
+       reset_hfcpci(hc);
+       while (cnt) {
+               inithfcpci(hc);
+               /*
+                * Finally enable IRQ output
+                * this is only allowed, if an IRQ routine is allready
+                * established for this HFC, so don't do that earlier
+                */
+               enable_hwirq(hc);
+               spin_unlock_irqrestore(&hc->lock, flags);
+               /* Timeout 80ms */
+               current->state = TASK_UNINTERRUPTIBLE;
+               schedule_timeout((80*HZ)/1000);
+               printk(KERN_INFO "HFC PCI: IRQ %d count %d\n",
+                       hc->irq, hc->irqcnt);
+               /* now switch timer interrupt off */
+               spin_lock_irqsave(&hc->lock, flags);
+               hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+               Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+               /* reinit mode reg */
+               Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+               if (!hc->irqcnt) {
+                       printk(KERN_WARNING
+                           "HFC PCI: IRQ(%d) getting no interrupts "
+                           "during init %d\n", hc->irq, 4 - cnt);
+                       if (cnt == 1) {
+                               spin_unlock_irqrestore(&hc->lock, flags);
+                               return -EIO;
+                       } else {
+                               reset_hfcpci(hc);
+                               cnt--;
+                       }
+               } else {
+                       spin_unlock_irqrestore(&hc->lock, flags);
+                       hc->initdone = 1;
+                       return 0;
+               }
+       }
+       disable_hwirq(hc);
+       spin_unlock_irqrestore(&hc->lock, flags);
+       free_irq(hc->irq, hc);
+       return -EIO;
+}
+
+static int
+channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+       u_char  slot;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT |
+                   MISDN_CTRL_DISCONNECT;
+               break;
+       case MISDN_CTRL_LOOP:
+               /* channel 0 disabled loop */
+               if (cq->channel < 0 || cq->channel > 2) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (cq->channel & 1) {
+                       if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                               slot = 0xC0;
+                       else
+                               slot = 0x80;
+                       printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n",
+                           __func__, slot);
+                       Write_hfc(hc, HFCPCI_B1_SSL, slot);
+                       Write_hfc(hc, HFCPCI_B1_RSL, slot);
+                       hc->hw.conn = (hc->hw.conn & ~7) | 6;
+                       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               }
+               if (cq->channel & 2) {
+                       if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                               slot = 0xC1;
+                       else
+                               slot = 0x81;
+                       printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n",
+                           __func__, slot);
+                       Write_hfc(hc, HFCPCI_B2_SSL, slot);
+                       Write_hfc(hc, HFCPCI_B2_RSL, slot);
+                       hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30;
+                       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               }
+               if (cq->channel & 3)
+                       hc->hw.trm |= 0x80;     /* enable IOM-loop */
+               else {
+                       hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+                       Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+                       hc->hw.trm &= 0x7f;     /* disable IOM-loop */
+               }
+               Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+               break;
+       case MISDN_CTRL_CONNECT:
+               if (cq->channel == cq->p1) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (cq->channel < 1 || cq->channel > 2 ||
+                   cq->p1 < 1 || cq->p1 > 2) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                       slot = 0xC0;
+               else
+                       slot = 0x80;
+               printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n",
+                   __func__, slot);
+               Write_hfc(hc, HFCPCI_B1_SSL, slot);
+               Write_hfc(hc, HFCPCI_B2_RSL, slot);
+               if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
+                       slot = 0xC1;
+               else
+                       slot = 0x81;
+               printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n",
+                   __func__, slot);
+               Write_hfc(hc, HFCPCI_B2_SSL, slot);
+               Write_hfc(hc, HFCPCI_B1_RSL, slot);
+               hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36;
+               Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               hc->hw.trm |= 0x80;
+               Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+               break;
+       case MISDN_CTRL_DISCONNECT:
+               hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+               Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+               hc->hw.trm &= 0x7f;     /* disable IOM-loop */
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch,
+    struct channel_req *rq)
+{
+       int err = 0;
+
+       if (debug & DEBUG_HW_OPEN)
+               printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+                   hc->dch.dev.id, __builtin_return_address(0));
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if (!hc->initdone) {
+               if (rq->protocol == ISDN_P_TE_S0) {
+                       err = create_l1(&hc->dch, hfc_l1callback);
+                       if (err)
+                               return err;
+               }
+               hc->hw.protocol = rq->protocol;
+               ch->protocol = rq->protocol;
+               err = init_card(hc);
+               if (err)
+                       return err;
+       } else {
+               if (rq->protocol != ch->protocol) {
+                       if (hc->hw.protocol == ISDN_P_TE_S0)
+                               l1_event(hc->dch.l1, CLOSE_CHANNEL);
+                       hc->hw.protocol = rq->protocol;
+                       ch->protocol = rq->protocol;
+                       hfcpci_setmode(hc);
+               }
+       }
+
+       if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) ||
+           ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) {
+               _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       }
+       rq->ch = ch;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+open_bchannel(struct hfc_pci *hc, struct channel_req *rq)
+{
+       struct bchannel         *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &hc->bch[rq->adr.channel - 1];
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch; /* TODO: E-channel */
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct hfc_pci          *hc = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+
+       if (dch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->adr.channel == 0)
+                       err = open_dchannel(hc, ch, rq);
+               else
+                       err = open_bchannel(hc, rq);
+               break;
+       case CLOSE_CHANNEL:
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+                           __func__, hc->dch.dev.id,
+                           __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(hc, arg);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+static int
+setup_hw(struct hfc_pci *hc)
+{
+       void    *buffer;
+
+       printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision);
+       hc->hw.cirm = 0;
+       hc->dch.state = 0;
+       pci_set_master(hc->pdev);
+       if (!hc->irq) {
+               printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
+               return 1;
+       }
+       hc->hw.pci_io = (char *)(ulong)hc->pdev->resource[1].start;
+
+       if (!hc->hw.pci_io) {
+               printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
+               return 1;
+       }
+       /* Allocate memory for FIFOS */
+       /* the memory needs to be on a 32k boundary within the first 4G */
+       pci_set_dma_mask(hc->pdev, 0xFFFF8000);
+       buffer = pci_alloc_consistent(hc->pdev, 0x8000, &hc->hw.dmahandle);
+       /* We silently assume the address is okay if nonzero */
+       if (!buffer) {
+               printk(KERN_WARNING
+                   "HFC-PCI: Error allocating memory for FIFO!\n");
+               return 1;
+       }
+       hc->hw.fifos = buffer;
+       pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle);
+       hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256);
+       printk(KERN_INFO
+               "HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n",
+               (u_long) hc->hw.pci_io, (u_long) hc->hw.fifos,
+               (u_long) virt_to_bus(hc->hw.fifos),
+               hc->irq, HZ);
+       /* enable memory mapped ports, disable busmaster */
+       pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+       hc->hw.int_m2 = 0;
+       disable_hwirq(hc);
+       hc->hw.int_m1 = 0;
+       Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+       /* At this point the needed PCI config is done */
+       /* fifos are still not enabled */
+       hc->hw.timer.function = (void *) hfcpci_Timer;
+       hc->hw.timer.data = (long) hc;
+       init_timer(&hc->hw.timer);
+       /* default PCM master */
+       test_and_set_bit(HFC_CFG_MASTER, &hc->cfg);
+       return 0;
+}
+
+static void
+release_card(struct hfc_pci *hc) {
+       u_long  flags;
+
+       spin_lock_irqsave(&hc->lock, flags);
+       hc->hw.int_m2 = 0; /* interrupt output off ! */
+       disable_hwirq(hc);
+       mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE);
+       mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE);
+       if (hc->dch.timer.function != NULL) {
+               del_timer(&hc->dch.timer);
+               hc->dch.timer.function = NULL;
+       }
+       spin_unlock_irqrestore(&hc->lock, flags);
+       if (hc->hw.protocol == ISDN_P_TE_S0)
+               l1_event(hc->dch.l1, CLOSE_CHANNEL);
+       if (hc->initdone)
+               free_irq(hc->irq, hc);
+       release_io_hfcpci(hc); /* must release after free_irq! */
+       mISDN_unregister_device(&hc->dch.dev);
+       mISDN_freebchannel(&hc->bch[1]);
+       mISDN_freebchannel(&hc->bch[0]);
+       mISDN_freedchannel(&hc->dch);
+       list_del(&hc->list);
+       pci_set_drvdata(hc->pdev, NULL);
+       kfree(hc);
+}
+
+static int
+setup_card(struct hfc_pci *card)
+{
+       int             err = -EINVAL;
+       u_int           i;
+       u_long          flags;
+       char            name[MISDN_MAX_IDLEN];
+
+       if (HFC_cnt >= MAX_CARDS)
+               return -EINVAL; /* maybe better value */
+
+       card->dch.debug = debug;
+       spin_lock_init(&card->lock);
+       mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state);
+       card->dch.hw = card;
+       card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+       card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       card->dch.dev.D.send = hfcpci_l2l1D;
+       card->dch.dev.D.ctrl = hfc_dctrl;
+       card->dch.dev.nrbchan = 2;
+       for (i = 0; i < 2; i++) {
+               card->bch[i].nr = i + 1;
+               test_and_set_bit(i + 1, &card->dch.dev.channelmap[0]);
+               card->bch[i].debug = debug;
+               mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+               card->bch[i].hw = card;
+               card->bch[i].ch.send = hfcpci_l2l1B;
+               card->bch[i].ch.ctrl = hfc_bctrl;
+               card->bch[i].ch.nr = i + 1;
+               list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels);
+       }
+       err = setup_hw(card);
+       if (err)
+               goto error;
+       snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1);
+       err = mISDN_register_device(&card->dch.dev, name);
+       if (err)
+               goto error;
+       HFC_cnt++;
+       write_lock_irqsave(&HFClock, flags);
+       list_add_tail(&card->list, &HFClist);
+       write_unlock_irqrestore(&HFClock, flags);
+       printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt);
+       return 0;
+error:
+       mISDN_freebchannel(&card->bch[1]);
+       mISDN_freebchannel(&card->bch[0]);
+       mISDN_freedchannel(&card->dch);
+       kfree(card);
+       return err;
+}
+
+/* private data in the PCI devices list */
+struct _hfc_map {
+       u_int   subtype;
+       u_int   flag;
+       char    *name;
+};
+
+static const struct _hfc_map hfc_map[] =
+{
+       {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"},
+       {HFC_CCD_B000, 0, "Billion B000"},
+       {HFC_CCD_B006, 0, "Billion B006"},
+       {HFC_CCD_B007, 0, "Billion B007"},
+       {HFC_CCD_B008, 0, "Billion B008"},
+       {HFC_CCD_B009, 0, "Billion B009"},
+       {HFC_CCD_B00A, 0, "Billion B00A"},
+       {HFC_CCD_B00B, 0, "Billion B00B"},
+       {HFC_CCD_B00C, 0, "Billion B00C"},
+       {HFC_CCD_B100, 0, "Seyeon B100"},
+       {HFC_CCD_B700, 0, "Primux II S0 B700"},
+       {HFC_CCD_B701, 0, "Primux II S0 NT B701"},
+       {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"},
+       {HFC_ASUS_0675, 0, "Asuscom/Askey 675"},
+       {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"},
+       {HFC_BERKOM_A1T, 0, "German telekom A1T"},
+       {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"},
+       {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"},
+       {HFC_DIGI_DF_M_IOM2_E, 0,
+           "Digi International DataFire Micro V IOM2 (Europe)"},
+       {HFC_DIGI_DF_M_E, 0,
+           "Digi International DataFire Micro V (Europe)"},
+       {HFC_DIGI_DF_M_IOM2_A, 0,
+           "Digi International DataFire Micro V IOM2 (North America)"},
+       {HFC_DIGI_DF_M_A, 0,
+           "Digi International DataFire Micro V (North America)"},
+       {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"},
+       {},
+};
+
+static struct pci_device_id hfc_ids[] =
+{
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[0]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[1]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[2]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[3]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[4]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[5]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[6]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[7]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[8]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[9]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[10]},
+       {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[11]},
+       {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[12]},
+       {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[13]},
+       {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[14]},
+       {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[15]},
+       {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[16]},
+       {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[17]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[18]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[19]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[20]},
+       {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[21]},
+       {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[22]},
+       {},
+};
+
+static int __devinit
+hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int             err = -ENOMEM;
+       struct hfc_pci  *card;
+       struct _hfc_map *m = (struct _hfc_map *)ent->driver_data;
+
+       card = kzalloc(sizeof(struct hfc_pci), GFP_ATOMIC);
+       if (!card) {
+               printk(KERN_ERR "No kmem for HFC card\n");
+               return err;
+       }
+       card->pdev = pdev;
+       card->subtype = m->subtype;
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+
+       printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n",
+              m->name, pci_name(pdev));
+
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_card(card);
+       if (err)
+               pci_set_drvdata(pdev, NULL);
+       return err;
+}
+
+static void __devexit
+hfc_remove_pci(struct pci_dev *pdev)
+{
+       struct hfc_pci  *card = pci_get_drvdata(pdev);
+       u_long          flags;
+
+       if (card) {
+               write_lock_irqsave(&HFClock, flags);
+               release_card(card);
+               write_unlock_irqrestore(&HFClock, flags);
+       } else
+               if (debug)
+                       printk(KERN_WARNING "%s: drvdata allready removed\n",
+                           __func__);
+}
+
+
+static struct pci_driver hfc_driver = {
+       .name = "hfcpci",
+       .probe = hfc_probe,
+       .remove = __devexit_p(hfc_remove_pci),
+       .id_table = hfc_ids,
+};
+
+static int __init
+HFC_init(void)
+{
+       int             err;
+
+       err = pci_register_driver(&hfc_driver);
+       return err;
+}
+
+static void __exit
+HFC_cleanup(void)
+{
+       struct hfc_pci  *card, *next;
+
+       list_for_each_entry_safe(card, next, &HFClist, list) {
+               release_card(card);
+       }
+       pci_unregister_driver(&hfc_driver);
+}
+
+module_init(HFC_init);
+module_exit(HFC_cleanup);
diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig
new file mode 100644 (file)
index 0000000..4938355
--- /dev/null
@@ -0,0 +1,44 @@
+#
+# modularer ISDN driver
+#
+
+menuconfig MISDN
+       tristate "Modular ISDN driver"
+       help
+         Enable support for the modular ISDN driver.
+
+if MISDN != n
+
+config MISDN_DSP
+       tristate "Digital Audio Processing of transparent data"
+       depends on MISDN
+       help
+         Enable support for digital audio processing capability.
+         This module may be used for special applications that require
+         cross connecting of bchannels, conferencing, dtmf decoding
+         echo cancelation, tone generation, and Blowfish encryption and
+         decryption.
+         It may use hardware features if available.
+         E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu
+         and get more informations about this module and it's usage.
+         If unsure, say 'N'.
+
+config MISDN_L1OIP
+       tristate "ISDN over IP tunnel"
+       depends on MISDN
+       help
+         Enable support for ISDN over IP tunnel.
+
+         It features:
+           - dynamic IP exchange, if one or both peers have dynamic IPs
+           - BRI (S0) and PRI (S2M) interface
+           - layer 1 control via network keepalive frames
+           - direct tunneling of physical interface via IP
+
+         NOTE: This protocol is called 'Layer 1 over IP' and is not
+         compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is
+         provided in the source code.
+
+source "drivers/isdn/hardware/mISDN/Kconfig"
+
+endif #MISDN
diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile
new file mode 100644 (file)
index 0000000..1cb5e63
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Makefile for the modular ISDN driver
+#
+
+obj-$(CONFIG_MISDN) += mISDN_core.o
+obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o
+obj-$(CONFIG_MISDN_L1OIP) += l1oip.o
+
+# multi objects
+
+mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o
+mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o
+l1oip-objs := l1oip_core.o l1oip_codec.o
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
new file mode 100644 (file)
index 0000000..3306817
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static u_int debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+static LIST_HEAD(devices);
+DEFINE_RWLOCK(device_lock);
+static u64             device_ids;
+#define MAX_DEVICE_ID  63
+
+static LIST_HEAD(Bprotocols);
+DEFINE_RWLOCK(bp_lock);
+
+struct mISDNdevice
+*get_mdevice(u_int id)
+{
+       struct mISDNdevice      *dev;
+
+       read_lock(&device_lock);
+       list_for_each_entry(dev, &devices, D.list)
+               if (dev->id == id) {
+                       read_unlock(&device_lock);
+                       return dev;
+               }
+       read_unlock(&device_lock);
+       return NULL;
+}
+
+int
+get_mdevice_count(void)
+{
+       struct mISDNdevice      *dev;
+       int                     cnt = 0;
+
+       read_lock(&device_lock);
+       list_for_each_entry(dev, &devices, D.list)
+               cnt++;
+       read_unlock(&device_lock);
+       return cnt;
+}
+
+static int
+get_free_devid(void)
+{
+       u_int   i;
+
+       for (i = 0; i <= MAX_DEVICE_ID; i++)
+               if (!test_and_set_bit(i, (u_long *)&device_ids))
+                       return i;
+       return -1;
+}
+
+int
+mISDN_register_device(struct mISDNdevice *dev, char *name)
+{
+       u_long  flags;
+       int     err;
+
+       dev->id = get_free_devid();
+       if (dev->id < 0)
+               return -EBUSY;
+       if (name && name[0])
+               strcpy(dev->name, name);
+       else
+               sprintf(dev->name, "mISDN%d", dev->id);
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "mISDN_register %s %d\n",
+                       dev->name, dev->id);
+       err = create_stack(dev);
+       if (err)
+               return err;
+       write_lock_irqsave(&device_lock, flags);
+       list_add_tail(&dev->D.list, &devices);
+       write_unlock_irqrestore(&device_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_register_device);
+
+void
+mISDN_unregister_device(struct mISDNdevice *dev) {
+       u_long  flags;
+
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "mISDN_unregister %s %d\n",
+                       dev->name, dev->id);
+       write_lock_irqsave(&device_lock, flags);
+       list_del(&dev->D.list);
+       write_unlock_irqrestore(&device_lock, flags);
+       test_and_clear_bit(dev->id, (u_long *)&device_ids);
+       delete_stack(dev);
+}
+EXPORT_SYMBOL(mISDN_unregister_device);
+
+u_int
+get_all_Bprotocols(void)
+{
+       struct Bprotocol        *bp;
+       u_int   m = 0;
+
+       read_lock(&bp_lock);
+       list_for_each_entry(bp, &Bprotocols, list)
+               m |= bp->Bprotocols;
+       read_unlock(&bp_lock);
+       return m;
+}
+
+struct Bprotocol *
+get_Bprotocol4mask(u_int m)
+{
+       struct Bprotocol        *bp;
+
+       read_lock(&bp_lock);
+       list_for_each_entry(bp, &Bprotocols, list)
+               if (bp->Bprotocols & m) {
+                       read_unlock(&bp_lock);
+                       return bp;
+               }
+       read_unlock(&bp_lock);
+       return NULL;
+}
+
+struct Bprotocol *
+get_Bprotocol4id(u_int id)
+{
+       u_int   m;
+
+       if (id < ISDN_P_B_START || id > 63) {
+               printk(KERN_WARNING "%s id not in range  %d\n",
+                   __func__, id);
+               return NULL;
+       }
+       m = 1 << (id & ISDN_P_B_MASK);
+       return get_Bprotocol4mask(m);
+}
+
+int
+mISDN_register_Bprotocol(struct Bprotocol *bp)
+{
+       u_long                  flags;
+       struct Bprotocol        *old;
+
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "%s: %s/%x\n", __func__,
+                   bp->name, bp->Bprotocols);
+       old = get_Bprotocol4mask(bp->Bprotocols);
+       if (old) {
+               printk(KERN_WARNING
+                   "register duplicate protocol old %s/%x new %s/%x\n",
+                   old->name, old->Bprotocols, bp->name, bp->Bprotocols);
+               return -EBUSY;
+       }
+       write_lock_irqsave(&bp_lock, flags);
+       list_add_tail(&bp->list, &Bprotocols);
+       write_unlock_irqrestore(&bp_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_register_Bprotocol);
+
+void
+mISDN_unregister_Bprotocol(struct Bprotocol *bp)
+{
+       u_long  flags;
+
+       if (debug & DEBUG_CORE)
+               printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
+                       bp->Bprotocols);
+       write_lock_irqsave(&bp_lock, flags);
+       list_del(&bp->list);
+       write_unlock_irqrestore(&bp_lock, flags);
+}
+EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
+
+int
+mISDNInit(void)
+{
+       int     err;
+
+       printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
+               MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
+       mISDN_initstack(&debug);
+       err = mISDN_inittimer(&debug);
+       if (err)
+               goto error;
+       err = l1_init(&debug);
+       if (err) {
+               mISDN_timer_cleanup();
+               goto error;
+       }
+       err = Isdnl2_Init(&debug);
+       if (err) {
+               mISDN_timer_cleanup();
+               l1_cleanup();
+               goto error;
+       }
+       err = misdn_sock_init(&debug);
+       if (err) {
+               mISDN_timer_cleanup();
+               l1_cleanup();
+               Isdnl2_cleanup();
+       }
+error:
+       return err;
+}
+
+void mISDN_cleanup(void)
+{
+       misdn_sock_cleanup();
+       mISDN_timer_cleanup();
+       l1_cleanup();
+       Isdnl2_cleanup();
+
+       if (!list_empty(&devices))
+               printk(KERN_ERR "%s devices still registered\n", __func__);
+
+       if (!list_empty(&Bprotocols))
+               printk(KERN_ERR "%s Bprotocols still registered\n", __func__);
+       printk(KERN_DEBUG "mISDNcore unloaded\n");
+}
+
+module_init(mISDNInit);
+module_exit(mISDN_cleanup);
+
diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h
new file mode 100644 (file)
index 0000000..7da7233
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef mISDN_CORE_H
+#define mISDN_CORE_H
+
+extern struct mISDNdevice      *get_mdevice(u_int);
+extern int                     get_mdevice_count(void);
+
+/* stack status flag */
+#define mISDN_STACK_ACTION_MASK                0x0000ffff
+#define mISDN_STACK_COMMAND_MASK       0x000f0000
+#define mISDN_STACK_STATUS_MASK                0xfff00000
+/* action bits 0-15 */
+#define mISDN_STACK_WORK       0
+#define mISDN_STACK_SETUP      1
+#define mISDN_STACK_CLEARING   2
+#define mISDN_STACK_RESTART    3
+#define mISDN_STACK_WAKEUP     4
+#define mISDN_STACK_ABORT      15
+/* command bits 16-19 */
+#define mISDN_STACK_STOPPED    16
+#define mISDN_STACK_INIT       17
+#define mISDN_STACK_THREADSTART        18
+/* status bits 20-31 */
+#define mISDN_STACK_BCHANNEL   20
+#define mISDN_STACK_ACTIVE      29
+#define mISDN_STACK_RUNNING     30
+#define mISDN_STACK_KILLED      31
+
+
+/* manager options */
+#define MGR_OPT_USER           24
+#define MGR_OPT_NETWORK                25
+
+extern int     connect_Bstack(struct mISDNdevice *, struct mISDNchannel *,
+                       u_int, struct sockaddr_mISDN *);
+extern int     connect_layer1(struct mISDNdevice *, struct mISDNchannel *,
+                       u_int, struct sockaddr_mISDN *);
+extern int     create_l2entity(struct mISDNdevice *, struct mISDNchannel *,
+                       u_int, struct sockaddr_mISDN *);
+
+extern int     create_stack(struct mISDNdevice *);
+extern int     create_teimanager(struct mISDNdevice *);
+extern void    delete_teimanager(struct mISDNchannel *);
+extern void    delete_channel(struct mISDNchannel *);
+extern void    delete_stack(struct mISDNdevice *);
+extern void    mISDN_initstack(u_int *);
+extern int      misdn_sock_init(u_int *);
+extern void     misdn_sock_cleanup(void);
+extern void    add_layer2(struct mISDNchannel *, struct mISDNstack *);
+extern void    __add_layer2(struct mISDNchannel *, struct mISDNstack *);
+
+extern u_int           get_all_Bprotocols(void);
+struct Bprotocol       *get_Bprotocol4mask(u_int);
+struct Bprotocol       *get_Bprotocol4id(u_int);
+
+extern int     mISDN_inittimer(u_int *);
+extern void    mISDN_timer_cleanup(void);
+
+extern int     l1_init(u_int *);
+extern void    l1_cleanup(void);
+extern int     Isdnl2_Init(u_int *);
+extern void    Isdnl2_cleanup(void);
+
+#endif
diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h
new file mode 100644 (file)
index 0000000..6c3fed6
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DEBUG_DSP_CTRL         0x0001
+#define DEBUG_DSP_CORE         0x0002
+#define DEBUG_DSP_DTMF         0x0004
+#define DEBUG_DSP_CMX          0x0010
+#define DEBUG_DSP_TONE         0x0020
+#define DEBUG_DSP_BLOWFISH     0x0040
+#define DEBUG_DSP_DELAY                0x0100
+#define DEBUG_DSP_DTMFCOEFF    0x8000 /* heavy output */
+
+/* options may be:
+ *
+ * bit 0 = use ulaw instead of alaw
+ * bit 1 = enable hfc hardware accelleration for all channels
+ *
+ */
+#define DSP_OPT_ULAW           (1<<0)
+#define DSP_OPT_NOHARDWARE     (1<<1)
+
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include "dsp_ecdis.h"
+
+extern int dsp_options;
+extern int dsp_debug;
+extern int dsp_poll;
+extern int dsp_tics;
+extern spinlock_t dsp_lock;
+extern struct work_struct dsp_workq;
+extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */
+
+/***************
+ * audio stuff *
+ ***************/
+
+extern s32 dsp_audio_alaw_to_s32[256];
+extern s32 dsp_audio_ulaw_to_s32[256];
+extern s32 *dsp_audio_law_to_s32;
+extern u8 dsp_audio_s16_to_law[65536];
+extern u8 dsp_audio_alaw_to_ulaw[256];
+extern u8 dsp_audio_mix_law[65536];
+extern u8 dsp_audio_seven2law[128];
+extern u8 dsp_audio_law2seven[256];
+extern void dsp_audio_generate_law_tables(void);
+extern void dsp_audio_generate_s2law_table(void);
+extern void dsp_audio_generate_seven(void);
+extern void dsp_audio_generate_mix_table(void);
+extern void dsp_audio_generate_ulaw_samples(void);
+extern void dsp_audio_generate_volume_changes(void);
+extern u8 dsp_silence;
+
+
+/*************
+ * cmx stuff *
+ *************/
+
+#define MAX_POLL       256     /* maximum number of send-chunks */
+
+#define CMX_BUFF_SIZE  0x8000  /* must be 2**n (0x1000 about 1/2 second) */
+#define CMX_BUFF_HALF  0x4000  /* CMX_BUFF_SIZE / 2 */
+#define CMX_BUFF_MASK  0x7fff  /* CMX_BUFF_SIZE - 1 */
+
+/* how many seconds will we check the lowest delay until the jitter buffer
+   is reduced by that delay */
+#define MAX_SECONDS_JITTER_CHECK 5
+
+extern struct timer_list dsp_spl_tl;
+extern u32 dsp_spl_jiffies;
+
+/* the structure of conferences:
+ *
+ * each conference has a unique number, given by user space.
+ * the conferences are linked in a chain.
+ * each conference has members linked in a chain.
+ * each dsplayer points to a member, each member points to a dsplayer.
+ */
+
+/* all members within a conference (this is linked 1:1 with the dsp) */
+struct dsp;
+struct dsp_conf_member {
+       struct list_head        list;
+       struct dsp              *dsp;
+};
+
+/* the list of all conferences */
+struct dsp_conf {
+       struct list_head        list;
+       u32                     id;
+                               /* all cmx stacks with the same ID are
+                                connected */
+       struct list_head        mlist;
+       int                     software; /* conf is processed by software */
+       int                     hardware; /* conf is processed by hardware */
+                               /* note: if both unset, has only one member */
+};
+
+
+/**************
+ * DTMF stuff *
+ **************/
+
+#define DSP_DTMF_NPOINTS 102
+
+#define ECHOCAN_BUFLEN (4*128)
+
+struct dsp_dtmf {
+       int             treshold; /* above this is dtmf (square of) */
+       int             software; /* dtmf uses software decoding */
+       int             hardware; /* dtmf uses hardware decoding */
+       int             size; /* number of bytes in buffer */
+       signed short    buffer[DSP_DTMF_NPOINTS];
+               /* buffers one full dtmf frame */
+       u8              lastwhat, lastdigit;
+       int             count;
+       u8              digits[16]; /* just the dtmf result */
+};
+
+
+/******************
+ * pipeline stuff *
+ ******************/
+struct dsp_pipeline {
+       rwlock_t  lock;
+       struct list_head list;
+       int inuse;
+};
+
+/***************
+ * tones stuff *
+ ***************/
+
+struct dsp_tone {
+       int             software; /* tones are generated by software */
+       int             hardware; /* tones are generated by hardware */
+       int             tone;
+       void            *pattern;
+       int             count;
+       int             index;
+       struct timer_list tl;
+};
+
+/*****************
+ * general stuff *
+ *****************/
+
+struct dsp {
+       struct list_head list;
+       struct mISDNchannel     ch;
+       struct mISDNchannel     *up;
+       unsigned char   name[64];
+       int             b_active;
+       int             echo; /* echo is enabled */
+       int             rx_disabled; /* what the user wants */
+       int             rx_is_off; /* what the card is */
+       int             tx_mix;
+       struct dsp_tone tone;
+       struct dsp_dtmf dtmf;
+       int             tx_volume, rx_volume;
+
+       /* queue for sending frames */
+       struct work_struct      workq;
+       struct sk_buff_head     sendq;
+       int             hdlc;   /* if mode is hdlc */
+       int             data_pending;   /* currently an unconfirmed frame */
+
+       /* conference stuff */
+       u32             conf_id;
+       struct dsp_conf *conf;
+       struct dsp_conf_member
+                       *member;
+
+       /* buffer stuff */
+       int             rx_W; /* current write pos for data without timestamp */
+       int             rx_R; /* current read pos for transmit clock */
+       int             rx_init; /* if set, pointers will be adjusted first */
+       int             tx_W; /* current write pos for transmit data */
+       int             tx_R; /* current read pos for transmit clock */
+       int             rx_delay[MAX_SECONDS_JITTER_CHECK];
+       int             tx_delay[MAX_SECONDS_JITTER_CHECK];
+       u8              tx_buff[CMX_BUFF_SIZE];
+       u8              rx_buff[CMX_BUFF_SIZE];
+       int             last_tx; /* if set, we transmitted last poll interval */
+       int             cmx_delay; /* initial delay of buffers,
+                               or 0 for dynamic jitter buffer */
+       int             tx_dejitter; /* if set, dejitter tx buffer */
+       int             tx_data; /* enables tx-data of CMX to upper layer */
+
+       /* hardware stuff */
+       struct dsp_features features;
+       int             features_rx_off; /* set if rx_off is featured */
+       int             pcm_slot_rx; /* current PCM slot (or -1) */
+       int             pcm_bank_rx;
+       int             pcm_slot_tx;
+       int             pcm_bank_tx;
+       int             hfc_conf; /* unique id of current conference (or -1) */
+
+       /* encryption stuff */
+       int             bf_enable;
+       u32             bf_p[18];
+       u32             bf_s[1024];
+       int             bf_crypt_pos;
+       u8              bf_data_in[9];
+       u8              bf_crypt_out[9];
+       int             bf_decrypt_in_pos;
+       int             bf_decrypt_out_pos;
+       u8              bf_crypt_inring[16];
+       u8              bf_data_out[9];
+       int             bf_sync;
+
+       struct dsp_pipeline
+                       pipeline;
+};
+
+/* functions */
+
+extern void dsp_change_volume(struct sk_buff *skb, int volume);
+
+extern struct list_head dsp_ilist;
+extern struct list_head conf_ilist;
+extern void dsp_cmx_debug(struct dsp *dsp);
+extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp);
+extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id);
+extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_send(void *arg);
+extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb);
+extern int dsp_cmx_del_conf_member(struct dsp *dsp);
+extern int dsp_cmx_del_conf(struct dsp_conf *conf);
+
+extern void dsp_dtmf_goertzel_init(struct dsp *dsp);
+extern void dsp_dtmf_hardware(struct dsp *dsp);
+extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len,
+               int fmt);
+
+extern int dsp_tone(struct dsp *dsp, int tone);
+extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len);
+extern void dsp_tone_timeout(void *arg);
+
+extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len);
+extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len);
+extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen);
+extern void dsp_bf_cleanup(struct dsp *dsp);
+
+extern int  dsp_pipeline_module_init(void);
+extern void dsp_pipeline_module_exit(void);
+extern int  dsp_pipeline_init(struct dsp_pipeline *pipeline);
+extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline);
+extern int  dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg);
+extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data,
+               int len);
+extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data,
+               int len);
+
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c
new file mode 100644 (file)
index 0000000..1c2dd56
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * Audio support data for mISDN_dsp.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ * Rewritten by Peter
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+/* ulaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_ulaw_to_s32[256];
+/* alaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_alaw_to_s32[256];
+
+s32 *dsp_audio_law_to_s32;
+EXPORT_SYMBOL(dsp_audio_law_to_s32);
+
+/* signed 16-bit -> law */
+u8 dsp_audio_s16_to_law[65536];
+EXPORT_SYMBOL(dsp_audio_s16_to_law);
+
+/* alaw -> ulaw */
+u8 dsp_audio_alaw_to_ulaw[256];
+/* ulaw -> alaw */
+u8 dsp_audio_ulaw_to_alaw[256];
+u8 dsp_silence;
+
+
+/*****************************************************
+ * generate table for conversion of s16 to alaw/ulaw *
+ *****************************************************/
+
+#define AMI_MASK 0x55
+
+static inline unsigned char linear2alaw(short int linear)
+{
+       int mask;
+       int seg;
+       int pcm_val;
+       static int seg_end[8] = {
+               0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+       };
+
+       pcm_val = linear;
+       if (pcm_val >= 0) {
+               /* Sign (7th) bit = 1 */
+               mask = AMI_MASK | 0x80;
+       } else {
+               /* Sign bit = 0 */
+               mask = AMI_MASK;
+               pcm_val = -pcm_val;
+       }
+
+       /* Convert the scaled magnitude to segment number. */
+       for (seg = 0;  seg < 8;  seg++) {
+               if (pcm_val <= seg_end[seg])
+                       break;
+       }
+       /* Combine the sign, segment, and quantization bits. */
+       return  ((seg << 4) |
+                ((pcm_val >> ((seg)  ?  (seg + 3)  :  4)) & 0x0F)) ^ mask;
+}
+
+
+static inline short int alaw2linear(unsigned char alaw)
+{
+       int i;
+       int seg;
+
+       alaw ^= AMI_MASK;
+       i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
+       seg = (((int) alaw & 0x70) >> 4);
+       if (seg)
+               i = (i + 0x100) << (seg - 1);
+       return (short int) ((alaw & 0x80)  ?  i  :  -i);
+}
+
+static inline short int ulaw2linear(unsigned char ulaw)
+{
+       short mu, e, f, y;
+       static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
+
+       mu = 255 - ulaw;
+       e = (mu & 0x70) / 16;
+       f = mu & 0x0f;
+       y = f * (1 << (e + 3));
+       y += etab[e];
+       if (mu & 0x80)
+               y = -y;
+       return y;
+}
+
+#define BIAS 0x84   /*!< define the add-in bias for 16 bit samples */
+
+static unsigned char linear2ulaw(short sample)
+{
+       static int exp_lut[256] = {
+               0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+               5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+               5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+               7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+       int sign, exponent, mantissa;
+       unsigned char ulawbyte;
+
+       /* Get the sample into sign-magnitude. */
+       sign = (sample >> 8) & 0x80;      /* set aside the sign */
+       if (sign != 0)
+               sample = -sample;             /* get magnitude */
+
+       /* Convert from 16 bit linear to ulaw. */
+       sample = sample + BIAS;
+       exponent = exp_lut[(sample >> 7) & 0xFF];
+       mantissa = (sample >> (exponent + 3)) & 0x0F;
+       ulawbyte = ~(sign | (exponent << 4) | mantissa);
+
+       return ulawbyte;
+}
+
+static int reverse_bits(int i)
+{
+       int z, j;
+       z = 0;
+
+       for (j = 0; j < 8; j++) {
+               if ((i & (1 << j)) != 0)
+                       z |= 1 << (7 - j);
+       }
+       return z;
+}
+
+
+void dsp_audio_generate_law_tables(void)
+{
+       int i;
+       for (i = 0; i < 256; i++)
+               dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i));
+
+       for (i = 0; i < 256; i++)
+               dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i));
+
+       for (i = 0; i < 256; i++) {
+               dsp_audio_alaw_to_ulaw[i] =
+                       linear2ulaw(dsp_audio_alaw_to_s32[i]);
+               dsp_audio_ulaw_to_alaw[i] =
+                       linear2alaw(dsp_audio_ulaw_to_s32[i]);
+       }
+}
+
+void
+dsp_audio_generate_s2law_table(void)
+{
+       int i;
+
+       if (dsp_options & DSP_OPT_ULAW) {
+               /* generating ulaw-table */
+               for (i = -32768; i < 32768; i++) {
+                       dsp_audio_s16_to_law[i & 0xffff] =
+                               reverse_bits(linear2ulaw(i));
+               }
+       } else {
+               /* generating alaw-table */
+               for (i = -32768; i < 32768; i++) {
+                       dsp_audio_s16_to_law[i & 0xffff] =
+                               reverse_bits(linear2alaw(i));
+               }
+       }
+}
+
+
+/*
+ * the seven bit sample is the number of every second alaw-sample ordered by
+ * aplitude. 0x00 is negative, 0x7f is positive amplitude.
+ */
+u8 dsp_audio_seven2law[128];
+u8 dsp_audio_law2seven[256];
+
+/********************************************************************
+ * generate table for conversion law from/to 7-bit alaw-like sample *
+ ********************************************************************/
+
+void
+dsp_audio_generate_seven(void)
+{
+       int i, j, k;
+       u8 spl;
+       u8 sorted_alaw[256];
+
+       /* generate alaw table, sorted by the linear value */
+       for (i = 0; i < 256; i++) {
+               j = 0;
+               for (k = 0; k < 256; k++) {
+                       if (dsp_audio_alaw_to_s32[k]
+                               < dsp_audio_alaw_to_s32[i]) {
+                       j++;
+                       }
+               }
+               sorted_alaw[j] = i;
+       }
+
+       /* generate tabels */
+       for (i = 0; i < 256; i++) {
+               /* spl is the source: the law-sample (converted to alaw) */
+               spl = i;
+               if (dsp_options & DSP_OPT_ULAW)
+                       spl = dsp_audio_ulaw_to_alaw[i];
+               /* find the 7-bit-sample */
+               for (j = 0; j < 256; j++) {
+                       if (sorted_alaw[j] == spl)
+                               break;
+               }
+               /* write 7-bit audio value */
+               dsp_audio_law2seven[i] = j >> 1;
+       }
+       for (i = 0; i < 128; i++) {
+               spl = sorted_alaw[i << 1];
+               if (dsp_options & DSP_OPT_ULAW)
+                       spl = dsp_audio_alaw_to_ulaw[spl];
+               dsp_audio_seven2law[i] = spl;
+       }
+}
+
+
+/* mix 2*law -> law */
+u8 dsp_audio_mix_law[65536];
+
+/******************************************************
+ * generate mix table to mix two law samples into one *
+ ******************************************************/
+
+void
+dsp_audio_generate_mix_table(void)
+{
+       int i, j;
+       s32 sample;
+
+       i = 0;
+       while (i < 256) {
+               j = 0;
+               while (j < 256) {
+                       sample = dsp_audio_law_to_s32[i];
+                       sample += dsp_audio_law_to_s32[j];
+                       if (sample > 32767)
+                               sample = 32767;
+                       if (sample < -32768)
+                               sample = -32768;
+                       dsp_audio_mix_law[(i<<8)|j] =
+                               dsp_audio_s16_to_law[sample & 0xffff];
+                       j++;
+               }
+               i++;
+       }
+}
+
+
+/*************************************
+ * generate different volume changes *
+ *************************************/
+
+static u8 dsp_audio_reduce8[256];
+static u8 dsp_audio_reduce7[256];
+static u8 dsp_audio_reduce6[256];
+static u8 dsp_audio_reduce5[256];
+static u8 dsp_audio_reduce4[256];
+static u8 dsp_audio_reduce3[256];
+static u8 dsp_audio_reduce2[256];
+static u8 dsp_audio_reduce1[256];
+static u8 dsp_audio_increase1[256];
+static u8 dsp_audio_increase2[256];
+static u8 dsp_audio_increase3[256];
+static u8 dsp_audio_increase4[256];
+static u8 dsp_audio_increase5[256];
+static u8 dsp_audio_increase6[256];
+static u8 dsp_audio_increase7[256];
+static u8 dsp_audio_increase8[256];
+
+static u8 *dsp_audio_volume_change[16] = {
+       dsp_audio_reduce8,
+       dsp_audio_reduce7,
+       dsp_audio_reduce6,
+       dsp_audio_reduce5,
+       dsp_audio_reduce4,
+       dsp_audio_reduce3,
+       dsp_audio_reduce2,
+       dsp_audio_reduce1,
+       dsp_audio_increase1,
+       dsp_audio_increase2,
+       dsp_audio_increase3,
+       dsp_audio_increase4,
+       dsp_audio_increase5,
+       dsp_audio_increase6,
+       dsp_audio_increase7,
+       dsp_audio_increase8,
+};
+
+void
+dsp_audio_generate_volume_changes(void)
+{
+       register s32 sample;
+       int i;
+       int num[]   = { 110, 125, 150, 175, 200, 300, 400, 500 };
+       int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 };
+
+       i = 0;
+       while (i < 256) {
+               dsp_audio_reduce8[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff];
+               dsp_audio_reduce7[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff];
+               dsp_audio_reduce6[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff];
+               dsp_audio_reduce5[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff];
+               dsp_audio_reduce4[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff];
+               dsp_audio_reduce3[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff];
+               dsp_audio_reduce2[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff];
+               dsp_audio_reduce1[i] = dsp_audio_s16_to_law[
+                       (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[0] / denum[0];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[1] / denum[1];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[2] / denum[2];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[3] / denum[3];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[4] / denum[4];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[5] / denum[5];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[6] / denum[6];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff];
+               sample = dsp_audio_law_to_s32[i] * num[7] / denum[7];
+               if (sample < -32768)
+                       sample = -32768;
+               else if (sample > 32767)
+                       sample = 32767;
+               dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff];
+
+               i++;
+       }
+}
+
+
+/**************************************
+ * change the volume of the given skb *
+ **************************************/
+
+/* this is a helper function for changing volume of skb. the range may be
+ * -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8
+ */
+void
+dsp_change_volume(struct sk_buff *skb, int volume)
+{
+       u8 *volume_change;
+       int i, ii;
+       u8 *p;
+       int shift;
+
+       if (volume == 0)
+               return;
+
+       /* get correct conversion table */
+       if (volume < 0) {
+               shift = volume + 8;
+               if (shift < 0)
+                       shift = 0;
+       } else {
+               shift = volume + 7;
+               if (shift > 15)
+                       shift = 15;
+       }
+       volume_change = dsp_audio_volume_change[shift];
+       i = 0;
+       ii = skb->len;
+       p = skb->data;
+       /* change volume */
+       while (i < ii) {
+               *p = volume_change[*p];
+               p++;
+               i++;
+       }
+}
+
diff --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h
new file mode 100644 (file)
index 0000000..038191b
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * biquad.h - General telephony bi-quad section routines (currently this just
+ *            handles canonic/type 2 form)
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+struct biquad2_state {
+       int32_t gain;
+       int32_t a1;
+       int32_t a2;
+       int32_t b1;
+       int32_t b2;
+
+       int32_t z1;
+       int32_t z2;
+};
+
+static inline void biquad2_init(struct biquad2_state *bq,
+    int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2)
+{
+       bq->gain = gain;
+       bq->a1 = a1;
+       bq->a2 = a2;
+       bq->b1 = b1;
+       bq->b2 = b2;
+
+       bq->z1 = 0;
+       bq->z2 = 0;
+}
+
+static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample)
+{
+       int32_t y;
+       int32_t z0;
+
+       z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2;
+       y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2;
+
+       bq->z2 = bq->z1;
+       bq->z1 = z0 >> 15;
+       y >>= 15;
+       return  y;
+}
diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c
new file mode 100644 (file)
index 0000000..18e411e
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Blowfish encryption/decryption for mISDN_dsp.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+/*
+ * how to encode a sample stream to 64-bit blocks that will be encryped
+ *
+ * first of all, data is collected until a block of 9 samples are received.
+ * of course, a packet may have much more than 9 sample, but is may have
+ * not excacly the multiple of 9 samples. if there is a rest, the next
+ * received data will complete the block.
+ *
+ * the block is then converted to 9 uLAW samples without the least sigificant
+ * bit. the result is a 7-bit encoded sample.
+ *
+ * the samples will be reoganised to form 8 bytes of data:
+ * (5(6) means: encoded sample no. 5, bit 6)
+ *
+ * 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6)
+ * 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5)
+ * 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4)
+ * 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3)
+ * 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2)
+ * 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1)
+ * 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
+ * 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0)
+ *
+ * the missing bit 0 of the last byte is filled with some
+ * random noise, to fill all 8 bytes.
+ *
+ * the 8 bytes will be encrypted using blowfish.
+ *
+ * the result will be converted into 9 bytes. the bit 7 is used for
+ * checksumme (CS) for sync (0, 1) and for the last bit:
+ * (5(6) means: crypted byte 5, bit 6)
+ *
+ * 1    0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1)
+ * 0    0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2)
+ * 0    1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3)
+ * 0    2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4)
+ * 0    3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5)
+ * CS   4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6)
+ * CS   5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7)
+ * CS   6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0)
+ * 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
+ *
+ * the checksum is used to detect transmission errors and frame drops.
+ *
+ * synchronisation of received block is done by shifting the upper bit of each
+ * byte (bit 7) to a shift register. if the rigister has the first five bits
+ * (10000), this is used to find the sync. only if sync has been found, the
+ * current block of 9 received bytes are decrypted. before that the check
+ * sum is calculated. if it is incorrect the block is dropped.
+ * this will avoid loud noise due to corrupt encrypted data.
+ *
+ * if the last block is corrupt, the current decoded block is repeated
+ * until a valid block has been received.
+ */
+
+/*
+ *  some blowfish parts are taken from the
+ * crypto-api for faster implementation
+ */
+
+struct bf_ctx {
+       u32 p[18];
+       u32 s[1024];
+};
+
+static const u32 bf_pbox[16 + 2] = {
+       0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+       0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+       0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+       0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+       0x9216d5d9, 0x8979fb1b,
+};
+
+static const u32 bf_sbox[256 * 4] = {
+       0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+       0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+       0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+       0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+       0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+       0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+       0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+       0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+       0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+       0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+       0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+       0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+       0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+       0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+       0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+       0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+       0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+       0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+       0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+       0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+       0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+       0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+       0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+       0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+       0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+       0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+       0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+       0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+       0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+       0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+       0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+       0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+       0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+       0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+       0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+       0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+       0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+       0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+       0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+       0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+       0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+       0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+       0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+       0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+       0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+       0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+       0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+       0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+       0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+       0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+       0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+       0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+       0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+       0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+       0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+       0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+       0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+       0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+       0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+       0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+       0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+       0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+       0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+       0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+       0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+       0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+       0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+       0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+       0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+       0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+       0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+       0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+       0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+       0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+       0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+       0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+       0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+       0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+       0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+       0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+       0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+       0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+       0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+       0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+       0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+       0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+       0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+       0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+       0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+       0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+       0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+       0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+       0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+       0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+       0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+       0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+       0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+       0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+       0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+       0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+       0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+       0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+       0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+       0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+       0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+       0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+       0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+       0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+       0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+       0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+       0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+       0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+       0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+       0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+       0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+       0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+       0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+       0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+       0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+       0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+       0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+       0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+       0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+       0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+       0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+       0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+       0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+       0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+       0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+       0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+       0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+       0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+       0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+       0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+       0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+       0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+       0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+       0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+       0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+       0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+       0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+       0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+       0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+       0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+       0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+       0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+       0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+       0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+       0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+       0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+       0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+       0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+       0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+       0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+       0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+       0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+       0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+       0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+       0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+       0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+       0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+       0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+       0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+       0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+       0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+       0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+       0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+       0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+       0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+       0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+       0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+       0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+       0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+       0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+       0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+       0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+       0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+       0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+       0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+       0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+       0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+       0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+       0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+       0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+       0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+       0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+       0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+       0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+       0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+       0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+       0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+       0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+       0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+       0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+       0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+       0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+       0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+       0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+       0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+       0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+       0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+       0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+       0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+       0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+       0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+       0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+       0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+       0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+       0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+       0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+       0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+       0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+       0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+       0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+       0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+       0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+       0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+       0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+       0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+       0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+       0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+       0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+       0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+       0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+       0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+       0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+       0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+       0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+       0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+       0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+       0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+       0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+       0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+       0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+       0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+       0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+       0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+       0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+       0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+       0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+       0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+       0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+       0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+       0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+       0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+       0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+       0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+       0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+       0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+       0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+       0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+       0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+       0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+       0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+       0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+       0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+};
+
+/*
+ * Round loop unrolling macros, S is a pointer to a S-Box array
+ * organized in 4 unsigned longs at a row.
+ */
+#define GET32_3(x) (((x) & 0xff))
+#define GET32_2(x) (((x) >> (8)) & (0xff))
+#define GET32_1(x) (((x) >> (16)) & (0xff))
+#define GET32_0(x) (((x) >> (24)) & (0xff))
+
+#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \
+    S[512 + GET32_2(x)]) + S[768 + GET32_3(x)])
+
+#define EROUND(a, b, n)  do { b ^= P[n]; a ^= bf_F(b); } while (0)
+#define DROUND(a, b, n)  do { a ^= bf_F(b); b ^= P[n]; } while (0)
+
+
+/*
+ * encrypt isdn data frame
+ * every block with 9 samples is encrypted
+ */
+void
+dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len)
+{
+       int i = 0, j = dsp->bf_crypt_pos;
+       u8 *bf_data_in = dsp->bf_data_in;
+       u8 *bf_crypt_out = dsp->bf_crypt_out;
+       u32 *P = dsp->bf_p;
+       u32 *S = dsp->bf_s;
+       u32 yl, yr;
+       u32 cs;
+       u8 nibble;
+
+       while (i < len) {
+               /* collect a block of 9 samples */
+               if (j < 9) {
+                       bf_data_in[j] = *data;
+                       *data++ = bf_crypt_out[j++];
+                       i++;
+                       continue;
+               }
+               j = 0;
+               /* transcode 9 samples xlaw to 8 bytes */
+               yl = dsp_audio_law2seven[bf_data_in[0]];
+               yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[1]];
+               yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[2]];
+               yl = (yl<<7) | dsp_audio_law2seven[bf_data_in[3]];
+               nibble = dsp_audio_law2seven[bf_data_in[4]];
+               yr = nibble;
+               yl = (yl<<4) | (nibble>>3);
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[5]];
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[6]];
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[7]];
+               yr = (yr<<7) | dsp_audio_law2seven[bf_data_in[8]];
+               yr = (yr<<1) | (bf_data_in[0] & 1);
+
+               /* fill unused bit with random noise of audio input */
+               /* encrypt */
+
+               EROUND(yr, yl, 0);
+               EROUND(yl, yr, 1);
+               EROUND(yr, yl, 2);
+               EROUND(yl, yr, 3);
+               EROUND(yr, yl, 4);
+               EROUND(yl, yr, 5);
+               EROUND(yr, yl, 6);
+               EROUND(yl, yr, 7);
+               EROUND(yr, yl, 8);
+               EROUND(yl, yr, 9);
+               EROUND(yr, yl, 10);
+               EROUND(yl, yr, 11);
+               EROUND(yr, yl, 12);
+               EROUND(yl, yr, 13);
+               EROUND(yr, yl, 14);
+               EROUND(yl, yr, 15);
+               yl ^= P[16];
+               yr ^= P[17];
+
+               /* calculate 3-bit checksumme */
+               cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15)
+                       ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30)
+                       ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10)
+                       ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25)
+                       ^ (yr>>28) ^ (yr>>31);
+
+               /*
+                * transcode 8 crypted bytes to 9 data bytes with sync
+                * and checksum information
+                */
+               bf_crypt_out[0] = (yl>>25) | 0x80;
+               bf_crypt_out[1] = (yl>>18) & 0x7f;
+               bf_crypt_out[2] = (yl>>11) & 0x7f;
+               bf_crypt_out[3] = (yl>>4) & 0x7f;
+               bf_crypt_out[4] = ((yl<<3) & 0x78) | ((yr>>29) & 0x07);
+               bf_crypt_out[5] = ((yr>>22) & 0x7f) | ((cs<<5) & 0x80);
+               bf_crypt_out[6] = ((yr>>15) & 0x7f) | ((cs<<6) & 0x80);
+               bf_crypt_out[7] = ((yr>>8) & 0x7f) | (cs<<7);
+               bf_crypt_out[8] = yr;
+       }
+
+       /* write current count */
+       dsp->bf_crypt_pos = j;
+
+}
+
+
+/*
+ * decrypt isdn data frame
+ * every block with 9 bytes is decrypted
+ */
+void
+dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len)
+{
+       int i = 0;
+       u8 j = dsp->bf_decrypt_in_pos;
+       u8 k = dsp->bf_decrypt_out_pos;
+       u8 *bf_crypt_inring = dsp->bf_crypt_inring;
+       u8 *bf_data_out = dsp->bf_data_out;
+       u16 sync = dsp->bf_sync;
+       u32 *P = dsp->bf_p;
+       u32 *S = dsp->bf_s;
+       u32 yl, yr;
+       u8 nibble;
+       u8 cs, cs0, cs1, cs2;
+
+       while (i < len) {
+               /*
+                * shift upper bit and rotate data to buffer ring
+                * send current decrypted data
+                */
+               sync = (sync<<1) | ((*data)>>7);
+               bf_crypt_inring[j++ & 15] = *data;
+               *data++ = bf_data_out[k++];
+               i++;
+               if (k == 9)
+                       k = 0; /* repeat if no sync has been found */
+               /* check if not in sync */
+               if ((sync&0x1f0) != 0x100)
+                       continue;
+               j -= 9;
+               /* transcode receive data to 64 bit block of encrypted data */
+               yl = bf_crypt_inring[j++ & 15];
+               yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               yl = (yl<<7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+               yr = nibble;
+               yl = (yl<<4) | (nibble>>3);
+               cs2 = bf_crypt_inring[j++ & 15];
+               yr = (yr<<7) | (cs2 & 0x7f);
+               cs1 = bf_crypt_inring[j++ & 15];
+               yr = (yr<<7) | (cs1 & 0x7f);
+               cs0 = bf_crypt_inring[j++ & 15];
+               yr = (yr<<7) | (cs0 & 0x7f);
+               yr = (yr<<8) | bf_crypt_inring[j++ & 15];
+
+               /* calculate 3-bit checksumme */
+               cs = yl ^ (yl>>3) ^ (yl>>6) ^ (yl>>9) ^ (yl>>12) ^ (yl>>15)
+                       ^ (yl>>18) ^ (yl>>21) ^ (yl>>24) ^ (yl>>27) ^ (yl>>30)
+                       ^ (yr<<2) ^ (yr>>1) ^ (yr>>4) ^ (yr>>7) ^ (yr>>10)
+                       ^ (yr>>13) ^ (yr>>16) ^ (yr>>19) ^ (yr>>22) ^ (yr>>25)
+                       ^ (yr>>28) ^ (yr>>31);
+
+               /* check if frame is valid */
+               if ((cs&0x7) != (((cs2>>5)&4) | ((cs1>>6)&2) | (cs0 >> 7))) {
+                       if (dsp_debug & DEBUG_DSP_BLOWFISH)
+                               printk(KERN_DEBUG
+                                   "DSP BLOWFISH: received corrupt frame, "
+                                   "checksumme is not correct\n");
+                       continue;
+               }
+
+               /* decrypt */
+               yr ^= P[17];
+               yl ^= P[16];
+               DROUND(yl, yr, 15);
+               DROUND(yr, yl, 14);
+               DROUND(yl, yr, 13);
+               DROUND(yr, yl, 12);
+               DROUND(yl, yr, 11);
+               DROUND(yr, yl, 10);
+               DROUND(yl, yr, 9);
+               DROUND(yr, yl, 8);
+               DROUND(yl, yr, 7);
+               DROUND(yr, yl, 6);
+               DROUND(yl, yr, 5);
+               DROUND(yr, yl, 4);
+               DROUND(yl, yr, 3);
+               DROUND(yr, yl, 2);
+               DROUND(yl, yr, 1);
+               DROUND(yr, yl, 0);
+
+               /* transcode 8 crypted bytes to 9 sample bytes */
+               bf_data_out[0] = dsp_audio_seven2law[(yl>>25) & 0x7f];
+               bf_data_out[1] = dsp_audio_seven2law[(yl>>18) & 0x7f];
+               bf_data_out[2] = dsp_audio_seven2law[(yl>>11) & 0x7f];
+               bf_data_out[3] = dsp_audio_seven2law[(yl>>4) & 0x7f];
+               bf_data_out[4] = dsp_audio_seven2law[((yl<<3) & 0x78) |
+                   ((yr>>29) & 0x07)];
+
+               bf_data_out[5] = dsp_audio_seven2law[(yr>>22) & 0x7f];
+               bf_data_out[6] = dsp_audio_seven2law[(yr>>15) & 0x7f];
+               bf_data_out[7] = dsp_audio_seven2law[(yr>>8) & 0x7f];
+               bf_data_out[8] = dsp_audio_seven2law[(yr>>1) & 0x7f];
+               k = 0; /* start with new decoded frame */
+       }
+
+       /* write current count and sync */
+       dsp->bf_decrypt_in_pos = j;
+       dsp->bf_decrypt_out_pos = k;
+       dsp->bf_sync = sync;
+}
+
+
+/* used to encrypt S and P boxes */
+static inline void
+encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src)
+{
+       u32 yl = src[0];
+       u32 yr = src[1];
+
+       EROUND(yr, yl, 0);
+       EROUND(yl, yr, 1);
+       EROUND(yr, yl, 2);
+       EROUND(yl, yr, 3);
+       EROUND(yr, yl, 4);
+       EROUND(yl, yr, 5);
+       EROUND(yr, yl, 6);
+       EROUND(yl, yr, 7);
+       EROUND(yr, yl, 8);
+       EROUND(yl, yr, 9);
+       EROUND(yr, yl, 10);
+       EROUND(yl, yr, 11);
+       EROUND(yr, yl, 12);
+       EROUND(yl, yr, 13);
+       EROUND(yr, yl, 14);
+       EROUND(yl, yr, 15);
+
+       yl ^= P[16];
+       yr ^= P[17];
+
+       dst[0] = yr;
+       dst[1] = yl;
+}
+
+/*
+ * initialize the dsp for encryption and decryption using the same key
+ * Calculates the blowfish S and P boxes for encryption and decryption.
+ * The margin of keylen must be 4-56 bytes.
+ * returns 0 if ok.
+ */
+int
+dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen)
+{
+       short i, j, count;
+       u32 data[2], temp;
+       u32 *P = (u32 *)dsp->bf_p;
+       u32 *S = (u32 *)dsp->bf_s;
+
+       if (keylen < 4 || keylen > 56)
+               return 1;
+
+       /* Set dsp states */
+       i = 0;
+       while (i < 9) {
+               dsp->bf_crypt_out[i] = 0xff;
+               dsp->bf_data_out[i] = dsp_silence;
+               i++;
+       }
+       dsp->bf_crypt_pos = 0;
+       dsp->bf_decrypt_in_pos = 0;
+       dsp->bf_decrypt_out_pos = 0;
+       dsp->bf_sync = 0x1ff;
+       dsp->bf_enable = 1;
+
+       /* Copy the initialization s-boxes */
+       for (i = 0, count = 0; i < 256; i++)
+               for (j = 0; j < 4; j++, count++)
+                       S[count] = bf_sbox[count];
+
+       /* Set the p-boxes */
+       for (i = 0; i < 16 + 2; i++)
+               P[i] = bf_pbox[i];
+
+       /* Actual subkey generation */
+       for (j = 0, i = 0; i < 16 + 2; i++) {
+               temp = (((u32)key[j] << 24) |
+                   ((u32)key[(j + 1) % keylen] << 16) |
+                   ((u32)key[(j + 2) % keylen] << 8) |
+                   ((u32)key[(j + 3) % keylen]));
+
+               P[i] = P[i] ^ temp;
+               j = (j + 4) % keylen;
+       }
+
+       data[0] = 0x00000000;
+       data[1] = 0x00000000;
+
+       for (i = 0; i < 16 + 2; i += 2) {
+               encrypt_block(P, S, data, data);
+
+               P[i] = data[0];
+               P[i + 1] = data[1];
+       }
+
+       for (i = 0; i < 4; i++) {
+               for (j = 0, count = i * 256; j < 256; j += 2, count += 2) {
+                       encrypt_block(P, S, data, data);
+
+                       S[count] = data[0];
+                       S[count + 1] = data[1];
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+ * turn encryption off
+ */
+void
+dsp_bf_cleanup(struct dsp *dsp)
+{
+       dsp->bf_enable = 0;
+}
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
new file mode 100644 (file)
index 0000000..e92b1ba
--- /dev/null
@@ -0,0 +1,1886 @@
+/*
+ * Audio crossconnecting/conferrencing (hardware level).
+ *
+ * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*
+ * The process of adding and removing parties to/from a conference:
+ *
+ * There is a chain of struct dsp_conf which has one or more members in a chain
+ * of struct dsp_conf_member.
+ *
+ * After a party is added, the conference is checked for hardware capability.
+ * Also if a party is removed, the conference is checked again.
+ *
+ * There are 3 different solutions: -1 = software, 0 = hardware-crossconnect
+ * 1-n = hardware-conference. The n will give the conference number.
+ *
+ * Depending on the change after removal or insertion of a party, hardware
+ * commands are given.
+ *
+ * The current solution is stored within the struct dsp_conf entry.
+ */
+
+/*
+ * HOW THE CMX WORKS:
+ *
+ * There are 3 types of interaction: One member is alone, in this case only
+ * data flow from upper to lower layer is done.
+ * Two members will also exchange their data so they are crossconnected.
+ * Three or more members will be added in a conference and will hear each
+ * other but will not receive their own speech (echo) if not enabled.
+ *
+ * Features of CMX are:
+ *  - Crossconnecting or even conference, if more than two members are together.
+ *  - Force mixing of transmit data with other crossconnect/conference members.
+ *  - Echo generation to benchmark the delay of audio processing.
+ *  - Use hardware to minimize cpu load, disable FIFO load and minimize delay.
+ *  - Dejittering and clock generation.
+ *
+ * There are 2 buffers:
+ *
+ *
+ * RX-Buffer
+ *                 R             W
+ *                 |             |
+ * ----------------+-------------+-------------------
+ *
+ * The rx-buffer is a ring buffer used to store the received data for each
+ * individual member. This is only the case if data needs to be dejittered
+ * or in case of a conference where different clocks require reclocking.
+ * The transmit-clock (R) will read the buffer.
+ * If the clock overruns the write-pointer, we will have a buffer underrun.
+ * If the write pointer always has a certain distance from the transmit-
+ * clock, we will have a delay. The delay will dynamically be increased and
+ * reduced.
+ *
+ *
+ * TX-Buffer
+ *                  R        W
+ *                  |        |
+ * -----------------+--------+-----------------------
+ *
+ * The tx-buffer is a ring buffer to queue the transmit data from user space
+ * until it will be mixed or sent. There are two pointers, R and W. If the write
+ * pointer W would reach or overrun R, the buffer would overrun. In this case
+ * (some) data is dropped so that it will not overrun.
+ * Additionally a dynamic dejittering can be enabled. this allows data from
+ * user space that have jitter and different clock source.
+ *
+ *
+ * Clock:
+ *
+ * A Clock is not required, if the data source has exactly one clock. In this
+ * case the data source is forwarded to the destination.
+ *
+ * A Clock is required, because the data source
+ *  - has multiple clocks.
+ *  - has no usable clock due to jitter or packet loss (VoIP).
+ * In this case the system's clock is used. The clock resolution depends on
+ * the jiffie resolution.
+ *
+ * If a member joins a conference:
+ *
+ * - If a member joins, its rx_buff is set to silence and change read pointer
+ *   to transmit clock.
+ *
+ * The procedure of received data from card is explained in cmx_receive.
+ * The procedure of received data from user space is explained in cmx_transmit.
+ * The procedure of transmit data to card is cmx_send.
+ *
+ *
+ * Interaction with other features:
+ *
+ * DTMF:
+ * DTMF decoding is done before the data is crossconnected.
+ *
+ * Volume change:
+ * Changing rx-volume is done before the data is crossconnected. The tx-volume
+ * must be changed whenever data is transmitted to the card by the cmx.
+ *
+ * Tones:
+ * If a tone is enabled, it will be processed whenever data is transmitted to
+ * the card. It will replace the tx-data from the user space.
+ * If tones are generated by hardware, this conference member is removed for
+ * this time.
+ *
+ * Disable rx-data:
+ * If cmx is realized in hardware, rx data will be disabled if requested by
+ * the upper layer. If dtmf decoding is done by software and enabled, rx data
+ * will not be diabled but blocked to the upper layer.
+ *
+ * HFC conference engine:
+ * If it is possible to realize all features using hardware, hardware will be
+ * used if not forbidden by control command. Disabling rx-data provides
+ * absolutely traffic free audio processing. (except for the quick 1-frame
+ * upload of a tone loop, only once for a new tone)
+ *
+ */
+
+/* delay.h is required for hw_lock.h */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+/*
+ * debugging of multi party conference,
+ * by using conference even with two members
+ */
+
+/* #define CMX_CONF_DEBUG */
+
+/*#define CMX_DEBUG * massive read/write pointer output */
+/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */
+
+static inline int
+count_list_member(struct list_head *head)
+{
+       int                     cnt = 0;
+       struct list_head        *m;
+
+       list_for_each(m, head)
+               cnt++;
+       return cnt;
+}
+
+/*
+ * debug cmx memory structure
+ */
+void
+dsp_cmx_debug(struct dsp *dsp)
+{
+       struct dsp_conf *conf;
+       struct dsp_conf_member  *member;
+       struct dsp              *odsp;
+
+       printk(KERN_DEBUG "-----Current DSP\n");
+       list_for_each_entry(odsp, &dsp_ilist, list) {
+               printk(KERN_DEBUG "* %s echo=%d txmix=%d",
+                   odsp->name, odsp->echo, odsp->tx_mix);
+               if (odsp->conf)
+                       printk(" (Conf %d)", odsp->conf->id);
+               if (dsp == odsp)
+                       printk(" *this*");
+               printk("\n");
+       }
+       printk(KERN_DEBUG "-----Current Conf:\n");
+       list_for_each_entry(conf, &conf_ilist, list) {
+               printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, conf);
+               list_for_each_entry(member, &conf->mlist, list) {
+                       printk(KERN_DEBUG
+                           "  - member = %s (slot_tx %d, bank_tx %d, "
+                           "slot_rx %d, bank_rx %d hfc_conf %d)%s\n",
+                           member->dsp->name, member->dsp->pcm_slot_tx,
+                           member->dsp->pcm_bank_tx, member->dsp->pcm_slot_rx,
+                           member->dsp->pcm_bank_rx, member->dsp->hfc_conf,
+                           (member->dsp == dsp) ? " *this*" : "");
+               }
+       }
+       printk(KERN_DEBUG "-----end\n");
+}
+
+/*
+ * search conference
+ */
+static struct dsp_conf *
+dsp_cmx_search_conf(u32 id)
+{
+       struct dsp_conf *conf;
+
+       if (!id) {
+               printk(KERN_WARNING "%s: conference ID is 0.\n", __func__);
+               return NULL;
+       }
+
+       /* search conference */
+       list_for_each_entry(conf, &conf_ilist, list)
+               if (conf->id == id)
+                       return conf;
+
+       return NULL;
+}
+
+
+/*
+ * add member to conference
+ */
+static int
+dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf)
+{
+       struct dsp_conf_member *member;
+
+       if (!conf || !dsp) {
+               printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__);
+               return -EINVAL;
+       }
+       if (dsp->member) {
+               printk(KERN_WARNING "%s: dsp is already member in a conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (dsp->conf) {
+               printk(KERN_WARNING "%s: dsp is already in a conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC);
+       if (!member) {
+               printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n");
+               return -ENOMEM;
+       }
+       member->dsp = dsp;
+       /* clear rx buffer */
+       memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+       dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */
+       dsp->rx_W = 0;
+       dsp->rx_R = 0;
+
+       list_add_tail(&member->list, &conf->mlist);
+
+       dsp->conf = conf;
+       dsp->member = member;
+
+       return 0;
+}
+
+
+/*
+ * del member from conference
+ */
+int
+dsp_cmx_del_conf_member(struct dsp *dsp)
+{
+       struct dsp_conf_member *member;
+
+       if (!dsp) {
+               printk(KERN_WARNING "%s: dsp is 0.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (!dsp->conf) {
+               printk(KERN_WARNING "%s: dsp is not in a conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (list_empty(&dsp->conf->mlist)) {
+               printk(KERN_WARNING "%s: dsp has linked an empty conf.\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       /* find us in conf */
+       list_for_each_entry(member, &dsp->conf->mlist, list) {
+               if (member->dsp == dsp) {
+                       list_del(&member->list);
+                       dsp->conf = NULL;
+                       dsp->member = NULL;
+                       kfree(member);
+                       return 0;
+               }
+       }
+       printk(KERN_WARNING
+           "%s: dsp is not present in its own conf_meber list.\n",
+           __func__);
+
+       return -EINVAL;
+}
+
+
+/*
+ * new conference
+ */
+static struct dsp_conf
+*dsp_cmx_new_conf(u32 id)
+{
+       struct dsp_conf *conf;
+
+       if (!id) {
+               printk(KERN_WARNING "%s: id is 0.\n",
+                   __func__);
+               return NULL;
+       }
+
+       conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC);
+       if (!conf) {
+               printk(KERN_ERR "kmalloc struct dsp_conf failed\n");
+               return NULL;
+       }
+       INIT_LIST_HEAD(&conf->mlist);
+       conf->id = id;
+
+       list_add_tail(&conf->list, &conf_ilist);
+
+       return conf;
+}
+
+
+/*
+ * del conference
+ */
+int
+dsp_cmx_del_conf(struct dsp_conf *conf)
+{
+       if (!conf) {
+               printk(KERN_WARNING "%s: conf is null.\n",
+                   __func__);
+               return -EINVAL;
+       }
+
+       if (!list_empty(&conf->mlist)) {
+               printk(KERN_WARNING "%s: conf not empty.\n",
+                   __func__);
+               return -EINVAL;
+       }
+       list_del(&conf->list);
+       kfree(conf);
+
+       return 0;
+}
+
+
+/*
+ * send HW message to hfc card
+ */
+static void
+dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2,
+    u32 param3, u32 param4)
+{
+       struct mISDN_ctrl_req cq;
+
+       memset(&cq, 0, sizeof(cq));
+       cq.op = message;
+       cq.p1 = param1 | (param2 << 8);
+       cq.p2 = param3 | (param4 << 8);
+       if (dsp->ch.peer)
+               dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq);
+}
+
+
+/*
+ * do hardware update and set the software/hardware flag
+ *
+ * either a conference or a dsp instance can be given
+ * if only dsp instance is given, the instance is not associated with a conf
+ * and therefore removed. if a conference is given, the dsp is expected to
+ * be member of that conference.
+ */
+void
+dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp)
+{
+       struct dsp_conf_member  *member, *nextm;
+       struct dsp              *finddsp;
+       int             memb = 0, i, ii, i1, i2;
+       int             freeunits[8];
+       u_char          freeslots[256];
+       int             same_hfc = -1, same_pcm = -1, current_conf = -1,
+           all_conf = 1;
+
+       /* dsp gets updated (no conf) */
+       if (!conf) {
+               if (!dsp)
+                       return;
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG "%s checking dsp %s\n",
+                           __func__, dsp->name);
+one_member:
+               /* remove HFC conference if enabled */
+               if (dsp->hfc_conf >= 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s removing %s from HFC conf %d "
+                                   "because dsp is split\n", __func__,
+                                   dsp->name, dsp->hfc_conf);
+                       dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT,
+                           0, 0, 0, 0);
+                       dsp->hfc_conf = -1;
+               }
+               /* process hw echo */
+               if (dsp->features.pcm_banks < 1)
+                       return;
+               if (!dsp->echo) {
+                       /* NO ECHO: remove PCM slot if assigned */
+                       if (dsp->pcm_slot_tx >= 0 || dsp->pcm_slot_rx >= 0) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG "%s removing %s from"
+                                           " PCM slot %d (TX) %d (RX) because"
+                                           " dsp is split (no echo)\n",
+                                           __func__, dsp->name,
+                                           dsp->pcm_slot_tx, dsp->pcm_slot_rx);
+                               dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_DISC,
+                                   0, 0, 0, 0);
+                               dsp->pcm_slot_tx = -1;
+                               dsp->pcm_bank_tx = -1;
+                               dsp->pcm_slot_rx = -1;
+                               dsp->pcm_bank_rx = -1;
+                       }
+                       return;
+               }
+               /* ECHO: already echo */
+               if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_rx < 0 &&
+                   dsp->pcm_bank_tx == 2 && dsp->pcm_bank_rx == 2)
+                       return;
+               /* ECHO: if slot already assigned */
+               if (dsp->pcm_slot_tx >= 0) {
+                       dsp->pcm_slot_rx = dsp->pcm_slot_tx;
+                       dsp->pcm_bank_tx = 2; /* 2 means loop */
+                       dsp->pcm_bank_rx = 2;
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s refresh %s for echo using slot %d\n",
+                                   __func__, dsp->name,
+                                   dsp->pcm_slot_tx);
+                       dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
+                       return;
+               }
+               /* ECHO: find slot */
+               dsp->pcm_slot_tx = -1;
+               dsp->pcm_slot_rx = -1;
+               memset(freeslots, 1, sizeof(freeslots));
+               list_for_each_entry(finddsp, &dsp_ilist, list) {
+                       if (finddsp->features.pcm_id == dsp->features.pcm_id) {
+                               if (finddsp->pcm_slot_rx >= 0 &&
+                                   finddsp->pcm_slot_rx < sizeof(freeslots))
+                                       freeslots[finddsp->pcm_slot_tx] = 0;
+                               if (finddsp->pcm_slot_tx >= 0 &&
+                                   finddsp->pcm_slot_tx < sizeof(freeslots))
+                                       freeslots[finddsp->pcm_slot_rx] = 0;
+                       }
+               }
+               i = 0;
+               ii = dsp->features.pcm_slots;
+               while (i < ii) {
+                       if (freeslots[i])
+                               break;
+                       i++;
+               }
+               if (i == ii) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s no slot available for echo\n",
+                                   __func__);
+                       /* no more slots available */
+                       return;
+               }
+               /* assign free slot */
+               dsp->pcm_slot_tx = i;
+               dsp->pcm_slot_rx = i;
+               dsp->pcm_bank_tx = 2; /* loop */
+               dsp->pcm_bank_rx = 2;
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s assign echo for %s using slot %d\n",
+                           __func__, dsp->name, dsp->pcm_slot_tx);
+               dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
+                   dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
+               return;
+       }
+
+       /* conf gets updated (all members) */
+       if (dsp_debug & DEBUG_DSP_CMX)
+               printk(KERN_DEBUG "%s checking conference %d\n",
+                   __func__, conf->id);
+
+       if (list_empty(&conf->mlist)) {
+               printk(KERN_ERR "%s: conference whithout members\n",
+                   __func__);
+               return;
+       }
+       member = list_entry(conf->mlist.next, struct dsp_conf_member, list);
+       same_hfc = member->dsp->features.hfc_id;
+       same_pcm = member->dsp->features.pcm_id;
+       /* check all members in our conference */
+       list_for_each_entry(member, &conf->mlist, list) {
+               /* check if member uses mixing */
+               if (member->dsp->tx_mix) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_mix is turned on\n", __func__,
+                                   member->dsp->name);
+conf_software:
+                       list_for_each_entry(member, &conf->mlist, list) {
+                               dsp = member->dsp;
+                               /* remove HFC conference if enabled */
+                               if (dsp->hfc_conf >= 0) {
+                                       if (dsp_debug & DEBUG_DSP_CMX)
+                                               printk(KERN_DEBUG
+                                                   "%s removing %s from HFC "
+                                                   "conf %d because not "
+                                                   "possible with hardware\n",
+                                                   __func__,
+                                                   dsp->name,
+                                                   dsp->hfc_conf);
+                                       dsp_cmx_hw_message(dsp,
+                                           MISDN_CTRL_HFC_CONF_SPLIT,
+                                           0, 0, 0, 0);
+                                       dsp->hfc_conf = -1;
+                               }
+                               /* remove PCM slot if assigned */
+                               if (dsp->pcm_slot_tx >= 0 ||
+                                   dsp->pcm_slot_rx >= 0) {
+                                       if (dsp_debug & DEBUG_DSP_CMX)
+                                               printk(KERN_DEBUG "%s removing "
+                                                   "%s from PCM slot %d (TX)"
+                                                   " slot %d (RX) because not"
+                                                   " possible with hardware\n",
+                                                   __func__,
+                                                   dsp->name,
+                                                   dsp->pcm_slot_tx,
+                                                   dsp->pcm_slot_rx);
+                                       dsp_cmx_hw_message(dsp,
+                                           MISDN_CTRL_HFC_PCM_DISC,
+                                           0, 0, 0, 0);
+                                       dsp->pcm_slot_tx = -1;
+                                       dsp->pcm_bank_tx = -1;
+                                       dsp->pcm_slot_rx = -1;
+                                       dsp->pcm_bank_rx = -1;
+                               }
+                       }
+                       conf->hardware = 0;
+                       conf->software = 1;
+                       return;
+               }
+               /* check if member has echo turned on */
+               if (member->dsp->echo) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "echo is turned on\n", __func__,
+                                   member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if member has tx_mix turned on */
+               if (member->dsp->tx_mix) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_mix is turned on\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if member changes volume at an not suppoted level */
+               if (member->dsp->tx_volume) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_volume is changed\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               if (member->dsp->rx_volume) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "rx_volume is changed\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if tx-data turned on */
+               if (member->dsp->tx_data) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "tx_data is turned on\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if pipeline exists */
+               if (member->dsp->pipeline.inuse) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "pipeline exists\n", __func__,
+                                   member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if encryption is enabled */
+               if (member->dsp->bf_enable) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG "%s dsp %s cannot form a "
+                                   "conf, because encryption is enabled\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if member is on a card with PCM support */
+               if (member->dsp->features.pcm_id < 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "dsp has no PCM bus\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* check if relations are on the same PCM bus */
+               if (member->dsp->features.pcm_id != same_pcm) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s dsp %s cannot form a conf, because "
+                                   "dsp is on a different PCM bus than the "
+                                   "first dsp\n",
+                                   __func__, member->dsp->name);
+                       goto conf_software;
+               }
+               /* determine if members are on the same hfc chip */
+               if (same_hfc != member->dsp->features.hfc_id)
+                       same_hfc = -1;
+               /* if there are members already in a conference */
+               if (current_conf < 0 && member->dsp->hfc_conf >= 0)
+                       current_conf = member->dsp->hfc_conf;
+               /* if any member is not in a conference */
+               if (member->dsp->hfc_conf < 0)
+                       all_conf = 0;
+
+               memb++;
+       }
+
+       /* if no member, this is an error */
+       if (memb < 1)
+               return;
+
+       /* one member */
+       if (memb == 1) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s conf %d cannot form a HW conference, "
+                           "because dsp is alone\n", __func__, conf->id);
+               conf->hardware = 0;
+               conf->software = 0;
+               member = list_entry(conf->mlist.next, struct dsp_conf_member,
+                       list);
+               dsp = member->dsp;
+               goto one_member;
+       }
+
+       /*
+        * ok, now we are sure that all members are on the same pcm.
+        * now we will see if we have only two members, so we can do
+        * crossconnections, which don't have any limitations.
+        */
+
+       /* if we have only two members */
+       if (memb == 2) {
+               member = list_entry(conf->mlist.next, struct dsp_conf_member,
+                       list);
+               nextm = list_entry(member->list.next, struct dsp_conf_member,
+                       list);
+               /* remove HFC conference if enabled */
+               if (member->dsp->hfc_conf >= 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s removing %s from HFC conf %d because "
+                                   "two parties require only a PCM slot\n",
+                                   __func__, member->dsp->name,
+                                   member->dsp->hfc_conf);
+                       dsp_cmx_hw_message(member->dsp,
+                           MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
+                       member->dsp->hfc_conf = -1;
+               }
+               if (nextm->dsp->hfc_conf >= 0) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s removing %s from HFC conf %d because "
+                                   "two parties require only a PCM slot\n",
+                                   __func__, nextm->dsp->name,
+                                   nextm->dsp->hfc_conf);
+                       dsp_cmx_hw_message(nextm->dsp,
+                           MISDN_CTRL_HFC_CONF_SPLIT, 0, 0, 0, 0);
+                       nextm->dsp->hfc_conf = -1;
+               }
+               /* if members have two banks (and not on the same chip) */
+               if (member->dsp->features.pcm_banks > 1 &&
+                   nextm->dsp->features.pcm_banks > 1 &&
+                   member->dsp->features.hfc_id !=
+                   nextm->dsp->features.hfc_id) {
+                       /* if both members have same slots with crossed banks */
+                       if (member->dsp->pcm_slot_tx >= 0 &&
+                           member->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx >= 0 &&
+                           nextm->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx ==
+                           member->dsp->pcm_slot_rx &&
+                           nextm->dsp->pcm_slot_rx ==
+                           member->dsp->pcm_slot_tx &&
+                           nextm->dsp->pcm_slot_tx ==
+                           member->dsp->pcm_slot_tx &&
+                           member->dsp->pcm_bank_tx !=
+                           member->dsp->pcm_bank_rx &&
+                           nextm->dsp->pcm_bank_tx !=
+                           nextm->dsp->pcm_bank_rx) {
+                               /* all members have same slot */
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s dsp %s & %s stay joined on "
+                                           "PCM slot %d bank %d (TX) bank %d "
+                                           "(RX) (on different chips)\n",
+                                           __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name,
+                                           member->dsp->pcm_slot_tx,
+                                           member->dsp->pcm_bank_tx,
+                                           member->dsp->pcm_bank_rx);
+                               conf->hardware = 0;
+                               conf->software = 1;
+                               return;
+                       }
+                       /* find a new slot */
+                       memset(freeslots, 1, sizeof(freeslots));
+                       list_for_each_entry(dsp, &dsp_ilist, list) {
+                               if (dsp != member->dsp &&
+                                   dsp != nextm->dsp &&
+                                   member->dsp->features.pcm_id ==
+                                   dsp->features.pcm_id) {
+                                       if (dsp->pcm_slot_rx >= 0 &&
+                                           dsp->pcm_slot_rx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_tx] = 0;
+                                       if (dsp->pcm_slot_tx >= 0 &&
+                                           dsp->pcm_slot_tx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_rx] = 0;
+                               }
+                       }
+                       i = 0;
+                       ii = member->dsp->features.pcm_slots;
+                       while (i < ii) {
+                               if (freeslots[i])
+                                       break;
+                               i++;
+                       }
+                       if (i == ii) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s no slot available for "
+                                           "%s & %s\n", __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name);
+                               /* no more slots available */
+                               goto conf_software;
+                       }
+                       /* assign free slot */
+                       member->dsp->pcm_slot_tx = i;
+                       member->dsp->pcm_slot_rx = i;
+                       nextm->dsp->pcm_slot_tx = i;
+                       nextm->dsp->pcm_slot_rx = i;
+                       member->dsp->pcm_bank_rx = 0;
+                       member->dsp->pcm_bank_tx = 1;
+                       nextm->dsp->pcm_bank_rx = 1;
+                       nextm->dsp->pcm_bank_tx = 0;
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s adding %s & %s to new PCM slot %d "
+                                   "(TX and RX on different chips) because "
+                                   "both members have not same slots\n",
+                                   __func__,
+                                   member->dsp->name,
+                                   nextm->dsp->name,
+                                   member->dsp->pcm_slot_tx);
+                       dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
+                           member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
+                       dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
+                           nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
+                       conf->hardware = 1;
+                       conf->software = 0;
+                       return;
+               /* if members have one bank (or on the same chip) */
+               } else {
+                       /* if both members have different crossed slots */
+                       if (member->dsp->pcm_slot_tx >= 0 &&
+                           member->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx >= 0 &&
+                           nextm->dsp->pcm_slot_rx >= 0 &&
+                           nextm->dsp->pcm_slot_tx ==
+                           member->dsp->pcm_slot_rx &&
+                           nextm->dsp->pcm_slot_rx ==
+                           member->dsp->pcm_slot_tx &&
+                           member->dsp->pcm_slot_tx !=
+                           member->dsp->pcm_slot_rx &&
+                           member->dsp->pcm_bank_tx == 0 &&
+                           member->dsp->pcm_bank_rx == 0 &&
+                           nextm->dsp->pcm_bank_tx == 0 &&
+                           nextm->dsp->pcm_bank_rx == 0) {
+                               /* all members have same slot */
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s dsp %s & %s stay joined on PCM "
+                                           "slot %d (TX) %d (RX) on same chip "
+                                           "or one bank PCM)\n", __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name,
+                                           member->dsp->pcm_slot_tx,
+                                           member->dsp->pcm_slot_rx);
+                               conf->hardware = 0;
+                               conf->software = 1;
+                               return;
+                       }
+                       /* find two new slot */
+                       memset(freeslots, 1, sizeof(freeslots));
+                       list_for_each_entry(dsp, &dsp_ilist, list) {
+                               if (dsp != member->dsp &&
+                                   dsp != nextm->dsp &&
+                                   member->dsp->features.pcm_id ==
+                                   dsp->features.pcm_id) {
+                                       if (dsp->pcm_slot_rx >= 0 &&
+                                           dsp->pcm_slot_rx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_tx] = 0;
+                                       if (dsp->pcm_slot_tx >= 0 &&
+                                           dsp->pcm_slot_tx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_rx] = 0;
+                               }
+                       }
+                       i1 = 0;
+                       ii = member->dsp->features.pcm_slots;
+                       while (i1 < ii) {
+                               if (freeslots[i1])
+                                       break;
+                               i1++;
+                       }
+                       if (i1 == ii) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s no slot available "
+                                           "for %s & %s\n", __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name);
+                               /* no more slots available */
+                               goto conf_software;
+                       }
+                       i2 = i1+1;
+                       while (i2 < ii) {
+                               if (freeslots[i2])
+                                       break;
+                               i2++;
+                       }
+                       if (i2 == ii) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s no slot available "
+                                           "for %s & %s\n",
+                                           __func__,
+                                           member->dsp->name,
+                                           nextm->dsp->name);
+                               /* no more slots available */
+                               goto conf_software;
+                       }
+                       /* assign free slots */
+                       member->dsp->pcm_slot_tx = i1;
+                       member->dsp->pcm_slot_rx = i2;
+                       nextm->dsp->pcm_slot_tx = i2;
+                       nextm->dsp->pcm_slot_rx = i1;
+                       member->dsp->pcm_bank_rx = 0;
+                       member->dsp->pcm_bank_tx = 0;
+                       nextm->dsp->pcm_bank_rx = 0;
+                       nextm->dsp->pcm_bank_tx = 0;
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s adding %s & %s to new PCM slot %d "
+                                   "(TX) %d (RX) on same chip or one bank "
+                                   "PCM, because both members have not "
+                                   "crossed slots\n", __func__,
+                                   member->dsp->name,
+                                   nextm->dsp->name,
+                                   member->dsp->pcm_slot_tx,
+                                   member->dsp->pcm_slot_rx);
+                       dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           member->dsp->pcm_slot_tx, member->dsp->pcm_bank_tx,
+                           member->dsp->pcm_slot_rx, member->dsp->pcm_bank_rx);
+                       dsp_cmx_hw_message(nextm->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           nextm->dsp->pcm_slot_tx, nextm->dsp->pcm_bank_tx,
+                           nextm->dsp->pcm_slot_rx, nextm->dsp->pcm_bank_rx);
+                       conf->hardware = 1;
+                       conf->software = 0;
+                       return;
+               }
+       }
+
+       /*
+        * if we have more than two, we may check if we have a conference
+        * unit available on the chip. also all members must be on the same
+        */
+
+       /* if not the same HFC chip */
+       if (same_hfc < 0) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s conference %d cannot be formed, because "
+                           "members are on different chips or not "
+                           "on HFC chip\n",
+                           __func__, conf->id);
+               goto conf_software;
+       }
+
+       /* for more than two members.. */
+
+       /* in case of hdlc, we change to software */
+       if (dsp->hdlc)
+               goto conf_software;
+
+       /* if all members already have the same conference */
+       if (all_conf)
+               return;
+
+       /*
+        * if there is an existing conference, but not all members have joined
+        */
+       if (current_conf >= 0) {
+join_members:
+               list_for_each_entry(member, &conf->mlist, list) {
+                       /* join to current conference */
+                       if (member->dsp->hfc_conf == current_conf)
+                               continue;
+                       /* get a free timeslot first */
+                       memset(freeslots, 1, sizeof(freeslots));
+                       list_for_each_entry(dsp, &dsp_ilist, list) {
+                               /*
+                                * not checking current member, because
+                                * slot will be overwritten.
+                                */
+                               if (
+                                   dsp != member->dsp &&
+                               /* dsp must be on the same PCM */
+                                   member->dsp->features.pcm_id ==
+                                   dsp->features.pcm_id) {
+                                       /* dsp must be on a slot */
+                                       if (dsp->pcm_slot_tx >= 0 &&
+                                           dsp->pcm_slot_tx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_tx] = 0;
+                                       if (dsp->pcm_slot_rx >= 0 &&
+                                           dsp->pcm_slot_rx <
+                                           sizeof(freeslots))
+                                               freeslots[dsp->pcm_slot_rx] = 0;
+                               }
+                       }
+                       i = 0;
+                       ii = member->dsp->features.pcm_slots;
+                       while (i < ii) {
+                               if (freeslots[i])
+                                       break;
+                               i++;
+                       }
+                       if (i == ii) {
+                               /* no more slots available */
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s conference %d cannot be formed,"
+                                           " because no slot free\n",
+                                           __func__, conf->id);
+                               goto conf_software;
+                       }
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "%s changing dsp %s to HW conference "
+                                   "%d slot %d\n", __func__,
+                                   member->dsp->name, current_conf, i);
+                       /* assign free slot & set PCM & join conf */
+                       member->dsp->pcm_slot_tx = i;
+                       member->dsp->pcm_slot_rx = i;
+                       member->dsp->pcm_bank_tx = 2; /* loop */
+                       member->dsp->pcm_bank_rx = 2;
+                       member->dsp->hfc_conf = current_conf;
+                       dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_PCM_CONN,
+                           i, 2, i, 2);
+                       dsp_cmx_hw_message(member->dsp,
+                           MISDN_CTRL_HFC_CONF_JOIN, current_conf, 0, 0, 0);
+               }
+               return;
+       }
+
+       /*
+        * no member is in a conference yet, so we find a free one
+        */
+       memset(freeunits, 1, sizeof(freeunits));
+       list_for_each_entry(dsp, &dsp_ilist, list) {
+               /* dsp must be on the same chip */
+               if (dsp->features.hfc_id == same_hfc &&
+                   /* dsp must have joined a HW conference */
+                   dsp->hfc_conf >= 0 &&
+                   /* slot must be within range */
+                   dsp->hfc_conf < 8)
+                       freeunits[dsp->hfc_conf] = 0;
+       }
+       i = 0;
+       ii = 8;
+       while (i < ii) {
+               if (freeunits[i])
+                       break;
+               i++;
+       }
+       if (i == ii) {
+               /* no more conferences available */
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "%s conference %d cannot be formed, because "
+                           "no conference number free\n",
+                           __func__, conf->id);
+               goto conf_software;
+       }
+       /* join all members */
+       current_conf = i;
+       goto join_members;
+}
+
+
+/*
+ * conf_id != 0: join or change conference
+ * conf_id == 0: split from conference if not already
+ */
+int
+dsp_cmx_conf(struct dsp *dsp, u32 conf_id)
+{
+       int err;
+       struct dsp_conf *conf;
+       struct dsp_conf_member  *member;
+
+       /* if conference doesn't change */
+       if (dsp->conf_id == conf_id)
+               return 0;
+
+       /* first remove us from current conf */
+       if (dsp->conf_id) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG "removing us from conference %d\n",
+                               dsp->conf->id);
+               /* remove us from conf */
+               conf = dsp->conf;
+               err = dsp_cmx_del_conf_member(dsp);
+               if (err)
+                       return err;
+               dsp->conf_id = 0;
+
+               /* update hardware */
+               dsp_cmx_hardware(NULL, dsp);
+
+               /* conf now empty? */
+               if (list_empty(&conf->mlist)) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "conference is empty, so we remove it.\n");
+                       err = dsp_cmx_del_conf(conf);
+                       if (err)
+                               return err;
+               } else {
+                       /* update members left on conf */
+                       dsp_cmx_hardware(conf, NULL);
+               }
+       }
+
+       /* if split */
+       if (!conf_id)
+               return 0;
+
+       /* now add us to conf */
+       if (dsp_debug & DEBUG_DSP_CMX)
+               printk(KERN_DEBUG "searching conference %d\n",
+                       conf_id);
+       conf = dsp_cmx_search_conf(conf_id);
+       if (!conf) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "conference doesn't exist yet, creating.\n");
+               /* the conference doesn't exist, so we create */
+               conf = dsp_cmx_new_conf(conf_id);
+               if (!conf)
+                       return -EINVAL;
+       } else if (!list_empty(&conf->mlist)) {
+               member = list_entry(conf->mlist.next, struct dsp_conf_member,
+                       list);
+               if (dsp->hdlc && !member->dsp->hdlc) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "cannot join transparent conference.\n");
+                       return -EINVAL;
+               }
+               if (!dsp->hdlc && member->dsp->hdlc) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "cannot join hdlc conference.\n");
+                       return -EINVAL;
+               }
+       }
+       /* add conference member */
+       err = dsp_cmx_add_conf_member(dsp, conf);
+       if (err)
+               return err;
+       dsp->conf_id = conf_id;
+
+       /* if we are alone, we do nothing! */
+       if (list_empty(&conf->mlist)) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "we are alone in this conference, so exit.\n");
+               /* update hardware */
+               dsp_cmx_hardware(NULL, dsp);
+               return 0;
+       }
+
+       /* update members on conf */
+       dsp_cmx_hardware(conf, NULL);
+
+       return 0;
+}
+
+
+/*
+ * audio data is received from card
+ */
+void
+dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb)
+{
+       u8 *d, *p;
+       int len = skb->len;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       int w, i, ii;
+
+       /* check if we have sompen */
+       if (len < 1)
+               return;
+
+       /* half of the buffer should be larger than maximum packet size */
+       if (len >= CMX_BUFF_HALF) {
+               printk(KERN_ERR
+                   "%s line %d: packet from card is too large (%d bytes). "
+                   "please make card send smaller packets OR increase "
+                   "CMX_BUFF_SIZE\n", __FILE__, __LINE__, len);
+               return;
+       }
+
+       /*
+        * initialize pointers if not already -
+        * also add delay if requested by PH_SIGNAL
+        */
+       if (dsp->rx_init) {
+               dsp->rx_init = 0;
+               if (dsp->features.unordered) {
+                       dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+                       dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+                               & CMX_BUFF_MASK;
+               } else {
+                       dsp->rx_R = 0;
+                       dsp->rx_W = dsp->cmx_delay;
+               }
+       }
+       /* if frame contains time code, write directly */
+       if (dsp->features.unordered) {
+               dsp->rx_W = (hh->id & CMX_BUFF_MASK);
+               /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */
+       }
+       /*
+        * if we underrun (or maybe overrun),
+        * we set our new read pointer, and write silence to buffer
+        */
+       if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       printk(KERN_DEBUG
+                           "cmx_receive(dsp=%lx): UNDERRUN (or overrun the "
+                           "maximum delay), adjusting read pointer! "
+                           "(inst %s)\n", (u_long)dsp, dsp->name);
+               /* flush buffer */
+               if (dsp->features.unordered) {
+                       dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+                       dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+                               & CMX_BUFF_MASK;
+               } else {
+                       dsp->rx_R = 0;
+                       dsp->rx_W = dsp->cmx_delay;
+               }
+               memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+       }
+       /* if we have reached double delay, jump back to middle */
+       if (dsp->cmx_delay)
+               if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >=
+                   (dsp->cmx_delay << 1)) {
+                       if (dsp_debug & DEBUG_DSP_CMX)
+                               printk(KERN_DEBUG
+                                   "cmx_receive(dsp=%lx): OVERRUN (because "
+                                   "twice the delay is reached), adjusting "
+                                   "read pointer! (inst %s)\n",
+                                   (u_long)dsp, dsp->name);
+               /* flush buffer */
+               if (dsp->features.unordered) {
+                       dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+                       dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+                               & CMX_BUFF_MASK;
+               } else {
+                       dsp->rx_R = 0;
+                       dsp->rx_W = dsp->cmx_delay;
+               }
+               memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+       }
+
+       /* show where to write */
+#ifdef CMX_DEBUG
+       printk(KERN_DEBUG
+           "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n",
+           (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name);
+#endif
+
+       /* write data into rx_buffer */
+       p = skb->data;
+       d = dsp->rx_buff;
+       w = dsp->rx_W;
+       i = 0;
+       ii = len;
+       while (i < ii) {
+               d[w++ & CMX_BUFF_MASK] = *p++;
+               i++;
+       }
+
+       /* increase write-pointer */
+       dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
+}
+
+
+/*
+ * send (mixed) audio data to card and control jitter
+ */
+static void
+dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
+{
+       struct dsp_conf *conf = dsp->conf;
+       struct dsp *member, *other;
+       register s32 sample;
+       u8 *d, *p, *q, *o_q;
+       struct sk_buff *nskb, *txskb;
+       int r, rr, t, tt, o_r, o_rr;
+       int preload = 0;
+       struct mISDNhead *hh, *thh;
+
+       /* don't process if: */
+       if (!dsp->b_active) { /* if not active */
+               dsp->last_tx = 0;
+               return;
+       }
+       if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */
+           dsp->tx_R == dsp->tx_W && /* AND no tx-data */
+           !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */
+               dsp->last_tx = 0;
+               return;
+       }
+
+#ifdef CMX_DEBUG
+       printk(KERN_DEBUG
+           "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n",
+           members, dsp->name, conf, dsp->rx_R, dsp->rx_W);
+#endif
+
+       /* preload if we have delay set */
+       if (dsp->cmx_delay && !dsp->last_tx) {
+               preload = len;
+               if (preload < 128)
+                       preload = 128;
+       }
+
+       /* PREPARE RESULT */
+       nskb = mI_alloc_skb(len + preload, GFP_ATOMIC);
+       if (!nskb) {
+               printk(KERN_ERR
+                   "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n",
+                   len + preload);
+               return;
+       }
+       hh = mISDN_HEAD_P(nskb);
+       hh->prim = PH_DATA_REQ;
+       hh->id = 0;
+       dsp->last_tx = 1;
+
+       /* set pointers, indexes and stuff */
+       member = dsp;
+       p = dsp->tx_buff; /* transmit data */
+       q = dsp->rx_buff; /* received data */
+       d = skb_put(nskb, preload + len); /* result */
+       t = dsp->tx_R; /* tx-pointers */
+       tt = dsp->tx_W;
+       r = dsp->rx_R; /* rx-pointers */
+       rr = (r + len) & CMX_BUFF_MASK;
+
+       /* preload with silence, if required */
+       if (preload) {
+               memset(d, dsp_silence, preload);
+               d += preload;
+       }
+
+       /* PROCESS TONES/TX-DATA ONLY */
+       if (dsp->tone.tone && dsp->tone.software) {
+               /* -> copy tone */
+               dsp_tone_copy(dsp, d, len);
+               dsp->tx_R = 0; /* clear tx buffer */
+               dsp->tx_W = 0;
+               goto send_packet;
+       }
+       /* if we have tx-data but do not use mixing */
+       if (!dsp->tx_mix && t != tt) {
+               /* -> send tx-data and continue when not enough */
+#ifdef CMX_TX_DEBUG
+       sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p);
+#endif
+               while (r != rr && t != tt) {
+#ifdef CMX_TX_DEBUG
+                       if (strlen(debugbuf) < 48)
+                           sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]);
+#endif
+                       *d++ = p[t]; /* write tx_buff */
+                       t = (t+1) & CMX_BUFF_MASK;
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+               if (r == rr) {
+                       dsp->tx_R = t;
+#ifdef CMX_TX_DEBUG
+       printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+                       goto send_packet;
+               }
+       }
+#ifdef CMX_TX_DEBUG
+       printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+       /* PROCESS DATA (one member / no conf) */
+       if (!conf || members <= 1) {
+               /* -> if echo is NOT enabled */
+               if (!dsp->echo) {
+                       /* -> send tx-data if available or use 0-volume */
+                       while (r != rr && t != tt) {
+                               *d++ = p[t]; /* write tx_buff */
+                               t = (t+1) & CMX_BUFF_MASK;
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+                       if (r != rr)
+                               memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK);
+               /* -> if echo is enabled */
+               } else {
+                       /*
+                        * -> mix tx-data with echo if available,
+                        * or use echo only
+                        */
+                       while (r != rr && t != tt) {
+                               *d++ = dsp_audio_mix_law[(p[t]<<8)|q[r]];
+                               t = (t+1) & CMX_BUFF_MASK;
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+                       while (r != rr) {
+                               *d++ = q[r]; /* echo */
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+               }
+               dsp->tx_R = t;
+               goto send_packet;
+       }
+       /* PROCESS DATA (two members) */
+#ifdef CMX_CONF_DEBUG
+       if (0) {
+#else
+       if (members == 2) {
+#endif
+               /* "other" becomes other party */
+               other = (list_entry(conf->mlist.next,
+                   struct dsp_conf_member, list))->dsp;
+               if (other == member)
+                       other = (list_entry(conf->mlist.prev,
+                           struct dsp_conf_member, list))->dsp;
+               o_q = other->rx_buff; /* received data */
+               o_rr = (other->rx_R + len) & CMX_BUFF_MASK;
+                       /* end of rx-pointer */
+               o_r = (o_rr - rr + r) & CMX_BUFF_MASK;
+                       /* start rx-pointer at current read position*/
+               /* -> if echo is NOT enabled */
+               if (!dsp->echo) {
+                       /*
+                        * -> copy other member's rx-data,
+                        * if tx-data is available, mix
+                        */
+                       while (o_r != o_rr && t != tt) {
+                               *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]];
+                               t = (t+1) & CMX_BUFF_MASK;
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+                       while (o_r != o_rr) {
+                               *d++ = o_q[o_r];
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+               /* -> if echo is enabled */
+               } else {
+                       /*
+                        * -> mix other member's rx-data with echo,
+                        * if tx-data is available, mix
+                        */
+                       while (r != rr && t != tt) {
+                               sample = dsp_audio_law_to_s32[p[t]] +
+                                   dsp_audio_law_to_s32[q[r]] +
+                                   dsp_audio_law_to_s32[o_q[o_r]];
+                               if (sample < -32768)
+                                       sample = -32768;
+                               else if (sample > 32767)
+                                       sample = 32767;
+                               *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                                   /* tx-data + rx_data + echo */
+                               t = (t+1) & CMX_BUFF_MASK;
+                               r = (r+1) & CMX_BUFF_MASK;
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+                       while (r != rr) {
+                               *d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]];
+                               r = (r+1) & CMX_BUFF_MASK;
+                               o_r = (o_r+1) & CMX_BUFF_MASK;
+                       }
+               }
+               dsp->tx_R = t;
+               goto send_packet;
+       }
+#ifdef DSP_NEVER_DEFINED
+       }
+#endif
+       /* PROCESS DATA (three or more members) */
+       /* -> if echo is NOT enabled */
+       if (!dsp->echo) {
+               /*
+                * -> substract rx-data from conf-data,
+                * if tx-data is available, mix
+                */
+               while (r != rr && t != tt) {
+                       sample = dsp_audio_law_to_s32[p[t]] + *c++ -
+                           dsp_audio_law_to_s32[q[r]];
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf-rx+tx */
+                       r = (r+1) & CMX_BUFF_MASK;
+                       t = (t+1) & CMX_BUFF_MASK;
+               }
+               while (r != rr) {
+                       sample = *c++ - dsp_audio_law_to_s32[q[r]];
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf-rx */
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+       /* -> if echo is enabled */
+       } else {
+               /*
+                * -> encode conf-data, if tx-data
+                * is available, mix
+                */
+               while (r != rr && t != tt) {
+                       sample = dsp_audio_law_to_s32[p[t]] + *c++;
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf(echo)+tx */
+                       t = (t+1) & CMX_BUFF_MASK;
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+               while (r != rr) {
+                       sample = *c++;
+                       if (sample < -32768)
+                               sample = -32768;
+                       else if (sample > 32767)
+                               sample = 32767;
+                       *d++ = dsp_audio_s16_to_law[sample & 0xffff];
+                           /* conf(echo) */
+                       r = (r+1) & CMX_BUFF_MASK;
+               }
+       }
+       dsp->tx_R = t;
+       goto send_packet;
+
+send_packet:
+       /*
+        * send tx-data if enabled - don't filter,
+        * becuase we want what we send, not what we filtered
+        */
+       if (dsp->tx_data) {
+               /* PREPARE RESULT */
+               txskb = mI_alloc_skb(len, GFP_ATOMIC);
+               if (!txskb) {
+                       printk(KERN_ERR
+                           "FATAL ERROR in mISDN_dsp.o: "
+                           "cannot alloc %d bytes\n", len);
+               } else {
+                       thh = mISDN_HEAD_P(txskb);
+                       thh->prim = DL_DATA_REQ;
+                       thh->id = 0;
+                       memcpy(skb_put(txskb, len), nskb->data+preload, len);
+                       /* queue (trigger later) */
+                       skb_queue_tail(&dsp->sendq, txskb);
+               }
+       }
+       /* adjust volume */
+       if (dsp->tx_volume)
+               dsp_change_volume(nskb, dsp->tx_volume);
+       /* pipeline */
+       if (dsp->pipeline.inuse)
+               dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len);
+       /* crypt */
+       if (dsp->bf_enable)
+               dsp_bf_encrypt(dsp, nskb->data, nskb->len);
+       /* queue and trigger */
+       skb_queue_tail(&dsp->sendq, nskb);
+       schedule_work(&dsp->workq);
+}
+
+u32    samplecount;
+struct timer_list dsp_spl_tl;
+u32    dsp_spl_jiffies; /* calculate the next time to fire */
+u32    dsp_start_jiffies; /* jiffies at the time, the calculation begins */
+struct timeval dsp_start_tv; /* time at start of calculation */
+
+void
+dsp_cmx_send(void *arg)
+{
+       struct dsp_conf *conf;
+       struct dsp_conf_member *member;
+       struct dsp *dsp;
+       int mustmix, members;
+       s32 mixbuffer[MAX_POLL+100], *c;
+       u8 *p, *q;
+       int r, rr;
+       int jittercheck = 0, delay, i;
+       u_long flags;
+       struct timeval tv;
+       u32 elapsed;
+       s16 length;
+
+       /* lock */
+       spin_lock_irqsave(&dsp_lock, flags);
+
+       if (!dsp_start_tv.tv_sec) {
+               do_gettimeofday(&dsp_start_tv);
+               length = dsp_poll;
+       } else {
+               do_gettimeofday(&tv);
+               elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000)
+                   + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125));
+               dsp_start_tv.tv_sec = tv.tv_sec;
+               dsp_start_tv.tv_usec = tv.tv_usec;
+               length = elapsed;
+       }
+       if (length > MAX_POLL + 100)
+               length = MAX_POLL + 100;
+/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n",
+ length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16,
+ dsp_poll_diff & 0xffff);
+ */
+
+       /*
+        * check if jitter needs to be checked
+        * (this is about every second = 8192 samples)
+        */
+       samplecount += length;
+       if ((samplecount & 8191) < length)
+               jittercheck = 1;
+
+       /* loop all members that do not require conference mixing */
+       list_for_each_entry(dsp, &dsp_ilist, list) {
+               if (dsp->hdlc)
+                       continue;
+               conf = dsp->conf;
+               mustmix = 0;
+               members = 0;
+               if (conf) {
+                       members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+                       if (conf->software && members > 1)
+#else
+                       if (conf->software && members > 2)
+#endif
+                               mustmix = 1;
+               }
+
+               /* transmission required */
+               if (!mustmix) {
+                       dsp_cmx_send_member(dsp, length, mixbuffer, members);
+
+                       /*
+                        * unused mixbuffer is given to prevent a
+                        * potential null-pointer-bug
+                        */
+               }
+       }
+
+       /* loop all members that require conference mixing */
+       list_for_each_entry(conf, &conf_ilist, list) {
+               /* count members and check hardware */
+               members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+               if (conf->software && members > 1) {
+#else
+               if (conf->software && members > 2) {
+#endif
+                       /* check for hdlc conf */
+                       member = list_entry(conf->mlist.next,
+                               struct dsp_conf_member, list);
+                       if (member->dsp->hdlc)
+                               continue;
+                       /* mix all data */
+                       memset(mixbuffer, 0, length*sizeof(s32));
+                       list_for_each_entry(member, &conf->mlist, list) {
+                               dsp = member->dsp;
+                               /* get range of data to mix */
+                               c = mixbuffer;
+                               q = dsp->rx_buff;
+                               r = dsp->rx_R;
+                               rr = (r + length) & CMX_BUFF_MASK;
+                               /* add member's data */
+                               while (r != rr) {
+                                       *c++ += dsp_audio_law_to_s32[q[r]];
+                                       r = (r+1) & CMX_BUFF_MASK;
+                               }
+                       }
+
+                       /* process each member */
+                       list_for_each_entry(member, &conf->mlist, list) {
+                               /* transmission */
+                               dsp_cmx_send_member(member->dsp, length,
+                                   mixbuffer, members);
+                       }
+               }
+       }
+
+       /* delete rx-data, increment buffers, change pointers */
+       list_for_each_entry(dsp, &dsp_ilist, list) {
+               if (dsp->hdlc)
+                       continue;
+               p = dsp->rx_buff;
+               q = dsp->tx_buff;
+               r = dsp->rx_R;
+               /* move receive pointer when receiving */
+               if (!dsp->rx_is_off) {
+                       rr = (r + length) & CMX_BUFF_MASK;
+                       /* delete rx-data */
+                       while (r != rr) {
+                               p[r] = dsp_silence;
+                               r = (r+1) & CMX_BUFF_MASK;
+                       }
+                       /* increment rx-buffer pointer */
+                       dsp->rx_R = r; /* write incremented read pointer */
+               }
+
+               /* check current rx_delay */
+               delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK;
+               if (delay >= CMX_BUFF_HALF)
+                       delay = 0; /* will be the delay before next write */
+               /* check for lower delay */
+               if (delay < dsp->rx_delay[0])
+                       dsp->rx_delay[0] = delay;
+               /* check current tx_delay */
+               delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK;
+               if (delay >= CMX_BUFF_HALF)
+                       delay = 0; /* will be the delay before next write */
+               /* check for lower delay */
+               if (delay < dsp->tx_delay[0])
+                       dsp->tx_delay[0] = delay;
+               if (jittercheck) {
+                       /* find the lowest of all rx_delays */
+                       delay = dsp->rx_delay[0];
+                       i = 1;
+                       while (i < MAX_SECONDS_JITTER_CHECK) {
+                               if (delay > dsp->rx_delay[i])
+                                       delay = dsp->rx_delay[i];
+                               i++;
+                       }
+                       /*
+                        * remove rx_delay only if we have delay AND we
+                        * have not preset cmx_delay
+                        */
+                       if (delay && !dsp->cmx_delay) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s lowest rx_delay of %d bytes for"
+                                           " dsp %s are now removed.\n",
+                                           __func__, delay,
+                                           dsp->name);
+                               r = dsp->rx_R;
+                               rr = (r + delay) & CMX_BUFF_MASK;
+                               /* delete rx-data */
+                               while (r != rr) {
+                                       p[r] = dsp_silence;
+                                       r = (r+1) & CMX_BUFF_MASK;
+                               }
+                               /* increment rx-buffer pointer */
+                               dsp->rx_R = r;
+                                   /* write incremented read pointer */
+                       }
+                       /* find the lowest of all tx_delays */
+                       delay = dsp->tx_delay[0];
+                       i = 1;
+                       while (i < MAX_SECONDS_JITTER_CHECK) {
+                               if (delay > dsp->tx_delay[i])
+                                       delay = dsp->tx_delay[i];
+                               i++;
+                       }
+                       /*
+                        * remove delay only if we have delay AND we
+                        * have enabled tx_dejitter
+                        */
+                       if (delay && dsp->tx_dejitter) {
+                               if (dsp_debug & DEBUG_DSP_CMX)
+                                       printk(KERN_DEBUG
+                                           "%s lowest tx_delay of %d bytes for"
+                                           " dsp %s are now removed.\n",
+                                           __func__, delay,
+                                           dsp->name);
+                               r = dsp->tx_R;
+                               rr = (r + delay) & CMX_BUFF_MASK;
+                               /* delete tx-data */
+                               while (r != rr) {
+                                       q[r] = dsp_silence;
+                                       r = (r+1) & CMX_BUFF_MASK;
+                               }
+                               /* increment rx-buffer pointer */
+                               dsp->tx_R = r;
+                                   /* write incremented read pointer */
+                       }
+                       /* scroll up delays */
+                       i = MAX_SECONDS_JITTER_CHECK - 1;
+                       while (i) {
+                               dsp->rx_delay[i] = dsp->rx_delay[i-1];
+                               dsp->tx_delay[i] = dsp->tx_delay[i-1];
+                               i--;
+                       }
+                       dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+                       dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+               }
+       }
+
+       /* if next event would be in the past ... */
+       if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0)
+               dsp_spl_jiffies = jiffies + 1;
+       else
+               dsp_spl_jiffies += dsp_tics;
+
+       dsp_spl_tl.expires = dsp_spl_jiffies;
+       add_timer(&dsp_spl_tl);
+
+       /* unlock */
+       spin_unlock_irqrestore(&dsp_lock, flags);
+}
+
+/*
+ * audio data is transmitted from upper layer to the dsp
+ */
+void
+dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
+{
+       u_int w, ww;
+       u8 *d, *p;
+       int space; /* todo: , l = skb->len; */
+#ifdef CMX_TX_DEBUG
+       char debugbuf[256] = "";
+#endif
+
+       /* check if there is enough space, and then copy */
+       w = dsp->tx_W;
+       ww = dsp->tx_R;
+       p = dsp->tx_buff;
+       d = skb->data;
+       space = ww-w;
+       if (space <= 0)
+               space += CMX_BUFF_SIZE;
+       /* write-pointer should not overrun nor reach read pointer */
+       if (space-1 < skb->len)
+               /* write to the space we have left */
+               ww = (ww - 1) & CMX_BUFF_MASK;
+       else
+               /* write until all byte are copied */
+               ww = (w + skb->len) & CMX_BUFF_MASK;
+       dsp->tx_W = ww;
+
+       /* show current buffer */
+#ifdef CMX_DEBUG
+       printk(KERN_DEBUG
+           "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n",
+           (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name);
+#endif
+
+       /* copy transmit data to tx-buffer */
+#ifdef CMX_TX_DEBUG
+       sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p);
+#endif
+       while (w != ww) {
+#ifdef CMX_TX_DEBUG
+               if (strlen(debugbuf) < 48)
+                       sprintf(debugbuf+strlen(debugbuf), " %02x", *d);
+#endif
+               p[w] = *d++;
+               w = (w+1) & CMX_BUFF_MASK;
+       }
+#ifdef CMX_TX_DEBUG
+       printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+}
+
+/*
+ * hdlc data is received from card and sent to all members.
+ */
+void
+dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb)
+{
+       struct sk_buff *nskb = NULL;
+       struct dsp_conf_member *member;
+       struct mISDNhead *hh;
+
+       /* not if not active */
+       if (!dsp->b_active)
+               return;
+
+       /* check if we have sompen */
+       if (skb->len < 1)
+               return;
+
+       /* no conf */
+       if (!dsp->conf) {
+               /* in case of hardware (echo) */
+               if (dsp->pcm_slot_tx >= 0)
+                       return;
+               if (dsp->echo)
+                       nskb = skb_clone(skb, GFP_ATOMIC);
+                       if (nskb) {
+                               hh = mISDN_HEAD_P(nskb);
+                               hh->prim = PH_DATA_REQ;
+                               hh->id = 0;
+                               skb_queue_tail(&dsp->sendq, nskb);
+                               schedule_work(&dsp->workq);
+                       }
+               return;
+       }
+       /* in case of hardware conference */
+       if (dsp->conf->hardware)
+               return;
+       list_for_each_entry(member, &dsp->conf->mlist, list) {
+               if (dsp->echo || member->dsp != dsp) {
+                       nskb = skb_clone(skb, GFP_ATOMIC);
+                       if (nskb) {
+                               hh = mISDN_HEAD_P(nskb);
+                               hh->prim = PH_DATA_REQ;
+                               hh->id = 0;
+                               skb_queue_tail(&member->dsp->sendq, nskb);
+                               schedule_work(&member->dsp->workq);
+                       }
+               }
+       }
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
new file mode 100644 (file)
index 0000000..2f10ed8
--- /dev/null
@@ -0,0 +1,1191 @@
+/*
+ * Author       Andreas Eversberg (jolly@eversberg.eu)
+ * Based on source code structure by
+ *             Karsten Keil (keil@isdn4linux.de)
+ *
+ *             This file is (c) under GNU PUBLIC LICENSE
+ *             For changes and modifications please read
+ *             ../../../Documentation/isdn/mISDN.cert
+ *
+ * Thanks to    Karsten Keil (great drivers)
+ *              Cologne Chip (great chips)
+ *
+ * This module does:
+ *             Real-time tone generation
+ *             DTMF detection
+ *             Real-time cross-connection and conferrence
+ *             Compensate jitter due to system load and hardware fault.
+ *             All features are done in kernel space and will be realized
+ *             using hardware, if available and supported by chip set.
+ *             Blowfish encryption/decryption
+ */
+
+/* STRUCTURE:
+ *
+ * The dsp module provides layer 2 for b-channels (64kbit). It provides
+ * transparent audio forwarding with special digital signal processing:
+ *
+ * - (1) generation of tones
+ * - (2) detection of dtmf tones
+ * - (3) crossconnecting and conferences (clocking)
+ * - (4) echo generation for delay test
+ * - (5) volume control
+ * - (6) disable receive data
+ * - (7) pipeline
+ * - (8) encryption/decryption
+ *
+ * Look:
+ *             TX            RX
+ *         ------upper layer------
+ *             |             ^
+ *             |             |(6)
+ *             v             |
+ *       +-----+-------------+-----+
+ *       |(3)(4)                   |
+ *       |           CMX           |
+ *       |                         |
+ *       |           +-------------+
+ *       |           |       ^
+ *       |           |       |
+ *       |+---------+|  +----+----+
+ *       ||(1)      ||  |(2)      |
+ *       ||         ||  |         |
+ *       ||  Tones  ||  |  DTMF   |
+ *       ||         ||  |         |
+ *       ||         ||  |         |
+ *       |+----+----+|  +----+----+
+ *       +-----+-----+       ^
+ *             |             |
+ *             v             |
+ *        +----+----+   +----+----+
+ *        |(5)      |   |(5)      |
+ *        |         |   |         |
+ *        |TX Volume|   |RX Volume|
+ *        |         |   |         |
+ *        |         |   |         |
+ *        +----+----+   +----+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *        +----+-------------+----+
+ *        |(7)                    |
+ *        |                       |
+ *        |  Pipeline Processing  |
+ *        |                       |
+ *        |                       |
+ *        +----+-------------+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *        +----+----+   +----+----+
+ *        |(8)      |   |(8)      |
+ *        |         |   |         |
+ *        | Encrypt |   | Decrypt |
+ *        |         |   |         |
+ *        |         |   |         |
+ *        +----+----+   +----+----+
+ *             |             ^
+ *             |             |
+ *             v             |
+ *         ------card  layer------
+ *             TX            RX
+ *
+ * Above you can see the logical data flow. If software is used to do the
+ * process, it is actually the real data flow. If hardware is used, data
+ * may not flow, but hardware commands to the card, to provide the data flow
+ * as shown.
+ *
+ * NOTE: The channel must be activated in order to make dsp work, even if
+ * no data flow to the upper layer is intended. Activation can be done
+ * after and before controlling the setting using PH_CONTROL requests.
+ *
+ * DTMF: Will be detected by hardware if possible. It is done before CMX
+ * processing.
+ *
+ * Tones: Will be generated via software if endless looped audio fifos are
+ * not supported by hardware. Tones will override all data from CMX.
+ * It is not required to join a conference to use tones at any time.
+ *
+ * CMX: Is transparent when not used. When it is used, it will do
+ * crossconnections and conferences via software if not possible through
+ * hardware. If hardware capability is available, hardware is used.
+ *
+ * Echo: Is generated by CMX and is used to check performane of hard and
+ * software CMX.
+ *
+ * The CMX has special functions for conferences with one, two and more
+ * members. It will allow different types of data flow. Receive and transmit
+ * data to/form upper layer may be swithed on/off individually without loosing
+ * features of CMX, Tones and DTMF.
+ *
+ * Echo Cancellation: Sometimes we like to cancel echo from the interface.
+ * Note that a VoIP call may not have echo caused by the IP phone. The echo
+ * is generated by the telephone line connected to it. Because the delay
+ * is high, it becomes an echo. RESULT: Echo Cachelation is required if
+ * both echo AND delay is applied to an interface.
+ * Remember that software CMX always generates a more or less delay.
+ *
+ * If all used features can be realized in hardware, and if transmit and/or
+ * receive data ist disabled, the card may not send/receive any data at all.
+ * Not receiving is usefull if only announcements are played. Not sending is
+ * usefull if an answering machine records audio. Not sending and receiving is
+ * usefull during most states of the call. If supported by hardware, tones
+ * will be played without cpu load. Small PBXs and NT-Mode applications will
+ * not need expensive hardware when processing calls.
+ *
+ *
+ * LOCKING:
+ *
+ * When data is received from upper or lower layer (card), the complete dsp
+ * module is locked by a global lock.  This lock MUST lock irq, because it
+ * must lock timer events by DSP poll timer.
+ * When data is ready to be transmitted down, the data is queued and sent
+ * outside lock and timer event.
+ * PH_CONTROL must not change any settings, join or split conference members
+ * during process of data.
+ *
+ * HDLC:
+ *
+ * It works quite the same as transparent, except that HDLC data is forwarded
+ * to all other conference members if no hardware bridging is possible.
+ * Send data will be writte to sendq. Sendq will be sent if confirm is received.
+ * Conference cannot join, if one member is not hdlc.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include "core.h"
+#include "dsp.h"
+
+const char *mISDN_dsp_revision = "2.0";
+
+static int debug;
+static int options;
+static int poll;
+static int dtmfthreshold = 100;
+
+MODULE_AUTHOR("Andreas Eversberg");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(options, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR);
+MODULE_LICENSE("GPL");
+
+/*int spinnest = 0;*/
+
+spinlock_t dsp_lock; /* global dsp lock */
+struct list_head dsp_ilist;
+struct list_head conf_ilist;
+int dsp_debug;
+int dsp_options;
+int dsp_poll, dsp_tics;
+
+/* check if rx may be turned off or must be turned on */
+static void
+dsp_rx_off_member(struct dsp *dsp)
+{
+       struct mISDN_ctrl_req   cq;
+       int rx_off = 1;
+
+       if (!dsp->features_rx_off)
+               return;
+
+       /* not disabled */
+       if (!dsp->rx_disabled)
+               rx_off = 0;
+       /* software dtmf */
+       else if (dsp->dtmf.software)
+               rx_off = 0;
+       /* echo in software */
+       else if (dsp->echo && dsp->pcm_slot_tx < 0)
+               rx_off = 0;
+       /* bridge in software */
+       else if (dsp->conf) {
+               if (dsp->conf->software)
+                       rx_off = 0;
+       }
+
+       if (rx_off == dsp->rx_is_off)
+               return;
+
+       if (!dsp->ch.peer) {
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: no peer, no rx_off\n",
+                               __func__);
+               return;
+       }
+       cq.op = MISDN_CTRL_RX_OFF;
+       cq.p1 = rx_off;
+       if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
+               printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+       dsp->rx_is_off = rx_off;
+       if (dsp_debug & DEBUG_DSP_CORE)
+               printk(KERN_DEBUG "%s: %s set rx_off = %d\n",
+                       __func__, dsp->name, rx_off);
+}
+static void
+dsp_rx_off(struct dsp *dsp)
+{
+       struct dsp_conf_member  *member;
+
+       if (dsp_options & DSP_OPT_NOHARDWARE)
+               return;
+
+       /* no conf */
+       if (!dsp->conf) {
+               dsp_rx_off_member(dsp);
+               return;
+       }
+       /* check all members in conf */
+       list_for_each_entry(member, &dsp->conf->mlist, list) {
+               dsp_rx_off_member(member->dsp);
+       }
+}
+
+static int
+dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb)
+{
+       struct          sk_buff *nskb;
+       int ret = 0;
+       int cont;
+       u8 *data;
+       int len;
+
+       if (skb->len < sizeof(int))
+               printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__);
+       cont = *((int *)skb->data);
+       len = skb->len - sizeof(int);
+       data = skb->data + sizeof(int);
+
+       switch (cont) {
+       case DTMF_TONE_START: /* turn on DTMF */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: start dtmf\n", __func__);
+               if (len == sizeof(int)) {
+                       printk(KERN_NOTICE "changing DTMF Threshold "
+                               "to %d\n", *((int *)data));
+                       dsp->dtmf.treshold = (*(int *)data) * 10000;
+               }
+               /* init goertzel */
+               dsp_dtmf_goertzel_init(dsp);
+
+               /* check dtmf hardware */
+               dsp_dtmf_hardware(dsp);
+               break;
+       case DTMF_TONE_STOP: /* turn off DTMF */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: stop dtmf\n", __func__);
+               dsp->dtmf.hardware = 0;
+               dsp->dtmf.software = 0;
+               break;
+       case DSP_CONF_JOIN: /* join / update conference */
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (*((u32 *)data) == 0)
+                       goto conf_split;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: join conference %d\n",
+                               __func__, *((u32 *)data));
+               ret = dsp_cmx_conf(dsp, *((u32 *)data));
+                       /* dsp_cmx_hardware will also be called here */
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_CONF_SPLIT: /* remove from conference */
+conf_split:
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: release conference\n", __func__);
+               ret = dsp_cmx_conf(dsp, 0);
+                       /* dsp_cmx_hardware will also be called here */
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               dsp_rx_off(dsp);
+               break;
+       case DSP_TONE_PATT_ON: /* play tone */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn tone 0x%x on\n",
+                               __func__, *((int *)skb->data));
+               ret = dsp_tone(dsp, *((int *)data));
+               if (!ret) {
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_rx_off(dsp);
+               }
+               if (!dsp->tone.tone)
+                       goto tone_off;
+               break;
+       case DSP_TONE_PATT_OFF: /* stop tone */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn tone off\n", __func__);
+               dsp_tone(dsp, 0);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               /* reset tx buffers (user space data) */
+tone_off:
+               dsp->rx_W = 0;
+               dsp->rx_R = 0;
+               break;
+       case DSP_VOL_CHANGE_TX: /* change volume */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->tx_volume = *((int *)data);
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: change tx vol to %d\n",
+                               __func__, dsp->tx_volume);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               break;
+       case DSP_VOL_CHANGE_RX: /* change volume */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->rx_volume = *((int *)data);
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: change rx vol to %d\n",
+                               __func__, dsp->tx_volume);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               break;
+       case DSP_ECHO_ON: /* enable echo */
+               dsp->echo = 1; /* soft echo */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_ECHO_OFF: /* disable echo */
+               dsp->echo = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_RECEIVE_ON: /* enable receive to user space */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable receive to user "
+                               "space\n", __func__);
+               dsp->rx_disabled = 0;
+               dsp_rx_off(dsp);
+               break;
+       case DSP_RECEIVE_OFF: /* disable receive to user space */
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable receive to "
+                               "user space\n", __func__);
+               dsp->rx_disabled = 1;
+               dsp_rx_off(dsp);
+               break;
+       case DSP_MIX_ON: /* enable mixing of tx data */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable mixing of "
+                               "tx-data with conf mebers\n", __func__);
+               dsp->tx_mix = 1;
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_MIX_OFF: /* disable mixing of tx data */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable mixing of "
+                               "tx-data with conf mebers\n", __func__);
+               dsp->tx_mix = 0;
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_TXDATA_ON: /* enable txdata */
+               dsp->tx_data = 1;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: enable tx-data\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_TXDATA_OFF: /* disable txdata */
+               dsp->tx_data = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: disable tx-data\n", __func__);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               if (dsp_debug & DEBUG_DSP_CMX)
+                       dsp_cmx_debug(dsp);
+               break;
+       case DSP_DELAY: /* use delay algorithm instead of dynamic
+                          jitter algorithm */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < sizeof(int)) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->cmx_delay = (*((int *)data)) << 3;
+                       /* miliseconds to samples */
+               if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1))
+                       /* clip to half of maximum usable buffer
+                       (half of half buffer) */
+                       dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use delay algorithm to "
+                               "compensate jitter (%d samples)\n",
+                               __func__, dsp->cmx_delay);
+               break;
+       case DSP_JITTER: /* use dynamic jitter algorithm instead of
+                   delay algorithm */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->cmx_delay = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use jitter algorithm to "
+                               "compensate jitter\n", __func__);
+               break;
+       case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->tx_dejitter = 1;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use dejitter on TX "
+                               "buffer\n", __func__);
+               break;
+       case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dsp->tx_dejitter = 0;
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: use TX buffer without "
+                               "dejittering\n", __func__);
+               break;
+       case DSP_PIPELINE_CFG:
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len > 0 && ((char *)data)[len - 1]) {
+                       printk(KERN_DEBUG "%s: pipeline config string "
+                               "is not NULL terminated!\n", __func__);
+                       ret = -EINVAL;
+               } else {
+                       dsp->pipeline.inuse = 1;
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       ret = dsp_pipeline_build(&dsp->pipeline,
+                               len > 0 ? (char *)data : NULL);
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_rx_off(dsp);
+               }
+               break;
+       case DSP_BF_ENABLE_KEY: /* turn blowfish on */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (len < 4 || len > 56) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn blowfish on (key "
+                               "not shown)\n", __func__);
+               ret = dsp_bf_init(dsp, (u8 *)data, len);
+               /* set new cont */
+               if (!ret)
+                       cont = DSP_BF_ACCEPT;
+               else
+                       cont = DSP_BF_REJECT;
+               /* send indication if it worked to set it */
+               nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY,
+                       sizeof(int), &cont, GFP_ATOMIC);
+               if (nskb) {
+                       if (dsp->up) {
+                               if (dsp->up->send(dsp->up, nskb))
+                                       dev_kfree_skb(nskb);
+                       } else
+                               dev_kfree_skb(nskb);
+               }
+               if (!ret) {
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_dtmf_hardware(dsp);
+                       dsp_rx_off(dsp);
+               }
+               break;
+       case DSP_BF_DISABLE: /* turn blowfish off */
+               if (dsp->hdlc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: turn blowfish off\n", __func__);
+               dsp_bf_cleanup(dsp);
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               break;
+       default:
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: ctrl req %x unhandled\n",
+                               __func__, cont);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static void
+get_features(struct mISDNchannel *ch)
+{
+       struct dsp              *dsp = container_of(ch, struct dsp, ch);
+       struct mISDN_ctrl_req   cq;
+
+       if (dsp_options & DSP_OPT_NOHARDWARE)
+               return;
+       if (!ch->peer) {
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: no peer, no features\n",
+                               __func__);
+               return;
+       }
+       memset(&cq, 0, sizeof(cq));
+       cq.op = MISDN_CTRL_GETOP;
+       if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) {
+               printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+       if (cq.op & MISDN_CTRL_RX_OFF)
+               dsp->features_rx_off = 1;
+       if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) {
+               cq.op = MISDN_CTRL_HW_FEATURES;
+               *((u_long *)&cq.p1) = (u_long)&dsp->features;
+               if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) {
+                       printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+                               __func__);
+               }
+       } else
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: features not supported for %s\n",
+                               __func__, dsp->name);
+}
+
+static int
+dsp_function(struct mISDNchannel *ch,  struct sk_buff *skb)
+{
+       struct dsp                      *dsp = container_of(ch, struct dsp, ch);
+       struct mISDNhead        *hh;
+       int                     ret = 0;
+       u8                      *digits;
+       int                     cont;
+       struct                  sk_buff *nskb;
+       u_long                  flags;
+
+       hh = mISDN_HEAD_P(skb);
+       switch (hh->prim) {
+       /* FROM DOWN */
+       case (PH_DATA_CNF):
+               dsp->data_pending = 0;
+               /* trigger next hdlc frame, if any */
+               if (dsp->hdlc) {
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       if (dsp->b_active)
+                               schedule_work(&dsp->workq);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+               }
+               break;
+       case (PH_DATA_IND):
+       case (DL_DATA_IND):
+               if (skb->len < 1) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp->rx_is_off) {
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: rx-data during rx_off"
+                                       " for %s\n",
+                               __func__, dsp->name);
+               }
+               if (dsp->hdlc) {
+                       /* hdlc */
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       dsp_cmx_hdlc(dsp, skb);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+                       if (dsp->rx_disabled) {
+                               /* if receive is not allowed */
+                               break;
+                       }
+                       hh->prim = DL_DATA_IND;
+                       if (dsp->up)
+                               return dsp->up->send(dsp->up, skb);
+                       break;
+               }
+
+               /* decrypt if enabled */
+               if (dsp->bf_enable)
+                       dsp_bf_decrypt(dsp, skb->data, skb->len);
+               /* pipeline */
+               if (dsp->pipeline.inuse)
+                       dsp_pipeline_process_rx(&dsp->pipeline, skb->data,
+                               skb->len);
+               /* change volume if requested */
+               if (dsp->rx_volume)
+                       dsp_change_volume(skb, dsp->rx_volume);
+
+               /* check if dtmf soft decoding is turned on */
+               if (dsp->dtmf.software) {
+                       digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
+                               skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
+                       while (*digits) {
+                               if (dsp_debug & DEBUG_DSP_DTMF)
+                                       printk(KERN_DEBUG "%s: digit"
+                                           "(%c) to layer %s\n",
+                                           __func__, *digits, dsp->name);
+                               cont = DTMF_TONE_VAL | *digits;
+                               nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+                                   MISDN_ID_ANY, sizeof(int), &cont,
+                                   GFP_ATOMIC);
+                               if (nskb) {
+                                       if (dsp->up) {
+                                               if (dsp->up->send(
+                                                   dsp->up, nskb))
+                                               dev_kfree_skb(nskb);
+                                       } else
+                                               dev_kfree_skb(nskb);
+                               }
+                               digits++;
+                       }
+               }
+               /* we need to process receive data if software */
+               spin_lock_irqsave(&dsp_lock, flags);
+               if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) {
+                       /* process data from card at cmx */
+                       dsp_cmx_receive(dsp, skb);
+               }
+               spin_unlock_irqrestore(&dsp_lock, flags);
+
+               if (dsp->rx_disabled) {
+                       /* if receive is not allowed */
+                       break;
+               }
+               hh->prim = DL_DATA_IND;
+               if (dsp->up)
+                       return dsp->up->send(dsp->up, skb);
+               break;
+       case (PH_CONTROL_IND):
+               if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+                       printk(KERN_DEBUG "%s: PH_CONTROL INDICATION "
+                               "received: %x (len %d) %s\n", __func__,
+                               hh->id, skb->len, dsp->name);
+               switch (hh->id) {
+               case (DTMF_HFC_COEF): /* getting coefficients */
+                       if (!dsp->dtmf.hardware) {
+                               if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+                                       printk(KERN_DEBUG "%s: ignoring DTMF "
+                                               "coefficients from HFC\n",
+                                               __func__);
+                               break;
+                       }
+                       digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
+                               skb->len, 2);
+                       while (*digits) {
+                               int k;
+                               struct sk_buff *nskb;
+                               if (dsp_debug & DEBUG_DSP_DTMF)
+                                       printk(KERN_DEBUG "%s: digit"
+                                           "(%c) to layer %s\n",
+                                           __func__, *digits, dsp->name);
+                               k = *digits | DTMF_TONE_VAL;
+                               nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+                                       MISDN_ID_ANY, sizeof(int), &k,
+                                       GFP_ATOMIC);
+                               if (nskb) {
+                                       if (dsp->up) {
+                                               if (dsp->up->send(
+                                                   dsp->up, nskb))
+                                               dev_kfree_skb(nskb);
+                                       } else
+                                               dev_kfree_skb(nskb);
+                               }
+                               digits++;
+                       }
+                       break;
+               case (HFC_VOL_CHANGE_TX): /* change volume */
+                       if (skb->len != sizeof(int)) {
+                               ret = -EINVAL;
+                               break;
+                       }
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       dsp->tx_volume = *((int *)skb->data);
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: change tx volume to "
+                                       "%d\n", __func__, dsp->tx_volume);
+                       dsp_cmx_hardware(dsp->conf, dsp);
+                       dsp_dtmf_hardware(dsp);
+                       dsp_rx_off(dsp);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+                       break;
+               default:
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: ctrl ind %x unhandled "
+                                       "%s\n", __func__, hh->id, dsp->name);
+                       ret = -EINVAL;
+               }
+               break;
+       case (PH_ACTIVATE_IND):
+       case (PH_ACTIVATE_CNF):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: b_channel is now active %s\n",
+                               __func__, dsp->name);
+               /* bchannel now active */
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->b_active = 1;
+               dsp->data_pending = 0;
+               dsp->rx_init = 1;
+                       /* rx_W and rx_R will be adjusted on first frame */
+               dsp->rx_W = 0;
+               dsp->rx_R = 0;
+               memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_dtmf_hardware(dsp);
+               dsp_rx_off(dsp);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: done with activation, sending "
+                               "confirm to user space. %s\n", __func__,
+                               dsp->name);
+               /* send activation to upper layer */
+               hh->prim = DL_ESTABLISH_CNF;
+               if (dsp->up)
+                       return dsp->up->send(dsp->up, skb);
+               break;
+       case (PH_DEACTIVATE_IND):
+       case (PH_DEACTIVATE_CNF):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: b_channel is now inactive %s\n",
+                               __func__, dsp->name);
+               /* bchannel now inactive */
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->b_active = 0;
+               dsp->data_pending = 0;
+               dsp_cmx_hardware(dsp->conf, dsp);
+               dsp_rx_off(dsp);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               hh->prim = DL_RELEASE_CNF;
+               if (dsp->up)
+                       return dsp->up->send(dsp->up, skb);
+               break;
+       /* FROM UP */
+       case (DL_DATA_REQ):
+       case (PH_DATA_REQ):
+               if (skb->len < 1) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (dsp->hdlc) {
+                       /* hdlc */
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       if (dsp->b_active) {
+                               skb_queue_tail(&dsp->sendq, skb);
+                               schedule_work(&dsp->workq);
+                       }
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+                       return 0;
+               }
+               /* send data to tx-buffer (if no tone is played) */
+               if (!dsp->tone.tone) {
+                       spin_lock_irqsave(&dsp_lock, flags);
+                       dsp_cmx_transmit(dsp, skb);
+                       spin_unlock_irqrestore(&dsp_lock, flags);
+               }
+               break;
+       case (PH_CONTROL_REQ):
+               spin_lock_irqsave(&dsp_lock, flags);
+               ret = dsp_control_req(dsp, hh, skb);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               break;
+       case (DL_ESTABLISH_REQ):
+       case (PH_ACTIVATE_REQ):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: activating b_channel %s\n",
+                               __func__, dsp->name);
+               if (dsp->dtmf.hardware || dsp->dtmf.software)
+                       dsp_dtmf_goertzel_init(dsp);
+               get_features(ch);
+               /* send ph_activate */
+               hh->prim = PH_ACTIVATE_REQ;
+               if (ch->peer)
+                       return ch->recv(ch->peer, skb);
+               break;
+       case (DL_RELEASE_REQ):
+       case (PH_DEACTIVATE_REQ):
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: releasing b_channel %s\n",
+                               __func__, dsp->name);
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->tone.tone = 0;
+               dsp->tone.hardware = 0;
+               dsp->tone.software = 0;
+               if (timer_pending(&dsp->tone.tl))
+                       del_timer(&dsp->tone.tl);
+               if (dsp->conf)
+                       dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be
+                                                called here */
+               skb_queue_purge(&dsp->sendq);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               hh->prim = PH_DEACTIVATE_REQ;
+               if (ch->peer)
+                       return ch->recv(ch->peer, skb);
+               break;
+       default:
+               if (dsp_debug & DEBUG_DSP_CORE)
+                       printk(KERN_DEBUG "%s: msg %x unhandled %s\n",
+                               __func__, hh->prim, dsp->name);
+               ret = -EINVAL;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct dsp              *dsp = container_of(ch, struct dsp, ch);
+       u_long          flags;
+       int             err = 0;
+
+       if (debug & DEBUG_DSP_CTRL)
+       printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               break;
+       case CLOSE_CHANNEL:
+               if (dsp->ch.peer)
+                       dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL);
+
+               /* wait until workqueue has finished,
+                * must lock here, or we may hit send-process currently
+                * queueing. */
+               spin_lock_irqsave(&dsp_lock, flags);
+               dsp->b_active = 0;
+               spin_unlock_irqrestore(&dsp_lock, flags);
+               /* MUST not be locked, because it waits until queue is done. */
+               cancel_work_sync(&dsp->workq);
+               spin_lock_irqsave(&dsp_lock, flags);
+               if (timer_pending(&dsp->tone.tl))
+                       del_timer(&dsp->tone.tl);
+               skb_queue_purge(&dsp->sendq);
+               if (dsp_debug & DEBUG_DSP_CTRL)
+                       printk(KERN_DEBUG "%s: releasing member %s\n",
+                               __func__, dsp->name);
+               dsp->b_active = 0;
+               dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called
+                                        here */
+               dsp_pipeline_destroy(&dsp->pipeline);
+
+               if (dsp_debug & DEBUG_DSP_CTRL)
+                       printk(KERN_DEBUG "%s: remove & destroy object %s\n",
+                               __func__, dsp->name);
+               list_del(&dsp->list);
+               spin_unlock_irqrestore(&dsp_lock, flags);
+
+               if (dsp_debug & DEBUG_DSP_CTRL)
+                       printk(KERN_DEBUG "%s: dsp instance released\n",
+                               __func__);
+               vfree(dsp);
+               module_put(THIS_MODULE);
+               break;
+       }
+       return err;
+}
+
+static void
+dsp_send_bh(struct work_struct *work)
+{
+       struct dsp *dsp = container_of(work, struct dsp, workq);
+       struct sk_buff *skb;
+       struct mISDNhead        *hh;
+
+       if (dsp->hdlc && dsp->data_pending)
+               return; /* wait until data has been acknowledged */
+
+       /* send queued data */
+       while ((skb = skb_dequeue(&dsp->sendq))) {
+               /* in locked date, we must have still data in queue */
+               if (dsp->data_pending) {
+                       if (dsp_debug & DEBUG_DSP_CORE)
+                               printk(KERN_DEBUG "%s: fifo full %s, this is "
+                                       "no bug!\n", __func__, dsp->name);
+                       /* flush transparent data, if not acked */
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+               hh = mISDN_HEAD_P(skb);
+               if (hh->prim == DL_DATA_REQ) {
+                       /* send packet up */
+                       if (dsp->up) {
+                               if (dsp->up->send(dsp->up, skb))
+                                       dev_kfree_skb(skb);
+                       } else
+                               dev_kfree_skb(skb);
+               } else {
+                       /* send packet down */
+                       if (dsp->ch.peer) {
+                               dsp->data_pending = 1;
+                               if (dsp->ch.recv(dsp->ch.peer, skb)) {
+                                       dev_kfree_skb(skb);
+                                       dsp->data_pending = 0;
+                               }
+                       } else
+                               dev_kfree_skb(skb);
+               }
+       }
+}
+
+static int
+dspcreate(struct channel_req *crq)
+{
+       struct dsp              *ndsp;
+       u_long          flags;
+
+       if (crq->protocol != ISDN_P_B_L2DSP
+        && crq->protocol != ISDN_P_B_L2DSPHDLC)
+               return -EPROTONOSUPPORT;
+       ndsp = vmalloc(sizeof(struct dsp));
+       if (!ndsp) {
+               printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__);
+               return -ENOMEM;
+       }
+       memset(ndsp, 0, sizeof(struct dsp));
+       if (dsp_debug & DEBUG_DSP_CTRL)
+               printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__);
+
+       /* default enabled */
+       INIT_WORK(&ndsp->workq, (void *)dsp_send_bh);
+       skb_queue_head_init(&ndsp->sendq);
+       ndsp->ch.send = dsp_function;
+       ndsp->ch.ctrl = dsp_ctrl;
+       ndsp->up = crq->ch;
+       crq->ch = &ndsp->ch;
+       if (crq->protocol == ISDN_P_B_L2DSP) {
+               crq->protocol = ISDN_P_B_RAW;
+               ndsp->hdlc = 0;
+       } else {
+               crq->protocol = ISDN_P_B_HDLC;
+               ndsp->hdlc = 1;
+       }
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n",
+                       __func__);
+
+       sprintf(ndsp->name, "DSP_C%x(0x%p)",
+               ndsp->up->st->dev->id + 1, ndsp);
+       /* set frame size to start */
+       ndsp->features.hfc_id = -1; /* current PCM id */
+       ndsp->features.pcm_id = -1; /* current PCM id */
+       ndsp->pcm_slot_rx = -1; /* current CPM slot */
+       ndsp->pcm_slot_tx = -1;
+       ndsp->pcm_bank_rx = -1;
+       ndsp->pcm_bank_tx = -1;
+       ndsp->hfc_conf = -1; /* current conference number */
+       /* set tone timer */
+       ndsp->tone.tl.function = (void *)dsp_tone_timeout;
+       ndsp->tone.tl.data = (long) ndsp;
+       init_timer(&ndsp->tone.tl);
+
+       if (dtmfthreshold < 20 || dtmfthreshold > 500)
+               dtmfthreshold = 200;
+       ndsp->dtmf.treshold = dtmfthreshold*10000;
+
+       /* init pipeline append to list */
+       spin_lock_irqsave(&dsp_lock, flags);
+       dsp_pipeline_init(&ndsp->pipeline);
+       list_add_tail(&ndsp->list, &dsp_ilist);
+       spin_unlock_irqrestore(&dsp_lock, flags);
+
+       return 0;
+}
+
+
+static struct Bprotocol DSP = {
+       .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK))
+               | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)),
+       .name = "dsp",
+       .create = dspcreate
+};
+
+static int dsp_init(void)
+{
+       int err;
+       int tics;
+
+       printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision);
+
+       dsp_options = options;
+       dsp_debug = debug;
+
+       /* set packet size */
+       dsp_poll = poll;
+       if (dsp_poll) {
+               if (dsp_poll > MAX_POLL) {
+                       printk(KERN_ERR "%s: Wrong poll value (%d), use %d "
+                               "maximum.\n", __func__, poll, MAX_POLL);
+                       err = -EINVAL;
+                       return err;
+               }
+               if (dsp_poll < 8) {
+                       printk(KERN_ERR "%s: Wrong poll value (%d), use 8 "
+                               "minimum.\n", __func__, dsp_poll);
+                       err = -EINVAL;
+                       return err;
+               }
+               dsp_tics = poll * HZ / 8000;
+               if (dsp_tics * 8000 != poll * HZ) {
+                       printk(KERN_INFO "mISDN_dsp: Cannot clock every %d "
+                               "samples (0,125 ms). It is not a multiple of "
+                               "%d HZ.\n", poll, HZ);
+                       err = -EINVAL;
+                       return err;
+               }
+       } else {
+               poll = 8;
+               while (poll <= MAX_POLL) {
+                       tics = poll * HZ / 8000;
+                       if (tics * 8000 == poll * HZ) {
+                               dsp_tics = tics;
+                               dsp_poll = poll;
+                               if (poll >= 64)
+                                       break;
+                       }
+                       poll++;
+               }
+       }
+       if (dsp_poll == 0) {
+               printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel "
+                       "clock that equals exactly the duration of 8-256 "
+                       "samples. (Choose kernel clock speed like 100, 250, "
+                       "300, 1000)\n");
+               err = -EINVAL;
+               return err;
+       }
+       printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals "
+               "%d jiffies.\n", dsp_poll, dsp_tics);
+
+       spin_lock_init(&dsp_lock);
+       INIT_LIST_HEAD(&dsp_ilist);
+       INIT_LIST_HEAD(&conf_ilist);
+
+       /* init conversion tables */
+       dsp_audio_generate_law_tables();
+       dsp_silence = (dsp_options&DSP_OPT_ULAW)?0xff:0x2a;
+       dsp_audio_law_to_s32 = (dsp_options&DSP_OPT_ULAW)?dsp_audio_ulaw_to_s32:
+               dsp_audio_alaw_to_s32;
+       dsp_audio_generate_s2law_table();
+       dsp_audio_generate_seven();
+       dsp_audio_generate_mix_table();
+       if (dsp_options & DSP_OPT_ULAW)
+               dsp_audio_generate_ulaw_samples();
+       dsp_audio_generate_volume_changes();
+
+       err = dsp_pipeline_module_init();
+       if (err) {
+               printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, "
+                       "error(%d)\n", err);
+               return err;
+       }
+
+       err = mISDN_register_Bprotocol(&DSP);
+       if (err) {
+               printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err);
+               return err;
+       }
+
+       /* set sample timer */
+       dsp_spl_tl.function = (void *)dsp_cmx_send;
+       dsp_spl_tl.data = 0;
+       init_timer(&dsp_spl_tl);
+       dsp_spl_tl.expires = jiffies + dsp_tics;
+       dsp_spl_jiffies = dsp_spl_tl.expires;
+       add_timer(&dsp_spl_tl);
+
+       return 0;
+}
+
+
+static void dsp_cleanup(void)
+{
+       mISDN_unregister_Bprotocol(&DSP);
+
+       if (timer_pending(&dsp_spl_tl))
+               del_timer(&dsp_spl_tl);
+
+       if (!list_empty(&dsp_ilist)) {
+               printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not "
+                       "empty.\n");
+       }
+       if (!list_empty(&conf_ilist)) {
+               printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not "
+                       "all memory freed.\n");
+       }
+
+       dsp_pipeline_module_exit();
+}
+
+module_init(dsp_init);
+module_exit(dsp_cleanup);
+
diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c
new file mode 100644 (file)
index 0000000..efc371c
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * DTMF decoder.
+ *
+ * Copyright            by Andreas Eversberg (jolly@eversberg.eu)
+ *                     based on different decoders such as ISDN4Linux
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+#define NCOEFF            8     /* number of frequencies to be analyzed */
+
+/* For DTMF recognition:
+ * 2 * cos(2 * PI * k / N) precalculated for all k
+ */
+static u64 cos2pik[NCOEFF] =
+{
+       /* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */
+       55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630
+};
+
+/* digit matrix */
+static char dtmf_matrix[4][4] =
+{
+       {'1', '2', '3', 'A'},
+       {'4', '5', '6', 'B'},
+       {'7', '8', '9', 'C'},
+       {'*', '0', '#', 'D'}
+};
+
+/* dtmf detection using goertzel algorithm
+ * init function
+ */
+void dsp_dtmf_goertzel_init(struct dsp *dsp)
+{
+       dsp->dtmf.size = 0;
+       dsp->dtmf.lastwhat = '\0';
+       dsp->dtmf.lastdigit = '\0';
+       dsp->dtmf.count = 0;
+}
+
+/* check for hardware or software features
+ */
+void dsp_dtmf_hardware(struct dsp *dsp)
+{
+       int hardware = 1;
+
+       if (!dsp->features.hfc_dtmf)
+               hardware = 0;
+
+       /* check for volume change */
+       if (dsp->tx_volume) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because tx_volume is changed\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+       if (dsp->rx_volume) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because rx_volume is changed\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+       /* check if encryption is enabled */
+       if (dsp->bf_enable) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because encryption is enabled\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+       /* check if pipeline exists */
+       if (dsp->pipeline.inuse) {
+               if (dsp_debug & DEBUG_DSP_DTMF)
+                       printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+                               "because pipeline exists.\n",
+                               __func__, dsp->name);
+               hardware = 0;
+       }
+
+       dsp->dtmf.hardware = hardware;
+       dsp->dtmf.software = !hardware;
+}
+
+
+/*************************************************************
+ * calculate the coefficients of the given sample and decode *
+ *************************************************************/
+
+/* the given sample is decoded. if the sample is not long enough for a
+ * complete frame, the decoding is finished and continued with the next
+ * call of this function.
+ *
+ * the algorithm is very good for detection with a minimum of errors. i
+ * tested it allot. it even works with very short tones (40ms). the only
+ * disadvantage is, that it doesn't work good with different volumes of both
+ * tones. this will happen, if accoustically coupled dialers are used.
+ * it sometimes detects tones during speach, which is normal for decoders.
+ * use sequences to given commands during calls.
+ *
+ * dtmf - points to a structure of the current dtmf state
+ * spl and len - the sample
+ * fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder
+ */
+
+u8
+*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt)
+{
+       u8 what;
+       int size;
+       signed short *buf;
+       s32 sk, sk1, sk2;
+       int k, n, i;
+       s32 *hfccoeff;
+       s32 result[NCOEFF], tresh, treshl;
+       int lowgroup, highgroup;
+       s64 cos2pik_;
+
+       dsp->dtmf.digits[0] = '\0';
+
+       /* Note: The function will loop until the buffer has not enough samples
+        * left to decode a full frame.
+        */
+again:
+       /* convert samples */
+       size = dsp->dtmf.size;
+       buf = dsp->dtmf.buffer;
+       switch (fmt) {
+       case 0: /* alaw */
+       case 1: /* ulaw */
+               while (size < DSP_DTMF_NPOINTS && len) {
+                       buf[size++] = dsp_audio_law_to_s32[*data++];
+                       len--;
+               }
+               break;
+
+       case 2: /* HFC coefficients */
+       default:
+               if (len < 64) {
+                       if (len > 0)
+                               printk(KERN_ERR "%s: coefficients have invalid "
+                                       "size. (is=%d < must=%d)\n",
+                                       __func__, len, 64);
+                       return dsp->dtmf.digits;
+               }
+               hfccoeff = (s32 *)data;
+               for (k = 0; k < NCOEFF; k++) {
+                       sk2 = (*hfccoeff++)>>4;
+                       sk = (*hfccoeff++)>>4;
+                       if (sk > 32767 || sk < -32767 || sk2 > 32767
+                           || sk2 < -32767)
+                               printk(KERN_WARNING
+                                       "DTMF-Detection overflow\n");
+                       /* compute |X(k)|**2 */
+                       result[k] =
+                                (sk * sk) -
+                                (((cos2pik[k] * sk) >> 15) * sk2) +
+                                (sk2 * sk2);
+               }
+               data += 64;
+               len -= 64;
+               goto coefficients;
+               break;
+       }
+       dsp->dtmf.size = size;
+
+       if (size < DSP_DTMF_NPOINTS)
+               return dsp->dtmf.digits;
+
+       dsp->dtmf.size = 0;
+
+       /* now we have a full buffer of signed long samples - we do goertzel */
+       for (k = 0; k < NCOEFF; k++) {
+               sk = 0;
+               sk1 = 0;
+               sk2 = 0;
+               buf = dsp->dtmf.buffer;
+               cos2pik_ = cos2pik[k];
+               for (n = 0; n < DSP_DTMF_NPOINTS; n++) {
+                       sk = ((cos2pik_*sk1)>>15) - sk2 + (*buf++);
+                       sk2 = sk1;
+                       sk1 = sk;
+               }
+               sk >>= 8;
+               sk2 >>= 8;
+               if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767)
+                       printk(KERN_WARNING "DTMF-Detection overflow\n");
+               /* compute |X(k)|**2 */
+               result[k] =
+                       (sk * sk) -
+                       (((cos2pik[k] * sk) >> 15) * sk2) +
+                       (sk2 * sk2);
+       }
+
+       /* our (squared) coefficients have been calculated, we need to process
+        * them.
+        */
+coefficients:
+       tresh = 0;
+       for (i = 0; i < NCOEFF; i++) {
+               if (result[i] < 0)
+                       result[i] = 0;
+               if (result[i] > dsp->dtmf.treshold) {
+                       if (result[i] > tresh)
+                               tresh = result[i];
+               }
+       }
+
+       if (tresh == 0) {
+               what = 0;
+               goto storedigit;
+       }
+
+       if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+               printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d"
+                       " tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n",
+                       result[0]/10000, result[1]/10000, result[2]/10000,
+                       result[3]/10000, result[4]/10000, result[5]/10000,
+                       result[6]/10000, result[7]/10000, tresh/10000,
+                       result[0]/(tresh/100), result[1]/(tresh/100),
+                       result[2]/(tresh/100), result[3]/(tresh/100),
+                       result[4]/(tresh/100), result[5]/(tresh/100),
+                       result[6]/(tresh/100), result[7]/(tresh/100));
+
+       /* calc digit (lowgroup/highgroup) */
+       lowgroup = -1;
+       highgroup = -1;
+       treshl = tresh >> 3;  /* tones which are not on, must be below 9 dB */
+       tresh = tresh >> 2;  /* touchtones must match within 6 dB */
+       for (i = 0; i < NCOEFF; i++) {
+               if (result[i] < treshl)
+                       continue;  /* ignore */
+               if (result[i] < tresh) {
+                       lowgroup = -1;
+                       highgroup = -1;
+                       break;  /* noise inbetween */
+               }
+               /* good level found. This is allowed only one time per group */
+               if (i < NCOEFF/2) {
+                       /* lowgroup */
+                       if (lowgroup >= 0) {
+                               /* Bad. Another tone found. */
+                               lowgroup = -1;
+                               break;
+                       } else
+                               lowgroup = i;
+               } else {
+                       /* higroup */
+                       if (highgroup >= 0) {
+                               /* Bad. Another tone found. */
+                               highgroup = -1;
+                               break;
+                       } else
+                               highgroup = i-(NCOEFF/2);
+               }
+       }
+
+       /* get digit or null */
+       what = 0;
+       if (lowgroup >= 0 && highgroup >= 0)
+               what = dtmf_matrix[lowgroup][highgroup];
+
+storedigit:
+       if (what && (dsp_debug & DEBUG_DSP_DTMF))
+               printk(KERN_DEBUG "DTMF what: %c\n", what);
+
+       if (dsp->dtmf.lastwhat != what)
+               dsp->dtmf.count = 0;
+
+       /* the tone (or no tone) must remain 3 times without change */
+       if (dsp->dtmf.count == 2) {
+               if (dsp->dtmf.lastdigit != what) {
+                       dsp->dtmf.lastdigit = what;
+                       if (what) {
+                               if (dsp_debug & DEBUG_DSP_DTMF)
+                                       printk(KERN_DEBUG "DTMF digit: %c\n",
+                                               what);
+                               if ((strlen(dsp->dtmf.digits)+1)
+                                       < sizeof(dsp->dtmf.digits)) {
+                                       dsp->dtmf.digits[strlen(
+                                               dsp->dtmf.digits)+1] = '\0';
+                                       dsp->dtmf.digits[strlen(
+                                               dsp->dtmf.digits)] = what;
+                               }
+                       }
+               }
+       } else
+               dsp->dtmf.count++;
+
+       dsp->dtmf.lastwhat = what;
+
+       goto again;
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h
new file mode 100644 (file)
index 0000000..8a20af4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * ec_disable_detector.h - A detector which should eventually meet the
+ *                         G.164/G.165 requirements for detecting the
+ *                         2100Hz echo cancellor disable tone.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "dsp_biquad.h"
+
+struct ec_disable_detector_state {
+       struct biquad2_state notch;
+       int notch_level;
+       int channel_level;
+       int tone_present;
+       int tone_cycle_duration;
+       int good_cycles;
+       int hit;
+};
+
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+static inline void
+echo_can_disable_detector_init(struct ec_disable_detector_state *det)
+{
+    /* Elliptic notch */
+    /* This is actually centred at 2095Hz, but gets the balance we want, due
+       to the asymmetric walls of the notch */
+       biquad2_init(&det->notch,
+               (int32_t) (-0.7600000*32768.0),
+               (int32_t) (-0.1183852*32768.0),
+               (int32_t) (-0.5104039*32768.0),
+               (int32_t) (0.1567596*32768.0),
+               (int32_t) (1.0000000*32768.0));
+
+       det->channel_level = 0;
+       det->notch_level = 0;
+       det->tone_present = FALSE;
+       det->tone_cycle_duration = 0;
+       det->good_cycles = 0;
+       det->hit = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static inline int
+echo_can_disable_detector_update(struct ec_disable_detector_state *det,
+int16_t amp)
+{
+       int16_t notched;
+
+       notched = biquad2(&det->notch, amp);
+       /* Estimate the overall energy in the channel, and the energy in
+          the notch (i.e. overall channel energy - tone energy => noise).
+          Use abs instead of multiply for speed (is it really faster?).
+          Damp the overall energy a little more for a stable result.
+          Damp the notch energy a little less, so we don't damp out the
+          blip every time the phase reverses */
+       det->channel_level += ((abs(amp) - det->channel_level) >> 5);
+       det->notch_level += ((abs(notched) - det->notch_level) >> 4);
+       if (det->channel_level > 280) {
+               /* There is adequate energy in the channel.
+                Is it mostly at 2100Hz? */
+               if (det->notch_level*6 < det->channel_level) {
+                       /* The notch says yes, so we have the tone. */
+                       if (!det->tone_present) {
+                               /* Do we get a kick every 450+-25ms? */
+                               if (det->tone_cycle_duration >= 425*8
+                                       && det->tone_cycle_duration <= 475*8) {
+                                       det->good_cycles++;
+                                       if (det->good_cycles > 2)
+                                       det->hit = TRUE;
+                               }
+                               det->tone_cycle_duration = 0;
+                       }
+                       det->tone_present = TRUE;
+               } else
+                       det->tone_present = FALSE;
+               det->tone_cycle_duration++;
+       } else {
+               det->tone_present = FALSE;
+               det->tone_cycle_duration = 0;
+               det->good_cycles = 0;
+       }
+       return det->hit;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c
new file mode 100644 (file)
index 0000000..eb892d9
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * dsp_hwec.c:
+ * builtin mISDN dsp pipeline element for enabling the hw echocanceller
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mISDNdsp.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+static struct mISDN_dsp_element_arg args[] = {
+       { "deftaps", "128", "Set the number of taps of cancellation." },
+};
+
+static struct mISDN_dsp_element dsp_hwec_p = {
+       .name = "hwec",
+       .new = NULL,
+       .free = NULL,
+       .process_tx = NULL,
+       .process_rx = NULL,
+       .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg),
+       .args = args,
+};
+struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p;
+
+void dsp_hwec_enable(struct dsp *dsp, const char *arg)
+{
+       int deftaps = 128,
+               len;
+       struct mISDN_ctrl_req   cq;
+
+       if (!dsp) {
+               printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n",
+                       __func__);
+               return;
+       }
+
+       if (!arg)
+               goto _do;
+
+       len = strlen(arg);
+       if (!len)
+               goto _do;
+
+       {
+               char _dup[len + 1];
+               char *dup, *tok, *name, *val;
+               int tmp;
+
+               strcpy(_dup, arg);
+               dup = _dup;
+
+               while ((tok = strsep(&dup, ","))) {
+                       if (!strlen(tok))
+                               continue;
+                       name = strsep(&tok, "=");
+                       val = tok;
+
+                       if (!val)
+                               continue;
+
+                       if (!strcmp(name, "deftaps")) {
+                               if (sscanf(val, "%d", &tmp) == 1)
+                                       deftaps = tmp;
+                       }
+               }
+       }
+
+_do:
+       printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n",
+               __func__, deftaps);
+       memset(&cq, 0, sizeof(cq));
+       cq.op = MISDN_CTRL_HFC_ECHOCAN_ON;
+       cq.p1 = deftaps;
+       if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+               printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+}
+
+void dsp_hwec_disable(struct dsp *dsp)
+{
+       struct mISDN_ctrl_req   cq;
+
+       if (!dsp) {
+               printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n",
+                       __func__);
+               return;
+       }
+
+       printk(KERN_DEBUG "%s: disabling hwec\n", __func__);
+       memset(&cq, 0, sizeof(cq));
+       cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF;
+       if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+               printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+                       __func__);
+               return;
+       }
+}
+
+int dsp_hwec_init(void)
+{
+       mISDN_dsp_element_register(dsp_hwec);
+
+       return 0;
+}
+
+void dsp_hwec_exit(void)
+{
+       mISDN_dsp_element_unregister(dsp_hwec);
+}
+
diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h
new file mode 100644 (file)
index 0000000..eebe80c
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * dsp_hwec.h
+ */
+
+extern struct mISDN_dsp_element *dsp_hwec;
+extern void dsp_hwec_enable(struct dsp *dsp, const char *arg);
+extern void dsp_hwec_disable(struct dsp *dsp);
+extern int  dsp_hwec_init(void);
+extern void dsp_hwec_exit(void);
+
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
new file mode 100644 (file)
index 0000000..850260a
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * dsp_pipeline.c: pipelined audio processing
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+/* uncomment for debugging */
+/*#define PIPELINE_DEBUG*/
+
+struct dsp_pipeline_entry {
+       struct mISDN_dsp_element *elem;
+       void                *p;
+       struct list_head     list;
+};
+struct dsp_element_entry {
+       struct mISDN_dsp_element *elem;
+       struct device        dev;
+       struct list_head     list;
+};
+
+static LIST_HEAD(dsp_elements);
+
+/* sysfs */
+static struct class *elements_class;
+
+static ssize_t
+attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
+       ssize_t len = 0;
+       int i = 0;
+
+       *buf = 0;
+       for (; i < elem->num_args; ++i)
+               len = sprintf(buf, "%sName:        %s\n%s%s%sDescription: %s\n"
+                       "\n", buf,
+                         elem->args[i].name,
+                         elem->args[i].def ? "Default:     " : "",
+                         elem->args[i].def ? elem->args[i].def : "",
+                         elem->args[i].def ? "\n" : "",
+                         elem->args[i].desc);
+
+       return len;
+}
+
+static struct device_attribute element_attributes[] = {
+       __ATTR(args, 0444, attr_show_args, NULL),
+};
+
+int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
+{
+       struct dsp_element_entry *entry;
+       int ret, i;
+
+       if (!elem)
+               return -EINVAL;
+
+       entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->elem = elem;
+
+       entry->dev.class = elements_class;
+       dev_set_drvdata(&entry->dev, elem);
+       snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name);
+       ret = device_register(&entry->dev);
+       if (ret) {
+               printk(KERN_ERR "%s: failed to register %s\n",
+                       __func__, elem->name);
+               goto err1;
+       }
+
+       for (i = 0; i < (sizeof(element_attributes)
+               / sizeof(struct device_attribute)); ++i)
+               ret = device_create_file(&entry->dev,
+                               &element_attributes[i]);
+               if (ret) {
+                       printk(KERN_ERR "%s: failed to create device file\n",
+                               __func__);
+                       goto err2;
+               }
+
+       list_add_tail(&entry->list, &dsp_elements);
+
+       printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
+
+       return 0;
+
+err2:
+       device_unregister(&entry->dev);
+err1:
+       kfree(entry);
+       return ret;
+}
+EXPORT_SYMBOL(mISDN_dsp_element_register);
+
+void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
+{
+       struct dsp_element_entry *entry, *n;
+
+       if (!elem)
+               return;
+
+       list_for_each_entry_safe(entry, n, &dsp_elements, list)
+               if (entry->elem == elem) {
+                       list_del(&entry->list);
+                       device_unregister(&entry->dev);
+                       kfree(entry);
+                       printk(KERN_DEBUG "%s: %s unregistered\n",
+                               __func__, elem->name);
+                       return;
+               }
+       printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
+}
+EXPORT_SYMBOL(mISDN_dsp_element_unregister);
+
+int dsp_pipeline_module_init(void)
+{
+       elements_class = class_create(THIS_MODULE, "dsp_pipeline");
+       if (IS_ERR(elements_class))
+               return PTR_ERR(elements_class);
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
+#endif
+
+       dsp_hwec_init();
+
+       return 0;
+}
+
+void dsp_pipeline_module_exit(void)
+{
+       struct dsp_element_entry *entry, *n;
+
+       dsp_hwec_exit();
+
+       class_destroy(elements_class);
+
+       list_for_each_entry_safe(entry, n, &dsp_elements, list) {
+               list_del(&entry->list);
+               printk(KERN_WARNING "%s: element was still registered: %s\n",
+                       __func__, entry->elem->name);
+               kfree(entry);
+       }
+
+       printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
+}
+
+int dsp_pipeline_init(struct dsp_pipeline *pipeline)
+{
+       if (!pipeline)
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&pipeline->list);
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
+#endif
+
+       return 0;
+}
+
+static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+       struct dsp_pipeline_entry *entry, *n;
+
+       list_for_each_entry_safe(entry, n, &pipeline->list, list) {
+               list_del(&entry->list);
+               if (entry->elem == dsp_hwec)
+                       dsp_hwec_disable(container_of(pipeline, struct dsp,
+                               pipeline));
+               else
+                       entry->elem->free(entry->p);
+               kfree(entry);
+       }
+}
+
+void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+
+       if (!pipeline)
+               return;
+
+       _dsp_pipeline_destroy(pipeline);
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
+#endif
+}
+
+int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
+{
+       int len, incomplete = 0, found = 0;
+       char *dup, *tok, *name, *args;
+       struct dsp_element_entry *entry, *n;
+       struct dsp_pipeline_entry *pipeline_entry;
+       struct mISDN_dsp_element *elem;
+
+       if (!pipeline)
+               return -EINVAL;
+
+       if (!list_empty(&pipeline->list))
+               _dsp_pipeline_destroy(pipeline);
+
+       if (!cfg)
+               return 0;
+
+       len = strlen(cfg);
+       if (!len)
+               return 0;
+
+       dup = kmalloc(len + 1, GFP_KERNEL);
+       if (!dup)
+               return 0;
+       strcpy(dup, cfg);
+       while ((tok = strsep(&dup, "|"))) {
+               if (!strlen(tok))
+                       continue;
+               name = strsep(&tok, "(");
+               args = strsep(&tok, ")");
+               if (args && !*args)
+                       args = 0;
+
+               list_for_each_entry_safe(entry, n, &dsp_elements, list)
+                       if (!strcmp(entry->elem->name, name)) {
+                               elem = entry->elem;
+
+                               pipeline_entry = kmalloc(sizeof(struct
+                                       dsp_pipeline_entry), GFP_KERNEL);
+                               if (!pipeline_entry) {
+                                       printk(KERN_DEBUG "%s: failed to add "
+                                           "entry to pipeline: %s (out of "
+                                           "memory)\n", __func__, elem->name);
+                                       incomplete = 1;
+                                       goto _out;
+                               }
+                               pipeline_entry->elem = elem;
+
+                               if (elem == dsp_hwec) {
+                                       /* This is a hack to make the hwec
+                                          available as a pipeline module */
+                                       dsp_hwec_enable(container_of(pipeline,
+                                               struct dsp, pipeline), args);
+                                       list_add_tail(&pipeline_entry->list,
+                                               &pipeline->list);
+                               } else {
+                                       pipeline_entry->p = elem->new(args);
+                                       if (pipeline_entry->p) {
+                                               list_add_tail(&pipeline_entry->
+                                                       list, &pipeline->list);
+#ifdef PIPELINE_DEBUG
+                                               printk(KERN_DEBUG "%s: created "
+                                                   "instance of %s%s%s\n",
+                                                   __func__, name, args ?
+                                                   " with args " : "", args ?
+                                                   args : "");
+#endif
+                                       } else {
+                                               printk(KERN_DEBUG "%s: failed "
+                                                 "to add entry to pipeline: "
+                                                 "%s (new() returned NULL)\n",
+                                                 __func__, elem->name);
+                                               kfree(pipeline_entry);
+                                               incomplete = 1;
+                                       }
+                               }
+                               found = 1;
+                               break;
+                       }
+
+               if (found)
+                       found = 0;
+               else {
+                       printk(KERN_DEBUG "%s: element not found, skipping: "
+                               "%s\n", __func__, name);
+                       incomplete = 1;
+               }
+       }
+
+_out:
+       if (!list_empty(&pipeline->list))
+               pipeline->inuse = 1;
+       else
+               pipeline->inuse = 0;
+
+#ifdef PIPELINE_DEBUG
+       printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
+               __func__, incomplete ? " incomplete" : "", cfg);
+#endif
+       kfree(dup);
+       return 0;
+}
+
+void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+       struct dsp_pipeline_entry *entry;
+
+       if (!pipeline)
+               return;
+
+       list_for_each_entry(entry, &pipeline->list, list)
+               if (entry->elem->process_tx)
+                       entry->elem->process_tx(entry->p, data, len);
+}
+
+void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+       struct dsp_pipeline_entry *entry;
+
+       if (!pipeline)
+               return;
+
+       list_for_each_entry_reverse(entry, &pipeline->list, list)
+               if (entry->elem->process_rx)
+                       entry->elem->process_rx(entry->p, data, len);
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c
new file mode 100644 (file)
index 0000000..23dd0dd
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+
+#define DATA_S sample_silence
+#define SIZE_S (&sizeof_silence)
+#define DATA_GA sample_german_all
+#define SIZE_GA (&sizeof_german_all)
+#define DATA_GO sample_german_old
+#define SIZE_GO (&sizeof_german_old)
+#define DATA_DT sample_american_dialtone
+#define SIZE_DT (&sizeof_american_dialtone)
+#define DATA_RI sample_american_ringing
+#define SIZE_RI (&sizeof_american_ringing)
+#define DATA_BU sample_american_busy
+#define SIZE_BU (&sizeof_american_busy)
+#define DATA_S1 sample_special1
+#define SIZE_S1 (&sizeof_special1)
+#define DATA_S2 sample_special2
+#define SIZE_S2 (&sizeof_special2)
+#define DATA_S3 sample_special3
+#define SIZE_S3 (&sizeof_special3)
+
+/***************/
+/* tones loops */
+/***************/
+
+/* all tones are alaw encoded */
+/* the last sample+1 is in phase with the first sample. the error is low */
+
+static u8 sample_german_all[] = {
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+       0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
+       0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
+       0xdc, 0xfc, 0x6c,
+};
+static u32 sizeof_german_all = sizeof(sample_german_all);
+
+static u8 sample_german_old[] = {
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+       0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
+       0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
+       0x8c,
+};
+static u32 sizeof_german_old = sizeof(sample_german_old);
+
+static u8 sample_american_dialtone[] = {
+       0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c,
+       0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d,
+       0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0,
+       0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67,
+       0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67,
+       0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef,
+       0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8,
+       0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61,
+       0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e,
+       0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30,
+       0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d,
+       0x6d, 0x91, 0x19,
+};
+static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone);
+
+static u8 sample_american_ringing[] = {
+       0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90,
+       0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed,
+       0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c,
+       0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d,
+       0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec,
+       0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11,
+       0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00,
+       0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39,
+       0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6,
+       0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3,
+       0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b,
+       0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f,
+       0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56,
+       0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59,
+       0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30,
+       0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d,
+       0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c,
+       0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd,
+       0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc,
+       0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d,
+       0x4d, 0xbd, 0x0d, 0xad, 0xe1,
+};
+static u32 sizeof_american_ringing = sizeof(sample_american_ringing);
+
+static u8 sample_american_busy[] = {
+       0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66,
+       0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96,
+       0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57,
+       0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f,
+       0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40,
+       0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d,
+       0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c,
+       0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d,
+       0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40,
+       0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7,
+       0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a,
+       0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7,
+       0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40,
+       0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d,
+       0x4d, 0x4d, 0x6d, 0x01,
+};
+static u32 sizeof_american_busy = sizeof(sample_american_busy);
+
+static u8 sample_special1[] = {
+       0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d,
+       0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd,
+       0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd,
+       0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd,
+       0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed,
+       0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41,
+       0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7,
+       0x6d, 0xbd, 0x2d,
+};
+static u32 sizeof_special1 = sizeof(sample_special1);
+
+static u8 sample_special2[] = {
+       0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
+       0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
+       0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
+       0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
+       0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
+       0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
+       0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
+       0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
+       0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
+       0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
+};
+static u32 sizeof_special2 = sizeof(sample_special2);
+
+static u8 sample_special3[] = {
+       0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
+       0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
+       0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
+       0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
+       0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
+       0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
+       0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
+       0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
+       0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
+       0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
+};
+static u32 sizeof_special3 = sizeof(sample_special3);
+
+static u8 sample_silence[] = {
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+       0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
+};
+static u32 sizeof_silence = sizeof(sample_silence);
+
+struct tones_samples {
+       u32 *len;
+       u8 *data;
+};
+static struct
+tones_samples samples[] = {
+       {&sizeof_german_all, sample_german_all},
+       {&sizeof_german_old, sample_german_old},
+       {&sizeof_american_dialtone, sample_american_dialtone},
+       {&sizeof_american_ringing, sample_american_ringing},
+       {&sizeof_american_busy, sample_american_busy},
+       {&sizeof_special1, sample_special1},
+       {&sizeof_special2, sample_special2},
+       {&sizeof_special3, sample_special3},
+       {NULL, NULL},
+};
+
+/***********************************
+ * generate ulaw from alaw samples *
+ ***********************************/
+
+void
+dsp_audio_generate_ulaw_samples(void)
+{
+       int i, j;
+
+       i = 0;
+       while (samples[i].len) {
+               j = 0;
+               while (j < (*samples[i].len)) {
+                       samples[i].data[j] =
+                               dsp_audio_alaw_to_ulaw[samples[i].data[j]];
+                       j++;
+               }
+               i++;
+       }
+}
+
+
+/****************************
+ * tone sequence definition *
+ ****************************/
+
+struct pattern {
+       int tone;
+       u8 *data[10];
+       u32 *siz[10];
+       u32 seq[10];
+} pattern[] = {
+       {TONE_GERMAN_DIALTONE,
+       {DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDDIALTONE,
+       {DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_DIALTONE,
+       {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_DIALPBX,
+       {DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0},
+       {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDDIALPBX,
+       {DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0},
+       {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_DIALPBX,
+       {DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, 0, 0, 0, 0},
+       {SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0},
+       {2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_RINGING,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDRINGING,
+       {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_RINGING,
+       {DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_RINGPBX,
+       {DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDRINGPBX,
+       {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_RINGPBX,
+       {DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_BUSY,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDBUSY,
+       {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_BUSY,
+       {DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_HANGUP,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_OLDHANGUP,
+       {DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_AMERICAN_HANGUP,
+       {DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_SPECIAL_INFO,
+       {DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_GASSENBESETZT,
+       {DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
+       {2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
+
+       {TONE_GERMAN_AUFSCHALTTON,
+       {DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
+       {SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
+       {1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
+
+       {0,
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+/******************
+ * copy tone data *
+ ******************/
+
+/* an sk_buff is generated from the number of samples needed.
+ * the count will be changed and may begin from 0 each pattern period.
+ * the clue is to precalculate the pointers and legths to use only one
+ * memcpy per function call, or two memcpy if the tone sequence changes.
+ *
+ * pattern - the type of the pattern
+ * count - the sample from the beginning of the pattern (phase)
+ * len - the number of bytes
+ *
+ * return - the sk_buff with the sample
+ *
+ * if tones has finished (e.g. knocking tone), dsp->tones is turned off
+ */
+void dsp_tone_copy(struct dsp *dsp, u8 *data, int len)
+{
+       int index, count, start, num;
+       struct pattern *pat;
+       struct dsp_tone *tone = &dsp->tone;
+
+       /* if we have no tone, we copy silence */
+       if (!tone->tone) {
+               memset(data, dsp_silence, len);
+               return;
+       }
+
+       /* process pattern */
+       pat = (struct pattern *)tone->pattern;
+               /* points to the current pattern */
+       index = tone->index; /* gives current sequence index */
+       count = tone->count; /* gives current sample */
+
+       /* copy sample */
+       while (len) {
+               /* find sample to start with */
+               while (42) {
+                       /* warp arround */
+                       if (!pat->seq[index]) {
+                               count = 0;
+                               index = 0;
+                       }
+                       /* check if we are currently playing this tone */
+                       if (count < pat->seq[index])
+                               break;
+                       if (dsp_debug & DEBUG_DSP_TONE)
+                               printk(KERN_DEBUG "%s: reaching next sequence "
+                                       "(index=%d)\n", __func__, index);
+                       count -= pat->seq[index];
+                       index++;
+               }
+               /* calculate start and number of samples */
+               start = count % (*(pat->siz[index]));
+               num = len;
+               if (num+count > pat->seq[index])
+                       num = pat->seq[index] - count;
+               if (num+start > (*(pat->siz[index])))
+                       num = (*(pat->siz[index])) - start;
+               /* copy memory */
+               memcpy(data, pat->data[index]+start, num);
+               /* reduce length */
+               data += num;
+               count += num;
+               len -= num;
+       }
+       tone->index = index;
+       tone->count = count;
+
+       /* return sk_buff */
+       return;
+}
+
+
+/*******************************
+ * send HW message to hfc card *
+ *******************************/
+
+static void
+dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len)
+{
+       struct sk_buff *nskb;
+
+       /* unlocking is not required, because we don't expect a response */
+       nskb = _alloc_mISDN_skb(PH_CONTROL_REQ,
+               (len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample,
+               GFP_ATOMIC);
+       if (nskb) {
+               if (dsp->ch.peer) {
+                       if (dsp->ch.recv(dsp->ch.peer, nskb))
+                               dev_kfree_skb(nskb);
+               } else
+                       dev_kfree_skb(nskb);
+       }
+}
+
+
+/*****************
+ * timer expires *
+ *****************/
+void
+dsp_tone_timeout(void *arg)
+{
+       struct dsp *dsp = arg;
+       struct dsp_tone *tone = &dsp->tone;
+       struct pattern *pat = (struct pattern *)tone->pattern;
+       int index = tone->index;
+
+       if (!tone->tone)
+               return;
+
+       index++;
+       if (!pat->seq[index])
+               index = 0;
+       tone->index = index;
+
+       /* set next tone */
+       if (pat->data[index] == DATA_S)
+               dsp_tone_hw_message(dsp, 0, 0);
+       else
+               dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index]));
+       /* set timer */
+       init_timer(&tone->tl);
+       tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000;
+       add_timer(&tone->tl);
+}
+
+
+/********************
+ * set/release tone *
+ ********************/
+
+/*
+ * tones are relaized by streaming or by special loop commands if supported
+ * by hardware. when hardware is used, the patterns will be controlled by
+ * timers.
+ */
+int
+dsp_tone(struct dsp *dsp, int tone)
+{
+       struct pattern *pat;
+       int i;
+       struct dsp_tone *tonet = &dsp->tone;
+
+       tonet->software = 0;
+       tonet->hardware = 0;
+
+       /* we turn off the tone */
+       if (!tone) {
+               if (dsp->features.hfc_loops)
+               if (timer_pending(&tonet->tl))
+                       del_timer(&tonet->tl);
+               if (dsp->features.hfc_loops)
+                       dsp_tone_hw_message(dsp, NULL, 0);
+               tonet->tone = 0;
+               return 0;
+       }
+
+       pat = NULL;
+       i = 0;
+       while (pattern[i].tone) {
+               if (pattern[i].tone == tone) {
+                       pat = &pattern[i];
+                       break;
+               }
+               i++;
+       }
+       if (!pat) {
+               printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone);
+               return -EINVAL;
+       }
+       if (dsp_debug & DEBUG_DSP_TONE)
+               printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n",
+                       __func__, tone, 0);
+       tonet->tone = tone;
+       tonet->pattern = pat;
+       tonet->index = 0;
+       tonet->count = 0;
+
+       if (dsp->features.hfc_loops) {
+               tonet->hardware = 1;
+               /* set first tone */
+               dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0]));
+               /* set timer */
+               if (timer_pending(&tonet->tl))
+                       del_timer(&tonet->tl);
+               init_timer(&tonet->tl);
+               tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000;
+               add_timer(&tonet->tl);
+       } else {
+               tonet->software = 1;
+       }
+
+       return 0;
+}
+
+
+
+
+
diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c
new file mode 100644 (file)
index 0000000..b5d6553
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * finite state machine implementation
+ *
+ * Author       Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "fsm.h"
+
+#define FSM_TIMER_DEBUG 0
+
+void
+mISDN_FsmNew(struct Fsm *fsm,
+       struct FsmNode *fnlist, int fncount)
+{
+       int i;
+
+       fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count *
+               fsm->event_count, GFP_KERNEL);
+
+       for (i = 0; i < fncount; i++)
+               if ((fnlist[i].state >= fsm->state_count) ||
+                   (fnlist[i].event >= fsm->event_count)) {
+                       printk(KERN_ERR
+                           "mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
+                           i, (long)fnlist[i].state, (long)fsm->state_count,
+                           (long)fnlist[i].event, (long)fsm->event_count);
+               } else
+                       fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
+                           fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
+}
+EXPORT_SYMBOL(mISDN_FsmNew);
+
+void
+mISDN_FsmFree(struct Fsm *fsm)
+{
+       kfree((void *) fsm->jumpmatrix);
+}
+EXPORT_SYMBOL(mISDN_FsmFree);
+
+int
+mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
+{
+       FSMFNPTR r;
+
+       if ((fi->state >= fi->fsm->state_count) ||
+           (event >= fi->fsm->event_count)) {
+               printk(KERN_ERR
+                   "mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
+                   (long)fi->state, (long)fi->fsm->state_count, event,
+                   (long)fi->fsm->event_count);
+               return 1;
+       }
+       r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+       if (r) {
+               if (fi->debug)
+                       fi->printdebug(fi, "State %s Event %s",
+                               fi->fsm->strState[fi->state],
+                               fi->fsm->strEvent[event]);
+               r(fi, event, arg);
+               return 0;
+       } else {
+               if (fi->debug)
+                       fi->printdebug(fi, "State %s Event %s no action",
+                               fi->fsm->strState[fi->state],
+                               fi->fsm->strEvent[event]);
+               return 1;
+       }
+}
+EXPORT_SYMBOL(mISDN_FsmEvent);
+
+void
+mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
+{
+       fi->state = newstate;
+       if (fi->debug)
+               fi->printdebug(fi, "ChangeState %s",
+                       fi->fsm->strState[newstate]);
+}
+EXPORT_SYMBOL(mISDN_FsmChangeState);
+
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
+#endif
+       mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+       ft->fi = fi;
+       ft->tl.function = (void *) FsmExpireTimer;
+       ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
+#endif
+       init_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmInitTimer);
+
+void
+mISDN_FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
+                       (long) ft, where);
+#endif
+       del_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmDelTimer);
+
+int
+mISDN_FsmAddTimer(struct FsmTimer *ft,
+           int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d",
+                       (long) ft, millisec, where);
+#endif
+
+       if (timer_pending(&ft->tl)) {
+               if (ft->fi->debug) {
+                       printk(KERN_WARNING
+                               "mISDN_FsmAddTimer: timer already active!\n");
+                       ft->fi->printdebug(ft->fi,
+                               "mISDN_FsmAddTimer already active!");
+               }
+               return -1;
+       }
+       init_timer(&ft->tl);
+       ft->event = event;
+       ft->arg = arg;
+       ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+       add_timer(&ft->tl);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_FsmAddTimer);
+
+void
+mISDN_FsmRestartTimer(struct FsmTimer *ft,
+           int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+       if (ft->fi->debug)
+               ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d",
+                       (long) ft, millisec, where);
+#endif
+
+       if (timer_pending(&ft->tl))
+               del_timer(&ft->tl);
+       init_timer(&ft->tl);
+       ft->event = event;
+       ft->arg = arg;
+       ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+       add_timer(&ft->tl);
+}
+EXPORT_SYMBOL(mISDN_FsmRestartTimer);
diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h
new file mode 100644 (file)
index 0000000..928f5be
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ * Author       Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MISDN_FSM_H
+#define _MISDN_FSM_H
+
+#include <linux/timer.h>
+
+/* Statemachine */
+
+struct FsmInst;
+
+typedef void (*FSMFNPTR)(struct FsmInst *, int, void *);
+
+struct Fsm {
+       FSMFNPTR *jumpmatrix;
+       int state_count, event_count;
+       char **strEvent, **strState;
+};
+
+struct FsmInst {
+       struct Fsm *fsm;
+       int state;
+       int debug;
+       void *userdata;
+       int userint;
+       void (*printdebug) (struct FsmInst *, char *, ...);
+};
+
+struct FsmNode {
+       int state, event;
+       void (*routine) (struct FsmInst *, int, void *);
+};
+
+struct FsmTimer {
+       struct FsmInst *fi;
+       struct timer_list tl;
+       int event;
+       void *arg;
+};
+
+extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int);
+extern void mISDN_FsmFree(struct Fsm *);
+extern int mISDN_FsmEvent(struct FsmInst *, int , void *);
+extern void mISDN_FsmChangeState(struct FsmInst *, int);
+extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *);
+extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int);
+extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int);
+extern void mISDN_FsmDelTimer(struct FsmTimer *, int);
+
+#endif
diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c
new file mode 100644 (file)
index 0000000..2596fba
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+
+static void
+dchannel_bh(struct work_struct *ws)
+{
+       struct dchannel *dch  = container_of(ws, struct dchannel, workq);
+       struct sk_buff  *skb;
+       int             err;
+
+       if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
+               while ((skb = skb_dequeue(&dch->rqueue))) {
+                       if (likely(dch->dev.D.peer)) {
+                               err = dch->dev.D.recv(dch->dev.D.peer, skb);
+                               if (err)
+                                       dev_kfree_skb(skb);
+                       } else
+                               dev_kfree_skb(skb);
+               }
+       }
+       if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
+               if (dch->phfunc)
+                       dch->phfunc(dch);
+       }
+}
+
+static void
+bchannel_bh(struct work_struct *ws)
+{
+       struct bchannel *bch  = container_of(ws, struct bchannel, workq);
+       struct sk_buff  *skb;
+       int             err;
+
+       if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
+               while ((skb = skb_dequeue(&bch->rqueue))) {
+                       if (bch->rcount >= 64)
+                               printk(KERN_WARNING "B-channel %p receive "
+                                       "queue if full, but empties...\n", bch);
+                       bch->rcount--;
+                       if (likely(bch->ch.peer)) {
+                               err = bch->ch.recv(bch->ch.peer, skb);
+                               if (err)
+                                       dev_kfree_skb(skb);
+                       } else
+                               dev_kfree_skb(skb);
+               }
+       }
+}
+
+int
+mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
+{
+       test_and_set_bit(FLG_HDLC, &ch->Flags);
+       ch->maxlen = maxlen;
+       ch->hw = NULL;
+       ch->rx_skb = NULL;
+       ch->tx_skb = NULL;
+       ch->tx_idx = 0;
+       ch->phfunc = phf;
+       skb_queue_head_init(&ch->squeue);
+       skb_queue_head_init(&ch->rqueue);
+       INIT_LIST_HEAD(&ch->dev.bchannels);
+       INIT_WORK(&ch->workq, dchannel_bh);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_initdchannel);
+
+int
+mISDN_initbchannel(struct bchannel *ch, int maxlen)
+{
+       ch->Flags = 0;
+       ch->maxlen = maxlen;
+       ch->hw = NULL;
+       ch->rx_skb = NULL;
+       ch->tx_skb = NULL;
+       ch->tx_idx = 0;
+       skb_queue_head_init(&ch->rqueue);
+       ch->rcount = 0;
+       ch->next_skb = NULL;
+       INIT_WORK(&ch->workq, bchannel_bh);
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_initbchannel);
+
+int
+mISDN_freedchannel(struct dchannel *ch)
+{
+       if (ch->tx_skb) {
+               dev_kfree_skb(ch->tx_skb);
+               ch->tx_skb = NULL;
+       }
+       if (ch->rx_skb) {
+               dev_kfree_skb(ch->rx_skb);
+               ch->rx_skb = NULL;
+       }
+       skb_queue_purge(&ch->squeue);
+       skb_queue_purge(&ch->rqueue);
+       flush_scheduled_work();
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_freedchannel);
+
+int
+mISDN_freebchannel(struct bchannel *ch)
+{
+       if (ch->tx_skb) {
+               dev_kfree_skb(ch->tx_skb);
+               ch->tx_skb = NULL;
+       }
+       if (ch->rx_skb) {
+               dev_kfree_skb(ch->rx_skb);
+               ch->rx_skb = NULL;
+       }
+       if (ch->next_skb) {
+               dev_kfree_skb(ch->next_skb);
+               ch->next_skb = NULL;
+       }
+       skb_queue_purge(&ch->rqueue);
+       ch->rcount = 0;
+       flush_scheduled_work();
+       return 0;
+}
+EXPORT_SYMBOL(mISDN_freebchannel);
+
+static inline u_int
+get_sapi_tei(u_char *p)
+{
+       u_int   sapi, tei;
+
+       sapi = *p >> 2;
+       tei = p[1] >> 1;
+       return sapi | (tei << 8);
+}
+
+void
+recv_Dchannel(struct dchannel *dch)
+{
+       struct mISDNhead *hh;
+
+       if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
+               dev_kfree_skb(dch->rx_skb);
+               dch->rx_skb = NULL;
+               return;
+       }
+       hh = mISDN_HEAD_P(dch->rx_skb);
+       hh->prim = PH_DATA_IND;
+       hh->id = get_sapi_tei(dch->rx_skb->data);
+       skb_queue_tail(&dch->rqueue, dch->rx_skb);
+       dch->rx_skb = NULL;
+       schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel);
+
+void
+recv_Bchannel(struct bchannel *bch)
+{
+       struct mISDNhead *hh;
+
+       hh = mISDN_HEAD_P(bch->rx_skb);
+       hh->prim = PH_DATA_IND;
+       hh->id = MISDN_ID_ANY;
+       if (bch->rcount >= 64) {
+               dev_kfree_skb(bch->rx_skb);
+               bch->rx_skb = NULL;
+               return;
+       }
+       bch->rcount++;
+       skb_queue_tail(&bch->rqueue, bch->rx_skb);
+       bch->rx_skb = NULL;
+       schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel);
+
+void
+recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
+{
+       skb_queue_tail(&dch->rqueue, skb);
+       schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel_skb);
+
+void
+recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
+{
+       if (bch->rcount >= 64) {
+               dev_kfree_skb(skb);
+               return;
+       }
+       bch->rcount++;
+       skb_queue_tail(&bch->rqueue, skb);
+       schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel_skb);
+
+static void
+confirm_Dsend(struct dchannel *dch)
+{
+       struct sk_buff  *skb;
+
+       skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
+           0, NULL, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_ERR "%s: no skb id %x\n", __func__,
+                   mISDN_HEAD_ID(dch->tx_skb));
+               return;
+       }
+       skb_queue_tail(&dch->rqueue, skb);
+       schedule_event(dch, FLG_RECVQUEUE);
+}
+
+int
+get_next_dframe(struct dchannel *dch)
+{
+       dch->tx_idx = 0;
+       dch->tx_skb = skb_dequeue(&dch->squeue);
+       if (dch->tx_skb) {
+               confirm_Dsend(dch);
+               return 1;
+       }
+       dch->tx_skb = NULL;
+       test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+       return 0;
+}
+EXPORT_SYMBOL(get_next_dframe);
+
+void
+confirm_Bsend(struct bchannel *bch)
+{
+       struct sk_buff  *skb;
+
+       if (bch->rcount >= 64)
+               return;
+       skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
+           0, NULL, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_ERR "%s: no skb id %x\n", __func__,
+                   mISDN_HEAD_ID(bch->tx_skb));
+               return;
+       }
+       bch->rcount++;
+       skb_queue_tail(&bch->rqueue, skb);
+       schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(confirm_Bsend);
+
+int
+get_next_bframe(struct bchannel *bch)
+{
+       bch->tx_idx = 0;
+       if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
+               bch->tx_skb = bch->next_skb;
+               if (bch->tx_skb) {
+                       bch->next_skb = NULL;
+                       test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               confirm_Bsend(bch); /* not for transparent */
+                       return 1;
+               } else {
+                       test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+                       printk(KERN_WARNING "B TX_NEXT without skb\n");
+               }
+       }
+       bch->tx_skb = NULL;
+       test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+       return 0;
+}
+EXPORT_SYMBOL(get_next_bframe);
+
+void
+queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
+{
+       struct mISDNhead *hh;
+
+       if (!skb) {
+               _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
+       } else {
+               if (ch->peer) {
+                       hh = mISDN_HEAD_P(skb);
+                       hh->prim = pr;
+                       hh->id = id;
+                       if (!ch->recv(ch->peer, skb))
+                               return;
+               }
+               dev_kfree_skb(skb);
+       }
+}
+EXPORT_SYMBOL(queue_ch_frame);
+
+int
+dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
+{
+       /* check oversize */
+       if (skb->len <= 0) {
+               printk(KERN_WARNING "%s: skb too small\n", __func__);
+               return -EINVAL;
+       }
+       if (skb->len > ch->maxlen) {
+               printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+                       __func__, skb->len, ch->maxlen);
+               return -EINVAL;
+       }
+       /* HW lock must be obtained */
+       if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+               skb_queue_tail(&ch->squeue, skb);
+               return 0;
+       } else {
+               /* write to fifo */
+               ch->tx_skb = skb;
+               ch->tx_idx = 0;
+               return 1;
+       }
+}
+EXPORT_SYMBOL(dchannel_senddata);
+
+int
+bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
+{
+
+       /* check oversize */
+       if (skb->len <= 0) {
+               printk(KERN_WARNING "%s: skb too small\n", __func__);
+               return -EINVAL;
+       }
+       if (skb->len > ch->maxlen) {
+               printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+                       __func__, skb->len, ch->maxlen);
+               return -EINVAL;
+       }
+       /* HW lock must be obtained */
+       /* check for pending next_skb */
+       if (ch->next_skb) {
+               printk(KERN_WARNING
+                   "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
+                   __func__, skb->len, ch->next_skb->len);
+               return -EBUSY;
+       }
+       if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+               test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
+               ch->next_skb = skb;
+               return 0;
+       } else {
+               /* write to fifo */
+               ch->tx_skb = skb;
+               ch->tx_idx = 0;
+               return 1;
+       }
+}
+EXPORT_SYMBOL(bchannel_senddata);
diff --git a/drivers/isdn/mISDN/l1oip.h b/drivers/isdn/mISDN/l1oip.h
new file mode 100644 (file)
index 0000000..a23d575
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * see notice in l1oip.c
+ */
+
+/* debugging */
+#define DEBUG_L1OIP_INIT       0x00010000
+#define DEBUG_L1OIP_SOCKET     0x00020000
+#define DEBUG_L1OIP_MGR                0x00040000
+#define DEBUG_L1OIP_MSG                0x00080000
+
+/* enable to disorder received bchannels by sequence 2143658798... */
+/*
+#define REORDER_DEBUG
+*/
+
+/* frames */
+#define L1OIP_MAX_LEN          2048            /* max packet size form l2 */
+#define L1OIP_MAX_PERFRAME     1400            /* max data size in one frame */
+
+
+/* timers */
+#define L1OIP_KEEPALIVE                15
+#define L1OIP_TIMEOUT          65
+
+
+/* socket */
+#define L1OIP_DEFAULTPORT      931
+
+
+/* channel structure */
+struct l1oip_chan {
+       struct dchannel         *dch;
+       struct bchannel         *bch;
+       u32                     tx_counter;     /* counts xmit bytes/packets */
+       u32                     rx_counter;     /* counts recv bytes/packets */
+       u32                     codecstate;     /* used by codec to save data */
+#ifdef REORDER_DEBUG
+       int                     disorder_flag;
+       struct sk_buff          *disorder_skb;
+       u32                     disorder_cnt;
+#endif
+};
+
+
+/* card structure */
+struct l1oip {
+       struct list_head        list;
+
+       /* card */
+       int                     registered;     /* if registered with mISDN */
+       char                    name[MISDN_MAX_IDLEN];
+       int                     idx;            /* card index */
+       int                     pri;            /* 1=pri, 0=bri */
+       int                     d_idx;          /* current dchannel number */
+       int                     b_num;          /* number of bchannels */
+       u32                     id;             /* id of connection */
+       int                     ondemand;       /* if transmis. is on demand */
+       int                     bundle;         /* bundle channels in one frm */
+       int                     codec;          /* codec to use for transmis. */
+       int                     limit;          /* limit number of bchannels */
+
+       /* timer */
+       struct timer_list       keep_tl;
+       struct timer_list       timeout_tl;
+       int                     timeout_on;
+       struct work_struct      workq;
+
+       /* socket */
+       struct socket           *socket;        /* if set, socket is created */
+       struct completion       socket_complete;/* completion of sock thread */
+       struct task_struct      *socket_thread;
+       spinlock_t              socket_lock;    /* access sock outside thread */
+       u32                     remoteip;       /* if all set, ip is assigned */
+       u16                     localport;      /* must always be set */
+       u16                     remoteport;     /* must always be set */
+       struct sockaddr_in      sin_local;      /* local socket name */
+       struct sockaddr_in      sin_remote;     /* remote socket name */
+       struct msghdr           sendmsg;        /* ip message to send */
+       struct iovec            sendiov;        /* iov for message */
+
+       /* frame */
+       struct l1oip_chan       chan[128];      /* channel instances */
+};
+
+extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state);
+extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result);
+extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result);
+extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result);
+extern void l1oip_4bit_free(void);
+extern int l1oip_4bit_alloc(int ulaw);
+
diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c
new file mode 100644 (file)
index 0000000..a2dc457
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+
+ * l1oip_codec.c  generic codec using lookup table
+ *  -> conversion from a-Law to u-Law
+ *  -> conversion from u-Law to a-Law
+ *  -> compression by reducing the number of sample resolution to 4
+ *
+ * NOTE: It is not compatible with any standard codec like ADPCM.
+ *
+ * Author      Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+/*
+
+How the codec works:
+--------------------
+
+The volume is increased to increase the dynamic range of the audio signal.
+Each sample is converted to a-LAW with only 16 steps of level resolution.
+A pair of two samples are stored in one byte.
+
+The first byte is stored in the upper bits, the second byte is stored in the
+lower bits.
+
+To speed up compression and decompression, two lookup tables are formed:
+
+- 16 bits index for two samples (law encoded) with 8 bit compressed result.
+- 8 bits index for one compressed data with 16 bits decompressed result.
+
+NOTE: The bytes are handled as they are law-encoded.
+
+*/
+
+#include <linux/vmalloc.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+/* definitions of codec. don't use calculations, code may run slower. */
+
+static u8 *table_com;
+static u16 *table_dec;
+
+
+/* alaw -> ulaw */
+static u8 alaw_to_ulaw[256] =
+{
+       0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
+       0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
+       0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
+       0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
+       0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
+       0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
+       0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
+       0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
+       0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
+       0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
+       0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
+       0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
+       0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
+       0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
+       0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
+       0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
+       0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
+       0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
+       0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
+       0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
+       0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
+       0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
+       0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
+       0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
+       0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
+       0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
+       0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
+       0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
+       0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
+       0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
+       0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
+       0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
+};
+
+/* ulaw -> alaw */
+static u8 ulaw_to_alaw[256] =
+{
+       0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
+       0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
+       0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
+       0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
+       0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
+       0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
+       0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
+       0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
+       0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
+       0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
+       0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
+       0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
+       0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
+       0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
+       0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
+       0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
+       0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
+       0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
+       0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
+       0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
+       0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
+       0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
+       0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
+       0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
+       0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
+       0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
+       0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
+       0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
+       0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
+       0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
+       0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
+       0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
+};
+
+/* alaw -> 4bit compression */
+static u8 alaw_to_4bit[256] = {
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02,
+       0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+       0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+       0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+};
+
+/* 4bit -> alaw decompression */
+static u8 _4bit_to_alaw[16] = {
+       0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b,
+       0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c,
+};
+
+/* ulaw -> 4bit compression */
+static u8 ulaw_to_4bit[256] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+       0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+       0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+       0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+       0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+       0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b,
+       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+       0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a,
+       0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+       0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+       0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+};
+
+/* 4bit -> ulaw decompression */
+static u8 _4bit_to_ulaw[16] = {
+       0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71,
+       0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f,
+};
+
+
+/*
+ * Compresses data to the result buffer
+ * The result size must be at least half of the input buffer.
+ * The number of samples also must be even!
+ */
+int
+l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state)
+{
+       int ii, i = 0, o = 0;
+
+       if (!len)
+               return 0;
+
+       /* send saved byte and first input byte */
+       if (*state) {
+               *result++ = table_com[(((*state)<<8)&0xff00) | (*data++)];
+               len--;
+               o++;
+       }
+
+       ii = len >> 1;
+
+       while (i < ii) {
+               *result++ = table_com[(data[0]<<8) | (data[1])];
+               data += 2;
+               i++;
+               o++;
+       }
+
+       /* if len has an odd number, we save byte for next call */
+       if (len & 1)
+               *state = 0x100 + *data;
+       else
+               *state = 0;
+
+       return o;
+}
+
+/* Decompress data to the result buffer
+ * The result size must be the number of sample in packet. (2 * input data)
+ * The number of samples in the result are even!
+ */
+int
+l1oip_4bit_to_law(u8 *data, int len, u8 *result)
+{
+       int i = 0;
+       u16 r;
+
+       while (i < len) {
+               r = table_dec[*data++];
+               *result++ = r>>8;
+               *result++ = r;
+               i++;
+       }
+
+       return len << 1;
+}
+
+
+/*
+ * law conversion
+ */
+int
+l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result)
+{
+       int i = 0;
+
+       while (i < len) {
+               *result++ = alaw_to_ulaw[*data++];
+               i++;
+       }
+
+       return len;
+}
+
+int
+l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result)
+{
+       int i = 0;
+
+       while (i < len) {
+               *result++ = ulaw_to_alaw[*data++];
+               i++;
+       }
+
+       return len;
+}
+
+
+/*
+ * generate/free compression and decompression table
+ */
+void
+l1oip_4bit_free(void)
+{
+       if (table_dec)
+               vfree(table_dec);
+       if (table_com)
+               vfree(table_com);
+       table_com = NULL;
+       table_dec = NULL;
+}
+
+int
+l1oip_4bit_alloc(int ulaw)
+{
+       int i1, i2, c, sample;
+
+       /* in case, it is called again */
+       if (table_dec)
+               return 0;
+
+       /* alloc conversion tables */
+       table_com = vmalloc(65536);
+       table_dec = vmalloc(512);
+       if (!table_com | !table_dec) {
+               l1oip_4bit_free();
+               return -ENOMEM;
+       }
+       memset(table_com, 0, 65536);
+       memset(table_dec, 0, 512);
+       /* generate compression table */
+       i1 = 0;
+       while (i1 < 256) {
+               if (ulaw)
+                       c = ulaw_to_4bit[i1];
+               else
+                       c = alaw_to_4bit[i1];
+               i2 = 0;
+               while (i2 < 256) {
+                       table_com[(i1<<8) | i2] |= (c<<4);
+                       table_com[(i2<<8) | i1] |= c;
+                       i2++;
+               }
+               i1++;
+       }
+
+       /* generate decompression table */
+       i1 = 0;
+       while (i1 < 16) {
+               if (ulaw)
+                       sample = _4bit_to_ulaw[i1];
+               else
+                       sample = _4bit_to_alaw[i1];
+               i2 = 0;
+               while (i2 < 16) {
+                       table_dec[(i1<<4) | i2] |= (sample<<8);
+                       table_dec[(i2<<4) | i1] |= sample;
+                       i2++;
+               }
+               i1++;
+       }
+
+       return 0;
+}
+
+
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
new file mode 100644 (file)
index 0000000..155b997
--- /dev/null
@@ -0,0 +1,1518 @@
+/*
+
+ * l1oip.c  low level driver for tunneling layer 1 over IP
+ *
+ * NOTE: It is not compatible with TDMoIP nor "ISDN over IP".
+ *
+ * Author      Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* module parameters:
+ * type:
+       Value 1 = BRI
+       Value 2 = PRI
+       Value 3 = BRI (multi channel frame, not supported yet)
+       Value 4 = PRI (multi channel frame, not supported yet)
+       A multi channel frame reduces overhead to a single frame for all
+       b-channels, but increases delay.
+       (NOTE: Multi channel frames are not implemented yet.)
+
+ * codec:
+       Value 0 = transparent (default)
+       Value 1 = transfer ALAW
+       Value 2 = transfer ULAW
+       Value 3 = transfer generic 4 bit compression.
+
+ * ulaw:
+       0 = we use a-Law (default)
+       1 = we use u-Law
+
+ * limit:
+       limitation of B-channels to control bandwidth (1...126)
+       BRI: 1 or 2
+       PRI: 1-30, 31-126 (126, because dchannel ist not counted here)
+       Also limited ressources are used for stack, resulting in less channels.
+       It is possible to have more channels than 30 in PRI mode, this must
+       be supported by the application.
+
+ * ip:
+       byte representation of remote ip address (127.0.0.1 -> 127,0,0,1)
+       If not given or four 0, no remote address is set.
+       For multiple interfaces, concat ip addresses. (127,0,0,1,127,0,0,1)
+
+ * port:
+       port number (local interface)
+       If not given or 0, port 931 is used for fist instance, 932 for next...
+       For multiple interfaces, different ports must be given.
+
+ * remoteport:
+       port number (remote interface)
+       If not given or 0, remote port equals local port
+       For multiple interfaces on equal sites, different ports must be given.
+
+ * ondemand:
+       0 = fixed (always transmit packets, even when remote side timed out)
+       1 = on demand (only transmit packets, when remote side is detected)
+       the default is 0
+       NOTE: ID must also be set for on demand.
+
+ * id:
+       optional value to identify frames. This value must be equal on both
+       peers and should be random. If omitted or 0, no ID is transmitted.
+
+ * debug:
+       NOTE: only one debug value must be given for all cards
+       enable debugging (see l1oip.h for debug options)
+
+
+Special mISDN controls:
+
+ op = MISDN_CTRL_SETPEER*
+ p1 = bytes 0-3 : remote IP address in network order (left element first)
+ p2 = bytes 1-2 : remote port in network order (high byte first)
+ optional:
+ p2 = bytes 3-4 : local port in network order (high byte first)
+
+ op = MISDN_CTRL_UNSETPEER*
+
+ * Use l1oipctrl for comfortable setting or removing ip address.
+   (Layer 1 Over IP CTRL)
+
+
+L1oIP-Protocol
+--------------
+
+Frame Header:
+
+ 7 6 5 4 3 2 1 0
++---------------+
+|Ver|T|I|Coding |
++---------------+
+|  ID byte 3 *  |
++---------------+
+|  ID byte 2 *  |
++---------------+
+|  ID byte 1 *  |
++---------------+
+|  ID byte 0 *  |
++---------------+
+|M|   Channel   |
++---------------+
+|    Length *   |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data....     |
+
+...
+
+|               |
++---------------+
+|M|   Channel   |
++---------------+
+|    Length *   |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data....     |
+
+...
+
+
+* Only included in some cases.
+
+- Ver = Version
+If version is missmatch, the frame must be ignored.
+
+- T = Type of interface
+Must be 0 for S0 or 1 for E1.
+
+- I = Id present
+If bit is set, four ID bytes are included in frame.
+
+- ID = Connection ID
+Additional ID to prevent Denial of Service attacs. Also it prevents hijacking
+connections with dynamic IP. The ID should be random and must not be 0.
+
+- Coding = Type of codec
+Must be 0 for no transcoding. Also for D-channel and other HDLC frames.
+ 1 and 2 are reserved for explicitly use of a-LAW or u-LAW codec.
+ 3 is used for generic table compressor.
+
+- M = More channels to come. If this flag is 1, the following byte contains
+the length of the channel data. After the data block, the next channel will
+be defined. The flag for the last channel block (or if only one channel is
+transmitted), must be 0 and no length is given.
+
+- Channel = Channel number
+0 reserved
+1-3 channel data for S0 (3 is D-channel)
+1-31 channel data for E1 (16 is D-channel)
+32-127 channel data for extended E1 (16 is D-channel)
+
+- The length is used if the M-flag is 1. It is used to find the next channel
+inside frame.
+NOTE: A value of 0 equals 256 bytes of data.
+ -> For larger data blocks, a single frame must be used.
+ -> For larger streams, a single frame or multiple blocks with same channel ID
+   must be used.
+
+- Time Base = Timestamp of first sample in frame
+The "Time Base" is used to rearange packets and to detect packet loss.
+The 16 bits are sent in network order (MSB first) and count 1/8000 th of a
+second. This causes a wrap arround each 8,192 seconds. There is no requirement
+for the initial "Time Base", but 0 should be used for the first packet.
+In case of HDLC data, this timestamp counts the packet or byte number.
+
+
+Two Timers:
+
+After initialisation, a timer of 15 seconds is started. Whenever a packet is
+transmitted, the timer is reset to 15 seconds again. If the timer expires, an
+empty packet is transmitted. This keep the connection alive.
+
+When a valid packet is received, a timer 65 seconds is started. The interface
+become ACTIVE. If the timer expires, the interface becomes INACTIVE.
+
+
+Dynamic IP handling:
+
+To allow dynamic IP, the ID must be non 0. In this case, any packet with the
+correct port number and ID will be accepted. If the remote side changes its IP
+the new IP is used for all transmitted packets until it changes again.
+
+
+On Demand:
+
+If the ondemand parameter is given, the remote IP is set to 0 on timeout.
+This will stop keepalive traffic to remote. If the remote is online again,
+traffic will continue to the remote address. This is usefull for road warriors.
+This feature only works with ID set, otherwhise it is highly unsecure.
+
+
+Socket and Thread
+-----------------
+
+The complete socket opening and closing is done by a thread.
+When the thread opened a socket, the hc->socket descriptor is set. Whenever a
+packet shall be sent to the socket, the hc->socket must be checked wheter not
+NULL. To prevent change in socket descriptor, the hc->socket_lock must be used.
+To change the socket, a recall of l1oip_socket_open() will safely kill the
+socket process and create a new one.
+
+*/
+
+#define L1OIP_VERSION  0       /* 0...3 */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+#include <linux/init.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+#include "core.h"
+#include "l1oip.h"
+
+static const char *l1oip_revision = "2.00";
+
+static int l1oip_cnt;
+static spinlock_t l1oip_lock;
+static struct list_head l1oip_ilist;
+
+#define MAX_CARDS      16
+static u_int type[MAX_CARDS];
+static u_int codec[MAX_CARDS];
+static u_int ip[MAX_CARDS*4];
+static u_int port[MAX_CARDS];
+static u_int remoteport[MAX_CARDS];
+static u_int ondemand[MAX_CARDS];
+static u_int limit[MAX_CARDS];
+static u_int id[MAX_CARDS];
+static int debug;
+static int ulaw;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ip, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(remoteport, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ondemand, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(limit, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(id, uint, NULL, S_IRUGO | S_IWUSR);
+module_param(ulaw, uint, S_IRUGO | S_IWUSR);
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+/*
+ * send a frame via socket, if open and restart timer
+ */
+static int
+l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask,
+       u16 timebase, u8 *buf, int len)
+{
+       u8 *p;
+       int multi = 0;
+       u8 frame[len+32];
+       struct socket *socket = NULL;
+       mm_segment_t oldfs;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: sending data to socket (len = %d)\n",
+                       __func__, len);
+
+       p = frame;
+
+       /* restart timer */
+       if ((int)(hc->keep_tl.expires-jiffies) < 5*HZ) {
+               del_timer(&hc->keep_tl);
+               hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+               add_timer(&hc->keep_tl);
+       } else
+               hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: resetting timer\n", __func__);
+
+       /* drop if we have no remote ip or port */
+       if (!hc->sin_remote.sin_addr.s_addr || !hc->sin_remote.sin_port) {
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: dropping frame, because remote "
+                               "IP is not set.\n", __func__);
+               return len;
+       }
+
+       /* assemble frame */
+       *p++ = (L1OIP_VERSION<<6) /* version and coding */
+            | (hc->pri?0x20:0x00) /* type */
+            | (hc->id?0x10:0x00) /* id */
+            | localcodec;
+       if (hc->id) {
+               *p++ = hc->id>>24; /* id */
+               *p++ = hc->id>>16;
+               *p++ = hc->id>>8;
+               *p++ = hc->id;
+       }
+       *p++ = (multi == 1)?0x80:0x00 + channel; /* m-flag, channel */
+       if (multi == 1)
+               *p++ = len; /* length */
+       *p++ = timebase>>8; /* time base */
+       *p++ = timebase;
+
+       if (buf && len) { /* add data to frame */
+               if (localcodec == 1 && ulaw)
+                       l1oip_ulaw_to_alaw(buf, len, p);
+               else if (localcodec == 2 && !ulaw)
+                       l1oip_alaw_to_ulaw(buf, len, p);
+               else if (localcodec == 3)
+                       len = l1oip_law_to_4bit(buf, len, p,
+                               &hc->chan[channel].codecstate);
+               else
+                       memcpy(p, buf, len);
+       }
+       len += p - frame;
+
+       /* check for socket in safe condition */
+       spin_lock(&hc->socket_lock);
+       if (!hc->socket) {
+               spin_unlock(&hc->socket_lock);
+               return 0;
+       }
+       /* seize socket */
+       socket = hc->socket;
+       hc->socket = NULL;
+       spin_unlock(&hc->socket_lock);
+       /* send packet */
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: sending packet to socket (len "
+                       "= %d)\n", __func__, len);
+       hc->sendiov.iov_base = frame;
+       hc->sendiov.iov_len  = len;
+       oldfs = get_fs();
+       set_fs(KERNEL_DS);
+       len = sock_sendmsg(socket, &hc->sendmsg, len);
+       set_fs(oldfs);
+       /* give socket back */
+       hc->socket = socket; /* no locking required */
+
+       return len;
+}
+
+
+/*
+ * receive channel data from socket
+ */
+static void
+l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase,
+       u8 *buf, int len)
+{
+       struct sk_buff *nskb;
+       struct bchannel *bch;
+       struct dchannel *dch;
+       u8 *p;
+       u32 rx_counter;
+
+       if (len == 0) {
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: received empty keepalive data, "
+                               "ignoring\n", __func__);
+               return;
+       }
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: received data, sending to mISDN (%d)\n",
+                       __func__, len);
+
+       if (channel < 1 || channel > 127) {
+               printk(KERN_WARNING "%s: packet error - channel %d out of "
+                       "range\n", __func__, channel);
+               return;
+       }
+       dch = hc->chan[channel].dch;
+       bch = hc->chan[channel].bch;
+       if (!dch && !bch) {
+               printk(KERN_WARNING "%s: packet error - channel %d not in "
+                       "stack\n", __func__, channel);
+               return;
+       }
+
+       /* prepare message */
+       nskb = mI_alloc_skb((remotecodec == 3)?(len<<1):len, GFP_ATOMIC);
+       if (!nskb) {
+               printk(KERN_ERR "%s: No mem for skb.\n", __func__);
+               return;
+       }
+       p = skb_put(nskb, (remotecodec == 3)?(len<<1):len);
+
+       if (remotecodec == 1 && ulaw)
+               l1oip_alaw_to_ulaw(buf, len, p);
+       else if (remotecodec == 2 && !ulaw)
+               l1oip_ulaw_to_alaw(buf, len, p);
+       else if (remotecodec == 3)
+               len = l1oip_4bit_to_law(buf, len, p);
+       else
+               memcpy(p, buf, len);
+
+       /* send message up */
+       if (dch && len >= 2) {
+               dch->rx_skb = nskb;
+               recv_Dchannel(dch);
+       }
+       if (bch) {
+               /* expand 16 bit sequence number to 32 bit sequence number */
+               rx_counter = hc->chan[channel].rx_counter;
+               if (((s16)(timebase - rx_counter)) >= 0) {
+                       /* time has changed forward */
+                       if (timebase >= (rx_counter & 0xffff))
+                               rx_counter =
+                                       (rx_counter & 0xffff0000) | timebase;
+                       else
+                               rx_counter = ((rx_counter & 0xffff0000)+0x10000)
+                                       | timebase;
+               } else {
+                       /* time has changed backwards */
+                       if (timebase < (rx_counter & 0xffff))
+                               rx_counter =
+                                       (rx_counter & 0xffff0000) | timebase;
+                       else
+                               rx_counter = ((rx_counter & 0xffff0000)-0x10000)
+                                       | timebase;
+               }
+               hc->chan[channel].rx_counter = rx_counter;
+
+#ifdef REORDER_DEBUG
+               if (hc->chan[channel].disorder_flag) {
+                       struct sk_buff *skb;
+                       int cnt;
+                       skb = hc->chan[channel].disorder_skb;
+                       hc->chan[channel].disorder_skb = nskb;
+                       nskb = skb;
+                       cnt = hc->chan[channel].disorder_cnt;
+                       hc->chan[channel].disorder_cnt = rx_counter;
+                       rx_counter = cnt;
+               }
+               hc->chan[channel].disorder_flag ^= 1;
+               if (nskb)
+#endif
+               queue_ch_frame(&bch->ch, PH_DATA_IND, rx_counter, nskb);
+       }
+}
+
+
+/*
+ * parse frame and extract channel data
+ */
+static void
+l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
+{
+       u32                     id;
+       u8                      channel;
+       u8                      remotecodec;
+       u16                     timebase;
+       int                     m, mlen;
+       int                     len_start = len; /* initial frame length */
+       struct dchannel         *dch = hc->chan[hc->d_idx].dch;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: received frame, parsing... (%d)\n",
+                       __func__, len);
+
+       /* check lenght */
+       if (len < 1+1+2) {
+               printk(KERN_WARNING "%s: packet error - length %d below "
+                       "4 bytes\n", __func__, len);
+               return;
+       }
+
+       /* check version */
+       if (((*buf)>>6) != L1OIP_VERSION) {
+               printk(KERN_WARNING "%s: packet error - unknown version %d\n",
+                       __func__, buf[0]>>6);
+               return;
+       }
+
+       /* check type */
+       if (((*buf)&0x20) && !hc->pri) {
+               printk(KERN_WARNING "%s: packet error - received E1 packet "
+                       "on S0 interface\n", __func__);
+               return;
+       }
+       if (!((*buf)&0x20) && hc->pri) {
+               printk(KERN_WARNING "%s: packet error - received S0 packet "
+                       "on E1 interface\n", __func__);
+               return;
+       }
+
+       /* get id flag */
+       id = (*buf>>4)&1;
+
+       /* check coding */
+       remotecodec = (*buf) & 0x0f;
+       if (remotecodec > 3) {
+               printk(KERN_WARNING "%s: packet error - remotecodec %d "
+                       "unsupported\n", __func__, remotecodec);
+               return;
+       }
+       buf++;
+       len--;
+
+       /* check id */
+       if (id) {
+               if (!hc->id) {
+                       printk(KERN_WARNING "%s: packet error - packet has id "
+                               "0x%x, but we have not\n", __func__, id);
+                       return;
+               }
+               if (len < 4) {
+                       printk(KERN_WARNING "%s: packet error - packet too "
+                               "short for ID value\n", __func__);
+                       return;
+               }
+               id = (*buf++) << 24;
+               id += (*buf++) << 16;
+               id += (*buf++) << 8;
+               id += (*buf++);
+               len -= 4;
+
+               if (id != hc->id) {
+                       printk(KERN_WARNING "%s: packet error - ID mismatch, "
+                               "got 0x%x, we 0x%x\n",
+                               __func__, id, hc->id);
+                       return;
+               }
+       } else {
+               if (hc->id) {
+                       printk(KERN_WARNING "%s: packet error - packet has no "
+                               "ID, but we have\n", __func__);
+                       return;
+               }
+       }
+
+multiframe:
+       if (len < 1) {
+               printk(KERN_WARNING "%s: packet error - packet too short, "
+                       "channel expected at position %d.\n",
+                       __func__, len-len_start+1);
+               return;
+       }
+
+       /* get channel and multiframe flag */
+       channel = *buf&0x7f;
+       m = *buf >> 7;
+       buf++;
+       len--;
+
+       /* check length on multiframe */
+       if (m) {
+               if (len < 1) {
+                       printk(KERN_WARNING "%s: packet error - packet too "
+                               "short, length expected at position %d.\n",
+                               __func__, len_start-len-1);
+                       return;
+               }
+
+               mlen = *buf++;
+               len--;
+               if (mlen == 0)
+                       mlen = 256;
+               if (len < mlen+3) {
+                       printk(KERN_WARNING "%s: packet error - length %d at "
+                               "position %d exceeds total length %d.\n",
+                               __func__, mlen, len_start-len-1, len_start);
+                       return;
+               }
+               if (len == mlen+3) {
+                       printk(KERN_WARNING "%s: packet error - length %d at "
+                               "position %d will not allow additional "
+                               "packet.\n",
+                               __func__, mlen, len_start-len+1);
+                       return;
+               }
+       } else
+               mlen = len-2; /* single frame, substract timebase */
+
+       if (len < 2) {
+               printk(KERN_WARNING "%s: packet error - packet too short, time "
+                       "base expected at position %d.\n",
+                       __func__, len-len_start+1);
+               return;
+       }
+
+       /* get time base */
+       timebase = (*buf++) << 8;
+       timebase |= (*buf++);
+       len -= 2;
+
+       /* if inactive, we send up a PH_ACTIVATE and activate */
+       if (!test_bit(FLG_ACTIVE, &dch->Flags)) {
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: interface become active due to "
+                               "received packet\n", __func__);
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_ATOMIC);
+       }
+
+       /* distribute packet */
+       l1oip_socket_recv(hc, remotecodec, channel, timebase, buf, mlen);
+       buf += mlen;
+       len -= mlen;
+
+       /* multiframe */
+       if (m)
+               goto multiframe;
+
+       /* restart timer */
+       if ((int)(hc->timeout_tl.expires-jiffies) < 5*HZ || !hc->timeout_on) {
+               hc->timeout_on = 1;
+               del_timer(&hc->timeout_tl);
+               hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+               add_timer(&hc->timeout_tl);
+       } else /* only adjust timer */
+               hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+
+       /* if ip or source port changes */
+       if ((hc->sin_remote.sin_addr.s_addr != sin->sin_addr.s_addr)
+        || (hc->sin_remote.sin_port != sin->sin_port)) {
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: remote address changes from "
+                               "0x%08x to 0x%08x (port %d to %d)\n", __func__,
+                               ntohl(hc->sin_remote.sin_addr.s_addr),
+                               ntohl(sin->sin_addr.s_addr),
+                               ntohs(hc->sin_remote.sin_port),
+                               ntohs(sin->sin_port));
+               hc->sin_remote.sin_addr.s_addr = sin->sin_addr.s_addr;
+               hc->sin_remote.sin_port = sin->sin_port;
+       }
+}
+
+
+/*
+ * socket stuff
+ */
+static int
+l1oip_socket_thread(void *data)
+{
+       struct l1oip *hc = (struct l1oip *)data;
+       int ret = 0;
+       struct msghdr msg;
+       struct iovec iov;
+       mm_segment_t oldfs;
+       struct sockaddr_in sin_rx;
+       unsigned char recvbuf[1500];
+       int recvlen;
+       struct socket *socket = NULL;
+       DECLARE_COMPLETION(wait);
+
+       /* make daemon */
+       allow_signal(SIGTERM);
+
+       /* create socket */
+       if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &socket)) {
+               printk(KERN_ERR "%s: Failed to create socket.\n", __func__);
+               return -EIO;
+       }
+
+       /* set incoming address */
+       hc->sin_local.sin_family = AF_INET;
+       hc->sin_local.sin_addr.s_addr = INADDR_ANY;
+       hc->sin_local.sin_port = htons((unsigned short)hc->localport);
+
+       /* set outgoing address */
+       hc->sin_remote.sin_family = AF_INET;
+       hc->sin_remote.sin_addr.s_addr = htonl(hc->remoteip);
+       hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport);
+
+       /* bind to incomming port */
+       if (socket->ops->bind(socket, (struct sockaddr *)&hc->sin_local,
+           sizeof(hc->sin_local))) {
+               printk(KERN_ERR "%s: Failed to bind socket to port %d.\n",
+                       __func__, hc->localport);
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       /* check sk */
+       if (socket->sk == NULL) {
+               printk(KERN_ERR "%s: socket->sk == NULL\n", __func__);
+               ret = -EIO;
+               goto fail;
+       }
+
+       /* build receive message */
+       msg.msg_name = &sin_rx;
+       msg.msg_namelen = sizeof(sin_rx);
+       msg.msg_control = NULL;
+       msg.msg_controllen = 0;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       /* build send message */
+       hc->sendmsg.msg_name = &hc->sin_remote;
+       hc->sendmsg.msg_namelen = sizeof(hc->sin_remote);
+       hc->sendmsg.msg_control = NULL;
+       hc->sendmsg.msg_controllen = 0;
+       hc->sendmsg.msg_iov    = &hc->sendiov;
+       hc->sendmsg.msg_iovlen = 1;
+
+       /* give away socket */
+       spin_lock(&hc->socket_lock);
+       hc->socket = socket;
+       spin_unlock(&hc->socket_lock);
+
+       /* read loop */
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket created and open\n",
+                       __func__);
+       while (!signal_pending(current)) {
+               iov.iov_base = recvbuf;
+               iov.iov_len = sizeof(recvbuf);
+               oldfs = get_fs();
+               set_fs(KERNEL_DS);
+               recvlen = sock_recvmsg(socket, &msg, sizeof(recvbuf), 0);
+               set_fs(oldfs);
+               if (recvlen > 0) {
+                       l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen);
+               } else {
+                       if (debug & DEBUG_L1OIP_SOCKET)
+                           printk(KERN_WARNING "%s: broken pipe on socket\n",
+                               __func__);
+               }
+       }
+
+       /* get socket back, check first if in use, maybe by send function */
+       spin_lock(&hc->socket_lock);
+       /* if hc->socket is NULL, it is in use until it is given back */
+       while (!hc->socket) {
+               spin_unlock(&hc->socket_lock);
+               schedule_timeout(HZ/10);
+               spin_lock(&hc->socket_lock);
+       }
+       hc->socket = NULL;
+       spin_unlock(&hc->socket_lock);
+
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket thread terminating\n",
+                       __func__);
+
+fail:
+       /* close socket */
+       if (socket)
+               sock_release(socket);
+
+       /* if we got killed, signal completion */
+       complete(&hc->socket_complete);
+       hc->socket_thread = NULL; /* show termination of thread */
+
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket thread terminated\n",
+                       __func__);
+       return ret;
+}
+
+static void
+l1oip_socket_close(struct l1oip *hc)
+{
+       /* kill thread */
+       if (hc->socket_thread) {
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: socket thread exists, "
+                               "killing...\n", __func__);
+               send_sig(SIGTERM, hc->socket_thread, 0);
+               wait_for_completion(&hc->socket_complete);
+       }
+}
+
+static int
+l1oip_socket_open(struct l1oip *hc)
+{
+       /* in case of reopen, we need to close first */
+       l1oip_socket_close(hc);
+
+       init_completion(&hc->socket_complete);
+
+       /* create receive process */
+       hc->socket_thread = kthread_run(l1oip_socket_thread, hc, "l1oip_%s",
+               hc->name);
+       if (IS_ERR(hc->socket_thread)) {
+               int err = PTR_ERR(hc->socket_thread);
+               printk(KERN_ERR "%s: Failed (%d) to create socket process.\n",
+                       __func__, err);
+               hc->socket_thread = NULL;
+               sock_release(hc->socket);
+               return err;
+       }
+       if (debug & DEBUG_L1OIP_SOCKET)
+               printk(KERN_DEBUG "%s: socket thread created\n", __func__);
+
+       return 0;
+}
+
+
+static void
+l1oip_send_bh(struct work_struct *work)
+{
+       struct l1oip *hc = container_of(work, struct l1oip, workq);
+
+       if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+               printk(KERN_DEBUG "%s: keepalive timer expired, sending empty "
+                       "frame on dchannel\n", __func__);
+
+       /* send an empty l1oip frame at D-channel */
+       l1oip_socket_send(hc, 0, hc->d_idx, 0, 0, NULL, 0);
+}
+
+
+/*
+ * timer stuff
+ */
+static void
+l1oip_keepalive(void *data)
+{
+       struct l1oip *hc = (struct l1oip *)data;
+
+       schedule_work(&hc->workq);
+}
+
+static void
+l1oip_timeout(void *data)
+{
+       struct l1oip                    *hc = (struct l1oip *)data;
+       struct dchannel         *dch = hc->chan[hc->d_idx].dch;
+
+       if (debug & DEBUG_L1OIP_MSG)
+               printk(KERN_DEBUG "%s: timeout timer expired, turn layer one "
+                       "down.\n", __func__);
+
+       hc->timeout_on = 0; /* state that timer must be initialized next time */
+
+       /* if timeout, we send up a PH_DEACTIVATE and deactivate */
+       if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: interface become deactivated "
+                               "due to timeout\n", __func__);
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_ATOMIC);
+       }
+
+       /* if we have ondemand set, we remove ip address */
+       if (hc->ondemand) {
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: on demand causes ip address to "
+                               "be removed\n", __func__);
+               hc->sin_remote.sin_addr.s_addr = 0;
+       }
+}
+
+
+/*
+ * message handling
+ */
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct l1oip                    *hc = dch->hw;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+       int                     l, ll;
+       unsigned char           *p;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (skb->len < 1) {
+                       printk(KERN_WARNING "%s: skb too small\n",
+                               __func__);
+                       break;
+               }
+               if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+                       printk(KERN_WARNING "%s: skb too large\n",
+                               __func__);
+                       break;
+               }
+               /* send frame */
+               p = skb->data;
+               l = skb->len;
+               while (l) {
+                       ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+                       l1oip_socket_send(hc, 0, dch->slot, 0,
+                               hc->chan[dch->slot].tx_counter++, p, ll);
+                       p += ll;
+                       l -= ll;
+               }
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+               return 0;
+       case PH_ACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+                               , __func__, dch->slot, hc->b_num+1);
+               skb_trim(skb, 0);
+               if (test_bit(FLG_ACTIVE, &dch->Flags))
+                       queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+               else
+                       queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+               return 0;
+       case PH_DEACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+                               "(1..%d)\n", __func__, dch->slot,
+                               hc->b_num+1);
+               skb_trim(skb, 0);
+               if (test_bit(FLG_ACTIVE, &dch->Flags))
+                       queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+               else
+                       queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+               return 0;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+       struct l1oip    *hc = dch->hw;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER;
+               break;
+       case MISDN_CTRL_SETPEER:
+               hc->remoteip = (u32)cq->p1;
+               hc->remoteport = cq->p2 & 0xffff;
+               hc->localport = cq->p2 >> 16;
+               if (!hc->remoteport)
+                       hc->remoteport = hc->localport;
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: got new ip address from user "
+                               "space.\n", __func__);
+                       l1oip_socket_open(hc);
+               break;
+       case MISDN_CTRL_UNSETPEER:
+               if (debug & DEBUG_L1OIP_SOCKET)
+                       printk(KERN_DEBUG "%s: removing ip address.\n",
+                               __func__);
+               hc->remoteip = 0;
+               l1oip_socket_open(hc);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+       if (debug & DEBUG_HW_OPEN)
+               printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+                   dch->dev.id, __builtin_return_address(0));
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+           (dch->dev.D.protocol != rq->protocol)) {
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_WARNING "%s: change protocol %x to %x\n",
+                       __func__, dch->dev.D.protocol, rq->protocol);
+       }
+       if (dch->dev.D.protocol != rq->protocol)
+               dch->dev.D.protocol = rq->protocol;
+
+       if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+               _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       }
+       rq->ch = &dch->dev.D;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+       struct bchannel *bch;
+       int             ch;
+
+       if (!test_bit(rq->adr.channel & 0x1f,
+               &dch->dev.channelmap[rq->adr.channel >> 5]))
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       ch = rq->adr.channel; /* BRI: 1=B1 2=B2  PRI: 1..15,17.. */
+       bch = hc->chan[ch].bch;
+       if (!bch) {
+               printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+                   __func__, ch);
+               return -EINVAL;
+       }
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       if (!try_module_get(THIS_MODULE))
+               printk(KERN_WARNING "%s:cannot get module\n", __func__);
+       return 0;
+}
+
+static int
+l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct l1oip                    *hc = dch->hw;
+       struct channel_req      *rq;
+       int                     err = 0;
+
+       if (dch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               switch (rq->protocol) {
+               case ISDN_P_TE_S0:
+               case ISDN_P_NT_S0:
+                       if (hc->pri) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq);
+                       break;
+               case ISDN_P_TE_E1:
+               case ISDN_P_NT_E1:
+                       if (!hc->pri) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = open_dchannel(hc, dch, rq);
+                       break;
+               default:
+                       err = open_bchannel(hc, dch, rq);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               if (debug & DEBUG_HW_OPEN)
+                       printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+                           __func__, dch->dev.id,
+                           __builtin_return_address(0));
+               module_put(THIS_MODULE);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_dctrl(dch, arg);
+               break;
+       default:
+               if (dch->debug & DEBUG_HW)
+                       printk(KERN_DEBUG "%s: unknown command %x\n",
+                           __func__, cmd);
+               err = -EINVAL;
+       }
+       return err;
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel         *bch = container_of(ch, struct bchannel, ch);
+       struct l1oip                    *hc = bch->hw;
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       int                     l, ll, i;
+       unsigned char           *p;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               if (skb->len <= 0) {
+                       printk(KERN_WARNING "%s: skb too small\n",
+                               __func__);
+                       break;
+               }
+               if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+                       printk(KERN_WARNING "%s: skb too large\n",
+                               __func__);
+                       break;
+               }
+               /* check for AIS / ulaw-silence */
+               p = skb->data;
+               l = skb->len;
+               for (i = 0; i < l; i++) {
+                       if (*p++ != 0xff)
+                               break;
+               }
+               if (i == l) {
+                       if (debug & DEBUG_L1OIP_MSG)
+                               printk(KERN_DEBUG "%s: got AIS, not sending, "
+                                       "but counting\n", __func__);
+                       hc->chan[bch->slot].tx_counter += l;
+                       skb_trim(skb, 0);
+                       queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+                       return 0;
+               }
+               /* check for silence */
+               p = skb->data;
+               l = skb->len;
+               for (i = 0; i < l; i++) {
+                       if (*p++ != 0x2a)
+                               break;
+               }
+               if (i == l) {
+                       if (debug & DEBUG_L1OIP_MSG)
+                               printk(KERN_DEBUG "%s: got silence, not sending"
+                                       ", but counting\n", __func__);
+                       hc->chan[bch->slot].tx_counter += l;
+                       skb_trim(skb, 0);
+                       queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+                       return 0;
+               }
+
+               /* send frame */
+               p = skb->data;
+               l = skb->len;
+               while (l) {
+                       ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+                       l1oip_socket_send(hc, hc->codec, bch->slot, 0,
+                               hc->chan[bch->slot].tx_counter, p, ll);
+                       hc->chan[bch->slot].tx_counter += ll;
+                       p += ll;
+                       l -= ll;
+               }
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+               return 0;
+       case PH_ACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+                               , __func__, bch->slot, hc->b_num+1);
+               hc->chan[bch->slot].codecstate = 0;
+               test_and_set_bit(FLG_ACTIVE, &bch->Flags);
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+               return 0;
+       case PH_DEACTIVATE_REQ:
+               if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+                       printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+                               "(1..%d)\n", __func__, bch->slot,
+                               hc->b_num+1);
+               test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+               skb_trim(skb, 0);
+               queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+               return 0;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int                     ret = 0;
+       struct dsp_features     *features =
+               (struct dsp_features *)(*((u_long *)&cq->p1));
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_HW_FEATURES_OP;
+               break;
+       case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+               if (debug & DEBUG_L1OIP_MSG)
+                       printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+                           __func__);
+               /* create confirm */
+               features->unclocked = 1;
+               features->unordered = 1;
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown Op %x\n",
+                   __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+l1oip_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       int             err = -EINVAL;
+
+       if (bch->debug & DEBUG_HW)
+               printk(KERN_DEBUG "%s: cmd:%x %p\n",
+                   __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(THIS_MODULE);
+               err = 0;
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_bctrl(bch, arg);
+               break;
+       default:
+               printk(KERN_WARNING "%s: unknown prim(%x)\n",
+                       __func__, cmd);
+       }
+       return err;
+}
+
+
+/*
+ * cleanup module and stack
+ */
+static void
+release_card(struct l1oip *hc)
+{
+       int     ch;
+
+       if (timer_pending(&hc->keep_tl))
+               del_timer(&hc->keep_tl);
+
+       if (timer_pending(&hc->timeout_tl))
+               del_timer(&hc->timeout_tl);
+
+       if (hc->socket_thread)
+               l1oip_socket_close(hc);
+
+       if (hc->registered && hc->chan[hc->d_idx].dch)
+               mISDN_unregister_device(&hc->chan[hc->d_idx].dch->dev);
+       for (ch = 0; ch < 128; ch++) {
+               if (hc->chan[ch].dch) {
+                       mISDN_freedchannel(hc->chan[ch].dch);
+                       kfree(hc->chan[ch].dch);
+               }
+               if (hc->chan[ch].bch) {
+                       mISDN_freebchannel(hc->chan[ch].bch);
+                       kfree(hc->chan[ch].bch);
+#ifdef REORDER_DEBUG
+                       if (hc->chan[ch].disorder_skb)
+                               dev_kfree_skb(hc->chan[ch].disorder_skb);
+#endif
+               }
+       }
+
+       spin_lock(&l1oip_lock);
+       list_del(&hc->list);
+       spin_unlock(&l1oip_lock);
+
+       kfree(hc);
+}
+
+static void
+l1oip_cleanup(void)
+{
+       struct l1oip *hc, *next;
+
+       list_for_each_entry_safe(hc, next, &l1oip_ilist, list)
+               release_card(hc);
+
+       l1oip_4bit_free();
+}
+
+
+/*
+ * module and stack init
+ */
+static int
+init_card(struct l1oip *hc, int pri, int bundle)
+{
+       struct dchannel *dch;
+       struct bchannel *bch;
+       int             ret;
+       int             i, ch;
+
+       spin_lock_init(&hc->socket_lock);
+       hc->idx = l1oip_cnt;
+       hc->pri = pri;
+       hc->d_idx = pri?16:3;
+       hc->b_num = pri?30:2;
+       hc->bundle = bundle;
+       if (hc->pri)
+               sprintf(hc->name, "l1oip-e1.%d", l1oip_cnt + 1);
+       else
+               sprintf(hc->name, "l1oip-s0.%d", l1oip_cnt + 1);
+
+       switch (codec[l1oip_cnt]) {
+       case 0: /* as is */
+       case 1: /* alaw */
+       case 2: /* ulaw */
+       case 3: /* 4bit */
+               break;
+       default:
+               printk(KERN_ERR "Codec(%d) not supported.\n",
+                       codec[l1oip_cnt]);
+               return -EINVAL;
+       }
+       hc->codec = codec[l1oip_cnt];
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: using codec %d\n",
+                       __func__, hc->codec);
+
+       if (id[l1oip_cnt] == 0) {
+               printk(KERN_WARNING "Warning: No 'id' value given or "
+                       "0, this is highly unsecure. Please use 32 "
+                       "bit randmom number 0x...\n");
+       }
+       hc->id = id[l1oip_cnt];
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: using id 0x%x\n", __func__, hc->id);
+
+       hc->ondemand = ondemand[l1oip_cnt];
+       if (hc->ondemand && !hc->id) {
+               printk(KERN_ERR "%s: ondemand option only allowed in "
+                       "conjunction with non 0 ID\n", __func__);
+               return -EINVAL;
+       }
+
+       if (limit[l1oip_cnt])
+               hc->b_num = limit[l1oip_cnt];
+       if (!pri && hc->b_num > 2) {
+               printk(KERN_ERR "Maximum limit for BRI interface is 2 "
+                       "channels.\n");
+               return -EINVAL;
+       }
+       if (pri && hc->b_num > 126) {
+               printk(KERN_ERR "Maximum limit for PRI interface is 126 "
+                       "channels.\n");
+               return -EINVAL;
+       }
+       if (pri && hc->b_num > 30) {
+               printk(KERN_WARNING "Maximum limit for BRI interface is 30 "
+                       "channels.\n");
+               printk(KERN_WARNING "Your selection of %d channels must be "
+                       "supported by application.\n", hc->limit);
+       }
+
+       hc->remoteip = ip[l1oip_cnt<<2] << 24
+                    | ip[(l1oip_cnt<<2)+1] << 16
+                    | ip[(l1oip_cnt<<2)+2] << 8
+                    | ip[(l1oip_cnt<<2)+3];
+       hc->localport = port[l1oip_cnt]?:(L1OIP_DEFAULTPORT+l1oip_cnt);
+       if (remoteport[l1oip_cnt])
+               hc->remoteport = remoteport[l1oip_cnt];
+       else
+               hc->remoteport = hc->localport;
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: using local port %d remote ip "
+                       "%d.%d.%d.%d port %d ondemand %d\n", __func__,
+                       hc->localport, hc->remoteip >> 24,
+                       (hc->remoteip >> 16) & 0xff,
+                       (hc->remoteip >> 8) & 0xff, hc->remoteip & 0xff,
+                       hc->remoteport, hc->ondemand);
+
+       dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+       if (!dch)
+               return -ENOMEM;
+       dch->debug = debug;
+       mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL);
+       dch->hw = hc;
+       if (pri)
+               dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+       else
+               dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+       dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+           (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       dch->dev.D.send = handle_dmsg;
+       dch->dev.D.ctrl = l1oip_dctrl;
+       dch->dev.nrbchan = hc->b_num;
+       dch->slot = hc->d_idx;
+       hc->chan[hc->d_idx].dch = dch;
+       i = 1;
+       for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+               if (ch == 15)
+                       i++;
+               bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+               if (!bch) {
+                       printk(KERN_ERR "%s: no memory for bchannel\n",
+                           __func__);
+                       return -ENOMEM;
+               }
+               bch->nr = i + ch;
+               bch->slot = i + ch;
+               bch->debug = debug;
+               mISDN_initbchannel(bch, MAX_DATA_MEM);
+               bch->hw = hc;
+               bch->ch.send = handle_bmsg;
+               bch->ch.ctrl = l1oip_bctrl;
+               bch->ch.nr = i + ch;
+               list_add(&bch->ch.list, &dch->dev.bchannels);
+               hc->chan[i + ch].bch = bch;
+               test_and_set_bit(bch->nr & 0x1f,
+                       &dch->dev.channelmap[bch->nr >> 5]);
+       }
+       ret = mISDN_register_device(&dch->dev, hc->name);
+       if (ret)
+               return ret;
+       hc->registered = 1;
+
+       if (debug & DEBUG_L1OIP_INIT)
+               printk(KERN_DEBUG "%s: Setting up network card(%d)\n",
+                       __func__, l1oip_cnt + 1);
+       ret = l1oip_socket_open(hc);
+       if (ret)
+               return ret;
+
+       hc->keep_tl.function = (void *)l1oip_keepalive;
+       hc->keep_tl.data = (ulong)hc;
+       init_timer(&hc->keep_tl);
+       hc->keep_tl.expires = jiffies + 2*HZ; /* two seconds first time */
+       add_timer(&hc->keep_tl);
+
+       hc->timeout_tl.function = (void *)l1oip_timeout;
+       hc->timeout_tl.data = (ulong)hc;
+       init_timer(&hc->timeout_tl);
+       hc->timeout_on = 0; /* state that we have timer off */
+
+       return 0;
+}
+
+static int __init
+l1oip_init(void)
+{
+       int             pri, bundle;
+       struct l1oip            *hc;
+       int             ret;
+
+       printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n",
+               l1oip_revision);
+
+       INIT_LIST_HEAD(&l1oip_ilist);
+       spin_lock_init(&l1oip_lock);
+
+       if (l1oip_4bit_alloc(ulaw))
+               return -ENOMEM;
+
+       l1oip_cnt = 0;
+       while (type[l1oip_cnt] && l1oip_cnt < MAX_CARDS) {
+               switch (type[l1oip_cnt] & 0xff) {
+               case 1:
+                       pri = 0;
+                       bundle = 0;
+                       break;
+               case 2:
+                       pri = 1;
+                       bundle = 0;
+                       break;
+               case 3:
+                       pri = 0;
+                       bundle = 1;
+                       break;
+               case 4:
+                       pri = 1;
+                       bundle = 1;
+                       break;
+               default:
+                       printk(KERN_ERR "Card type(%d) not supported.\n",
+                               type[l1oip_cnt] & 0xff);
+                       l1oip_cleanup();
+                       return -EINVAL;
+               }
+
+               if (debug & DEBUG_L1OIP_INIT)
+                       printk(KERN_DEBUG "%s: interface %d is %s with %s.\n",
+                               __func__, l1oip_cnt, pri?"PRI":"BRI",
+                               bundle?"bundled IP packet for all B-channels"
+                                :"seperate IP packets for every B-channel");
+
+               hc = kzalloc(sizeof(struct l1oip), GFP_ATOMIC);
+               if (!hc) {
+                       printk(KERN_ERR "No kmem for L1-over-IP driver.\n");
+                       l1oip_cleanup();
+                       return -ENOMEM;
+               }
+               INIT_WORK(&hc->workq, (void *)l1oip_send_bh);
+
+               spin_lock(&l1oip_lock);
+               list_add_tail(&hc->list, &l1oip_ilist);
+               spin_unlock(&l1oip_lock);
+
+               ret = init_card(hc, pri, bundle);
+               if (ret) {
+                       l1oip_cleanup();
+                       return ret;
+               }
+
+               l1oip_cnt++;
+       }
+       printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt);
+       return 0;
+}
+
+module_init(l1oip_init);
+module_exit(l1oip_cleanup);
+
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c
new file mode 100644 (file)
index 0000000..fced1a2
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+#include "layer1.h"
+#include "fsm.h"
+
+static int *debug;
+
+struct layer1 {
+       u_long                  Flags;
+       struct FsmInst          l1m;
+       struct FsmTimer         timer;
+       int                     delay;
+       struct dchannel         *dch;
+       dchannel_l1callback     *dcb;
+};
+
+#define TIMER3_VALUE 7000
+
+static
+struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL};
+
+enum {
+       ST_L1_F2,
+       ST_L1_F3,
+       ST_L1_F4,
+       ST_L1_F5,
+       ST_L1_F6,
+       ST_L1_F7,
+       ST_L1_F8,
+};
+
+#define L1S_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1SState[] =
+{
+       "ST_L1_F2",
+       "ST_L1_F3",
+       "ST_L1_F4",
+       "ST_L1_F5",
+       "ST_L1_F6",
+       "ST_L1_F7",
+       "ST_L1_F8",
+};
+
+enum {
+       EV_PH_ACTIVATE,
+       EV_PH_DEACTIVATE,
+       EV_RESET_IND,
+       EV_DEACT_CNF,
+       EV_DEACT_IND,
+       EV_POWER_UP,
+       EV_ANYSIG_IND,
+       EV_INFO2_IND,
+       EV_INFO4_IND,
+       EV_TIMER_DEACT,
+       EV_TIMER_ACT,
+       EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+       "EV_PH_ACTIVATE",
+       "EV_PH_DEACTIVATE",
+       "EV_RESET_IND",
+       "EV_DEACT_CNF",
+       "EV_DEACT_IND",
+       "EV_POWER_UP",
+       "EV_ANYSIG_IND",
+       "EV_INFO2_IND",
+       "EV_INFO4_IND",
+       "EV_TIMER_DEACT",
+       "EV_TIMER_ACT",
+       "EV_TIMER3",
+};
+
+static void
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct layer1 *l1 = fi->userdata;
+       va_list va;
+
+       va_start(va, fmt);
+       printk(KERN_DEBUG "%s: ", l1->dch->dev.name);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F3);
+       if (test_bit(FLG_L1_ACTIVATING, &l1->Flags))
+               l1->dcb(l1->dch, HW_POWERUP_REQ);
+}
+
+static void
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F3);
+       mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2);
+       test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags);
+}
+
+static void
+l1_power_up_s(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
+               mISDN_FsmChangeState(fi, ST_L1_F4);
+               l1->dcb(l1->dch, INFO3_P8);
+       } else
+               mISDN_FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_F5);
+}
+
+static void
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F6);
+       l1->dcb(l1->dch, INFO3_P8);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L1_F7);
+       l1->dcb(l1->dch, INFO3_P8);
+       if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags))
+               mISDN_FsmDelTimer(&l1->timer, 4);
+       if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) {
+               if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags))
+                       mISDN_FsmDelTimer(&l1->timer, 3);
+               mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2);
+               test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags);
+       }
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags);
+       if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
+               if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+                       l1->dcb(l1->dch, HW_D_NOBLOCKED);
+               l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+       }
+       if (l1->l1m.state != ST_L1_F6) {
+               mISDN_FsmChangeState(fi, ST_L1_F3);
+               l1->dcb(l1->dch, HW_POWERUP_REQ);
+       }
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags);
+       test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags);
+       l1->dcb(l1->dch, PH_ACTIVATE_IND);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags);
+       test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags);
+       if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+               l1->dcb(l1->dch, HW_D_NOBLOCKED);
+       l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+       l1->dcb(l1->dch, HW_DEACT_REQ);
+}
+
+static void
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+       test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
+       l1->dcb(l1->dch, HW_RESET_REQ);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer1 *l1 = fi->userdata;
+
+       if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) &&
+           (!test_bit(FLG_L1_T3RUN, &l1->Flags))) {
+               test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags);
+               if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
+                       l1->dcb(l1->dch, HW_D_NOBLOCKED);
+               l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+       }
+}
+
+static struct FsmNode L1SFnList[] =
+{
+       {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
+       {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
+       {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
+       {ST_L1_F3, EV_RESET_IND, l1_reset},
+       {ST_L1_F4, EV_RESET_IND, l1_reset},
+       {ST_L1_F5, EV_RESET_IND, l1_reset},
+       {ST_L1_F6, EV_RESET_IND, l1_reset},
+       {ST_L1_F7, EV_RESET_IND, l1_reset},
+       {ST_L1_F8, EV_RESET_IND, l1_reset},
+       {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
+       {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
+       {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
+       {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
+       {ST_L1_F3, EV_POWER_UP,  l1_power_up_s},
+       {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5},
+       {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8},
+       {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8},
+       {ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
+       {ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
+       {ST_L1_F3, EV_TIMER3, l1_timer3},
+       {ST_L1_F4, EV_TIMER3, l1_timer3},
+       {ST_L1_F5, EV_TIMER3, l1_timer3},
+       {ST_L1_F6, EV_TIMER3, l1_timer3},
+       {ST_L1_F8, EV_TIMER3, l1_timer3},
+       {ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
+       {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
+       {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
+};
+
+static void
+release_l1(struct layer1 *l1) {
+       mISDN_FsmDelTimer(&l1->timer, 0);
+       if (l1->dch)
+               l1->dch->l1 = NULL;
+       module_put(THIS_MODULE);
+       kfree(l1);
+}
+
+int
+l1_event(struct layer1 *l1, u_int event)
+{
+       int             err = 0;
+
+       if (!l1)
+               return -EINVAL;
+       switch (event) {
+       case HW_RESET_IND:
+               mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
+               break;
+       case HW_DEACT_IND:
+               mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL);
+               break;
+       case HW_POWERUP_IND:
+               mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL);
+               break;
+       case HW_DEACT_CNF:
+               mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
+               break;
+       case ANYSIGNAL:
+               mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+               break;
+       case LOSTFRAMING:
+               mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+               break;
+       case INFO2:
+               mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL);
+               break;
+       case INFO4_P8:
+               mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+               break;
+       case INFO4_P10:
+               mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+               break;
+       case PH_ACTIVATE_REQ:
+               if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
+                       l1->dcb(l1->dch, PH_ACTIVATE_IND);
+               else {
+                       test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags);
+                       mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               release_l1(l1);
+               break;
+       default:
+               if (*debug & DEBUG_L1)
+                       printk(KERN_DEBUG "%s %x unhandled\n",
+                           __func__, event);
+               err = -EINVAL;
+       }
+       return err;
+}
+EXPORT_SYMBOL(l1_event);
+
+int
+create_l1(struct dchannel *dch, dchannel_l1callback *dcb) {
+       struct layer1   *nl1;
+
+       nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC);
+       if (!nl1) {
+               printk(KERN_ERR "kmalloc struct layer1 failed\n");
+               return -ENOMEM;
+       }
+       nl1->l1m.fsm = &l1fsm_s;
+       nl1->l1m.state = ST_L1_F3;
+       nl1->Flags = 0;
+       nl1->l1m.debug = *debug & DEBUG_L1_FSM;
+       nl1->l1m.userdata = nl1;
+       nl1->l1m.userint = 0;
+       nl1->l1m.printdebug = l1m_debug;
+       nl1->dch = dch;
+       nl1->dcb = dcb;
+       mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer);
+       __module_get(THIS_MODULE);
+       dch->l1 = nl1;
+       return 0;
+}
+EXPORT_SYMBOL(create_l1);
+
+int
+l1_init(u_int *deb)
+{
+       debug = deb;
+       l1fsm_s.state_count = L1S_STATE_COUNT;
+       l1fsm_s.event_count = L1_EVENT_COUNT;
+       l1fsm_s.strEvent = strL1Event;
+       l1fsm_s.strState = strL1SState;
+       mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
+       return 0;
+}
+
+void
+l1_cleanup(void)
+{
+       mISDN_FsmFree(&l1fsm_s);
+}
diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h
new file mode 100644 (file)
index 0000000..9c8125f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ * Layer 1 defines
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define FLG_L1_ACTIVATING      1
+#define FLG_L1_ACTIVATED       2
+#define FLG_L1_DEACTTIMER      3
+#define FLG_L1_ACTTIMER                4
+#define FLG_L1_T3RUN           5
+#define FLG_L1_PULL_REQ                6
+#define FLG_L1_UINT            7
+#define FLG_L1_DBLOCKED                8
+
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
new file mode 100644 (file)
index 0000000..f5ad888
--- /dev/null
@@ -0,0 +1,2216 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "fsm.h"
+#include "layer2.h"
+
+static int *debug;
+
+static
+struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL};
+
+static char *strL2State[] =
+{
+       "ST_L2_1",
+       "ST_L2_2",
+       "ST_L2_3",
+       "ST_L2_4",
+       "ST_L2_5",
+       "ST_L2_6",
+       "ST_L2_7",
+       "ST_L2_8",
+};
+
+enum {
+       EV_L2_UI,
+       EV_L2_SABME,
+       EV_L2_DISC,
+       EV_L2_DM,
+       EV_L2_UA,
+       EV_L2_FRMR,
+       EV_L2_SUPER,
+       EV_L2_I,
+       EV_L2_DL_DATA,
+       EV_L2_ACK_PULL,
+       EV_L2_DL_UNITDATA,
+       EV_L2_DL_ESTABLISH_REQ,
+       EV_L2_DL_RELEASE_REQ,
+       EV_L2_MDL_ASSIGN,
+       EV_L2_MDL_REMOVE,
+       EV_L2_MDL_ERROR,
+       EV_L1_DEACTIVATE,
+       EV_L2_T200,
+       EV_L2_T203,
+       EV_L2_SET_OWN_BUSY,
+       EV_L2_CLEAR_OWN_BUSY,
+       EV_L2_FRAME_ERROR,
+};
+
+#define L2_EVENT_COUNT (EV_L2_FRAME_ERROR+1)
+
+static char *strL2Event[] =
+{
+       "EV_L2_UI",
+       "EV_L2_SABME",
+       "EV_L2_DISC",
+       "EV_L2_DM",
+       "EV_L2_UA",
+       "EV_L2_FRMR",
+       "EV_L2_SUPER",
+       "EV_L2_I",
+       "EV_L2_DL_DATA",
+       "EV_L2_ACK_PULL",
+       "EV_L2_DL_UNITDATA",
+       "EV_L2_DL_ESTABLISH_REQ",
+       "EV_L2_DL_RELEASE_REQ",
+       "EV_L2_MDL_ASSIGN",
+       "EV_L2_MDL_REMOVE",
+       "EV_L2_MDL_ERROR",
+       "EV_L1_DEACTIVATE",
+       "EV_L2_T200",
+       "EV_L2_T203",
+       "EV_L2_SET_OWN_BUSY",
+       "EV_L2_CLEAR_OWN_BUSY",
+       "EV_L2_FRAME_ERROR",
+};
+
+static void
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct layer2 *l2 = fi->userdata;
+       va_list va;
+
+       if (!(*debug & DEBUG_L2_FSM))
+               return;
+       va_start(va, fmt);
+       printk(KERN_DEBUG "l2 (tei %d): ", l2->tei);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+inline u_int
+l2headersize(struct layer2 *l2, int ui)
+{
+       return ((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+               (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
+}
+
+inline u_int
+l2addrsize(struct layer2 *l2)
+{
+       return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1;
+}
+
+static u_int
+l2_newid(struct layer2 *l2)
+{
+       u_int   id;
+
+       id = l2->next_id++;
+       if (id == 0x7fff)
+               l2->next_id = 1;
+       id <<= 16;
+       id |= l2->tei << 8;
+       id |= l2->sapi;
+       return id;
+}
+
+static void
+l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb)
+{
+       int     err;
+
+       if (!l2->up)
+               return;
+       mISDN_HEAD_PRIM(skb) = prim;
+       mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr;
+       err = l2->up->send(l2->up, skb);
+       if (err) {
+               printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+               dev_kfree_skb(skb);
+       }
+}
+
+static void
+l2up_create(struct layer2 *l2, u_int prim, int len, void *arg)
+{
+       struct sk_buff  *skb;
+       struct mISDNhead *hh;
+       int             err;
+
+       if (!l2->up)
+               return;
+       skb = mI_alloc_skb(len, GFP_ATOMIC);
+       if (!skb)
+               return;
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = (l2->ch.nr << 16) | l2->ch.addr;
+       if (len)
+               memcpy(skb_put(skb, len), arg, len);
+       err = l2->up->send(l2->up, skb);
+       if (err) {
+               printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+               dev_kfree_skb(skb);
+       }
+}
+
+static int
+l2down_skb(struct layer2 *l2, struct sk_buff *skb) {
+       int ret;
+
+       ret = l2->ch.recv(l2->ch.peer, skb);
+       if (ret && (*debug & DEBUG_L2_RECV))
+               printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret);
+       return ret;
+}
+
+static int
+l2down_raw(struct layer2 *l2, struct sk_buff *skb)
+{
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+       if (hh->prim == PH_DATA_REQ) {
+               if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+                       skb_queue_tail(&l2->down_queue, skb);
+                       return 0;
+               }
+               l2->down_id = mISDN_HEAD_ID(skb);
+       }
+       return l2down_skb(l2, skb);
+}
+
+static int
+l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb)
+{
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+       hh->prim = prim;
+       hh->id = id;
+       return l2down_raw(l2, skb);
+}
+
+static int
+l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg)
+{
+       struct sk_buff  *skb;
+       int             err;
+       struct mISDNhead *hh;
+
+       skb = mI_alloc_skb(len, GFP_ATOMIC);
+       if (!skb)
+               return -ENOMEM;
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = id;
+       if (len)
+               memcpy(skb_put(skb, len), arg, len);
+       err = l2down_raw(l2, skb);
+       if (err)
+               dev_kfree_skb(skb);
+       return err;
+}
+
+static int
+ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) {
+       struct sk_buff *nskb = skb;
+       int ret = -EAGAIN;
+
+       if (test_bit(FLG_L1_NOTREADY, &l2->flag)) {
+               if (hh->id == l2->down_id) {
+                       nskb = skb_dequeue(&l2->down_queue);
+                       if (nskb) {
+                               l2->down_id = mISDN_HEAD_ID(nskb);
+                               if (l2down_skb(l2, nskb)) {
+                                       dev_kfree_skb(nskb);
+                                       l2->down_id = MISDN_ID_NONE;
+                               }
+                       } else
+                               l2->down_id = MISDN_ID_NONE;
+                       if (ret) {
+                               dev_kfree_skb(skb);
+                               ret = 0;
+                       }
+                       if (l2->down_id == MISDN_ID_NONE) {
+                               test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+                               mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+                       }
+               }
+       }
+       if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+               nskb = skb_dequeue(&l2->down_queue);
+               if (nskb) {
+                       l2->down_id = mISDN_HEAD_ID(nskb);
+                       if (l2down_skb(l2, nskb)) {
+                               dev_kfree_skb(nskb);
+                               l2->down_id = MISDN_ID_NONE;
+                               test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+                       }
+               } else
+                       test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+       }
+       return ret;
+}
+
+static int
+l2mgr(struct layer2 *l2, u_int prim, void *arg) {
+       long c = (long)arg;
+
+       printk(KERN_WARNING
+           "l2mgr: addr:%x prim %x %c\n", l2->id, prim, (char)c);
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               !test_bit(FLG_FIXED_TEI, &l2->flag)) {
+               switch (c) {
+               case 'C':
+               case 'D':
+               case 'G':
+               case 'H':
+                       l2_tei(l2, prim, (u_long)arg);
+                       break;
+               }
+       }
+       return 0;
+}
+
+static void
+set_peer_busy(struct layer2 *l2) {
+       test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+       if (skb_queue_len(&l2->i_queue) || skb_queue_len(&l2->ui_queue))
+               test_and_set_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+clear_peer_busy(struct layer2 *l2) {
+       if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
+               test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+InitWin(struct layer2 *l2)
+{
+       int i;
+
+       for (i = 0; i < MAX_WINDOW; i++)
+               l2->windowar[i] = NULL;
+}
+
+static int
+freewin(struct layer2 *l2)
+{
+       int i, cnt = 0;
+
+       for (i = 0; i < MAX_WINDOW; i++) {
+               if (l2->windowar[i]) {
+                       cnt++;
+                       dev_kfree_skb(l2->windowar[i]);
+                       l2->windowar[i] = NULL;
+               }
+       }
+       return cnt;
+}
+
+static void
+ReleaseWin(struct layer2 *l2)
+{
+       int cnt = freewin(l2);
+
+       if (cnt)
+               printk(KERN_WARNING
+                   "isdnl2 freed %d skbuffs in release\n", cnt);
+}
+
+inline unsigned int
+cansend(struct layer2 *l2)
+{
+       unsigned int p1;
+
+       if (test_bit(FLG_MOD128, &l2->flag))
+               p1 = (l2->vs - l2->va) % 128;
+       else
+               p1 = (l2->vs - l2->va) % 8;
+       return (p1 < l2->window) && !test_bit(FLG_PEER_BUSY, &l2->flag);
+}
+
+inline void
+clear_exception(struct layer2 *l2)
+{
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       test_and_clear_bit(FLG_REJEXC, &l2->flag);
+       test_and_clear_bit(FLG_OWN_BUSY, &l2->flag);
+       clear_peer_busy(l2);
+}
+
+static int
+sethdraddr(struct layer2 *l2, u_char *header, int rsp)
+{
+       u_char *ptr = header;
+       int crbit = rsp;
+
+       if (test_bit(FLG_LAPD, &l2->flag)) {
+               if (test_bit(FLG_LAPD_NET, &l2->flag))
+                       crbit = !crbit;
+               *ptr++ = (l2->sapi << 2) | (crbit ? 2 : 0);
+               *ptr++ = (l2->tei << 1) | 1;
+               return 2;
+       } else {
+               if (test_bit(FLG_ORIG, &l2->flag))
+                       crbit = !crbit;
+               if (crbit)
+                       *ptr++ = l2->addr.B;
+               else
+                       *ptr++ = l2->addr.A;
+               return 1;
+       }
+}
+
+static inline void
+enqueue_super(struct layer2 *l2, struct sk_buff *skb)
+{
+       if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb))
+               dev_kfree_skb(skb);
+}
+
+static inline void
+enqueue_ui(struct layer2 *l2, struct sk_buff *skb)
+{
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UI_IND, 0);
+       if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb))
+               dev_kfree_skb(skb);
+}
+
+inline int
+IsUI(u_char *data)
+{
+       return (data[0] & 0xef) == UI;
+}
+
+inline int
+IsUA(u_char *data)
+{
+       return (data[0] & 0xef) == UA;
+}
+
+inline int
+IsDM(u_char *data)
+{
+       return (data[0] & 0xef) == DM;
+}
+
+inline int
+IsDISC(u_char *data)
+{
+       return (data[0] & 0xef) == DISC;
+}
+
+inline int
+IsRR(u_char *data, struct layer2 *l2)
+{
+       if (test_bit(FLG_MOD128, &l2->flag))
+               return data[0] == RR;
+       else
+               return (data[0] & 0xf) == 1;
+}
+
+inline int
+IsSFrame(u_char *data, struct layer2 *l2)
+{
+       register u_char d = *data;
+
+       if (!test_bit(FLG_MOD128, &l2->flag))
+               d &= 0xf;
+       return ((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c);
+}
+
+inline int
+IsSABME(u_char *data, struct layer2 *l2)
+{
+       u_char d = data[0] & ~0x10;
+
+       return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM;
+}
+
+inline int
+IsREJ(u_char *data, struct layer2 *l2)
+{
+       return test_bit(FLG_MOD128, &l2->flag) ?
+               data[0] == REJ : (data[0] & 0xf) == REJ;
+}
+
+inline int
+IsFRMR(u_char *data)
+{
+       return (data[0] & 0xef) == FRMR;
+}
+
+inline int
+IsRNR(u_char *data, struct layer2 *l2)
+{
+       return test_bit(FLG_MOD128, &l2->flag) ?
+           data[0] == RNR : (data[0] & 0xf) == RNR;
+}
+
+int
+iframe_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       u_int   i;
+       int     rsp = *skb->data & 0x2;
+
+       i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1);
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (rsp)
+               return 'L';
+       if (skb->len < i)
+               return 'N';
+       if ((skb->len - i) > l2->maxlen)
+               return 'O';
+       return 0;
+}
+
+int
+super_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       if (skb->len != l2addrsize(l2) +
+           (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1))
+               return 'N';
+       return 0;
+}
+
+int
+unnum_error(struct layer2 *l2, struct sk_buff *skb, int wantrsp)
+{
+       int rsp = (*skb->data & 0x2) >> 1;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (rsp != wantrsp)
+               return 'L';
+       if (skb->len != l2addrsize(l2) + 1)
+               return 'N';
+       return 0;
+}
+
+int
+UI_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       int rsp = *skb->data & 0x2;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (rsp)
+               return 'L';
+       if (skb->len > l2->maxlen + l2addrsize(l2) + 1)
+               return 'O';
+       return 0;
+}
+
+int
+FRMR_error(struct layer2 *l2, struct sk_buff *skb)
+{
+       u_int   headers = l2addrsize(l2) + 1;
+       u_char  *datap = skb->data + headers;
+       int     rsp = *skb->data & 0x2;
+
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+       if (!rsp)
+               return 'L';
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               if (skb->len < headers + 5)
+                       return 'N';
+               else if (*debug & DEBUG_L2)
+                       l2m_debug(&l2->l2m,
+                           "FRMR information %2x %2x %2x %2x %2x",
+                           datap[0], datap[1], datap[2], datap[3], datap[4]);
+       } else {
+               if (skb->len < headers + 3)
+                       return 'N';
+               else if (*debug & DEBUG_L2)
+                       l2m_debug(&l2->l2m,
+                           "FRMR information %2x %2x %2x",
+                           datap[0], datap[1], datap[2]);
+       }
+       return 0;
+}
+
+static unsigned int
+legalnr(struct layer2 *l2, unsigned int nr)
+{
+       if (test_bit(FLG_MOD128, &l2->flag))
+               return ((nr - l2->va) % 128) <= ((l2->vs - l2->va) % 128);
+       else
+               return ((nr - l2->va) % 8) <= ((l2->vs - l2->va) % 8);
+}
+
+static void
+setva(struct layer2 *l2, unsigned int nr)
+{
+       struct sk_buff  *skb;
+
+       while (l2->va != nr) {
+               l2->va++;
+               if (test_bit(FLG_MOD128, &l2->flag))
+                       l2->va %= 128;
+               else
+                       l2->va %= 8;
+               if (l2->windowar[l2->sow]) {
+                       skb_trim(l2->windowar[l2->sow], 0);
+                       skb_queue_tail(&l2->tmp_queue, l2->windowar[l2->sow]);
+                       l2->windowar[l2->sow] = NULL;
+               }
+               l2->sow = (l2->sow + 1) % l2->window;
+       }
+       skb = skb_dequeue(&l2->tmp_queue);
+       while (skb) {
+               dev_kfree_skb(skb);
+               skb = skb_dequeue(&l2->tmp_queue);
+       }
+}
+
+static void
+send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr)
+{
+       u_char tmp[MAX_L2HEADER_LEN];
+       int i;
+
+       i = sethdraddr(l2, tmp, cr);
+       tmp[i++] = cmd;
+       if (skb)
+               skb_trim(skb, 0);
+       else {
+               skb = mI_alloc_skb(i, GFP_ATOMIC);
+               if (!skb) {
+                       printk(KERN_WARNING "%s: can't alloc skbuff\n",
+                               __func__);
+                       return;
+               }
+       }
+       memcpy(skb_put(skb, i), tmp, i);
+       enqueue_super(l2, skb);
+}
+
+
+inline u_char
+get_PollFlag(struct layer2 *l2, struct sk_buff *skb)
+{
+       return skb->data[l2addrsize(l2)] & 0x10;
+}
+
+inline u_char
+get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb)
+{
+       u_char PF;
+
+       PF = get_PollFlag(l2, skb);
+       dev_kfree_skb(skb);
+       return PF;
+}
+
+inline void
+start_t200(struct layer2 *l2, int i)
+{
+       mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i);
+       test_and_set_bit(FLG_T200_RUN, &l2->flag);
+}
+
+inline void
+restart_t200(struct layer2 *l2, int i)
+{
+       mISDN_FsmRestartTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, i);
+       test_and_set_bit(FLG_T200_RUN, &l2->flag);
+}
+
+inline void
+stop_t200(struct layer2 *l2, int i)
+{
+       if (test_and_clear_bit(FLG_T200_RUN, &l2->flag))
+               mISDN_FsmDelTimer(&l2->t200, i);
+}
+
+inline void
+st5_dl_release_l2l3(struct layer2 *l2)
+{
+       int pr;
+
+       if (test_and_clear_bit(FLG_PEND_REL, &l2->flag))
+               pr = DL_RELEASE_CNF;
+       else
+               pr = DL_RELEASE_IND;
+       l2up_create(l2, pr, 0, NULL);
+}
+
+inline void
+lapb_dl_release_l2l3(struct layer2 *l2, int f)
+{
+       if (test_bit(FLG_LAPB, &l2->flag))
+               l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL);
+       l2up_create(l2, f, 0, NULL);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+       struct layer2 *l2 = fi->userdata;
+       u_char cmd;
+
+       clear_exception(l2);
+       l2->rc = 0;
+       cmd = (test_bit(FLG_MOD128, &l2->flag) ? SABME : SABM) | 0x10;
+       send_uframe(l2, NULL, cmd, CMD);
+       mISDN_FsmDelTimer(&l2->t203, 1);
+       restart_t200(l2, 1);
+       test_and_clear_bit(FLG_PEND_REL, &l2->flag);
+       freewin(l2);
+       mISDN_FsmChangeState(fi, ST_L2_5);
+}
+
+static void
+l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       if (get_PollFlagFree(l2, skb))
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'C');
+       else
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'D');
+
+}
+
+static void
+l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       if (get_PollFlagFree(l2, skb))
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+       else {
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'E');
+               establishlink(fi);
+               test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+       }
+}
+
+static void
+l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       if (get_PollFlagFree(l2, skb))
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+       else
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'E');
+       establishlink(fi);
+       test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static void
+l2_go_st3(struct FsmInst *fi, int event, void *arg)
+{
+       dev_kfree_skb((struct sk_buff *)arg);
+       mISDN_FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2_mdl_assign(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+
+       mISDN_FsmChangeState(fi, ST_L2_3);
+       dev_kfree_skb((struct sk_buff *)arg);
+       l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->ui_queue, skb);
+       mISDN_FsmChangeState(fi, ST_L2_2);
+       l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->ui_queue, skb);
+}
+
+static void
+tx_ui(struct layer2 *l2)
+{
+       struct sk_buff *skb;
+       u_char header[MAX_L2HEADER_LEN];
+       int i;
+
+       i = sethdraddr(l2, header, CMD);
+       if (test_bit(FLG_LAPD_NET, &l2->flag))
+               header[1] = 0xff; /* tei 127 */
+       header[i++] = UI;
+       while ((skb = skb_dequeue(&l2->ui_queue))) {
+               memcpy(skb_push(skb, i), header, i);
+               enqueue_ui(l2, skb);
+       }
+}
+
+static void
+l2_send_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->ui_queue, skb);
+       tx_ui(l2);
+}
+
+static void
+l2_got_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_pull(skb, l2headersize(l2, 1));
+/*
+ *             in states 1-3 for broadcast
+ */
+
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UI_IND, 0);
+       l2up(l2, DL_UNITDATA_IND, skb);
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       establishlink(fi);
+       test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       test_and_clear_bit(FLG_PEND_REL, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_l3_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       establishlink(fi);
+       test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_release(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_trim(skb, 0);
+       l2up(l2, DL_RELEASE_CNF, skb);
+}
+
+static void
+l2_pend_rel(struct FsmInst *fi, int event, void *arg)
+{
+       struct sk_buff *skb = arg;
+       struct layer2 *l2 = fi->userdata;
+
+       test_and_set_bit(FLG_PEND_REL, &l2->flag);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_disconnect(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       freewin(l2);
+       mISDN_FsmChangeState(fi, ST_L2_6);
+       l2->rc = 0;
+       send_uframe(l2, NULL, DISC | 0x10, CMD);
+       mISDN_FsmDelTimer(&l2->t203, 1);
+       restart_t200(l2, 2);
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_start_multi(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+
+       l2->vs = 0;
+       l2->va = 0;
+       l2->vr = 0;
+       l2->sow = 0;
+       clear_exception(l2);
+       send_uframe(l2, NULL, UA | get_PollFlag(l2, skb), RSP);
+       mISDN_FsmChangeState(fi, ST_L2_7);
+       mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3);
+       skb_trim(skb, 0);
+       l2up(l2, DL_ESTABLISH_IND, skb);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_send_UA(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+}
+
+static void
+l2_send_DM(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       send_uframe(l2, skb, DM | get_PollFlag(l2, skb), RSP);
+}
+
+static void
+l2_restart_multi(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+       int             est = 0;
+
+       send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+
+       l2mgr(l2, MDL_ERROR_IND, (void *) 'F');
+
+       if (l2->vs != l2->va) {
+               skb_queue_purge(&l2->i_queue);
+               est = 1;
+       }
+
+       clear_exception(l2);
+       l2->vs = 0;
+       l2->va = 0;
+       l2->vr = 0;
+       l2->sow = 0;
+       mISDN_FsmChangeState(fi, ST_L2_7);
+       stop_t200(l2, 3);
+       mISDN_FsmRestartTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 3);
+
+       if (est)
+               l2up_create(l2, DL_ESTABLISH_IND, 0, NULL);
+/*             mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ *                 MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED,
+ *                 0, NULL, 0);
+ */
+       if (skb_queue_len(&l2->i_queue) && cansend(l2))
+               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+}
+
+static void
+l2_stop_multi(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       mISDN_FsmDelTimer(&l2->t203, 3);
+       stop_t200(l2, 4);
+
+       send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+       skb_queue_purge(&l2->i_queue);
+       freewin(l2);
+       lapb_dl_release_l2l3(l2, DL_RELEASE_IND);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_connected(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+       int pr = -1;
+
+       if (!get_PollFlag(l2, skb)) {
+               l2_mdl_error_ua(fi, event, arg);
+               return;
+       }
+       dev_kfree_skb(skb);
+       if (test_and_clear_bit(FLG_PEND_REL, &l2->flag))
+               l2_disconnect(fi, event, NULL);
+       if (test_and_clear_bit(FLG_L3_INIT, &l2->flag)) {
+               pr = DL_ESTABLISH_CNF;
+       } else if (l2->vs != l2->va) {
+               skb_queue_purge(&l2->i_queue);
+               pr = DL_ESTABLISH_IND;
+       }
+       stop_t200(l2, 5);
+       l2->vr = 0;
+       l2->vs = 0;
+       l2->va = 0;
+       l2->sow = 0;
+       mISDN_FsmChangeState(fi, ST_L2_7);
+       mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 4);
+       if (pr != -1)
+               l2up_create(l2, pr, 0, NULL);
+
+       if (skb_queue_len(&l2->i_queue) && cansend(l2))
+               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_released(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!get_PollFlag(l2, skb)) {
+               l2_mdl_error_ua(fi, event, arg);
+               return;
+       }
+       dev_kfree_skb(skb);
+       stop_t200(l2, 6);
+       lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!get_PollFlagFree(l2, skb)) {
+               establishlink(fi);
+               test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       }
+}
+
+static void
+l2_st5_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (get_PollFlagFree(l2, skb)) {
+               stop_t200(l2, 7);
+               if (!test_bit(FLG_L3_INIT, &l2->flag))
+                       skb_queue_purge(&l2->i_queue);
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       l2down_create(l2, PH_DEACTIVATE_REQ,
+                               l2_newid(l2), 0, NULL);
+               st5_dl_release_l2l3(l2);
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       }
+}
+
+static void
+l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (get_PollFlagFree(l2, skb)) {
+               stop_t200(l2, 8);
+               lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       }
+}
+
+void
+enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf)
+{
+       struct sk_buff *skb;
+       u_char tmp[MAX_L2HEADER_LEN];
+       int i;
+
+       i = sethdraddr(l2, tmp, cr);
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               tmp[i++] = typ;
+               tmp[i++] = (l2->vr << 1) | (pf ? 1 : 0);
+       } else
+               tmp[i++] = (l2->vr << 5) | typ | (pf ? 0x10 : 0);
+       skb = mI_alloc_skb(i, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_WARNING
+                   "isdnl2 can't alloc sbbuff for enquiry_cr\n");
+               return;
+       }
+       memcpy(skb_put(skb, i), tmp, i);
+       enqueue_super(l2, skb);
+}
+
+inline void
+enquiry_response(struct layer2 *l2)
+{
+       if (test_bit(FLG_OWN_BUSY, &l2->flag))
+               enquiry_cr(l2, RNR, RSP, 1);
+       else
+               enquiry_cr(l2, RR, RSP, 1);
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+}
+
+inline void
+transmit_enquiry(struct layer2 *l2)
+{
+       if (test_bit(FLG_OWN_BUSY, &l2->flag))
+               enquiry_cr(l2, RNR, CMD, 1);
+       else
+               enquiry_cr(l2, RR, CMD, 1);
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       start_t200(l2, 9);
+}
+
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       l2mgr(l2, MDL_ERROR_IND, (void *) 'J');
+       establishlink(fi);
+       test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static void
+invoke_retransmission(struct layer2 *l2, unsigned int nr)
+{
+       u_int   p1;
+
+       if (l2->vs != nr) {
+               while (l2->vs != nr) {
+                       (l2->vs)--;
+                       if (test_bit(FLG_MOD128, &l2->flag)) {
+                               l2->vs %= 128;
+                               p1 = (l2->vs - l2->va) % 128;
+                       } else {
+                               l2->vs %= 8;
+                               p1 = (l2->vs - l2->va) % 8;
+                       }
+                       p1 = (p1 + l2->sow) % l2->window;
+                       if (l2->windowar[p1])
+                               skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+                       else
+                               printk(KERN_WARNING
+                                   "%s: windowar[%d] is NULL\n",
+                                   __func__, p1);
+                       l2->windowar[p1] = NULL;
+               }
+               mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+       }
+}
+
+static void
+l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+       int PollFlag, rsp, typ = RR;
+       unsigned int nr;
+
+       rsp = *skb->data & 0x2;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+
+       skb_pull(skb, l2addrsize(l2));
+       if (IsRNR(skb->data, l2)) {
+               set_peer_busy(l2);
+               typ = RNR;
+       } else
+               clear_peer_busy(l2);
+       if (IsREJ(skb->data, l2))
+               typ = REJ;
+
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               PollFlag = (skb->data[1] & 0x1) == 0x1;
+               nr = skb->data[1] >> 1;
+       } else {
+               PollFlag = (skb->data[0] & 0x10);
+               nr = (skb->data[0] >> 5) & 0x7;
+       }
+       dev_kfree_skb(skb);
+
+       if (PollFlag) {
+               if (rsp)
+                       l2mgr(l2, MDL_ERROR_IND, (void *) 'A');
+               else
+                       enquiry_response(l2);
+       }
+       if (legalnr(l2, nr)) {
+               if (typ == REJ) {
+                       setva(l2, nr);
+                       invoke_retransmission(l2, nr);
+                       stop_t200(l2, 10);
+                       if (mISDN_FsmAddTimer(&l2->t203, l2->T203,
+                                       EV_L2_T203, NULL, 6))
+                               l2m_debug(&l2->l2m, "Restart T203 ST7 REJ");
+               } else if ((nr == l2->vs) && (typ == RR)) {
+                       setva(l2, nr);
+                       stop_t200(l2, 11);
+                       mISDN_FsmRestartTimer(&l2->t203, l2->T203,
+                                       EV_L2_T203, NULL, 7);
+               } else if ((l2->va != nr) || (typ == RNR)) {
+                       setva(l2, nr);
+                       if (typ != RR)
+                               mISDN_FsmDelTimer(&l2->t203, 9);
+                       restart_t200(l2, 12);
+               }
+               if (skb_queue_len(&l2->i_queue) && (typ == RR))
+                       mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+       } else
+               nrerrorrecovery(fi);
+}
+
+static void
+l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!test_bit(FLG_L3_INIT, &l2->flag))
+               skb_queue_tail(&l2->i_queue, skb);
+       else
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_feed_i_pull(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->i_queue, skb);
+       mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+}
+
+static void
+l2_feed_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_tail(&l2->i_queue, skb);
+}
+
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb = arg;
+       int             PollFlag, i;
+       u_int           ns, nr;
+
+       i = l2addrsize(l2);
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               PollFlag = ((skb->data[i + 1] & 0x1) == 0x1);
+               ns = skb->data[i] >> 1;
+               nr = (skb->data[i + 1] >> 1) & 0x7f;
+       } else {
+               PollFlag = (skb->data[i] & 0x10);
+               ns = (skb->data[i] >> 1) & 0x7;
+               nr = (skb->data[i] >> 5) & 0x7;
+       }
+       if (test_bit(FLG_OWN_BUSY, &l2->flag)) {
+               dev_kfree_skb(skb);
+               if (PollFlag)
+                       enquiry_response(l2);
+       } else {
+               if (l2->vr == ns) {
+                       l2->vr++;
+                       if (test_bit(FLG_MOD128, &l2->flag))
+                               l2->vr %= 128;
+                       else
+                               l2->vr %= 8;
+                       test_and_clear_bit(FLG_REJEXC, &l2->flag);
+                       if (PollFlag)
+                               enquiry_response(l2);
+                       else
+                               test_and_set_bit(FLG_ACK_PEND, &l2->flag);
+                       skb_pull(skb, l2headersize(l2, 0));
+                       l2up(l2, DL_DATA_IND, skb);
+               } else {
+                       /* n(s)!=v(r) */
+                       dev_kfree_skb(skb);
+                       if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+                               if (PollFlag)
+                                       enquiry_response(l2);
+                       } else {
+                               enquiry_cr(l2, REJ, RSP, PollFlag);
+                               test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+                       }
+               }
+       }
+       if (legalnr(l2, nr)) {
+               if (!test_bit(FLG_PEER_BUSY, &l2->flag) &&
+                   (fi->state == ST_L2_7)) {
+                       if (nr == l2->vs) {
+                               stop_t200(l2, 13);
+                               mISDN_FsmRestartTimer(&l2->t203, l2->T203,
+                                               EV_L2_T203, NULL, 7);
+                       } else if (nr != l2->va)
+                               restart_t200(l2, 14);
+               }
+               setva(l2, nr);
+       } else {
+               nrerrorrecovery(fi);
+               return;
+       }
+       if (skb_queue_len(&l2->i_queue) && (fi->state == ST_L2_7))
+               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+       if (test_and_clear_bit(FLG_ACK_PEND, &l2->flag))
+               enquiry_cr(l2, RR, RSP, 0);
+}
+
+static void
+l2_got_tei(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       u_int           info;
+
+       l2->tei = (signed char)(long)arg;
+       set_channel_address(&l2->ch, l2->sapi, l2->tei);
+       info = DL_INFO_L2_CONNECT;
+       l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info);
+       if (fi->state == ST_L2_3) {
+               establishlink(fi);
+               test_and_set_bit(FLG_L3_INIT, &l2->flag);
+       } else
+               mISDN_FsmChangeState(fi, ST_L2_4);
+       if (skb_queue_len(&l2->ui_queue))
+               tx_ui(l2);
+}
+
+static void
+l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+       } else if (l2->rc == l2->N200) {
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+               skb_queue_purge(&l2->i_queue);
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'G');
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       l2down_create(l2, PH_DEACTIVATE_REQ,
+                               l2_newid(l2), 0, NULL);
+               st5_dl_release_l2l3(l2);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       } else {
+               l2->rc++;
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+               send_uframe(l2, NULL, (test_bit(FLG_MOD128, &l2->flag) ?
+                       SABME : SABM) | 0x10, CMD);
+       }
+}
+
+static void
+l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+       } else if (l2->rc == l2->N200) {
+               mISDN_FsmChangeState(fi, ST_L2_4);
+               test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'H');
+               lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+               if (l2->tm)
+                       l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       } else {
+               l2->rc++;
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200,
+                           NULL, 9);
+               send_uframe(l2, NULL, DISC | 0x10, CMD);
+       }
+}
+
+static void
+l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+               return;
+       }
+       test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+       l2->rc = 0;
+       mISDN_FsmChangeState(fi, ST_L2_8);
+       transmit_enquiry(l2);
+       l2->rc++;
+}
+
+static void
+l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 9);
+               return;
+       }
+       test_and_clear_bit(FLG_T200_RUN, &l2->flag);
+       if (l2->rc == l2->N200) {
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'I');
+               establishlink(fi);
+               test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+       } else {
+               transmit_enquiry(l2);
+               l2->rc++;
+       }
+}
+
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       if (test_bit(FLG_LAPD, &l2->flag) &&
+               test_bit(FLG_DCHAN_BUSY, &l2->flag)) {
+               mISDN_FsmAddTimer(&l2->t203, l2->T203, EV_L2_T203, NULL, 9);
+               return;
+       }
+       mISDN_FsmChangeState(fi, ST_L2_8);
+       transmit_enquiry(l2);
+       l2->rc = 0;
+}
+
+static void
+l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2   *l2 = fi->userdata;
+       struct sk_buff  *skb, *nskb, *oskb;
+       u_char          header[MAX_L2HEADER_LEN];
+       u_int           i, p1;
+
+       if (!cansend(l2))
+               return;
+
+       skb = skb_dequeue(&l2->i_queue);
+       if (!skb)
+               return;
+
+       if (test_bit(FLG_MOD128, &l2->flag))
+               p1 = (l2->vs - l2->va) % 128;
+       else
+               p1 = (l2->vs - l2->va) % 8;
+       p1 = (p1 + l2->sow) % l2->window;
+       if (l2->windowar[p1]) {
+               printk(KERN_WARNING "isdnl2 try overwrite ack queue entry %d\n",
+                   p1);
+               dev_kfree_skb(l2->windowar[p1]);
+       }
+       l2->windowar[p1] = skb;
+       i = sethdraddr(l2, header, CMD);
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               header[i++] = l2->vs << 1;
+               header[i++] = l2->vr << 1;
+               l2->vs = (l2->vs + 1) % 128;
+       } else {
+               header[i++] = (l2->vr << 5) | (l2->vs << 1);
+               l2->vs = (l2->vs + 1) % 8;
+       }
+
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       p1 = skb_headroom(nskb);
+       if (p1 >= i)
+               memcpy(skb_push(nskb, i), header, i);
+       else {
+               printk(KERN_WARNING
+                   "isdnl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+               oskb = nskb;
+               nskb = mI_alloc_skb(oskb->len + i, GFP_ATOMIC);
+               if (!nskb) {
+                       dev_kfree_skb(oskb);
+                       printk(KERN_WARNING "%s: no skb mem\n", __func__);
+                       return;
+               }
+               memcpy(skb_put(nskb, i), header, i);
+               memcpy(skb_put(nskb, oskb->len), oskb->data, oskb->len);
+               dev_kfree_skb(oskb);
+       }
+       l2down(l2, PH_DATA_REQ, l2_newid(l2), nskb);
+       test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       if (!test_and_set_bit(FLG_T200_RUN, &l2->flag)) {
+               mISDN_FsmDelTimer(&l2->t203, 13);
+               mISDN_FsmAddTimer(&l2->t200, l2->T200, EV_L2_T200, NULL, 11);
+       }
+}
+
+static void
+l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+       int PollFlag, rsp, rnr = 0;
+       unsigned int nr;
+
+       rsp = *skb->data & 0x2;
+       if (test_bit(FLG_ORIG, &l2->flag))
+               rsp = !rsp;
+
+       skb_pull(skb, l2addrsize(l2));
+
+       if (IsRNR(skb->data, l2)) {
+               set_peer_busy(l2);
+               rnr = 1;
+       } else
+               clear_peer_busy(l2);
+
+       if (test_bit(FLG_MOD128, &l2->flag)) {
+               PollFlag = (skb->data[1] & 0x1) == 0x1;
+               nr = skb->data[1] >> 1;
+       } else {
+               PollFlag = (skb->data[0] & 0x10);
+               nr = (skb->data[0] >> 5) & 0x7;
+       }
+       dev_kfree_skb(skb);
+       if (rsp && PollFlag) {
+               if (legalnr(l2, nr)) {
+                       if (rnr) {
+                               restart_t200(l2, 15);
+                       } else {
+                               stop_t200(l2, 16);
+                               mISDN_FsmAddTimer(&l2->t203, l2->T203,
+                                           EV_L2_T203, NULL, 5);
+                               setva(l2, nr);
+                       }
+                       invoke_retransmission(l2, nr);
+                       mISDN_FsmChangeState(fi, ST_L2_7);
+                       if (skb_queue_len(&l2->i_queue) && cansend(l2))
+                               mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+               } else
+                       nrerrorrecovery(fi);
+       } else {
+               if (!rsp && PollFlag)
+                       enquiry_response(l2);
+               if (legalnr(l2, nr))
+                       setva(l2, nr);
+               else
+                       nrerrorrecovery(fi);
+       }
+}
+
+static void
+l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_pull(skb, l2addrsize(l2) + 1);
+
+       if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */
+           (IsUA(skb->data) && (fi->state == ST_L2_7))) {
+               l2mgr(l2, MDL_ERROR_IND, (void *) 'K');
+               establishlink(fi);
+               test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+       }
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->ui_queue);
+       l2->tei = GROUP_TEI;
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->ui_queue);
+       l2->tei = GROUP_TEI;
+       l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       l2->tei = GROUP_TEI;
+       stop_t200(l2, 17);
+       st5_dl_release_l2l3(l2);
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->ui_queue);
+       l2->tei = GROUP_TEI;
+       stop_t200(l2, 18);
+       l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       l2->tei = GROUP_TEI;
+       stop_t200(l2, 17);
+       mISDN_FsmDelTimer(&l2->t203, 19);
+       l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+/*     mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ *             MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED,
+ *             0, NULL, 0);
+ */
+       mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+               l2up(l2, DL_RELEASE_IND, skb);
+       else
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       stop_t200(l2, 19);
+       st5_dl_release_l2l3(l2);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+       dev_kfree_skb(skb);
+}
+
+static void
+l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->ui_queue);
+       stop_t200(l2, 20);
+       l2up(l2, DL_RELEASE_CNF, skb);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       freewin(l2);
+       stop_t200(l2, 19);
+       mISDN_FsmDelTimer(&l2->t203, 19);
+       l2up(l2, DL_RELEASE_IND, skb);
+       mISDN_FsmChangeState(fi, ST_L2_4);
+       if (l2->tm)
+               l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!test_and_set_bit(FLG_OWN_BUSY, &l2->flag)) {
+               enquiry_cr(l2, RNR, RSP, 0);
+               test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_clear_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+       struct sk_buff *skb = arg;
+
+       if (!test_and_clear_bit(FLG_OWN_BUSY, &l2->flag)) {
+               enquiry_cr(l2, RR, RSP, 0);
+               test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+       }
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static void
+l2_frame_error(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       l2mgr(l2, MDL_ERROR_IND, arg);
+}
+
+static void
+l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
+{
+       struct layer2 *l2 = fi->userdata;
+
+       l2mgr(l2, MDL_ERROR_IND, arg);
+       establishlink(fi);
+       test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static struct FsmNode L2FnList[] =
+{
+       {ST_L2_1, EV_L2_DL_ESTABLISH_REQ, l2_mdl_assign},
+       {ST_L2_2, EV_L2_DL_ESTABLISH_REQ, l2_go_st3},
+       {ST_L2_4, EV_L2_DL_ESTABLISH_REQ, l2_establish},
+       {ST_L2_5, EV_L2_DL_ESTABLISH_REQ, l2_discard_i_setl3},
+       {ST_L2_7, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+       {ST_L2_8, EV_L2_DL_ESTABLISH_REQ, l2_l3_reestablish},
+       {ST_L2_4, EV_L2_DL_RELEASE_REQ, l2_release},
+       {ST_L2_5, EV_L2_DL_RELEASE_REQ, l2_pend_rel},
+       {ST_L2_7, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+       {ST_L2_8, EV_L2_DL_RELEASE_REQ, l2_disconnect},
+       {ST_L2_5, EV_L2_DL_DATA, l2_feed_i_if_reest},
+       {ST_L2_7, EV_L2_DL_DATA, l2_feed_i_pull},
+       {ST_L2_8, EV_L2_DL_DATA, l2_feed_iqueue},
+       {ST_L2_1, EV_L2_DL_UNITDATA, l2_queue_ui_assign},
+       {ST_L2_2, EV_L2_DL_UNITDATA, l2_queue_ui},
+       {ST_L2_3, EV_L2_DL_UNITDATA, l2_queue_ui},
+       {ST_L2_4, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_5, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_6, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_7, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_8, EV_L2_DL_UNITDATA, l2_send_ui},
+       {ST_L2_1, EV_L2_MDL_ASSIGN, l2_got_tei},
+       {ST_L2_2, EV_L2_MDL_ASSIGN, l2_got_tei},
+       {ST_L2_3, EV_L2_MDL_ASSIGN, l2_got_tei},
+       {ST_L2_2, EV_L2_MDL_ERROR, l2_st24_tei_remove},
+       {ST_L2_3, EV_L2_MDL_ERROR, l2_st3_tei_remove},
+       {ST_L2_4, EV_L2_MDL_REMOVE, l2_st24_tei_remove},
+       {ST_L2_5, EV_L2_MDL_REMOVE, l2_st5_tei_remove},
+       {ST_L2_6, EV_L2_MDL_REMOVE, l2_st6_tei_remove},
+       {ST_L2_7, EV_L2_MDL_REMOVE, l2_tei_remove},
+       {ST_L2_8, EV_L2_MDL_REMOVE, l2_tei_remove},
+       {ST_L2_4, EV_L2_SABME, l2_start_multi},
+       {ST_L2_5, EV_L2_SABME, l2_send_UA},
+       {ST_L2_6, EV_L2_SABME, l2_send_DM},
+       {ST_L2_7, EV_L2_SABME, l2_restart_multi},
+       {ST_L2_8, EV_L2_SABME, l2_restart_multi},
+       {ST_L2_4, EV_L2_DISC, l2_send_DM},
+       {ST_L2_5, EV_L2_DISC, l2_send_DM},
+       {ST_L2_6, EV_L2_DISC, l2_send_UA},
+       {ST_L2_7, EV_L2_DISC, l2_stop_multi},
+       {ST_L2_8, EV_L2_DISC, l2_stop_multi},
+       {ST_L2_4, EV_L2_UA, l2_mdl_error_ua},
+       {ST_L2_5, EV_L2_UA, l2_connected},
+       {ST_L2_6, EV_L2_UA, l2_released},
+       {ST_L2_7, EV_L2_UA, l2_mdl_error_ua},
+       {ST_L2_8, EV_L2_UA, l2_mdl_error_ua},
+       {ST_L2_4, EV_L2_DM, l2_reestablish},
+       {ST_L2_5, EV_L2_DM, l2_st5_dm_release},
+       {ST_L2_6, EV_L2_DM, l2_st6_dm_release},
+       {ST_L2_7, EV_L2_DM, l2_mdl_error_dm},
+       {ST_L2_8, EV_L2_DM, l2_st8_mdl_error_dm},
+       {ST_L2_1, EV_L2_UI, l2_got_ui},
+       {ST_L2_2, EV_L2_UI, l2_got_ui},
+       {ST_L2_3, EV_L2_UI, l2_got_ui},
+       {ST_L2_4, EV_L2_UI, l2_got_ui},
+       {ST_L2_5, EV_L2_UI, l2_got_ui},
+       {ST_L2_6, EV_L2_UI, l2_got_ui},
+       {ST_L2_7, EV_L2_UI, l2_got_ui},
+       {ST_L2_8, EV_L2_UI, l2_got_ui},
+       {ST_L2_7, EV_L2_FRMR, l2_got_FRMR},
+       {ST_L2_8, EV_L2_FRMR, l2_got_FRMR},
+       {ST_L2_7, EV_L2_SUPER, l2_st7_got_super},
+       {ST_L2_8, EV_L2_SUPER, l2_st8_got_super},
+       {ST_L2_7, EV_L2_I, l2_got_iframe},
+       {ST_L2_8, EV_L2_I, l2_got_iframe},
+       {ST_L2_5, EV_L2_T200, l2_st5_tout_200},
+       {ST_L2_6, EV_L2_T200, l2_st6_tout_200},
+       {ST_L2_7, EV_L2_T200, l2_st7_tout_200},
+       {ST_L2_8, EV_L2_T200, l2_st8_tout_200},
+       {ST_L2_7, EV_L2_T203, l2_st7_tout_203},
+       {ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+       {ST_L2_7, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+       {ST_L2_8, EV_L2_SET_OWN_BUSY, l2_set_own_busy},
+       {ST_L2_7, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+       {ST_L2_8, EV_L2_CLEAR_OWN_BUSY, l2_clear_own_busy},
+       {ST_L2_4, EV_L2_FRAME_ERROR, l2_frame_error},
+       {ST_L2_5, EV_L2_FRAME_ERROR, l2_frame_error},
+       {ST_L2_6, EV_L2_FRAME_ERROR, l2_frame_error},
+       {ST_L2_7, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+       {ST_L2_8, EV_L2_FRAME_ERROR, l2_frame_error_reest},
+       {ST_L2_1, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+       {ST_L2_2, EV_L1_DEACTIVATE, l2_st24_tei_remove},
+       {ST_L2_3, EV_L1_DEACTIVATE, l2_st3_tei_remove},
+       {ST_L2_4, EV_L1_DEACTIVATE, l2_st14_persistant_da},
+       {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistant_da},
+       {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistant_da},
+       {ST_L2_7, EV_L1_DEACTIVATE, l2_persistant_da},
+       {ST_L2_8, EV_L1_DEACTIVATE, l2_persistant_da},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static int
+ph_data_indication(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb)
+{
+       u_char  *datap = skb->data;
+       int     ret = -EINVAL;
+       int     psapi, ptei;
+       u_int   l;
+       int     c = 0;
+
+       l = l2addrsize(l2);
+       if (skb->len <= l) {
+               mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *) 'N');
+               return ret;
+       }
+       if (test_bit(FLG_LAPD, &l2->flag)) { /* Maybe not needed */
+               psapi = *datap++;
+               ptei = *datap++;
+               if ((psapi & 1) || !(ptei & 1)) {
+                       printk(KERN_WARNING
+                           "l2 D-channel frame wrong EA0/EA1\n");
+                       return ret;
+               }
+               psapi >>= 2;
+               ptei >>= 1;
+               if (psapi != l2->sapi) {
+                       /* not our bussiness
+                        * printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n",
+                        *  __func__,
+                        *      psapi, l2->sapi);
+                        */
+                       dev_kfree_skb(skb);
+                       return 0;
+               }
+               if ((ptei != l2->tei) && (ptei != GROUP_TEI)) {
+                       /* not our bussiness
+                        * printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n",
+                        *  __func__,
+                        *      ptei, l2->tei, psapi);
+                        */
+                       dev_kfree_skb(skb);
+                       return 0;
+               }
+       } else
+               datap += l;
+       if (!(*datap & 1)) {    /* I-Frame */
+               c = iframe_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb);
+       } else if (IsSFrame(datap, l2)) {       /* S-Frame */
+               c = super_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb);
+       } else if (IsUI(datap)) {
+               c = UI_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb);
+       } else if (IsSABME(datap, l2)) {
+               c = unnum_error(l2, skb, CMD);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb);
+       } else if (IsUA(datap)) {
+               c = unnum_error(l2, skb, RSP);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb);
+       } else if (IsDISC(datap)) {
+               c = unnum_error(l2, skb, CMD);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb);
+       } else if (IsDM(datap)) {
+               c = unnum_error(l2, skb, RSP);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb);
+       } else if (IsFRMR(datap)) {
+               c = FRMR_error(l2, skb);
+               if (!c)
+                       ret = mISDN_FsmEvent(&l2->l2m, EV_L2_FRMR, skb);
+       } else
+               c = 'L';
+       if (c) {
+               printk(KERN_WARNING "l2 D-channel frame error %c\n", c);
+               mISDN_FsmEvent(&l2->l2m, EV_L2_FRAME_ERROR, (void *)(long)c);
+       }
+       return ret;
+}
+
+static int
+l2_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct layer2           *l2 = container_of(ch, struct layer2, ch);
+       struct mISDNhead        *hh =  mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+
+       if (*debug & DEBUG_L2_RECV)
+               printk(KERN_DEBUG "%s: prim(%x) id(%x) tei(%d)\n",
+                   __func__, hh->prim, hh->id, l2->tei);
+       switch (hh->prim) {
+       case PH_DATA_IND:
+               ret = ph_data_indication(l2, hh, skb);
+               break;
+       case PH_DATA_CNF:
+               ret = ph_data_confirm(l2, hh, skb);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_L1_ACTIV, &l2->flag);
+               l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL);
+               if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+                       ret = mISDN_FsmEvent(&l2->l2m,
+                               EV_L2_DL_ESTABLISH_REQ, skb);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_L1_ACTIV, &l2->flag);
+               l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL);
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb);
+               break;
+       case MPH_INFORMATION_IND:
+               if (!l2->up)
+                       break;
+               ret = l2->up->send(l2->up, skb);
+               break;
+       case DL_DATA_REQ:
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb);
+               break;
+       case DL_UNITDATA_REQ:
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb);
+               break;
+       case DL_ESTABLISH_REQ:
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       test_and_set_bit(FLG_ORIG, &l2->flag);
+               if (test_bit(FLG_L1_ACTIV, &l2->flag)) {
+                       if (test_bit(FLG_LAPD, &l2->flag) ||
+                               test_bit(FLG_ORIG, &l2->flag))
+                               ret = mISDN_FsmEvent(&l2->l2m,
+                                       EV_L2_DL_ESTABLISH_REQ, skb);
+               } else {
+                       if (test_bit(FLG_LAPD, &l2->flag) ||
+                               test_bit(FLG_ORIG, &l2->flag)) {
+                               test_and_set_bit(FLG_ESTAB_PEND,
+                                       &l2->flag);
+                       }
+                       ret = l2down(l2, PH_ACTIVATE_REQ, l2_newid(l2),
+                           skb);
+               }
+               break;
+       case DL_RELEASE_REQ:
+               if (test_bit(FLG_LAPB, &l2->flag))
+                       l2down_create(l2, PH_DEACTIVATE_REQ,
+                               l2_newid(l2), 0, NULL);
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ,
+                   skb);
+               break;
+       default:
+               if (*debug & DEBUG_L2)
+                       l2m_debug(&l2->l2m, "l2 unknown pr %04x",
+                           hh->prim);
+       }
+       if (ret) {
+               dev_kfree_skb(skb);
+               ret = 0;
+       }
+       return ret;
+}
+
+int
+tei_l2(struct layer2 *l2, u_int cmd, u_long arg)
+{
+       int             ret = -EINVAL;
+
+       if (*debug & DEBUG_L2_TEI)
+               printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+       switch (cmd) {
+       case (MDL_ASSIGN_REQ):
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg);
+               break;
+       case (MDL_REMOVE_REQ):
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL);
+               break;
+       case (MDL_ERROR_IND):
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+               break;
+       case (MDL_ERROR_RSP):
+               /* ETS 300-125 5.3.2.1 Test: TC13010 */
+               printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n");
+               ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+               break;
+       }
+       return ret;
+}
+
+static void
+release_l2(struct layer2 *l2)
+{
+       mISDN_FsmDelTimer(&l2->t200, 21);
+       mISDN_FsmDelTimer(&l2->t203, 16);
+       skb_queue_purge(&l2->i_queue);
+       skb_queue_purge(&l2->ui_queue);
+       skb_queue_purge(&l2->down_queue);
+       ReleaseWin(l2);
+       if (test_bit(FLG_LAPD, &l2->flag)) {
+               release_tei(l2);
+               if (l2->ch.st)
+                       l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D,
+                           CLOSE_CHANNEL, NULL);
+       }
+       kfree(l2);
+}
+
+static int
+l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct layer2           *l2 = container_of(ch, struct layer2, ch);
+       u_int                   info;
+
+       if (*debug & DEBUG_L2_CTRL)
+               printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               if (test_bit(FLG_LAPD, &l2->flag)) {
+                       set_channel_address(&l2->ch, l2->sapi, l2->tei);
+                       info = DL_INFO_L2_CONNECT;
+                       l2up_create(l2, DL_INFORMATION_IND,
+                           sizeof(info), &info);
+               }
+               break;
+       case CLOSE_CHANNEL:
+               if (l2->ch.peer)
+                       l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL);
+               release_l2(l2);
+               break;
+       }
+       return 0;
+}
+
+struct layer2 *
+create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, u_long arg)
+{
+       struct layer2           *l2;
+       struct channel_req      rq;
+
+       l2 = kzalloc(sizeof(struct layer2), GFP_KERNEL);
+       if (!l2) {
+               printk(KERN_ERR "kzalloc layer2 failed\n");
+               return NULL;
+       }
+       l2->next_id = 1;
+       l2->down_id = MISDN_ID_NONE;
+       l2->up = ch;
+       l2->ch.st = ch->st;
+       l2->ch.send = l2_send;
+       l2->ch.ctrl = l2_ctrl;
+       switch (protocol) {
+       case ISDN_P_LAPD_NT:
+               test_and_set_bit(FLG_LAPD, &l2->flag);
+               test_and_set_bit(FLG_LAPD_NET, &l2->flag);
+               test_and_set_bit(FLG_MOD128, &l2->flag);
+               l2->sapi = 0;
+               l2->maxlen = MAX_DFRAME_LEN;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       l2->window = 7;
+               else
+                       l2->window = 1;
+               if (test_bit(OPTION_L2_PTP, &options))
+                       test_and_set_bit(FLG_PTP, &l2->flag);
+               if (test_bit(OPTION_L2_FIXEDTEI, &options))
+                       test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+               l2->tei = (u_int)arg;
+               l2->T200 = 1000;
+               l2->N200 = 3;
+               l2->T203 = 10000;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       rq.protocol = ISDN_P_NT_E1;
+               else
+                       rq.protocol = ISDN_P_NT_S0;
+               rq.adr.channel = 0;
+               l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+               break;
+       case ISDN_P_LAPD_TE:
+               test_and_set_bit(FLG_LAPD, &l2->flag);
+               test_and_set_bit(FLG_MOD128, &l2->flag);
+               test_and_set_bit(FLG_ORIG, &l2->flag);
+               l2->sapi = 0;
+               l2->maxlen = MAX_DFRAME_LEN;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       l2->window = 7;
+               else
+                       l2->window = 1;
+               if (test_bit(OPTION_L2_PTP, &options))
+                       test_and_set_bit(FLG_PTP, &l2->flag);
+               if (test_bit(OPTION_L2_FIXEDTEI, &options))
+                       test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+               l2->tei = (u_int)arg;
+               l2->T200 = 1000;
+               l2->N200 = 3;
+               l2->T203 = 10000;
+               if (test_bit(OPTION_L2_PMX, &options))
+                       rq.protocol = ISDN_P_TE_E1;
+               else
+                       rq.protocol = ISDN_P_TE_S0;
+               rq.adr.channel = 0;
+               l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+               break;
+       case ISDN_P_B_X75SLP:
+               test_and_set_bit(FLG_LAPB, &l2->flag);
+               l2->window = 7;
+               l2->maxlen = MAX_DATA_SIZE;
+               l2->T200 = 1000;
+               l2->N200 = 4;
+               l2->T203 = 5000;
+               l2->addr.A = 3;
+               l2->addr.B = 1;
+               break;
+       default:
+               printk(KERN_ERR "layer2 create failed prt %x\n",
+                       protocol);
+               kfree(l2);
+               return NULL;
+       }
+       skb_queue_head_init(&l2->i_queue);
+       skb_queue_head_init(&l2->ui_queue);
+       skb_queue_head_init(&l2->down_queue);
+       skb_queue_head_init(&l2->tmp_queue);
+       InitWin(l2);
+       l2->l2m.fsm = &l2fsm;
+       if (test_bit(FLG_LAPB, &l2->flag) ||
+               test_bit(FLG_PTP, &l2->flag) ||
+               test_bit(FLG_LAPD_NET, &l2->flag))
+               l2->l2m.state = ST_L2_4;
+       else
+               l2->l2m.state = ST_L2_1;
+       l2->l2m.debug = *debug;
+       l2->l2m.userdata = l2;
+       l2->l2m.userint = 0;
+       l2->l2m.printdebug = l2m_debug;
+
+       mISDN_FsmInitTimer(&l2->l2m, &l2->t200);
+       mISDN_FsmInitTimer(&l2->l2m, &l2->t203);
+       return l2;
+}
+
+static int
+x75create(struct channel_req *crq)
+{
+       struct layer2   *l2;
+
+       if (crq->protocol != ISDN_P_B_X75SLP)
+               return -EPROTONOSUPPORT;
+       l2 = create_l2(crq->ch, crq->protocol, 0, 0);
+       if (!l2)
+               return -ENOMEM;
+       crq->ch = &l2->ch;
+       crq->protocol = ISDN_P_B_HDLC;
+       return 0;
+}
+
+static struct Bprotocol X75SLP = {
+       .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)),
+       .name = "X75SLP",
+       .create = x75create
+};
+
+int
+Isdnl2_Init(u_int *deb)
+{
+       debug = deb;
+       mISDN_register_Bprotocol(&X75SLP);
+       l2fsm.state_count = L2_STATE_COUNT;
+       l2fsm.event_count = L2_EVENT_COUNT;
+       l2fsm.strEvent = strL2Event;
+       l2fsm.strState = strL2State;
+       mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList));
+       TEIInit(deb);
+       return 0;
+}
+
+void
+Isdnl2_cleanup(void)
+{
+       mISDN_unregister_Bprotocol(&X75SLP);
+       TEIFree();
+       mISDN_FsmFree(&l2fsm);
+}
+
diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h
new file mode 100644 (file)
index 0000000..de2dd02
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Layer 2 defines
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/skbuff.h>
+#include "fsm.h"
+
+#define MAX_WINDOW     8
+
+struct manager {
+       struct mISDNchannel     ch;
+       struct mISDNchannel     bcast;
+       u_long                  options;
+       struct list_head        layer2;
+       rwlock_t                lock;
+       struct FsmInst          deact;
+       struct FsmTimer         datimer;
+       struct sk_buff_head     sendq;
+       struct mISDNchannel     *up;
+       u_int                   nextid;
+       u_int                   lastid;
+};
+
+struct teimgr {
+       int                     ri;
+       int                     rcnt;
+       struct FsmInst          tei_m;
+       struct FsmTimer         timer;
+       int                     tval, nval;
+       struct layer2           *l2;
+       struct manager          *mgr;
+};
+
+struct laddr {
+       u_char  A;
+       u_char  B;
+};
+
+struct layer2 {
+       struct list_head        list;
+       struct mISDNchannel     ch;
+       u_long                  flag;
+       int                     id;
+       struct mISDNchannel     *up;
+       signed char             sapi;
+       signed char             tei;
+       struct laddr            addr;
+       u_int                   maxlen;
+       struct teimgr           *tm;
+       u_int                   vs, va, vr;
+       int                     rc;
+       u_int                   window;
+       u_int                   sow;
+       struct FsmInst          l2m;
+       struct FsmTimer         t200, t203;
+       int                     T200, N200, T203;
+       u_int                   next_id;
+       u_int                   down_id;
+       struct sk_buff          *windowar[MAX_WINDOW];
+       struct sk_buff_head     i_queue;
+       struct sk_buff_head     ui_queue;
+       struct sk_buff_head     down_queue;
+       struct sk_buff_head     tmp_queue;
+};
+
+enum {
+       ST_L2_1,
+       ST_L2_2,
+       ST_L2_3,
+       ST_L2_4,
+       ST_L2_5,
+       ST_L2_6,
+       ST_L2_7,
+       ST_L2_8,
+};
+
+#define L2_STATE_COUNT (ST_L2_8+1)
+
+extern struct layer2   *create_l2(struct mISDNchannel *, u_int,
+                               u_long, u_long);
+extern int             tei_l2(struct layer2 *, u_int, u_long arg);
+
+
+/* from tei.c */
+extern int             l2_tei(struct layer2 *, u_int, u_long arg);
+extern void            release_tei(struct layer2 *);
+extern int             TEIInit(u_int *);
+extern void            TEIFree(void);
+
+#define MAX_L2HEADER_LEN 4
+
+#define RR     0x01
+#define RNR    0x05
+#define REJ    0x09
+#define SABME  0x6f
+#define SABM   0x2f
+#define DM     0x0f
+#define UI     0x03
+#define DISC   0x43
+#define UA     0x63
+#define FRMR   0x87
+#define XID    0xaf
+
+#define CMD    0
+#define RSP    1
+
+#define LC_FLUSH_WAIT 1
+
+#define FLG_LAPB       0
+#define FLG_LAPD       1
+#define FLG_ORIG       2
+#define FLG_MOD128     3
+#define FLG_PEND_REL   4
+#define FLG_L3_INIT    5
+#define FLG_T200_RUN   6
+#define FLG_ACK_PEND   7
+#define FLG_REJEXC     8
+#define FLG_OWN_BUSY   9
+#define FLG_PEER_BUSY  10
+#define FLG_DCHAN_BUSY 11
+#define FLG_L1_ACTIV   12
+#define FLG_ESTAB_PEND 13
+#define FLG_PTP                14
+#define FLG_FIXED_TEI  15
+#define FLG_L2BLOCK    16
+#define FLG_L1_NOTREADY        17
+#define FLG_LAPD_NET   18
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
new file mode 100644 (file)
index 0000000..4ba4cc3
--- /dev/null
@@ -0,0 +1,781 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static int     *debug;
+
+static struct proto mISDN_proto = {
+       .name           = "misdn",
+       .owner          = THIS_MODULE,
+       .obj_size       = sizeof(struct mISDN_sock)
+};
+
+#define _pms(sk)       ((struct mISDN_sock *)sk)
+
+static struct mISDN_sock_list  data_sockets = {
+       .lock = __RW_LOCK_UNLOCKED(data_sockets.lock)
+};
+
+static struct mISDN_sock_list  base_sockets = {
+       .lock = __RW_LOCK_UNLOCKED(base_sockets.lock)
+};
+
+#define L2_HEADER_LEN  4
+
+static inline struct sk_buff *
+_l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
+{
+       struct sk_buff  *skb;
+
+       skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
+       if (likely(skb))
+               skb_reserve(skb, L2_HEADER_LEN);
+       return skb;
+}
+
+static void
+mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk)
+{
+       write_lock_bh(&l->lock);
+       sk_add_node(sk, &l->head);
+       write_unlock_bh(&l->lock);
+}
+
+static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk)
+{
+       write_lock_bh(&l->lock);
+       sk_del_node_init(sk);
+       write_unlock_bh(&l->lock);
+}
+
+static int
+mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDN_sock *msk;
+       int     err;
+
+       msk = container_of(ch, struct mISDN_sock, ch);
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
+       if (msk->sk.sk_state == MISDN_CLOSED)
+               return -EUNATCH;
+       __net_timestamp(skb);
+       err = sock_queue_rcv_skb(&msk->sk, skb);
+       if (err)
+               printk(KERN_WARNING "%s: error %d\n", __func__, err);
+       return err;
+}
+
+static int
+mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct mISDN_sock *msk;
+
+       msk = container_of(ch, struct mISDN_sock, ch);
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               msk->sk.sk_state = MISDN_CLOSED;
+               break;
+       }
+       return 0;
+}
+
+static inline void
+mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
+{
+       struct timeval  tv;
+
+       if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
+               skb_get_timestamp(skb, &tv);
+               put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
+       }
+}
+
+static int
+mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+    struct msghdr *msg, size_t len, int flags)
+{
+       struct sk_buff          *skb;
+       struct sock             *sk = sock->sk;
+       struct sockaddr_mISDN   *maddr;
+
+       int             copied, err;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
+                       __func__, (int)len, flags, _pms(sk)->ch.nr,
+                       sk->sk_protocol);
+       if (flags & (MSG_OOB))
+               return -EOPNOTSUPP;
+
+       if (sk->sk_state == MISDN_CLOSED)
+               return 0;
+
+       skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
+       if (!skb)
+               return err;
+
+       if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+               msg->msg_namelen = sizeof(struct sockaddr_mISDN);
+               maddr = (struct sockaddr_mISDN *)msg->msg_name;
+               maddr->family = AF_ISDN;
+               maddr->dev = _pms(sk)->dev->id;
+               if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+                   (sk->sk_protocol == ISDN_P_LAPD_NT)) {
+                       maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
+                       maddr->tei =  (mISDN_HEAD_ID(skb) >> 8) & 0xff;
+                       maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
+               } else {
+                       maddr->channel = _pms(sk)->ch.nr;
+                       maddr->sapi = _pms(sk)->ch.addr & 0xFF;
+                       maddr->tei =  (_pms(sk)->ch.addr >> 8) & 0xFF;
+               }
+       } else {
+               if (msg->msg_namelen)
+                       printk(KERN_WARNING "%s: too small namelen %d\n",
+                           __func__, msg->msg_namelen);
+               msg->msg_namelen = 0;
+       }
+
+       copied = skb->len + MISDN_HEADER_LEN;
+       if (len < copied) {
+               if (flags & MSG_PEEK)
+                       atomic_dec(&skb->users);
+               else
+                       skb_queue_head(&sk->sk_receive_queue, skb);
+               return -ENOSPC;
+       }
+       memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
+           MISDN_HEADER_LEN);
+
+       err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+       mISDN_sock_cmsg(sk, msg, skb);
+
+       skb_free_datagram(sk, skb);
+
+       return err ? : copied;
+}
+
+static int
+mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+    struct msghdr *msg, size_t len)
+{
+       struct sock             *sk = sock->sk;
+       struct sk_buff          *skb;
+       int                     err = -ENOMEM;
+       struct sockaddr_mISDN   *maddr;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
+                    __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
+                    sk->sk_protocol);
+
+       if (msg->msg_flags & MSG_OOB)
+               return -EOPNOTSUPP;
+
+       if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+               return -EINVAL;
+
+       if (len < MISDN_HEADER_LEN)
+               return -EINVAL;
+
+       if (sk->sk_state != MISDN_BOUND)
+               return -EBADFD;
+
+       lock_sock(sk);
+
+       skb = _l2_alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               goto done;
+
+       if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+               err = -EFAULT;
+               goto drop;
+       }
+
+       memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
+       skb_pull(skb, MISDN_HEADER_LEN);
+
+       if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+               /* if we have a address, we use it */
+               maddr = (struct sockaddr_mISDN *)msg->msg_name;
+               mISDN_HEAD_ID(skb) = maddr->channel;
+       } else { /* use default for L2 messages */
+               if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+                   (sk->sk_protocol == ISDN_P_LAPD_NT))
+                   mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
+       }
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s: ID:%x\n",
+                    __func__, mISDN_HEAD_ID(skb));
+
+       err = -ENODEV;
+       if (!_pms(sk)->ch.peer ||
+           (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb)))
+               goto drop;
+
+       err = len;
+
+done:
+       release_sock(sk);
+       return err;
+
+drop:
+       kfree_skb(skb);
+       goto done;
+}
+
+static int
+data_sock_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+       if (!sk)
+               return 0;
+       switch (sk->sk_protocol) {
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_E1:
+       case ISDN_P_NT_E1:
+               if (sk->sk_state == MISDN_BOUND)
+                       delete_channel(&_pms(sk)->ch);
+               else
+                       mISDN_sock_unlink(&data_sockets, sk);
+               break;
+       case ISDN_P_LAPD_TE:
+       case ISDN_P_LAPD_NT:
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+       case ISDN_P_B_X75SLP:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_L2DSP:
+       case ISDN_P_B_L2DSPHDLC:
+               delete_channel(&_pms(sk)->ch);
+               mISDN_sock_unlink(&data_sockets, sk);
+               break;
+       }
+
+       lock_sock(sk);
+
+       sock_orphan(sk);
+       skb_queue_purge(&sk->sk_receive_queue);
+
+       release_sock(sk);
+       sock_put(sk);
+
+       return 0;
+}
+
+static int
+data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p)
+{
+       struct mISDN_ctrl_req   cq;
+       int                     err = -EINVAL, val;
+       struct mISDNchannel     *bchan, *next;
+
+       lock_sock(sk);
+       if (!_pms(sk)->dev) {
+               err = -ENODEV;
+               goto done;
+       }
+       switch (cmd) {
+       case IMCTRLREQ:
+               if (copy_from_user(&cq, p, sizeof(cq))) {
+                       err = -EFAULT;
+                       break;
+               }
+               if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
+                       list_for_each_entry_safe(bchan, next,
+                               &_pms(sk)->dev->bchannels, list) {
+                               if (bchan->nr == cq.channel) {
+                                       err = bchan->ctrl(bchan,
+                                               CONTROL_CHANNEL, &cq);
+                                       break;
+                               }
+                       }
+               } else
+                       err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D,
+                               CONTROL_CHANNEL, &cq);
+               if (err)
+                       break;
+               if (copy_to_user(p, &cq, sizeof(cq)))
+                       err = -EFAULT;
+               break;
+       case IMCLEAR_L2:
+               if (sk->sk_protocol != ISDN_P_LAPD_NT) {
+                       err = -EINVAL;
+                       break;
+               }
+               if (get_user(val, (int __user *)p)) {
+                       err = -EFAULT;
+                       break;
+               }
+               err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
+                   CONTROL_CHANNEL, &val);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+done:
+       release_sock(sk);
+       return err;
+}
+
+static int
+data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       int                     err = 0, id;
+       struct sock             *sk = sock->sk;
+       struct mISDNdevice      *dev;
+       struct mISDNversion     ver;
+
+       switch (cmd) {
+       case IMGETVERSION:
+               ver.major = MISDN_MAJOR_VERSION;
+               ver.minor = MISDN_MINOR_VERSION;
+               ver.release = MISDN_RELEASE;
+               if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+                       err = -EFAULT;
+               break;
+       case IMGETCOUNT:
+               id = get_mdevice_count();
+               if (put_user(id, (int __user *)arg))
+                       err = -EFAULT;
+               break;
+       case IMGETDEVINFO:
+               if (get_user(id, (int __user *)arg)) {
+                       err = -EFAULT;
+                       break;
+               }
+               dev = get_mdevice(id);
+               if (dev) {
+                       struct mISDN_devinfo di;
+
+                       di.id = dev->id;
+                       di.Dprotocols = dev->Dprotocols;
+                       di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+                       di.protocol = dev->D.protocol;
+                       memcpy(di.channelmap, dev->channelmap,
+                               MISDN_CHMAP_SIZE * 4);
+                       di.nrbchan = dev->nrbchan;
+                       strcpy(di.name, dev->name);
+                       if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+                               err = -EFAULT;
+               } else
+                       err = -ENODEV;
+               break;
+       default:
+               if (sk->sk_state == MISDN_BOUND)
+                       err = data_sock_ioctl_bound(sk, cmd,
+                               (void __user *)arg);
+               else
+                       err = -ENOTCONN;
+       }
+       return err;
+}
+
+static int data_sock_setsockopt(struct socket *sock, int level, int optname,
+       char __user *optval, int len)
+{
+       struct sock *sk = sock->sk;
+       int err = 0, opt = 0;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock,
+                   level, optname, optval, len);
+
+       lock_sock(sk);
+
+       switch (optname) {
+       case MISDN_TIME_STAMP:
+               if (get_user(opt, (int __user *)optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (opt)
+                       _pms(sk)->cmask |= MISDN_TIME_STAMP;
+               else
+                       _pms(sk)->cmask &= ~MISDN_TIME_STAMP;
+               break;
+       default:
+               err = -ENOPROTOOPT;
+               break;
+       }
+       release_sock(sk);
+       return err;
+}
+
+static int data_sock_getsockopt(struct socket *sock, int level, int optname,
+       char __user *optval, int __user *optlen)
+{
+       struct sock *sk = sock->sk;
+       int len, opt;
+
+       if (get_user(len, optlen))
+               return -EFAULT;
+
+       switch (optname) {
+       case MISDN_TIME_STAMP:
+               if (_pms(sk)->cmask & MISDN_TIME_STAMP)
+                       opt = 1;
+               else
+                       opt = 0;
+
+               if (put_user(opt, optval))
+                       return -EFAULT;
+               break;
+       default:
+               return -ENOPROTOOPT;
+       }
+
+       return 0;
+}
+
+static int
+data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+       struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+       struct sock *sk = sock->sk;
+       int err = 0;
+
+       if (*debug & DEBUG_SOCKET)
+               printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+       if (addr_len != sizeof(struct sockaddr_mISDN))
+               return -EINVAL;
+       if (!maddr || maddr->family != AF_ISDN)
+               return -EINVAL;
+
+       lock_sock(sk);
+
+       if (_pms(sk)->dev) {
+               err = -EALREADY;
+               goto done;
+       }
+       _pms(sk)->dev = get_mdevice(maddr->dev);
+       if (!_pms(sk)->dev) {
+               err = -ENODEV;
+               goto done;
+       }
+       _pms(sk)->ch.send = mISDN_send;
+       _pms(sk)->ch.ctrl = mISDN_ctrl;
+
+       switch (sk->sk_protocol) {
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_E1:
+       case ISDN_P_NT_E1:
+               mISDN_sock_unlink(&data_sockets, sk);
+               err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch,
+                   sk->sk_protocol, maddr);
+               if (err)
+                       mISDN_sock_link(&data_sockets, sk);
+               break;
+       case ISDN_P_LAPD_TE:
+       case ISDN_P_LAPD_NT:
+               err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch,
+                   sk->sk_protocol, maddr);
+               break;
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+       case ISDN_P_B_X75SLP:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_L2DSP:
+       case ISDN_P_B_L2DSPHDLC:
+               err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch,
+                   sk->sk_protocol, maddr);
+               break;
+       default:
+               err = -EPROTONOSUPPORT;
+       }
+       if (err)
+               goto done;
+       sk->sk_state = MISDN_BOUND;
+       _pms(sk)->ch.protocol = sk->sk_protocol;
+
+done:
+       release_sock(sk);
+       return err;
+}
+
+static int
+data_sock_getname(struct socket *sock, struct sockaddr *addr,
+    int *addr_len, int peer)
+{
+       struct sockaddr_mISDN   *maddr = (struct sockaddr_mISDN *) addr;
+       struct sock             *sk = sock->sk;
+
+       if (!_pms(sk)->dev)
+               return -EBADFD;
+
+       lock_sock(sk);
+
+       *addr_len = sizeof(*maddr);
+       maddr->dev = _pms(sk)->dev->id;
+       maddr->channel = _pms(sk)->ch.nr;
+       maddr->sapi = _pms(sk)->ch.addr & 0xff;
+       maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
+       release_sock(sk);
+       return 0;
+}
+
+static const struct proto_ops data_sock_ops = {
+       .family         = PF_ISDN,
+       .owner          = THIS_MODULE,
+       .release        = data_sock_release,
+       .ioctl          = data_sock_ioctl,
+       .bind           = data_sock_bind,
+       .getname        = data_sock_getname,
+       .sendmsg        = mISDN_sock_sendmsg,
+       .recvmsg        = mISDN_sock_recvmsg,
+       .poll           = datagram_poll,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = data_sock_setsockopt,
+       .getsockopt     = data_sock_getsockopt,
+       .connect        = sock_no_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .mmap           = sock_no_mmap
+};
+
+static int
+data_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+       struct sock *sk;
+
+       if (sock->type != SOCK_DGRAM)
+               return -ESOCKTNOSUPPORT;
+
+       sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+       if (!sk)
+               return -ENOMEM;
+
+       sock_init_data(sock, sk);
+
+       sock->ops = &data_sock_ops;
+       sock->state = SS_UNCONNECTED;
+       sock_reset_flag(sk, SOCK_ZAPPED);
+
+       sk->sk_protocol = protocol;
+       sk->sk_state    = MISDN_OPEN;
+       mISDN_sock_link(&data_sockets, sk);
+
+       return 0;
+}
+
+static int
+base_sock_release(struct socket *sock)
+{
+       struct sock *sk = sock->sk;
+
+       printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+       if (!sk)
+               return 0;
+
+       mISDN_sock_unlink(&base_sockets, sk);
+       sock_orphan(sk);
+       sock_put(sk);
+
+       return 0;
+}
+
+static int
+base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+       int                     err = 0, id;
+       struct mISDNdevice      *dev;
+       struct mISDNversion     ver;
+
+       switch (cmd) {
+       case IMGETVERSION:
+               ver.major = MISDN_MAJOR_VERSION;
+               ver.minor = MISDN_MINOR_VERSION;
+               ver.release = MISDN_RELEASE;
+               if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+                       err = -EFAULT;
+               break;
+       case IMGETCOUNT:
+               id = get_mdevice_count();
+               if (put_user(id, (int __user *)arg))
+                       err = -EFAULT;
+               break;
+       case IMGETDEVINFO:
+               if (get_user(id, (int __user *)arg)) {
+                       err = -EFAULT;
+                       break;
+               }
+               dev = get_mdevice(id);
+               if (dev) {
+                       struct mISDN_devinfo di;
+
+                       di.id = dev->id;
+                       di.Dprotocols = dev->Dprotocols;
+                       di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+                       di.protocol = dev->D.protocol;
+                       memcpy(di.channelmap, dev->channelmap,
+                               MISDN_CHMAP_SIZE * 4);
+                       di.nrbchan = dev->nrbchan;
+                       strcpy(di.name, dev->name);
+                       if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+                               err = -EFAULT;
+               } else
+                       err = -ENODEV;
+               break;
+       default:
+               err = -EINVAL;
+       }
+       return err;
+}
+
+static int
+base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+       struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+       struct sock *sk = sock->sk;
+       int err = 0;
+
+       if (!maddr || maddr->family != AF_ISDN)
+               return -EINVAL;
+
+       lock_sock(sk);
+
+       if (_pms(sk)->dev) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       _pms(sk)->dev = get_mdevice(maddr->dev);
+       if (!_pms(sk)->dev) {
+               err = -ENODEV;
+               goto done;
+       }
+       sk->sk_state = MISDN_BOUND;
+
+done:
+       release_sock(sk);
+       return err;
+}
+
+static const struct proto_ops base_sock_ops = {
+       .family         = PF_ISDN,
+       .owner          = THIS_MODULE,
+       .release        = base_sock_release,
+       .ioctl          = base_sock_ioctl,
+       .bind           = base_sock_bind,
+       .getname        = sock_no_getname,
+       .sendmsg        = sock_no_sendmsg,
+       .recvmsg        = sock_no_recvmsg,
+       .poll           = sock_no_poll,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .connect        = sock_no_connect,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .mmap           = sock_no_mmap
+};
+
+
+static int
+base_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+       struct sock *sk;
+
+       if (sock->type != SOCK_RAW)
+               return -ESOCKTNOSUPPORT;
+
+       sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+       if (!sk)
+               return -ENOMEM;
+
+       sock_init_data(sock, sk);
+       sock->ops = &base_sock_ops;
+       sock->state = SS_UNCONNECTED;
+       sock_reset_flag(sk, SOCK_ZAPPED);
+       sk->sk_protocol = protocol;
+       sk->sk_state    = MISDN_OPEN;
+       mISDN_sock_link(&base_sockets, sk);
+
+       return 0;
+}
+
+static int
+mISDN_sock_create(struct net *net, struct socket *sock, int proto)
+{
+       int err = -EPROTONOSUPPORT;
+
+       switch  (proto) {
+       case ISDN_P_BASE:
+               err = base_sock_create(net, sock, proto);
+               break;
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_E1:
+       case ISDN_P_NT_E1:
+       case ISDN_P_LAPD_TE:
+       case ISDN_P_LAPD_NT:
+       case ISDN_P_B_RAW:
+       case ISDN_P_B_HDLC:
+       case ISDN_P_B_X75SLP:
+       case ISDN_P_B_L2DTMF:
+       case ISDN_P_B_L2DSP:
+       case ISDN_P_B_L2DSPHDLC:
+               err = data_sock_create(net, sock, proto);
+               break;
+       default:
+               return err;
+       }
+
+       return err;
+}
+
+static struct
+net_proto_family mISDN_sock_family_ops = {
+       .owner  = THIS_MODULE,
+       .family = PF_ISDN,
+       .create = mISDN_sock_create,
+};
+
+int
+misdn_sock_init(u_int *deb)
+{
+       int err;
+
+       debug = deb;
+       err = sock_register(&mISDN_sock_family_ops);
+       if (err)
+               printk(KERN_ERR "%s: error(%d)\n", __func__, err);
+       return err;
+}
+
+void
+misdn_sock_cleanup(void)
+{
+       sock_unregister(PF_ISDN);
+}
+
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
new file mode 100644 (file)
index 0000000..54cfddc
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/kthread.h>
+#include "core.h"
+
+static u_int   *debug;
+
+static inline void
+_queue_message(struct mISDNstack *st, struct sk_buff *skb)
+{
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+
+       if (*debug & DEBUG_QUEUE_FUNC)
+               printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+                   __func__, hh->prim, hh->id, skb);
+       skb_queue_tail(&st->msgq, skb);
+       if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) {
+               test_and_set_bit(mISDN_STACK_WORK, &st->status);
+               wake_up_interruptible(&st->workq);
+       }
+}
+
+int
+mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       _queue_message(ch->st, skb);
+       return 0;
+}
+
+static struct mISDNchannel *
+get_channel4id(struct mISDNstack *st, u_int id)
+{
+       struct mISDNchannel     *ch;
+
+       mutex_lock(&st->lmutex);
+       list_for_each_entry(ch, &st->layer2, list) {
+               if (id == ch->nr)
+                       goto unlock;
+       }
+       ch = NULL;
+unlock:
+       mutex_unlock(&st->lmutex);
+       return ch;
+}
+
+static void
+send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb)
+{
+       struct hlist_node       *node;
+       struct sock             *sk;
+       struct sk_buff          *cskb = NULL;
+
+       read_lock(&sl->lock);
+       sk_for_each(sk, node, &sl->head) {
+               if (sk->sk_state != MISDN_BOUND)
+                       continue;
+               if (!cskb)
+                       cskb = skb_copy(skb, GFP_KERNEL);
+               if (!cskb) {
+                       printk(KERN_WARNING "%s no skb\n", __func__);
+                       break;
+               }
+               if (!sock_queue_rcv_skb(sk, cskb))
+                       cskb = NULL;
+       }
+       read_unlock(&sl->lock);
+       if (cskb)
+               dev_kfree_skb(cskb);
+}
+
+static void
+send_layer2(struct mISDNstack *st, struct sk_buff *skb)
+{
+       struct sk_buff          *cskb;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       struct mISDNchannel     *ch;
+       int                     ret;
+
+       if (!st)
+               return;
+       mutex_lock(&st->lmutex);
+       if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */
+               list_for_each_entry(ch, &st->layer2, list) {
+                       if (list_is_last(&ch->list, &st->layer2)) {
+                               cskb = skb;
+                               skb = NULL;
+                       } else {
+                               cskb = skb_copy(skb, GFP_KERNEL);
+                       }
+                       if (cskb) {
+                               ret = ch->send(ch, cskb);
+                               if (ret) {
+                                       if (*debug & DEBUG_SEND_ERR)
+                                               printk(KERN_DEBUG
+                                                   "%s ch%d prim(%x) addr(%x)"
+                                                   " err %d\n",
+                                                   __func__, ch->nr,
+                                                   hh->prim, ch->addr, ret);
+                                       dev_kfree_skb(cskb);
+                               }
+                       } else {
+                               printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+                                   __func__, ch->nr, ch->addr);
+                               goto out;
+                       }
+               }
+       } else {
+               list_for_each_entry(ch, &st->layer2, list) {
+                       if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) {
+                               ret = ch->send(ch, skb);
+                               if (!ret)
+                                       skb = NULL;
+                               goto out;
+                       }
+               }
+               ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb);
+               if (!ret)
+                       skb = NULL;
+               else if (*debug & DEBUG_SEND_ERR)
+                       printk(KERN_DEBUG
+                           "%s ch%d mgr prim(%x) addr(%x) err %d\n",
+                           __func__, ch->nr, hh->prim, ch->addr, ret);
+       }
+out:
+       mutex_unlock(&st->lmutex);
+       if (skb)
+               dev_kfree_skb(skb);
+}
+
+static inline int
+send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
+{
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       struct mISDNchannel     *ch;
+       int     lm;
+
+       lm = hh->prim & MISDN_LAYERMASK;
+       if (*debug & DEBUG_QUEUE_FUNC)
+               printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+                   __func__, hh->prim, hh->id, skb);
+       if (lm == 0x1) {
+               if (!hlist_empty(&st->l1sock.head)) {
+                       __net_timestamp(skb);
+                       send_socklist(&st->l1sock, skb);
+               }
+               return st->layer1->send(st->layer1, skb);
+       } else if (lm == 0x2) {
+               if (!hlist_empty(&st->l1sock.head))
+                       send_socklist(&st->l1sock, skb);
+               send_layer2(st, skb);
+               return 0;
+       } else if (lm == 0x4) {
+               ch = get_channel4id(st, hh->id);
+               if (ch)
+                       return ch->send(ch, skb);
+               else
+                       printk(KERN_WARNING
+                           "%s: dev(%s) prim(%x) id(%x) no channel\n",
+                           __func__, st->dev->name, hh->prim, hh->id);
+       } else if (lm == 0x8) {
+               WARN_ON(lm == 0x8);
+               ch = get_channel4id(st, hh->id);
+               if (ch)
+                       return ch->send(ch, skb);
+               else
+                       printk(KERN_WARNING
+                           "%s: dev(%s) prim(%x) id(%x) no channel\n",
+                           __func__, st->dev->name, hh->prim, hh->id);
+       } else {
+               /* broadcast not handled yet */
+               printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
+                   __func__, st->dev->name, hh->prim);
+       }
+       return -ESRCH;
+}
+
+static void
+do_clear_stack(struct mISDNstack *st)
+{
+}
+
+static int
+mISDNStackd(void *data)
+{
+       struct mISDNstack *st = data;
+       int err = 0;
+
+#ifdef CONFIG_SMP
+       lock_kernel();
+#endif
+       sigfillset(&current->blocked);
+#ifdef CONFIG_SMP
+       unlock_kernel();
+#endif
+       if (*debug & DEBUG_MSG_THREAD)
+               printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name);
+
+       if (st->notify != NULL) {
+               complete(st->notify);
+               st->notify = NULL;
+       }
+
+       for (;;) {
+               struct sk_buff  *skb;
+
+               if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) {
+                       test_and_clear_bit(mISDN_STACK_WORK, &st->status);
+                       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+               } else
+                       test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+               while (test_bit(mISDN_STACK_WORK, &st->status)) {
+                       skb = skb_dequeue(&st->msgq);
+                       if (!skb) {
+                               test_and_clear_bit(mISDN_STACK_WORK,
+                                       &st->status);
+                               /* test if a race happens */
+                               skb = skb_dequeue(&st->msgq);
+                               if (!skb)
+                                       continue;
+                               test_and_set_bit(mISDN_STACK_WORK,
+                                   &st->status);
+                       }
+#ifdef MISDN_MSG_STATS
+                       st->msg_cnt++;
+#endif
+                       err = send_msg_to_layer(st, skb);
+                       if (unlikely(err)) {
+                               if (*debug & DEBUG_SEND_ERR)
+                                       printk(KERN_DEBUG
+                                           "%s: %s prim(%x) id(%x) "
+                                           "send call(%d)\n",
+                                           __func__, st->dev->name,
+                                           mISDN_HEAD_PRIM(skb),
+                                           mISDN_HEAD_ID(skb), err);
+                               dev_kfree_skb(skb);
+                               continue;
+                       }
+                       if (unlikely(test_bit(mISDN_STACK_STOPPED,
+                           &st->status))) {
+                               test_and_clear_bit(mISDN_STACK_WORK,
+                                   &st->status);
+                               test_and_clear_bit(mISDN_STACK_RUNNING,
+                                   &st->status);
+                               break;
+                       }
+               }
+               if (test_bit(mISDN_STACK_CLEARING, &st->status)) {
+                       test_and_set_bit(mISDN_STACK_STOPPED, &st->status);
+                       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+                       do_clear_stack(st);
+                       test_and_clear_bit(mISDN_STACK_CLEARING, &st->status);
+                       test_and_set_bit(mISDN_STACK_RESTART, &st->status);
+               }
+               if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) {
+                       test_and_clear_bit(mISDN_STACK_STOPPED, &st->status);
+                       test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+                       if (!skb_queue_empty(&st->msgq))
+                               test_and_set_bit(mISDN_STACK_WORK,
+                                   &st->status);
+               }
+               if (test_bit(mISDN_STACK_ABORT, &st->status))
+                       break;
+               if (st->notify != NULL) {
+                       complete(st->notify);
+                       st->notify = NULL;
+               }
+#ifdef MISDN_MSG_STATS
+               st->sleep_cnt++;
+#endif
+               test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+               wait_event_interruptible(st->workq, (st->status &
+                   mISDN_STACK_ACTION_MASK));
+               if (*debug & DEBUG_MSG_THREAD)
+                       printk(KERN_DEBUG "%s: %s wake status %08lx\n",
+                           __func__, st->dev->name, st->status);
+               test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
+
+               test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
+
+               if (test_bit(mISDN_STACK_STOPPED, &st->status)) {
+                       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+#ifdef MISDN_MSG_STATS
+                       st->stopped_cnt++;
+#endif
+               }
+       }
+#ifdef MISDN_MSG_STATS
+       printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
+           "msg %d sleep %d stopped\n",
+           st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt);
+       printk(KERN_DEBUG
+           "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n",
+           st->dev->name, st->thread->utime, st->thread->stime);
+       printk(KERN_DEBUG
+           "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
+           st->dev->name, st->thread->nvcsw, st->thread->nivcsw);
+       printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
+           st->dev->name);
+#endif
+       test_and_set_bit(mISDN_STACK_KILLED, &st->status);
+       test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+       test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+       test_and_clear_bit(mISDN_STACK_ABORT, &st->status);
+       skb_queue_purge(&st->msgq);
+       st->thread = NULL;
+       if (st->notify != NULL) {
+               complete(st->notify);
+               st->notify = NULL;
+       }
+       return 0;
+}
+
+static int
+l1_receive(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       if (!ch->st)
+               return -ENODEV;
+       __net_timestamp(skb);
+       _queue_message(ch->st, skb);
+       return 0;
+}
+
+void
+set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei)
+{
+       ch->addr = sapi | (tei << 8);
+}
+
+void
+__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+       list_add_tail(&ch->list, &st->layer2);
+}
+
+void
+add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+       mutex_lock(&st->lmutex);
+       __add_layer2(ch, st);
+       mutex_unlock(&st->lmutex);
+}
+
+static int
+st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       if (!ch->st || ch->st->layer1)
+               return -EINVAL;
+       return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg);
+}
+
+int
+create_stack(struct mISDNdevice *dev)
+{
+       struct mISDNstack       *newst;
+       int                     err;
+       DECLARE_COMPLETION_ONSTACK(done);
+
+       newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL);
+       if (!newst) {
+               printk(KERN_ERR "kmalloc mISDN_stack failed\n");
+               return -ENOMEM;
+       }
+       newst->dev = dev;
+       INIT_LIST_HEAD(&newst->layer2);
+       INIT_HLIST_HEAD(&newst->l1sock.head);
+       rwlock_init(&newst->l1sock.lock);
+       init_waitqueue_head(&newst->workq);
+       skb_queue_head_init(&newst->msgq);
+       mutex_init(&newst->lmutex);
+       dev->D.st = newst;
+       err = create_teimanager(dev);
+       if (err) {
+               printk(KERN_ERR "kmalloc teimanager failed\n");
+               kfree(newst);
+               return err;
+       }
+       dev->teimgr->peer = &newst->own;
+       dev->teimgr->recv = mISDN_queue_message;
+       dev->teimgr->st = newst;
+       newst->layer1 = &dev->D;
+       dev->D.recv = l1_receive;
+       dev->D.peer = &newst->own;
+       newst->own.st = newst;
+       newst->own.ctrl = st_own_ctrl;
+       newst->own.send = mISDN_queue_message;
+       newst->own.recv = mISDN_queue_message;
+       if (*debug & DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name);
+       newst->notify = &done;
+       newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
+               newst->dev->name);
+       if (IS_ERR(newst->thread)) {
+               err = PTR_ERR(newst->thread);
+               printk(KERN_ERR
+                       "mISDN:cannot create kernel thread for %s (%d)\n",
+                       newst->dev->name, err);
+               delete_teimanager(dev->teimgr);
+               kfree(newst);
+       } else
+               wait_for_completion(&done);
+       return err;
+}
+
+int
+connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
+               u_int protocol, struct sockaddr_mISDN *adr)
+{
+       struct mISDN_sock       *msk = container_of(ch, struct mISDN_sock, ch);
+       struct channel_req      rq;
+       int                     err;
+
+
+       if (*debug &  DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, dev->name, protocol, adr->dev, adr->channel,
+                        adr->sapi, adr->tei);
+       switch (protocol) {
+       case ISDN_P_NT_S0:
+       case ISDN_P_NT_E1:
+       case ISDN_P_TE_S0:
+       case ISDN_P_TE_E1:
+#ifdef PROTOCOL_CHECK
+               /* this should be enhanced */
+               if (!list_empty(&dev->D.st->layer2)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+               if (!hlist_empty(&dev->D.st->l1sock.head)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+#endif
+               ch->recv = mISDN_queue_message;
+               ch->peer = &dev->D.st->own;
+               ch->st = dev->D.st;
+               rq.protocol = protocol;
+               rq.adr.channel = 0;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+               if (err)
+                       return err;
+               write_lock_bh(&dev->D.st->l1sock.lock);
+               sk_add_node(&msk->sk, &dev->D.st->l1sock.head);
+               write_unlock_bh(&dev->D.st->l1sock.lock);
+               break;
+       default:
+               return -ENOPROTOOPT;
+       }
+       return 0;
+}
+
+int
+connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
+    u_int protocol, struct sockaddr_mISDN *adr)
+{
+       struct channel_req      rq, rq2;
+       int                     pmask, err;
+       struct Bprotocol        *bp;
+
+       if (*debug &  DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, dev->name, protocol,
+                       adr->dev, adr->channel, adr->sapi,
+                       adr->tei);
+       ch->st = dev->D.st;
+       pmask = 1 << (protocol & ISDN_P_B_MASK);
+       if (pmask & dev->Bprotocols) {
+               rq.protocol = protocol;
+               rq.adr = *adr;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               if (err)
+                       return err;
+               ch->recv = rq.ch->send;
+               ch->peer = rq.ch;
+               rq.ch->recv = ch->send;
+               rq.ch->peer = ch;
+               rq.ch->st = dev->D.st;
+       } else {
+               bp = get_Bprotocol4mask(pmask);
+               if (!bp)
+                       return -ENOPROTOOPT;
+               rq2.protocol = protocol;
+               rq2.adr = *adr;
+               rq2.ch = ch;
+               err = bp->create(&rq2);
+               if (err)
+                       return err;
+               ch->recv = rq2.ch->send;
+               ch->peer = rq2.ch;
+               rq2.ch->st = dev->D.st;
+               rq.protocol = rq2.protocol;
+               rq.adr = *adr;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               if (err) {
+                       rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL);
+                       return err;
+               }
+               rq2.ch->recv = rq.ch->send;
+               rq2.ch->peer = rq.ch;
+               rq.ch->recv = rq2.ch->send;
+               rq.ch->peer = rq2.ch;
+               rq.ch->st = dev->D.st;
+       }
+       ch->protocol = protocol;
+       ch->nr = rq.ch->nr;
+       return 0;
+}
+
+int
+create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
+    u_int protocol, struct sockaddr_mISDN *adr)
+{
+       struct channel_req      rq;
+       int                     err;
+
+       if (*debug &  DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, dev->name, protocol,
+                       adr->dev, adr->channel, adr->sapi,
+                       adr->tei);
+       rq.protocol = ISDN_P_TE_S0;
+       if (dev->Dprotocols & (1 << ISDN_P_TE_E1))
+               rq.protocol = ISDN_P_TE_E1;
+       switch (protocol) {
+       case ISDN_P_LAPD_NT:
+               rq.protocol = ISDN_P_NT_S0;
+               if (dev->Dprotocols & (1 << ISDN_P_NT_E1))
+                       rq.protocol = ISDN_P_NT_E1;
+       case ISDN_P_LAPD_TE:
+#ifdef PROTOCOL_CHECK
+               /* this should be enhanced */
+               if (!list_empty(&dev->D.st->layer2)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+               if (!hlist_empty(&dev->D.st->l1sock.head)
+                       && dev->D.protocol != protocol)
+                       return -EBUSY;
+#endif
+               ch->recv = mISDN_queue_message;
+               ch->peer = &dev->D.st->own;
+               ch->st = dev->D.st;
+               rq.adr.channel = 0;
+               err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+               printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+               if (err)
+                       break;
+               rq.protocol = protocol;
+               rq.adr = *adr;
+               rq.ch = ch;
+               err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq);
+               printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err);
+               if (!err) {
+                       if ((protocol == ISDN_P_LAPD_NT) && !rq.ch)
+                               break;
+                       add_layer2(rq.ch, dev->D.st);
+                       rq.ch->recv = mISDN_queue_message;
+                       rq.ch->peer = &dev->D.st->own;
+                       rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */
+               }
+               break;
+       default:
+               err = -EPROTONOSUPPORT;
+       }
+       return err;
+}
+
+void
+delete_channel(struct mISDNchannel *ch)
+{
+       struct mISDN_sock       *msk = container_of(ch, struct mISDN_sock, ch);
+       struct mISDNchannel     *pch;
+
+       if (!ch->st) {
+               printk(KERN_WARNING "%s: no stack\n", __func__);
+               return;
+       }
+       if (*debug & DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
+                   ch->st->dev->name, ch->protocol);
+       if (ch->protocol >= ISDN_P_B_START) {
+               if (ch->peer) {
+                       ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL);
+                       ch->peer = NULL;
+               }
+               return;
+       }
+       switch (ch->protocol) {
+       case ISDN_P_NT_S0:
+       case ISDN_P_TE_S0:
+       case ISDN_P_NT_E1:
+       case ISDN_P_TE_E1:
+               write_lock_bh(&ch->st->l1sock.lock);
+               sk_del_node_init(&msk->sk);
+               write_unlock_bh(&ch->st->l1sock.lock);
+               ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL);
+               break;
+       case ISDN_P_LAPD_TE:
+               pch = get_channel4id(ch->st, ch->nr);
+               if (pch) {
+                       mutex_lock(&ch->st->lmutex);
+                       list_del(&pch->list);
+                       mutex_unlock(&ch->st->lmutex);
+                       pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+                       pch = ch->st->dev->teimgr;
+                       pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+               } else
+                       printk(KERN_WARNING "%s: no l2 channel\n",
+                           __func__);
+               break;
+       case ISDN_P_LAPD_NT:
+               pch = ch->st->dev->teimgr;
+               if (pch) {
+                       pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+               } else
+                       printk(KERN_WARNING "%s: no l2 channel\n",
+                           __func__);
+               break;
+       default:
+               break;
+       }
+       return;
+}
+
+void
+delete_stack(struct mISDNdevice *dev)
+{
+       struct mISDNstack       *st = dev->D.st;
+       DECLARE_COMPLETION_ONSTACK(done);
+
+       if (*debug & DEBUG_CORE_FUNC)
+               printk(KERN_DEBUG "%s: st(%s)\n", __func__,
+                   st->dev->name);
+       if (dev->teimgr)
+               delete_teimanager(dev->teimgr);
+       if (st->thread) {
+               if (st->notify) {
+                       printk(KERN_WARNING "%s: notifier in use\n",
+                           __func__);
+                               complete(st->notify);
+               }
+               st->notify = &done;
+               test_and_set_bit(mISDN_STACK_ABORT, &st->status);
+               test_and_set_bit(mISDN_STACK_WAKEUP, &st->status);
+               wake_up_interruptible(&st->workq);
+               wait_for_completion(&done);
+       }
+       if (!list_empty(&st->layer2))
+               printk(KERN_WARNING "%s: layer2 list not empty\n",
+                   __func__);
+       if (!hlist_empty(&st->l1sock.head))
+               printk(KERN_WARNING "%s: layer1 list not empty\n",
+                   __func__);
+       kfree(st);
+}
+
+void
+mISDN_initstack(u_int *dp)
+{
+       debug = dp;
+}
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
new file mode 100644 (file)
index 0000000..56a76a0
--- /dev/null
@@ -0,0 +1,1340 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "layer2.h"
+#include <linux/random.h>
+#include "core.h"
+
+#define ID_REQUEST     1
+#define ID_ASSIGNED    2
+#define ID_DENIED      3
+#define ID_CHK_REQ     4
+#define ID_CHK_RES     5
+#define ID_REMOVE      6
+#define ID_VERIFY      7
+
+#define TEI_ENTITY_ID  0xf
+
+#define MGR_PH_ACTIVE  16
+#define MGR_PH_NOTREADY        17
+
+#define DATIMER_VAL    10000
+
+static         u_int   *debug;
+
+static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL};
+
+enum {
+       ST_L1_DEACT,
+       ST_L1_DEACT_PENDING,
+       ST_L1_ACTIV,
+};
+#define DEACT_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strDeactState[] =
+{
+       "ST_L1_DEACT",
+       "ST_L1_DEACT_PENDING",
+       "ST_L1_ACTIV",
+};
+
+enum {
+       EV_ACTIVATE,
+       EV_ACTIVATE_IND,
+       EV_DEACTIVATE,
+       EV_DEACTIVATE_IND,
+       EV_UI,
+       EV_DATIMER,
+};
+
+#define DEACT_EVENT_COUNT (EV_DATIMER+1)
+
+static char *strDeactEvent[] =
+{
+       "EV_ACTIVATE",
+       "EV_ACTIVATE_IND",
+       "EV_DEACTIVATE",
+       "EV_DEACTIVATE_IND",
+       "EV_UI",
+       "EV_DATIMER",
+};
+
+static void
+da_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct manager  *mgr = fi->userdata;
+       va_list va;
+
+       if (!(*debug & DEBUG_L2_TEIFSM))
+               return;
+       va_start(va, fmt);
+       printk(KERN_DEBUG "mgr(%d): ", mgr->ch.st->dev->id);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+static void
+da_activate(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+
+       if (fi->state == ST_L1_DEACT_PENDING)
+               mISDN_FsmDelTimer(&mgr->datimer, 1);
+       mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+}
+
+static void
+da_deactivate_ind(struct FsmInst *fi, int event, void *arg)
+{
+       mISDN_FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+da_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+       struct layer2   *l2;
+       u_long          flags;
+
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->l2m.state > ST_L2_4) {
+                       /* have still activ TEI */
+                       read_unlock_irqrestore(&mgr->lock, flags);
+                       return;
+               }
+       }
+       read_unlock_irqrestore(&mgr->lock, flags);
+       /* All TEI are inactiv */
+       mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 1);
+       mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING);
+}
+
+static void
+da_ui(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+
+       /* restart da timer */
+       mISDN_FsmDelTimer(&mgr->datimer, 2);
+       mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 2);
+
+}
+
+static void
+da_timer(struct FsmInst *fi, int event, void *arg)
+{
+       struct manager  *mgr = fi->userdata;
+       struct layer2   *l2;
+       u_long          flags;
+
+       /* check again */
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->l2m.state > ST_L2_4) {
+                       /* have still activ TEI */
+                       read_unlock_irqrestore(&mgr->lock, flags);
+                       mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+                       return;
+               }
+       }
+       read_unlock_irqrestore(&mgr->lock, flags);
+       /* All TEI are inactiv */
+       mISDN_FsmChangeState(fi, ST_L1_DEACT);
+       _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL,
+           GFP_ATOMIC);
+}
+
+static struct FsmNode DeactFnList[] =
+{
+       {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate},
+       {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind},
+       {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate},
+       {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate},
+       {ST_L1_DEACT_PENDING, EV_UI, da_ui},
+       {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer},
+};
+
+enum {
+       ST_TEI_NOP,
+       ST_TEI_IDREQ,
+       ST_TEI_IDVERIFY,
+};
+
+#define TEI_STATE_COUNT (ST_TEI_IDVERIFY+1)
+
+static char *strTeiState[] =
+{
+       "ST_TEI_NOP",
+       "ST_TEI_IDREQ",
+       "ST_TEI_IDVERIFY",
+};
+
+enum {
+       EV_IDREQ,
+       EV_ASSIGN,
+       EV_ASSIGN_REQ,
+       EV_DENIED,
+       EV_CHKREQ,
+       EV_CHKRESP,
+       EV_REMOVE,
+       EV_VERIFY,
+       EV_TIMER,
+};
+
+#define TEI_EVENT_COUNT (EV_TIMER+1)
+
+static char *strTeiEvent[] =
+{
+       "EV_IDREQ",
+       "EV_ASSIGN",
+       "EV_ASSIGN_REQ",
+       "EV_DENIED",
+       "EV_CHKREQ",
+       "EV_CHKRESP",
+       "EV_REMOVE",
+       "EV_VERIFY",
+       "EV_TIMER",
+};
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+       struct teimgr   *tm = fi->userdata;
+       va_list va;
+
+       if (!(*debug & DEBUG_L2_TEIFSM))
+               return;
+       va_start(va, fmt);
+       printk(KERN_DEBUG "tei(%d): ", tm->l2->tei);
+       vprintk(fmt, va);
+       printk("\n");
+       va_end(va);
+}
+
+
+
+static int
+get_free_id(struct manager *mgr)
+{
+       u64             ids = 0;
+       int             i;
+       struct layer2   *l2;
+
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->ch.nr > 63) {
+                       printk(KERN_WARNING
+                           "%s: more as 63 layer2 for one device\n",
+                           __func__);
+                       return -EBUSY;
+               }
+               test_and_set_bit(l2->ch.nr, (u_long *)&ids);
+       }
+       for (i = 1; i < 64; i++)
+               if (!test_bit(i, (u_long *)&ids))
+                       return i;
+       printk(KERN_WARNING "%s: more as 63 layer2 for one device\n",
+           __func__);
+       return -EBUSY;
+}
+
+static int
+get_free_tei(struct manager *mgr)
+{
+       u64             ids = 0;
+       int             i;
+       struct layer2   *l2;
+
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if (l2->ch.nr == 0)
+                       continue;
+               if ((l2->ch.addr & 0xff) != 0)
+                       continue;
+               i = l2->ch.addr >> 8;
+               if (i < 64)
+                       continue;
+               i -= 64;
+
+               test_and_set_bit(i, (u_long *)&ids);
+       }
+       for (i = 0; i < 64; i++)
+               if (!test_bit(i, (u_long *)&ids))
+                       return i + 64;
+       printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n",
+           __func__);
+       return -1;
+}
+
+static void
+teiup_create(struct manager *mgr, u_int prim, int len, void *arg)
+{
+       struct sk_buff  *skb;
+       struct mISDNhead *hh;
+       int             err;
+
+       skb = mI_alloc_skb(len, GFP_ATOMIC);
+       if (!skb)
+               return;
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = (mgr->ch.nr << 16) | mgr->ch.addr;
+       if (len)
+               memcpy(skb_put(skb, len), arg, len);
+       err = mgr->up->send(mgr->up, skb);
+       if (err) {
+               printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+               dev_kfree_skb(skb);
+       }
+}
+
+static u_int
+new_id(struct manager *mgr)
+{
+       u_int   id;
+
+       id = mgr->nextid++;
+       if (id == 0x7fff)
+               mgr->nextid = 1;
+       id <<= 16;
+       id |= GROUP_TEI << 8;
+       id |= TEI_SAPI;
+       return id;
+}
+
+static void
+do_send(struct manager *mgr)
+{
+       if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+               return;
+
+       if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) {
+               struct sk_buff  *skb = skb_dequeue(&mgr->sendq);
+
+               if (!skb) {
+                       test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+                       return;
+               }
+               mgr->lastid = mISDN_HEAD_ID(skb);
+               mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+               if (mgr->ch.recv(mgr->ch.peer, skb)) {
+                       dev_kfree_skb(skb);
+                       test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+                       mgr->lastid = MISDN_ID_NONE;
+               }
+       }
+}
+
+static void
+do_ack(struct manager *mgr, u_int id)
+{
+       if (test_bit(MGR_PH_NOTREADY, &mgr->options)) {
+               if (id == mgr->lastid) {
+                       if (test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+                               struct sk_buff  *skb;
+
+                               skb = skb_dequeue(&mgr->sendq);
+                               if (skb) {
+                                       mgr->lastid = mISDN_HEAD_ID(skb);
+                                       if (!mgr->ch.recv(mgr->ch.peer, skb))
+                                               return;
+                                       dev_kfree_skb(skb);
+                               }
+                       }
+                       mgr->lastid = MISDN_ID_NONE;
+                       test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+               }
+       }
+}
+
+static void
+mgr_send_down(struct manager *mgr, struct sk_buff *skb)
+{
+       skb_queue_tail(&mgr->sendq, skb);
+       if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+               _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+                   NULL, GFP_KERNEL);
+       } else {
+               do_send(mgr);
+       }
+}
+
+static int
+dl_unit_data(struct manager *mgr, struct sk_buff *skb)
+{
+       if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */
+               return -EINVAL;
+       if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+               _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+                   NULL, GFP_KERNEL);
+       skb_push(skb, 3);
+       skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */
+       skb->data[1] = 0xff; /* TEI 127 */
+       skb->data[2] = UI;   /* UI frame */
+       mISDN_HEAD_PRIM(skb) = PH_DATA_REQ;
+       mISDN_HEAD_ID(skb) = new_id(mgr);
+       skb_queue_tail(&mgr->sendq, skb);
+       do_send(mgr);
+       return 0;
+}
+
+unsigned int
+random_ri(void)
+{
+       u16 x;
+
+       get_random_bytes(&x, sizeof(x));
+       return x;
+}
+
+static struct layer2 *
+findtei(struct manager *mgr, int tei)
+{
+       struct layer2   *l2;
+       u_long          flags;
+
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if ((l2->sapi == 0) && (l2->tei > 0) &&
+                   (l2->tei != GROUP_TEI) && (l2->tei == tei))
+                       goto done;
+       }
+       l2 = NULL;
+done:
+       read_unlock_irqrestore(&mgr->lock, flags);
+       return l2;
+}
+
+static void
+put_tei_msg(struct manager *mgr, u_char m_id, unsigned int ri, u_char tei)
+{
+       struct sk_buff *skb;
+       u_char bp[8];
+
+       bp[0] = (TEI_SAPI << 2);
+       if (test_bit(MGR_OPT_NETWORK, &mgr->options))
+               bp[0] |= 2; /* CR:=1 for net command */
+       bp[1] = (GROUP_TEI << 1) | 0x1;
+       bp[2] = UI;
+       bp[3] = TEI_ENTITY_ID;
+       bp[4] = ri >> 8;
+       bp[5] = ri & 0xff;
+       bp[6] = m_id;
+       bp[7] = (tei << 1) | 1;
+       skb = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr),
+           8, bp, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_WARNING "%s: no skb for tei msg\n", __func__);
+               return;
+       }
+       mgr_send_down(mgr, skb);
+}
+
+static void
+tei_id_request(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (tm->l2->tei != GROUP_TEI) {
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "assign request for allready assigned tei %d",
+                       tm->l2->tei);
+               return;
+       }
+       tm->ri = random_ri();
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "assign request ri %d", tm->ri);
+       put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+       mISDN_FsmChangeState(fi, ST_TEI_IDREQ);
+       mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1);
+       tm->nval = 3;
+}
+
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr   *tm = fi->userdata;
+       struct layer2   *l2;
+       u_char *dp = arg;
+       int ri, tei;
+
+       ri = ((unsigned int) *dp++ << 8);
+       ri += *dp++;
+       dp++;
+       tei = *dp >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity assign ri %d tei %d",
+                       ri, tei);
+       l2 = findtei(tm->mgr, tei);
+       if (l2) {       /* same tei is in use */
+               if (ri != l2->tm->ri) {
+                       tm->tei_m.printdebug(fi,
+                               "possible duplicate assignment tei %d", tei);
+                       tei_l2(l2, MDL_ERROR_RSP, 0);
+               }
+       } else if (ri == tm->ri) {
+               mISDN_FsmDelTimer(&tm->timer, 1);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+               tei_l2(tm->l2, MDL_ASSIGN_REQ, tei);
+       }
+}
+
+static void
+tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr   *tm = fi->userdata;
+       struct layer2   *l2;
+       u_char *dp = arg;
+       int tei, ri;
+
+       ri = ((unsigned int) *dp++ << 8);
+       ri += *dp++;
+       dp++;
+       tei = *dp >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d",
+                       ri, tei);
+       l2 = findtei(tm->mgr, tei);
+       if (l2) {       /* same tei is in use */
+               if (ri != l2->tm->ri) { /* and it wasn't our request */
+                       tm->tei_m.printdebug(fi,
+                               "possible duplicate assignment tei %d", tei);
+                       mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL);
+               }
+       }
+}
+
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int ri, tei;
+
+       ri = ((unsigned int) *dp++ << 8);
+       ri += *dp++;
+       dp++;
+       tei = *dp >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity denied ri %d tei %d",
+                       ri, tei);
+}
+
+static void
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = *(dp+3) >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity check req tei %d", tei);
+       if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) ||
+           (tei == tm->l2->tei))) {
+               mISDN_FsmDelTimer(&tm->timer, 4);
+               mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+               put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei);
+       }
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = *(dp+3) >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity remove tei %d", tei);
+       if ((tm->l2->tei != GROUP_TEI) &&
+           ((tei == GROUP_TEI) || (tei == tm->l2->tei))) {
+               mISDN_FsmDelTimer(&tm->timer, 5);
+               mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+               tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+       }
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "id verify request for tei %d",
+                       tm->l2->tei);
+       put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+       mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+       mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+       tm->nval = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (--tm->nval) {
+               tm->ri = random_ri();
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi, "assign req(%d) ri %d",
+                               4 - tm->nval, tm->ri);
+               put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+               mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3);
+       } else {
+               tm->tei_m.printdebug(fi, "assign req failed");
+               tei_l2(tm->l2, MDL_ERROR_RSP, 0);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+       }
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (--tm->nval) {
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi,
+                               "id verify req(%d) for tei %d",
+                               3 - tm->nval, tm->l2->tei);
+               put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+               mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+       } else {
+               tm->tei_m.printdebug(fi, "verify req for tei %d failed",
+                       tm->l2->tei);
+               tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+       }
+}
+
+static struct FsmNode TeiFnListUser[] =
+{
+       {ST_TEI_NOP, EV_IDREQ, tei_id_request},
+       {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
+       {ST_TEI_NOP, EV_VERIFY, tei_id_verify},
+       {ST_TEI_NOP, EV_REMOVE, tei_id_remove},
+       {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req},
+       {ST_TEI_IDREQ, EV_TIMER, tei_id_req_tout},
+       {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
+       {ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
+       {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout},
+       {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
+       {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
+};
+
+static void
+tei_l2remove(struct layer2 *l2)
+{
+       put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei);
+       tei_l2(l2, MDL_REMOVE_REQ, 0);
+       list_del(&l2->ch.list);
+       l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+}
+
+static void
+tei_assign_req(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+
+       if (tm->l2->tei == GROUP_TEI) {
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "net tei assign request without tei");
+               return;
+       }
+       tm->ri = ((unsigned int) *dp++ << 8);
+       tm->ri += *dp++;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(&tm->tei_m,
+                       "net assign request ri %d teim %d", tm->ri, *dp);
+       put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei);
+       mISDN_FsmChangeState(fi, ST_TEI_NOP);
+}
+
+static void
+tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr   *tm = fi->userdata;
+
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "id check request for tei %d",
+                   tm->l2->tei);
+       tm->rcnt = 0;
+       put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+       mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+       mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+       tm->nval = 2;
+}
+
+static void
+tei_id_chk_resp(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = dp[3] >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity check resp tei %d", tei);
+       if (tei == tm->l2->tei)
+               tm->rcnt++;
+}
+
+static void
+tei_id_verify_net(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+       u_char *dp = arg;
+       int tei;
+
+       tei = dp[3] >> 1;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(fi, "identity verify req tei %d/%d",
+                   tei, tm->l2->tei);
+       if (tei == tm->l2->tei)
+               tei_id_chk_req_net(fi, event, arg);
+}
+
+static void
+tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg)
+{
+       struct teimgr *tm = fi->userdata;
+
+       if (tm->rcnt == 1) {
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi,
+                           "check req for tei %d sucessful\n", tm->l2->tei);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+       } else if (tm->rcnt > 1) {
+               /* duplicate assignment; remove */
+               tei_l2remove(tm->l2);
+       } else if (--tm->nval) {
+               if (*debug & DEBUG_L2_TEI)
+                       tm->tei_m.printdebug(fi,
+                               "id check req(%d) for tei %d",
+                               3 - tm->nval, tm->l2->tei);
+               put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+               mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+       } else {
+               tm->tei_m.printdebug(fi, "check req for tei %d failed",
+                       tm->l2->tei);
+               mISDN_FsmChangeState(fi, ST_TEI_NOP);
+               tei_l2remove(tm->l2);
+       }
+}
+
+static struct FsmNode TeiFnListNet[] =
+{
+       {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req},
+       {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net},
+       {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net},
+       {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net},
+       {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp},
+};
+
+static void
+tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len)
+{
+       if (test_bit(FLG_FIXED_TEI, &tm->l2->flag))
+               return;
+       if (*debug & DEBUG_L2_TEI)
+               tm->tei_m.printdebug(&tm->tei_m, "tei handler mt %x", mt);
+       if (mt == ID_ASSIGNED)
+               mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN, dp);
+       else if (mt == ID_DENIED)
+               mISDN_FsmEvent(&tm->tei_m, EV_DENIED, dp);
+       else if (mt == ID_CHK_REQ)
+               mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, dp);
+       else if (mt == ID_REMOVE)
+               mISDN_FsmEvent(&tm->tei_m, EV_REMOVE, dp);
+       else if (mt == ID_VERIFY)
+               mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp);
+       else if (mt == ID_CHK_RES)
+               mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp);
+}
+
+static struct layer2 *
+create_new_tei(struct manager *mgr, int tei)
+{
+       u_long          opt = 0;
+       u_long          flags;
+       int             id;
+       struct layer2   *l2;
+
+       if (!mgr->up)
+               return NULL;
+       if (tei < 64)
+               test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+       if (mgr->ch.st->dev->Dprotocols
+         & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+               test_and_set_bit(OPTION_L2_PMX, &opt);
+       l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, (u_int)opt, (u_long)tei);
+       if (!l2) {
+               printk(KERN_WARNING "%s:no memory for layer2\n", __func__);
+               return NULL;
+       }
+       l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+       if (!l2->tm) {
+               kfree(l2);
+               printk(KERN_WARNING "%s:no memory for teimgr\n", __func__);
+               return NULL;
+       }
+       l2->tm->mgr = mgr;
+       l2->tm->l2 = l2;
+       l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+       l2->tm->tei_m.userdata = l2->tm;
+       l2->tm->tei_m.printdebug = tei_debug;
+       l2->tm->tei_m.fsm = &teifsmn;
+       l2->tm->tei_m.state = ST_TEI_NOP;
+       l2->tm->tval = 2000; /* T202  2 sec */
+       mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+       write_lock_irqsave(&mgr->lock, flags);
+       id = get_free_id(mgr);
+       list_add_tail(&l2->list, &mgr->layer2);
+       write_unlock_irqrestore(&mgr->lock, flags);
+       if (id < 0) {
+               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+               printk(KERN_WARNING "%s:no free id\n", __func__);
+               return NULL;
+       } else {
+               l2->ch.nr = id;
+               __add_layer2(&l2->ch, mgr->ch.st);
+               l2->ch.recv = mgr->ch.recv;
+               l2->ch.peer = mgr->ch.peer;
+               l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+       }
+       return l2;
+}
+
+static void
+new_tei_req(struct manager *mgr, u_char *dp)
+{
+       int             tei, ri;
+       struct layer2   *l2;
+
+       ri = dp[0] << 8;
+       ri += dp[1];
+       if (!mgr->up)
+               goto denied;
+       tei = get_free_tei(mgr);
+       if (tei < 0) {
+               printk(KERN_WARNING "%s:No free tei\n", __func__);
+               goto denied;
+       }
+       l2 = create_new_tei(mgr, tei);
+       if (!l2)
+               goto denied;
+       else
+               mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp);
+       return;
+denied:
+       put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI);
+}
+
+static int
+ph_data_ind(struct manager *mgr, struct sk_buff *skb)
+{
+       int             ret = -EINVAL;
+       struct layer2   *l2;
+       u_long          flags;
+       u_char          mt;
+
+       if (skb->len < 8) {
+               if (*debug  & DEBUG_L2_TEI)
+                       printk(KERN_DEBUG "%s: short mgr frame %d/8\n",
+                           __func__, skb->len);
+               goto done;
+       }
+       if (*debug  & DEBUG_L2_TEI)
+
+       if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */
+               goto done;
+       if (skb->data[0] & 1) /* EA0 formal error */
+               goto done;
+       if (!(skb->data[1] & 1)) /* EA1 formal error */
+               goto done;
+       if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */
+               goto done;
+       if ((skb->data[2] & 0xef) != UI) /* not UI */
+               goto done;
+       if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */
+               goto done;
+       mt = skb->data[6];
+       switch (mt) {
+       case ID_REQUEST:
+       case ID_CHK_RES:
+       case ID_VERIFY:
+               if (!test_bit(MGR_OPT_NETWORK, &mgr->options))
+                       goto done;
+               break;
+       case ID_ASSIGNED:
+       case ID_DENIED:
+       case ID_CHK_REQ:
+       case ID_REMOVE:
+               if (test_bit(MGR_OPT_NETWORK, &mgr->options))
+                       goto done;
+               break;
+       default:
+               goto done;
+       }
+       ret = 0;
+       if (mt == ID_REQUEST) {
+               new_tei_req(mgr, &skb->data[4]);
+               goto done;
+       }
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4);
+       }
+       read_unlock_irqrestore(&mgr->lock, flags);
+done:
+       return ret;
+}
+
+int
+l2_tei(struct layer2 *l2, u_int cmd, u_long arg)
+{
+       struct teimgr   *tm = l2->tm;
+
+       if (test_bit(FLG_FIXED_TEI, &l2->flag))
+               return 0;
+       if (*debug & DEBUG_L2_TEI)
+               printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+       switch (cmd) {
+       case MDL_ASSIGN_IND:
+               mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL);
+               break;
+       case MDL_ERROR_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei);
+               if (test_bit(MGR_OPT_USER, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL);
+               break;
+       case MDL_STATUS_UP_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL);
+               break;
+       case MDL_STATUS_DOWN_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL);
+               break;
+       case MDL_STATUS_UI_IND:
+               if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+                       mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL);
+               break;
+       }
+       return 0;
+}
+
+void
+release_tei(struct layer2 *l2)
+{
+       struct teimgr   *tm = l2->tm;
+       u_long          flags;
+
+       mISDN_FsmDelTimer(&tm->timer, 1);
+       write_lock_irqsave(&tm->mgr->lock, flags);
+       list_del(&l2->list);
+       write_unlock_irqrestore(&tm->mgr->lock, flags);
+       l2->tm = NULL;
+       kfree(tm);
+}
+
+static int
+create_teimgr(struct manager *mgr, struct channel_req *crq)
+{
+       struct layer2   *l2;
+       u_long          opt = 0;
+       u_long          flags;
+       int             id;
+
+       if (*debug & DEBUG_L2_TEI)
+               printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+                       __func__, mgr->ch.st->dev->name, crq->protocol,
+                       crq->adr.dev, crq->adr.channel, crq->adr.sapi,
+                       crq->adr.tei);
+       if (crq->adr.sapi != 0) /* not supported yet */
+               return -EINVAL;
+       if (crq->adr.tei > GROUP_TEI)
+               return -EINVAL;
+       if (crq->adr.tei < 64)
+               test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+       if (crq->adr.tei == 0)
+               test_and_set_bit(OPTION_L2_PTP, &opt);
+       if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+               if (crq->protocol == ISDN_P_LAPD_TE)
+                       return -EPROTONOSUPPORT;
+               if ((crq->adr.tei != 0) && (crq->adr.tei != 127))
+                       return -EINVAL;
+               if (mgr->up) {
+                       printk(KERN_WARNING
+                           "%s: only one network manager is allowed\n",
+                           __func__);
+                       return -EBUSY;
+               }
+       } else if (test_bit(MGR_OPT_USER, &mgr->options)) {
+               if (crq->protocol == ISDN_P_LAPD_NT)
+                       return -EPROTONOSUPPORT;
+               if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI))
+                       return -EINVAL; /* dyn tei */
+       } else {
+               if (crq->protocol == ISDN_P_LAPD_NT)
+                       test_and_set_bit(MGR_OPT_NETWORK, &mgr->options);
+               if (crq->protocol == ISDN_P_LAPD_TE)
+                       test_and_set_bit(MGR_OPT_USER, &mgr->options);
+       }
+       if (mgr->ch.st->dev->Dprotocols
+         & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+               test_and_set_bit(OPTION_L2_PMX, &opt);
+       if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) {
+               mgr->up = crq->ch;
+               id = DL_INFO_L2_CONNECT;
+               teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id);
+               crq->ch = NULL;
+               if (!list_empty(&mgr->layer2)) {
+                       read_lock_irqsave(&mgr->lock, flags);
+                       list_for_each_entry(l2, &mgr->layer2, list) {
+                               l2->up = mgr->up;
+                               l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+                       }
+                       read_unlock_irqrestore(&mgr->lock, flags);
+               }
+               return 0;
+       }
+       l2 = create_l2(crq->ch, crq->protocol, (u_int)opt,
+               (u_long)crq->adr.tei);
+       if (!l2)
+               return -ENOMEM;
+       l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+       if (!l2->tm) {
+               kfree(l2);
+               printk(KERN_ERR "kmalloc teimgr failed\n");
+               return -ENOMEM;
+       }
+       l2->tm->mgr = mgr;
+       l2->tm->l2 = l2;
+       l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+       l2->tm->tei_m.userdata = l2->tm;
+       l2->tm->tei_m.printdebug = tei_debug;
+       if (crq->protocol == ISDN_P_LAPD_TE) {
+               l2->tm->tei_m.fsm = &teifsmu;
+               l2->tm->tei_m.state = ST_TEI_NOP;
+               l2->tm->tval = 1000; /* T201  1 sec */
+       } else {
+               l2->tm->tei_m.fsm = &teifsmn;
+               l2->tm->tei_m.state = ST_TEI_NOP;
+               l2->tm->tval = 2000; /* T202  2 sec */
+       }
+       mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+       write_lock_irqsave(&mgr->lock, flags);
+       id = get_free_id(mgr);
+       list_add_tail(&l2->list, &mgr->layer2);
+       write_unlock_irqrestore(&mgr->lock, flags);
+       if (id < 0) {
+               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+       } else {
+               l2->ch.nr = id;
+               l2->up->nr = id;
+               crq->ch = &l2->ch;
+               id = 0;
+       }
+       return id;
+}
+
+static int
+mgr_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct manager  *mgr;
+       struct mISDNhead        *hh =  mISDN_HEAD_P(skb);
+       int                     ret = -EINVAL;
+
+       mgr = container_of(ch, struct manager, ch);
+       if (*debug & DEBUG_L2_RECV)
+               printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+                   __func__, hh->prim, hh->id);
+       switch (hh->prim) {
+       case PH_DATA_IND:
+               mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+               ret = ph_data_ind(mgr, skb);
+               break;
+       case PH_DATA_CNF:
+               do_ack(mgr, hh->id);
+               ret = 0;
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(MGR_PH_ACTIVE, &mgr->options);
+               mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL);
+               do_send(mgr);
+               ret = 0;
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options);
+               mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL);
+               ret = 0;
+               break;
+       case DL_UNITDATA_REQ:
+               return dl_unit_data(mgr, skb);
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+free_teimanager(struct manager *mgr)
+{
+       struct layer2   *l2, *nl2;
+
+       if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+               /* not locked lock is taken in release tei */
+               mgr->up = NULL;
+               if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) {
+                       list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+                               put_tei_msg(mgr, ID_REMOVE, 0, l2->tei);
+                               mutex_lock(&mgr->ch.st->lmutex);
+                               list_del(&l2->ch.list);
+                               mutex_unlock(&mgr->ch.st->lmutex);
+                               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+                       }
+                       test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options);
+               } else {
+                       list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+                               l2->up = NULL;
+                       }
+               }
+       }
+       if (test_bit(MGR_OPT_USER, &mgr->options)) {
+               if (list_empty(&mgr->layer2))
+                       test_and_clear_bit(MGR_OPT_USER, &mgr->options);
+       }
+       mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL);
+       return 0;
+}
+
+static int
+ctrl_teimanager(struct manager *mgr, void *arg)
+{
+       /* currently we only have one option */
+       int     clean = *((int *)arg);
+
+       if (clean)
+               test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options);
+       else
+               test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options);
+       return 0;
+}
+
+/* This function does create a L2 for fixed TEI in NT Mode */
+static int
+check_data(struct manager *mgr, struct sk_buff *skb)
+{
+       struct mISDNhead        *hh =  mISDN_HEAD_P(skb);
+       int                     ret, tei;
+       struct layer2           *l2;
+
+       if (*debug & DEBUG_L2_CTRL)
+               printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+                   __func__, hh->prim, hh->id);
+       if (test_bit(MGR_OPT_USER, &mgr->options))
+               return -ENOTCONN;
+       if (hh->prim != PH_DATA_IND)
+               return -ENOTCONN;
+       if (skb->len != 3)
+               return -ENOTCONN;
+       if (skb->data[0] != 0)
+               /* only SAPI 0 command */
+               return -ENOTCONN;
+       if (!(skb->data[1] & 1)) /* invalid EA1 */
+               return -EINVAL;
+       tei = skb->data[1] >> 0;
+       if (tei > 63) /* not a fixed tei */
+               return -ENOTCONN;
+       if ((skb->data[2] & ~0x10) != SABME)
+               return -ENOTCONN;
+       /* We got a SABME for a fixed TEI */
+       l2 = create_new_tei(mgr, tei);
+       if (!l2)
+               return -ENOMEM;
+       ret = l2->ch.send(&l2->ch, skb);
+       return ret;
+}
+
+void
+delete_teimanager(struct mISDNchannel *ch)
+{
+       struct manager  *mgr;
+       struct layer2   *l2, *nl2;
+
+       mgr = container_of(ch, struct manager, ch);
+       /* not locked lock is taken in release tei */
+       list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+               mutex_lock(&mgr->ch.st->lmutex);
+               list_del(&l2->ch.list);
+               mutex_unlock(&mgr->ch.st->lmutex);
+               l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+       }
+       list_del(&mgr->ch.list);
+       list_del(&mgr->bcast.list);
+       skb_queue_purge(&mgr->sendq);
+       kfree(mgr);
+}
+
+static int
+mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+       struct manager  *mgr;
+       int             ret = -EINVAL;
+
+       mgr = container_of(ch, struct manager, ch);
+       if (*debug & DEBUG_L2_CTRL)
+               printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               ret = create_teimgr(mgr, arg);
+               break;
+       case CLOSE_CHANNEL:
+               ret = free_teimanager(mgr);
+               break;
+       case CONTROL_CHANNEL:
+               ret = ctrl_teimanager(mgr, arg);
+               break;
+       case CHECK_DATA:
+               ret = check_data(mgr, arg);
+               break;
+       }
+       return ret;
+}
+
+static int
+mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct manager          *mgr = container_of(ch, struct manager, bcast);
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       struct sk_buff          *cskb = NULL;
+       struct layer2           *l2;
+       u_long                  flags;
+       int                     ret;
+
+       read_lock_irqsave(&mgr->lock, flags);
+       list_for_each_entry(l2, &mgr->layer2, list) {
+               if ((hh->id & MISDN_ID_SAPI_MASK) ==
+                   (l2->ch.addr & MISDN_ID_SAPI_MASK)) {
+                       if (list_is_last(&l2->list, &mgr->layer2)) {
+                               cskb = skb;
+                               skb = NULL;
+                       } else {
+                               if (!cskb)
+                                       cskb = skb_copy(skb, GFP_KERNEL);
+                       }
+                       if (cskb) {
+                               ret = l2->ch.send(&l2->ch, cskb);
+                               if (ret) {
+                                       if (*debug & DEBUG_SEND_ERR)
+                                               printk(KERN_DEBUG
+                                                   "%s ch%d prim(%x) addr(%x)"
+                                                   " err %d\n",
+                                                   __func__, l2->ch.nr,
+                                                   hh->prim, l2->ch.addr, ret);
+                               } else
+                                       cskb = NULL;
+                       } else {
+                               printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+                                   __func__, ch->nr, ch->addr);
+                               goto out;
+                       }
+               }
+       }
+out:
+       read_unlock_irqrestore(&mgr->lock, flags);
+       if (cskb)
+               dev_kfree_skb(cskb);
+       if (skb)
+               dev_kfree_skb(skb);
+       return 0;
+}
+
+static int
+mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+
+       return -EINVAL;
+}
+
+int
+create_teimanager(struct mISDNdevice *dev)
+{
+       struct manager *mgr;
+
+       mgr = kzalloc(sizeof(struct manager), GFP_KERNEL);
+       if (!mgr)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&mgr->layer2);
+       mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock);
+       skb_queue_head_init(&mgr->sendq);
+       mgr->nextid = 1;
+       mgr->lastid = MISDN_ID_NONE;
+       mgr->ch.send = mgr_send;
+       mgr->ch.ctrl = mgr_ctrl;
+       mgr->ch.st = dev->D.st;
+       set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI);
+       add_layer2(&mgr->ch, dev->D.st);
+       mgr->bcast.send = mgr_bcast;
+       mgr->bcast.ctrl = mgr_bcast_ctrl;
+       mgr->bcast.st = dev->D.st;
+       set_channel_address(&mgr->bcast, 0, GROUP_TEI);
+       add_layer2(&mgr->bcast, dev->D.st);
+       mgr->deact.debug = *debug & DEBUG_MANAGER;
+       mgr->deact.userdata = mgr;
+       mgr->deact.printdebug = da_debug;
+       mgr->deact.fsm = &deactfsm;
+       mgr->deact.state = ST_L1_DEACT;
+       mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer);
+       dev->teimgr = &mgr->ch;
+       return 0;
+}
+
+int TEIInit(u_int *deb)
+{
+       debug = deb;
+       teifsmu.state_count = TEI_STATE_COUNT;
+       teifsmu.event_count = TEI_EVENT_COUNT;
+       teifsmu.strEvent = strTeiEvent;
+       teifsmu.strState = strTeiState;
+       mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser));
+       teifsmn.state_count = TEI_STATE_COUNT;
+       teifsmn.event_count = TEI_EVENT_COUNT;
+       teifsmn.strEvent = strTeiEvent;
+       teifsmn.strState = strTeiState;
+       mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet));
+       deactfsm.state_count =  DEACT_STATE_COUNT;
+       deactfsm.event_count = DEACT_EVENT_COUNT;
+       deactfsm.strEvent = strDeactEvent;
+       deactfsm.strState = strDeactState;
+       mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList));
+       return 0;
+}
+
+void TEIFree(void)
+{
+       mISDN_FsmFree(&teifsmu);
+       mISDN_FsmFree(&teifsmn);
+       mISDN_FsmFree(&deactfsm);
+}
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
new file mode 100644 (file)
index 0000000..b5fabc7
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ *
+ * general timer device for using in ISDN stacks
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mISDNif.h>
+
+static int     *debug;
+
+
+struct mISDNtimerdev {
+       int                     next_id;
+       struct list_head        pending;
+       struct list_head        expired;
+       wait_queue_head_t       wait;
+       u_int                   work;
+       spinlock_t              lock; /* protect lists */
+};
+
+struct mISDNtimer {
+       struct list_head        list;
+       struct  mISDNtimerdev   *dev;
+       struct timer_list       tl;
+       int                     id;
+};
+
+static int
+mISDN_open(struct inode *ino, struct file *filep)
+{
+       struct mISDNtimerdev    *dev;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+       dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+       dev->next_id = 1;
+       INIT_LIST_HEAD(&dev->pending);
+       INIT_LIST_HEAD(&dev->expired);
+       spin_lock_init(&dev->lock);
+       dev->work = 0;
+       init_waitqueue_head(&dev->wait);
+       filep->private_data = dev;
+       __module_get(THIS_MODULE);
+       return 0;
+}
+
+static int
+mISDN_close(struct inode *ino, struct file *filep)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       struct mISDNtimer       *timer, *next;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+       list_for_each_entry_safe(timer, next, &dev->pending, list) {
+               del_timer(&timer->tl);
+               kfree(timer);
+       }
+       list_for_each_entry_safe(timer, next, &dev->expired, list) {
+               kfree(timer);
+       }
+       kfree(dev);
+       module_put(THIS_MODULE);
+       return 0;
+}
+
+static ssize_t
+mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       struct mISDNtimer       *timer;
+       u_long  flags;
+       int     ret = 0;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
+                       filep, buf, (int)count, off);
+       if (*off != filep->f_pos)
+               return -ESPIPE;
+
+       if (list_empty(&dev->expired) && (dev->work == 0)) {
+               if (filep->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               wait_event_interruptible(dev->wait, (dev->work ||
+                   !list_empty(&dev->expired)));
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+       }
+       if (count < sizeof(int))
+               return -ENOSPC;
+       if (dev->work)
+               dev->work = 0;
+       if (!list_empty(&dev->expired)) {
+               spin_lock_irqsave(&dev->lock, flags);
+               timer = (struct mISDNtimer *)dev->expired.next;
+               list_del(&timer->list);
+               spin_unlock_irqrestore(&dev->lock, flags);
+               if (put_user(timer->id, (int *)buf))
+                       ret = -EFAULT;
+               else
+                       ret = sizeof(int);
+               kfree(timer);
+       }
+       return ret;
+}
+
+static loff_t
+mISDN_llseek(struct file *filep, loff_t offset, int orig)
+{
+       return -ESPIPE;
+}
+
+static ssize_t
+mISDN_write(struct file *filep, const char *buf, size_t count, loff_t *off)
+{
+       return -EOPNOTSUPP;
+}
+
+static unsigned int
+mISDN_poll(struct file *filep, poll_table *wait)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       unsigned int            mask = POLLERR;
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
+       if (dev) {
+               poll_wait(filep, &dev->wait, wait);
+               mask = 0;
+               if (dev->work || !list_empty(&dev->expired))
+                       mask |= (POLLIN | POLLRDNORM);
+               if (*debug & DEBUG_TIMER)
+                       printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
+                               dev->work, list_empty(&dev->expired));
+       }
+       return mask;
+}
+
+static void
+dev_expire_timer(struct mISDNtimer *timer)
+{
+       u_long                  flags;
+
+       spin_lock_irqsave(&timer->dev->lock, flags);
+       list_del(&timer->list);
+       list_add_tail(&timer->list, &timer->dev->expired);
+       spin_unlock_irqrestore(&timer->dev->lock, flags);
+       wake_up_interruptible(&timer->dev->wait);
+}
+
+static int
+misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
+{
+       int                     id;
+       u_long                  flags;
+       struct mISDNtimer       *timer;
+
+       if (!timeout) {
+               dev->work = 1;
+               wake_up_interruptible(&dev->wait);
+               id = 0;
+       } else {
+               timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
+               if (!timer)
+                       return -ENOMEM;
+               spin_lock_irqsave(&dev->lock, flags);
+               timer->id = dev->next_id++;
+               if (dev->next_id < 0)
+                       dev->next_id = 1;
+               list_add_tail(&timer->list, &dev->pending);
+               spin_unlock_irqrestore(&dev->lock, flags);
+               timer->dev = dev;
+               timer->tl.data = (long)timer;
+               timer->tl.function = (void *) dev_expire_timer;
+               init_timer(&timer->tl);
+               timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
+               add_timer(&timer->tl);
+               id = timer->id;
+       }
+       return id;
+}
+
+static int
+misdn_del_timer(struct mISDNtimerdev *dev, int id)
+{
+       u_long                  flags;
+       struct mISDNtimer       *timer;
+       int                     ret = 0;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       list_for_each_entry(timer, &dev->pending, list) {
+               if (timer->id == id) {
+                       list_del_init(&timer->list);
+                       del_timer(&timer->tl);
+                       ret = timer->id;
+                       kfree(timer);
+                       goto unlock;
+               }
+       }
+unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int
+mISDN_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+    unsigned long arg)
+{
+       struct mISDNtimerdev    *dev = filep->private_data;
+       int                     id, tout, ret = 0;
+
+
+       if (*debug & DEBUG_TIMER)
+               printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
+                   filep, cmd, arg);
+       switch (cmd) {
+       case IMADDTIMER:
+               if (get_user(tout, (int __user *)arg)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               id = misdn_add_timer(dev, tout);
+               if (*debug & DEBUG_TIMER)
+                       printk(KERN_DEBUG "%s add %d id %d\n", __func__,
+                           tout, id);
+               if (id < 0) {
+                       ret = id;
+                       break;
+               }
+               if (put_user(id, (int __user *)arg))
+                       ret = -EFAULT;
+               break;
+       case IMDELTIMER:
+               if (get_user(id, (int __user *)arg)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               if (*debug & DEBUG_TIMER)
+                       printk(KERN_DEBUG "%s del id %d\n", __func__, id);
+               id = misdn_del_timer(dev, id);
+               if (put_user(id, (int __user *)arg))
+                       ret = -EFAULT;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static struct file_operations mISDN_fops = {
+       .llseek         = mISDN_llseek,
+       .read           = mISDN_read,
+       .write          = mISDN_write,
+       .poll           = mISDN_poll,
+       .ioctl          = mISDN_ioctl,
+       .open           = mISDN_open,
+       .release        = mISDN_close,
+};
+
+static struct miscdevice mISDNtimer = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "mISDNtimer",
+       .fops   = &mISDN_fops,
+};
+
+int
+mISDN_inittimer(int *deb)
+{
+       int     err;
+
+       debug = deb;
+       err = misc_register(&mISDNtimer);
+       if (err)
+               printk(KERN_WARNING "mISDN: Could not register timer device\n");
+       return err;
+}
+
+void mISDN_timer_cleanup(void)
+{
+       misc_deregister(&mISDNtimer);
+}
index c7cc760a1777f96fad3ba631c76df774e4579adf..af251a5df844929097ef67adb394936871bbec3b 100644 (file)
@@ -814,7 +814,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
        }
 
        /* release skb */
-       BUG_TRAP(skb);
+       WARN_ON(!skb);
        dev_kfree_skb(skb);
        tx_buf->first_bd = 0;
        tx_buf->skb = NULL;
@@ -837,9 +837,9 @@ static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp)
        used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS;
 
 #ifdef BNX2X_STOP_ON_ERROR
-       BUG_TRAP(used >= 0);
-       BUG_TRAP(used <= fp->bp->tx_ring_size);
-       BUG_TRAP((fp->bp->tx_ring_size - used) <= MAX_TX_AVAIL);
+       WARN_ON(used < 0);
+       WARN_ON(used > fp->bp->tx_ring_size);
+       WARN_ON((fp->bp->tx_ring_size - used) > MAX_TX_AVAIL);
 #endif
 
        return (s16)(fp->bp->tx_ring_size) - used;
@@ -4374,7 +4374,7 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp)
                        }
                        ring_prod = NEXT_RX_IDX(ring_prod);
                        cqe_ring_prod = NEXT_RCQ_IDX(cqe_ring_prod);
-                       BUG_TRAP(ring_prod > i);
+                       WARN_ON(ring_prod <= i);
                }
 
                fp->rx_bd_prod = ring_prod;
index c3ad89e302bd1ae6695e0e377faf21b4a3fa4008..cebb25e36e8238c8217d7db0204a6e8ccd221ca5 100644 (file)
@@ -3321,7 +3321,7 @@ int qeth_change_mtu(struct net_device *dev, int new_mtu)
        struct qeth_card *card;
        char dbf_text[15];
 
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "chgmtu");
        sprintf(dbf_text, "%8x", new_mtu);
@@ -3343,7 +3343,7 @@ struct net_device_stats *qeth_get_stats(struct net_device *dev)
 {
        struct qeth_card *card;
 
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 5, "getstat");
 
@@ -3395,7 +3395,7 @@ void qeth_tx_timeout(struct net_device *dev)
 {
        struct qeth_card *card;
 
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        card->stats.tx_errors++;
        qeth_schedule_recovery(card);
 }
@@ -3403,7 +3403,7 @@ EXPORT_SYMBOL_GPL(qeth_tx_timeout);
 
 int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        int rc = 0;
 
        switch (regnum) {
@@ -4253,7 +4253,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_stats_count);
 void qeth_core_get_ethtool_stats(struct net_device *dev,
                struct ethtool_stats *stats, u64 *data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        data[0] = card->stats.rx_packets -
                                card->perf_stats.initial_rx_packets;
        data[1] = card->perf_stats.bufs_rec;
@@ -4313,7 +4313,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_strings);
 void qeth_core_get_drvinfo(struct net_device *dev,
                struct ethtool_drvinfo *info)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        if (card->options.layer2)
                strcpy(info->driver, "qeth_l2");
        else
@@ -4331,7 +4331,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo);
 int qeth_core_ethtool_get_settings(struct net_device *netdev,
                                        struct ethtool_cmd *ecmd)
 {
-       struct qeth_card *card = netdev_priv(netdev);
+       struct qeth_card *card = netdev->ml_priv;
        enum qeth_link_types link_type;
 
        if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan))
index 3fbc3bdec0c5cfada6edccd973072f81ee2b97f7..a8b069cd9a4c63efb2197e24ba469790692bcdce 100644 (file)
@@ -35,7 +35,7 @@ static int qeth_l2_recover(void *);
 
 static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct mii_ioctl_data *mii_data;
        int rc = 0;
 
@@ -317,7 +317,7 @@ static void qeth_l2_process_vlans(struct qeth_card *card, int clear)
 
 static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct qeth_vlan_vid *id;
 
        QETH_DBF_TEXT_(TRACE, 4, "aid:%d", vid);
@@ -334,7 +334,7 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
        struct qeth_vlan_vid *id, *tmpid = NULL;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
        spin_lock_bh(&card->vlanlock);
@@ -566,7 +566,7 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
 static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 {
        struct sockaddr *addr = p;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        int rc = 0;
 
        QETH_DBF_TEXT(TRACE, 3, "setmac");
@@ -590,7 +590,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
 
 static void qeth_l2_set_multicast_list(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct dev_mc_list *dm;
 
        if (card->info.type == QETH_CARD_TYPE_OSN)
@@ -612,7 +612,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        int rc;
        struct qeth_hdr *hdr = NULL;
        int elements = 0;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct sk_buff *new_skb = skb;
        int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_get_cast_type(card, skb);
@@ -767,7 +767,7 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
 
 static int qeth_l2_open(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethopen");
        if (card->state != CARD_STATE_SOFTSETUP)
@@ -791,7 +791,7 @@ static int qeth_l2_open(struct net_device *dev)
 
 static int qeth_l2_stop(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethstop");
        netif_tx_disable(dev);
@@ -838,7 +838,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
 
 static int qeth_l2_ethtool_set_tso(struct net_device *dev, u32 data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        if (data) {
                if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -894,7 +894,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
        if (!card->dev)
                return -ENODEV;
 
-       card->dev->priv = card;
+       card->dev->ml_priv = card;
        card->dev->tx_timeout = &qeth_tx_timeout;
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->open = qeth_l2_open;
@@ -1178,7 +1178,7 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len)
        QETH_DBF_TEXT(TRACE, 2, "osnsdmc");
        if (!dev)
                return -ENODEV;
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        if (!card)
                return -ENODEV;
        if ((card->state != CARD_STATE_UP) &&
@@ -1201,7 +1201,7 @@ int qeth_osn_register(unsigned char *read_dev_no, struct net_device **dev,
        *dev = qeth_l2_netdev_by_devno(read_dev_no);
        if (*dev == NULL)
                return -ENODEV;
-       card = netdev_priv(*dev);
+       card = (*dev)->ml_priv;
        if (!card)
                return -ENODEV;
        if ((assist_cb == NULL) || (data_cb == NULL))
@@ -1219,7 +1219,7 @@ void qeth_osn_deregister(struct net_device *dev)
        QETH_DBF_TEXT(TRACE, 2, "osndereg");
        if (!dev)
                return;
-       card = netdev_priv(dev);
+       card = dev->ml_priv;
        if (!card)
                return;
        card->osn_info.assist_cb = NULL;
index 38de31b557082cbe006f227bb3f6e88603ed3dc4..3e1d13857350959519524ec655f5659d3f633444 100644 (file)
@@ -1813,7 +1813,7 @@ static void qeth_l3_free_vlan_addresses(struct qeth_card *card,
 static void qeth_l3_vlan_rx_register(struct net_device *dev,
                        struct vlan_group *grp)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        unsigned long flags;
 
        QETH_DBF_TEXT(TRACE, 4, "vlanreg");
@@ -1825,7 +1825,7 @@ static void qeth_l3_vlan_rx_register(struct net_device *dev,
 static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 {
        struct net_device *vlandev;
-       struct qeth_card *card = (struct qeth_card *) dev->priv;
+       struct qeth_card *card = dev->ml_priv;
        struct in_device *in_dev;
 
        if (card->info.type == QETH_CARD_TYPE_IQD)
@@ -1851,7 +1851,7 @@ static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
 
 static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        unsigned long flags;
 
        QETH_DBF_TEXT_(TRACE, 4, "kid:%d", vid);
@@ -2013,7 +2013,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev,
                }
        }
 
-       if (rc && !(netdev_priv(vlan_dev_real_dev(dev)) == (void *)card))
+       if (rc && !(vlan_dev_real_dev(dev)->ml_priv == (void *)card))
                return 0;
 
        return rc;
@@ -2047,9 +2047,9 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
 
        rc = qeth_l3_verify_dev(dev);
        if (rc == QETH_REAL_CARD)
-               card = netdev_priv(dev);
+               card = dev->ml_priv;
        else if (rc == QETH_VLAN_CARD)
-               card = netdev_priv(vlan_dev_real_dev(dev));
+               card = vlan_dev_real_dev(dev)->ml_priv;
        if (card && card->options.layer2)
                card = NULL;
        QETH_DBF_TEXT_(TRACE, 4, "%d", rc);
@@ -2110,7 +2110,7 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
 
 static void qeth_l3_set_multicast_list(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 3, "setmulti");
        qeth_l3_delete_mc_addresses(card);
@@ -2438,7 +2438,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
 
 static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct qeth_arp_cache_entry arp_entry;
        struct mii_ioctl_data *mii_data;
        int rc = 0;
@@ -2595,7 +2595,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        u16 *tag;
        struct qeth_hdr *hdr = NULL;
        int elements_needed = 0;
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        struct sk_buff *new_skb = NULL;
        int ipv = qeth_get_ip_version(skb);
        int cast_type = qeth_get_cast_type(card, skb);
@@ -2763,7 +2763,7 @@ tx_drop:
 
 static int qeth_l3_open(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethopen");
        if (card->state != CARD_STATE_SOFTSETUP)
@@ -2780,7 +2780,7 @@ static int qeth_l3_open(struct net_device *dev)
 
 static int qeth_l3_stop(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        QETH_DBF_TEXT(TRACE, 4, "qethstop");
        netif_tx_disable(dev);
@@ -2792,14 +2792,14 @@ static int qeth_l3_stop(struct net_device *dev)
 
 static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        return (card->options.checksum_type == HW_CHECKSUMMING);
 }
 
 static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
        enum qeth_card_states old_state;
        enum qeth_checksum_types csum_type;
 
@@ -2825,7 +2825,7 @@ static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
 
 static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data)
 {
-       struct qeth_card *card = netdev_priv(dev);
+       struct qeth_card *card = dev->ml_priv;
 
        if (data) {
                if (card->options.large_send == QETH_LARGE_SEND_NO) {
@@ -2915,7 +2915,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
                return -ENODEV;
 
        card->dev->hard_start_xmit = qeth_l3_hard_start_xmit;
-       card->dev->priv = card;
+       card->dev->ml_priv = card;
        card->dev->tx_timeout = &qeth_tx_timeout;
        card->dev->watchdog_timeo = QETH_TX_TIMEOUT;
        card->dev->open = qeth_l3_open;
index d82d800389f6018039abefa9668e7f7ad4f90c17..e74308bdabd3dc9d5b317dbf8eeadb4dc4f8ceab 100644 (file)
@@ -2405,35 +2405,18 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
        u64 rchar, wchar, syscr, syscw;
        struct task_io_accounting ioac;
 
-       if (!whole) {
-               rchar = task->rchar;
-               wchar = task->wchar;
-               syscr = task->syscr;
-               syscw = task->syscw;
-               memcpy(&ioac, &task->ioac, sizeof(ioac));
-       } else {
-               unsigned long flags;
-               struct task_struct *t = task;
-               rchar = wchar = syscr = syscw = 0;
-               memset(&ioac, 0, sizeof(ioac));
+       rchar = task->rchar;
+       wchar = task->wchar;
+       syscr = task->syscr;
+       syscw = task->syscw;
+       memcpy(&ioac, &task->ioac, sizeof(ioac));
 
-               rcu_read_lock();
-               do {
-                       rchar += t->rchar;
-                       wchar += t->wchar;
-                       syscr += t->syscr;
-                       syscw += t->syscw;
-
-                       ioac.read_bytes += t->ioac.read_bytes;
-                       ioac.write_bytes += t->ioac.write_bytes;
-                       ioac.cancelled_write_bytes +=
-                                       t->ioac.cancelled_write_bytes;
-                       t = next_thread(t);
-               } while (t != task);
-               rcu_read_unlock();
+       if (whole) {
+               unsigned long flags;
 
                if (lock_task_sighand(task, &flags)) {
                        struct signal_struct *sig = task->signal;
+                       struct task_struct *t = task;
 
                        rchar += sig->rchar;
                        wchar += sig->wchar;
@@ -2444,11 +2427,20 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
                        ioac.write_bytes += sig->ioac.write_bytes;
                        ioac.cancelled_write_bytes +=
                                        sig->ioac.cancelled_write_bytes;
-
+                       while_each_thread(task, t) {
+                               rchar += t->rchar;
+                               wchar += t->wchar;
+                               syscr += t->syscr;
+                               syscw += t->syscw;
+
+                               ioac.read_bytes += t->ioac.read_bytes;
+                               ioac.write_bytes += t->ioac.write_bytes;
+                               ioac.cancelled_write_bytes +=
+                                       t->ioac.cancelled_write_bytes;
+                       }
                        unlock_task_sighand(task, &flags);
                }
        }
-
        return sprintf(buffer,
                        "rchar: %llu\n"
                        "wchar: %llu\n"
@@ -2457,13 +2449,9 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole)
                        "read_bytes: %llu\n"
                        "write_bytes: %llu\n"
                        "cancelled_write_bytes: %llu\n",
-                       (unsigned long long)rchar,
-                       (unsigned long long)wchar,
-                       (unsigned long long)syscr,
-                       (unsigned long long)syscw,
-                       (unsigned long long)ioac.read_bytes,
-                       (unsigned long long)ioac.write_bytes,
-                       (unsigned long long)ioac.cancelled_write_bytes);
+                       rchar, wchar, syscr, syscw,
+                       ioac.read_bytes, ioac.write_bytes,
+                       ioac.cancelled_write_bytes);
 }
 
 static int proc_tid_io_accounting(struct task_struct *task, char *buffer)
index a3034d20ebd5ef2f7d264b62311afa06a7ac7327..c764a8fcb058e33fb703e56af17eaa7fb966d7ea 100644 (file)
@@ -13,7 +13,7 @@
  *
  * While the GPIO programming interface defines valid GPIO numbers
  * to be in the range 0..MAX_INT, this library restricts them to the
- * smaller range 0..ARCH_NR_GPIOS.
+ * smaller range 0..ARCH_NR_GPIOS-1.
  */
 
 #ifndef ARCH_NR_GPIOS
index 9a71d4cc88c8f399c63109a21a5dcc5ec9847270..32e0ef0f6e1fcdd7f67d83ffe985e6cbe8eb9a08 100644 (file)
@@ -273,7 +273,10 @@ struct hstate {};
 #define huge_page_mask(h) PAGE_MASK
 #define huge_page_order(h) 0
 #define huge_page_shift(h) PAGE_SHIFT
-#define pages_per_huge_page(h) 1
+static inline unsigned int pages_per_huge_page(struct hstate *h)
+{
+       return 1;
+}
 #endif
 
 #endif /* _LINUX_HUGETLB_H */
diff --git a/include/linux/mISDNdsp.h b/include/linux/mISDNdsp.h
new file mode 100644 (file)
index 0000000..6b71d2d
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef __mISDNdsp_H__
+#define __mISDNdsp_H__
+
+struct mISDN_dsp_element_arg {
+       char    *name;
+       char    *def;
+       char    *desc;
+};
+
+struct mISDN_dsp_element {
+       char    *name;
+       void    *(*new)(const char *arg);
+       void    (*free)(void *p);
+       void    (*process_tx)(void *p, unsigned char *data, int len);
+       void    (*process_rx)(void *p, unsigned char *data, int len);
+       int     num_args;
+       struct mISDN_dsp_element_arg
+               *args;
+};
+
+extern int  mISDN_dsp_element_register(struct mISDN_dsp_element *elem);
+extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem);
+
+struct dsp_features {
+       int     hfc_id; /* unique id to identify the chip (or -1) */
+       int     hfc_dtmf; /* set if HFCmulti card supports dtmf */
+       int     hfc_loops; /* set if card supports tone loops */
+       int     hfc_echocanhw; /* set if card supports echocancelation*/
+       int     pcm_id; /* unique id to identify the pcm bus (or -1) */
+       int     pcm_slots; /* number of slots on the pcm bus */
+       int     pcm_banks; /* number of IO banks of pcm bus */
+       int     unclocked; /* data is not clocked (has jitter/loss) */
+       int     unordered; /* data is unordered (packets have index) */
+};
+
+#endif
+
diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h
new file mode 100644 (file)
index 0000000..e794dfb
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ *   Basic declarations for the mISDN HW channels
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef MISDNHW_H
+#define MISDNHW_H
+#include <linux/mISDNif.h>
+#include <linux/timer.h>
+
+/*
+ * HW DEBUG 0xHHHHGGGG
+ * H - hardware driver specific bits
+ * G - for all drivers
+ */
+
+#define DEBUG_HW               0x00000001
+#define DEBUG_HW_OPEN          0x00000002
+#define DEBUG_HW_DCHANNEL      0x00000100
+#define DEBUG_HW_DFIFO         0x00000200
+#define DEBUG_HW_BCHANNEL      0x00001000
+#define DEBUG_HW_BFIFO         0x00002000
+
+#define MAX_DFRAME_LEN_L1      300
+#define MAX_MON_FRAME          32
+#define MAX_LOG_SPACE          2048
+#define MISDN_COPY_SIZE                32
+
+/* channel->Flags bit field */
+#define FLG_TX_BUSY            0       /* tx_buf in use */
+#define FLG_TX_NEXT            1       /* next_skb in use */
+#define FLG_L1_BUSY            2       /* L1 is permanent busy */
+#define FLG_L2_ACTIVATED       3       /* activated from L2 */
+#define FLG_OPEN               5       /* channel is in use */
+#define FLG_ACTIVE             6       /* channel is activated */
+#define FLG_BUSY_TIMER         7
+/* channel type */
+#define FLG_DCHANNEL           8       /* channel is D-channel */
+#define FLG_BCHANNEL           9       /* channel is B-channel */
+#define FLG_ECHANNEL           10      /* channel is E-channel */
+#define FLG_TRANSPARENT                12      /* channel use transparent data */
+#define FLG_HDLC               13      /* channel use hdlc data */
+#define FLG_L2DATA             14      /* channel use L2 DATA primitivs */
+#define FLG_ORIGIN             15      /* channel is on origin site */
+/* channel specific stuff */
+/* arcofi specific */
+#define FLG_ARCOFI_TIMER       16
+#define FLG_ARCOFI_ERROR       17
+/* isar specific */
+#define FLG_INITIALIZED                16
+#define FLG_DLEETX             17
+#define FLG_LASTDLE            18
+#define FLG_FIRST              19
+#define FLG_LASTDATA           20
+#define FLG_NMD_DATA           21
+#define FLG_FTI_RUN            22
+#define FLG_LL_OK              23
+#define FLG_LL_CONN            24
+#define FLG_DTMFSEND           25
+
+/* workq events */
+#define FLG_RECVQUEUE          30
+#define        FLG_PHCHANGE            31
+
+#define schedule_event(s, ev)  do { \
+                                       test_and_set_bit(ev, &((s)->Flags)); \
+                                       schedule_work(&((s)->workq)); \
+                               } while (0)
+
+struct dchannel {
+       struct mISDNdevice      dev;
+       u_long                  Flags;
+       struct work_struct      workq;
+       void                    (*phfunc) (struct dchannel *);
+       u_int                   state;
+       void                    *l1;
+       /* HW access */
+       u_char                  (*read_reg) (void *, u_char);
+       void                    (*write_reg) (void *, u_char, u_char);
+       void                    (*read_fifo) (void *, u_char *, int);
+       void                    (*write_fifo) (void *, u_char *, int);
+       void                    *hw;
+       int                     slot;   /* multiport card channel slot */
+       struct timer_list       timer;
+       /* receive data */
+       struct sk_buff          *rx_skb;
+       int                     maxlen;
+       /* send data */
+       struct sk_buff_head     squeue;
+       struct sk_buff_head     rqueue;
+       struct sk_buff          *tx_skb;
+       int                     tx_idx;
+       int                     debug;
+       /* statistics */
+       int                     err_crc;
+       int                     err_tx;
+       int                     err_rx;
+};
+
+typedef int    (dchannel_l1callback)(struct dchannel *, u_int);
+extern int     create_l1(struct dchannel *, dchannel_l1callback *);
+
+/* private L1 commands */
+#define INFO0          0x8002
+#define INFO1          0x8102
+#define INFO2          0x8202
+#define INFO3_P8       0x8302
+#define INFO3_P10      0x8402
+#define INFO4_P8       0x8502
+#define INFO4_P10      0x8602
+#define LOSTFRAMING    0x8702
+#define ANYSIGNAL      0x8802
+#define HW_POWERDOWN   0x8902
+#define HW_RESET_REQ   0x8a02
+#define HW_POWERUP_REQ 0x8b02
+#define HW_DEACT_REQ   0x8c02
+#define HW_ACTIVATE_REQ        0x8e02
+#define HW_D_NOBLOCKED  0x8f02
+#define HW_RESET_IND   0x9002
+#define HW_POWERUP_IND 0x9102
+#define HW_DEACT_IND   0x9202
+#define HW_ACTIVATE_IND        0x9302
+#define HW_DEACT_CNF   0x9402
+#define HW_TESTLOOP    0x9502
+#define HW_TESTRX_RAW  0x9602
+#define HW_TESTRX_HDLC 0x9702
+#define HW_TESTRX_OFF  0x9802
+
+struct layer1;
+extern int     l1_event(struct layer1 *, u_int);
+
+
+struct bchannel {
+       struct mISDNchannel     ch;
+       int                     nr;
+       u_long                  Flags;
+       struct work_struct      workq;
+       u_int                   state;
+       /* HW access */
+       u_char                  (*read_reg) (void *, u_char);
+       void                    (*write_reg) (void *, u_char, u_char);
+       void                    (*read_fifo) (void *, u_char *, int);
+       void                    (*write_fifo) (void *, u_char *, int);
+       void                    *hw;
+       int                     slot;   /* multiport card channel slot */
+       struct timer_list       timer;
+       /* receive data */
+       struct sk_buff          *rx_skb;
+       int                     maxlen;
+       /* send data */
+       struct sk_buff          *next_skb;
+       struct sk_buff          *tx_skb;
+       struct sk_buff_head     rqueue;
+       int                     rcount;
+       int                     tx_idx;
+       int                     debug;
+       /* statistics */
+       int                     err_crc;
+       int                     err_tx;
+       int                     err_rx;
+};
+
+extern int     mISDN_initdchannel(struct dchannel *, int, void *);
+extern int     mISDN_initbchannel(struct bchannel *, int);
+extern int     mISDN_freedchannel(struct dchannel *);
+extern int     mISDN_freebchannel(struct bchannel *);
+extern void    queue_ch_frame(struct mISDNchannel *, u_int,
+                       int, struct sk_buff *);
+extern int     dchannel_senddata(struct dchannel *, struct sk_buff *);
+extern int     bchannel_senddata(struct bchannel *, struct sk_buff *);
+extern void    recv_Dchannel(struct dchannel *);
+extern void    recv_Bchannel(struct bchannel *);
+extern void    recv_Dchannel_skb(struct dchannel *, struct sk_buff *);
+extern void    recv_Bchannel_skb(struct bchannel *, struct sk_buff *);
+extern void    confirm_Bsend(struct bchannel *bch);
+extern int     get_next_bframe(struct bchannel *);
+extern int     get_next_dframe(struct dchannel *);
+
+#endif
diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h
new file mode 100644 (file)
index 0000000..5c948f3
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ *
+ * Author      Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008  by Karsten Keil <kkeil@novell.com>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU LESSER GENERAL PUBLIC LICENSE for more details.
+ *
+ */
+
+#ifndef mISDNIF_H
+#define mISDNIF_H
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+
+/*
+ * ABI Version 32 bit
+ *
+ * <8 bit> Major version
+ *             - changed if any interface become backwards incompatible
+ *
+ * <8 bit> Minor version
+ *              - changed if any interface is extended but backwards compatible
+ *
+ * <16 bit> Release number
+ *              - should be incremented on every checkin
+ */
+#define        MISDN_MAJOR_VERSION     1
+#define        MISDN_MINOR_VERSION     0
+#define MISDN_RELEASE          18
+
+/* primitives for information exchange
+ * generell format
+ * <16  bit  0 >
+ * <8  bit command>
+ *    BIT 8 = 1 LAYER private
+ *    BIT 7 = 1 answer
+ *    BIT 6 = 1 DATA
+ * <8  bit target layer mask>
+ *
+ * Layer = 00 is reserved for general commands
+   Layer = 01  L2 -> HW
+   Layer = 02  HW -> L2
+   Layer = 04  L3 -> L2
+   Layer = 08  L2 -> L3
+ * Layer = FF is reserved for broadcast commands
+ */
+
+#define MISDN_CMDMASK          0xff00
+#define MISDN_LAYERMASK                0x00ff
+
+/* generell commands */
+#define OPEN_CHANNEL           0x0100
+#define CLOSE_CHANNEL          0x0200
+#define CONTROL_CHANNEL                0x0300
+#define CHECK_DATA             0x0400
+
+/* layer 2 -> layer 1 */
+#define PH_ACTIVATE_REQ                0x0101
+#define PH_DEACTIVATE_REQ      0x0201
+#define PH_DATA_REQ            0x2001
+#define MPH_ACTIVATE_REQ       0x0501
+#define MPH_DEACTIVATE_REQ     0x0601
+#define MPH_INFORMATION_REQ    0x0701
+#define PH_CONTROL_REQ         0x0801
+
+/* layer 1 -> layer 2 */
+#define PH_ACTIVATE_IND                0x0102
+#define PH_ACTIVATE_CNF                0x4102
+#define PH_DEACTIVATE_IND      0x0202
+#define PH_DEACTIVATE_CNF      0x4202
+#define PH_DATA_IND            0x2002
+#define MPH_ACTIVATE_IND       0x0502
+#define MPH_DEACTIVATE_IND     0x0602
+#define MPH_INFORMATION_IND    0x0702
+#define PH_DATA_CNF            0x6002
+#define PH_CONTROL_IND         0x0802
+#define PH_CONTROL_CNF         0x4802
+
+/* layer 3 -> layer 2 */
+#define DL_ESTABLISH_REQ       0x1004
+#define DL_RELEASE_REQ         0x1104
+#define DL_DATA_REQ            0x3004
+#define DL_UNITDATA_REQ                0x3104
+#define DL_INFORMATION_REQ     0x0004
+
+/* layer 2 -> layer 3 */
+#define DL_ESTABLISH_IND       0x1008
+#define DL_ESTABLISH_CNF       0x5008
+#define DL_RELEASE_IND         0x1108
+#define DL_RELEASE_CNF         0x5108
+#define DL_DATA_IND            0x3008
+#define DL_UNITDATA_IND                0x3108
+#define DL_INFORMATION_IND     0x0008
+
+/* intern layer 2 managment */
+#define MDL_ASSIGN_REQ         0x1804
+#define MDL_ASSIGN_IND         0x1904
+#define MDL_REMOVE_REQ         0x1A04
+#define MDL_REMOVE_IND         0x1B04
+#define MDL_STATUS_UP_IND      0x1C04
+#define MDL_STATUS_DOWN_IND    0x1D04
+#define MDL_STATUS_UI_IND      0x1E04
+#define MDL_ERROR_IND          0x1F04
+#define MDL_ERROR_RSP          0x5F04
+
+/* DL_INFORMATION_IND types */
+#define DL_INFO_L2_CONNECT     0x0001
+#define DL_INFO_L2_REMOVED     0x0002
+
+/* PH_CONTROL types */
+/* TOUCH TONE IS 0x20XX  XX "0"..."9", "A","B","C","D","*","#" */
+#define DTMF_TONE_VAL          0x2000
+#define DTMF_TONE_MASK         0x007F
+#define DTMF_TONE_START                0x2100
+#define DTMF_TONE_STOP         0x2200
+#define DTMF_HFC_COEF          0x4000
+#define DSP_CONF_JOIN          0x2403
+#define DSP_CONF_SPLIT         0x2404
+#define DSP_RECEIVE_OFF                0x2405
+#define DSP_RECEIVE_ON         0x2406
+#define DSP_ECHO_ON            0x2407
+#define DSP_ECHO_OFF           0x2408
+#define DSP_MIX_ON             0x2409
+#define DSP_MIX_OFF            0x240a
+#define DSP_DELAY              0x240b
+#define DSP_JITTER             0x240c
+#define DSP_TXDATA_ON          0x240d
+#define DSP_TXDATA_OFF         0x240e
+#define DSP_TX_DEJITTER                0x240f
+#define DSP_TX_DEJ_OFF         0x2410
+#define DSP_TONE_PATT_ON       0x2411
+#define DSP_TONE_PATT_OFF      0x2412
+#define DSP_VOL_CHANGE_TX      0x2413
+#define DSP_VOL_CHANGE_RX      0x2414
+#define DSP_BF_ENABLE_KEY      0x2415
+#define DSP_BF_DISABLE         0x2416
+#define DSP_BF_ACCEPT          0x2416
+#define DSP_BF_REJECT          0x2417
+#define DSP_PIPELINE_CFG       0x2418
+#define HFC_VOL_CHANGE_TX      0x2601
+#define HFC_VOL_CHANGE_RX      0x2602
+#define HFC_SPL_LOOP_ON                0x2603
+#define HFC_SPL_LOOP_OFF       0x2604
+
+/* DSP_TONE_PATT_ON parameter */
+#define TONE_OFF                       0x0000
+#define TONE_GERMAN_DIALTONE           0x0001
+#define TONE_GERMAN_OLDDIALTONE                0x0002
+#define TONE_AMERICAN_DIALTONE         0x0003
+#define TONE_GERMAN_DIALPBX            0x0004
+#define TONE_GERMAN_OLDDIALPBX         0x0005
+#define TONE_AMERICAN_DIALPBX          0x0006
+#define TONE_GERMAN_RINGING            0x0007
+#define TONE_GERMAN_OLDRINGING         0x0008
+#define TONE_AMERICAN_RINGPBX          0x000b
+#define TONE_GERMAN_RINGPBX            0x000c
+#define TONE_GERMAN_OLDRINGPBX         0x000d
+#define TONE_AMERICAN_RINGING          0x000e
+#define TONE_GERMAN_BUSY               0x000f
+#define TONE_GERMAN_OLDBUSY            0x0010
+#define TONE_AMERICAN_BUSY             0x0011
+#define TONE_GERMAN_HANGUP             0x0012
+#define TONE_GERMAN_OLDHANGUP          0x0013
+#define TONE_AMERICAN_HANGUP           0x0014
+#define TONE_SPECIAL_INFO              0x0015
+#define TONE_GERMAN_GASSENBESETZT      0x0016
+#define TONE_GERMAN_AUFSCHALTTON       0x0016
+
+/* MPH_INFORMATION_IND */
+#define L1_SIGNAL_LOS_OFF      0x0010
+#define L1_SIGNAL_LOS_ON       0x0011
+#define L1_SIGNAL_AIS_OFF      0x0012
+#define L1_SIGNAL_AIS_ON       0x0013
+#define L1_SIGNAL_RDI_OFF      0x0014
+#define L1_SIGNAL_RDI_ON       0x0015
+#define L1_SIGNAL_SLIP_RX      0x0020
+#define L1_SIGNAL_SLIP_TX      0x0021
+
+/*
+ * protocol ids
+ * D channel 1-31
+ * B channel 33 - 63
+ */
+
+#define ISDN_P_NONE            0
+#define ISDN_P_BASE            0
+#define ISDN_P_TE_S0           0x01
+#define ISDN_P_NT_S0           0x02
+#define ISDN_P_TE_E1           0x03
+#define ISDN_P_NT_E1           0x04
+#define ISDN_P_LAPD_TE         0x10
+#define        ISDN_P_LAPD_NT          0x11
+
+#define ISDN_P_B_MASK          0x1f
+#define ISDN_P_B_START         0x20
+
+#define ISDN_P_B_RAW           0x21
+#define ISDN_P_B_HDLC          0x22
+#define ISDN_P_B_X75SLP                0x23
+#define ISDN_P_B_L2DTMF                0x24
+#define ISDN_P_B_L2DSP         0x25
+#define ISDN_P_B_L2DSPHDLC     0x26
+
+#define OPTION_L2_PMX          1
+#define OPTION_L2_PTP          2
+#define OPTION_L2_FIXEDTEI     3
+#define OPTION_L2_CLEANUP      4
+
+/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
+#define MISDN_MAX_IDLEN                20
+
+struct mISDNhead {
+       unsigned int    prim;
+       unsigned int    id;
+}  __attribute__((packed));
+
+#define MISDN_HEADER_LEN       sizeof(struct mISDNhead)
+#define MAX_DATA_SIZE          2048
+#define MAX_DATA_MEM           (MAX_DATA_SIZE + MISDN_HEADER_LEN)
+#define MAX_DFRAME_LEN         260
+
+#define MISDN_ID_ADDR_MASK     0xFFFF
+#define MISDN_ID_TEI_MASK      0xFF00
+#define MISDN_ID_SAPI_MASK     0x00FF
+#define MISDN_ID_TEI_ANY       0x7F00
+
+#define MISDN_ID_ANY           0xFFFF
+#define MISDN_ID_NONE          0xFFFE
+
+#define GROUP_TEI              127
+#define TEI_SAPI               63
+#define CTRL_SAPI              0
+
+#define MISDN_CHMAP_SIZE       4
+
+#define SOL_MISDN      0
+
+struct sockaddr_mISDN {
+       sa_family_t    family;
+       unsigned char   dev;
+       unsigned char   channel;
+       unsigned char   sapi;
+       unsigned char   tei;
+};
+
+/* timer device ioctl */
+#define IMADDTIMER     _IOR('I', 64, int)
+#define IMDELTIMER     _IOR('I', 65, int)
+/* socket ioctls */
+#define        IMGETVERSION    _IOR('I', 66, int)
+#define        IMGETCOUNT      _IOR('I', 67, int)
+#define IMGETDEVINFO   _IOR('I', 68, int)
+#define IMCTRLREQ      _IOR('I', 69, int)
+#define IMCLEAR_L2     _IOR('I', 70, int)
+
+struct mISDNversion {
+       unsigned char   major;
+       unsigned char   minor;
+       unsigned short  release;
+};
+
+struct mISDN_devinfo {
+       u_int                   id;
+       u_int                   Dprotocols;
+       u_int                   Bprotocols;
+       u_int                   protocol;
+       u_long                  channelmap[MISDN_CHMAP_SIZE];
+       u_int                   nrbchan;
+       char                    name[MISDN_MAX_IDLEN];
+};
+
+/* CONTROL_CHANNEL parameters */
+#define MISDN_CTRL_GETOP               0x0000
+#define MISDN_CTRL_LOOP                        0x0001
+#define MISDN_CTRL_CONNECT             0x0002
+#define MISDN_CTRL_DISCONNECT          0x0004
+#define MISDN_CTRL_PCMCONNECT          0x0010
+#define MISDN_CTRL_PCMDISCONNECT       0x0020
+#define MISDN_CTRL_SETPEER             0x0040
+#define MISDN_CTRL_UNSETPEER           0x0080
+#define MISDN_CTRL_RX_OFF              0x0100
+#define MISDN_CTRL_HW_FEATURES_OP      0x2000
+#define MISDN_CTRL_HW_FEATURES         0x2001
+#define MISDN_CTRL_HFC_OP              0x4000
+#define MISDN_CTRL_HFC_PCM_CONN                0x4001
+#define MISDN_CTRL_HFC_PCM_DISC                0x4002
+#define MISDN_CTRL_HFC_CONF_JOIN       0x4003
+#define MISDN_CTRL_HFC_CONF_SPLIT      0x4004
+#define MISDN_CTRL_HFC_RECEIVE_OFF     0x4005
+#define MISDN_CTRL_HFC_RECEIVE_ON      0x4006
+#define MISDN_CTRL_HFC_ECHOCAN_ON      0x4007
+#define MISDN_CTRL_HFC_ECHOCAN_OFF     0x4008
+
+
+/* socket options */
+#define MISDN_TIME_STAMP               0x0001
+
+struct mISDN_ctrl_req {
+       int             op;
+       int             channel;
+       int             p1;
+       int             p2;
+};
+
+/* muxer options */
+#define MISDN_OPT_ALL          1
+#define MISDN_OPT_TEIMGR       2
+
+#ifdef __KERNEL__
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/completion.h>
+
+#define DEBUG_CORE             0x000000ff
+#define DEBUG_CORE_FUNC                0x00000002
+#define DEBUG_SOCKET           0x00000004
+#define DEBUG_MANAGER          0x00000008
+#define DEBUG_SEND_ERR         0x00000010
+#define DEBUG_MSG_THREAD       0x00000020
+#define DEBUG_QUEUE_FUNC       0x00000040
+#define DEBUG_L1               0x0000ff00
+#define DEBUG_L1_FSM           0x00000200
+#define DEBUG_L2               0x00ff0000
+#define DEBUG_L2_FSM           0x00020000
+#define DEBUG_L2_CTRL          0x00040000
+#define DEBUG_L2_RECV          0x00080000
+#define DEBUG_L2_TEI           0x00100000
+#define DEBUG_L2_TEIFSM                0x00200000
+#define DEBUG_TIMER            0x01000000
+
+#define mISDN_HEAD_P(s)                ((struct mISDNhead *)&s->cb[0])
+#define mISDN_HEAD_PRIM(s)     (((struct mISDNhead *)&s->cb[0])->prim)
+#define mISDN_HEAD_ID(s)       (((struct mISDNhead *)&s->cb[0])->id)
+
+/* socket states */
+#define MISDN_OPEN     1
+#define MISDN_BOUND    2
+#define MISDN_CLOSED   3
+
+struct mISDNchannel;
+struct mISDNdevice;
+struct mISDNstack;
+
+struct channel_req {
+       u_int                   protocol;
+       struct sockaddr_mISDN   adr;
+       struct mISDNchannel     *ch;
+};
+
+typedef        int     (ctrl_func_t)(struct mISDNchannel *, u_int, void *);
+typedef        int     (send_func_t)(struct mISDNchannel *, struct sk_buff *);
+typedef int    (create_func_t)(struct channel_req *);
+
+struct Bprotocol {
+       struct list_head        list;
+       char                    *name;
+       u_int                   Bprotocols;
+       create_func_t           *create;
+};
+
+struct mISDNchannel {
+       struct list_head        list;
+       u_int                   protocol;
+       u_int                   nr;
+       u_long                  opt;
+       u_int                   addr;
+       struct mISDNstack       *st;
+       struct mISDNchannel     *peer;
+       send_func_t             *send;
+       send_func_t             *recv;
+       ctrl_func_t             *ctrl;
+};
+
+struct mISDN_sock_list {
+       struct hlist_head       head;
+       rwlock_t                lock;
+};
+
+struct mISDN_sock {
+       struct sock             sk;
+       struct mISDNchannel     ch;
+       u_int                   cmask;
+       struct mISDNdevice      *dev;
+};
+
+
+
+struct mISDNdevice {
+       struct mISDNchannel     D;
+       u_int                   id;
+       char                    name[MISDN_MAX_IDLEN];
+       u_int                   Dprotocols;
+       u_int                   Bprotocols;
+       u_int                   nrbchan;
+       u_long                  channelmap[MISDN_CHMAP_SIZE];
+       struct list_head        bchannels;
+       struct mISDNchannel     *teimgr;
+       struct device           dev;
+};
+
+struct mISDNstack {
+       u_long                  status;
+       struct mISDNdevice      *dev;
+       struct task_struct      *thread;
+       struct completion       *notify;
+       wait_queue_head_t       workq;
+       struct sk_buff_head     msgq;
+       struct list_head        layer2;
+       struct mISDNchannel     *layer1;
+       struct mISDNchannel     own;
+       struct mutex            lmutex; /* protect lists */
+       struct mISDN_sock_list  l1sock;
+#ifdef MISDN_MSG_STATS
+       u_int                   msg_cnt;
+       u_int                   sleep_cnt;
+       u_int                   stopped_cnt;
+#endif
+};
+
+/* global alloc/queue dunctions */
+
+static inline struct sk_buff *
+mI_alloc_skb(unsigned int len, gfp_t gfp_mask)
+{
+       struct sk_buff  *skb;
+
+       skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask);
+       if (likely(skb))
+               skb_reserve(skb, MISDN_HEADER_LEN);
+       return skb;
+}
+
+static inline struct sk_buff *
+_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask)
+{
+       struct sk_buff  *skb = mI_alloc_skb(len, gfp_mask);
+       struct mISDNhead *hh;
+
+       if (!skb)
+               return NULL;
+       if (len)
+               memcpy(skb_put(skb, len), dp, len);
+       hh = mISDN_HEAD_P(skb);
+       hh->prim = prim;
+       hh->id = id;
+       return skb;
+}
+
+static inline void
+_queue_data(struct mISDNchannel *ch, u_int prim,
+    u_int id, u_int len, void *dp, gfp_t gfp_mask)
+{
+       struct sk_buff          *skb;
+
+       if (!ch->peer)
+               return;
+       skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask);
+       if (!skb)
+               return;
+       if (ch->recv(ch->peer, skb))
+               dev_kfree_skb(skb);
+}
+
+/* global register/unregister functions */
+
+extern int     mISDN_register_device(struct mISDNdevice *, char *name);
+extern void    mISDN_unregister_device(struct mISDNdevice *);
+extern int     mISDN_register_Bprotocol(struct Bprotocol *);
+extern void    mISDN_unregister_Bprotocol(struct Bprotocol *);
+
+extern void    set_channel_address(struct mISDNchannel *, u_int, u_int);
+
+#endif /* __KERNEL__ */
+#endif /* mISDNIF_H */
index c3b1761aba2693d431bc3f65e8987eaaaf8070b5..ffe479ba077995042e4c941795878361cf56c23c 100644 (file)
 #define PCI_DEVICE_ID_MOXA_C320                0x3200
 
 #define PCI_VENDOR_ID_CCD              0x1397
+#define PCI_DEVICE_ID_CCD_HFC4S                0x08B4
+#define PCI_SUBDEVICE_ID_CCD_PMX2S     0x1234
+#define PCI_DEVICE_ID_CCD_HFC8S                0x16B8
 #define PCI_DEVICE_ID_CCD_2BD0         0x2bd0
+#define PCI_DEVICE_ID_CCD_HFCE1                0x30B1
+#define PCI_SUBDEVICE_ID_CCD_SPD4S     0x3136
+#define PCI_SUBDEVICE_ID_CCD_SPDE1     0x3137
 #define PCI_DEVICE_ID_CCD_B000         0xb000
 #define PCI_DEVICE_ID_CCD_B006         0xb006
 #define PCI_DEVICE_ID_CCD_B007         0xb007
 #define PCI_DEVICE_ID_CCD_B00B         0xb00b
 #define PCI_DEVICE_ID_CCD_B00C         0xb00c
 #define PCI_DEVICE_ID_CCD_B100         0xb100
+#define PCI_SUBDEVICE_ID_CCD_IOB4ST    0xB520
+#define PCI_SUBDEVICE_ID_CCD_IOB8STR   0xB521
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST    0xB522
+#define PCI_SUBDEVICE_ID_CCD_IOB1E1    0xB523
+#define PCI_SUBDEVICE_ID_CCD_SWYX4S    0xB540
+#define PCI_SUBDEVICE_ID_CCD_JH4S20    0xB550
+#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1  0xB552
+#define PCI_SUBDEVICE_ID_CCD_BN4S      0xB560
+#define PCI_SUBDEVICE_ID_CCD_BN8S      0xB562
+#define PCI_SUBDEVICE_ID_CCD_BNE1      0xB563
+#define PCI_SUBDEVICE_ID_CCD_BNE1D     0xB564
+#define PCI_SUBDEVICE_ID_CCD_BNE1DP    0xB565
+#define PCI_SUBDEVICE_ID_CCD_BN2S      0xB566
+#define PCI_SUBDEVICE_ID_CCD_BN1SM     0xB567
+#define PCI_SUBDEVICE_ID_CCD_BN4SM     0xB568
+#define PCI_SUBDEVICE_ID_CCD_BN2SM     0xB569
+#define PCI_SUBDEVICE_ID_CCD_BNE1M     0xB56A
+#define PCI_SUBDEVICE_ID_CCD_BN8SP     0xB56B
+#define PCI_SUBDEVICE_ID_CCD_HFC4S     0xB620
+#define PCI_SUBDEVICE_ID_CCD_HFC8S     0xB622
 #define PCI_DEVICE_ID_CCD_B700         0xb700
 #define PCI_DEVICE_ID_CCD_B701         0xb701
+#define PCI_SUBDEVICE_ID_CCD_HFCE1     0xC523
+#define PCI_SUBDEVICE_ID_CCD_OV2S      0xE884
+#define PCI_SUBDEVICE_ID_CCD_OV4S      0xE888
+#define PCI_SUBDEVICE_ID_CCD_OV8S      0xE998
 
 #define PCI_VENDOR_ID_EXAR             0x13a8
 #define PCI_DEVICE_ID_EXAR_XR17C152    0x0152
 
 #define PCI_VENDOR_ID_3COM_2           0xa727
 
+#define PCI_VENDOR_ID_DIGIUM           0xd161
+#define PCI_DEVICE_ID_DIGIUM_HFC4S     0xb410
+
 #define PCI_SUBVENDOR_ID_EXSYS         0xd84d
 #define PCI_SUBDEVICE_ID_EXSYS_4014    0x4014
 #define PCI_SUBDEVICE_ID_EXSYS_4055    0x4055
index f4d386c191f587507dac62b17de0ea26cfe3fbee..ca643b13b02635bfd33bae20588418ca52ff3f3b 100644 (file)
@@ -755,13 +755,6 @@ extern void __rtnl_unlock(void);
        } \
 } while(0)
 
-#define BUG_TRAP(x) do { \
-       if (unlikely(!(x))) { \
-               printk(KERN_ERR "KERNEL: assertion (%s) failed at %s (%d)\n", \
-                       #x,  __FILE__ , __LINE__); \
-       } \
-} while(0)
-
 static inline u32 rtm_get_table(struct rtattr **rta, u8 table)
 {
        return RTA_GET_U32(rta[RTA_TABLE-1]);
index 9ff8e849940396ed48b26a534af638e04accd54c..5ff9676c1e2ca83e93511891230189772fafe2a0 100644 (file)
@@ -96,6 +96,7 @@ int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr);
 /*
  * Common kmalloc functions provided by all allocators
  */
+void * __must_check __krealloc(const void *, size_t, gfp_t);
 void * __must_check krealloc(const void *, size_t, gfp_t);
 void kfree(const void *);
 size_t ksize(const void *);
index 950af631e7fb17981e94d65b21345a3d737c2cfb..dc5086fe7736c10f40bfbab0996b7d589ccf5bbe 100644 (file)
@@ -189,7 +189,8 @@ struct ucred {
 #define AF_BLUETOOTH   31      /* Bluetooth sockets            */
 #define AF_IUCV                32      /* IUCV sockets                 */
 #define AF_RXRPC       33      /* RxRPC sockets                */
-#define AF_MAX         34      /* For now.. */
+#define AF_ISDN                34      /* mISDN sockets                */
+#define AF_MAX         35      /* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC      AF_UNSPEC
@@ -225,6 +226,7 @@ struct ucred {
 #define PF_BLUETOOTH   AF_BLUETOOTH
 #define PF_IUCV                AF_IUCV
 #define PF_RXRPC       AF_RXRPC
+#define PF_ISDN                AF_ISDN
 #define PF_MAX         AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
index 0c96e7bed5db39e7eff4718353697ce52e06df96..8d6e991ef4dfb79d2a8d75bd14cf4241bafbfc95 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include <linux/bug.h>
 
 #include <net/sock.h>
 
@@ -170,7 +171,7 @@ static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue
 {
        struct request_sock *req = queue->rskq_accept_head;
 
-       BUG_TRAP(req != NULL);
+       WARN_ON(req == NULL);
 
        queue->rskq_accept_head = req->dl_next;
        if (queue->rskq_accept_head == NULL)
@@ -185,7 +186,7 @@ static inline struct sock *reqsk_queue_get_child(struct request_sock_queue *queu
        struct request_sock *req = reqsk_queue_remove(queue);
        struct sock *child = req->sk;
 
-       BUG_TRAP(child != NULL);
+       WARN_ON(child == NULL);
 
        sk_acceptq_removed(parent);
        __reqsk_free(req);
index 0efd83097ecf94aa7bceeb08737bc29caa74e91b..9341ca77bd88240741afbcbabed9019f247a7531 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -3,6 +3,7 @@
 #include <linux/string.h>
 #include <linux/module.h>
 #include <linux/err.h>
+#include <linux/sched.h>
 #include <asm/uaccess.h>
 
 /**
@@ -69,25 +70,22 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp)
 EXPORT_SYMBOL(kmemdup);
 
 /**
- * krealloc - reallocate memory. The contents will remain unchanged.
+ * __krealloc - like krealloc() but don't free @p.
  * @p: object to reallocate memory for.
  * @new_size: how many bytes of memory are required.
  * @flags: the type of memory to allocate.
  *
- * The contents of the object pointed to are preserved up to the
- * lesser of the new and old sizes.  If @p is %NULL, krealloc()
- * behaves exactly like kmalloc().  If @size is 0 and @p is not a
- * %NULL pointer, the object pointed to is freed.
+ * This function is like krealloc() except it never frees the originally
+ * allocated buffer. Use this if you don't want to free the buffer immediately
+ * like, for example, with RCU.
  */
-void *krealloc(const void *p, size_t new_size, gfp_t flags)
+void *__krealloc(const void *p, size_t new_size, gfp_t flags)
 {
        void *ret;
        size_t ks = 0;
 
-       if (unlikely(!new_size)) {
-               kfree(p);
+       if (unlikely(!new_size))
                return ZERO_SIZE_PTR;
-       }
 
        if (p)
                ks = ksize(p);
@@ -96,10 +94,37 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags)
                return (void *)p;
 
        ret = kmalloc_track_caller(new_size, flags);
-       if (ret && p) {
+       if (ret && p)
                memcpy(ret, p, ks);
+
+       return ret;
+}
+EXPORT_SYMBOL(__krealloc);
+
+/**
+ * krealloc - reallocate memory. The contents will remain unchanged.
+ * @p: object to reallocate memory for.
+ * @new_size: how many bytes of memory are required.
+ * @flags: the type of memory to allocate.
+ *
+ * The contents of the object pointed to are preserved up to the
+ * lesser of the new and old sizes.  If @p is %NULL, krealloc()
+ * behaves exactly like kmalloc().  If @size is 0 and @p is not a
+ * %NULL pointer, the object pointed to is freed.
+ */
+void *krealloc(const void *p, size_t new_size, gfp_t flags)
+{
+       void *ret;
+
+       if (unlikely(!new_size)) {
                kfree(p);
+               return ZERO_SIZE_PTR;
        }
+
+       ret = __krealloc(p, new_size, flags);
+       if (ret && p != ret)
+               kfree(p);
+
        return ret;
 }
 EXPORT_SYMBOL(krealloc);
index 07b5b82c5eabae0e2be2158b7fb738bd95668e3c..0c850427a85b437e4e82e80b6c5f7f5bc52e06d0 100644 (file)
@@ -959,7 +959,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -986,7 +986,7 @@ static unsigned long atalk_sum_skb(const struct sk_buff *skb, int offset,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 690bc3ab186c67cee4ba7844bc19b32ba2c2aafb..1a58af51a2e24a374d2d558e65e8206a5c96f329 100644 (file)
@@ -93,28 +93,20 @@ static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
 
 static int __init ebtable_filter_init(void)
 {
-       int i, j, ret;
+       int ret;
 
        ret = ebt_register_table(&frame_filter);
        if (ret < 0)
                return ret;
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
-               if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
-                       goto cleanup;
-       return ret;
-cleanup:
-       for (j = 0; j < i; j++)
-               nf_unregister_hook(&ebt_ops_filter[j]);
-       ebt_unregister_table(&frame_filter);
+       ret = nf_register_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
+       if (ret < 0)
+               ebt_unregister_table(&frame_filter);
        return ret;
 }
 
 static void __exit ebtable_filter_fini(void)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_filter); i++)
-               nf_unregister_hook(&ebt_ops_filter[i]);
+       nf_unregister_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter));
        ebt_unregister_table(&frame_filter);
 }
 
index 5b495fe2d0b6bb6326d583766c28af7ff272ff93..f60c1e78e57537541e9e1d2ddbd186ff4e681df5 100644 (file)
@@ -100,28 +100,20 @@ static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
 
 static int __init ebtable_nat_init(void)
 {
-       int i, ret, j;
+       int ret;
 
        ret = ebt_register_table(&frame_nat);
        if (ret < 0)
                return ret;
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
-               if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
-                       goto cleanup;
-       return ret;
-cleanup:
-       for (j = 0; j < i; j++)
-               nf_unregister_hook(&ebt_ops_nat[j]);
-       ebt_unregister_table(&frame_nat);
+       ret = nf_register_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
+       if (ret < 0)
+               ebt_unregister_table(&frame_nat);
        return ret;
 }
 
 static void __exit ebtable_nat_fini(void)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ebt_ops_nat); i++)
-               nf_unregister_hook(&ebt_ops_nat[i]);
+       nf_unregister_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat));
        ebt_unregister_table(&frame_nat);
 }
 
index 8a28fc93b72424bafe31a63e336ca4ead747dc89..dd61dcad60199a9ae29242f0c240e4bf4160df77 100644 (file)
@@ -285,7 +285,7 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -315,7 +315,7 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -366,7 +366,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -402,7 +402,7 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
                for (; list; list=list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 53af7841018a73573bfb89819b392bcd92434b42..8d13a9b9f1df66a51daf0aefe71a6a854ea47095 100644 (file)
@@ -1973,7 +1973,7 @@ static void net_tx_action(struct softirq_action *h)
                        struct sk_buff *skb = clist;
                        clist = clist->next;
 
-                       BUG_TRAP(!atomic_read(&skb->users));
+                       WARN_ON(atomic_read(&skb->users));
                        __kfree_skb(skb);
                }
        }
@@ -3847,7 +3847,7 @@ static void rollback_registered(struct net_device *dev)
                dev->uninit(dev);
 
        /* Notifier chain MUST detach us from master device. */
-       BUG_TRAP(!dev->master);
+       WARN_ON(dev->master);
 
        /* Remove entries from kobject tree */
        netdev_unregister_kobject(dev);
@@ -4169,9 +4169,9 @@ void netdev_run_todo(void)
 
                /* paranoia */
                BUG_ON(atomic_read(&dev->refcnt));
-               BUG_TRAP(!dev->ip_ptr);
-               BUG_TRAP(!dev->ip6_ptr);
-               BUG_TRAP(!dev->dn_ptr);
+               WARN_ON(dev->ip_ptr);
+               WARN_ON(dev->ip6_ptr);
+               WARN_ON(dev->dn_ptr);
 
                if (dev->destructor)
                        dev->destructor(dev);
index 2d3035d3abd7a923eef8849f0b31590ee418bdd4..7552495aff7aef090d99654312d5365afa421e95 100644 (file)
@@ -123,7 +123,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue)
                }
        }
 
-       BUG_TRAP(lopt->qlen == 0);
+       WARN_ON(lopt->qlen != 0);
        if (lopt_size > PAGE_SIZE)
                vfree(lopt);
        else
index e4115672b6cfb1094de109c3fd56db796fc3741b..4e0c922741897eb1ad158880ea9cb96f692894fe 100644 (file)
@@ -1200,7 +1200,7 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -1229,7 +1229,7 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -1475,7 +1475,7 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
                skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + frag->size;
                if ((copy = end - offset) > 0) {
@@ -1503,7 +1503,7 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -1552,7 +1552,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -1581,7 +1581,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -1629,7 +1629,7 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -1662,7 +1662,7 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
                        __wsum csum2;
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
@@ -2373,7 +2373,7 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -2397,7 +2397,7 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 4a0ad152c9c4492c35fc3abc6ac21bf4580abb20..a6b3437ff082d9cf98b59bb754b409817f3b4597 100644 (file)
@@ -192,13 +192,13 @@ void sk_stream_kill_queues(struct sock *sk)
        __skb_queue_purge(&sk->sk_error_queue);
 
        /* Next, the write queue. */
-       BUG_TRAP(skb_queue_empty(&sk->sk_write_queue));
+       WARN_ON(!skb_queue_empty(&sk->sk_write_queue));
 
        /* Account for returned memory. */
        sk_mem_reclaim(sk);
 
-       BUG_TRAP(!sk->sk_wmem_queued);
-       BUG_TRAP(!sk->sk_forward_alloc);
+       WARN_ON(sk->sk_wmem_queued);
+       WARN_ON(sk->sk_forward_alloc);
 
        /* It is _impossible_ for the backlog to contain anything
         * when we get here.  All user references to this socket
index 8c6b706963ff01b02c87a54f2c70103e7f7f8f29..164b090d5ac3d5fed2b1d84753d11101f8a790f0 100644 (file)
@@ -27,7 +27,6 @@
 
 #include <linux/dmaengine.h>
 #include <linux/socket.h>
-#include <linux/rtnetlink.h> /* for BUG_TRAP */
 #include <net/tcp.h>
 #include <net/netdma.h>
 
@@ -72,7 +71,7 @@ int dma_skb_copy_datagram_iovec(struct dma_chan *chan,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                copy = end - offset;
@@ -101,7 +100,7 @@ int dma_skb_copy_datagram_iovec(struct dma_chan *chan,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        copy = end - offset;
index 32617e0576cb04e240d654f13c2cbecb64d9230a..743d85fcd6518d6551a88f6362da60d3895269c9 100644 (file)
@@ -164,7 +164,7 @@ static inline bool dccp_loss_free(const u64 s1, const u64 s2, const u64 ndp)
 {
        s64 delta = dccp_delta_seqno(s1, s2);
 
-       BUG_TRAP(delta >= 0);
+       WARN_ON(delta < 0);
        return (u64)delta <= ndp + 1;
 }
 
index 08392ed86c25403ea574c1e2b39786b8488e4625..df2f110df94a4bf2ccc1dcecb1d588fea65ff778 100644 (file)
@@ -413,7 +413,7 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
 
                /* Stop the REQUEST timer */
                inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
-               BUG_TRAP(sk->sk_send_head != NULL);
+               WARN_ON(sk->sk_send_head == NULL);
                __kfree_skb(sk->sk_send_head);
                sk->sk_send_head = NULL;
 
index 2622ace17c467d528efbc41de9705a5b4d4ec49e..a835b88237cbb77d295ba7a036c57b17a1871f91 100644 (file)
@@ -283,7 +283,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
                 * ICMPs are not backlogged, hence we cannot get an established
                 * socket here.
                 */
-               BUG_TRAP(!req->sk);
+               WARN_ON(req->sk);
 
                if (seq != dccp_rsk(req)->dreq_iss) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index b74e8b2cbe55938fe79873097ac17ae16111aba3..da509127e00cbc8077eb8058f74085e3beb03627 100644 (file)
@@ -186,7 +186,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                 * ICMPs are not backlogged, hence we cannot get an established
                 * socket here.
                 */
-               BUG_TRAP(req->sk == NULL);
+               WARN_ON(req->sk != NULL);
 
                if (seq != dccp_rsk(req)->dreq_iss) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index a0b56009611f149da6e172998e104cd62b740984..b622d9744856a9e8320f812cb9e9d85699f1a6a0 100644 (file)
@@ -327,7 +327,7 @@ int dccp_disconnect(struct sock *sk, int flags)
        inet_csk_delack_init(sk);
        __sk_dst_reset(sk);
 
-       BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
+       WARN_ON(inet->num && !icsk->icsk_bind_hash);
 
        sk->sk_error_report(sk);
        return err;
@@ -981,7 +981,7 @@ adjudge_to_death:
         */
        local_bh_disable();
        bh_lock_sock(sk);
-       BUG_TRAP(!sock_owned_by_user(sk));
+       WARN_ON(sock_owned_by_user(sk));
 
        /* Have we already been destroyed by a softirq or backlog? */
        if (state != DCCP_CLOSED && sk->sk_state == DCCP_CLOSED)
index 3608d5342ca22335bf474fcaac15d9ebb10f83a7..6a5b961b6f5c08fdec108f72e66a2352c0199c78 100644 (file)
@@ -106,7 +106,7 @@ static void dccp_retransmit_timer(struct sock *sk)
         *      -- Acks     in client-PARTOPEN state (sec. 8.1.5)
         *      -- CloseReq in server-CLOSEREQ state (sec. 8.3)
         *      -- Close    in   node-CLOSING  state (sec. 8.3)                */
-       BUG_TRAP(sk->sk_send_head != NULL);
+       WARN_ON(sk->sk_send_head == NULL);
 
        /*
         * More than than 4MSL (8 minutes) has passed, a RESET(aborted) was
index 354f6b54e492c2a19bf8cc67cd5f8ce54dbae4d0..8a3ac1fa71a976e8c8f4c4f2dfd9fa87af2f9a99 100644 (file)
@@ -148,10 +148,10 @@ void inet_sock_destruct(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(!sk->sk_wmem_queued);
-       BUG_TRAP(!sk->sk_forward_alloc);
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(sk->sk_wmem_queued);
+       WARN_ON(sk->sk_forward_alloc);
 
        kfree(inet->opt);
        dst_release(sk->sk_dst_cache);
@@ -338,7 +338,7 @@ lookup_protocol:
        answer_flags = answer->flags;
        rcu_read_unlock();
 
-       BUG_TRAP(answer_prot->slab != NULL);
+       WARN_ON(answer_prot->slab == NULL);
 
        err = -ENOBUFS;
        sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
@@ -658,8 +658,8 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
 
        lock_sock(sk2);
 
-       BUG_TRAP((1 << sk2->sk_state) &
-                (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE));
+       WARN_ON(!((1 << sk2->sk_state) &
+                 (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE)));
 
        sock_graft(sk2, newsock);
 
index 2e667e2f90df2e3c97845e7eee59cabd180c55de..91d3d96805d03aef392e0f514b1bd1750a9757d4 100644 (file)
@@ -138,8 +138,8 @@ void in_dev_finish_destroy(struct in_device *idev)
 {
        struct net_device *dev = idev->dev;
 
-       BUG_TRAP(!idev->ifa_list);
-       BUG_TRAP(!idev->mc_list);
+       WARN_ON(idev->ifa_list);
+       WARN_ON(idev->mc_list);
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
               idev, dev ? dev->name : "NIL");
@@ -399,7 +399,7 @@ static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
        }
        ipv4_devconf_setall(in_dev);
        if (ifa->ifa_dev != in_dev) {
-               BUG_TRAP(!ifa->ifa_dev);
+               WARN_ON(ifa->ifa_dev);
                in_dev_hold(in_dev);
                ifa->ifa_dev = in_dev;
        }
index bb81c958b7447ecc1f7cee2b898ab727756cb927..0c1ae68ee84bb24a08b73d6798ceb66db63bd176 100644 (file)
@@ -167,7 +167,7 @@ tb_not_found:
 success:
        if (!inet_csk(sk)->icsk_bind_hash)
                inet_bind_hash(sk, tb, snum);
-       BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb);
+       WARN_ON(inet_csk(sk)->icsk_bind_hash != tb);
        ret = 0;
 
 fail_unlock:
@@ -260,7 +260,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
        }
 
        newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);
-       BUG_TRAP(newsk->sk_state != TCP_SYN_RECV);
+       WARN_ON(newsk->sk_state == TCP_SYN_RECV);
 out:
        release_sock(sk);
        return newsk;
@@ -386,7 +386,7 @@ struct request_sock *inet_csk_search_req(const struct sock *sk,
                    ireq->rmt_addr == raddr &&
                    ireq->loc_addr == laddr &&
                    AF_INET_FAMILY(req->rsk_ops->family)) {
-                       BUG_TRAP(!req->sk);
+                       WARN_ON(req->sk);
                        *prevp = prev;
                        break;
                }
@@ -539,14 +539,14 @@ EXPORT_SYMBOL_GPL(inet_csk_clone);
  */
 void inet_csk_destroy_sock(struct sock *sk)
 {
-       BUG_TRAP(sk->sk_state == TCP_CLOSE);
-       BUG_TRAP(sock_flag(sk, SOCK_DEAD));
+       WARN_ON(sk->sk_state != TCP_CLOSE);
+       WARN_ON(!sock_flag(sk, SOCK_DEAD));
 
        /* It cannot be in hash table! */
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
 
        /* If it has not 0 inet_sk(sk)->num, it must be bound */
-       BUG_TRAP(!inet_sk(sk)->num || inet_csk(sk)->icsk_bind_hash);
+       WARN_ON(inet_sk(sk)->num && !inet_csk(sk)->icsk_bind_hash);
 
        sk->sk_prot->destroy(sk);
 
@@ -629,7 +629,7 @@ void inet_csk_listen_stop(struct sock *sk)
 
                local_bh_disable();
                bh_lock_sock(child);
-               BUG_TRAP(!sock_owned_by_user(child));
+               WARN_ON(sock_owned_by_user(child));
                sock_hold(child);
 
                sk->sk_prot->disconnect(child, O_NONBLOCK);
@@ -647,7 +647,7 @@ void inet_csk_listen_stop(struct sock *sk)
                sk_acceptq_removed(sk);
                __reqsk_free(req);
        }
-       BUG_TRAP(!sk->sk_ack_backlog);
+       WARN_ON(sk->sk_ack_backlog);
 }
 
 EXPORT_SYMBOL_GPL(inet_csk_listen_stop);
index 0546a0bc97ea37bdc1b9bef0c22d6b82ec190abf..6c52e08f786e6f574e71c589ddbb86b26f2f43a0 100644 (file)
@@ -134,8 +134,8 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f,
        struct sk_buff *fp;
        struct netns_frags *nf;
 
-       BUG_TRAP(q->last_in & INET_FRAG_COMPLETE);
-       BUG_TRAP(del_timer(&q->timer) == 0);
+       WARN_ON(!(q->last_in & INET_FRAG_COMPLETE));
+       WARN_ON(del_timer(&q->timer) != 0);
 
        /* Release all fragment data. */
        fp = q->fragments;
index 115f53722d2058d8847a53139ddf2fd635960e7d..44981906fb913e7afbb4f7e03cd0192b9fdcda12 100644 (file)
@@ -305,7 +305,7 @@ unique:
        inet->num = lport;
        inet->sport = htons(lport);
        sk->sk_hash = hash;
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        __sk_add_node(sk, &head->chain);
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
        write_unlock(lock);
@@ -342,7 +342,7 @@ void __inet_hash_nolisten(struct sock *sk)
        rwlock_t *lock;
        struct inet_ehash_bucket *head;
 
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
 
        sk->sk_hash = inet_sk_ehashfn(sk);
        head = inet_ehash_bucket(hashinfo, sk->sk_hash);
@@ -367,7 +367,7 @@ static void __inet_hash(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
        lock = &hashinfo->lhash_lock;
 
@@ -450,7 +450,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row,
                         */
                        inet_bind_bucket_for_each(tb, node, &head->chain) {
                                if (tb->ib_net == net && tb->port == port) {
-                                       BUG_TRAP(!hlist_empty(&tb->owners));
+                                       WARN_ON(hlist_empty(&tb->owners));
                                        if (tb->fastreuse >= 0)
                                                goto next_port;
                                        if (!check_established(death_row, sk,
index 75c2def8f9a0cbcd04a0a324e28cde2e756bb480..d985bd613d25d3895f3b7d32fee5f571062d52be 100644 (file)
@@ -86,7 +86,7 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
                        hashinfo->bhash_size)];
        spin_lock(&bhead->lock);
        tw->tw_tb = icsk->icsk_bind_hash;
-       BUG_TRAP(icsk->icsk_bind_hash);
+       WARN_ON(!icsk->icsk_bind_hash);
        inet_twsk_add_bind_node(tw, &tw->tw_tb->owners);
        spin_unlock(&bhead->lock);
 
index 38d38f058018ddf55b7a17257576cf9310c4af3b..2152d222b954b3eca22bba260e30a5a551316da0 100644 (file)
@@ -488,8 +488,8 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
                qp->q.fragments = head;
        }
 
-       BUG_TRAP(head != NULL);
-       BUG_TRAP(FRAG_CB(head)->offset == 0);
+       WARN_ON(head == NULL);
+       WARN_ON(FRAG_CB(head)->offset != 0);
 
        /* Allocate a new buffer for the datagram. */
        ihlen = ip_hdrlen(head);
index 465544f6281afbab2990bc561f946f60ae4615aa..d533a89e08de14d2e7417b41dddcb99d8e9c881b 100644 (file)
@@ -118,7 +118,7 @@ static int ip_dev_loopback_xmit(struct sk_buff *newskb)
        __skb_pull(newskb, skb_network_offset(newskb));
        newskb->pkt_type = PACKET_LOOPBACK;
        newskb->ip_summed = CHECKSUM_UNNECESSARY;
-       BUG_TRAP(newskb->dst);
+       WARN_ON(!newskb->dst);
        netif_rx(newskb);
        return 0;
 }
index 3be4d07e7ed9880a17d78a5476e240a1d1d40bf0..082f5dd3156c609b324af20330c7875b58ddfbb1 100644 (file)
@@ -55,32 +55,53 @@ static struct xt_table packet_filter = {
 };
 
 /* The work comes in here from netfilter.c */
-static unsigned int arpt_hook(unsigned int hook,
-                             struct sk_buff *skb,
-                             const struct net_device *in,
-                             const struct net_device *out,
-                             int (*okfn)(struct sk_buff *))
+static unsigned int arpt_in_hook(unsigned int hook,
+                                struct sk_buff *skb,
+                                const struct net_device *in,
+                                const struct net_device *out,
+                                int (*okfn)(struct sk_buff *))
 {
-       return arpt_do_table(skb, hook, in, out, init_net.ipv4.arptable_filter);
+       return arpt_do_table(skb, hook, in, out,
+                            dev_net(in)->ipv4.arptable_filter);
+}
+
+static unsigned int arpt_out_hook(unsigned int hook,
+                                 struct sk_buff *skb,
+                                 const struct net_device *in,
+                                 const struct net_device *out,
+                                 int (*okfn)(struct sk_buff *))
+{
+       return arpt_do_table(skb, hook, in, out,
+                            dev_net(out)->ipv4.arptable_filter);
+}
+
+static unsigned int arpt_forward_hook(unsigned int hook,
+                                     struct sk_buff *skb,
+                                     const struct net_device *in,
+                                     const struct net_device *out,
+                                     int (*okfn)(struct sk_buff *))
+{
+       return arpt_do_table(skb, hook, in, out,
+                            dev_net(in)->ipv4.arptable_filter);
 }
 
 static struct nf_hook_ops arpt_ops[] __read_mostly = {
        {
-               .hook           = arpt_hook,
+               .hook           = arpt_in_hook,
                .owner          = THIS_MODULE,
                .pf             = NF_ARP,
                .hooknum        = NF_ARP_IN,
                .priority       = NF_IP_PRI_FILTER,
        },
        {
-               .hook           = arpt_hook,
+               .hook           = arpt_out_hook,
                .owner          = THIS_MODULE,
                .pf             = NF_ARP,
                .hooknum        = NF_ARP_OUT,
                .priority       = NF_IP_PRI_FILTER,
        },
        {
-               .hook           = arpt_hook,
+               .hook           = arpt_forward_hook,
                .owner          = THIS_MODULE,
                .pf             = NF_ARP,
                .hooknum        = NF_ARP_FORWARD,
index 2b472ac2263a612a3d6776764abd1410e1a7ed01..db6d312128e1f3623d78a579bca55dc9c7d125fb 100644 (file)
@@ -32,7 +32,7 @@ static struct
        struct ipt_replace repl;
        struct ipt_standard entries[3];
        struct ipt_error term;
-} initial_table __initdata = {
+} initial_table __net_initdata = {
        .repl = {
                .name = "security",
                .valid_hooks = SECURITY_VALID_HOOKS,
index e4ab0ac94f9225f4ac9f6d2f12d4ece0c1383400..a507c5e27d0e7d057d0870f9d718903c2d083e4e 100644 (file)
@@ -1502,7 +1502,7 @@ unsigned short ip_rt_frag_needed(struct net *net, struct iphdr *iph,
                                    rth->fl.iif != 0 ||
                                    dst_metric_locked(&rth->u.dst, RTAX_MTU) ||
                                    !net_eq(dev_net(rth->u.dst.dev), net) ||
-                                   !rt_is_expired(rth))
+                                   rt_is_expired(rth))
                                        continue;
 
                                if (new_mtu < 68 || new_mtu >= old_mtu) {
index 51bc24d3b8a71580a097ff13df9a4781f10b0575..9d38005abbace6956bf3a5493aaacf58d7a9d90b 100644 (file)
@@ -299,6 +299,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
        ireq->rmt_port          = th->source;
        ireq->loc_addr          = ip_hdr(skb)->daddr;
        ireq->rmt_addr          = ip_hdr(skb)->saddr;
+       ireq->ecn_ok            = 0;
        ireq->snd_wscale        = tcp_opt.snd_wscale;
        ireq->rcv_wscale        = tcp_opt.rcv_wscale;
        ireq->sack_ok           = tcp_opt.sack_ok;
index 0b491bf03db48e275e0b4ad2978d80e9ffd92d57..1ab341e5d3e0f70383fdfa460b0ee9071b282df7 100644 (file)
@@ -1096,7 +1096,7 @@ void tcp_cleanup_rbuf(struct sock *sk, int copied)
 #if TCP_DEBUG
        struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);
 
-       BUG_TRAP(!skb || before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
+       WARN_ON(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq));
 #endif
 
        if (inet_csk_ack_scheduled(sk)) {
@@ -1358,7 +1358,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                                goto found_ok_skb;
                        if (tcp_hdr(skb)->fin)
                                goto found_fin_ok;
-                       BUG_TRAP(flags & MSG_PEEK);
+                       WARN_ON(!(flags & MSG_PEEK));
                        skb = skb->next;
                } while (skb != (struct sk_buff *)&sk->sk_receive_queue);
 
@@ -1421,8 +1421,8 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 
                        tp->ucopy.len = len;
 
-                       BUG_TRAP(tp->copied_seq == tp->rcv_nxt ||
-                                (flags & (MSG_PEEK | MSG_TRUNC)));
+                       WARN_ON(tp->copied_seq != tp->rcv_nxt &&
+                               !(flags & (MSG_PEEK | MSG_TRUNC)));
 
                        /* Ugly... If prequeue is not empty, we have to
                         * process it before releasing socket, otherwise
@@ -1844,7 +1844,7 @@ adjudge_to_death:
         */
        local_bh_disable();
        bh_lock_sock(sk);
-       BUG_TRAP(!sock_owned_by_user(sk));
+       WARN_ON(sock_owned_by_user(sk));
 
        /* Have we already been destroyed by a softirq or backlog? */
        if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
@@ -1973,7 +1973,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));
        __sk_dst_reset(sk);
 
-       BUG_TRAP(!inet->num || icsk->icsk_bind_hash);
+       WARN_ON(inet->num && !icsk->icsk_bind_hash);
 
        sk->sk_error_report(sk);
        return err;
index 75efd244f2afc68138ed9c1ffc6c382670adf542..67ccce2a96bd0d7d506bc7bca08a7ac29c38fea6 100644 (file)
@@ -1629,10 +1629,10 @@ advance_sp:
 out:
 
 #if FASTRETRANS_DEBUG > 0
-       BUG_TRAP((int)tp->sacked_out >= 0);
-       BUG_TRAP((int)tp->lost_out >= 0);
-       BUG_TRAP((int)tp->retrans_out >= 0);
-       BUG_TRAP((int)tcp_packets_in_flight(tp) >= 0);
+       WARN_ON((int)tp->sacked_out < 0);
+       WARN_ON((int)tp->lost_out < 0);
+       WARN_ON((int)tp->retrans_out < 0);
+       WARN_ON((int)tcp_packets_in_flight(tp) < 0);
 #endif
        return flag;
 }
@@ -2181,7 +2181,7 @@ static void tcp_mark_head_lost(struct sock *sk, int packets)
        int err;
        unsigned int mss;
 
-       BUG_TRAP(packets <= tp->packets_out);
+       WARN_ON(packets > tp->packets_out);
        if (tp->lost_skb_hint) {
                skb = tp->lost_skb_hint;
                cnt = tp->lost_cnt_hint;
@@ -2610,7 +2610,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
        /* E. Check state exit conditions. State can be terminated
         *    when high_seq is ACKed. */
        if (icsk->icsk_ca_state == TCP_CA_Open) {
-               BUG_TRAP(tp->retrans_out == 0);
+               WARN_ON(tp->retrans_out != 0);
                tp->retrans_stamp = 0;
        } else if (!before(tp->snd_una, tp->high_seq)) {
                switch (icsk->icsk_ca_state) {
@@ -2972,9 +2972,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets)
        }
 
 #if FASTRETRANS_DEBUG > 0
-       BUG_TRAP((int)tp->sacked_out >= 0);
-       BUG_TRAP((int)tp->lost_out >= 0);
-       BUG_TRAP((int)tp->retrans_out >= 0);
+       WARN_ON((int)tp->sacked_out < 0);
+       WARN_ON((int)tp->lost_out < 0);
+       WARN_ON((int)tp->retrans_out < 0);
        if (!tp->packets_out && tcp_is_sack(tp)) {
                icsk = inet_csk(sk);
                if (tp->lost_out) {
@@ -3877,7 +3877,7 @@ static void tcp_sack_remove(struct tcp_sock *tp)
                        int i;
 
                        /* RCV.NXT must cover all the block! */
-                       BUG_TRAP(!before(tp->rcv_nxt, sp->end_seq));
+                       WARN_ON(before(tp->rcv_nxt, sp->end_seq));
 
                        /* Zap this SACK, by moving forward any other SACKS. */
                        for (i=this_sack+1; i < num_sacks; i++)
index a82df6307567e61675720a9bc6f5f03dcecbdb4b..a2b06d0cc26b70075a6cd649db7cb7c19451f76c 100644 (file)
@@ -418,7 +418,7 @@ void tcp_v4_err(struct sk_buff *skb, u32 info)
                /* ICMPs are not backlogged, hence we cannot get
                   an established socket here.
                 */
-               BUG_TRAP(!req->sk);
+               WARN_ON(req->sk);
 
                if (seq != tcp_rsk(req)->snt_isn) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index 328e0cf42b3c6e164c09da5d54b29566ff5c1480..5ab6ba19c3ce8547076f9fefbdf83c60f3c01c44 100644 (file)
@@ -287,7 +287,7 @@ static void tcp_retransmit_timer(struct sock *sk)
        if (!tp->packets_out)
                goto out;
 
-       BUG_TRAP(!tcp_write_queue_empty(sk));
+       WARN_ON(tcp_write_queue_empty(sk));
 
        if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
            !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
index 74d543d504a1d6fcfbe859235e70bd05418b4364..a7842c54f58a0905201751a9d32a7e270875aaf4 100644 (file)
@@ -313,8 +313,10 @@ static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
 void in6_dev_finish_destroy(struct inet6_dev *idev)
 {
        struct net_device *dev = idev->dev;
-       BUG_TRAP(idev->addr_list==NULL);
-       BUG_TRAP(idev->mc_list==NULL);
+
+       WARN_ON(idev->addr_list != NULL);
+       WARN_ON(idev->mc_list != NULL);
+
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "in6_dev_finish_destroy: %s\n", dev ? dev->name : "NIL");
 #endif
@@ -517,8 +519,9 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
 
 void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
 {
-       BUG_TRAP(ifp->if_next==NULL);
-       BUG_TRAP(ifp->lst_next==NULL);
+       WARN_ON(ifp->if_next != NULL);
+       WARN_ON(ifp->lst_next != NULL);
+
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "inet6_ifa_finish_destroy\n");
 #endif
index 60461ad7fa6ff0deaad6d6d27f463594ae0d7825..c708ca842298d37590b5d9dd0e6f79bcbe0fb63b 100644 (file)
@@ -150,7 +150,7 @@ lookup_protocol:
        answer_flags = answer->flags;
        rcu_read_unlock();
 
-       BUG_TRAP(answer_prot->slab != NULL);
+       WARN_ON(answer_prot->slab == NULL);
 
        err = -ENOBUFS;
        sk = sk_alloc(net, PF_INET6, GFP_KERNEL, answer_prot);
index 87801cc1b2f88405b363ebb11b25d58c1d16eea5..16d43f20b32f83c5994cb4d1479c6393c5ae0961 100644 (file)
@@ -98,7 +98,7 @@ struct request_sock *inet6_csk_search_req(const struct sock *sk,
                    ipv6_addr_equal(&treq->rmt_addr, raddr) &&
                    ipv6_addr_equal(&treq->loc_addr, laddr) &&
                    (!treq->iif || treq->iif == iif)) {
-                       BUG_TRAP(req->sk == NULL);
+                       WARN_ON(req->sk != NULL);
                        *prevp = prev;
                        return req;
                }
index 00a8a5f9380c3d454419c75074a5eccf3c2bc4f7..1646a565825513421332b02411f00aa863f9c261 100644 (file)
@@ -28,7 +28,7 @@ void __inet6_hash(struct sock *sk)
        struct hlist_head *list;
        rwlock_t *lock;
 
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
 
        if (sk->sk_state == TCP_LISTEN) {
                list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];
@@ -202,7 +202,7 @@ unique:
         * in hash table socket with a funny identity. */
        inet->num = lport;
        inet->sport = htons(lport);
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        __sk_add_node(sk, &head->chain);
        sk->sk_hash = hash;
        sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
index 08ea2de28d63c358eeec0a3b59469914dd3fca4b..52dddc25d3e6e6c96a83fd7ccd6ade53b9160fa0 100644 (file)
@@ -287,7 +287,7 @@ static int fib6_dump_node(struct fib6_walker_t *w)
                        w->leaf = rt;
                        return 1;
                }
-               BUG_TRAP(res!=0);
+               WARN_ON(res == 0);
        }
        w->leaf = NULL;
        return 0;
@@ -778,7 +778,7 @@ out:
                        pn->leaf = fib6_find_prefix(info->nl_net, pn);
 #if RT6_DEBUG >= 2
                        if (!pn->leaf) {
-                               BUG_TRAP(pn->leaf != NULL);
+                               WARN_ON(pn->leaf == NULL);
                                pn->leaf = info->nl_net->ipv6.ip6_null_entry;
                        }
 #endif
@@ -942,7 +942,7 @@ struct fib6_node * fib6_locate(struct fib6_node *root,
 
 #ifdef CONFIG_IPV6_SUBTREES
        if (src_len) {
-               BUG_TRAP(saddr!=NULL);
+               WARN_ON(saddr == NULL);
                if (fn && fn->subtree)
                        fn = fib6_locate_1(fn->subtree, saddr, src_len,
                                           offsetof(struct rt6_info, rt6i_src));
@@ -996,9 +996,9 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
                RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
                iter++;
 
-               BUG_TRAP(!(fn->fn_flags&RTN_RTINFO));
-               BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT));
-               BUG_TRAP(fn->leaf==NULL);
+               WARN_ON(fn->fn_flags & RTN_RTINFO);
+               WARN_ON(fn->fn_flags & RTN_TL_ROOT);
+               WARN_ON(fn->leaf != NULL);
 
                children = 0;
                child = NULL;
@@ -1014,7 +1014,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
                        fn->leaf = fib6_find_prefix(net, fn);
 #if RT6_DEBUG >= 2
                        if (fn->leaf==NULL) {
-                               BUG_TRAP(fn->leaf);
+                               WARN_ON(!fn->leaf);
                                fn->leaf = net->ipv6.ip6_null_entry;
                        }
 #endif
@@ -1025,16 +1025,17 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
                pn = fn->parent;
 #ifdef CONFIG_IPV6_SUBTREES
                if (FIB6_SUBTREE(pn) == fn) {
-                       BUG_TRAP(fn->fn_flags&RTN_ROOT);
+                       WARN_ON(!(fn->fn_flags & RTN_ROOT));
                        FIB6_SUBTREE(pn) = NULL;
                        nstate = FWS_L;
                } else {
-                       BUG_TRAP(!(fn->fn_flags&RTN_ROOT));
+                       WARN_ON(fn->fn_flags & RTN_ROOT);
 #endif
                        if (pn->right == fn) pn->right = child;
                        else if (pn->left == fn) pn->left = child;
 #if RT6_DEBUG >= 2
-                       else BUG_TRAP(0);
+                       else
+                               WARN_ON(1);
 #endif
                        if (child)
                                child->parent = pn;
@@ -1154,14 +1155,14 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
 
 #if RT6_DEBUG >= 2
        if (rt->u.dst.obsolete>0) {
-               BUG_TRAP(fn==NULL);
+               WARN_ON(fn != NULL);
                return -ENOENT;
        }
 #endif
        if (fn == NULL || rt == net->ipv6.ip6_null_entry)
                return -ENOENT;
 
-       BUG_TRAP(fn->fn_flags&RTN_RTINFO);
+       WARN_ON(!(fn->fn_flags & RTN_RTINFO));
 
        if (!(rt->rt6i_flags&RTF_CACHE)) {
                struct fib6_node *pn = fn;
@@ -1266,7 +1267,7 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
                        w->node = pn;
 #ifdef CONFIG_IPV6_SUBTREES
                        if (FIB6_SUBTREE(pn) == fn) {
-                               BUG_TRAP(fn->fn_flags&RTN_ROOT);
+                               WARN_ON(!(fn->fn_flags & RTN_ROOT));
                                w->state = FWS_L;
                                continue;
                        }
@@ -1281,7 +1282,7 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
                                continue;
                        }
 #if RT6_DEBUG >= 2
-                       BUG_TRAP(0);
+                       WARN_ON(1);
 #endif
                }
        }
@@ -1323,7 +1324,7 @@ static int fib6_clean_node(struct fib6_walker_t *w)
                        }
                        return 0;
                }
-               BUG_TRAP(res==0);
+               WARN_ON(res != 0);
        }
        w->leaf = rt;
        return 0;
index 6407c64ea4a5c08f1e6260b04e70a4cc142fb3e0..6811901e6b1ec94c0e3084de16c7b8c408bb0503 100644 (file)
@@ -116,7 +116,7 @@ static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
        __skb_pull(newskb, skb_network_offset(newskb));
        newskb->pkt_type = PACKET_LOOPBACK;
        newskb->ip_summed = CHECKSUM_UNNECESSARY;
-       BUG_TRAP(newskb->dst);
+       WARN_ON(!newskb->dst);
 
        netif_rx(newskb);
        return 0;
index ad1cc5bbf977a9d766a78a065277d7aa457f7806..31295c8f619688099477dd5e230c61f8199d51da 100644 (file)
@@ -164,8 +164,8 @@ static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb)
                        calc_padlen(sizeof(*dstopt), 6));
 
        hao->type = IPV6_TLV_HAO;
+       BUILD_BUG_ON(sizeof(*hao) != 18);
        hao->length = sizeof(*hao) - 2;
-       BUG_TRAP(hao->length == 16);
 
        len = ((char *)hao - (char *)dstopt) + sizeof(*hao);
 
@@ -174,7 +174,7 @@ static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb)
        memcpy(&iph->saddr, x->coaddr, sizeof(iph->saddr));
        spin_unlock_bh(&x->lock);
 
-       BUG_TRAP(len == x->props.header_len);
+       WARN_ON(len != x->props.header_len);
        dstopt->hdrlen = (x->props.header_len >> 3) - 1;
 
        return 0;
@@ -317,7 +317,7 @@ static int mip6_destopt_init_state(struct xfrm_state *x)
        x->props.header_len = sizeof(struct ipv6_destopt_hdr) +
                calc_padlen(sizeof(struct ipv6_destopt_hdr), 6) +
                sizeof(struct ipv6_destopt_hao);
-       BUG_TRAP(x->props.header_len == 24);
+       WARN_ON(x->props.header_len != 24);
 
        return 0;
 }
@@ -380,7 +380,7 @@ static int mip6_rthdr_output(struct xfrm_state *x, struct sk_buff *skb)
        rt2->rt_hdr.segments_left = 1;
        memset(&rt2->reserved, 0, sizeof(rt2->reserved));
 
-       BUG_TRAP(rt2->rt_hdr.hdrlen == 2);
+       WARN_ON(rt2->rt_hdr.hdrlen != 2);
 
        memcpy(&rt2->addr, &iph->daddr, sizeof(rt2->addr));
        spin_lock_bh(&x->lock);
index a07abee30497ad25dc8eeee9a98715d9deedb176..6e7131036bc6bf7603daa7401ff418fb836368c0 100644 (file)
@@ -31,7 +31,7 @@ static struct
        struct ip6t_replace repl;
        struct ip6t_standard entries[3];
        struct ip6t_error term;
-} initial_table __initdata = {
+} initial_table __net_initdata = {
        .repl = {
                .name = "security",
                .valid_hooks = SECURITY_VALID_HOOKS,
index cf20bc4fd60db346eab94b35ec3b84ee255dd265..52d06dd4b8176f01a4e0b796e38d1fedf5c8c7c8 100644 (file)
@@ -416,8 +416,8 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
 
        fq_kill(fq);
 
-       BUG_TRAP(head != NULL);
-       BUG_TRAP(NFCT_FRAG6_CB(head)->offset == 0);
+       WARN_ON(head == NULL);
+       WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
 
        /* Unfragmented part is taken from the first segment. */
        payload_len = ((head->data - skb_network_header(head)) -
index 6ab957ec2dd6d4a64bee43cc20d754e7e85ca579..89184b576e2377f1190c69a36476d320e263c265 100644 (file)
@@ -473,8 +473,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
                fq->q.fragments = head;
        }
 
-       BUG_TRAP(head != NULL);
-       BUG_TRAP(FRAG6_CB(head)->offset == 0);
+       WARN_ON(head == NULL);
+       WARN_ON(FRAG6_CB(head)->offset != 0);
 
        /* Unfragmented part is taken from the first segment. */
        payload_len = ((head->data - skb_network_header(head)) -
index 6a68eeb7bbf8ba76b5834d5960991aad4a3021da..a46badd1082d9c55a2388dd3e31d2882db68df06 100644 (file)
@@ -223,6 +223,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
 
        req->expires = 0UL;
        req->retrans = 0;
+       ireq->ecn_ok            = 0;
        ireq->snd_wscale        = tcp_opt.snd_wscale;
        ireq->rcv_wscale        = tcp_opt.rcv_wscale;
        ireq->sack_ok           = tcp_opt.sack_ok;
index ae45f98350143e524798fa6ef4b8014c07685dc1..cff778b23a7feca249b9edab81c9d6afdee8597b 100644 (file)
@@ -421,7 +421,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                /* ICMPs are not backlogged, hence we cannot get
                 * an established socket here.
                 */
-               BUG_TRAP(req->sk == NULL);
+               WARN_ON(req->sk != NULL);
 
                if (seq != tcp_rsk(req)->snt_isn) {
                        NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
index f0fc46c8038d0632bca9fb3b031af5b908f1ff2d..d628df97e02ed5b81002e725bdda6f200649fc08 100644 (file)
@@ -96,8 +96,8 @@ static void pfkey_sock_destruct(struct sock *sk)
                return;
        }
 
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
 
        atomic_dec(&pfkey_socks_nr);
 }
index 3469bc71a385eacb66b2099c334833469ec889cf..4b2c769d555fce4d6131b08f5aac6d53b3a48be6 100644 (file)
@@ -95,7 +95,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
        newlen = newoff + t->len;
        rcu_read_unlock();
 
-       new = krealloc(ct->ext, newlen, gfp);
+       new = __krealloc(ct->ext, newlen, gfp);
        if (!new)
                return NULL;
 
@@ -115,10 +115,10 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp)
                ct->ext = new;
        }
 
-       ct->ext->offset[id] = newoff;
-       ct->ext->len = newlen;
-       memset((void *)ct->ext + newoff, 0, newlen - newoff);
-       return (void *)ct->ext + newoff;
+       new->offset[id] = newoff;
+       new->len = newlen;
+       memset((void *)new + newoff, 0, newlen - newoff);
+       return (void *)new + newoff;
 }
 EXPORT_SYMBOL(__nf_ct_ext_add);
 
index 98bfe277eab22ef15ac39cd08185f0f63cc8030e..b0eacc0007cc39b1c19ccf0c38800e0e50a8d608 100644 (file)
@@ -158,9 +158,10 @@ static void netlink_sock_destruct(struct sock *sk)
                printk(KERN_ERR "Freeing alive netlink socket %p\n", sk);
                return;
        }
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(!nlk_sk(sk)->groups);
+
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(nlk_sk(sk)->groups);
 }
 
 /* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on
index d56cae112dc83b4223b28924f508b426a02c0594..c718e7e3f7dea66facd1483b0b27dd5b4ef974bc 100644 (file)
@@ -260,8 +260,8 @@ static inline struct packet_sock *pkt_sk(struct sock *sk)
 
 static void packet_sock_destruct(struct sock *sk)
 {
-       BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_rmem_alloc));
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
 
        if (!sock_flag(sk, SOCK_DEAD)) {
                printk("Attempt to release alive packet socket: %p\n", sk);
index 4b2682feeedcbcf61d2b1e9fb729ad9cfff9abc1..32e489118bebcc7462316456cf9a4e8dffeaae31 100644 (file)
@@ -660,9 +660,9 @@ static void rxrpc_sock_destructor(struct sock *sk)
 
        rxrpc_purge_queue(&sk->sk_receive_queue);
 
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(sk_unhashed(sk));
-       BUG_TRAP(!sk->sk_socket);
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(!sk_unhashed(sk));
+       WARN_ON(sk->sk_socket);
 
        if (!sock_flag(sk, SOCK_DEAD)) {
                printk("Attempt to release alive rxrpc socket: %p\n", sk);
index 74e662cbb2c5b7a61a1c0815a358899c6abc694a..d308c19aa3f9be6d6087aecfa27dcc55dead6eed 100644 (file)
@@ -41,7 +41,7 @@ void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
                        return;
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
 }
 EXPORT_SYMBOL(tcf_hash_destroy);
 
index 32c3f9d9fb7ad89d06e3b45fc5c664936e9b56ad..38015b49394755d06cd1cfa56306ea358c968a49 100644 (file)
@@ -116,7 +116,7 @@ static void tcf_police_destroy(struct tcf_police *p)
                        return;
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
 }
 
 static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
index 527db2559dd28163173d4ee875be430d31cc7f1f..246f9065ce34b0ad8f7b176807dea7d50221b087 100644 (file)
@@ -345,7 +345,7 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key)
                        }
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
        return 0;
 }
 
@@ -368,7 +368,7 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode **hn;
 
-       BUG_TRAP(!ht->refcnt);
+       WARN_ON(ht->refcnt);
 
        u32_clear_hnode(tp, ht);
 
@@ -380,7 +380,7 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
                }
        }
 
-       BUG_TRAP(0);
+       WARN_ON(1);
        return -ENOENT;
 }
 
@@ -389,7 +389,7 @@ static void u32_destroy(struct tcf_proto *tp)
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode *root_ht = xchg(&tp->root, NULL);
 
-       BUG_TRAP(root_ht != NULL);
+       WARN_ON(root_ht == NULL);
 
        if (root_ht && --root_ht->refcnt == 0)
                u32_destroy_hnode(tp, root_ht);
@@ -407,7 +407,7 @@ static void u32_destroy(struct tcf_proto *tp)
                while ((ht = tp_c->hlist) != NULL) {
                        tp_c->hlist = ht->next;
 
-                       BUG_TRAP(ht->refcnt == 0);
+                       WARN_ON(ht->refcnt != 0);
 
                        kfree(ht);
                }
index f1d2f8ec8b4cde95e206ff184627ae9b8bdcf55b..14954bf4a6836c8fe21731072af228d23f85b33e 100644 (file)
@@ -1175,7 +1175,7 @@ static void cbq_unlink_class(struct cbq_class *this)
                                this->tparent->children = NULL;
                }
        } else {
-               BUG_TRAP(this->sibling == this);
+               WARN_ON(this->sibling != this);
        }
 }
 
@@ -1699,7 +1699,7 @@ static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl)
 {
        struct cbq_sched_data *q = qdisc_priv(sch);
 
-       BUG_TRAP(!cl->filters);
+       WARN_ON(cl->filters);
 
        tcf_destroy_chain(&cl->filter_list);
        qdisc_destroy(cl->q);
index 43abd4d27ea6d767ae9c2227510b2628bb3d2a90..fd2a6cadb1155429fff6de7aa4045ef00899deb9 100644 (file)
@@ -746,5 +746,5 @@ void dev_shutdown(struct net_device *dev)
 {
        netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc);
        shutdown_scheduler_queue(dev, &dev->rx_queue, NULL);
-       BUG_TRAP(!timer_pending(&dev->watchdog_timer));
+       WARN_ON(timer_pending(&dev->watchdog_timer));
 }
index 30c999c61b01f1ab771fb3bb75984b7595fbaee4..75a40951c4f24edc68684462980e74dfd7123214 100644 (file)
@@ -524,7 +524,7 @@ htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, long *diff)
  */
 static inline void htb_activate(struct htb_sched *q, struct htb_class *cl)
 {
-       BUG_TRAP(!cl->level && cl->un.leaf.q && cl->un.leaf.q->q.qlen);
+       WARN_ON(cl->level || !cl->un.leaf.q || !cl->un.leaf.q->q.qlen);
 
        if (!cl->prio_activity) {
                cl->prio_activity = 1 << (cl->un.leaf.aprio = cl->un.leaf.prio);
@@ -542,7 +542,7 @@ static inline void htb_activate(struct htb_sched *q, struct htb_class *cl)
  */
 static inline void htb_deactivate(struct htb_sched *q, struct htb_class *cl)
 {
-       BUG_TRAP(cl->prio_activity);
+       WARN_ON(!cl->prio_activity);
 
        htb_deactivate_prios(q, cl);
        cl->prio_activity = 0;
@@ -757,7 +757,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                u32 *pid;
        } stk[TC_HTB_MAXDEPTH], *sp = stk;
 
-       BUG_TRAP(tree->rb_node);
+       WARN_ON(!tree->rb_node);
        sp->root = tree->rb_node;
        sp->pptr = pptr;
        sp->pid = pid;
@@ -777,7 +777,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                                *sp->pptr = (*sp->pptr)->rb_left;
                        if (sp > stk) {
                                sp--;
-                               BUG_TRAP(*sp->pptr);
+                               WARN_ON(!*sp->pptr);
                                if (!*sp->pptr)
                                        return NULL;
                                htb_next_rb_node(sp->pptr);
@@ -792,7 +792,7 @@ static struct htb_class *htb_lookup_leaf(struct rb_root *tree, int prio,
                        sp->pid = cl->un.inner.last_ptr_id + prio;
                }
        }
-       BUG_TRAP(0);
+       WARN_ON(1);
        return NULL;
 }
 
@@ -810,7 +810,7 @@ static struct sk_buff *htb_dequeue_tree(struct htb_sched *q, int prio,
 
        do {
 next:
-               BUG_TRAP(cl);
+               WARN_ON(!cl);
                if (!cl)
                        return NULL;
 
@@ -1185,7 +1185,7 @@ static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
 {
        struct htb_class *parent = cl->parent;
 
-       BUG_TRAP(!cl->level && cl->un.leaf.q && !cl->prio_activity);
+       WARN_ON(cl->level || !cl->un.leaf.q || cl->prio_activity);
 
        if (parent->cmode != HTB_CAN_SEND)
                htb_safe_rb_erase(&parent->pq_node, q->wait_pq + parent->level);
@@ -1205,7 +1205,7 @@ static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
 static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
 {
        if (!cl->level) {
-               BUG_TRAP(cl->un.leaf.q);
+               WARN_ON(!cl->un.leaf.q);
                qdisc_destroy(cl->un.leaf.q);
        }
        gen_kill_estimator(&cl->bstats, &cl->rate_est);
index 73f53844ce976128f35a59663d2a9b3f13ed254f..8589da666568b50fc47ac1a2c280a0a96c92a5f3 100644 (file)
@@ -536,14 +536,7 @@ static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
 
        opt.limit = q->limit;
        opt.divisor = SFQ_HASH_DIVISOR;
-       opt.flows = 0;
-       if (q->tail != SFQ_DEPTH) {
-               unsigned int i;
-
-               for (i = 0; i < SFQ_HASH_DIVISOR; i++)
-                       if (q->ht[i] != SFQ_DEPTH)
-                               opt.flows++;
-       }
+       opt.flows = q->limit;
 
        NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 
index ec2a0a33fd780e8c8185c74cbbcf453dbebeb99a..8472b8b349c464ac8a900d6b23241ce59efc5397 100644 (file)
@@ -464,7 +464,7 @@ static void sctp_association_destroy(struct sctp_association *asoc)
                spin_unlock_bh(&sctp_assocs_id_lock);
        }
 
-       BUG_TRAP(!atomic_read(&asoc->rmem_alloc));
+       WARN_ON(atomic_read(&asoc->rmem_alloc));
 
        if (asoc->base.malloced) {
                kfree(asoc);
index 6e7fec74bdb3f522250c194a91880bc627ea6c5b..015606b54d9b28325aad62a71ea4fc160b686cd8 100644 (file)
@@ -227,7 +227,7 @@ static void __unix_remove_socket(struct sock *sk)
 
 static void __unix_insert_socket(struct hlist_head *list, struct sock *sk)
 {
-       BUG_TRAP(sk_unhashed(sk));
+       WARN_ON(!sk_unhashed(sk));
        sk_add_node(sk, list);
 }
 
@@ -350,9 +350,9 @@ static void unix_sock_destructor(struct sock *sk)
 
        skb_queue_purge(&sk->sk_receive_queue);
 
-       BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));
-       BUG_TRAP(sk_unhashed(sk));
-       BUG_TRAP(!sk->sk_socket);
+       WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+       WARN_ON(!sk_unhashed(sk));
+       WARN_ON(sk->sk_socket);
        if (!sock_flag(sk, SOCK_DEAD)) {
                printk("Attempt to release alive unix socket: %p\n", sk);
                return;
index 23a2cc04b8cdb5f4a9f215643bfc1df130800322..96036cf2216d09c7dea7907c31c73c3b42f5b7cf 100644 (file)
@@ -718,7 +718,7 @@ int skb_icv_walk(const struct sk_buff *skb, struct hash_desc *desc,
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
 
-               BUG_TRAP(start <= offset + len);
+               WARN_ON(start > offset + len);
 
                end = start + skb_shinfo(skb)->frags[i].size;
                if ((copy = end - offset) > 0) {
@@ -748,7 +748,7 @@ int skb_icv_walk(const struct sk_buff *skb, struct hash_desc *desc,
                for (; list; list = list->next) {
                        int end;
 
-                       BUG_TRAP(start <= offset + len);
+                       WARN_ON(start > offset + len);
 
                        end = start + list->len;
                        if ((copy = end - offset) > 0) {
index 800f669083fb4af63a745778993441981fccf937..c609a4b98e15f9b730701a15e9292fbaffd3000c 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/percpu.h>
-#include <linux/rtnetlink.h>
 #include <linux/smp.h>
 #include <linux/vmalloc.h>
 #include <net/ip.h>
@@ -251,7 +250,7 @@ static void ipcomp_free_tfms(struct crypto_comp **tfms)
                        break;
        }
 
-       BUG_TRAP(pos);
+       WARN_ON(!pos);
 
        if (--pos->users)
                return;
index 72fddafd891a0b3f3be603804e6c5727a56a5b3d..4c6914ef7d92df5830baba92363108e502217c3f 100644 (file)
@@ -538,7 +538,7 @@ EXPORT_SYMBOL(xfrm_state_alloc);
 
 void __xfrm_state_destroy(struct xfrm_state *x)
 {
-       BUG_TRAP(x->km.state == XFRM_STATE_DEAD);
+       WARN_ON(x->km.state != XFRM_STATE_DEAD);
 
        spin_lock_bh(&xfrm_state_lock);
        list_del(&x->all);
index 5ba13908b5b4fd09e8b9470fffd0af8daf5a4553..40d06c533f89fb574efd68507142fbe9b770d9f7 100644 (file)
@@ -5653,27 +5653,20 @@ static struct nf_hook_ops selinux_ipv6_ops[] = {
 static int __init selinux_nf_ip_init(void)
 {
        int err = 0;
-       u32 iter;
 
        if (!selinux_enabled)
                goto out;
 
        printk(KERN_DEBUG "SELinux:  Registering netfilter hooks\n");
 
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) {
-               err = nf_register_hook(&selinux_ipv4_ops[iter]);
-               if (err)
-                       panic("SELinux: nf_register_hook for IPv4: error %d\n",
-                             err);
-       }
+       err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
+       if (err)
+               panic("SELinux: nf_register_hooks for IPv4: error %d\n", err);
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) {
-               err = nf_register_hook(&selinux_ipv6_ops[iter]);
-               if (err)
-                       panic("SELinux: nf_register_hook for IPv6: error %d\n",
-                             err);
-       }
+       err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
+       if (err)
+               panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
 #endif /* IPV6 */
 
 out:
@@ -5685,15 +5678,11 @@ __initcall(selinux_nf_ip_init);
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 static void selinux_nf_ip_exit(void)
 {
-       u32 iter;
-
        printk(KERN_DEBUG "SELinux:  Unregistering netfilter hooks\n");
 
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
-               nf_unregister_hook(&selinux_ipv4_ops[iter]);
+       nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
-               nf_unregister_hook(&selinux_ipv6_ops[iter]);
+       nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
 #endif /* IPV6 */
 }
 #endif