]> git.pld-linux.org Git - packages/kernel.git/commitdiff
- mISDN support - from snap 2004-11-20
authorcieciwa <cieciwa@pld-linux.org>
Mon, 22 Nov 2004 10:49:50 +0000 (10:49 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    mISDN-2004-11-20.patch -> 1.1

mISDN-2004-11-20.patch [new file with mode: 0644]

diff --git a/mISDN-2004-11-20.patch b/mISDN-2004-11-20.patch
new file mode 100644 (file)
index 0000000..8cc0b3e
--- /dev/null
@@ -0,0 +1,49862 @@
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/Kconfig linux-2.6.8.1/drivers/isdn/hardware/Kconfig
+--- linux-2.6.8.1.org/drivers/isdn/hardware/Kconfig    2004-08-14 10:56:23.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/Kconfig        2004-11-22 09:33:38.540684840 +0000
+@@ -8,3 +8,5 @@
+ source "drivers/isdn/hardware/eicon/Kconfig"
++source "drivers/isdn/hardware/mISDN/Kconfig"
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/Makefile linux-2.6.8.1/drivers/isdn/hardware/Makefile
+--- linux-2.6.8.1.org/drivers/isdn/hardware/Makefile   2004-08-14 10:55:19.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/Makefile       2004-11-22 09:33:38.528686664 +0000
+@@ -4,3 +4,4 @@
+ obj-$(CONFIG_CAPI_AVM)                += avm/
+ obj-$(CONFIG_CAPI_EICON)      += eicon/
++obj-$(CONFIG_MISDN_DRV)               += mISDN/
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Kconfig linux-2.6.8.1/drivers/isdn/hardware/mISDN/Kconfig
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Kconfig      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/Kconfig  2004-11-22 09:33:38.563681344 +0000
+@@ -0,0 +1,89 @@
++#
++# modularer ISDN driver
++#
++
++menu "Modular ISDN driver"
++      depends on NET && ISDN && ISDN_CAPI!=n
++
++config MISDN_DRV
++      tristate "Support modular ISDN driver"
++      help
++        Enable support for the modular ISDN driver.
++          This driver is the successor of the famous HiSax driver.
++
++if MISDN_DRV!=n
++
++config MISDN_MEMDEBUG
++      bool "Enable memory leak debug for mISDN"
++      help
++        This option is for watching the use of several resources in mISDN.
++        It includes extra code to maintain list of allocated memory and
++        sk_buffs. On module unload you can see not freed resources an
++        their allocation orging and some object specific informations.
++        If unsure, say 'N'.
++
++config MISDN_AVM_FRITZ
++      bool "Support for AVM Fritz!Cards"
++      depends on PCI || ISA
++      help
++        Enable support for AVM Fritz!Card PCI and PnP.
++
++config MISDN_HFCPCI
++      bool "Support for HFC PCI cards"
++      depends on PCI
++      help
++        Enable support for card with Cologne Chips Design HFC PCI based
++        cards.
++
++config MISDN_HFCMULTI
++      bool "Support for HFC multiport cards (HFC-4S/8S/E1)"
++      depends on PCI
++      help
++        Enable support for card 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)
++
++if MISDN_HFCMULTI!=n
++
++config HFCMULTI_PCIMEM
++      bool "HFC multiport driver with memory mapped IO"
++      depends on PCI
++      help
++        Use memory mapped PCI access rather than IO access.
++        This feature MIGHT be slightly faster, especially when
++        using hardware DTMF detection. Also it may cause trouble with some
++        PCI bridges.
++        If unsure, say 'N'.
++
++endif
++
++config MISDN_SPEEDFAX
++      bool "Support for Sedlbauer Speedfax+"
++      depends on PCI || ISA
++      help
++        Enable support for Sedlbauer Speedfax+.
++
++config MISDN_W6692
++      bool "Support for Winbond 6692 based cards"
++      depends on PCI
++      help
++        Enable support for Winbond 6692 PCI chip based cards.
++
++config MISDN_DSP
++      bool "Digital Audio Processing of transparent data"
++      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
++        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.jolly.de
++        and get more informations about this module and it's usage.
++        If unsure, say 'N'.
++
++endif
++
++endmenu
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Makefile linux-2.6.8.1/drivers/isdn/hardware/mISDN/Makefile
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/Makefile     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/Makefile 2004-11-22 09:33:38.551683168 +0000
+@@ -0,0 +1,71 @@
++# Makefile for the modular ISDN driver
++#
++# EXTRA_CFLAGS += -S -g
++#
++
++ifdef CONFIG_MISDN_MEMDEBUG
++      EXTRA_CFLAGS += -DMISDN_MEMDEBUG
++endif
++
++obj-$(CONFIG_MISDN_DRV) += mISDN_core.o
++obj-$(CONFIG_MISDN_DRV) += mISDN_isac.o
++obj-$(CONFIG_MISDN_DRV) += mISDN_l1.o
++obj-$(CONFIG_MISDN_DRV) += mISDN_l2.o
++obj-$(CONFIG_MISDN_DRV) += mISDN_x25dte.o
++obj-$(CONFIG_MISDN_DRV) += l3udss1.o
++obj-$(CONFIG_MISDN_DRV) += mISDN_capi.o
++obj-$(CONFIG_MISDN_DRV) += mISDN_dtmf.o
++
++ifdef CONFIG_MISDN_AVM_FRITZ
++obj-$(CONFIG_MISDN_DRV) += avmfritz.o
++endif
++
++ifdef CONFIG_MISDN_HFCPCI
++obj-$(CONFIG_MISDN_DRV) += hfcpci.o
++endif
++
++ifdef CONFIG_MISDN_SPEEDFAX
++obj-$(CONFIG_MISDN_DRV) += sedlfax.o
++obj-$(CONFIG_MISDN_DRV) += faxl3.o
++endif
++
++ifdef CONFIG_MISDN_W6692
++obj-$(CONFIG_MISDN_DRV) += w6692pci.o
++endif
++
++ifdef CONFIG_MISDN_HFCMULTI
++obj-$(CONFIG_MISDN_DRV) += hfcmulti.o
++endif
++
++ifdef CONFIG_MISDN_DSP
++obj-$(CONFIG_MISDN_DRV) += mISDN_dsp.o
++endif
++
++ifdef CONFIG_I4L_CAPI_LAYER
++obj-$(CONFIG_MISDN_DRV) += I4LmISDN.o
++endif
++
++# multi objects
++
++sedlfax-objs := sedl_fax.o isar.o
++avmfritz-objs := avm_fritz.o
++hfcpci-objs := hfc_pci.o
++w6692pci-objs := w6692.o
++hfcmulti-objs := hfc_multi.o
++mISDN_isac-objs := isac.o arcofi.o
++mISDN_core-objs := core.o stack.o udevice.o helper.o debug.o fsm.o \
++                      dchannel.o bchannel.o l3helper.o
++ifdef CONFIG_MISDN_MEMDEBUG
++mISDN_core-objs += memdbg.o
++endif
++mISDN_l1-objs := layer1.o
++mISDN_l2-objs := layer2.o tei.o
++l3udss1-objs := layer3.o l3_udss1.o
++mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.o \
++                   asn1_aoc.o asn1_comp.o asn1_generic.o asn1_diversion.o \
++                   asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \
++                   supp_serv.o
++mISDN_dtmf-objs := dtmf.o
++mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o
++mISDN_x25dte-objs := x25_dte.o x25_l3.o
++I4LmISDN-objs := i4l_mISDN.o
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/app_plci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/app_plci.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/app_plci.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/app_plci.c       2004-11-22 09:33:37.679815712 +0000
+@@ -0,0 +1,1882 @@
++/* $Id$
++ *
++ */
++
++#include "m_capi.h"
++#include "helper.h"
++#include "debug.h"
++#include "dss1.h"
++
++#define AppPlciDebug(aplci, lev, fmt, args...) \
++      capidebug(lev, fmt, ## args)
++
++static void   AppPlciClearOtherApps(AppPlci_t *);
++static void   AppPlciInfoIndMsg(AppPlci_t *,  __u32, unsigned char);
++static void   AppPlciInfoIndIE(AppPlci_t *, unsigned char, __u32, Q931_info_t *);
++static int    AppPlciLinkUp(AppPlci_t *);
++static int    AppPlciLinkDown(AppPlci_t *);
++
++static u_char BEARER_SPEECH_64K_ALAW[] = {4, 3, 0x80, 0x90, 0xA3};
++static u_char BEARER_SPEECH_64K_ULAW[] = {4, 3, 0x80, 0x90, 0xA2};
++static u_char BEARER_UNRES_DIGITAL_64K[] = {4, 2, 0x88, 0x90};
++static u_char BEARER_RES_DIGITAL_64K[] = {4, 2, 0x89, 0x90};
++static u_char BEARER_31AUDIO_64K_ALAW[] = {4, 3, 0x90, 0x90, 0xA3};
++static u_char BEARER_31AUDIO_64K_ULAW[] = {4, 3, 0x90, 0x90, 0xA2};
++static u_char HLC_TELEPHONY[] = {0x7d, 2, 0x91, 0x81};
++static u_char HLC_FACSIMILE[] = {0x7d, 2, 0x91, 0x84};
++
++__u16 q931CIPValue(Q931_info_t *qi)
++{
++      __u16   CIPValue = 0;
++      u_char  *p;
++
++      if (!qi)
++              return 0;
++      if (!qi->bearer_capability)
++              return 0;
++      p = (u_char *)qi;
++      p += L3_EXTRA_SIZE + qi->bearer_capability;
++      if (memcmp(p, BEARER_SPEECH_64K_ALAW, 5) == 0
++          || memcmp(p, BEARER_SPEECH_64K_ULAW, 5) == 0) {
++              CIPValue = 1;
++      } else if (memcmp(p, BEARER_UNRES_DIGITAL_64K, 4) == 0) {
++              CIPValue = 2;
++      } else if (memcmp(p, BEARER_RES_DIGITAL_64K, 4) == 0) {
++              CIPValue = 3;
++      } else if (memcmp(p, BEARER_31AUDIO_64K_ALAW, 5) == 0
++                 || memcmp(p, BEARER_31AUDIO_64K_ULAW, 5) == 0) {
++              CIPValue = 4;
++      } else {
++              CIPValue = 0;
++      }
++
++      if (!qi->hlc)
++              return CIPValue;
++
++      p = (u_char *)qi;
++      p += L3_EXTRA_SIZE + qi->hlc;
++      if ((CIPValue == 1) || (CIPValue == 4)) {
++              if (memcmp(p, HLC_TELEPHONY, 4) == 0) {
++                      CIPValue = 16;
++              } else if (memcmp(p, HLC_FACSIMILE, 4) == 0) {
++                      CIPValue = 17;
++              }
++      }
++      return CIPValue;
++}
++
++u_int plci_parse_channel_id(u_char *p)
++{
++      u_int   cid = -1;
++      int     l;
++
++      if (p) {
++              p++;
++              capidebug(CAPI_DBG_PLCI_INFO, "%s: l(%d) %x", __FUNCTION__, p[0], p[1]);
++              l = *p++;
++              if (l == 1) {
++                      cid = *p;
++              } else if (l == 3) {
++                      cid =  *p++ << 16;
++                      cid |= *p++ << 8;
++                      cid |= *p;
++              }
++      }
++      return(cid);
++}
++
++__u16 CIPValue2setup(__u16 CIPValue, struct sk_buff *skb)
++{
++      switch (CIPValue) {
++              case 16:
++                      mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW);
++                      mISDN_AddvarIE(skb, HLC_TELEPHONY);
++                      break;
++              case 17:
++                      mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW);
++                      mISDN_AddvarIE(skb, HLC_FACSIMILE);
++                      break;
++              case 1:
++                      mISDN_AddvarIE(skb, BEARER_SPEECH_64K_ALAW);
++                      break;
++              case 2:
++                      mISDN_AddvarIE(skb, BEARER_UNRES_DIGITAL_64K);
++                      break;
++              case 3:
++                      mISDN_AddvarIE(skb, BEARER_RES_DIGITAL_64K);
++                      break;
++              case 4:
++                      mISDN_AddvarIE(skb, BEARER_31AUDIO_64K_ALAW);
++                      break;
++              default:
++                      return CapiIllMessageParmCoding;
++      }
++      return 0;
++}
++
++__u16 cmsg2setup_req(_cmsg *cmsg, struct sk_buff *skb)
++{
++      if (CIPValue2setup(cmsg->CIPValue, skb))
++              goto err;
++      mISDN_AddIE(skb, IE_CALLING_PN, cmsg->CallingPartyNumber);
++      mISDN_AddIE(skb, IE_CALLING_SUB, cmsg->CallingPartySubaddress);
++      mISDN_AddIE(skb, IE_CALLED_PN, cmsg->CalledPartyNumber);
++      mISDN_AddIE(skb, IE_CALLED_SUB, cmsg->CalledPartySubaddress);
++      mISDN_AddIE(skb, IE_BEARER, cmsg->BC);
++      mISDN_AddIE(skb, IE_LLC, cmsg->LLC);
++      mISDN_AddIE(skb, IE_HLC, cmsg->HLC);
++      return 0;
++ err:
++      return CapiIllMessageParmCoding;
++}
++
++__u16 cmsg2info_req(_cmsg *cmsg, struct sk_buff *skb)
++{
++      mISDN_AddIE(skb, IE_KEYPAD, cmsg->Keypadfacility);
++      mISDN_AddIE(skb, IE_CALLED_PN, cmsg->CalledPartyNumber);
++      return 0;
++}
++
++__u16 cmsg2alerting_req(_cmsg *cmsg, struct sk_buff *skb)
++{
++      mISDN_AddIE(skb, IE_USER_USER, cmsg->Useruserdata);
++      return 0;
++}
++
++__u16 AppPlciCheckBprotocol(AppPlci_t *aplci, _cmsg *cmsg)
++{
++      struct capi_ctr *ctrl = aplci->contr->ctrl;
++      u_long          sprot;
++
++      sprot = ctrl->profile.support1;
++      if (!test_bit(cmsg->B1protocol, &sprot))
++              return CapiB1ProtocolNotSupported;
++      sprot = ctrl->profile.support2;
++      if (!test_bit(cmsg->B2protocol, &sprot))
++              return CapiB2ProtocolNotSupported;
++      sprot = ctrl->profile.support3;
++      if (!test_bit(cmsg->B3protocol, &sprot))
++              return CapiB3ProtocolNotSupported;
++      aplci->Bprotocol.B1 = cmsg->B1protocol;
++      aplci->Bprotocol.B2 = cmsg->B2protocol;
++      aplci->Bprotocol.B3 = cmsg->B3protocol;
++      if (cmsg->B1configuration && cmsg->B1configuration[0]) {
++              if (cmsg->B1configuration[0] > 15) {
++                      int_errtxt("B1cfg too large(%d)", cmsg->B1configuration[0]);
++                      return CapiB1ProtocolParameterNotSupported;
++              }
++              memcpy(&aplci->Bprotocol.B1cfg[0], cmsg->B1configuration, cmsg->B1configuration[0] + 1);
++      } else
++              aplci->Bprotocol.B1cfg[0] = 0;
++      if (cmsg->B2configuration && cmsg->B2configuration[0]) {
++              if (cmsg->B2configuration[0] > 15) {
++                      int_errtxt("B2cfg too large(%d)", cmsg->B2configuration[0]);
++                      return CapiB2ProtocolParameterNotSupported;
++              }
++              memcpy(&aplci->Bprotocol.B2cfg[0], cmsg->B2configuration, cmsg->B2configuration[0] + 1);
++      } else
++              aplci->Bprotocol.B2cfg[0] = 0;
++      if (cmsg->B3configuration && cmsg->B3configuration[0]) {
++              if (cmsg->B3configuration[0] > 79) {
++                      int_errtxt("B3cfg too large(%d)", cmsg->B3configuration[0]);
++                      return CapiB3ProtocolParameterNotSupported;
++              }
++              memcpy(&aplci->Bprotocol.B3cfg[0], cmsg->B3configuration, cmsg->B3configuration[0] + 1);
++      } else
++              aplci->Bprotocol.B3cfg[0] = 0;
++      return 0;
++}
++
++// --------------------------------------------------------------------
++// PLCI state machine
++//
++// Some rules:
++//   *  EV_AP_*  events come from CAPI Application
++//   *  EV_L3_*  events come from the ISDN stack
++//   *  EV_PI_*  events generated in PLCI handling
++//   *  messages are send in the routine that handle the event
++//
++// --------------------------------------------------------------------
++
++enum {
++      ST_PLCI_P_0,
++      ST_PLCI_P_0_1,
++      ST_PLCI_P_1,
++      ST_PLCI_P_2,
++      ST_PLCI_P_3,
++      ST_PLCI_P_4,
++      ST_PLCI_P_ACT,
++      ST_PLCI_P_5,
++      ST_PLCI_P_6,
++      ST_PLCI_P_RES,
++}
++
++const ST_PLCI_COUNT = ST_PLCI_P_RES + 1;
++
++static char *str_st_plci[] = {        
++      "ST_PLCI_P_0",
++      "ST_PLCI_P_0_1",
++      "ST_PLCI_P_1",
++      "ST_PLCI_P_2",
++      "ST_PLCI_P_3",
++      "ST_PLCI_P_4",
++      "ST_PLCI_P_ACT",
++      "ST_PLCI_P_5",
++      "ST_PLCI_P_6",
++      "ST_PLCI_P_RES",
++}; 
++
++enum {
++      EV_AP_CONNECT_REQ,
++      EV_PI_CONNECT_CONF,
++      EV_PI_CONNECT_IND,
++      EV_AP_CONNECT_RESP,
++      EV_PI_CONNECT_ACTIVE_IND,
++      EV_AP_CONNECT_ACTIVE_RESP,
++      EV_AP_ALERT_REQ,
++      EV_AP_INFO_REQ,
++      EV_PI_INFO_IND,
++      EV_PI_FACILITY_IND,
++      EV_AP_SELECT_B_PROTOCOL_REQ,
++      EV_AP_DISCONNECT_REQ,
++      EV_PI_DISCONNECT_IND,
++      EV_AP_DISCONNECT_RESP,
++      EV_AP_SUSPEND_REQ,
++      EV_PI_SUSPEND_CONF,
++      EV_AP_RESUME_REQ,
++      EV_PI_RESUME_CONF,
++      EV_PI_CHANNEL_ERR,
++      EV_L3_SETUP_IND,
++      EV_L3_SETUP_CONF_ERR,
++      EV_L3_SETUP_CONF,
++      EV_L3_SETUP_COMPL_IND,
++      EV_L3_DISCONNECT_IND,
++      EV_L3_RELEASE_IND,
++      EV_L3_RELEASE_PROC_IND,
++      EV_L3_NOTIFY_IND,
++      EV_L3_SUSPEND_ERR,
++      EV_L3_SUSPEND_CONF,
++      EV_L3_RESUME_ERR,
++      EV_L3_RESUME_CONF,
++      EV_L3_REJECT_IND,
++      EV_PH_CONTROL_IND,
++      EV_AP_RELEASE,
++}
++
++const EV_PLCI_COUNT = EV_AP_RELEASE + 1;
++
++static char* str_ev_plci[] = {
++      "EV_AP_CONNECT_REQ",
++      "EV_PI_CONNECT_CONF",
++      "EV_PI_CONNECT_IND",
++      "EV_AP_CONNECT_RESP",
++      "EV_PI_CONNECT_ACTIVE_IND",
++      "EV_AP_CONNECT_ACTIVE_RESP",
++      "EV_AP_ALERT_REQ",
++      "EV_AP_INFO_REQ",
++      "EV_PI_INFO_IND",
++      "EV_PI_FACILITY_IND",
++      "EV_AP_SELECT_B_PROTOCOL_REQ",
++      "EV_AP_DISCONNECT_REQ",
++      "EV_PI_DISCONNECT_IND",
++      "EV_AP_DISCONNECT_RESP",
++      "EV_AP_SUSPEND_REQ",
++      "EV_PI_SUSPEND_CONF",
++      "EV_AP_RESUME_REQ",
++      "EV_PI_RESUME_CONF",
++      "EV_PI_CHANNEL_ERR",
++      "EV_L3_SETUP_IND",
++      "EV_L3_SETUP_CONF_ERR",
++      "EV_L3_SETUP_CONF",
++      "EV_L3_SETUP_COMPL_IND",
++      "EV_L3_DISCONNECT_IND",
++      "EV_L3_RELEASE_IND",
++      "EV_L3_RELEASE_PROC_IND",
++      "EV_L3_NOTIFY_IND",
++      "EV_L3_SUSPEND_ERR",
++      "EV_L3_SUSPEND_CONF",
++      "EV_L3_RESUME_ERR",
++      "EV_L3_RESUME_CONF",
++      "EV_L3_REJECT_IND",
++      "EV_PH_CONTROL_IND",
++      "EV_AP_RELEASE",
++};
++
++static struct Fsm plci_fsm =
++{ 0, 0, 0, 0, 0 };
++
++static void
++AppPlci_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      char tmp[128];
++      char *p = tmp;
++      va_list args;
++      AppPlci_t *aplci = fi->userdata;
++  
++      va_start(args, fmt);
++      p += sprintf(p, "APLCI 0x%x: ", aplci->addr);
++      p += vsprintf(p, fmt, args);
++      *p = 0;
++      AppPlciDebug(aplci, CAPI_DBG_PLCI_STATE, tmp);
++      va_end(args);
++}
++
++static inline void
++Send2Application(AppPlci_t *aplci, _cmsg *cmsg)
++{
++      SendCmsg2Application(aplci->appl, cmsg);
++}
++
++static void
++SendingDelayedMsg(AppPlci_t *aplci)
++{
++      struct sk_buff  *skb;
++
++      while((skb = skb_dequeue(&aplci->delayedq))) {
++              if (test_bit(APPL_STATE_RELEASE, &aplci->appl->state)) {
++                      printk(KERN_WARNING "%s: Application allready released\n", __FUNCTION__);
++                      dev_kfree_skb(skb);
++              } else {
++#ifdef OLDCAPI_DRIVER_INTERFACE
++                      aplci->appl->contr->ctrl->handle_capimsg(aplci->appl->contr->ctrl, aplci->appl->ApplId, skb);
++#else
++                      capi_ctr_handle_message(aplci->appl->contr->ctrl, aplci->appl->ApplId, skb);
++#endif
++              }
++      }
++      test_and_clear_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state);
++}
++
++static void
++Send2ApplicationDelayed(AppPlci_t *aplci, _cmsg *cmsg)
++{
++      struct sk_buff  *skb;
++      
++      if (test_bit(APPL_STATE_RELEASE, &aplci->appl->state)) {
++              printk(KERN_WARNING "%s: Application allready released\n", __FUNCTION__);
++              cmsg_free(cmsg);
++              return;
++      }
++      if (!(skb = alloc_skb(CAPI_MSG_DEFAULT_LEN, GFP_ATOMIC))) {
++              printk(KERN_WARNING "%s: no mem for %d bytes\n", __FUNCTION__, CAPI_MSG_DEFAULT_LEN);
++              int_error();
++              cmsg_free(cmsg);
++              return;
++      }
++      capi_cmsg2message(cmsg, skb->data);
++      AppPlciDebug(aplci, CAPI_DBG_APPL_MSG, "%s: len(%d) applid(%x) %s msgnr(%d) addr(%08x)",
++              __FUNCTION__, CAPIMSG_LEN(skb->data), cmsg->ApplId, capi_cmd2str(cmsg->Command, cmsg->Subcommand),
++              cmsg->Messagenumber, cmsg->adr.adrController);
++      cmsg_free(cmsg);
++      if (CAPI_MSG_DEFAULT_LEN < CAPIMSG_LEN(skb->data)) {
++              printk(KERN_ERR "%s: CAPI_MSG_DEFAULT_LEN overrun (%d/%d)\n", __FUNCTION__,
++                      CAPIMSG_LEN(skb->data), CAPI_MSG_DEFAULT_LEN);
++              int_error();
++              dev_kfree_skb(skb);
++              return;
++      }
++      skb_put(skb, CAPIMSG_LEN(skb->data));
++      skb_queue_tail(&aplci->delayedq, skb);
++      if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state) &&
++              !test_and_set_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state))
++              SendingDelayedMsg(aplci);
++}
++
++static inline void
++AppPlciCmsgHeader(AppPlci_t *aplci, _cmsg *cmsg, __u8 cmd, __u8 subcmd)
++{
++      capi_cmsg_header(cmsg, aplci->appl->ApplId, cmd, subcmd, 
++                       aplci->appl->MsgId++, aplci->addr);
++}
++
++static void
++plci_connect_req(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t          *plci = aplci->plci;
++      struct sk_buff  *skb;
++      _cmsg           *cmsg = arg;
++      __u16           Info = 0;
++
++      mISDN_FsmChangeState(fi, ST_PLCI_P_0_1);
++      test_and_set_bit(PLCI_STATE_OUTGOING, &plci->state);
++
++      skb = mISDN_alloc_l3msg(260, MT_SETUP);
++      
++      if (!skb) {
++              Info = CapiNoPlciAvailable;
++              goto answer;
++      }
++      if ((Info = cmsg2setup_req(cmsg, skb))) {
++              goto answer;
++      }
++      if ((Info = AppPlciCheckBprotocol(aplci, cmsg))) {
++              goto answer;
++      }
++
++      plciNewCrReq(plci);
++      plciL4L3(plci, CC_SETUP | REQUEST, skb);
++answer:
++      capi_cmsg_answer(cmsg);
++      cmsg->Info = Info;
++      if (cmsg->Info == 0) 
++              cmsg->adr.adrPLCI = aplci->addr;
++      mISDN_FsmEvent(fi, EV_PI_CONNECT_CONF, cmsg);
++}
++
++static void
++plci_connect_conf(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      _cmsg           *cmsg = arg;
++  
++      if (cmsg->Info == 0) {
++              Send2Application(aplci, cmsg);
++              mISDN_FsmChangeState(fi, ST_PLCI_P_1);
++      } else {
++              Send2Application(aplci, cmsg);
++              mISDN_FsmChangeState(fi, ST_PLCI_P_0);
++              AppPlciDestr(aplci);
++      }
++}
++
++static void
++plci_connect_ind(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_PLCI_P_2);
++      Send2Application(fi->userdata, arg);
++}
++
++static void plci_suspend_req(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t  *plci = aplci->plci;
++
++      plciL4L3(plci, CC_SUSPEND | REQUEST, arg); 
++}
++
++static void plci_resume_req(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t          *plci = aplci->plci;
++
++      // we already sent CONF with Info = SuppInfo = 0
++      mISDN_FsmChangeState(fi, ST_PLCI_P_RES);
++      plciNewCrReq(plci);
++      plciL4L3(plci, CC_RESUME | REQUEST, arg);
++}
++
++static void
++plci_alert_req(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t          *plci = aplci->plci;
++      _cmsg           *cmsg = arg;
++      __u16           Info = 0;
++      
++      if (test_and_set_bit(PLCI_STATE_ALERTING, &plci->state)) {
++              Info = 0x0003; // other app is already alerting
++      } else {
++              struct sk_buff  *skb = mISDN_alloc_l3msg(10, MT_ALERTING);
++              if (!skb) {
++                      int_error();
++                      goto answer;
++              }
++              Info = cmsg2alerting_req(cmsg, skb);
++              if (Info == 0) {
++                      plciL4L3(plci, CC_ALERTING | REQUEST, skb);
++              }
++      }
++answer:       
++      capi_cmsg_answer(cmsg);
++      cmsg->Info = Info;
++      Send2Application(aplci, cmsg);
++}
++
++static void
++plci_connect_resp(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t          *plci = aplci->plci;
++      unsigned char   cause[4];
++      _cmsg           *cmsg = arg;
++      struct sk_buff  *skb;
++
++      if (cmsg->Reject == 0) { // accept
++              if (AppPlciCheckBprotocol(aplci, cmsg)) {
++                      int_error();
++              }
++              AppPlciClearOtherApps(aplci);
++              plciL4L3(plci, CC_CONNECT | REQUEST, NULL);
++              mISDN_FsmChangeState(fi, ST_PLCI_P_4);
++              cmsg_free(cmsg);
++              return;
++      }
++      // ignore, reject 
++      memcpy(cause, "\x02\x80", 2); // IE CAUSE, location = local
++      switch (cmsg->Reject) {
++              case 2: cause[2] = 0x90; break; // normal call clearing
++              case 3: cause[2] = 0x91; break; // user busy
++              case 4: cause[2] = 0xac; break; // req circuit/channel not avail
++              case 5: cause[2] = 0x9d; break; // fac rejected
++              case 6: cause[2] = 0x86; break; // channel unacceptable
++              case 7: cause[2] = 0xd8; break; // incompatible dest
++              case 8: cause[2] = 0x9b; break; // dest out of order
++              default:
++                      if ((cmsg->Reject & 0xff00) == 0x3400) {
++                              cause[2] = cmsg->Reject & 0xff;
++                      } else {
++                              cause[2] = 0x90; break; // normal call clearing
++                      }
++      }
++      // FIXME
++      // WHY ???
++      // if (cmsg->Reject != 1) {
++              // ignore
++      //      AppPlciClearOtherApps(aplci);
++      // }
++      // plciDetachAppPlci(plci, aplci);
++      if (plci->nAppl == 1) {
++              int prim;
++              if (test_bit(PLCI_STATE_ALERTING, &plci->state))
++                      prim = CC_DISCONNECT | REQUEST;
++              else 
++                      // if we already answered, we can't just ignore but must clear actively
++                      prim = CC_RELEASE_COMPLETE | REQUEST;
++              skb = mISDN_alloc_l3msg(10, MT_DISCONNECT);
++              if (!skb) {
++                      plciL4L3(plci, prim, NULL);
++              } else {
++                      mISDN_AddIE(skb, IE_CAUSE, cause);
++                      plciL4L3(plci, prim, skb);
++              }
++      }
++      cmsg->Command = CAPI_DISCONNECT;
++      cmsg->Subcommand = CAPI_IND;
++      cmsg->Messagenumber = aplci->appl->MsgId++;
++      cmsg->Reject = 0x3400 | cause[2];
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_connect_active_ind(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t *aplci = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_PLCI_P_ACT);
++      AppPlciLinkUp(aplci);
++      if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state))
++              Send2Application(aplci, arg);
++      else
++              Send2ApplicationDelayed(aplci, arg);
++}
++
++static void plci_connect_active_resp(struct FsmInst *fi, int event, void *arg)
++{
++      cmsg_free(arg);
++}
++
++static void plci_disconnect_req(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t          *plci = aplci->plci;
++      u_char          cause[4];
++      _cmsg           *cmsg = arg;
++
++      mISDN_FsmChangeState(fi, ST_PLCI_P_5);
++      
++      if (!plci) {
++              int_error();
++              return;
++      }
++      // FIXME handle additional Inf
++      capi_cmsg_answer(cmsg);
++      cmsg->Reason = 0; // disconnect initiated
++      Send2Application(aplci, cmsg);
++
++      AppPlciLinkDown(aplci);
++
++      if (!aplci->cause[0]) { // FIXME handle additional Info
++              struct sk_buff  *skb;
++
++              skb = mISDN_alloc_l3msg(10, MT_DISCONNECT);
++              if (!skb) {
++                      plciL4L3(plci, CC_DISCONNECT | REQUEST, NULL);
++              } else {
++                      memcpy(cause, "\x02\x80\x90", 3); // normal call clearing
++                      mISDN_AddIE(skb, IE_CAUSE, cause);
++                      plciL4L3(plci, CC_DISCONNECT | REQUEST, skb);
++              }
++      } else {
++              /* release physical link */
++              // FIXME
++              plciL4L3(plci, CC_RELEASE | REQUEST, NULL);
++      }
++}
++
++static void plci_suspend_conf(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_PLCI_P_5);
++}
++
++static void plci_resume_conf(struct FsmInst *fi, int event, void *arg)
++{
++      // facility_ind Resume: Reason = 0
++      AppPlci_t       *aplci = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_PLCI_P_ACT);
++      AppPlciLinkUp(aplci);
++      if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state))
++              Send2Application(aplci, arg);
++      else
++              Send2ApplicationDelayed(aplci, arg);
++}
++
++static void
++plci_disconnect_ind(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_PLCI_P_6);
++      Send2Application(fi->userdata, arg);
++}
++
++static void
++plci_disconnect_resp(struct FsmInst *fi, int event, void *arg)
++{
++      if (arg)
++              cmsg_free(arg);
++      mISDN_FsmChangeState(fi, ST_PLCI_P_0);
++      AppPlciDestr(fi->userdata);
++}
++
++static void
++plci_appl_release(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlciDestr(fi->userdata);
++}
++
++static void
++plci_appl_release_disc(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t          *plci = aplci->plci;
++
++      mISDN_FsmChangeState(fi, ST_PLCI_P_5);
++      
++      if (!plci) {
++              int_error();
++              return;
++      }
++
++      AppPlciLinkDown(aplci);
++
++      if (!aplci->cause[0]) {
++              struct sk_buff  *skb;
++
++              skb = mISDN_alloc_l3msg(10, MT_DISCONNECT);
++              if (!skb) {
++                      plciL4L3(plci, CC_DISCONNECT | REQUEST, NULL);
++              } else {
++                      u_char *cause = "\x02\x80\x9f";
++
++                      mISDN_AddIE(skb, IE_CAUSE, cause);
++                      plciL4L3(plci, CC_DISCONNECT | REQUEST, skb);
++              }
++      } else {
++              /* release physical link */
++              // FIXME
++              plciL4L3(plci, CC_RELEASE | REQUEST, NULL);
++      }
++}
++
++static void
++plci_cc_setup_conf(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      _cmsg           *cmsg;
++      Q931_info_t     *qi = arg;
++      u_char          *p;
++
++      if (aplci->channel == -1) {/* no valid channel set */
++              mISDN_FsmEvent(fi, EV_PI_CHANNEL_ERR, NULL);
++              return;
++      }
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT_ACTIVE, CAPI_IND);
++      if (qi) {
++              p = (u_char *)qi;
++              p += L3_EXTRA_SIZE;
++              if (qi->connected_nr)
++                      cmsg->ConnectedNumber = &p[qi->connected_nr + 1];
++              if (qi->connected_sub)
++                      cmsg->ConnectedSubaddress = &p[qi->connected_sub + 1];
++              if (qi->llc)
++                      cmsg->LLC = &p[qi->llc + 1];
++      }
++      if (mISDN_FsmEvent(fi, EV_PI_CONNECT_ACTIVE_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_cc_setup_conf_err(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      _cmsg           *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND);
++      cmsg->Reason = CapiProtocolErrorLayer3;
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_channel_err(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      _cmsg           *cmsg;
++      u_char          cause[4];
++      struct sk_buff  *skb;
++
++      skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE);
++      if (skb) {
++              cause[0] = 2;
++              cause[1] = 0x80;
++              cause[2] = 0x86; /* channel unacceptable */
++              mISDN_AddIE(skb, IE_CAUSE, cause);
++              plciL4L3(aplci->plci, CC_RELEASE_COMPLETE | REQUEST, skb);
++      } else
++              int_error();
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND);
++      cmsg->Reason = CapiProtocolErrorLayer3;
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_cc_setup_ind(struct FsmInst *fi, int event, void *arg)
++{ 
++      AppPlci_t       *aplci = fi->userdata;
++      Q931_info_t     *qi = arg;
++      _cmsg           *cmsg;
++      u_char          *p;
++
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT, CAPI_IND);
++
++      // FIXME: CW
++      if (qi) {
++              p = (u_char *)qi;
++              p += L3_EXTRA_SIZE;
++              cmsg->CIPValue = q931CIPValue(qi);
++              if (qi->called_nr)
++                      cmsg->CalledPartyNumber = &p[qi->called_nr + 1];
++              if (qi->called_sub)
++                      cmsg->CalledPartySubaddress = &p[qi->called_sub + 1];
++              if (qi->calling_nr)
++                      cmsg->CallingPartyNumber = &p[qi->calling_nr + 1];
++              if (qi->calling_sub)
++                      cmsg->CallingPartySubaddress = &p[qi->calling_sub + 1];
++              if (qi->bearer_capability)
++                      cmsg->BC = &p[qi->bearer_capability + 1];
++              if (qi->llc)
++                      cmsg->LLC = &p[qi->llc + 1];
++              if (qi->hlc)
++                      cmsg->HLC = &p[qi->hlc + 1];
++              // all else set to default
++      }
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_CONNECT_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_cc_setup_compl_ind(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      _cmsg           *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_CONNECT_ACTIVE, CAPI_IND);
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_CONNECT_ACTIVE_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_cc_disconnect_ind(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Q931_info_t     *qi = arg;
++      u_char          *p;
++
++      if (qi) {
++              p = (u_char *)qi;
++              p += L3_EXTRA_SIZE;
++              if (qi->cause)
++                      memcpy(aplci->cause, &p[qi->cause + 1], 3);
++      }
++      if (aplci->appl->InfoMask & CAPI_INFOMASK_EARLYB3)
++              return;
++
++      AppPlciLinkDown(aplci);
++      plciL4L3(aplci->plci, CC_RELEASE | REQUEST, NULL);
++}
++
++static void
++plci_cc_release_ind(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Q931_info_t     *qi = arg;
++      u_char          *p;
++      _cmsg           *cmsg;
++
++      AppPlciLinkDown(aplci);
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND);
++      if (qi) {
++              p = (u_char *)qi;
++              p += L3_EXTRA_SIZE;
++              if (qi->cause)
++                      cmsg->Reason = 0x3400 | p[qi->cause + 3];
++              else if (aplci->cause[0]) // cause from CC_DISCONNECT IND
++                      cmsg->Reason = 0x3400 | aplci->cause[2];
++              else
++                      cmsg->Reason = 0;
++      } else {
++              cmsg->Reason = CapiProtocolErrorLayer1;
++      }
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_cc_notify_ind(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Q931_info_t     *qi = arg;
++      _cmsg           *cmsg;
++      __u8            tmp[10], *p, *nf;
++
++      if (!qi || !qi->notify)
++              return;
++      nf = (u_char *)qi;
++      nf += L3_EXTRA_SIZE + qi->notify + 1;
++      if (nf[0] != 1) // len != 1
++              return;
++      switch (nf[1]) {
++              case 0x80: // user suspended
++              case 0x81: // user resumed
++                      if (!aplci->appl)
++                              break;
++                      if (!(aplci->appl->NotificationMask & SuppServiceTP))
++                              break;
++                      CMSG_ALLOC(cmsg);
++                      AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND);
++                      p = &tmp[1];
++                      p += capiEncodeWord(p, 0x8002 + (nf[1] & 1)); // Suspend/Resume Notification
++                      *p++ = 0; // empty struct
++                      tmp[0] = p - &tmp[1];
++                      cmsg->FacilitySelector = 0x0003;
++                      cmsg->FacilityIndicationParameter = tmp;
++                      Send2Application(aplci, cmsg);
++                      break;
++      }
++}
++
++static void
++AppPlci_suspend_reply(AppPlci_t *aplci, __u16 SuppServiceReason)
++{
++      _cmsg   *cmsg;
++      __u8    tmp[10], *p;
++
++      if (aplci->appl) {
++              CMSG_ALLOC(cmsg);
++              AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND);
++              p = &tmp[1];
++              p += capiEncodeWord(p, 0x0004); // Suspend
++              p += capiEncodeFacIndSuspend(p, SuppServiceReason);
++              tmp[0] = p - &tmp[1];
++              cmsg->FacilitySelector = 0x0003;
++              cmsg->FacilityIndicationParameter = tmp;
++              Send2Application(aplci, cmsg);
++      }
++      if (SuppServiceReason == CapiSuccess)
++              mISDN_FsmEvent(&aplci->plci_m, EV_PI_SUSPEND_CONF, NULL);
++}
++
++static void
++plci_cc_suspend_err(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Q931_info_t     *qi = arg;
++      u_char          *p;
++      __u16           SuppServiceReason;
++      
++      if (qi) { // reject from network
++              if (qi->cause) {
++                      p = (u_char *)qi;
++                      p += L3_EXTRA_SIZE + qi->cause;
++                      SuppServiceReason = 0x3400 | p[3];
++              } else
++                      SuppServiceReason = CapiProtocolErrorLayer3;
++      } else { // timeout
++              SuppServiceReason = CapiTimeOut;
++      }
++      AppPlci_suspend_reply(aplci, SuppServiceReason);
++}
++
++static void
++plci_cc_suspend_conf(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      _cmsg           *cmsg;
++
++      AppPlciLinkDown(aplci);
++
++      AppPlci_suspend_reply(aplci, CapiSuccess);
++      
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND);
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_cc_resume_err(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Q931_info_t     *qi = arg;
++      u_char          *p;
++      _cmsg           *cmsg;
++      
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_DISCONNECT, CAPI_IND);
++      if (qi) { // reject from network
++              if (qi->cause) {
++                      p = (u_char *)qi;
++                      p += L3_EXTRA_SIZE + qi->cause;
++                      cmsg->Reason = 0x3400 | p[3];
++              } else
++                      cmsg->Reason = 0;
++      } else { // timeout
++              cmsg->Reason = CapiProtocolErrorLayer1;
++      }
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_DISCONNECT_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_cc_resume_conf(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Q931_info_t     *qi = arg;
++      _cmsg           *cmsg;
++      __u8            tmp[10], *p;
++      
++      if (!qi || !qi->channel_id) {
++              int_error();
++              return;
++      }
++      p = (u_char *)qi;
++      p += L3_EXTRA_SIZE + qi->channel_id;
++      aplci->channel = plci_parse_channel_id(p);
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND);
++      p = &tmp[1];
++      p += capiEncodeWord(p, 0x0005); // Suspend
++      p += capiEncodeFacIndSuspend(p, CapiSuccess);
++      tmp[0] = p - &tmp[1];
++      cmsg->FacilitySelector = 0x0003;
++      cmsg->FacilityIndicationParameter = tmp;
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_PI_RESUME_CONF, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++plci_select_b_protocol_req(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      _cmsg           *cmsg = arg;
++      __u16           Info;
++      int             ret;
++
++      Info = AppPlciCheckBprotocol(aplci, cmsg);
++      if (Info)
++              goto answer;
++
++      ret = AppPlciLinkDown(aplci);
++      if (ret) {
++              Info = 0x2001;
++              goto answer;
++      }
++      ret = AppPlciLinkUp(aplci);
++      if (ret < 0)
++              Info = 0x2001;
++      else
++              Info = ret;
++answer:
++      capi_cmsg_answer(cmsg);
++      cmsg->Info = Info;
++      if (test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state))
++              Send2Application(aplci, arg);
++      else
++              Send2ApplicationDelayed(aplci, arg);
++}
++
++static void
++plci_info_req_overlap(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      Plci_t          *plci = aplci->plci;
++      _cmsg           *cmsg = arg;
++      __u16           Info = 0;
++      struct sk_buff  *skb;
++
++      skb = mISDN_alloc_l3msg(100, MT_INFORMATION);
++      if (skb) {
++              Info = cmsg2info_req(cmsg, skb);
++              if (Info == CapiSuccess)
++                      plciL4L3(plci, CC_INFORMATION | REQUEST, skb);
++              else
++                      kfree_skb(skb);
++      }
++      capi_cmsg_answer(cmsg);
++      cmsg->Info = Info;
++      Send2Application(aplci, cmsg);
++}
++
++static void
++plci_cc_ph_control_ind(struct FsmInst *fi, int event, void *arg)
++{
++      AppPlci_t       *aplci = fi->userdata;
++      int             *tt = arg;
++      _cmsg           *cmsg;
++      __u8            tmp[2];
++
++      if (!arg)
++              return;
++      AppPlciDebug(aplci, CAPI_DBG_PLCI_INFO, "%s: tt(%x)", __FUNCTION__, *tt);
++      if ((*tt & ~DTMF_TONE_MASK) != DTMF_TONE_VAL)
++              return;
++
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_FACILITY, CAPI_IND);
++      tmp[0] = 1;
++      tmp[1] = *tt & DTMF_TONE_MASK;
++      cmsg->FacilitySelector = 0x0001;
++      cmsg->FacilityIndicationParameter = tmp;
++      Send2Application(aplci, cmsg);
++}
++
++static void
++plci_info_req(struct FsmInst *fi, int event, void *arg)
++{
++      // FIXME handle INFO CONF
++      if (arg)
++              cmsg_free(arg);
++}
++
++static struct FsmNode fn_plci_list[] =
++{
++  {ST_PLCI_P_0,               EV_AP_CONNECT_REQ,              plci_connect_req},
++  {ST_PLCI_P_0,               EV_PI_CONNECT_IND,              plci_connect_ind},
++  {ST_PLCI_P_0,               EV_AP_RESUME_REQ,               plci_resume_req},
++  {ST_PLCI_P_0,               EV_L3_SETUP_IND,                plci_cc_setup_ind},
++  {ST_PLCI_P_0,               EV_AP_RELEASE,                  plci_appl_release},
++
++  {ST_PLCI_P_0_1,     EV_PI_CONNECT_CONF,             plci_connect_conf},
++  {ST_PLCI_P_0_1,     EV_AP_RELEASE,                  plci_appl_release},
++
++  {ST_PLCI_P_1,               EV_PI_CONNECT_ACTIVE_IND,       plci_connect_active_ind},
++  {ST_PLCI_P_1,               EV_AP_DISCONNECT_REQ,           plci_disconnect_req},
++  {ST_PLCI_P_1,               EV_PI_DISCONNECT_IND,           plci_disconnect_ind},
++  {ST_PLCI_P_1,               EV_AP_INFO_REQ,                 plci_info_req_overlap},
++  {ST_PLCI_P_1,               EV_L3_SETUP_CONF,               plci_cc_setup_conf},
++  {ST_PLCI_P_1,               EV_L3_SETUP_CONF_ERR,           plci_cc_setup_conf_err},
++  {ST_PLCI_P_1,               EV_L3_DISCONNECT_IND,           plci_cc_disconnect_ind},
++  {ST_PLCI_P_1,               EV_L3_RELEASE_PROC_IND,         plci_cc_setup_conf_err},
++  {ST_PLCI_P_1,               EV_L3_RELEASE_IND,              plci_cc_release_ind},
++  {ST_PLCI_P_1,               EV_L3_REJECT_IND,               plci_cc_release_ind},
++  {ST_PLCI_P_1,               EV_PI_CHANNEL_ERR,              plci_channel_err},
++  {ST_PLCI_P_1,               EV_AP_RELEASE,                  plci_appl_release_disc},
++
++  {ST_PLCI_P_2,               EV_AP_ALERT_REQ,                plci_alert_req},
++  {ST_PLCI_P_2,               EV_AP_CONNECT_RESP,             plci_connect_resp},
++  {ST_PLCI_P_2,               EV_AP_DISCONNECT_REQ,           plci_disconnect_req},
++  {ST_PLCI_P_2,               EV_PI_DISCONNECT_IND,           plci_disconnect_ind},
++  {ST_PLCI_P_2,               EV_AP_INFO_REQ,                 plci_info_req},
++  {ST_PLCI_P_2,               EV_L3_RELEASE_IND,              plci_cc_release_ind},
++  {ST_PLCI_P_2,               EV_AP_RELEASE,                  plci_appl_release_disc},
++
++  {ST_PLCI_P_4,               EV_PI_CONNECT_ACTIVE_IND,       plci_connect_active_ind},
++  {ST_PLCI_P_4,               EV_AP_DISCONNECT_REQ,           plci_disconnect_req},
++  {ST_PLCI_P_4,               EV_PI_DISCONNECT_IND,           plci_disconnect_ind},
++  {ST_PLCI_P_4,               EV_AP_INFO_REQ,                 plci_info_req},
++  {ST_PLCI_P_4,               EV_L3_SETUP_COMPL_IND,          plci_cc_setup_compl_ind},
++  {ST_PLCI_P_4,               EV_L3_RELEASE_IND,              plci_cc_release_ind},
++  {ST_PLCI_P_4,               EV_PI_CHANNEL_ERR,              plci_channel_err},
++  {ST_PLCI_P_4,               EV_AP_RELEASE,                  plci_appl_release_disc},
++
++  {ST_PLCI_P_ACT,     EV_AP_CONNECT_ACTIVE_RESP,      plci_connect_active_resp},
++  {ST_PLCI_P_ACT,     EV_AP_DISCONNECT_REQ,           plci_disconnect_req},
++  {ST_PLCI_P_ACT,     EV_PI_DISCONNECT_IND,           plci_disconnect_ind},
++  {ST_PLCI_P_ACT,     EV_AP_INFO_REQ,                 plci_info_req},
++  {ST_PLCI_P_ACT,     EV_AP_SELECT_B_PROTOCOL_REQ,    plci_select_b_protocol_req},
++  {ST_PLCI_P_ACT,     EV_AP_SUSPEND_REQ,              plci_suspend_req},
++  {ST_PLCI_P_ACT,     EV_PI_SUSPEND_CONF,             plci_suspend_conf},
++  {ST_PLCI_P_ACT,     EV_L3_DISCONNECT_IND,           plci_cc_disconnect_ind},
++  {ST_PLCI_P_ACT,     EV_L3_RELEASE_IND,              plci_cc_release_ind},
++  {ST_PLCI_P_ACT,     EV_L3_NOTIFY_IND,               plci_cc_notify_ind},
++  {ST_PLCI_P_ACT,     EV_L3_SUSPEND_ERR,              plci_cc_suspend_err},
++  {ST_PLCI_P_ACT,     EV_L3_SUSPEND_CONF,             plci_cc_suspend_conf},
++  {ST_PLCI_P_ACT,     EV_PH_CONTROL_IND,              plci_cc_ph_control_ind},
++  {ST_PLCI_P_ACT,     EV_AP_RELEASE,                  plci_appl_release_disc},
++
++  {ST_PLCI_P_5,               EV_PI_DISCONNECT_IND,           plci_disconnect_ind},
++  {ST_PLCI_P_5,               EV_L3_RELEASE_IND,              plci_cc_release_ind},
++
++  {ST_PLCI_P_6,               EV_AP_DISCONNECT_RESP,          plci_disconnect_resp},
++  {ST_PLCI_P_6,               EV_AP_RELEASE,                  plci_disconnect_resp},
++
++  {ST_PLCI_P_RES,     EV_PI_RESUME_CONF,              plci_resume_conf},
++  {ST_PLCI_P_RES,     EV_PI_DISCONNECT_IND,           plci_disconnect_ind},
++  {ST_PLCI_P_RES,     EV_L3_RESUME_ERR,               plci_cc_resume_err},
++  {ST_PLCI_P_RES,     EV_L3_RESUME_CONF,              plci_cc_resume_conf},
++  {ST_PLCI_P_RES,     EV_AP_RELEASE,                  plci_appl_release_disc},
++};
++
++const int FN_PLCI_COUNT = sizeof(fn_plci_list)/sizeof(struct FsmNode);
++
++int
++AppPlciConstr(AppPlci_t **aplci, Application_t *appl, Plci_t *plci)
++{
++      AppPlci_t       *apl = AppPlci_alloc(); 
++
++      if (!apl)
++              return(-ENOMEM);
++      memset(apl, 0, sizeof(AppPlci_t));
++      INIT_LIST_HEAD(&apl->head);
++      INIT_LIST_HEAD(&apl->Nccis);
++      apl->addr = plci->addr;
++      apl->appl = appl;
++      apl->plci = plci;
++      apl->contr = plci->contr;
++      apl->plci_m.fsm        = &plci_fsm;
++      apl->plci_m.state      = ST_PLCI_P_0;
++      apl->plci_m.debug      = plci->contr->debug & CAPI_DBG_PLCI_STATE;
++      apl->plci_m.userdata   = apl;
++      apl->plci_m.printdebug = AppPlci_debug;
++      apl->channel = -1;
++      skb_queue_head_init(&apl->delayedq);
++      *aplci = apl;
++      return(0);
++}
++
++void AppPlciDestr(AppPlci_t *aplci)
++{
++      struct list_head        *item, *next;
++      
++      if (aplci->plci) {
++              AppPlciDebug(aplci, CAPI_DBG_PLCI, "%s plci state %s", __FUNCTION__,
++                      str_st_plci[aplci->plci_m.state]);
++              if (aplci->plci_m.state != ST_PLCI_P_0) {
++                      struct sk_buff  *skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE);
++                      unsigned char cause[] = {2,0x80,0x80| CAUSE_RESOURCES_UNAVAIL};
++
++                      if (skb) {
++                              mISDN_AddIE(skb, IE_CAUSE, cause);
++                              plciL4L3(aplci->plci, CC_RELEASE_COMPLETE | REQUEST, skb);
++                      }
++              }
++              plciDetachAppPlci(aplci->plci, aplci);
++      }
++      list_for_each_safe(item, next, &aplci->Nccis) {
++              ncciDelAppPlci((Ncci_t *)item);
++      }
++      if (aplci->appl)
++              ApplicationDelAppPlci(aplci->appl, aplci);
++      skb_queue_purge(&aplci->delayedq);
++      AppPlci_free(aplci);
++}
++
++void
++AppPlciRelease(AppPlci_t *aplci)
++{
++      struct list_head        *item, *next;
++
++      list_for_each_safe(item, next, &aplci->Nccis) {
++              ncciApplRelease((Ncci_t *)item);
++      }
++      mISDN_FsmEvent(&aplci->plci_m, EV_AP_RELEASE, NULL);
++}
++
++static int
++AppPlciLinkUp(AppPlci_t *aplci)
++{
++      mISDN_pid_t     pid;
++      mISDN_stPara_t  stpara;
++      int             retval;
++
++      if (aplci->channel == -1) {/* no valid channel set */
++              int_error();
++              return(-EINVAL);
++      }
++      memset(&pid, 0, sizeof(mISDN_pid_t));
++      pid.layermask = ISDN_LAYER(1) | ISDN_LAYER(2) | ISDN_LAYER(3) |
++              ISDN_LAYER(4);
++      if (test_bit(PLCI_STATE_OUTGOING, &aplci->plci->state))
++              pid.global = 1; // DTE, orginate
++      else
++              pid.global = 2; // DCE, answer
++      if (aplci->Bprotocol.B1 > 23) {
++              int_errtxt("wrong B1 prot %x", aplci->Bprotocol.B1);
++              return(0x3001);
++      }
++      pid.protocol[1] = (1 << aplci->Bprotocol.B1) |
++              ISDN_PID_LAYER(1) | ISDN_PID_BCHANNEL_BIT;
++      if (aplci->Bprotocol.B1cfg[0])
++              pid.param[1] = &aplci->Bprotocol.B1cfg[0];
++      if (aplci->Bprotocol.B2 > 23) {
++              int_errtxt("wrong B2 prot %x", aplci->Bprotocol.B2);
++              return(0x3002);
++      }
++      pid.protocol[2] = (1 << aplci->Bprotocol.B2) |
++              ISDN_PID_LAYER(2) | ISDN_PID_BCHANNEL_BIT;
++      if (aplci->Bprotocol.B2cfg[0])
++              pid.param[2] = &aplci->Bprotocol.B2cfg[0];
++      /* handle DTMF TODO */
++      if ((pid.protocol[2] == ISDN_PID_L2_B_TRANS) &&
++              (pid.protocol[1] == ISDN_PID_L1_B_64TRANS))
++              pid.protocol[2] = ISDN_PID_L2_B_TRANSDTMF;
++      if (aplci->Bprotocol.B3 > 23) {
++              int_errtxt("wrong B3 prot %x", aplci->Bprotocol.B3);
++              return(0x3003);
++      }
++      pid.protocol[3] = (1 << aplci->Bprotocol.B3) |
++              ISDN_PID_LAYER(3) | ISDN_PID_BCHANNEL_BIT;
++      if (aplci->Bprotocol.B3cfg[0])
++              pid.param[3] = &aplci->Bprotocol.B3cfg[0];
++      capidebug(CAPI_DBG_PLCI, "AppPlciLinkUp B1(%x) B2(%x) B3(%x) global(%d) ch(%x)",
++              pid.protocol[1], pid.protocol[2], pid.protocol[3], pid.global, 
++              aplci->channel);
++      capidebug(CAPI_DBG_PLCI, "AppPlciLinkUp ch(%d) aplci->contr->linklist(%p)",
++              aplci->channel & 3, aplci->contr->linklist);
++      pid.protocol[4] = ISDN_PID_L4_B_CAPI20;
++      aplci->link = ControllerSelChannel(aplci->contr, aplci->channel);
++      if (!aplci->link) {
++              int_error();
++              return(-EBUSY);
++      }
++      capidebug(CAPI_DBG_NCCI, "AppPlciLinkUp aplci->link(%p)", aplci->link);
++      memset(&aplci->link->inst.pid, 0, sizeof(mISDN_pid_t));
++      aplci->link->inst.data = aplci;
++      aplci->link->inst.pid.layermask = ISDN_LAYER(4);
++      aplci->link->inst.pid.protocol[4] = ISDN_PID_L4_B_CAPI20;
++      if (pid.protocol[3] == ISDN_PID_L3_B_TRANS) {
++              aplci->link->inst.pid.protocol[3] = ISDN_PID_L3_B_TRANS;
++              aplci->link->inst.pid.layermask |= ISDN_LAYER(3);
++      }
++      retval = aplci->link->inst.obj->ctrl(aplci->link->st,
++              MGR_REGLAYER | INDICATION, &aplci->link->inst); 
++      if (retval) {
++              printk(KERN_WARNING "%s MGR_REGLAYER | INDICATION ret(%d)\n",
++                      __FUNCTION__, retval);
++              return(retval);
++      }
++      stpara.maxdatalen = aplci->appl->reg_params.datablklen;
++      stpara.up_headerlen = CAPI_B3_DATA_IND_HEADER_SIZE;
++      stpara.down_headerlen = 0;
++                        
++      retval = aplci->link->inst.obj->ctrl(aplci->link->st,
++              MGR_ADDSTPARA | REQUEST, &stpara);
++      if (retval) {
++              printk(KERN_WARNING "%s MGR_SETSTACK | REQUEST ret(%d)\n",
++                      __FUNCTION__, retval);
++      }
++      retval = aplci->link->inst.obj->ctrl(aplci->link->st,
++              MGR_SETSTACK | REQUEST, &pid);
++      if (retval) {
++              printk(KERN_WARNING "%s MGR_SETSTACK | REQUEST ret(%d)\n",
++                      __FUNCTION__, retval);
++              return(retval);
++      }
++      return(0);
++}
++
++static int
++ReleaseLink(AppPlci_t *aplci)
++{
++      int retval = 0;
++
++      if (aplci->link) {
++#if 0
++              if (ncci->ncci_m.state != ST_NCCI_N_0)
++                      ncciL4L3(ncci, DL_RELEASE | REQUEST, 0, 0, NULL, NULL);
++#endif
++              retval = aplci->link->inst.obj->ctrl(aplci->link->inst.st,
++                      MGR_CLEARSTACK | REQUEST, NULL);
++              if (retval)
++                      int_error();
++              aplci->link = NULL;
++              skb_queue_purge(&aplci->delayedq);
++              test_and_clear_bit(PLCI_STATE_STACKREADY, &aplci->plci->state);
++      }
++      return(retval);
++}
++
++Ncci_t        *
++getNCCI4addr(AppPlci_t *aplci, __u32 addr, int mode)
++{
++      Ncci_t                  *ncci;
++      struct list_head        *item;
++      int                     cnt = 0;
++
++      list_for_each(item, &aplci->Nccis) {
++              cnt++;
++              ncci = (Ncci_t *)item;
++              if (ncci->addr == addr)
++                      return(ncci);
++              if (mode == GET_NCCI_ONLY_PLCI) {
++                      if (ncci->addr == (addr & 0xffff))
++                              return(ncci);
++              }
++      }
++      if (!cnt)
++              return(NULL);
++      if (mode != GET_NCCI_PLCI)
++              return(NULL);
++      if (1 == cnt) {
++              if (!(addr & 0xffff0000))
++                      return(ncci);
++      }
++      return(NULL);
++}
++
++void
++AppPlciDelNCCI(Ncci_t *ncci) {
++      list_del_init(&ncci->head);
++}
++
++static __inline__ Ncci_t *
++get_single_NCCI(AppPlci_t *aplci)
++{
++      struct list_head        *item = aplci->Nccis.next;
++
++      if (item == &aplci->Nccis)
++              return(NULL);
++      if (item->next != &aplci->Nccis)
++              return(NULL);   // more as one NCCI
++      return((Ncci_t *)item); 
++}
++
++static int
++PL_l3l4(mISDNif_t *hif, struct sk_buff *skb)
++{
++      AppPlci_t               *aplci;
++      Ncci_t                  *ncci;
++      int                     ret = -EINVAL;
++      mISDN_head_t            *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      aplci = hif->fdata;
++      if (!aplci)
++              return(-EINVAL);
++      ncci = get_single_NCCI(aplci);
++      capidebug(CAPI_DBG_NCCI_L3, "%s: prim(%x) dinfo (%x) skb(%p) APLCI(%x) ncci(%p)",
++              __FUNCTION__, hh->prim, hh->dinfo, skb, aplci->addr, ncci);
++      if (!ncci) {
++              if ((hh->prim != (DL_ESTABLISH | INDICATION)) && (hh->prim != (DL_ESTABLISH | CONFIRM))) {
++                      int_error();
++                      return(-ENODEV);
++              }
++              ncci = ncciConstr(aplci);
++              if (!ncci) {
++                      int_error();
++                      return(-ENOMEM);
++              }
++      }
++      return(ncci_l3l4(ncci, hh, skb));
++}
++
++static int
++PL_l3l4mux(mISDNif_t *hif, struct sk_buff *skb)
++{
++      AppPlci_t       *aplci;
++      Ncci_t          *ncci;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++      __u32           addr;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      aplci = hif->fdata;
++      if (!aplci)
++              return(-EINVAL);
++      
++      capidebug(CAPI_DBG_NCCI_L3, "%s: prim(%x) dinfo (%x) skb->len(%d)",
++              __FUNCTION__, hh->prim, hh->dinfo, skb->len);
++      if (skb->len < 4) {
++              int_error();
++              return(-EINVAL);
++      }
++      addr = CAPIMSG_U32(skb->data, 0);
++      ncci = getNCCI4addr(aplci, addr, GET_NCCI_ONLY_PLCI);
++      if (hh->prim == CAPI_CONNECT_B3_IND) {
++              if (ncci) {
++                      int_error();
++                      return(-EBUSY);
++              }
++              ncci = ncciConstr(aplci);
++              if (!ncci) {
++                      int_error();
++                      return(-ENOMEM);
++              }
++              addr &= 0xffff0000;
++              addr |= aplci->addr;
++              ncci->addr = addr;
++              capimsg_setu32(skb->data, 0, addr);
++#ifdef OLDCAPI_DRIVER_INTERFACE
++              ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, addr, ncci->window);
++#endif
++      } else if (hh->prim == CAPI_CONNECT_B3_CONF) {
++              if (ncci && ((addr & 0xffff0000) != 0)) {
++                      if (ncci->addr != addr) {
++                              ncci->addr = addr;
++#ifdef OLDCAPI_DRIVER_INTERFACE
++                              ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, addr, ncci->window);
++#endif
++                      } else
++                              int_error();
++              }
++      }
++      if (!ncci) {
++              int_error();
++              return(-ENODEV);
++      }
++      return(ncci_l3l4_direct(ncci, hh, skb));
++}
++
++int
++AppPlcimISDN_SetIF(AppPlci_t *aplci, u_int prim, void *arg)
++{
++      int ret;
++
++      if (aplci->Bprotocol.B3 == 0) // transparent
++              ret = mISDN_SetIF(&aplci->link->inst, arg, prim, NULL, PL_l3l4, aplci);
++      else
++              ret = mISDN_SetIF(&aplci->link->inst, arg, prim, NULL, PL_l3l4mux, aplci);
++      if (ret)
++              return(ret);
++      
++      if (!test_and_set_bit(PLCI_STATE_SENDDELAYED, &aplci->plci->state)) {
++              test_and_set_bit(PLCI_STATE_STACKREADY, &aplci->plci->state);
++              SendingDelayedMsg(aplci);
++      } else
++              test_and_set_bit(PLCI_STATE_STACKREADY, &aplci->plci->state);
++      return(0);
++}
++
++void
++AppPlci_l3l4(AppPlci_t *aplci, int pr, void *arg)
++{
++      Q931_info_t     *qi = arg;
++      u_char          *ie;
++
++      AppPlciDebug(aplci, CAPI_DBG_PLCI_L3, "%s: aplci(%x) pr(%x) arg(%p)",
++              __FUNCTION__, aplci->addr, pr, arg);
++      switch (pr) {
++              case CC_SETUP | INDICATION:
++                      if (!qi)
++                              return;
++                      AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                      AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi);
++                      AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi);
++                      AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi);
++                      AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi);
++                      if (qi->channel_id) {
++                              ie = (u_char *)qi;
++                              ie += L3_EXTRA_SIZE + qi->channel_id;
++                              aplci->channel = plci_parse_channel_id(ie);
++                      }
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_IND, arg); 
++                      break;
++              case CC_TIMEOUT | INDICATION:
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_CONF_ERR, arg); 
++                      break;
++              case CC_CONNECT | INDICATION:
++                      if (qi) {       
++                              AppPlciInfoIndIE(aplci, IE_DATE, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi);
++                              AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi);
++                              AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi);
++                              AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi);
++                              if (qi->channel_id) {
++                                      ie = (u_char *)qi;
++                                      ie += L3_EXTRA_SIZE + qi->channel_id;
++                                      aplci->channel = plci_parse_channel_id(ie);
++                              }
++                      }
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_CONF, arg); 
++                      break;
++              case CC_CONNECT_ACKNOWLEDGE | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi);
++                              if (qi->channel_id) {
++                                      ie = (u_char *)qi;
++                                      ie += L3_EXTRA_SIZE + qi->channel_id;
++                                      aplci->channel = plci_parse_channel_id(ie);
++                              }
++                      }
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_SETUP_COMPL_IND, arg); 
++                      break;
++              case CC_DISCONNECT | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_EARLYB3, MT_DISCONNECT);
++                              AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi);
++                              AppPlciInfoIndIE(aplci, IE_PROGRESS, CAPI_INFOMASK_PROGRESS, qi);
++                              AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi);
++                      }
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_DISCONNECT_IND, arg); 
++                      break;
++              case CC_RELEASE | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi);
++                              AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi);
++                      }
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_IND, arg); 
++                      break;
++              case CC_RELEASE_COMPLETE | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi);
++                              AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi);
++                      }
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_IND, arg);
++                      break;
++              case CC_RELEASE_CR | INDICATION:
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_RELEASE_PROC_IND, arg); 
++                      break;
++              case CC_SETUP_ACKNOWLEDGE | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_SETUP_ACKNOWLEDGE);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_PROGRESS,
++                                      CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi);
++                              AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi);
++                              if (qi->channel_id) {
++                                      ie = (u_char *)qi;
++                                      ie += L3_EXTRA_SIZE + qi->channel_id;
++                                      aplci->channel = plci_parse_channel_id(ie);
++                              }
++                      }
++                      break;
++              case CC_PROCEEDING | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_CALL_PROCEEDING);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_PROGRESS,
++                                      CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi);
++                              AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi);
++                              if (qi->channel_id) {
++                                      ie = (u_char *)qi;
++                                      ie += L3_EXTRA_SIZE + qi->channel_id;
++                                      aplci->channel = plci_parse_channel_id(ie);
++                              }
++                      }
++                      break;
++              case CC_ALERTING | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_ALERTING);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi);
++                              AppPlciInfoIndIE(aplci, IE_PROGRESS,
++                                      CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi);
++                              AppPlciInfoIndIE(aplci, IE_FACILITY, CAPI_INFOMASK_FACILITY, qi);
++                              AppPlciInfoIndIE(aplci, IE_CHANNEL_ID, CAPI_INFOMASK_CHANNELID, qi);
++                              if (qi->channel_id) {
++                                      ie = (u_char *)qi;
++                                      ie += L3_EXTRA_SIZE + qi->channel_id;
++                                      aplci->channel = plci_parse_channel_id(ie);
++                              }
++                      }
++                      break;
++              case CC_PROGRESS | INDICATION:
++                      if (qi) {
++                              AppPlciInfoIndMsg(aplci, CAPI_INFOMASK_PROGRESS, MT_PROGRESS);
++                              AppPlciInfoIndIE(aplci, IE_CAUSE, CAPI_INFOMASK_CAUSE, qi);
++                              AppPlciInfoIndIE(aplci, IE_DISPLAY, CAPI_INFOMASK_DISPLAY, qi);
++                              AppPlciInfoIndIE(aplci, IE_USER_USER, CAPI_INFOMASK_USERUSER, qi);
++                              AppPlciInfoIndIE(aplci, IE_PROGRESS,
++                                      CAPI_INFOMASK_PROGRESS | CAPI_INFOMASK_EARLYB3, qi);
++                      }
++                      break;
++              case CC_SUSPEND_ACKNOWLEDGE | INDICATION:
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_SUSPEND_CONF, arg); 
++                      break;
++              case CC_SUSPEND_REJECT | INDICATION:
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_SUSPEND_ERR, arg); 
++                      break;
++              case CC_RESUME_ACKNOWLEDGE | INDICATION:
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_RESUME_CONF, arg); 
++                      break;
++              case CC_RESUME_REJECT | INDICATION:
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_RESUME_ERR, arg); 
++                      break;
++              case CC_NOTIFY | INDICATION:
++                      mISDN_FsmEvent(&aplci->plci_m, EV_L3_NOTIFY_IND, arg); 
++                      break;
++              case PH_CONTROL | INDICATION:
++                      /* TOUCH TONE */
++                      mISDN_FsmEvent(&aplci->plci_m, EV_PH_CONTROL_IND, arg);
++                      break;
++              default:
++                      AppPlciDebug(aplci, CAPI_DBG_WARN, 
++                         "%s: pr 0x%x not handled", __FUNCTION__, pr);
++                      break;
++      }
++}
++
++void
++AppPlciGetCmsg(AppPlci_t *aplci, _cmsg *cmsg)
++{
++      int     retval = 0;
++
++      switch (CMSGCMD(cmsg)) {
++              case CAPI_INFO_REQ:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_INFO_REQ, cmsg);
++                      break;
++              case CAPI_ALERT_REQ:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_ALERT_REQ, cmsg);
++                      break;
++              case CAPI_CONNECT_REQ:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_REQ, cmsg);
++                      break;
++              case CAPI_CONNECT_RESP:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_RESP, cmsg);
++                      break;
++              case CAPI_DISCONNECT_REQ:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_DISCONNECT_REQ, cmsg);
++                      break;
++              case CAPI_DISCONNECT_RESP:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_DISCONNECT_RESP, cmsg);
++                      break;
++              case CAPI_CONNECT_ACTIVE_RESP:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_CONNECT_ACTIVE_RESP, cmsg);
++                      break;
++              case CAPI_SELECT_B_PROTOCOL_REQ:
++                      retval = mISDN_FsmEvent(&aplci->plci_m, EV_AP_SELECT_B_PROTOCOL_REQ, cmsg);
++                      break;
++              default:
++                      int_error();
++                      retval = -1;
++      }
++      if (retval) { 
++              if (cmsg->Command == CAPI_REQ) {
++                      capi_cmsg_answer(cmsg);
++                      cmsg->Info = CapiMessageNotSupportedInCurrentState;
++                      Send2Application(aplci, cmsg);
++              } else
++                      cmsg_free(cmsg);
++      }
++}
++
++__u16
++AppPlciSendMessage(AppPlci_t *aplci, struct sk_buff *skb)
++{
++      _cmsg   *cmsg;
++      __u16   ret;
++
++      cmsg = cmsg_alloc();
++      if (!cmsg) {
++              int_error();
++              ret = CAPI_REGOSRESOURCEERR;
++      } else {
++              capi_message2cmsg(cmsg, skb->data);
++              AppPlciGetCmsg(aplci, cmsg);
++              dev_kfree_skb(skb);
++              ret = CAPI_NOERROR;
++      }
++      return(ret);
++}
++
++int
++ConnectB3Request(AppPlci_t *aplci, struct sk_buff *skb)
++{
++      Ncci_t  *ncci = ncciConstr(aplci);
++
++      if (!ncci) {
++              int_error();
++              return(-ENOMEM);
++      }
++      ncciSendMessage(ncci, skb);
++      return(0);
++}
++
++static int
++AppPlciLinkDown(AppPlci_t *aplci)
++{
++      struct list_head        *item, *next;
++
++      list_for_each_safe(item, next, &aplci->Nccis) {
++              ncciReleaseLink((Ncci_t *)item);
++      }
++      ReleaseLink(aplci);
++      return(0);
++}
++
++int
++AppPlciFacSuspendReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      __u8            *CallIdentity;
++      struct sk_buff  *skb;
++
++      CallIdentity = facReqParm->u.Suspend.CallIdentity;
++      if (CallIdentity && CallIdentity[0] > 8) 
++              return CapiIllMessageParmCoding;
++      skb = mISDN_alloc_l3msg(20, MT_SUSPEND);
++      if (!skb) {
++              int_error();
++              return CapiIllMessageParmCoding;
++      }
++      if (CallIdentity && CallIdentity[0])
++              mISDN_AddIE(skb, IE_CALL_ID, CallIdentity);
++
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_SUSPEND_REQ, skb)) {
++              // no routine
++              facConfParm->u.Info.SupplementaryServiceInfo = 
++                      CapiRequestNotAllowedInThisState;
++              kfree(skb);
++      } else {
++              facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
++      }
++      return CapiSuccess;
++}
++
++int
++AppPlciFacResumeReq(AppPlci_t *aplci, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      __u8            *CallIdentity;
++      struct sk_buff  *skb;
++
++      CallIdentity = facReqParm->u.Resume.CallIdentity;
++      if (CallIdentity && CallIdentity[0] > 8) {
++              AppPlciDestr(aplci);
++              return CapiIllMessageParmCoding;
++      }
++      skb = mISDN_alloc_l3msg(20, MT_RESUME);
++      if (!skb) {
++              int_error();
++              AppPlciDestr(aplci);
++              return CapiIllMessageParmCoding;
++      }
++      if (CallIdentity && CallIdentity[0])
++              mISDN_AddIE(skb, IE_CALL_ID, CallIdentity);
++      if (mISDN_FsmEvent(&aplci->plci_m, EV_AP_RESUME_REQ, skb))
++              kfree(skb);
++
++      facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
++      return CapiSuccess;
++}
++
++static void
++AppPlciClearOtherApps(AppPlci_t *aplci)
++{
++      AppPlci_t               *o_aplci;
++      _cmsg                   *cm;
++      struct list_head        *item, *next;
++
++      if (aplci->plci)
++              return;
++      if (aplci->plci->nAppl <= 1)
++              return;
++      list_for_each_safe(item, next, &aplci->plci->AppPlcis) {
++              o_aplci = (AppPlci_t *)item;
++              if (o_aplci != aplci) {
++                      CMSG_ALLOC(cm);
++                      AppPlciCmsgHeader(o_aplci, cm, CAPI_DISCONNECT, CAPI_IND);
++                      cm->Reason = 0x3304; // other application got the call
++                      mISDN_FsmEvent(&o_aplci->plci_m, EV_PI_DISCONNECT_IND, cm);
++              }
++      } 
++}
++
++static void
++AppPlciInfoIndMsg(AppPlci_t *aplci,  __u32 mask, unsigned char mt)
++{
++      _cmsg   *cmsg;
++
++      if ((!aplci->appl) || (!(aplci->appl->InfoMask & mask)))
++              return;
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_INFO, CAPI_IND);
++      cmsg->InfoNumber = 0x8000 | mt;
++      cmsg->InfoElement = 0;
++      Send2Application(aplci, cmsg);
++}
++
++static void
++AppPlciInfoIndIE(AppPlci_t *aplci, unsigned char ie, __u32 mask, Q931_info_t *qi)
++{
++      _cmsg           *cmsg;
++      u_char          *iep = NULL;
++      u16             *ies;
++      
++
++      if ((!aplci->appl) || (!(aplci->appl->InfoMask & mask)))
++              return;
++      if (!qi)
++              return;
++      ies = &qi->bearer_capability;
++      if (ie & 0x80) { /* single octett */
++              int_error();
++              return;
++      } else {
++              if (mISDN_l3_ie2pos(ie) < 0)
++                      return;
++              ies += mISDN_l3_ie2pos(ie);
++              if (!*ies)
++                      return;
++              iep = (u_char *)qi;
++              iep += L3_EXTRA_SIZE + *ies +1;
++      }
++      CMSG_ALLOC(cmsg);
++      AppPlciCmsgHeader(aplci, cmsg, CAPI_INFO, CAPI_IND);
++      cmsg->InfoNumber = ie;
++      cmsg->InfoElement = iep;
++      if (ie == IE_PROGRESS && aplci->appl->InfoMask & CAPI_INFOMASK_EARLYB3) {
++              if (iep[0] == 0x02 && iep[2] == 0x88) { // in-band information available
++                      AppPlciLinkUp(aplci);
++                      if (!test_bit(PLCI_STATE_STACKREADY, &aplci->plci->state)) {
++                              Send2ApplicationDelayed(aplci,cmsg);
++                              return;
++                      }
++              }
++      }
++      Send2Application(aplci, cmsg);
++}
++
++void init_AppPlci(void)
++{
++      plci_fsm.state_count = ST_PLCI_COUNT;
++      plci_fsm.event_count = EV_PLCI_COUNT;
++      plci_fsm.strEvent = str_ev_plci;
++      plci_fsm.strState = str_st_plci;
++      
++      mISDN_FsmNew(&plci_fsm, fn_plci_list, FN_PLCI_COUNT);
++}
++
++
++void free_AppPlci(void)
++{
++      mISDN_FsmFree(&plci_fsm);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/appl.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/appl.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/appl.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/appl.c   2004-11-22 09:33:37.689814192 +0000
+@@ -0,0 +1,539 @@
++/* $Id$
++ *
++ *  Applications are owned by the controller and only
++ *  handle this controller, multiplexing multiple
++ *  controller with one application is done in the higher
++ *  driver independ CAPI driver. The application contain
++ *  the Listen state machine.
++ *
++ */
++
++#include "m_capi.h"
++#include "helper.h"
++#include "debug.h"
++#include "mISDNManufacturer.h"
++
++#define applDebug(appl, lev, fmt, args...) \
++        capidebug(lev, fmt, ## args)
++
++static struct list_head       garbage_applications = LIST_HEAD_INIT(garbage_applications);
++
++int
++ApplicationConstr(Controller_t *contr, __u16 ApplId, capi_register_params *rp)
++{
++      Application_t   *appl = kmalloc(sizeof(Application_t), GFP_ATOMIC);
++
++      if (!appl) {
++              return(-ENOMEM);
++      }
++      memset(appl, 0, sizeof(Application_t));
++      INIT_LIST_HEAD(&appl->head);
++      appl->contr = contr;
++      appl->maxplci = contr->maxplci;
++      appl->AppPlcis  = kmalloc(appl->maxplci * sizeof(AppPlci_t *), GFP_ATOMIC);
++      if (!appl->AppPlcis) {
++              kfree(appl);
++              return(-ENOMEM);
++      }
++      memset(appl->AppPlcis, 0, appl->maxplci * sizeof(AppPlci_t *));
++      appl->ApplId = ApplId;
++      appl->MsgId = 1;
++      appl->NotificationMask = 0;
++      memcpy(&appl->reg_params, rp, sizeof(capi_register_params));
++      listenConstr(appl);
++      list_add(&appl->head, &contr->Applications);
++      test_and_set_bit(APPL_STATE_ACTIV, &appl->state);
++      return(0);
++}
++
++/*
++ * Destroy the Application
++ *
++ * depending who initiate this we cannot release imediatly, if
++ * any AppPlci is still in use.
++ *
++ * @who:   0 - a AppPlci is released in state APPL_STATE_RELEASE
++ *         1 - Application is released from CAPI application
++ *         2 - the controller is resetted
++ *         3 - the controller is removed
++ *         4 - the CAPI module will be unload
++ */
++int
++ApplicationDestr(Application_t *appl, int who)
++{
++      int             i, used = 0;
++      AppPlci_t       **aplci_p = appl->AppPlcis;
++
++      if (test_and_set_bit(APPL_STATE_DESTRUCTOR, &appl->state)) {
++              // we are allready in this function
++              return(-EBUSY);
++      }
++      test_and_set_bit(APPL_STATE_RELEASE, &appl->state);
++      test_and_clear_bit(APPL_STATE_ACTIV, &appl->state);
++      listenDestr(appl);
++      if (who > 2) {
++              appl->contr = NULL;
++      }
++      if (aplci_p) {
++              for (i = 0; i < appl->maxplci; i++) {
++                      if (*aplci_p) {
++                              switch (who) {
++                                      case 4:
++                                              AppPlciDestr(*aplci_p);
++                                              *aplci_p = NULL;
++                                              break;
++                                      case 1:
++                                      case 2:
++                                      case 3:
++                                              AppPlciRelease(*aplci_p);
++                                      case 0:
++                                              if ((volatile AppPlci_t *)(*aplci_p))
++                                                      used++;
++                                              break;
++                              }
++                      }
++                      aplci_p++;
++              }
++      }
++      if (used) {
++              if (who == 3) {
++                      list_del_init(&appl->head);
++                      list_add(&appl->head, &garbage_applications);
++              }
++              test_and_clear_bit(APPL_STATE_DESTRUCTOR, &appl->state);
++              return(-EBUSY);
++      }
++      list_del_init(&appl->head);
++      appl->maxplci = 0;
++      kfree(appl->AppPlcis);
++      appl->AppPlcis = NULL;
++      kfree(appl);
++      return(0);
++}
++
++AppPlci_t *
++getAppPlci4addr(Application_t *appl, __u32 addr)
++{
++      int plci_idx = (addr >> 8) & 0xff;
++
++      if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) {
++              int_error();
++              return NULL;
++      }
++      return(appl->AppPlcis[plci_idx - 1]);
++}
++
++static void
++FacilityReq(Application_t *appl, struct sk_buff *skb)
++{
++      _cmsg           *cmsg;
++      AppPlci_t       *aplci;
++      Ncci_t          *ncci;
++
++      cmsg = cmsg_alloc();
++      if (!cmsg) {
++              int_error();
++              dev_kfree_skb(skb);
++              return;
++      }
++      capi_message2cmsg(cmsg, skb->data);
++      switch (cmsg->FacilitySelector) {
++              case 0x0000: // Handset
++              case 0x0001: // DTMF
++                      aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
++                      if (aplci) {
++                              ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_PLCI);
++                              if (ncci) {
++                                      ncciGetCmsg(ncci, cmsg);
++                                      break;
++                              }
++                      }
++                      SendCmsgAnswer2Application(appl, cmsg, CapiIllContrPlciNcci);
++                      break;
++              case 0x0003: // SupplementaryServices
++                      SupplementaryFacilityReq(appl, cmsg);
++                      break;
++              default:
++                      int_error();
++                      SendCmsgAnswer2Application(appl, cmsg, CapiFacilityNotSupported);
++                      break;
++      }
++      dev_kfree_skb(skb);
++}
++
++__u16
++ApplicationSendMessage(Application_t *appl, struct sk_buff *skb)
++{
++      Plci_t          *plci;
++      AppPlci_t       *aplci;
++      Ncci_t          *ncci;
++      __u16           ret = CAPI_NOERROR;
++
++      switch (CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data))) {
++              // for NCCI state machine
++              case CAPI_DATA_B3_REQ:
++              case CAPI_DATA_B3_RESP:
++              case CAPI_CONNECT_B3_RESP:
++              case CAPI_CONNECT_B3_ACTIVE_RESP:
++              case CAPI_DISCONNECT_B3_REQ:
++              case CAPI_DISCONNECT_B3_RESP:
++                      aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
++                      if (!aplci) {
++                              AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
++                              goto free;
++                      }
++                      ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_EXACT);
++                      if (!ncci) {
++                              int_error();
++                              AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
++                              goto free;
++                      }
++                      ret = ncciSendMessage(ncci, skb);
++                      break;
++              // new NCCI
++              case CAPI_CONNECT_B3_REQ:
++                      aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
++                      if (!aplci) {
++                              AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
++                              goto free;
++                      }
++                      ConnectB3Request(aplci, skb);
++                      break;
++              // for PLCI state machine
++              case CAPI_INFO_REQ:
++              case CAPI_ALERT_REQ:
++              case CAPI_CONNECT_RESP:
++              case CAPI_CONNECT_ACTIVE_RESP:
++              case CAPI_DISCONNECT_REQ:
++              case CAPI_DISCONNECT_RESP:
++              case CAPI_SELECT_B_PROTOCOL_REQ:
++                      aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
++                      if (!aplci) {
++                              AnswerMessage2Application(appl, skb, CapiIllContrPlciNcci);
++                              goto free;
++                      }
++                      ret = AppPlciSendMessage(aplci, skb);
++                      break;
++              case CAPI_CONNECT_REQ:
++                      if (ControllerNewPlci(appl->contr, &plci, MISDN_ID_ANY)) {
++                              AnswerMessage2Application(appl, skb, CapiNoPlciAvailable);
++                              goto free;
++                      }
++                      aplci = ApplicationNewAppPlci(appl, plci);
++                      if (!aplci) {
++                              AnswerMessage2Application(appl, skb, CapiNoPlciAvailable);
++                              goto free;
++                      }
++                      ret = AppPlciSendMessage(aplci, skb);
++                      break;
++
++              // for LISTEN state machine
++              case CAPI_LISTEN_REQ:
++                      ret = listenSendMessage(appl, skb);
++                      break;
++
++              // other
++              case CAPI_FACILITY_REQ:
++                      FacilityReq(appl, skb);
++                      break;
++              case CAPI_FACILITY_RESP:
++                      goto free;
++              case CAPI_MANUFACTURER_REQ:
++                      applManufacturerReq(appl, skb);
++                      break;
++              case CAPI_INFO_RESP:
++                      goto free;
++              default:
++                      applDebug(appl, CAPI_DBG_WARN, "applSendMessage: %#x %#x not handled!", 
++                              CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
++                      ret = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
++                      break;
++      }
++      return(ret);
++ free:
++      dev_kfree_skb(skb);
++      return(ret);
++}
++
++AppPlci_t *
++ApplicationNewAppPlci(Application_t *appl, Plci_t *plci)
++{
++      AppPlci_t       *aplci;
++      int             plci_idx = (plci->addr >> 8) & 0xff;
++
++      if (test_bit(APPL_STATE_RELEASE, &appl->state))
++              return(NULL);
++      if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) {
++              int_error();
++              return(NULL);
++      }
++      if (appl->AppPlcis[plci_idx - 1]) {
++              int_error();
++              return(NULL);
++      }
++      if (AppPlciConstr(&aplci, appl, plci)) {
++              int_error();
++              return(NULL);
++      }
++      applDebug(appl, CAPI_DBG_APPL_INFO, "ApplicationNewAppPlci: idx(%d) aplci(%p) appl(%p) plci(%p)",
++              plci_idx, aplci, appl, plci);
++      appl->AppPlcis[plci_idx - 1] = aplci;
++      plciAttachAppPlci(plci, aplci);
++      return(aplci);
++}
++
++void
++ApplicationDelAppPlci(Application_t *appl, AppPlci_t *aplci)
++{
++      int     plci_idx = (aplci->addr >> 8) & 0xff;
++
++      if ((plci_idx < 1) || (plci_idx >= appl->maxplci)) {
++              int_error();
++              return;
++      }
++      if (appl->AppPlcis[plci_idx - 1] != aplci) {
++              int_error();
++              return;
++      }
++      appl->AppPlcis[plci_idx - 1] = NULL;
++      if (test_bit(APPL_STATE_RELEASE, &appl->state) &&
++              !test_bit(APPL_STATE_DESTRUCTOR, &appl->state))
++              ApplicationDestr(appl, 0);
++}
++
++void
++SendCmsg2Application(Application_t *appl, _cmsg *cmsg)
++{
++      struct sk_buff  *skb;
++      
++      if (test_bit(APPL_STATE_RELEASE, &appl->state)) {
++              /* Application is released and cannot receive messages
++               * anymore. To avoid stalls in the state machines we
++               * must answer INDICATIONS.
++               */
++              AppPlci_t       *aplci;
++              Ncci_t          *ncci;
++
++              if (CAPI_IND != cmsg->Subcommand)
++                      goto free;
++              switch(cmsg->Command) {
++                      // for NCCI state machine
++                      case CAPI_CONNECT_B3:
++                              cmsg->Reject = 2;
++                      case CAPI_CONNECT_B3_ACTIVE:
++                      case CAPI_DISCONNECT_B3:
++                              aplci = getAppPlci4addr(appl, (cmsg->adr.adrNCCI & 0xffff));
++                              if (!aplci)
++                                      goto free;
++                              ncci = getNCCI4addr(aplci, cmsg->adr.adrNCCI, GET_NCCI_EXACT); 
++                              if (!ncci) {
++                                      int_error();
++                                      goto free;
++                              }
++                              capi_cmsg_answer(cmsg);
++                              ncciGetCmsg(ncci, cmsg);
++                              break;
++                      // for PLCI state machine
++                      case CAPI_CONNECT:
++                              cmsg->Reject = 2;
++                      case CAPI_CONNECT_ACTIVE:
++                      case CAPI_DISCONNECT:
++                              aplci = getAppPlci4addr(appl, (cmsg->adr.adrPLCI & 0xffff));
++                              if (!aplci)
++                                      goto free;
++                              capi_cmsg_answer(cmsg);
++                              AppPlciGetCmsg(aplci, cmsg);
++                              break;
++                      case CAPI_FACILITY:
++                      case CAPI_MANUFACTURER:
++                      case CAPI_INFO:
++                              goto free;
++                      default:
++                              int_error();
++                              goto free;
++              }
++              return;
++      }
++      if (!(skb = alloc_skb(CAPI_MSG_DEFAULT_LEN, GFP_ATOMIC))) {
++              printk(KERN_WARNING "%s: no mem for %d bytes\n", __FUNCTION__, CAPI_MSG_DEFAULT_LEN);
++              int_error();
++              goto free;
++      }
++      capi_cmsg2message(cmsg, skb->data);
++      applDebug(appl, CAPI_DBG_APPL_MSG, "%s: len(%d) applid(%x) %s msgnr(%d) addr(%08x)",
++              __FUNCTION__, CAPIMSG_LEN(skb->data), cmsg->ApplId, capi_cmd2str(cmsg->Command, cmsg->Subcommand),
++              cmsg->Messagenumber, cmsg->adr.adrController);
++      if (CAPI_MSG_DEFAULT_LEN < CAPIMSG_LEN(skb->data)) {
++              printk(KERN_ERR "%s: CAPI_MSG_DEFAULT_LEN overrun (%d/%d)\n",  __FUNCTION__,
++                      CAPIMSG_LEN(skb->data), CAPI_MSG_DEFAULT_LEN);
++              int_error();
++              dev_kfree_skb(skb);
++              goto free; 
++      }
++      skb_put(skb, CAPIMSG_LEN(skb->data));
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      appl->contr->ctrl->handle_capimsg(appl->contr->ctrl, cmsg->ApplId, skb);
++#else
++      capi_ctr_handle_message(appl->contr->ctrl, cmsg->ApplId, skb);
++#endif
++free:
++      cmsg_free(cmsg);
++}
++
++void
++SendCmsgAnswer2Application(Application_t *appl, _cmsg *cmsg, __u16 Info)
++{
++      capi_cmsg_answer(cmsg);
++      cmsg->Info = Info;
++      SendCmsg2Application(appl, cmsg);
++}
++
++void
++AnswerMessage2Application(Application_t *appl, struct sk_buff *skb, __u16 Info)
++{
++      _cmsg   *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      capi_message2cmsg(cmsg, skb->data);
++      SendCmsgAnswer2Application(appl, cmsg, Info);
++}
++
++#define AVM_MANUFACTURER_ID   0x214D5641 /* "AVM!" */
++#define CLASS_AVM             0x00
++#define FUNCTION_AVM_D2_TRACE 0x01
++
++struct AVMD2Trace {
++      __u8 Length;
++      __u8 data[4];
++};
++
++void applManufacturerReqAVM(Application_t *appl, _cmsg *cmsg, struct sk_buff *skb)
++{
++      struct AVMD2Trace *at;
++
++      if (cmsg->Class != CLASS_AVM) {
++              applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown class %#x\n", cmsg->Class);
++              cmsg_free(cmsg);
++              dev_kfree_skb(skb);
++              return;
++      }
++      switch (cmsg->Function) {
++              case FUNCTION_AVM_D2_TRACE:
++                      at = (struct AVMD2Trace *)cmsg->ManuData;
++                      if (!at || at->Length != 4) {
++                              int_error();
++                              break;
++                      }
++                      if (memcmp(at->data, "\200\014\000\000", 4) == 0) {
++                              test_and_set_bit(APPL_STATE_D2TRACE, &appl->state);
++                      } else if (memcmp(at->data, "\000\000\000\000", 4) == 0) {
++                              test_and_clear_bit(APPL_STATE_D2TRACE, &appl->state);
++                      } else {
++                              int_error();
++                      }
++                      break;
++              default:
++                      applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown function %#x\n", cmsg->Function);
++      }
++      cmsg_free(cmsg);
++      dev_kfree_skb(skb);
++}
++
++void applManufacturerReqmISDN(Application_t *appl, _cmsg *cmsg, struct sk_buff *skb)
++{
++      AppPlci_t       *aplci;
++      Ncci_t          *ncci;
++
++      switch (cmsg->Class) {
++              case mISDN_MF_CLASS_HANDSET:
++                      /* Note normally MANUFATURER messages are only defined for
++                       * controller address we extent it here to PLCI/NCCI
++                       */
++                      aplci = getAppPlci4addr(appl, CAPIMSG_CONTROL(skb->data));
++                      if (aplci) {
++                              ncci = getNCCI4addr(aplci, CAPIMSG_NCCI(skb->data), GET_NCCI_PLCI);
++                              if (ncci) {
++                                      cmsg_free(cmsg);
++                                      ncciSendMessage(ncci, skb);
++                                      return;
++                              }
++                      }
++                      SendCmsgAnswer2Application(appl, cmsg, CapiIllContrPlciNcci);
++                      break;
++              default:
++                      cmsg_free(cmsg);
++                      break;
++      }
++      dev_kfree_skb(skb);
++}
++
++void
++applManufacturerReq(Application_t *appl, struct sk_buff *skb)
++{
++      _cmsg   *cmsg;
++
++      if (skb->len < 16 + 8) {
++              dev_kfree_skb(skb);
++              return;
++      }
++      cmsg = cmsg_alloc();
++      if (!cmsg) {
++              int_error();
++              dev_kfree_skb(skb);
++              return;
++      }
++      capi_message2cmsg(cmsg, skb->data);
++      switch (cmsg->ManuID) {
++              case mISDN_MANUFACTURER_ID:
++                      applManufacturerReqmISDN(appl, cmsg, skb);
++                      break;
++              case AVM_MANUFACTURER_ID:
++                      applManufacturerReqAVM(appl, cmsg, skb);
++                      break;
++              default:
++                      applDebug(appl, CAPI_DBG_APPL_INFO, "CAPI: unknown ManuID %#x\n", cmsg->ManuID);
++                      cmsg_free(cmsg);
++                      dev_kfree_skb(skb);
++                      break;
++      }
++}
++
++void applD2Trace(Application_t *appl, u_char *buf, int len)
++{
++      _cmsg   *cmsg;
++      __u8    manuData[255];
++
++      if (!test_bit(APPL_STATE_D2TRACE, &appl->state))
++              return;
++      
++      CMSG_ALLOC(cmsg);
++      capi_cmsg_header(cmsg, appl->ApplId, CAPI_MANUFACTURER, CAPI_IND, 
++                       appl->MsgId++, appl->contr->addr);
++      cmsg->ManuID = AVM_MANUFACTURER_ID;
++      cmsg->Class = CLASS_AVM;
++      cmsg->Function = FUNCTION_AVM_D2_TRACE;
++      cmsg->ManuData = (_cstruct) &manuData;
++      manuData[0] = 2 + len; // length
++      manuData[1] = 0x80;
++      manuData[2] = 0x0f;
++      memcpy(&manuData[3], buf, len);
++      
++      SendCmsg2Application(appl, cmsg);
++}
++
++void
++free_Application(void)
++{
++      struct list_head        *item, *next;
++      int                     n = 0;
++
++      if (list_empty(&garbage_applications)) {
++              printk(KERN_DEBUG "%s: no garbage\n", __FUNCTION__);
++              return;
++      }
++      list_for_each_safe(item, next, &garbage_applications) {
++              ApplicationDestr((Application_t *)item, 4);
++              n++;
++      }
++      printk(KERN_WARNING"%s: %d garbage items\n", __FUNCTION__, n);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.c 2004-11-22 09:33:37.700812520 +0000
+@@ -0,0 +1,141 @@
++/* $Id$
++ *
++ * arcofi.c   Ansteuerung ARCOFI 2165
++ *
++ * Author     Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++ 
++#include "dchannel.h"
++#include "layer1.h"
++#include "isac.h"
++#include "arcofi.h"
++#include "debug.h"
++
++#define ARCOFI_TIMER_VALUE    20
++
++static void
++add_arcofi_timer(dchannel_t *dch) {
++      isac_chip_t     *isac = dch->hw;
++
++      if (test_and_set_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
++              del_timer(&isac->arcofitimer);
++      }       
++      init_timer(&isac->arcofitimer);
++      isac->arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000);
++      add_timer(&isac->arcofitimer);
++}
++
++static void
++send_arcofi(dchannel_t *dch) {
++      u_char          val;
++      isac_chip_t     *isac = dch->hw;
++      
++      add_arcofi_timer(dch);
++      isac->mon_txp = 0;
++      isac->mon_txc = isac->arcofi_list->len;
++      memcpy(isac->mon_tx, isac->arcofi_list->msg, isac->mon_txc);
++      switch(isac->arcofi_bc) {
++              case 0: break;
++              case 1: isac->mon_tx[1] |= 0x40;
++                      break;
++              default: break;
++      }
++      isac->mocr &= 0x0f;
++      isac->mocr |= 0xa0;
++      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++      val = dch->read_reg(dch->inst.data, ISAC_MOSR);
++      dch->write_reg(dch->inst.data, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
++      isac->mocr |= 0x10;
++      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++}
++
++int
++arcofi_fsm(dchannel_t *dch, int event, void *data) {
++      isac_chip_t     *isac = dch->hw;
++
++      if (dch->debug & L1_DEB_MONITOR) {
++              mISDN_debugprint(&dch->inst, "arcofi state %d event %d", isac->arcofi_state, event);
++      }
++      if (event == ARCOFI_TIMEOUT) {
++              isac->arcofi_state = ARCOFI_NOP;
++              test_and_set_bit(FLG_ARCOFI_ERROR, &dch->DFlags);
++              wake_up(&isac->arcofi_wait);
++              return(1);
++      }
++      switch (isac->arcofi_state) {
++              case ARCOFI_NOP:
++                      if (event == ARCOFI_START) {
++                              isac->arcofi_list = data;
++                              isac->arcofi_state = ARCOFI_TRANSMIT;
++                              send_arcofi(dch);
++                      }
++                      break;
++              case ARCOFI_TRANSMIT:
++                      if (event == ARCOFI_TX_END) {
++                              if (isac->arcofi_list->receive) {
++                                      add_arcofi_timer(dch);
++                                      isac->arcofi_state = ARCOFI_RECEIVE;
++                              } else {
++                                      if (isac->arcofi_list->next) {
++                                              isac->arcofi_list =
++                                                      isac->arcofi_list->next;
++                                              send_arcofi(dch);
++                                      } else {
++                                              if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
++                                                      del_timer(&isac->arcofitimer);
++                                              }
++                                              isac->arcofi_state = ARCOFI_NOP;
++                                              wake_up(&isac->arcofi_wait);
++                                      }
++                              }
++                      }
++                      break;
++              case ARCOFI_RECEIVE:
++                      if (event == ARCOFI_RX_END) {
++                              if (isac->arcofi_list->next) {
++                                      isac->arcofi_list =
++                                              isac->arcofi_list->next;
++                                      isac->arcofi_state = ARCOFI_TRANSMIT;
++                                      send_arcofi(dch);
++                              } else {
++                                      if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
++                                              del_timer(&isac->arcofitimer);
++                                      }
++                                      isac->arcofi_state = ARCOFI_NOP;
++                                      wake_up(&isac->arcofi_wait);
++                              }
++                      }
++                      break;
++              default:
++                      mISDN_debugprint(&dch->inst, "Arcofi unknown state %x", isac->arcofi_state);
++                      return(2);
++      }
++      return(0);
++}
++
++static void
++arcofi_timer(dchannel_t *dch) {
++      arcofi_fsm(dch, ARCOFI_TIMEOUT, NULL);
++}
++
++void
++clear_arcofi(dchannel_t *dch) {
++      isac_chip_t     *isac = dch->hw;
++
++      if (test_and_clear_bit(FLG_ARCOFI_TIMER, &dch->DFlags)) {
++              del_timer(&isac->arcofitimer);
++      }
++}
++
++void
++init_arcofi(dchannel_t *dch) {
++      isac_chip_t     *isac = dch->hw;
++
++      isac->arcofitimer.function = (void *) arcofi_timer;
++      isac->arcofitimer.data = (long) dch;
++      init_timer(&isac->arcofitimer);
++      dch->type |= ISAC_TYPE_ARCOFI;
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/arcofi.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/arcofi.h 2004-11-22 09:33:37.710811000 +0000
+@@ -0,0 +1,32 @@
++/* $Id$
++ *
++ * arcofi.h   Ansteuerung ARCOFI 2165
++ *
++ * Author     Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++ 
++#define ARCOFI_USE    1
++
++/* states */
++#define ARCOFI_NOP    0
++#define ARCOFI_TRANSMIT       1
++#define ARCOFI_RECEIVE        2
++/* events */
++#define ARCOFI_START  1
++#define ARCOFI_TX_END 2
++#define ARCOFI_RX_END 3
++#define ARCOFI_TIMEOUT        4
++
++struct arcofi_msg {
++      struct arcofi_msg       *next;
++      u_char                  receive;
++      u_char                  len;
++      u_char                  msg[10];
++};
++
++extern int arcofi_fsm(dchannel_t *, int, void *);
++extern void init_arcofi(dchannel_t *);
++extern void clear_arcofi(dchannel_t *);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.c   2004-11-22 09:33:37.720809480 +0000
+@@ -0,0 +1,133 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++
++int ParseTag(u_char *p, u_char *end, int *tag)
++{
++      *tag = *p;
++      return 1;
++}
++
++int ParseLen(u_char *p, u_char *end, int *len)
++{
++      int l, i;
++
++      if (*p == 0x80) { // indefinite
++              *len = -1;
++              return 1;
++      }
++      if (!(*p & 0x80)) { // one byte
++              *len = *p;
++              return 1;
++      }
++      *len = 0;
++      l = *p & ~0x80;
++      p++;
++      for (i = 0; i < l; i++) {
++              *len = (*len << 8) + *p; 
++              p++;
++      }
++      return l+1;
++}
++
++int
++ParseASN1(u_char *p, u_char *end, int level)
++{
++      int tag, len;
++      int ret;
++      int j;
++      u_char *tag_end, *beg;
++
++      beg = p;
++
++      CallASN1(ret, p, end, ParseTag(p, end, &tag));
++      CallASN1(ret, p, end, ParseLen(p, end, &len));
++#ifdef ASN1_DEBUG
++      for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " ");
++      print_asn1msg(PRT_DEBUG_DECODE, "TAG 0x%02x LEN %3d\n", tag, len);
++#endif
++      
++      if (tag & ASN1_TAG_CONSTRUCTED) {
++              if (len == -1) { // indefinite
++                      while (*p) {
++                              CallASN1(ret, p, end, ParseASN1(p, end, level + 1));
++                      }
++                      p++;
++                      if (*p) 
++                              return -1;
++                      p++;
++              } else {
++                      tag_end = p + len;
++                      while (p < tag_end) {
++                              CallASN1(ret, p, end, ParseASN1(p, end, level +1));
++                      }
++              }
++      } else {
++              for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " ");
++              while (len--) {
++                      print_asn1msg(PRT_DEBUG_DECODE, "%02x ", *p);
++                      p++;
++              }
++              print_asn1msg(PRT_DEBUG_DECODE, "\n");
++      }
++      for (j = 0; j < level*5; j++) print_asn1msg(PRT_DEBUG_DECODE, " ");
++      print_asn1msg(PRT_DEBUG_DECODE, "END (%d)\n", p - beg - 2);
++      return p - beg;
++}
++
++#if 0
++
++#if 0
++u_char data[] = {"\xA2\x03\x02\x01\xA3"};
++#endif
++#if 0 // ActNotDiv
++u_char data[] = {"\xA1\x2C\x02\x01\x7E\x02\x01\x09\x30\x24\x0A"
++               "\x01\x02\x0A\x01\x03\x30\x0C\x80\x0A\x30\x31"
++               "\x33\x30\x31\x34\x34\x37\x37\x30\xA1\x0E\x0A"
++               "\x01\x02\x12\x09\x32\x31\x31\x33\x34\x31\x38\x33\x30"};
++#endif
++#if 0 // ActDiv
++u_char data[] = {"\xA1\x24\x02\x01\xA1\x02\x01\x07\x30\x1C\x0A"
++               "\x01\x02\x0A\x01\x01\x30\x0C\x80\x0A\x30"
++               "\x31\x33\x30\x31\x34\x34\x37\x37\x30\x80"
++               "\x06\x33\x34\x31\x38\x33\x30"};
++#endif
++#if 0 // DeactNotDiv
++u_char data[] = {"\xA1\x1E\x02\x01\x08\x02\x01\x0A\x30\x16\x0A"
++               "\x01\x02\x0A\x01\x03\xA1\x0E\x0A\x01\x02\x12"
++               "\x09\x32\x31\x31\x33\x34\x31\x38\x33\x30"};
++#endif
++#if 1 // DeactDiv
++u_char data[] = {"\xA1\x16\x02\x01\xB1\x02\x01\x08\x30\x0E\x0A"
++               "\x01\x02\x0A\x01\x01\x80\x06\x33\x34\x31\x38\x33\x30"};
++#endif
++#if 0 // AOCE, 0 Einheiten
++u_char data[] = {"\xA1\x15\x02\x02\x00\xDC\x02\x01\x24\x30\x0C"
++               "\x30\x0A\xA1\x05\x30\x03\x02\x01\x00\x82\x01\x00"};
++#endif
++#if 0 // AOCE, 1 Einheit
++u_char data[] = {"\xA1\x15\x02\x02\x00\xBC\x02\x01\x24\x30\x0C\x30"
++               "\x0A\xA1\x05\x30\x03\x02\x01\x01\x82\x01\x00"};
++#endif
++#if 0 // AOCD currency
++u_char data[] = {"\xA1\x1A\x02\x02\x1C\x65\x02\x01\x21\x30\x11\xA1\x0C\x81\x02\x44\x4D\xA2\x06\x81\x01\x18\x82\x01\x01\x82\x01\x00"};
++#endif
++u_char *end = data + 47;
++
++#include "asn1_component.h"
++
++void
++main()
++{
++      struct Aoc chan;
++
++#ifdef ASN1_DEBUG
++      ParseASN1(data, end, 0);
++#endif
++
++      ParseComponent(&chan, data, end);
++}
++
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1.h       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1.h   2004-11-22 09:33:37.730807960 +0000
+@@ -0,0 +1,235 @@
++/* $Id$
++ *
++ */
++
++#include <linux/mISDNif.h>
++#include "helper.h"
++
++#ifndef __ASN1_H__
++#define __ASN1_H__
++
++typedef enum {
++      invoke       = 1,
++      returnResult = 2,
++      returnError  = 3,
++      reject       = 4,
++} asn1Component;
++
++struct PublicPartyNumber {
++      int publicTypeOfNumber;
++      char numberDigits[30];
++};
++
++struct PartyNumber {
++      int type;
++      union {
++              char unknown[30];
++              struct PublicPartyNumber publicPartyNumber;
++      } p;
++};
++
++struct Address {
++      struct PartyNumber partyNumber;
++      char partySubaddress[30];
++};
++
++struct ServedUserNr {
++      int all;
++      struct PartyNumber partyNumber;
++};
++
++struct ActDivNotification {
++      int procedure;
++      int basicService;
++      struct ServedUserNr servedUserNr;
++      struct Address address;
++};
++
++struct DeactDivNotification {
++      int procedure;
++      int basicService;
++      struct ServedUserNr servedUserNr;
++};
++
++struct ServedUserNumberList {
++      struct PartyNumber partyNumber[10];
++};
++
++struct IntResult {
++      struct ServedUserNr servedUserNr;
++      int procedure;
++      int basicService;
++      struct Address address;
++};
++
++struct IntResultList {
++      struct IntResult intResult[10];
++};
++
++struct asn1Invoke {
++      __u16 invokeId;
++      __u16 operationValue;
++      union {
++              struct ActDivNotification actNot;
++              struct DeactDivNotification deactNot;
++      } o;
++};
++
++struct asn1ReturnResult {
++      __u16 invokeId;
++      union {
++              struct ServedUserNumberList list;
++              struct IntResultList resultList;
++      } o;
++};
++
++struct asn1ReturnError {
++      __u16 invokeId;
++      __u16 errorValue;
++};
++
++struct asn1_parm {
++      asn1Component comp;
++      union {
++              struct asn1Invoke       inv;
++              struct asn1ReturnResult retResult;
++              struct asn1ReturnError  retError;
++      } u;
++};
++
++
++#undef ASN1_DEBUG
++
++#ifdef ASN1_DEBUG
++#define print_asn1msg(dummy, fmt, args...) printk(fmt, ## args)
++#else
++#define print_asn1msg(dummy, fmt, args...) 
++#endif
++
++int ParseASN1(u_char *p, u_char *end, int level);
++
++int ParseTag(u_char *p, u_char *end, int *tag);
++int ParseLen(u_char *p, u_char *end, int *len);
++
++#define ASN1_TAG_BOOLEAN           (0x01) // is that true?
++#define ASN1_TAG_INTEGER           (0x02)
++#define ASN1_TAG_BIT_STRING        (0x03)
++#define ASN1_TAG_OCTET_STRING      (0x04)
++#define ASN1_TAG_NULL              (0x05)
++#define ASN1_TAG_OBJECT_IDENTIFIER (0x06)
++#define ASN1_TAG_ENUM              (0x0a)
++#define ASN1_TAG_SEQUENCE          (0x30)
++#define ASN1_TAG_SET               (0x31)
++#define ASN1_TAG_NUMERIC_STRING    (0x12)
++#define ASN1_TAG_PRINTABLE_STRING  (0x13)
++#define ASN1_TAG_IA5_STRING        (0x16)
++#define ASN1_TAG_UTC_TIME          (0x17)
++
++#define ASN1_TAG_CONSTRUCTED       (0x20)
++#define ASN1_TAG_CONTEXT_SPECIFIC  (0x80)
++
++#define ASN1_TAG_EXPLICIT          (0x100)
++#define ASN1_TAG_OPT               (0x200)
++#define ASN1_NOT_TAGGED            (0x400)
++
++#define CallASN1(ret, p, end, todo) do { \
++        ret = todo; \
++      if (ret < 0) { \
++                int_error(); \
++                return -1; \
++        } \
++        p += ret; \
++} while (0)
++
++#define INIT \
++      int tag, len; \
++      int ret; \
++      u_char *beg; \
++        \
++        print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> %s\n", __FUNCTION__); \
++      beg = p; \
++      CallASN1(ret, p, end, ParseTag(p, end, &tag)); \
++      CallASN1(ret, p, end, ParseLen(p, end, &len)); \
++        if (len >= 0) { \
++                if (p + len > end) \
++                        return -1; \
++                end = p + len; \
++        }
++
++#define XSEQUENCE_1(todo, act_tag, the_tag, arg1) do { \
++      if (p < end) { \
++              if (((the_tag) &~ ASN1_TAG_OPT) == ASN1_NOT_TAGGED) { \
++                      if (((u_char)act_tag == *p) || ((act_tag) == ASN1_NOT_TAGGED)) { \
++                              CallASN1(ret, p, end, todo(pc, p, end, arg1)); \
++                        } else { \
++                                if (!((the_tag) & ASN1_TAG_OPT)) { \
++                                        print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 1 %s:%d\n", __FUNCTION__, __LINE__); \
++                                      return -1; \
++                                } \
++                        } \
++              } else { \
++                        if ((the_tag) & ASN1_TAG_EXPLICIT) { \
++                              if ((u_char)(((the_tag) & 0xff) | (ASN1_TAG_CONTEXT_SPECIFIC | ASN1_TAG_CONSTRUCTED)) == *p) { \
++                                        int xtag, xlen; \
++                                      CallASN1(ret, p, end, ParseTag(p, end, &xtag)); \
++                                      CallASN1(ret, p, end, ParseLen(p, end, &xlen)); \
++                                      CallASN1(ret, p, end, todo(pc, p, end, arg1)); \
++                                } else { \
++                                        if (!(the_tag) & ASN1_TAG_OPT) { \
++                                                print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 2 %s:%d\n", __FUNCTION__, __LINE__); \
++                                              return -1; \
++                                        } \
++                                } \
++                        } else { \
++                              if ((u_char)(((the_tag) & 0xff) | (ASN1_TAG_CONTEXT_SPECIFIC | (act_tag & ASN1_TAG_CONSTRUCTED))) == *p) { \
++                                      CallASN1(ret, p, end, todo(pc, p, end, arg1)); \
++                                } else { \
++                                        if (!(the_tag) & ASN1_TAG_OPT) { \
++                                                print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 3 %s:%d\n", __FUNCTION__, __LINE__); \
++                                              return -1; \
++                                        } \
++                                } \
++                      } \
++              } \
++        } else { \
++                if (!(the_tag) & ASN1_TAG_OPT) { \
++                        print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 4 %s:%d\n", __FUNCTION__, __LINE__); \
++                      return -1; \
++                } \
++        } \
++} while (0)
++
++#define XSEQUENCE_OPT_1(todo, act_tag, the_tag, arg1) \
++        XSEQUENCE_1(todo, act_tag, (the_tag | ASN1_TAG_OPT), arg1)
++
++#define XSEQUENCE(todo, act_tag, the_tag) XSEQUENCE_1(todo, act_tag, the_tag, -1)
++#define XSEQUENCE_OPT(todo, act_tag, the_tag) XSEQUENCE_OPT_1(todo, act_tag, the_tag, -1)
++
++#define XCHOICE_1(todo, act_tag, the_tag, arg1) \
++      if (act_tag == ASN1_NOT_TAGGED) { \
++              return todo(pc, beg, end, arg1); \
++        } \
++        if (the_tag == ASN1_NOT_TAGGED) { \
++                if (act_tag == tag) { \
++                            return todo(pc, beg, end, arg1); \
++                  } \
++         } else { \
++                if ((the_tag | (0x80 | (act_tag & 0x20))) == tag) { \
++                            return todo(pc, beg, end, arg1); \
++                  } \
++       }
++
++#define XCHOICE(todo, act_tag, the_tag) XCHOICE_1(todo, act_tag, the_tag, -1)
++
++#define XCHOICE_DEFAULT do {\
++          print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> err 5 %s:%d\n", __FUNCTION__, __LINE__); \
++          return -1; \
++        } while (0)
++
++#define CHECK_P do { \
++        if (p >= end) \
++                 return -1; \
++        } while (0) 
++
++
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.c   2004-11-22 09:33:37.740806440 +0000
+@@ -0,0 +1,233 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++#include "asn1_generic.h"
++#include "asn1_address.h"
++
++void buildnumber(char *num, int oc3, int oc3a, char *result, int version,
++               int *provider, int *sondernummer, int *intern, int *local,
++               int dir, int who);
++
++
++// ======================================================================
++// Address Types EN 300 196-1 D.3
++
++int ParsePresentationRestricted(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      int ret;
++
++      ret = ParseNull(pc, p, end, -1);
++      if (ret < 0)
++              return ret;
++      strcpy(str, "(presentation restricted)");
++      return ret;
++}
++
++int ParseNotAvailInterworking(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      int ret;
++
++      ret = ParseNull(pc, p, end, -1);
++      if (ret < 0)
++              return ret;
++      strcpy(str, "(not available)");
++      return ret;
++}
++
++int ParsePresentedAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      INIT;
++
++      XCHOICE_1(ParseAddressScreened, ASN1_TAG_SEQUENCE, 0, str);
++      XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str);
++      XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str);
++      XCHOICE_1(ParseAddressScreened, ASN1_TAG_NULL, 3, str);
++      XCHOICE_DEFAULT;
++}
++
++int ParsePresentedNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      INIT;
++
++      XCHOICE_1(ParseNumberScreened, ASN1_TAG_SEQUENCE, 0, str);
++      XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str);
++      XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str);
++      XCHOICE_1(ParseNumberScreened, ASN1_TAG_NULL, 3, str);
++      XCHOICE_DEFAULT;
++}
++
++int ParsePresentedNumberUnscreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      struct PartyNumber partyNumber;
++      INIT;
++
++      XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 0, &partyNumber); // FIXME EXP
++      XCHOICE_1(ParsePresentationRestricted, ASN1_TAG_NULL, 1, str);
++      XCHOICE_1(ParseNotAvailInterworking, ASN1_TAG_NULL, 2, str);
++      XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 3, &partyNumber); // FIXME EXP
++      XCHOICE_DEFAULT;
++}
++
++int ParseNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      struct PartyNumber partyNumber;
++      char screeningIndicator[30];
++      INIT;
++
++      XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &partyNumber);
++      XSEQUENCE_1(ParseScreeningIndicator, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, screeningIndicator);
++
++//    str += sprintf(str, "%s", partyNumber);
++
++      return p - beg;
++}
++
++int ParseAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      struct PartyNumber partyNumber;
++      char partySubaddress[30] = { 0, };
++      char screeningIndicator[30];
++      INIT;
++
++      XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &partyNumber);
++      XSEQUENCE_1(ParseScreeningIndicator, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, screeningIndicator);
++      XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, partySubaddress);
++
++//    str += sprintf(str, "%s", partyNumber);
++      if (strlen(partySubaddress))
++              str += sprintf(str, ".%s", partySubaddress);
++
++      return p - beg;
++}
++
++int ParseAddress(struct asn1_parm *pc, u_char *p, u_char *end, struct Address *address)
++{
++      INIT;
++
++      address->partySubaddress[0] = 0;
++      XSEQUENCE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &address->partyNumber);
++      
++      XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, address->partySubaddress);
++
++      return p - beg;
++}
++
++int ParsePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PartyNumber *partyNumber)
++{
++      INIT;
++
++      partyNumber->type = 0;
++      XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 0, partyNumber->p.unknown); // unknownPartyNumber
++      partyNumber->type = 1;
++      XCHOICE_1(ParsePublicPartyNumber, ASN1_TAG_SEQUENCE, 1, &partyNumber->p.publicPartyNumber); 
++#if 0
++      XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 3, str); // dataPartyNumber
++      XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 4, str); // telexPartyNumber
++      XCHOICE_1(ParsePrivatePartyNumber, ASN1_TAG_SEQUENCE, 5, str);
++      XCHOICE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, 8, str); // nationalStandardPartyNumber
++#endif
++      XCHOICE_DEFAULT;
++}
++
++int ParsePublicPartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PublicPartyNumber *publicPartyNumber)
++{
++      INIT;
++
++      XSEQUENCE_1(ParsePublicTypeOfNumber, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &publicPartyNumber->publicTypeOfNumber);
++      XSEQUENCE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, ASN1_NOT_TAGGED, publicPartyNumber->numberDigits);
++
++      return p - beg;
++}
++
++#if 0
++int ParsePrivatePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      int privateTypeOfNumber;
++      char numberDigits[20];
++      INIT;
++
++      XSEQUENCE_1(ParsePrivateTypeOfNumber, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &privateTypeOfNumber); 
++      XSEQUENCE_1(ParseNumberDigits, ASN1_TAG_NUMERIC_STRING, ASN1_NOT_TAGGED, numberDigits); 
++
++      switch (privateTypeOfNumber) {
++      case 0: str += sprintf(str, "(unknown)"); break;
++      case 1: str += sprintf(str, "(regional2)"); break;
++      case 2: str += sprintf(str, "(regional1)"); break;
++      case 3: str += sprintf(str, "(ptn)"); break;
++      case 4: str += sprintf(str, "(local)"); break;
++      case 6: str += sprintf(str, "(abbrev)"); break;
++      }
++      str += sprintf(str, numberDigits);
++
++      return p - beg;
++}
++#endif
++
++int ParsePublicTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *publicTypeOfNumber)
++{
++      return ParseEnum(pc, p, end, publicTypeOfNumber);
++}
++
++#if 0
++int ParsePrivateTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *privateTypeOfNumber)
++{
++      return ParseEnum(pc, p, end, privateTypeOfNumber);
++}
++#endif
++
++int ParsePartySubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      INIT;
++
++      XCHOICE_1(ParseUserSpecifiedSubaddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, str);
++      XCHOICE_1(ParseNSAPSubaddress, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, str);
++      XCHOICE_DEFAULT;
++}
++
++int ParseUserSpecifiedSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      int oddCountIndicator;
++      INIT;
++
++      XSEQUENCE_1(ParseSubaddressInformation, ASN1_TAG_OCTET_STRING, ASN1_NOT_TAGGED, str);
++      XSEQUENCE_OPT_1(ParseBoolean, ASN1_TAG_BOOLEAN, ASN1_NOT_TAGGED, &oddCountIndicator);
++      
++      return p - beg;
++}
++
++int ParseNSAPSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      return ParseOctetString(pc, p, end, str);
++}
++
++int ParseSubaddressInformation(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      return ParseOctetString(pc, p, end, str);
++}
++
++int ParseScreeningIndicator(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      int ret;
++      int screeningIndicator;
++
++      ret = ParseEnum(pc, p, end, &screeningIndicator);
++      if (ret < 0)
++              return ret;
++      
++      switch (screeningIndicator) {
++      case 0: sprintf(str, "user provided, not screened"); break;
++      case 1: sprintf(str, "user provided, passed"); break;
++      case 2: sprintf(str, "user provided, failed"); break;
++      case 3: sprintf(str, "network provided"); break;
++      default: sprintf(str, "(%d)", screeningIndicator); break;
++      }
++
++      return ret;
++}
++
++int ParseNumberDigits(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      return ParseNumericString(pc, p, end, str);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_address.h       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_address.h   2004-11-22 09:33:37.751804768 +0000
+@@ -0,0 +1,25 @@
++/* $Id$
++ *
++ */
++
++// ======================================================================
++// Address Types EN 300 196-1 D.3
++
++int ParsePresentedAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParsePresentedNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParsePresentedNumberUnscreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseAddressScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseNumberScreened(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseAddress(struct asn1_parm *pc, u_char *p, u_char *end, struct Address *address);
++int ParsePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PartyNumber *partyNumber);
++int ParsePublicPartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, struct PublicPartyNumber *publicPartyNumber);
++int ParsePrivatePartyNumber(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParsePublicTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *publicTypeOfNumber);
++int ParsePrivateTypeOfNumber(struct asn1_parm *pc, u_char *p, u_char *end, int *privateTypeOfNumber);
++int ParsePartySubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseUserSpecifiedSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseNSAPSubaddress(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseSubaddressInformation(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseScreeningIndicator(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseNumberDigits(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.c       2004-11-22 09:33:37.761803248 +0000
+@@ -0,0 +1,355 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++#include "asn1_generic.h"
++#if 0
++#include "asn1_address.h"
++#endif
++#include "asn1_aoc.h"
++
++// ======================================================================
++// AOC EN 300 182-1 V1.3.3
++
++#if 0
++// AOCDCurrency
++
++int
++ParseAOCDCurrency(struct Channel *chanp, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
++      XCHOICE(ParseAOCDCurrencyInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE_DEFAULT;
++}
++#endif
++// AOCDChargingUnit
++
++int
++ParseAOCDChargingUnit(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
++      XCHOICE(ParseAOCDChargingUnitInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE_DEFAULT;
++}
++
++#if 0
++// AOCECurrency
++
++int
++ParseAOCECurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
++      XCHOICE(ParseAOCECurrencyInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE_DEFAULT;
++}
++#endif
++
++// AOCEChargingUnit
++
++int
++ParseAOCEChargingUnit(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // chargeNotAvail
++      XCHOICE(ParseAOCEChargingUnitInfo, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE_DEFAULT;
++}
++
++#if 0
++// AOCDCurrencyInfo
++
++int
++ParseAOCDSpecificCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int typeOfChargingInfo;
++      int billingId;
++      INIT;
++
++      XSEQUENCE(ParseRecordedCurrency, ASN1_TAG_SEQUENCE, 1);
++      XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_ENUM, 2, &typeOfChargingInfo);
++      XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_ENUM, 3, &billingId);
++
++      return p - beg;
++}
++
++int
++ParseAOCDCurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseAOCDSpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge
++      XCHOICE_DEFAULT;
++}
++#endif
++
++// AOCDChargingUnitInfo
++
++int
++ParseAOCDSpecificChargingUnits(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int recordedUnits;
++      int typeOfChargingInfo;
++      int billingId;
++      INIT;
++
++      XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_SEQUENCE, 1, &recordedUnits);
++      XSEQUENCE_1(ParseTypeOfChargingInfo, ASN1_TAG_ENUM, 2, &typeOfChargingInfo);
++      XSEQUENCE_OPT_1(ParseAOCDBillingId, ASN1_TAG_ENUM, 3, &billingId);
++
++//    p_L3L4(pc, CC_CHARGE | INDICATION, &recordedUnits);
++
++      return p - beg;
++}
++
++int
++ParseAOCDChargingUnitInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseAOCDSpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge
++      XCHOICE_DEFAULT;
++}
++
++#if 0
++// RecordedCurrency
++
++int
++ParseRecordedCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      char currency[11];
++      INIT;
++
++      XSEQUENCE_1(ParseCurrency, ASN1_TAG_IA5_STRING, 1, currency);
++      XSEQUENCE(ParseAmount, ASN1_TAG_SEQUENCE, 2);
++
++      return p - beg;
++}
++#endif
++
++// RecordedUnitsList
++
++int
++ParseRecordedUnitsList(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits)
++{
++      int i;
++      INIT;
++
++      XSEQUENCE_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, recordedUnits);
++      for (i = 0; i < 31; i++) 
++              XSEQUENCE_OPT_1(ParseRecordedUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, recordedUnits);
++
++      return p - beg;
++}
++
++// TypeOfChargingInfo
++
++int
++ParseTypeOfChargingInfo(struct asn1_parm *pc, u_char *p, u_char *end, int *typeOfChargingInfo)
++{
++      return ParseEnum(pc, p, end, typeOfChargingInfo);
++}
++
++// RecordedUnits
++
++int
++ParseRecordedUnitsChoice(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits)
++{
++      INIT;
++
++      XCHOICE_1(ParseNumberOfUnits, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, recordedUnits);
++      XCHOICE(ParseNull, ASN1_TAG_NULL, ASN1_NOT_TAGGED); // not available
++      XCHOICE_DEFAULT;
++}
++
++int
++ParseRecordedUnits(struct asn1_parm *pc, u_char *p, u_char *end, int *recordedUnits)
++{
++      int typeOfUnit;
++      INIT;
++
++      XSEQUENCE_1(ParseRecordedUnitsChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, recordedUnits);
++      XSEQUENCE_OPT_1(ParseTypeOfUnit, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &typeOfUnit);
++
++      return p - beg;
++}
++
++// AOCDBillingId
++
++int
++ParseAOCDBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId)
++{
++      return ParseEnum(pc, p, end, billingId);
++}
++
++#if 0
++// AOCECurrencyInfo
++
++int
++ParseAOCESpecificCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int billingId;
++      INIT;
++
++      XSEQUENCE(ParseRecordedCurrency, ASN1_TAG_SEQUENCE, 1);
++      XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_ENUM, 2, &billingId);
++
++      return p - beg;
++}
++
++int
++ParseAOCECurrencyInfoChoice(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseAOCESpecificCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge
++      XCHOICE_DEFAULT;
++}
++
++int
++ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XSEQUENCE(ParseAOCECurrencyInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED);
++      XSEQUENCE_OPT(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED);
++      XCHOICE_DEFAULT;
++}
++#endif
++
++// AOCEChargingUnitInfo
++
++int
++ParseAOCESpecificChargingUnits(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int recordedUnits;
++      int billingId;
++      INIT;
++
++      XSEQUENCE_1(ParseRecordedUnitsList, ASN1_TAG_SEQUENCE, 1, &recordedUnits);
++      XSEQUENCE_OPT_1(ParseAOCEBillingId, ASN1_TAG_ENUM, 2, &billingId);
++
++//    p_L3L4(pc, CC_CHARGE | INDICATION, &recordedUnits);
++
++      return p - beg;
++}
++
++int
++ParseAOCEChargingUnitInfoChoice(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XCHOICE(ParseAOCESpecificChargingUnits, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      XCHOICE(ParseNull, ASN1_TAG_NULL, 1); // freeOfCharge
++      XCHOICE_DEFAULT;
++}
++
++int
++ParseAOCEChargingUnitInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      XSEQUENCE(ParseAOCEChargingUnitInfoChoice, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED);
++      XSEQUENCE_OPT(ParseChargingAssociation, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED);
++
++      return p - beg;
++}
++
++// AOCEBillingId
++
++int
++ParseAOCEBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId)
++{
++      return ParseEnum(pc, p, end, billingId);
++}
++
++#if 0
++// Currency
++
++int
++ParseCurrency(struct asn1_parm *pc, u_char *p, u_char *end, char *currency)
++{
++      return ParseIA5String(chanp, p, end, currency);
++}
++
++// Amount
++
++int
++ParseAmount(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int amount;
++      int multiplier;
++      INIT;
++
++      XSEQUENCE_1(ParseCurrencyAmount, ASN1_TAG_INTEGER, 1, &amount);
++      XSEQUENCE_1(ParseMultiplier, ASN1_TAG_INTEGER, 2, &multiplier);
++
++      return p - beg;
++}
++
++// CurrencyAmount
++
++int
++ParseCurrencyAmount(struct asn1_parm *pc, u_char *p, u_char *end, int *currencyAmount)
++{
++      return ParseInteger(chanp, p, end, currencyAmount);
++}
++
++// Multiplier
++
++int
++ParseMultiplier(struct asn1_parm *pc, u_char *p, u_char *end, int *multiplier)
++{
++      return ParseEnum(chanp, p, end, multiplier);
++}
++#endif
++
++// TypeOfUnit
++
++int
++ParseTypeOfUnit(struct asn1_parm *pc, u_char *p, u_char *end, int *typeOfUnit)
++{
++      return ParseInteger(pc, p, end, typeOfUnit);
++}
++
++// NumberOfUnits
++
++int
++ParseNumberOfUnits(struct asn1_parm *pc, u_char *p, u_char *end, int *numberOfUnits)
++{
++      return ParseInteger(pc, p, end, numberOfUnits);
++}
++
++// Charging Association
++
++int
++ParseChargingAssociation(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++//    char partyNumber[30];
++      INIT;
++
++//    XCHOICE_1(ParsePartyNumber, ASN1_TAG_SEQUENCE, 0, partyNumber);
++      XCHOICE(ParseChargeIdentifier, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED);
++      XCHOICE_DEFAULT;
++}
++
++// ChargeIdentifier
++
++int
++ParseChargeIdentifier(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int chargeIdentifier;
++
++      return ParseInteger(pc, p, end, &chargeIdentifier);
++}
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_aoc.h   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_aoc.h       2004-11-22 09:33:37.771801728 +0000
+@@ -0,0 +1,30 @@
++/* $Id$
++ *
++ */
++
++// ======================================================================
++// AOC EN 300 182-1 V1.3.3
++
++int ParseAOCDCurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy);
++int ParseAOCDChargingUnit(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseAOCECurrency(struct asn1_parm *pc, u_char *p, u_char *end, int dummy);
++int ParseAOCEChargingUnit(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseAOCDCurrencyInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseAOCDChargingUnitInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseRecordedCurrency(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseRecordedUnitsList(struct asn1_parm *pc,u_char *p, u_char *end, int *recordedUnits);
++int ParseTypeOfChargingInfo(struct asn1_parm *pc,u_char *p, u_char *end, int *typeOfChargingInfo);
++int ParseRecordedUnits(struct asn1_parm *pc,u_char *p, u_char *end, int *recordedUnits);
++int ParseAOCDBillingId(struct asn1_parm *pc, u_char *p, u_char *end, int *billingId);
++int ParseAOCECurrencyInfo(struct asn1_parm *pc, u_char *p, u_char *end, int dummy);
++int ParseAOCEChargingUnitInfo(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseAOCEBillingId(struct asn1_parm *pc,u_char *p, u_char *end, int *billingId);
++int ParseCurrency(struct asn1_parm *pc,u_char *p, u_char *end, char *currency);
++int ParseAmount(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseCurrencyAmount(struct asn1_parm *pc,u_char *p, u_char *end, int *currencyAmount);
++int ParseMultiplier(struct asn1_parm *pc,u_char *p, u_char *end, int *multiplier);
++int ParseTypeOfUnit(struct asn1_parm *pc,u_char *p, u_char *end, int *typeOfUnit);
++int ParseNumberOfUnits(struct asn1_parm *pc,u_char *p, u_char *end, int *numberOfUnits);
++int ParseChargingAssociation(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++int ParseChargeIdentifier(struct asn1_parm *pc,u_char *p, u_char *end, int dummy);
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.c 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.c     2004-11-22 09:33:37.781800208 +0000
+@@ -0,0 +1,16 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++#include "asn1_generic.h"
++#include "asn1_basic_service.h"
++
++// ======================================================================
++// Basic Service Elements EN 300 196-1 D.6
++
++int ParseBasicService(struct asn1_parm *pc, u_char *p, u_char *end, int *basicService)
++{
++      return ParseEnum(pc, p, end, basicService);
++}
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_basic_service.h 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_basic_service.h     2004-11-22 09:33:37.791798688 +0000
+@@ -0,0 +1,9 @@
++/* $Id$
++ *
++ */
++
++// ======================================================================
++// Basic Service Elements EN 300 196-1 D.6
++
++int ParseBasicService(struct asn1_parm *pc, u_char *p, u_char *end, int *basicService);
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.c      2004-11-22 09:33:37.802797016 +0000
+@@ -0,0 +1,151 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++#include "asn1_comp.h"
++#include "asn1_generic.h"
++#include "asn1_aoc.h"
++#include "asn1_diversion.h"
++
++// ======================================================================
++// Component EN 300 196-1 D.1
++
++int
++ParseInvokeId(struct asn1_parm *pc, u_char *p, u_char *end, int *invokeId)
++{
++      return ParseInteger(pc, p, end, invokeId);
++}
++
++int
++ParseErrorValue(struct asn1_parm *pc, u_char *p, u_char *end, int *errorValue)
++{
++      return ParseInteger(pc, p, end, errorValue);
++}
++
++int
++ParseOperationValue(struct asn1_parm *pc, u_char *p, u_char *end, int *operationValue)
++{
++      return ParseInteger(pc, p, end, operationValue);
++}
++
++int
++ParseInvokeComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int invokeId, operationValue;
++      INIT;
++
++      pc->comp = invoke;
++      XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId);
++//    XSEQUENCE_OPT(ParseLinkedId, ASN1_TAG_INTEGER, 0);
++      XSEQUENCE_1(ParseOperationValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &operationValue);
++      pc->u.inv.invokeId = invokeId;
++      pc->u.inv.operationValue = operationValue;
++      switch (operationValue) {
++#if 0
++      case 7:  XSEQUENCE(ParseARGActivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++      case 8:  XSEQUENCE(ParseARGDeactivationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++#endif
++      case 9:  XSEQUENCE_1(ParseARGActivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.actNot); break;
++      case 10: XSEQUENCE_1(ParseARGDeactivationStatusNotificationDiv, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &pc->u.inv.o.deactNot); break;
++#if 0
++      case 11: XSEQUENCE(ParseARGInterrogationDiversion, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++      case 12: XSEQUENCE(ParseARGDiversionInformation, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++      case 17: XSEQUENCE(ParseARGInterrogateServedUserNumbers, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++//    case 30: XSEQUENCE(ParseChargingRequest, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++//    case 31: XSEQUENCE(ParseAOCSCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++//    case 32: XSEQUENCE(ParseAOCSSpecialArr, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++      case 33: XSEQUENCE(ParseAOCDCurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++      case 34: XSEQUENCE(ParseAOCDChargingUnit, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++      case 35: XSEQUENCE(ParseAOCECurrency, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++      case 36: XSEQUENCE(ParseAOCEChargingUnit, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED); break;
++#endif
++      default:
++              return -1;
++      }
++
++      return p - beg;
++}
++
++int
++ParseReturnResultComponentSequence(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int operationValue;
++      INIT;
++
++      XSEQUENCE_1(ParseOperationValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &operationValue);
++      switch (operationValue) {
++      case 11: XSEQUENCE(ParseRESInterrogationDiversion, ASN1_TAG_SET, ASN1_NOT_TAGGED); break;
++      case 17: XSEQUENCE(ParseRESInterrogateServedUserNumbers, ASN1_TAG_SET, ASN1_NOT_TAGGED); break;
++      default: return -1;
++      }
++              
++      return p - beg;
++}
++
++int
++ParseReturnResultComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int invokeId;
++      INIT;
++
++      pc->comp = returnResult;
++      XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId);
++      XSEQUENCE_OPT(ParseReturnResultComponentSequence, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED);
++      pc->u.retResult.invokeId = invokeId;
++
++      return p - beg;
++}
++
++int
++ParseReturnErrorComponent(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++        int invokeId;
++        int errorValue;
++        char error[80];
++        INIT;
++
++      pc->comp = returnError;
++
++      XSEQUENCE_1(ParseInvokeId, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &invokeId);
++      XSEQUENCE_1(ParseErrorValue, ASN1_TAG_INTEGER, ASN1_NOT_TAGGED, &errorValue);
++
++      pc->u.retError.invokeId = invokeId;
++      pc->u.retError.errorValue = errorValue;
++
++      switch (errorValue) {
++              case 0: sprintf(error, "not subscribed"); break;
++              case 3: sprintf(error, "not available"); break;
++              case 4: sprintf(error, "not implemented"); break;
++              case 6: sprintf(error, "invalid served user nr"); break;
++              case 7: sprintf(error, "invalid call state"); break;
++              case 8: sprintf(error, "basic service not provided"); break;
++              case 9: sprintf(error, "not incoming call"); break;
++              case 10: sprintf(error, "supplementary service interaction not allowed"); break;
++              case 11: sprintf(error, "resource unavailable"); break;
++              case 12: sprintf(error, "invalid diverted-to nr"); break;
++              case 14: sprintf(error, "special service nr"); break;
++              case 15: sprintf(error, "diversion to served user nr"); break;
++              case 23: sprintf(error, "incoming call accepted"); break;
++              case 24: sprintf(error, "number of diversions exceeded"); break;
++              case 46: sprintf(error, "not activated"); break;
++              case 48: sprintf(error, "request already accepted"); break;
++              default: sprintf(error, "(%d)", errorValue); break;
++      }
++      print_asn1msg(PRT_DEBUG_DECODE, "ReturnError: %s\n", error);
++
++      return p - beg;
++}
++
++int
++ParseComponent(struct asn1_parm *pc, u_char *p, u_char *end)
++{
++        INIT;
++
++      XCHOICE(ParseInvokeComponent, ASN1_TAG_SEQUENCE, 1);
++      XCHOICE(ParseReturnResultComponent, ASN1_TAG_SEQUENCE, 2);
++      XCHOICE(ParseReturnErrorComponent, ASN1_TAG_SEQUENCE, 3);
++//    XCHOICE(ParseRejectComponent, ASN1_TAG_SEQUENCE, 4);
++      XCHOICE_DEFAULT;
++}
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_comp.h  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_comp.h      2004-11-22 09:33:37.812795496 +0000
+@@ -0,0 +1,13 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++
++int ParseInvokeId(struct asn1_parm *parm, u_char *p, u_char *end, int *invokeId);
++int ParseOperationValue(struct asn1_parm *parm, u_char *p, u_char *end, int *operationValue);
++int ParseInvokeComponent(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseReturnResultComponent(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseComponent(struct asn1_parm *parm, u_char *p, u_char *end);
++int XParseComponent(struct asn1_parm *parm, u_char *p, u_char *end);
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.c 2004-11-22 09:33:37.822793976 +0000
+@@ -0,0 +1,248 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++#include "asn1_generic.h"
++#include "asn1_address.h"
++#include "asn1_basic_service.h"
++#include "asn1_diversion.h"
++
++// ======================================================================
++// Diversion Supplementary Services ETS 300 207-1 Table 3
++
++#if 0
++int
++ParseARGActivationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int procedure, basicService;
++      struct ServedUserNr servedUserNr;
++      struct Address address;
++      INIT;
++
++      XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure);
++      XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService);
++      XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &address);
++      XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr);
++
++      return p - beg;
++}
++
++int
++ParseARGDeactivationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int procedure, basicService;
++      struct ServedUserNr servedUserNr;
++      INIT;
++
++      XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure);
++      XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService);
++      XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr);
++
++      print_asn1msg(PRT_SHOWNUMBERS, "Deactivation Diversion %d (%d), \n",
++                procedure, basicService);
++      return p - beg;
++}
++#endif
++
++int
++ParseARGActivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct ActDivNotification *actNot)
++{
++      INIT;
++
++      XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &actNot->procedure);
++      XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &actNot->basicService);
++      XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &actNot->address);
++      XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &actNot->servedUserNr);
++
++      return p - beg;
++}
++
++int
++ParseARGDeactivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct DeactDivNotification *deactNot)
++{
++      INIT;
++
++      XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &deactNot->procedure);
++      XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &deactNot->basicService);
++      XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &deactNot->servedUserNr);
++
++      return p - beg;
++}
++
++#if 0
++int 
++ParseARGInterrogationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int procedure, basicService;
++      struct ServedUserNr servedUserNr;
++      INIT;
++
++      XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &procedure);
++      XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService);
++      XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr);
++
++      print_asn1msg(PRT_SHOWNUMBERS, "Interrogation Diversion %d (%d), \n",
++              procedure, basicService);
++      return p - beg;
++}
++#endif
++
++int 
++ParseRESInterrogationDiversion(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      print_asn1msg(PRT_SHOWNUMBERS, "Interrogation Diversion Result\n");
++      return ParseIntResultList(pc, p,  end, &pc->u.retResult.o.resultList);
++}
++
++#if 0
++int 
++ParseARGInterrogateServedUserNumbers(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      print_asn1msg(PRT_SHOWNUMBERS, "Interrogate Served User Numbers\n");
++      return 0;
++}
++#endif
++
++int 
++ParseRESInterrogateServedUserNumbers(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      int ret;
++
++      ret = ParseServedUserNumberList(pc, p, end, &pc->u.retResult.o.list);
++      if (ret < 0)
++              return ret;
++
++      print_asn1msg(PRT_SHOWNUMBERS, "Interrogate Served User Numbers:\n");
++      
++      return ret;
++}
++
++int 
++ParseARGDiversionInformation(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      char diversionReason[20];
++      int basicService;
++      char servedUserSubaddress[30];
++      char callingAddress[80];
++      char originalCalledNr[80];
++      char lastDivertingNr[80];
++      char lastDivertingReason[20];
++      INIT;
++      
++      servedUserSubaddress[0] = 0;
++      callingAddress[0] = 0;
++      originalCalledNr[0] = 0;
++      lastDivertingNr[0] = 0;
++      lastDivertingReason[0] = 0;
++
++      XSEQUENCE_1(ParseDiversionReason, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, diversionReason);
++      XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &basicService);
++      XSEQUENCE_OPT_1(ParsePartySubaddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, servedUserSubaddress);
++      XSEQUENCE_OPT_1(ParsePresentedAddressScreened, ASN1_NOT_TAGGED, 0 | ASN1_TAG_EXPLICIT, callingAddress);
++      XSEQUENCE_OPT_1(ParsePresentedNumberUnscreened, ASN1_NOT_TAGGED, 1 | ASN1_TAG_EXPLICIT, originalCalledNr);
++      XSEQUENCE_OPT_1(ParsePresentedNumberUnscreened, ASN1_NOT_TAGGED, 2 | ASN1_TAG_EXPLICIT, lastDivertingNr);
++      XSEQUENCE_OPT_1(ParseDiversionReason, ASN1_TAG_ENUM, 3 | ASN1_TAG_EXPLICIT, lastDivertingReason);
++//    XSEQUENCE_OPT_1(ParseQ931InformationElement, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, userInfo);
++      print_asn1msg(PRT_SHOWNUMBERS, "Diversion Information %s(%d) %s\n"
++                "  callingAddress %s originalCalled Nr %s\n"
++                "  lastDivertingNr %s lastDiverting Reason %s\n",
++                diversionReason, basicService, servedUserSubaddress, callingAddress,
++                originalCalledNr, lastDivertingNr, lastDivertingReason);
++      return p - beg;
++}
++
++int 
++ParseIntResultList(struct asn1_parm *pc, u_char *p, u_char *end, struct IntResultList *intResultList)
++{
++      int i;
++      INIT;
++
++      for (i = 0; i < 10; i++) {
++              intResultList->intResult[i].basicService = -1;
++              XSEQUENCE_OPT_1(ParseIntResult, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, 
++                              &intResultList->intResult[i] );
++      }
++
++      return p - beg;
++}
++
++int 
++ParseIntResult(struct asn1_parm *pc, u_char *p, u_char *end, struct IntResult *intResult)
++{
++      INIT;
++
++      XSEQUENCE_1(ParseServedUserNr, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &intResult->servedUserNr);
++      XSEQUENCE_1(ParseBasicService, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &intResult->basicService);
++      XSEQUENCE_1(ParseProcedure, ASN1_TAG_ENUM, ASN1_NOT_TAGGED, &intResult->procedure);
++      XSEQUENCE_1(ParseAddress, ASN1_TAG_SEQUENCE, ASN1_NOT_TAGGED, &intResult->address);
++
++      return p - beg;
++}
++
++int
++ParseServedUserNrAll(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNr *servedUserNr)
++{
++      int ret;
++
++      ret = ParseNull(pc, p, end, 0);
++      if (ret < 0)
++              return ret;
++      servedUserNr->all = 1;
++
++      return ret;
++}
++
++int
++ParseServedUserNr(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNr *servedUserNr)
++{
++      INIT;
++
++      servedUserNr->all = 0;
++      XCHOICE_1(ParseServedUserNrAll, ASN1_TAG_NULL, ASN1_NOT_TAGGED, servedUserNr);
++      XCHOICE_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &servedUserNr->partyNumber);
++      XCHOICE_DEFAULT;
++}
++
++int
++ParseProcedure(struct asn1_parm *pc, u_char *p, u_char *end, int *procedure)
++{
++      return ParseEnum(pc, p, end, procedure);
++}
++
++int ParseServedUserNumberList(struct asn1_parm *pc, u_char *p, u_char *end, struct ServedUserNumberList *list)
++{
++      int i;
++      INIT;
++
++      for (i = 0; i < 10; i++) {
++              list->partyNumber[i].type = -1;
++              XSEQUENCE_OPT_1(ParsePartyNumber, ASN1_NOT_TAGGED, ASN1_NOT_TAGGED, &list->partyNumber[i]);
++      }
++
++      return p - beg;
++}
++
++int
++ParseDiversionReason(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      int ret;
++      int diversionReason;
++
++      ret = ParseEnum(pc, p, end, &diversionReason);
++      if (ret < 0)
++              return ret;
++      
++      switch (diversionReason) {
++      case 0: sprintf(str, "unknown"); break;
++      case 1: sprintf(str, "CFU"); break;
++      case 2: sprintf(str, "CFB"); break;
++      case 3: sprintf(str, "CFNR"); break;
++      case 4: sprintf(str, "CD (Alerting)"); break;
++      case 5: sprintf(str, "CD (Immediate)"); break;
++      default: sprintf(str, "(%d)", diversionReason); break;
++      }
++
++      return ret;
++}
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_diversion.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_diversion.h 2004-11-22 09:33:37.832792456 +0000
+@@ -0,0 +1,22 @@
++/* $Id$
++ *
++ */
++
++#if 0
++int ParseARGActivationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseARGDeactivationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++#endif
++int ParseARGActivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct ActDivNotification *actNot);
++int ParseARGDeactivationStatusNotificationDiv(struct asn1_parm *pc, u_char *p, u_char *end, struct DeactDivNotification *deactNot);
++int ParseARGInterrogationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseRESInterrogationDiversion(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseARGInterrogateServedUserNumbers(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseRESInterrogateServedUserNumbers(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseARGDiversionInformation(struct asn1_parm *parm, u_char *p, u_char *end, int dummy);
++int ParseIntResult(struct asn1_parm *parm, u_char *p, u_char *end, struct IntResult *intResult);
++int ParseIntResultList(struct asn1_parm *parm, u_char *p, u_char *end, struct IntResultList *intResultList);
++int ParseServedUserNr(struct asn1_parm *parm, u_char *p, u_char *end, struct ServedUserNr *servedUserNr);
++int ParseProcedure(struct asn1_parm *pc, u_char *p, u_char *end, int *procedure);
++int ParseServedUserNumberList(struct asn1_parm *parm, u_char *p, u_char *end, struct ServedUserNumberList *list);
++int ParseDiversionReason(struct asn1_parm *parm, u_char *p, u_char *end, char *str);
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.c       2004-11-22 09:33:37.842790936 +0000
+@@ -0,0 +1,178 @@
++/* $Id$
++ *
++ */
++
++#include "m_capi.h"
++#include "helper.h"
++#include "asn1_enc.h"
++
++int encodeNull(__u8 *dest)
++{
++      dest[0] = 0x05;  // null
++      dest[1] = 0;     // length
++      return 2;
++}
++
++int encodeInt(__u8 *dest, __u32 i)
++{
++      __u8 *p;
++
++      dest[0] = 0x02;  // integer
++      dest[1] = 0;     // length
++      p = &dest[2];
++      do {
++              *p++ = i;
++              i >>= 8;
++      } while (i);
++
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
++int encodeEnum(__u8 *dest, __u32 i)
++{
++      __u8 *p;
++
++      dest[0] = 0x0a;  // integer
++      dest[1] = 0;     // length
++      p = &dest[2];
++      do {
++              *p++ = i;
++              i >>= 8;
++      } while (i);
++
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
++int encodeNumberDigits(__u8 *dest, __u8 *nd, __u8 len)
++{
++      __u8 *p;
++      int i;
++
++      dest[0] = 0x12;    // numeric string
++      dest[1] = 0x0;     // length
++      p = &dest[2];
++      for (i = 0; i < len; i++)
++              *p++ = *nd++;
++
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
++int encodePublicPartyNumber(__u8 *dest, __u8 *facilityPartyNumber)
++{
++      __u8 *p;
++
++      dest[0] = 0x20;  // sequence
++      dest[1] = 0;     // length
++      p = &dest[2];
++      p += encodeEnum(p, (facilityPartyNumber[2] & 0x70) >> 4);
++      p += encodeNumberDigits(p, &facilityPartyNumber[4], facilityPartyNumber[0] - 3);
++
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
++int encodePartyNumber(__u8 *dest, __u8 *facilityPartyNumber)
++{
++      __u8 *p = dest;
++
++      p = dest;
++      switch (facilityPartyNumber[1]) {
++      case 0: // unknown
++              p += encodeNumberDigits(p, &facilityPartyNumber[4], facilityPartyNumber[0] - 3);
++              dest[0] &= 0x20;
++              dest[0] |= 0x81;
++              break;
++      case 1: // publicPartyNumber
++              p += encodePublicPartyNumber(p, facilityPartyNumber);
++              dest[0] &= 0x20;
++              dest[0] |= 0x81;
++              break;
++      default:
++              int_error();
++              return -1;
++      }
++      return p - dest;
++}
++
++int encodeServedUserNumber(__u8 *dest, __u8 *servedUserNumber)
++{
++      if (servedUserNumber[0])
++              return encodePartyNumber(dest, servedUserNumber);
++        else
++              return encodeNull(dest);
++}
++
++int encodeAddress(__u8 *dest, __u8 *facilityPartyNumber, __u8 *calledPartySubaddress)
++{
++      __u8 *p = dest;
++
++      dest[0] = 0x30;  // invoke id tag, integer
++      dest[1] = 0;     // length
++      p = &dest[2];
++
++      p += encodePartyNumber(p, facilityPartyNumber);
++#if 0 // FIXME
++      if (calledPartySubaddress[0])
++              p += encodePartySubaddress(p, calledPartySubaddress);
++#endif
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
++int encodeActivationDiversion(__u8 *dest, struct FacReqCFActivate *CFActivate)
++{
++      __u8 *p;
++
++      dest[0] = 0x30;  // sequence
++      dest[1] = 0;     // length
++      p = &dest[2];
++
++      p += encodeEnum(p, CFActivate->Procedure);
++      p += encodeEnum(p, CFActivate->BasicService);
++      p += encodeAddress(p, CFActivate->ForwardedToNumber, CFActivate->ForwardedToSubaddress);
++      p += encodeServedUserNumber(p, CFActivate->ServedUserNumber);
++
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
++int encodeDeactivationDiversion(__u8 *dest, struct FacReqCFDeactivate *CFDeactivate)
++{
++      __u8 *p;
++
++      dest[0] = 0x30;  // sequence
++      dest[1] = 0;     // length
++      p = &dest[2];
++
++      p += encodeEnum(p, CFDeactivate->Procedure);
++      p += encodeEnum(p, CFDeactivate->BasicService);
++      p += encodeServedUserNumber(p, CFDeactivate->ServedUserNumber);
++
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
++int encodeInterrogationDiversion(__u8 *dest, struct FacReqCFInterrogateParameters *params)
++{
++      __u8 *p;
++
++      dest[0] = 0x30;  // sequence
++      dest[1] = 0;     // length
++      p = &dest[2];
++
++      p += encodeEnum(p, params->Procedure);
++#if 0
++      if (basicService == 0)
++              p += encodeNull(p);
++      else
++#endif
++      p += encodeEnum(p, params->BasicService);
++      p += encodeServedUserNumber(p, params->ServedUserNumber);
++
++      dest[1] = p - &dest[2];
++      return p - dest;
++}
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_enc.h   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_enc.h       2004-11-22 09:33:37.852789416 +0000
+@@ -0,0 +1,17 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++
++int encodeNull(__u8 *dest);
++int encodeInt(__u8 *dest, __u32 i);
++int encodeEnum(__u8 *dest, __u32 i);
++int encodeNumberDigits(__u8 *dest, __u8 *nd, __u8 len);
++int encodePublicPartyNumber(__u8 *dest, __u8 *facilityPartyNumber);
++int encodePartyNumber(__u8 *dest, __u8 *facilityPartyNumber);
++int encodeServedUserNumber(__u8 *dest, __u8 *servedUserNumber);
++int encodeAddress(__u8 *dest, __u8 *facilityPartyNumber, __u8 *calledPartySubaddress);
++int encodeActivationDiversion(__u8 *dest, struct FacReqCFActivate *CFActivate);
++int encodeDeactivationDiversion(__u8 *dest,struct FacReqCFDeactivate *CFDeactivate);
++int encodeInterrogationDiversion(__u8 *dest, struct FacReqCFInterrogateParameters *params);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.c   2004-11-22 09:33:37.862787896 +0000
+@@ -0,0 +1,115 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++#include "asn1_generic.h"
++
++// ======================================================================
++// general ASN.1
++
++int
++ParseBoolean(struct asn1_parm *pc, u_char *p, u_char *end, int *i)
++{
++      INIT;
++
++      *i = 0;
++      while (len--) {
++              CHECK_P;
++              *i = (*i >> 8) + *p;
++              p++;
++      }
++      print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> BOOL = %d %#x\n", *i, *i);
++      return p - beg;
++}
++
++int
++ParseNull(struct asn1_parm *pc, u_char *p, u_char *end, int dummy)
++{
++      INIT;
++
++      return p - beg;
++}
++
++int
++ParseInteger(struct asn1_parm *pc, u_char *p, u_char *end, int *i)
++{
++      INIT;
++
++      *i = 0;
++      while (len--) {
++              CHECK_P;
++              *i = (*i << 8) + *p;
++              p++;
++      }
++      print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> INT = %d %#x\n", *i, *i);
++      return p - beg;
++}
++
++int
++ParseEnum(struct asn1_parm *pc, u_char *p, u_char *end, int *i)
++{
++      INIT;
++
++      *i = 0;
++      while (len--) {
++              CHECK_P;
++              *i = (*i << 8) + *p;
++              p++;
++      }
++      print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> ENUM = %d %#x\n", *i, *i);
++      return p - beg;
++}
++
++#if 0
++int
++ParseIA5String(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      INIT;
++
++      print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> IA5 = ");
++      while (len--) {
++              CHECK_P;
++              print_asn1msg(PRT_DEBUG_DECODE, "%c", *p);
++              *str++ = *p;
++              p++;
++      }
++      print_asn1msg(PRT_DEBUG_DECODE, "\n");
++      *str = 0;
++      return p - beg;
++}
++#endif
++
++int
++ParseNumericString(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      INIT;
++
++      print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> NumStr = ");
++      while (len--) {
++              CHECK_P;
++              print_asn1msg(PRT_DEBUG_DECODE, "%c", *p);
++              *str++ = *p;
++              p++;
++      }
++      print_asn1msg(PRT_DEBUG_DECODE, "\n");
++      *str = 0;
++      return p - beg;
++}
++
++int
++ParseOctetString(struct asn1_parm *pc, u_char *p, u_char *end, char *str)
++{
++      INIT;
++
++      print_asn1msg(PRT_DEBUG_DECODE, " DEBUG> Octets = ");
++      while (len--) {
++              CHECK_P;
++              print_asn1msg(PRT_DEBUG_DECODE, " %02x", *p);
++              *str++ = *p;
++              p++;
++      }
++      print_asn1msg(PRT_DEBUG_DECODE, "\n");
++      *str = 0;
++      return p - beg;
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/asn1_generic.h       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/asn1_generic.h   2004-11-22 09:33:37.872786376 +0000
+@@ -0,0 +1,17 @@
++/* $Id$
++ *
++ */
++
++#include "asn1.h"
++
++// ======================================================================
++// general ASN.1
++
++int ParseBoolean(struct asn1_parm *pc, u_char *p, u_char *end, int *i);
++int ParseNull(struct asn1_parm *pc, u_char *p, u_char *end, int dummy);
++int ParseInteger(struct asn1_parm *pc, u_char *p, u_char *end, int *i);
++int ParseEnum(struct asn1_parm *pc, u_char *p, u_char *end, int *i);
++int ParseIA5String(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseNumericString(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++int ParseOctetString(struct asn1_parm *pc, u_char *p, u_char *end, char *str);
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/avm_fritz.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/avm_fritz.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/avm_fritz.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/avm_fritz.c      2004-11-22 09:33:37.882784856 +0000
+@@ -0,0 +1,1525 @@
++/* $Id$
++ *
++ * fritz_pci.c    low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
++ *              Thanks to AVM, Berlin for informations
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#ifdef NEW_ISAPNP
++#include <linux/pnp.h>
++#else
++#include <linux/isapnp.h>
++#endif
++#include <linux/delay.h>
++#include "dchannel.h"
++#include "bchannel.h"
++#include "isac.h"
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++
++#define SPIN_DEBUG
++#define LOCK_STATISTIC
++#include "hw_lock.h"
++
++static const char *avm_fritz_rev = "$Revision$";
++
++enum {
++      AVM_FRITZ_PCI,
++      AVM_FRITZ_PNP,
++      AVM_FRITZ_PCIV2,
++};
++
++#ifndef PCI_VENDOR_ID_AVM
++#define PCI_VENDOR_ID_AVM     0x1244
++#endif
++#ifndef PCI_DEVICE_ID_AVM_FRITZ
++#define PCI_DEVICE_ID_AVM_FRITZ       0xa00
++#endif
++#ifndef PCI_DEVICE_ID_AVM_A1_V2
++#define PCI_DEVICE_ID_AVM_A1_V2       0xe00
++#endif
++
++#define HDLC_FIFO             0x0
++#define HDLC_STATUS           0x4
++#define CHIP_WINDOW           0x10
++
++#define CHIP_INDEX            0x4
++#define AVM_HDLC_1            0x00
++#define AVM_HDLC_2            0x01
++#define AVM_ISAC_FIFO         0x02
++#define AVM_ISAC_REG_LOW      0x04
++#define AVM_ISAC_REG_HIGH     0x06
++
++#define AVM_STATUS0_IRQ_ISAC  0x01
++#define AVM_STATUS0_IRQ_HDLC  0x02
++#define AVM_STATUS0_IRQ_TIMER 0x04
++#define AVM_STATUS0_IRQ_MASK  0x07
++
++#define AVM_STATUS0_RESET     0x01
++#define AVM_STATUS0_DIS_TIMER 0x02
++#define AVM_STATUS0_RES_TIMER 0x04
++#define AVM_STATUS0_ENA_IRQ   0x08
++#define AVM_STATUS0_TESTBIT   0x10
++
++#define AVM_STATUS1_INT_SEL   0x0f
++#define AVM_STATUS1_ENA_IOM   0x80
++
++#define HDLC_MODE_ITF_FLG     0x01
++#define HDLC_MODE_TRANS       0x02
++#define HDLC_MODE_CCR_7       0x04
++#define HDLC_MODE_CCR_16      0x08
++#define HDLC_MODE_TESTLOOP    0x80
++
++#define HDLC_INT_XPR          0x80
++#define HDLC_INT_XDU          0x40
++#define HDLC_INT_RPR          0x20
++#define HDLC_INT_MASK         0xE0
++
++#define HDLC_STAT_RME         0x01
++#define HDLC_STAT_RDO         0x10
++#define HDLC_STAT_CRCVFRRAB   0x0E
++#define HDLC_STAT_CRCVFR      0x06
++#define HDLC_STAT_RML_MASK    0x3f00
++
++#define HDLC_CMD_XRS          0x80
++#define HDLC_CMD_XME          0x01
++#define HDLC_CMD_RRS          0x20
++#define HDLC_CMD_XML_MASK     0x3f00
++
++/* Fritz PCI v2.0 */
++
++#define  AVM_HDLC_FIFO_1        0x10
++#define  AVM_HDLC_FIFO_2        0x18
++
++#define  AVM_HDLC_STATUS_1      0x14
++#define  AVM_HDLC_STATUS_2      0x1c
++
++#define  AVM_ISACSX_INDEX       0x04
++#define  AVM_ISACSX_DATA        0x08
++
++/* data struct */
++
++struct hdlc_stat_reg {
++#ifdef __BIG_ENDIAN
++      u_char fill __attribute__((packed));
++      u_char mode __attribute__((packed));
++      u_char xml  __attribute__((packed));
++      u_char cmd  __attribute__((packed));
++#else
++      u_char cmd  __attribute__((packed));
++      u_char xml  __attribute__((packed));
++      u_char mode __attribute__((packed));
++      u_char fill __attribute__((packed));
++#endif
++};
++
++typedef struct hdlc_hw {
++      union {
++              u_int ctrl;
++              struct hdlc_stat_reg sr;
++      } ctrl;
++      u_int stat;
++} hdlc_hw_t;
++
++
++typedef struct _fritzpnppci {
++      struct list_head        list;
++      void                    *pdev;
++      u_int                   type;
++      u_int                   irq;
++      u_int                   irqcnt;
++      u_int                   addr;
++      mISDN_HWlock_t          lock;
++      isac_chip_t             isac;
++      hdlc_hw_t               hdlc[2];
++      dchannel_t              dch;
++      bchannel_t              bch[2];
++      u_char                  ctrlreg;
++} fritzpnppci;
++
++
++static int lock_dev(void *data, int nowait)
++{
++      register mISDN_HWlock_t *lock = &((fritzpnppci *)data)->lock;
++
++      return(lock_HW(lock, nowait));
++} 
++
++static void unlock_dev(void *data)
++{
++      register mISDN_HWlock_t *lock = &((fritzpnppci *)data)->lock;
++
++      unlock_HW(lock);
++}
++
++/* Interface functions */
++
++static u_char
++ReadISAC(void *fc, u_char offset)
++{
++      register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
++      register long addr = ((fritzpnppci *)fc)->addr;
++      register u_char val;
++
++      outb(idx, addr + CHIP_INDEX);
++      val = inb(addr + CHIP_WINDOW + (offset & 0xf));
++      return (val);
++}
++
++static void
++WriteISAC(void *fc, u_char offset, u_char value)
++{
++      register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
++      register long addr = ((fritzpnppci *)fc)->addr;
++
++      outb(idx, addr + CHIP_INDEX);
++      outb(value, addr + CHIP_WINDOW + (offset & 0xf));
++}
++
++static void
++ReadISACfifo(void *fc, u_char * data, int size)
++{
++      register long addr = ((fritzpnppci *)fc)->addr;
++
++      outb(AVM_ISAC_FIFO, addr + CHIP_INDEX);
++      insb(addr + CHIP_WINDOW, data, size);
++}
++
++static void
++WriteISACfifo(void *fc, u_char * data, int size)
++{
++      register long addr = ((fritzpnppci *)fc)->addr;
++
++      outb(AVM_ISAC_FIFO, addr + CHIP_INDEX);
++      outsb(addr + CHIP_WINDOW, data, size);
++}
++
++static unsigned char
++fcpci2_read_isac(void *fc, unsigned char offset)
++{
++      register long addr = ((fritzpnppci *)fc)->addr;
++      unsigned char val;
++
++      outl(offset, addr + AVM_ISACSX_INDEX);
++      val = inl(addr + AVM_ISACSX_DATA);
++      return val;
++}
++
++static void
++fcpci2_write_isac(void *fc, unsigned char offset, unsigned char value)
++{
++      register long addr = ((fritzpnppci *)fc)->addr;
++
++      outl(offset, addr + AVM_ISACSX_INDEX);
++      outl(value, addr + AVM_ISACSX_DATA);
++}
++
++static void
++fcpci2_read_isac_fifo(void *fc, unsigned char * data, int size)
++{
++      register long addr = ((fritzpnppci *)fc)->addr;
++      int i;
++
++      outl(0, addr + AVM_ISACSX_INDEX);
++      for (i = 0; i < size; i++)
++              data[i] = inl(addr + AVM_ISACSX_DATA);
++}
++
++static void
++fcpci2_write_isac_fifo(void *fc, unsigned char * data, int size)
++{
++      register long addr = ((fritzpnppci *)fc)->addr;
++      int i;
++
++      outl(0, addr + AVM_ISACSX_INDEX);
++      for (i = 0; i < size; i++)
++              outl(data[i], addr + AVM_ISACSX_DATA);
++}
++
++static inline
++bchannel_t *Sel_BCS(fritzpnppci *fc, int channel)
++{
++      if (fc->bch[0].protocol && (fc->bch[0].channel == channel))
++              return(&fc->bch[0]);
++      else if (fc->bch[1].protocol && (fc->bch[1].channel == channel))
++              return(&fc->bch[1]);
++      else
++              return(NULL);
++}
++
++static inline void
++__write_ctrl_pnp(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel, int which) {
++      register u_char idx = channel ? AVM_HDLC_2 : AVM_HDLC_1;
++
++      outb(idx, fc->addr + CHIP_INDEX);
++      if (which & 4)
++              outb(hdlc->ctrl.sr.mode, fc->addr + CHIP_WINDOW + HDLC_STATUS + 2);
++      if (which & 2)
++              outb(hdlc->ctrl.sr.xml, fc->addr + CHIP_WINDOW + HDLC_STATUS + 1);
++      if (which & 1)
++              outb(hdlc->ctrl.sr.cmd, fc->addr + CHIP_WINDOW + HDLC_STATUS);
++}
++
++static inline void
++__write_ctrl_pci(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel) {
++      register u_int idx = channel ? AVM_HDLC_2 : AVM_HDLC_1;
++
++      outl(idx, fc->addr + CHIP_INDEX);
++      outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS);
++}
++
++static inline void
++__write_ctrl_pciv2(fritzpnppci *fc, hdlc_hw_t *hdlc, int channel) {
++      outl(hdlc->ctrl.ctrl, fc->addr + (channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1));
++}
++
++void
++write_ctrl(bchannel_t *bch, int which) {
++      fritzpnppci     *fc = bch->inst.data;
++      hdlc_hw_t       *hdlc = bch->hw;
++
++      if (fc->dch.debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "hdlc %c wr%x ctrl %x",
++                      'A' + bch->channel, which, hdlc->ctrl.ctrl);
++      switch(fc->type) {
++              case AVM_FRITZ_PCIV2:
++                      __write_ctrl_pciv2(fc, hdlc, bch->channel);
++                      break;
++              case AVM_FRITZ_PCI:
++                      __write_ctrl_pci(fc, hdlc, bch->channel);
++                      break;
++              case AVM_FRITZ_PNP:
++                      __write_ctrl_pnp(fc, hdlc, bch->channel, which);
++                      break;
++      }
++}
++
++
++static inline u_int
++__read_status_pnp(u_long addr, u_int channel)
++{
++      register u_int stat;
++
++      outb(channel ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX);
++      stat = inb(addr + CHIP_WINDOW + HDLC_STATUS);
++      if (stat & HDLC_INT_RPR)
++              stat |= (inb(addr + CHIP_WINDOW + HDLC_STATUS + 1)) << 8;
++      return (stat);
++}
++
++static inline u_int
++__read_status_pci(u_long addr, u_int channel)
++{
++      outl(channel ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX);
++      return inl(addr + CHIP_WINDOW + HDLC_STATUS);
++}
++
++static inline u_int
++__read_status_pciv2(u_long addr, u_int channel)
++{
++      return inl(addr + (channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1));
++}
++
++
++static u_int
++read_status(fritzpnppci *fc, int channel)
++{
++      switch(fc->type) {
++              case AVM_FRITZ_PCIV2:
++                      return(__read_status_pciv2(fc->addr, channel));
++              case AVM_FRITZ_PCI:
++                      return(__read_status_pci(fc->addr, channel));
++              case AVM_FRITZ_PNP:
++                      return(__read_status_pnp(fc->addr, channel));
++      }
++      /* dummy */
++      return(0);
++}
++
++static int
++modehdlc(bchannel_t *bch, int bc, int protocol)
++{
++      hdlc_hw_t       *hdlc = bch->hw;
++
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "hdlc %c protocol %x-->%x ch %d-->%d",
++                      'A' + bch->channel, bch->protocol, protocol, bch->channel, bc);
++      if ((protocol != -1) && (bc != bch->channel))
++              printk(KERN_WARNING "%s: fritzcard mismatch channel(%d/%d)\n", __FUNCTION__, bch->channel, bc);
++      hdlc->ctrl.ctrl = 0;
++      switch (protocol) {
++              case (-1): /* used for init */
++                      bch->protocol = -1;
++                      bch->channel = bc;
++              case (ISDN_PID_NONE):
++                      if (bch->protocol == ISDN_PID_NONE)
++                              break;
++                      hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
++                      hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
++                      write_ctrl(bch, 5);
++                      bch->protocol = ISDN_PID_NONE;
++                      break;
++              case (ISDN_PID_L1_B_64TRANS):
++                      bch->protocol = protocol;
++                      hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
++                      hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
++                      write_ctrl(bch, 5);
++                      hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
++                      write_ctrl(bch, 1);
++                      hdlc->ctrl.sr.cmd = 0;
++                      bch_sched_event(bch, B_XMTBUFREADY);
++                      break;
++              case (ISDN_PID_L1_B_64HDLC):
++                      bch->protocol = protocol;
++                      hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
++                      hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
++                      write_ctrl(bch, 5);
++                      hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
++                      write_ctrl(bch, 1);
++                      hdlc->ctrl.sr.cmd = 0;
++                      bch_sched_event(bch, B_XMTBUFREADY);
++                      break;
++              default:
++                      mISDN_debugprint(&bch->inst, "prot not known %x", protocol);
++                      return(-ENOPROTOOPT);
++      }
++      return(0);
++}
++
++static void
++hdlc_empty_fifo(bchannel_t *bch, int count)
++{
++      register u_int *ptr;
++      u_char *p;
++      u_char idx = bch->channel ? AVM_HDLC_2 : AVM_HDLC_1;
++      int cnt=0;
++      fritzpnppci *fc = bch->inst.data;
++
++      if ((fc->dch.debug & L1_DEB_HSCX) && !(fc->dch.debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "hdlc_empty_fifo %d", count);
++      if (bch->rx_idx + count > MAX_DATA_MEM) {
++              if (fc->dch.debug & L1_DEB_WARN)
++                      mISDN_debugprint(&bch->inst, "hdlc_empty_fifo: incoming packet too large");
++              return;
++      }
++      p = bch->rx_buf + bch->rx_idx;
++      ptr = (u_int *)p;
++      bch->rx_idx += count;
++      if (fc->type == AVM_FRITZ_PCIV2) {
++              while (cnt < count) {
++#ifdef __powerpc__
++#ifdef CONFIG_APUS
++                      *ptr++ = in_le32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE));
++#else
++                      *ptr++ = in_be32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE));
++#endif /* CONFIG_APUS */
++#else
++                      *ptr++ = inl(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1));
++#endif /* __powerpc__ */
++                      cnt += 4;
++              }
++      } else if (fc->type == AVM_FRITZ_PCI) {
++              outl(idx, fc->addr + CHIP_INDEX);
++              while (cnt < count) {
++#ifdef __powerpc__
++#ifdef CONFIG_APUS
++                      *ptr++ = in_le32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE));
++#else
++                      *ptr++ = in_be32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE));
++#endif /* CONFIG_APUS */
++#else
++                      *ptr++ = inl(fc->addr + CHIP_WINDOW);
++#endif /* __powerpc__ */
++                      cnt += 4;
++              }
++      } else {
++              outb(idx, fc->addr + CHIP_INDEX);
++              while (cnt < count) {
++                      *p++ = inb(fc->addr + CHIP_WINDOW);
++                      cnt++;
++              }
++      }
++      if (fc->dch.debug & L1_DEB_HSCX_FIFO) {
++              char *t = bch->blog;
++
++              if (fc->type == AVM_FRITZ_PNP)
++                      p = (u_char *) ptr;
++              t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
++                           bch->channel ? 'B' : 'A', count);
++              mISDN_QuickHex(t, p, count);
++              mISDN_debugprint(&bch->inst, bch->blog);
++      }
++}
++
++#define HDLC_FIFO_SIZE        32
++
++static void
++hdlc_fill_fifo(bchannel_t *bch)
++{
++      fritzpnppci     *fc = bch->inst.data;
++      hdlc_hw_t       *hdlc = bch->hw;
++      int             count, cnt =0;
++      u_char          *p;
++      u_int           *ptr;
++
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "%s", __FUNCTION__);
++      count = bch->tx_len - bch->tx_idx;
++      if (count <= 0)
++              return;
++      p = bch->tx_buf + bch->tx_idx;
++      hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
++      if (count > HDLC_FIFO_SIZE) {
++              count = HDLC_FIFO_SIZE;
++      } else {
++              if (bch->protocol != ISDN_PID_L1_B_64TRANS)
++                      hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
++      }
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "%s: %d/%d", __FUNCTION__,
++                      count, bch->tx_idx);
++      ptr = (u_int *) p;
++      bch->tx_idx += count;
++      hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count);
++      if (fc->type == AVM_FRITZ_PCIV2) {
++              __write_ctrl_pciv2(fc, hdlc, bch->channel);
++              while (cnt<count) {
++#ifdef __powerpc__
++#ifdef CONFIG_APUS
++                      out_le32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE), *ptr++);
++#else
++                      out_be32((unsigned *)(fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1) +_IO_BASE), *ptr++);
++#endif /* CONFIG_APUS */
++#else
++                      outl(*ptr++, fc->addr + (bch->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1));
++#endif /* __powerpc__ */
++                      cnt += 4;
++              }
++      } else if (fc->type == AVM_FRITZ_PCI) {
++              __write_ctrl_pci(fc, hdlc, bch->channel);
++              while (cnt<count) {
++#ifdef __powerpc__
++#ifdef CONFIG_APUS
++                      out_le32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE), *ptr++);
++#else
++                      out_be32((unsigned *)(fc->addr + CHIP_WINDOW +_IO_BASE), *ptr++);
++#endif /* CONFIG_APUS */
++#else
++                      outl(*ptr++, fc->addr + CHIP_WINDOW);
++#endif /* __powerpc__ */
++                      cnt += 4;
++              }
++      } else {
++              __write_ctrl_pnp(fc, hdlc, bch->channel, 3);
++              while (cnt<count) {
++                      outb(*p++, fc->addr + CHIP_WINDOW);
++                      cnt++;
++              }
++      }
++      if (bch->debug & L1_DEB_HSCX_FIFO) {
++              char *t = bch->blog;
++
++              if (fc->type == AVM_FRITZ_PNP)
++                      p = (u_char *) ptr;
++              t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
++                           bch->channel ? 'B' : 'A', count);
++              mISDN_QuickHex(t, p, count);
++              mISDN_debugprint(&bch->inst, bch->blog);
++      }
++}
++
++static void
++HDLC_irq_xpr(bchannel_t *bch)
++{
++      if (bch->tx_idx < bch->tx_len)
++              hdlc_fill_fifo(bch);
++      else {
++              bch->tx_idx = 0;
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      if (bch->next_skb) {
++                              bch->tx_len = bch->next_skb->len;
++                              memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len);
++                              hdlc_fill_fifo(bch);
++                      } else {
++                              bch->tx_len = 0;
++                              printk(KERN_WARNING "hdlc tx irq TX_NEXT without skb\n");
++                              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                      }
++              } else {
++                      bch->tx_len = 0;
++                      test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              }
++              bch_sched_event(bch, B_XMTBUFREADY);
++      }
++}
++
++static void
++HDLC_irq(bchannel_t *bch, u_int stat)
++{
++      int             len;
++      struct sk_buff  *skb;
++      hdlc_hw_t       *hdlc = bch->hw;
++
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat);
++      if (stat & HDLC_INT_RPR) {
++              if (stat & HDLC_STAT_RDO) {
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "RDO");
++                      else
++                              mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat);
++                      hdlc->ctrl.sr.xml = 0;
++                      hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS;
++                      write_ctrl(bch, 1);
++                      hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
++                      write_ctrl(bch, 1);
++                      bch->rx_idx = 0;
++              } else {
++                      if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
++                              len = 32;
++                      hdlc_empty_fifo(bch, len);
++                      if ((stat & HDLC_STAT_RME) || (bch->protocol == ISDN_PID_L1_B_64TRANS)) {
++                              if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
++                                      (bch->protocol == ISDN_PID_L1_B_64TRANS)) {
++                                      if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen)))
++                                              printk(KERN_WARNING "HDLC: receive out of memory\n");
++                                      else {
++                                              memcpy(skb_put(skb, bch->rx_idx),
++                                                      bch->rx_buf, bch->rx_idx);
++                                              skb_queue_tail(&bch->rqueue, skb);
++                                      }
++                                      bch->rx_idx = 0;
++                                      bch_sched_event(bch, B_RCVBUFREADY);
++                              } else {
++                                      if (bch->debug & L1_DEB_HSCX)
++                                              mISDN_debugprint(&bch->inst, "invalid frame");
++                                      else
++                                              mISDN_debugprint(&bch->inst, "ch%d invalid frame %#x", bch->channel, stat);
++                                      bch->rx_idx = 0;
++                              }
++                      }
++              }
++      }
++      if (stat & HDLC_INT_XDU) {
++              /* Here we lost an TX interrupt, so
++               * restart transmitting the whole frame on HDLC
++               * in transparent mode we send the next data
++               */
++              if (bch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&bch->inst, "ch%d XDU tx_len(%d) tx_idx(%d) Flag(%lx)",
++                              bch->channel, bch->tx_len, bch->tx_idx, bch->Flag);
++              if (bch->tx_len) {
++                      if (bch->protocol != ISDN_PID_L1_B_64TRANS)
++                              bch->tx_idx = 0;
++              }
++              hdlc->ctrl.sr.xml = 0;
++              hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS;
++              write_ctrl(bch, 1);
++              hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
++              HDLC_irq_xpr(bch);
++              return;
++      } else if (stat & HDLC_INT_XPR)
++              HDLC_irq_xpr(bch);
++}
++
++static inline void
++HDLC_irq_main(fritzpnppci *fc)
++{
++      u_int stat;
++      bchannel_t *bch;
++
++      stat = read_status(fc, 0);
++      if (stat & HDLC_INT_MASK) {
++              if (!(bch = Sel_BCS(fc, 0))) {
++                      if (fc->bch[0].debug)
++                              mISDN_debugprint(&fc->bch[0].inst, "hdlc spurious channel 0 IRQ");
++              } else
++                      HDLC_irq(bch, stat);
++      }
++      stat = read_status(fc, 1);
++      if (stat & HDLC_INT_MASK) {
++              if (!(bch = Sel_BCS(fc, 1))) {
++                      if (fc->bch[1].debug)
++                              mISDN_debugprint(&fc->bch[1].inst, "hdlc spurious channel 1 IRQ");
++              } else
++                      HDLC_irq(bch, stat);
++      }
++}
++
++static irqreturn_t
++avm_fritz_interrupt(int intno, void *dev_id, struct pt_regs *regs)
++{
++      fritzpnppci     *fc = dev_id;
++      u_long          flags;
++      u_char val;
++      u_char sval;
++
++      spin_lock_irqsave(&fc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      fc->lock.spin_adr = (void *)0x2001;
++#endif
++      sval = inb(fc->addr + 2);
++      if (fc->dch.debug & L1_DEB_INTSTAT)
++              mISDN_debugprint(&fc->dch.inst, "irq stat0 %x", sval);
++      if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
++              /* possible a shared  IRQ reqest */
++#ifdef SPIN_DEBUG
++              fc->lock.spin_adr = NULL;
++#endif
++              spin_unlock_irqrestore(&fc->lock.lock, flags);
++              return IRQ_NONE;
++      }
++      fc->irqcnt++;
++      if (test_and_set_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
++                      __FUNCTION__, fc->lock.state);
++#ifdef SPIN_DEBUG
++              printk(KERN_ERR "%s: previous lock:%p\n",
++                      __FUNCTION__, fc->lock.busy_adr);
++#endif
++#ifdef LOCK_STATISTIC
++              fc->lock.irq_fail++;
++#endif
++      } else {
++#ifdef LOCK_STATISTIC
++              fc->lock.irq_ok++;
++#endif
++#ifdef SPIN_DEBUG
++              fc->lock.busy_adr = avm_fritz_interrupt;
++#endif
++      }
++
++      test_and_set_bit(STATE_FLAG_INIRQ, &fc->lock.state);
++#ifdef SPIN_DEBUG
++      fc->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&fc->lock.lock, flags);
++      if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
++              val = ReadISAC(fc, ISAC_ISTA);
++              mISDN_isac_interrupt(&fc->dch, val);
++      }
++      if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
++              HDLC_irq_main(fc);
++      }
++      if (fc->type == AVM_FRITZ_PNP) {
++              WriteISAC(fc, ISAC_MASK, 0xFF);
++              WriteISAC(fc, ISAC_MASK, 0x0);
++      }
++      spin_lock_irqsave(&fc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      fc->lock.spin_adr = (void *)0x2002;
++#endif
++      if (!test_and_clear_bit(STATE_FLAG_INIRQ, &fc->lock.state)) {
++      }
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      __FUNCTION__, fc->lock.state);
++      }
++#ifdef SPIN_DEBUG
++      fc->lock.busy_adr = NULL;
++      fc->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&fc->lock.lock, flags);
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t
++avm_fritzv2_interrupt(int intno, void *dev_id, struct pt_regs *regs)
++{
++      fritzpnppci     *fc = dev_id;
++      u_long          flags;
++      u_char val;
++      u_char sval;
++
++      spin_lock_irqsave(&fc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      fc->lock.spin_adr = (void *)0x2001;
++#endif
++      sval = inb(fc->addr + 2);
++      if (fc->dch.debug & L1_DEB_INTSTAT)
++              mISDN_debugprint(&fc->dch.inst, "irq stat0 %x", sval);
++      if (!(sval & AVM_STATUS0_IRQ_MASK)) {
++              /* possible a shared  IRQ reqest */
++#ifdef SPIN_DEBUG
++              fc->lock.spin_adr = NULL;
++#endif
++              spin_unlock_irqrestore(&fc->lock.lock, flags);
++              return IRQ_NONE;
++      }
++      fc->irqcnt++;
++      if (test_and_set_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
++                      __FUNCTION__, fc->lock.state);
++#ifdef SPIN_DEBUG
++              printk(KERN_ERR "%s: previous lock:%p\n",
++                      __FUNCTION__, fc->lock.busy_adr);
++#endif
++#ifdef LOCK_STATISTIC
++              fc->lock.irq_fail++;
++#endif
++      } else {
++#ifdef LOCK_STATISTIC
++              fc->lock.irq_ok++;
++#endif
++#ifdef SPIN_DEBUG
++              fc->lock.busy_adr = avm_fritz_interrupt;
++#endif
++      }
++
++      test_and_set_bit(STATE_FLAG_INIRQ, &fc->lock.state);
++#ifdef SPIN_DEBUG
++      fc->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&fc->lock.lock, flags);
++      if (sval & AVM_STATUS0_IRQ_HDLC) {
++              HDLC_irq_main(fc);
++      }
++      if (sval & AVM_STATUS0_IRQ_ISAC) {
++              val = fcpci2_read_isac(fc, ISACSX_ISTA);
++              mISDN_isac_interrupt(&fc->dch, val);
++      }
++      if (sval & AVM_STATUS0_IRQ_TIMER) {
++              if (fc->dch.debug & L1_DEB_INTSTAT)
++                      mISDN_debugprint(&fc->dch.inst, "Fc2 timer irq");
++              outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2);
++              udelay(1);
++              outb(fc->ctrlreg, fc->addr + 2);
++      }
++      spin_lock_irqsave(&fc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      fc->lock.spin_adr = (void *)0x2002;
++#endif
++      if (!test_and_clear_bit(STATE_FLAG_INIRQ, &fc->lock.state)) {
++      }
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &fc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      __FUNCTION__, fc->lock.state);
++      }
++#ifdef SPIN_DEBUG
++      fc->lock.busy_adr = NULL;
++      fc->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&fc->lock.lock, flags);
++      return IRQ_HANDLED;
++}
++
++static int
++hdlc_down(mISDNif_t *hif, struct sk_buff *skb)
++{
++      bchannel_t      *bch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      bch = hif->fdata;
++      if ((hh->prim == PH_DATA_REQ) ||
++              (hh->prim == (DL_DATA | REQUEST))) {
++              if (bch->next_skb) {
++                      mISDN_debugprint(&bch->inst, " l2l1 next_skb exist this shouldn't happen");
++                      return(-EBUSY);
++              }
++              bch->inst.lock(bch->inst.data, 0);
++              if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) {
++                      test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag);
++                      bch->next_skb = skb;
++                      bch->inst.unlock(bch->inst.data);
++                      return(0);
++              } else {
++                      bch->tx_len = skb->len;
++                      memcpy(bch->tx_buf, skb->data, bch->tx_len);
++                      bch->tx_idx = 0;
++                      hdlc_fill_fifo(bch);
++                      bch->inst.unlock(bch->inst.data);
++                      skb_trim(skb, 0);
++                      return(if_newhead(&bch->inst.up, hh->prim | CONFIRM,
++                              hh->dinfo, skb));
++              }
++      } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_ESTABLISH  | REQUEST))) {
++              if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
++                      ret = 0;
++              else {
++                      bch->inst.lock(bch->inst.data,0);
++                      ret = modehdlc(bch, bch->channel,
++                              bch->inst.pid.protocol[1]);
++                      bch->inst.unlock(bch->inst.data);
++              }
++              skb_trim(skb, 0);
++              return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
++      } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_RELEASE | REQUEST)) ||
++              (hh->prim == (MGR_DISCONNECT | REQUEST))) {
++              bch->inst.lock(bch->inst.data,0);
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      dev_kfree_skb(bch->next_skb);
++                      bch->next_skb = NULL;
++              }
++              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              modehdlc(bch, bch->channel, 0);
++              test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
++              bch->inst.unlock(bch->inst.data);
++              skb_trim(skb, 0);
++              if (hh->prim != (MGR_DISCONNECT | REQUEST))
++                      if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
++                              return(0);
++              ret = 0;
++      } else {
++              printk(KERN_WARNING "hdlc_down unknown prim(%x)\n", hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++static void
++inithdlc(fritzpnppci *fc)
++{
++      modehdlc(&fc->bch[0], 0, -1);
++      modehdlc(&fc->bch[1], 1, -1);
++}
++
++void
++clear_pending_hdlc_ints(fritzpnppci *fc)
++{
++      u_int val;
++
++      val = read_status(fc, 0);
++      mISDN_debugprint(&fc->dch.inst, "HDLC 1 STA %x", val);
++      val = read_status(fc, 1);
++      mISDN_debugprint(&fc->dch.inst, "HDLC 2 STA %x", val);
++}
++
++static void
++reset_avmpcipnp(fritzpnppci *fc)
++{
++      switch (fc->type) {
++              case AVM_FRITZ_PNP:
++              case AVM_FRITZ_PCI:
++                      fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER;
++                      break;
++              case AVM_FRITZ_PCIV2:
++                      fc->ctrlreg = AVM_STATUS0_RESET;
++                      break;
++      }
++      printk(KERN_INFO "AVM PCI/PnP: reset\n");
++      outb(fc->ctrlreg, fc->addr + 2);
++      mdelay(5);
++      switch (fc->type) {
++              case AVM_FRITZ_PNP:
++              case AVM_FRITZ_PCI:
++                      fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
++                      outb(fc->ctrlreg, fc->addr + 2);
++                      outb(AVM_STATUS1_ENA_IOM | fc->irq, fc->addr + 3);
++                      break;
++              case AVM_FRITZ_PCIV2:
++                      fc->ctrlreg = 0;
++                      outb(fc->ctrlreg, fc->addr + 2);
++                      break;
++      }
++      mdelay(1);
++      printk(KERN_INFO "AVM PCI/PnP: S0/S1 %x/%x\n", inb(fc->addr + 2), inb(fc->addr + 3));
++}
++
++static int init_card(fritzpnppci *fc)
++{
++      int             cnt = 3;
++      u_int           shared = SA_SHIRQ;
++
++      if (fc->type == AVM_FRITZ_PNP)
++              shared = 0;
++      lock_dev(fc, 0);
++      if (fc->type == AVM_FRITZ_PCIV2) {
++              if (request_irq(fc->irq, avm_fritzv2_interrupt, SA_SHIRQ,
++                      "AVM Fritz!PCI", fc)) {
++                      printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
++                              fc->irq);
++                      unlock_dev(fc);
++                      return(-EIO);
++              }
++      } else {
++              if (request_irq(fc->irq, avm_fritz_interrupt, shared,
++                      "AVM Fritz!PCI", fc)) {
++                      printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
++                              fc->irq);
++                      unlock_dev(fc);
++                      return(-EIO);
++              }
++      }
++      reset_avmpcipnp(fc);
++      while (cnt) {
++              int     ret;
++              mISDN_clear_isac(&fc->dch);
++              if ((ret=mISDN_isac_init(&fc->dch))) {
++                      printk(KERN_WARNING "mISDN: mISDN_isac_init failed with %d\n", ret);
++                      break;
++              }
++              clear_pending_hdlc_ints(fc);
++              inithdlc(fc);
++              outb(fc->ctrlreg, fc->addr + 2);
++              WriteISAC(fc, ISAC_MASK, 0);
++              fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
++              outb(fc->ctrlreg, fc->addr + 2);
++              /* RESET Receiver and Transmitter */
++              WriteISAC(fc, ISAC_CMDR, 0x41);
++              unlock_dev(fc);
++              /* Timeout 10ms */
++              current->state = TASK_UNINTERRUPTIBLE;
++              schedule_timeout((10*HZ)/1000);
++              printk(KERN_INFO "AVM Fritz!PCI: IRQ %d count %d\n",
++                      fc->irq, fc->irqcnt);
++              if (!fc->irqcnt) {
++                      printk(KERN_WARNING
++                             "AVM Fritz!PCI: IRQ(%d) getting no interrupts during init %d\n",
++                             fc->irq, 4 - cnt);
++                      if (cnt == 1) {
++                              return (-EIO);
++                      } else {
++                              reset_avmpcipnp(fc);
++                              cnt--;
++                      }
++              } else {
++                      return(0);
++              }
++              lock_dev(fc, 0);
++      }
++      unlock_dev(fc);
++      return(-EIO);
++}
++
++#define MAX_CARDS     4
++#define MODULE_PARM_T "1-4i"
++static int fritz_cnt;
++static u_int protocol[MAX_CARDS];
++static int layermask[MAX_CARDS];
++
++static mISDNobject_t  fritz;
++static int debug;
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++MODULE_PARM(protocol, MODULE_PARM_T);
++MODULE_PARM(layermask, MODULE_PARM_T);
++#endif
++
++int
++setup_fritz(fritzpnppci *fc)
++{
++      u_int   val, ver;
++
++      if (!request_region(fc->addr, 32, (fc->type == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) {
++              printk(KERN_WARNING
++                     "mISDN: %s config port %x-%x already in use\n",
++                     "AVM Fritz!PCI",
++                     fc->addr,
++                     fc->addr + 31);
++              return(-EIO);
++      }
++      switch (fc->type) {
++          case AVM_FRITZ_PCI:
++              val = inl(fc->addr);
++              printk(KERN_INFO "AVM PCI: stat %#x\n", val);
++              printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
++                      val & 0xff, (val>>8) & 0xff);
++              outl(AVM_HDLC_1, fc->addr + CHIP_INDEX);
++              ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24;
++              printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf);
++              fc->dch.read_reg = &ReadISAC;
++              fc->dch.write_reg = &WriteISAC;
++              fc->dch.read_fifo = &ReadISACfifo;
++              fc->dch.write_fifo = &WriteISACfifo;
++              fc->dch.type = ISAC_TYPE_ISAC;
++              break;
++          case AVM_FRITZ_PCIV2:
++              val = inl(fc->addr);
++              printk(KERN_INFO "AVM PCI V2: stat %#x\n", val);
++              printk(KERN_INFO "AVM PCI V2: Class %X Rev %d\n",
++                      val & 0xff, (val>>8) & 0xff);
++              ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24;
++              printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf);
++              fc->dch.read_reg = &fcpci2_read_isac;
++              fc->dch.write_reg = &fcpci2_write_isac;
++              fc->dch.read_fifo = &fcpci2_read_isac_fifo;
++              fc->dch.write_fifo = &fcpci2_write_isac_fifo;
++              fc->dch.type = ISAC_TYPE_ISACSX;
++              break;
++          case AVM_FRITZ_PNP:
++              val = inb(fc->addr);
++              ver = inb(fc->addr + 1);
++              printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
++              outb(AVM_HDLC_1, fc->addr + CHIP_INDEX);
++              ver = inb(fc->addr + CHIP_WINDOW + 7);
++              printk(KERN_INFO "AVM PnP: HDLC version %x\n", ver & 0xf);
++              fc->dch.read_reg = &ReadISAC;
++              fc->dch.write_reg = &WriteISAC;
++              fc->dch.read_fifo = &ReadISACfifo;
++              fc->dch.write_fifo = &WriteISACfifo;
++              fc->dch.type = ISAC_TYPE_ISAC;
++              break;
++          default:
++              release_region(fc->addr, 32);
++              printk(KERN_WARNING "AVM unknown type %d\n", fc->type);
++              return(-ENODEV);
++      }
++      printk(KERN_INFO "mISDN: %s config irq:%d base:0x%X\n",
++              (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" :
++              (fc->type == AVM_FRITZ_PCIV2) ? "AVM Fritz!PCIv2" : "AVM Fritz!PnP",
++              fc->irq, fc->addr);
++
++      fc->dch.hw = &fc->isac;
++      lock_dev(fc, 0);
++#ifdef SPIN_DEBUG
++      printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &fc->lock.busy_adr, fc->lock.busy_adr);
++      printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &fc->lock.busy_adr, fc->lock.busy_adr);
++#endif
++      unlock_dev(fc);
++      return(0);
++}
++
++static void
++release_card(fritzpnppci *card)
++{
++#ifdef LOCK_STATISTIC
++      printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",
++              card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq);
++      printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
++              card->lock.irq_ok, card->lock.irq_fail);
++#endif
++      lock_dev(card, 0);
++      outb(0, card->addr + 2);
++      free_irq(card->irq, card);
++      modehdlc(&card->bch[0], 0, ISDN_PID_NONE);
++      modehdlc(&card->bch[1], 1, ISDN_PID_NONE);
++      mISDN_isac_free(&card->dch);
++      release_region(card->addr, 32);
++      mISDN_free_bch(&card->bch[1]);
++      mISDN_free_bch(&card->bch[0]);
++      mISDN_free_dch(&card->dch);
++      fritz.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up);
++      fritz.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
++      list_del(&card->list);
++      unlock_dev(card);
++      if (card->type == AVM_FRITZ_PNP) {
++              pnp_disable_dev(card->pdev);
++              pnp_set_drvdata(card->pdev, NULL);
++      } else {
++              pci_disable_device(card->pdev);
++              pci_set_drvdata(card->pdev, NULL);
++      }
++      kfree(card);
++}
++
++static int
++fritz_manager(void *data, u_int prim, void *arg) {
++      fritzpnppci     *card;
++      mISDNinstance_t *inst = data;
++      struct sk_buff  *skb;
++      int             channel = -1;
++
++      if (debug & 0x10000)
++              printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n",
++                      __FUNCTION__, data, prim, arg);
++      if (!data) {
++              MGR_HASPROTOCOL_HANDLER(prim,arg,&fritz)
++              printk(KERN_ERR "%s: no data prim %x arg %p\n",
++                      __FUNCTION__, prim, arg);
++              return(-EINVAL);
++      }
++      list_for_each_entry(card, &fritz.ilist, list) {
++              if (&card->dch.inst == inst) {
++                      channel = 2;
++                      break;
++              }
++              if (&card->bch[0].inst == inst) {
++                      channel = 0;
++                      break;
++              }
++              if (&card->bch[1].inst == inst) {
++                      channel = 1;
++                      break;
++              }
++      }
++      if (channel<0) {
++              printk(KERN_WARNING "%s: no channel data %p prim %x arg %p\n",
++                      __FUNCTION__, data, prim, arg);
++              return(-EINVAL);
++      }
++
++      switch(prim) {
++          case MGR_REGLAYER | CONFIRM:
++              if (channel == 2)
++                      dch_set_para(&card->dch, &inst->st->para);
++              else
++                      bch_set_para(&card->bch[channel], &inst->st->para);
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++              if (channel == 2) {
++                      inst->down.fdata = &card->dch;
++                      if ((skb = create_link_skb(PH_CONTROL | REQUEST,
++                              HW_DEACTIVATE, 0, NULL, 0))) {
++                              if (mISDN_ISAC_l1hw(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              } else {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (hdlc_down(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              }
++              fritz.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
++              fritz.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++              break;
++          case MGR_CLRSTPARA | INDICATION:
++              arg = NULL;
++          case MGR_ADDSTPARA | INDICATION:
++              if (channel == 2)
++                      dch_set_para(&card->dch, arg);
++              else
++                      bch_set_para(&card->bch[channel], arg);
++              break;
++          case MGR_RELEASE | INDICATION:
++              if (channel == 2) {
++                      release_card(card);
++              } else {
++                      fritz.refcnt--;
++              }
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              if (channel==2)
++                      return(mISDN_SetIF(inst, arg, prim, mISDN_ISAC_l1hw, NULL,
++                              &card->dch));
++              else
++                      return(mISDN_SetIF(inst, arg, prim, hdlc_down, NULL,
++                              &card->bch[channel]));
++              break;
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_SETSTACK | CONFIRM:
++              if ((channel!=2) && (inst->pid.global == 2)) {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(PH_ACTIVATE | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (hdlc_down(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++                      if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
++                              if_link(&inst->up, DL_ESTABLISH | INDICATION,
++                                      0, 0, NULL, 0);
++                      else
++                              if_link(&inst->up, PH_ACTIVATE | INDICATION,
++                                      0, 0, NULL, 0);
++              }
++              break;
++          PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
++          PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST);
++          default:
++              printk(KERN_WARNING "%s: prim %x not handled\n",
++                      __FUNCTION__, prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static int __devinit setup_instance(fritzpnppci *card)
++{
++      int             i, err;
++      mISDN_pid_t     pid;
++      
++      list_add_tail(&card->list, &fritz.ilist);
++      card->dch.debug = debug;
++      lock_HW_init(&card->lock);
++      card->dch.inst.lock = lock_dev;
++      card->dch.inst.unlock = unlock_dev;
++      card->dch.inst.pid.layermask = ISDN_LAYER(0);
++      card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
++      mISDN_init_instance(&card->dch.inst, &fritz, card);
++      sprintf(card->dch.inst.name, "Fritz%d", fritz_cnt+1);
++      mISDN_set_dchannel_pid(&pid, protocol[fritz_cnt], layermask[fritz_cnt]);
++      mISDN_init_dch(&card->dch);
++      for (i=0; i<2; i++) {
++              card->bch[i].channel = i;
++              mISDN_init_instance(&card->bch[i].inst, &fritz, card);
++              card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
++              card->bch[i].inst.lock = lock_dev;
++              card->bch[i].inst.unlock = unlock_dev;
++              card->bch[i].debug = debug;
++              sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1);
++              mISDN_init_bch(&card->bch[i]);
++              card->bch[i].hw = &card->hdlc[i];
++      }
++      printk(KERN_DEBUG "fritz card %p dch %p bch1 %p bch2 %p\n",
++              card, &card->dch, &card->bch[0], &card->bch[1]);
++      err = setup_fritz(card);
++      if (err) {
++              mISDN_free_dch(&card->dch);
++              mISDN_free_bch(&card->bch[1]);
++              mISDN_free_bch(&card->bch[0]);
++              list_del(&card->list);
++              kfree(card);
++              return(err);
++      }
++      fritz_cnt++;
++      err = fritz.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst);
++      if (err) {
++              release_card(card);
++              return(err);
++      }
++      for (i=0; i<2; i++) {
++              err = fritz.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst);
++              if (err) {
++                      printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
++                      fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++                      return(err);
++              }
++      }
++      err = fritz.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid);
++      if (err) {
++              printk(KERN_ERR  "MGR_SETSTACK REQUEST dch err(%d)\n", err);
++              fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(err);
++      }
++      err = init_card(card);
++      if (err) {
++              fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(err);
++      }
++      fritz.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL);
++      printk(KERN_INFO "fritz %d cards installed\n", fritz_cnt);
++      return(0);
++}
++
++static int __devinit fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++      int             err = -ENOMEM;
++      fritzpnppci     *card;
++
++      if (!(card = kmalloc(sizeof(fritzpnppci), GFP_ATOMIC))) {
++              printk(KERN_ERR "No kmem for fritzcard\n");
++              return(err);
++      }
++      memset(card, 0, sizeof(fritzpnppci));
++      if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2)
++              card->type = AVM_FRITZ_PCIV2;
++      else
++              card->type = AVM_FRITZ_PCI;
++      card->pdev = pdev;
++      err = pci_enable_device(pdev);
++      if (err) {
++              kfree(card);
++              return(err);
++      }
++
++      printk(KERN_INFO "mISDN_fcpcipnp: found adapter %s at %s\n",
++             (char *) ent->driver_data, pdev->slot_name);
++
++      card->addr = pci_resource_start(pdev, 1);
++      card->irq = pdev->irq;
++      pci_set_drvdata(pdev, card);
++      err = setup_instance(card);
++      if (err)
++              pci_set_drvdata(pdev, NULL);
++      return(err);
++}
++
++#if defined(CONFIG_PNP)
++#ifdef NEW_ISAPNP
++static int __devinit fritzpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
++#else
++static int __devinit fritzpnp_probe(struct pci_dev *pdev, const struct isapnp_device_id *dev_id)
++#endif
++{
++      int             err;
++      fritzpnppci     *card;
++
++      if (!pdev)
++              return(-ENODEV);
++
++      if (!(card = kmalloc(sizeof(fritzpnppci), GFP_ATOMIC))) {
++              printk(KERN_ERR "No kmem for fritzcard\n");
++              return(-ENOMEM);
++      }
++      memset(card, 0, sizeof(fritzpnppci));
++      card->type = AVM_FRITZ_PNP;
++      card->pdev = pdev;
++      pnp_disable_dev(pdev);
++      err = pnp_activate_dev(pdev);
++      if (err<0) {
++              printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__,
++                      (char *)dev_id->driver_data, err);
++              kfree(card);
++              return(err);
++      }
++      card->addr = pnp_port_start(pdev, 0);
++      card->irq = pnp_irq(pdev, 0);
++
++      printk(KERN_INFO "mISDN_fcpcipnp: found adapter %s at IO %#x irq %d\n",
++             (char *)dev_id->driver_data, card->addr, card->irq);
++
++      pnp_set_drvdata(pdev, card);
++      err = setup_instance(card);
++      if (err)
++              pnp_set_drvdata(pdev, NULL);
++      return(err);
++}
++#endif /* CONFIG_PNP */
++
++static void __devexit fritz_remove_pci(struct pci_dev *pdev)
++{
++      fritzpnppci     *card = pci_get_drvdata(pdev);
++
++      if (card)
++              fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++      else
++              printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__);
++}
++
++#if defined(CONFIG_PNP)
++#ifdef NEW_ISAPNP
++static void __devexit fritz_remove_pnp(struct pnp_dev *pdev)
++#else
++static void __devexit fritz_remove_pnp(struct pci_dev *pdev)
++#endif
++{
++      fritzpnppci     *card = pnp_get_drvdata(pdev);
++
++      if (card)
++              fritz.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++      else
++              printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__);
++}
++#endif /* CONFIG_PNP */
++
++static struct pci_device_id fcpci_ids[] __devinitdata = {
++      { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1   , PCI_ANY_ID, PCI_ANY_ID,
++        0, 0, (unsigned long) "Fritz!Card PCI" },
++      { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
++        0, 0, (unsigned long) "Fritz!Card PCI v2" },
++      { }
++};
++MODULE_DEVICE_TABLE(pci, fcpci_ids);
++
++static struct pci_driver fcpci_driver = {
++      name:     "fcpci",
++      probe:    fritzpci_probe,
++      remove:   __devexit_p(fritz_remove_pci),
++      id_table: fcpci_ids,
++};
++
++#if defined(CONFIG_PNP)
++#ifdef NEW_ISAPNP
++static struct pnp_device_id fcpnp_ids[] __devinitdata = {
++      { 
++              .id             = "AVM0900",
++              .driver_data    = (unsigned long) "Fritz!Card PnP",
++      },
++};
++
++static struct pnp_driver fcpnp_driver = {
++#else
++static struct isapnp_device_id fcpnp_ids[] __devinitdata = {
++      { ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900),
++        ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), 
++        (unsigned long) "Fritz!Card PnP" },
++      { }
++};
++MODULE_DEVICE_TABLE(isapnp, fcpnp_ids);
++
++static struct isapnp_driver fcpnp_driver = {
++#endif
++      name:     "fcpnp",
++      probe:    fritzpnp_probe,
++      remove:   __devexit_p(fritz_remove_pnp),
++      id_table: fcpnp_ids,
++};
++#endif /* CONFIG_PNP */
++
++static char FritzName[] = "AVM Fritz";
++
++static int __init Fritz_init(void)
++{
++      int     err, pci_nr_found;
++
++      printk(KERN_INFO "AVM Fritz PCI/PnP driver Rev. %s\n", mISDN_getrev(avm_fritz_rev));
++#ifdef MODULE
++      fritz.owner = THIS_MODULE;
++#endif
++      INIT_LIST_HEAD(&fritz.ilist);
++      fritz.name = FritzName;
++      fritz.own_ctrl = fritz_manager;
++      fritz.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0;
++      fritz.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS |
++                                  ISDN_PID_L1_B_64HDLC;
++      fritz.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS;
++      if ((err = mISDN_register(&fritz))) {
++              printk(KERN_ERR "Can't register Fritz PCI error(%d)\n", err);
++              return(err);
++      }
++      err = pci_register_driver(&fcpci_driver);
++      if (err < 0)
++              goto out;
++      pci_nr_found = err;
++#if defined(CONFIG_PNP)
++      err = pnp_register_driver(&fcpnp_driver);
++      if (err < 0)
++              goto out_unregister_pci;
++#endif
++#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
++      if (pci_nr_found + err == 0) {
++              err = -ENODEV;
++              goto out_unregister_isapnp;
++      }
++#endif
++      return 0;
++
++#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
++ out_unregister_isapnp:
++#if defined(CONFIG_PNP)
++      pnp_unregister_driver(&fcpnp_driver);
++#endif
++#endif
++ out_unregister_pci:
++      pci_unregister_driver(&fcpci_driver);
++ out:
++      return err;
++}
++
++static void __exit Fritz_cleanup(void)
++{
++      fritzpnppci *card, *next;
++      int err;
++
++      if ((err = mISDN_unregister(&fritz))) {
++              printk(KERN_ERR "Can't unregister Fritz PCI error(%d)\n", err);
++      }
++      list_for_each_entry_safe(card, next, &fritz.ilist, list) {
++              printk(KERN_ERR "Fritz PCI card struct not empty refs %d\n",
++                      fritz.refcnt);
++              release_card(card);
++      }
++#if defined(CONFIG_PNP)
++      pnp_unregister_driver(&fcpnp_driver);
++#endif
++      pci_unregister_driver(&fcpci_driver);
++}
++
++module_init(Fritz_init);
++module_exit(Fritz_cleanup);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.c       2004-11-22 09:33:37.892783336 +0000
+@@ -0,0 +1,156 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mISDNif.h>
++#include "layer1.h"
++#include "bchannel.h"
++#include "helper.h"
++
++static void
++bchannel_bh(bchannel_t *bch)
++{
++      struct sk_buff  *skb;
++      u_int           pr;
++      int             ret;
++      mISDN_head_t    *hh;
++      mISDNif_t       *hif;
++
++      if (!bch)
++              return;
++      if (!bch->inst.up.func) {
++              printk(KERN_WARNING "%s: without up.func\n", __FUNCTION__);
++              return;
++      }
++#if 0
++      printk(KERN_DEBUG "%s: event %x\n", __FUNCTION__, bch->event);
++      if (bch->dev)
++              printk(KERN_DEBUG "%s: rpflg(%x) wpflg(%x)\n", __FUNCTION__,
++                      bch->dev->rport.Flag, bch->dev->wport.Flag);
++#endif
++      if (test_and_clear_bit(B_XMTBUFREADY, &bch->event)) {
++              skb = bch->next_skb;
++              if (skb) {
++                      hh = mISDN_HEAD_P(skb);
++                      bch->next_skb = NULL;
++                      if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS)
++                              pr = DL_DATA | CONFIRM;
++                      else
++                              pr = PH_DATA | CONFIRM;
++                      if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                              && bch->dev)
++                              hif = &bch->dev->rport.pif;
++                      else
++                              hif = &bch->inst.up;
++                      if (if_newhead(hif, pr, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++      if (test_and_clear_bit(B_RCVBUFREADY, &bch->event)) {
++              if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                      && bch->dev)
++                      hif = &bch->dev->rport.pif;
++              else
++                      hif = &bch->inst.up;
++              while ((skb = skb_dequeue(&bch->rqueue))) {
++                      if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS)
++                              pr = DL_DATA | INDICATION;
++                      else
++                              pr = PH_DATA | INDICATION;
++                      ret = if_newhead(hif, pr, MISDN_ID_ANY, skb);
++                      if (ret < 0) {
++                              printk(KERN_WARNING "%s: deliver err %d\n",
++                                      __FUNCTION__, ret);
++                              dev_kfree_skb(skb);
++                      }
++              }
++      }
++      if (bch->hw_bh)
++              bch->hw_bh(bch);
++}
++
++int
++mISDN_init_bch(bchannel_t *bch) {
++      int     devtyp = mISDN_RAW_DEVICE;
++
++      if (!(bch->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
++              printk(KERN_WARNING
++                      "mISDN: No memory for blog\n");
++              return(-ENOMEM);
++      }
++      if (!(bch->rx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) {
++              printk(KERN_WARNING
++                      "mISDN: No memory for bchannel rx_buf\n");
++              kfree(bch->blog);
++              bch->blog = NULL;
++              return (-ENOMEM);
++      }
++      if (!(bch->tx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) {
++              printk(KERN_WARNING
++                      "mISDN: No memory for bchannel tx_buf\n");
++              kfree(bch->blog);
++              bch->blog = NULL;
++              kfree(bch->rx_buf);
++              bch->rx_buf = NULL;
++              return (-ENOMEM);
++      }
++      skb_queue_head_init(&bch->rqueue);
++      bch->next_skb = NULL;
++      bch->Flag = 0;
++      bch->event = 0;
++      bch->rx_idx = 0;
++      bch->tx_len = 0;
++      bch->tx_idx = 0;
++      INIT_WORK(&bch->work, (void *)(void *)bchannel_bh, bch);
++      bch->hw_bh = NULL;
++      if (!bch->dev) {
++              if (bch->inst.obj->ctrl(&bch->dev, MGR_GETDEVICE | REQUEST,
++                      &devtyp)) {
++                      printk(KERN_WARNING
++                              "mISDN: no raw device for bchannel\n");
++              }
++      }
++      return(0);
++}
++
++int
++mISDN_free_bch(bchannel_t *bch) {
++#ifdef HAS_WORKQUEUE
++      if (bch->work.pending)
++              printk(KERN_ERR "mISDN_free_bch work:(%lx)\n", bch->work.pending);
++#else
++      if (bch->work.sync)
++              printk(KERN_ERR "mISDN_free_bch work:(%lx)\n", bch->work.sync);
++#endif
++      discard_queue(&bch->rqueue);
++      if (bch->blog) {
++              kfree(bch->blog);
++              bch->blog = NULL;
++      }
++      if (bch->rx_buf) {
++              kfree(bch->rx_buf);
++              bch->rx_buf = NULL;
++      }
++      if (bch->tx_buf) {
++              kfree(bch->tx_buf);
++              bch->tx_buf = NULL;
++      }
++      if (bch->next_skb) {
++              dev_kfree_skb(bch->next_skb);
++              bch->next_skb = NULL;
++      }
++      if (bch->inst.obj->ctrl(bch->dev, MGR_DELDEVICE | REQUEST, NULL)) {
++              printk(KERN_WARNING
++                      "mISDN: del raw device error\n");
++      } else
++              bch->dev = NULL;
++      return(0);
++}
++
++EXPORT_SYMBOL(mISDN_init_bch);
++EXPORT_SYMBOL(mISDN_free_bch);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/bchannel.h   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/bchannel.h       2004-11-22 09:33:37.903781664 +0000
+@@ -0,0 +1,98 @@
++/* $Id$
++ *
++ *   Basic declarations, defines for Bchannel hardware
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/mISDNif.h>
++#ifdef HAS_WORKQUEUE
++#include <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#endif
++#include <linux/smp.h>
++#include <linux/ptrace.h>
++#include <linux/interrupt.h>
++#include <linux/timer.h>
++#include <asm/io.h>
++#include <linux/ioport.h>
++#include <linux/skbuff.h>
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++#define MAX_BLOG_SPACE                256
++
++#define BC_FLG_INIT   1
++#define BC_FLG_ACTIV  2
++#define BC_FLG_TX_BUSY        3
++#define BC_FLG_NOFRAME        4
++#define BC_FLG_HALF   5
++#define BC_FLG_EMPTY  6
++#define BC_FLG_ORIG   7
++#define BC_FLG_DLEETX 8
++#define BC_FLG_LASTDLE        9
++#define BC_FLG_FIRST  10
++#define BC_FLG_LASTDATA       11
++#define BC_FLG_NMD_DATA       12
++#define BC_FLG_FTI_RUN        13
++#define BC_FLG_LL_OK  14
++#define BC_FLG_LL_CONN        15
++#define BC_FLG_TX_NEXT        16
++#define BC_FLG_DTMFSEND       17
++
++typedef struct _bchannel_t {
++      int                     channel;
++      int                     protocol;
++      u_long                  Flag;
++      int                     debug;
++      mISDNstack_t            *st;
++      mISDNinstance_t         inst;
++      mISDNdevice_t           *dev;
++      void                    *hw;
++      u_char                  (*Read_Reg)(void *, int, u_char);
++      void                    (*Write_Reg)(void *, int, u_char, u_char);
++      struct sk_buff          *next_skb;
++      u_char                  *tx_buf;
++      int                     tx_idx;
++      int                     tx_len;
++      u_char                  *rx_buf;
++      int                     rx_idx;
++      struct sk_buff_head     rqueue; /* B-Channel receive Queue */
++      u_char                  *blog;
++      u_char                  *conmsg;
++      struct timer_list       transbusy;
++      struct work_struct      work;
++      void                    (*hw_bh) (struct _bchannel_t *);
++      u_long                  event;
++      int                     maxdatasize;
++      int                     up_headerlen;
++      int                     err_crc;
++      int                     err_tx;
++      int                     err_rdo;
++      int                     err_inv;
++} bchannel_t;
++
++extern int mISDN_init_bch(bchannel_t *);
++extern int mISDN_free_bch(bchannel_t *);
++
++static inline void
++bch_set_para(bchannel_t *bch, mISDN_stPara_t *stp)
++{
++      if (stp) {
++              bch->maxdatasize = stp->maxdatalen;
++              bch->up_headerlen = stp->up_headerlen;
++      } else {
++              bch->maxdatasize = 0;
++              bch->up_headerlen = 0;
++      }
++}
++
++static inline void
++bch_sched_event(bchannel_t *bch, int event)
++{
++      test_and_set_bit(event, &bch->event);
++      schedule_work(&bch->work);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi.c   2004-11-22 09:33:37.913780144 +0000
+@@ -0,0 +1,443 @@
++/* $Id$
++ *
++ */
++
++#include <linux/module.h>
++#include "m_capi.h"
++#include "helper.h"
++#include "debug.h"
++
++static char *capi_revision = "$Revision$";
++
++static int debug = 0;
++static mISDNobject_t capi_obj;
++
++static char MName[] = "mISDN Capi 2.0";
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++#endif
++
++static char deb_buf[256];
++
++void capidebug(int level, char *fmt, ...)
++{
++      va_list args;
++
++      if (debug & level) {
++              va_start(args, fmt);
++              vsprintf(deb_buf, fmt, args);
++              printk(KERN_DEBUG "%s\n", deb_buf);
++              va_end(args);
++      }
++}
++
++#ifdef OLDCAPI_DRIVER_INTERFACE
++struct capi_driver_interface *cdrv_if;
++#endif
++
++kmem_cache_t  *mISDN_cmsg_cp;
++kmem_cache_t  *mISDN_AppPlci_cp;
++kmem_cache_t  *mISDN_ncci_cp;
++kmem_cache_t  *mISDN_sspc_cp;
++
++#ifdef MISDN_KMEM_DEBUG
++static struct list_head mISDN_kmem_garbage = LIST_HEAD_INIT(mISDN_kmem_garbage);
++
++_cmsg *
++_kd_cmsg_alloc(char *fn, int line)
++{
++      _kd_cmsg_t      *ki = kmem_cache_alloc(mISDN_cmsg_cp, GFP_ATOMIC);
++
++      if (!ki)
++              return(NULL);
++      ki->kdi.typ = KM_DBG_TYP_CM;
++      INIT_LIST_HEAD(&ki->kdi.head);
++      ki->kdi.line = line;
++      ki->kdi.file = fn;
++      list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
++      return(&ki->cm);
++}
++
++void
++cmsg_free(_cmsg *cm)
++{
++      km_dbg_item_t   *kdi;
++
++      if (!cm) {
++              int_errtxt("zero pointer free at %p", __builtin_return_address(0));
++              return;
++      }
++      kdi = KDB_GET_KDI(cm);
++      list_del(&kdi->head);
++      kmem_cache_free(mISDN_cmsg_cp, kdi);
++}
++
++AppPlci_t *
++_kd_AppPlci_alloc(char *fn, int line)
++{
++      _kd_AppPlci_t   *ki = kmem_cache_alloc(mISDN_AppPlci_cp, GFP_ATOMIC);
++
++      if (!ki)
++              return(NULL);
++      ki->kdi.typ = KM_DBG_TYP_AP;
++      INIT_LIST_HEAD(&ki->kdi.head);
++      ki->kdi.line = line;
++      ki->kdi.file = fn;
++      list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
++      return(&ki->ap);
++}
++
++void
++AppPlci_free(AppPlci_t *ap)
++{
++      km_dbg_item_t   *kdi;
++
++      if (!ap) {
++              int_errtxt("zero pointer free at %p", __builtin_return_address(0));
++              return;
++      }
++      kdi = KDB_GET_KDI(ap);
++      list_del(&kdi->head);
++      kmem_cache_free(mISDN_AppPlci_cp, kdi);
++}
++
++Ncci_t *
++_kd_ncci_alloc(char *fn, int line)
++{
++      _kd_Ncci_t      *ki = kmem_cache_alloc(mISDN_ncci_cp, GFP_ATOMIC);
++
++      if (!ki)
++              return(NULL);
++      ki->kdi.typ = KM_DBG_TYP_NI;
++      INIT_LIST_HEAD(&ki->kdi.head);
++      ki->kdi.line = line;
++      ki->kdi.file = fn;
++      list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
++      return(&ki->ni);
++}
++
++void
++ncci_free(Ncci_t *ni)
++{
++      km_dbg_item_t   *kdi;
++
++      if (!ni) {
++              int_errtxt("zero pointer free at %p", __builtin_return_address(0));
++              return;
++      }
++      kdi = KDB_GET_KDI(ni);
++      list_del(&kdi->head);
++      kmem_cache_free(mISDN_ncci_cp, kdi);
++}
++
++SSProcess_t *
++_kd_SSProcess_alloc(char *fn, int line)
++{
++      _kd_SSProcess_t *ki = kmem_cache_alloc(mISDN_sspc_cp, GFP_ATOMIC);
++
++      if (!ki)
++              return(NULL);
++      ki->kdi.typ = KM_DBG_TYP_SP;
++      INIT_LIST_HEAD(&ki->kdi.head);
++      ki->kdi.line = line;
++      ki->kdi.file = fn;
++      list_add_tail(&ki->kdi.head, &mISDN_kmem_garbage);
++      return(&ki->sp);
++}
++
++void
++SSProcess_free(SSProcess_t *sp)
++{
++      km_dbg_item_t   *kdi;
++
++      if (!sp) {
++              int_errtxt("zero pointer free at %p", __builtin_return_address(0));
++              return;
++      }
++      kdi = KDB_GET_KDI(sp);
++      list_del(&kdi->head);
++      kmem_cache_free(mISDN_sspc_cp, kdi);
++}
++
++static void
++free_garbage(void)
++{
++      struct list_head        *item, *next;
++      _kd_all_t               *kda;
++
++      list_for_each_safe(item, next, &mISDN_kmem_garbage) {
++              kda = (_kd_all_t *)item;
++              printk(KERN_DEBUG "garbage item found (%p <- %p -> %p) type%ld allocated at %s:%d\n",
++                      kda->kdi.head.prev, item, kda->kdi.head.next, kda->kdi.typ, kda->kdi.file, kda->kdi.line);
++              list_del(item);
++              switch(kda->kdi.typ) {
++                      case KM_DBG_TYP_CM:
++                              printk(KERN_DEBUG "cmsg cmd(%x,%x) appl(%x) addr(%x) nr(%d)\n",
++                                      kda->a.cm.Command,
++                                      kda->a.cm.Subcommand,
++                                      kda->a.cm.ApplId,
++                                      kda->a.cm.adr.adrController,
++                                      kda->a.cm.Messagenumber);
++                              kmem_cache_free(mISDN_cmsg_cp, item);
++                              break;
++                      case KM_DBG_TYP_AP:
++                              printk(KERN_DEBUG "AppPlci: PLCI(%x) m.state(%x) appl(%p)\n",
++                                      kda->a.ap.addr,
++                                      kda->a.ap.plci_m.state,
++                                      kda->a.ap.appl);
++                              kmem_cache_free(mISDN_AppPlci_cp, item);
++                              break;
++                      case KM_DBG_TYP_NI:
++                              printk(KERN_DEBUG "Ncci: NCCI(%x) state(%lx) m.state(%x) aplci(%p)\n",
++                                      kda->a.ni.addr,
++                                      kda->a.ni.state,
++                                      kda->a.ni.ncci_m.state,
++                                      kda->a.ni.AppPlci);
++                              kmem_cache_free(mISDN_ncci_cp, item);
++                              break;
++                      case KM_DBG_TYP_SP:
++                              printk(KERN_DEBUG "SSPc: addr(%x) id(%x) apid(%x) func(%x)\n",
++                                      kda->a.sp.addr,
++                                      kda->a.sp.invokeId,
++                                      kda->a.sp.ApplId,
++                                      kda->a.sp.Function);
++                              kmem_cache_free(mISDN_sspc_cp, item);
++                              break;
++                      default:
++                              printk(KERN_DEBUG "unknown garbage item(%p) type %ld\n",
++                                      item, kda->kdi.typ);
++                              break; 
++              }
++      }
++}
++
++#endif
++
++static void CapiCachesFree(void)
++{
++#ifdef MISDN_KMEM_DEBUG
++      free_garbage();
++#endif
++      if (mISDN_cmsg_cp) {
++              kmem_cache_destroy(mISDN_cmsg_cp);
++              mISDN_cmsg_cp = NULL;
++      }
++      if (mISDN_AppPlci_cp) {
++              kmem_cache_destroy(mISDN_AppPlci_cp);
++              mISDN_AppPlci_cp = NULL;
++      }
++      if (mISDN_ncci_cp) {
++              kmem_cache_destroy(mISDN_ncci_cp);
++              mISDN_ncci_cp = NULL;
++      }
++      if (mISDN_sspc_cp) {
++              kmem_cache_destroy(mISDN_sspc_cp);
++              mISDN_sspc_cp = NULL;
++      }
++}
++
++static int CapiNew(void)
++{
++      mISDN_cmsg_cp = NULL;
++      mISDN_AppPlci_cp = NULL;
++      mISDN_ncci_cp = NULL;
++      mISDN_sspc_cp = NULL;
++      mISDN_cmsg_cp = kmem_cache_create("mISDN_cmesg",
++#ifdef MISDN_KMEM_DEBUG
++                              sizeof(_kd_cmsg_t),
++#else
++                              sizeof(_cmsg),
++#endif
++                              0, 0, NULL, NULL);
++      if (!mISDN_cmsg_cp) {
++              CapiCachesFree();
++              return(-ENOMEM);
++      }
++      mISDN_AppPlci_cp = kmem_cache_create("mISDN_AppPlci",
++#ifdef MISDN_KMEM_DEBUG
++                              sizeof(_kd_AppPlci_t),
++#else
++                              sizeof(AppPlci_t),
++#endif
++                              0, 0, NULL, NULL);
++      if (!mISDN_AppPlci_cp) {
++              CapiCachesFree();
++              return(-ENOMEM);
++      }
++      mISDN_ncci_cp = kmem_cache_create("mISDN_Ncci",
++#ifdef MISDN_KMEM_DEBUG
++                              sizeof(_kd_Ncci_t),
++#else
++                              sizeof(Ncci_t),
++#endif
++                              0, 0, NULL, NULL);
++      if (!mISDN_ncci_cp) {
++              CapiCachesFree();
++              return(-ENOMEM);
++      }
++      mISDN_sspc_cp = kmem_cache_create("mISDN_SSProc",
++#ifdef MISDN_KMEM_DEBUG
++                              sizeof(_kd_SSProcess_t),
++#else
++                              sizeof(SSProcess_t),
++#endif
++                              0, 0, NULL, NULL);
++      if (!mISDN_sspc_cp) {
++              CapiCachesFree();
++              return(-ENOMEM);
++      }
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      cdrv_if = attach_capi_driver(&mISDN_driver);
++      if (!cdrv_if) {
++              CapiCachesFree();
++              printk(KERN_ERR "mISDN: failed to attach capi_driver\n");
++              return -EIO;
++      }
++#endif
++      init_listen();
++      init_AppPlci();
++      init_ncci();
++      return 0;
++}
++
++static int
++capi20_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      int             found=0;
++      PLInst_t        *plink = NULL;
++      Controller_t    *ctrl;
++
++      if (CAPI_DBG_INFO & debug)
++              printk(KERN_DEBUG "capi20_manager data:%p prim:%x arg:%p\n", data, prim, arg);
++      if (!data)
++              return(-EINVAL);
++      list_for_each_entry(ctrl, &capi_obj.ilist, list) {
++              if (&ctrl->inst == inst) {
++                      found++;
++                      break;
++              }
++              list_for_each_entry(plink, &ctrl->linklist, list) {
++                      if (&plink->inst == inst) {
++                              found++;
++                              break;
++                      }
++              }
++              if (found)
++                      break;
++              plink = NULL;
++      }
++      if (&ctrl->list == &capi_obj.ilist)
++              ctrl = NULL;
++      if (prim == (MGR_NEWLAYER | REQUEST)) {
++              int ret = ControllerConstr(&ctrl, data, arg, &capi_obj);
++              if (!ret)
++                      ctrl->debug = debug;
++              return(ret);
++      }
++      if (!ctrl) {
++              if (CAPI_DBG_WARN & debug)
++                      printk(KERN_WARNING "capi20_manager setif no instance\n");
++              return(-EINVAL);
++      }
++      switch(prim) {
++          case MGR_NEWENTITY | CONFIRM:
++              ctrl->entity = (int)arg;
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | INDICATION:
++          case MGR_SETIF | REQUEST:
++              if (&ctrl->inst == inst)
++                      return(mISDN_SetIF(inst, arg, prim, NULL, ControllerL3L4, ctrl));
++              else
++                      return(AppPlcimISDN_SetIF(inst->data, prim, arg));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_RELEASE | INDICATION:
++              if (CAPI_DBG_INFO & debug)
++                      printk(KERN_DEBUG "release_capi20 id %x\n", ctrl->inst.st->id);
++              ControllerDestr(ctrl);
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++              if (plink) {
++                      capi_obj.ctrl(plink->inst.down.peer, MGR_DISCONNECT | REQUEST,
++                              &plink->inst.down);
++                      capi_obj.ctrl(&plink->inst, MGR_UNREGLAYER | REQUEST, NULL);
++              }
++              break;
++          case MGR_CTRLREADY | INDICATION:
++              if (CAPI_DBG_INFO & debug)
++                      printk(KERN_DEBUG "ctrl %x ready\n", ctrl->inst.st->id);
++              ControllerRun(ctrl);
++              break;
++          default:
++              if (CAPI_DBG_WARN & debug)
++                      printk(KERN_WARNING "capi20_manager prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++int Capi20Init(void)
++{
++      int err;
++
++      printk(KERN_INFO "%s driver file version %s\n", MName, mISDN_getrev(capi_revision));
++#ifdef MODULE
++      capi_obj.owner = THIS_MODULE;
++#endif
++      capi_obj.name = MName;
++      capi_obj.DPROTO.protocol[4] = ISDN_PID_L4_CAPI20;
++      capi_obj.BPROTO.protocol[4] = ISDN_PID_L4_B_CAPI20;
++      capi_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_TRANS;
++      capi_obj.own_ctrl = capi20_manager;
++      INIT_LIST_HEAD(&capi_obj.ilist);
++      if ((err = CapiNew()))
++              return(err);
++      if ((err = mISDN_register(&capi_obj))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++#ifdef OLDCAPI_DRIVER_INTERFACE
++              detach_capi_driver(&mISDN_driver);
++#endif
++              CapiCachesFree();
++              free_listen();
++              free_AppPlci();
++              free_ncci();
++              free_Application();
++      }
++      return(err);
++}
++
++#ifdef MODULE
++static void Capi20cleanup(void)
++{
++      int             err;
++      Controller_t    *contr, *next;
++
++      if ((err = mISDN_unregister(&capi_obj))) {
++              printk(KERN_ERR "Can't unregister CAPI20 error(%d)\n", err);
++      }
++      if (!list_empty(&capi_obj.ilist)) {
++              printk(KERN_WARNING "mISDN controller list not empty\n");
++              list_for_each_entry_safe(contr, next, &capi_obj.ilist, list)
++                      ControllerDestr(contr);
++      }
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      detach_capi_driver(&mISDN_driver);
++#endif
++      free_Application();
++      CapiCachesFree();
++      free_listen();
++      free_AppPlci();
++      free_ncci();
++}
++
++module_init(Capi20Init);
++module_exit(Capi20cleanup);
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi_enc.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi_enc.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/capi_enc.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/capi_enc.c       2004-11-22 09:33:37.923778624 +0000
+@@ -0,0 +1,213 @@
++/* $Id$
++ *
++ */
++
++#include "m_capi.h"
++#include "asn1.h"
++
++int capiEncodeWord(__u8 *p, __u16 i)
++{
++      *p++ = i;
++      *p++ = i >> 8;
++      return 2;
++}
++
++int capiEncodeDWord(__u8 *p, __u32 i)
++{
++      *p++ = i;
++      *p++ = i >> 8;
++      *p++ = i >> 16;
++      *p++ = i >> 24;
++      return 4;
++}
++
++int capiEncodeFacilityPartyNumber(__u8 *dest, struct PartyNumber *partyNumber)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      switch (partyNumber->type) {
++      case 0: // unknown
++              *p++ = 0;
++              *p++ = 0;
++              *p++ = 0;
++              strcpy(p, partyNumber->p.unknown); p += strlen(partyNumber->p.unknown);
++              break;
++      case 1: // publicPartyNumber
++              *p++ = 1;
++              *p++ = partyNumber->p.publicPartyNumber.publicTypeOfNumber << 4;
++              *p++ = 0;
++              strcpy(p, partyNumber->p.publicPartyNumber.numberDigits);
++              p += strlen(partyNumber->p.publicPartyNumber.numberDigits);
++              break;
++      default: 
++              int_error();
++      }
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacilityPartyNumber2(__u8 *dest, struct ServedUserNr *servedUserNr)
++{
++      if (servedUserNr->all) {
++              *dest++ = 0; // empty struct;
++              return 1;
++      }
++      return capiEncodeFacilityPartyNumber(dest, &servedUserNr->partyNumber);
++}
++
++int capiEncodeServedUserNumbers(__u8 *dest, struct ServedUserNumberList *list)
++{
++      __u8 *p;
++      int i;
++
++      p = &dest[1];
++      for (i = 0; i < 10; i++) {
++              if (list->partyNumber[i].type >= 0)
++                      p += capiEncodeFacilityPartyNumber(p, &list->partyNumber[i]);
++      }
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeInterrogateResponse(__u8 *dest, struct IntResult *intResult)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, intResult->procedure);
++      p += capiEncodeWord(p, intResult->basicService);
++      p += capiEncodeFacilityPartyNumber2(p, &intResult->servedUserNr);
++      p += capiEncodeFacilityPartyNumber(p, &intResult->address.partyNumber);
++      *p++ = 0; // subaddress
++
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeInterrogateResponseList(__u8 *dest, struct IntResultList *list)
++{
++      __u8 *p;
++      int i;
++
++      p = &dest[1];
++      for (i = 0; i < 10; i++) {
++              if (list->intResult[i].basicService >= 0)
++                      p += capiEncodeInterrogateResponse(p, &list->intResult[i]);
++      }
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacIndCFact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, SupplementaryServiceReason);
++      p += capiEncodeDWord(p, Handle);
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacIndCFdeact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, SupplementaryServiceReason);
++      p += capiEncodeDWord(p, Handle);
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacIndCFinterParameters(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, 
++                                    struct IntResultList *list)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, SupplementaryServiceReason);
++      p += capiEncodeDWord(p, Handle);
++      p += capiEncodeInterrogateResponseList(p, list);
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacIndCFinterNumbers(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, 
++                                 struct ServedUserNumberList *list)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, SupplementaryServiceReason);
++      p += capiEncodeDWord(p, Handle);
++      p += capiEncodeServedUserNumbers(p, list);
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacIndCFNotAct(__u8 *dest, struct ActDivNotification *actNot)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, actNot->procedure);
++      p += capiEncodeWord(p, actNot->basicService);
++      p += capiEncodeFacilityPartyNumber2(p, &actNot->servedUserNr);
++      p += capiEncodeFacilityPartyNumber(p, &actNot->address.partyNumber);
++      *p++ = 0; // sub
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacIndCFNotDeact(__u8 *dest, struct DeactDivNotification *deactNot)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, deactNot->procedure);
++      p += capiEncodeWord(p, deactNot->basicService);
++      p += capiEncodeFacilityPartyNumber2(p, &deactNot->servedUserNr);
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacConfStruct(__u8 *dest, struct FacConfParm *facConfParm)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      switch (facConfParm->Function) {
++      case 0x0000:
++              p += capiEncodeWord(p, facConfParm->u.GetSupportedServices.SupplementaryServiceInfo);
++              p += capiEncodeDWord(p, facConfParm->u.GetSupportedServices.SupportedServices);
++              break;
++      default:
++              p += capiEncodeWord(p, facConfParm->u.Info.SupplementaryServiceInfo);
++      }
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacConfParm(__u8 *dest, struct FacConfParm *facConfParm)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, facConfParm->Function);
++      p += capiEncodeFacConfStruct(p, facConfParm);
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
++int capiEncodeFacIndSuspend(__u8 *dest, __u16  SupplementaryServiceReason)
++{
++      __u8 *p;
++
++      p = &dest[1];
++      p += capiEncodeWord(p, SupplementaryServiceReason);
++      dest[0] = p - &dest[1];
++      return p - dest;
++}
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/contr.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/contr.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/contr.c      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/contr.c  2004-11-22 09:33:37.933777104 +0000
+@@ -0,0 +1,702 @@
++/* $Id$
++ *
++ */
++
++#include <linux/vmalloc.h>
++#include <asm/uaccess.h>
++#include "m_capi.h"
++#include "helper.h"
++#include "debug.h"
++
++#define contrDebug(contr, lev, fmt, args...) \
++      if (contr->debug & lev) capidebug(lev, fmt, ## args)
++
++void
++ControllerDestr(Controller_t *contr)
++{
++      mISDNinstance_t         *inst = &contr->inst;
++      struct list_head        *item, *next;
++      u_long                  flags;
++
++      spin_lock_irqsave(&contr->list_lock, flags);
++      list_for_each_safe(item, next, &contr->Applications) {
++              ApplicationDestr(list_entry(item, Application_t, head), 3);
++      }
++      if (contr->plcis) {
++              Plci_t  *plci = contr->plcis;
++              int     i;
++
++              for (i = 0; i < contr->maxplci; i++) {
++                      AppPlci_t       *aplci;
++                      if (test_bit(PLCI_STATE_ACTIV, &plci->state)) {
++                              if (plci->nAppl) {
++                                      printk(KERN_ERR "%s: PLCI(%x) still busy (%d)\n",
++                                              __FUNCTION__, plci->addr, plci->nAppl);
++                                      list_for_each_safe(item, next, &plci->AppPlcis) {
++                                              aplci = (AppPlci_t *)item;
++                                              aplci->contr = NULL;
++                                              plciDetachAppPlci(plci, aplci);
++                                              AppPlciDestr(aplci);
++                                      }
++                              }
++                      }
++                      plci++;
++              }
++              kfree(contr->plcis);
++              contr->plcis = NULL;
++      }
++      list_for_each_safe(item, next, &contr->SSProcesse) {
++              SSProcessDestr(list_entry(item, SSProcess_t, head));
++      }
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      if (contr->ctrl)
++              cdrv_if->detach_ctr(contr->ctrl);
++#else
++      if (contr->ctrl) {
++              detach_capi_ctr(contr->ctrl);
++              kfree(contr->ctrl);
++      }
++#endif
++      contr->ctrl = NULL;
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      list_for_each_safe(item, next, &contr->linklist) {
++              PLInst_t        *plink = list_entry(item, PLInst_t, list);
++              list_del(&plink->list);
++              kfree(plink);
++      }
++      if (contr->entity != MISDN_ENTITY_NONE)
++              inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST, (void *)contr->entity);
++      inst->obj->ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      list_del(&contr->list);
++      spin_unlock_irqrestore(&contr->list_lock, flags);
++      kfree(contr);
++}
++
++void
++ControllerRun(Controller_t *contr)
++{
++      PLInst_t        *plink;
++      int             ret;
++
++      if (contr->inst.st && contr->inst.st->mgr)
++              sprintf(contr->ctrl->manu, "mISDN CAPI controller %s", contr->inst.st->mgr->name);
++      else
++              sprintf(contr->ctrl->manu, "mISDN CAPI");
++      strncpy(contr->ctrl->serial, "0002", CAPI_SERIAL_LEN);
++      contr->ctrl->version.majorversion = 2; 
++      contr->ctrl->version.minorversion = 0;
++      contr->ctrl->version.majormanuversion = 1;
++      contr->ctrl->version.minormanuversion = 0;
++      memset(&contr->ctrl->profile, 0, sizeof(struct capi_profile));
++      contr->ctrl->profile.ncontroller = 1;
++      contr->ctrl->profile.nbchannel = contr->nr_bc;
++      contrDebug(contr, CAPI_DBG_INFO, "%s: %s version(%s)",
++              __FUNCTION__, contr->ctrl->manu, contr->ctrl->serial);
++      // FIXME
++      ret = contr->inst.obj->ctrl(contr->inst.st, MGR_GLOBALOPT | REQUEST, &contr->ctrl->profile.goptions);
++      if (ret) {
++              /* Fallback on error, minimum set */
++              contr->ctrl->profile.goptions = GLOBALOPT_INTERNAL_CTRL;
++      }
++      /* add options we allways know about FIXME: DTMF */
++      contr->ctrl->profile.goptions |= GLOBALOPT_DTMF |
++                                       GLOBALOPT_SUPPLEMENTARY_SERVICE;
++
++      if (contr->nr_bc) {
++              mISDN_pid_t     pidmask;
++
++              memset(&pidmask, 0, sizeof(mISDN_pid_t));
++              pidmask.protocol[1] = 0x03ff;
++              pidmask.protocol[2] = 0x1fff;
++              pidmask.protocol[3] = 0x00ff;
++              if (list_empty(&contr->linklist)) {
++                      int_error();
++                      ret = -EINVAL;
++              } else {
++                      plink = list_entry(contr->linklist.next, PLInst_t, list);
++                      ret = plink->inst.obj->ctrl(plink->st, MGR_EVALSTACK | REQUEST, &pidmask);
++              }
++              if (ret) {
++                      /* Fallback on error, minimum set */
++                      int_error();
++                      contr->ctrl->profile.support1 = 3; // HDLC, TRANS
++                      contr->ctrl->profile.support2 = 3; // X75SLP, TRANS
++                      contr->ctrl->profile.support3 = 1; // TRANS
++              } else {
++                      contr->ctrl->profile.support1 = pidmask.protocol[1];
++                      contr->ctrl->profile.support2 = pidmask.protocol[2];
++                      contr->ctrl->profile.support3 = pidmask.protocol[3];
++              }
++      }
++      contrDebug(contr, CAPI_DBG_INFO, "%s: GLOBAL(%08X) B1(%08X) B2(%08X) B3(%08X)",
++              __FUNCTION__, contr->ctrl->profile.goptions, contr->ctrl->profile.support1,
++              contr->ctrl->profile.support2, contr->ctrl->profile.support2);
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      contr->ctrl->ready(contr->ctrl);
++#else
++      capi_ctr_ready(contr->ctrl);
++#endif
++}
++
++Application_t
++*getApplication4Id(Controller_t *contr, __u16 ApplId)
++{
++      struct list_head        *item;
++      Application_t           *ap = NULL;
++
++      list_for_each(item, &contr->Applications) {
++              ap = (Application_t *)item;
++              if (ap->ApplId == ApplId)
++                      break;
++              ap = NULL;
++      }
++      return(ap);
++}
++
++Plci_t
++*getPlci4Addr(Controller_t *contr, __u32 addr)
++{
++      int i = (addr >> 8) & 0xff;
++
++      if ((i < 1) || (i > contr->maxplci)) {
++              int_error();
++              return(NULL);
++      }
++      return(&contr->plcis[i - 1]);
++}
++
++static void
++RegisterApplication(struct capi_ctr *ctrl, __u16 ApplId, capi_register_params *rp)
++{
++      Controller_t    *contr = ctrl->driverdata;
++      Application_t   *appl;
++      u_long          flags;
++      int             ret;
++
++      contrDebug(contr, CAPI_DBG_APPL, "%s: ApplId(%x)", __FUNCTION__, ApplId);
++      appl = getApplication4Id(contr, ApplId);
++      if (appl) {
++              int_error();
++              return;
++      }
++      spin_lock_irqsave(&contr->list_lock, flags);
++      ret = ApplicationConstr(contr, ApplId, rp);
++      spin_unlock_irqrestore(&contr->list_lock, flags);
++      if (ret) {
++              int_error();
++              return;
++      }
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      contr->ctrl->appl_registered(contr->ctrl, ApplId);
++#endif
++}
++
++static void
++ReleaseApplication(struct capi_ctr *ctrl, __u16 ApplId)
++{
++      Controller_t    *contr = ctrl->driverdata;
++      Application_t   *appl;
++      u_long          flags;
++
++      contrDebug(contr, CAPI_DBG_APPL, "%s: ApplId(%x) caller:%lx", __FUNCTION__, ApplId, __builtin_return_address(0));
++      spin_lock_irqsave(&contr->list_lock, flags);
++      appl = getApplication4Id(contr, ApplId);
++      if (!appl) {
++              spin_unlock_irqrestore(&contr->list_lock, flags);
++              int_error();
++              return;
++      }
++      ApplicationDestr(appl, 1);
++      spin_unlock_irqrestore(&contr->list_lock, flags);
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      contr->ctrl->appl_released(contr->ctrl, ApplId);
++#endif
++}
++
++#ifdef OLDCAPI_DRIVER_INTERFACE
++static void
++#else
++static u16
++#endif
++SendMessage(struct capi_ctr *ctrl, struct sk_buff *skb)
++{
++      Controller_t    *contr = ctrl->driverdata;
++      Application_t   *appl;
++      int             ApplId;
++      int             err;
++
++      ApplId = CAPIMSG_APPID(skb->data);
++      appl = getApplication4Id(contr, ApplId);
++      if (!appl) {
++              int_error();
++              err = CAPI_ILLAPPNR;
++      } else
++              err = ApplicationSendMessage(appl, skb);
++#ifndef OLDCAPI_DRIVER_INTERFACE
++      return(err);
++#endif
++}
++
++static int
++LoadFirmware(struct capi_ctr *ctrl, capiloaddata *data)
++{
++      Controller_t    *contr = ctrl->driverdata;
++      struct firm {
++              int     len;
++              void    *data;
++      } firm;
++      int     retval;
++      
++      firm.len  = data->firmware.len;
++      if (data->firmware.user) {
++              firm.data = vmalloc(data->firmware.len);
++              if (!firm.data)
++                      return(-ENOMEM);
++              retval = copy_from_user(firm.data, data->firmware.data, data->firmware.len);
++              if (retval) {
++                      vfree(firm.data);
++                      return(retval);
++              }
++      } else
++              firm.data = data;
++      contr->inst.obj->ctrl(contr->inst.st, MGR_LOADFIRM | REQUEST, &firm);
++      if (data->firmware.user)
++              vfree(firm.data);
++      return(0);
++}
++
++static char *
++procinfo(struct capi_ctr *ctrl)
++{
++      Controller_t *contr = ctrl->driverdata;
++
++      if (CAPI_DBG_INFO & contr->debug)
++              printk(KERN_DEBUG "%s\n", __FUNCTION__);
++      if (!contr)
++              return "";
++      sprintf(contr->infobuf, "-");
++      return contr->infobuf;
++}
++
++static int
++read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
++{
++       int len = 0;
++
++       len += sprintf(page+len, "mISDN_read_proc\n");
++       if (off+count >= len)
++          *eof = 1;
++       if (len < off)
++           return 0;
++       *start = page + off;
++       return ((count < len-off) ? count : len-off);
++};
++
++
++static void
++ResetController(struct capi_ctr *ctrl)
++{
++      Controller_t            *contr = ctrl->driverdata;
++      struct list_head        *item, *next;
++      u_long                  flags;
++
++      spin_lock_irqsave(&contr->list_lock, flags);
++      list_for_each_safe(item, next, &contr->Applications) {
++              ApplicationDestr((Application_t *)item, 2);
++      }
++      list_for_each_safe(item, next, &contr->SSProcesse) {
++              SSProcessDestr((SSProcess_t *)item);
++      }
++      spin_unlock_irqrestore(&contr->list_lock, flags);
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      contr->ctrl->reseted(contr->ctrl);
++#else
++      capi_ctr_reseted(contr->ctrl);
++#endif
++}
++
++#ifdef OLDCAPI_DRIVER_INTERFACE
++static void
++Remove_Controller(struct capi_ctr *ctrl)
++{
++      Controller_t *contr = ctrl->driverdata;
++
++      if (CAPI_DBG_INFO & contr->debug)
++              printk(KERN_DEBUG "%s\n", __FUNCTION__);
++}
++
++struct capi_driver mISDN_driver = {
++      "mISDN",
++      "0.01",
++      LoadFirmware,
++      ResetController,
++      Remove_Controller,
++      RegisterApplication,
++      ReleaseApplication,
++      SendMessage,
++      procinfo,
++      read_proc,
++      0,
++      0,
++};
++#endif
++
++void
++ControllerD2Trace(Controller_t *contr, u_char *buf, int len)
++{
++      struct list_head        *item;
++
++      list_for_each(item, &contr->Applications) {
++              applD2Trace((Application_t *)item, buf, len);
++      }
++}
++
++static __inline__ Plci_t *
++getPlci4L3id(Controller_t *contr, u_int l3id)
++{
++      Plci_t  *plci = contr->plcis;
++      int     i;
++
++      for (i = 0; i < contr->maxplci; i++) {
++              if (test_bit(PLCI_STATE_ACTIV, &plci->state) &&
++                      (plci->l3id == l3id))
++                      return(plci);
++              plci++;
++      }
++      return(NULL);
++}
++
++int
++ControllerNewPlci(Controller_t *contr, Plci_t  **plci_p, u_int l3id)
++{
++      int     i;
++      Plci_t  *plci = contr->plcis;
++
++      for (i = 0; i < contr->maxplci; i++) {
++              if (!test_and_set_bit(PLCI_STATE_ACTIV, &plci->state))
++                      break;
++              plci++;
++      }
++      if (i == contr->maxplci) {
++              contrDebug(contr, CAPI_DBG_PLCI, "%s: no free PLCI",
++                      __FUNCTION__);
++              return(-EBUSY); //FIXME
++      }
++      *plci_p = plci;
++      if (l3id == MISDN_ID_ANY) {
++              if (contr->entity == MISDN_ENTITY_NONE) {
++                      printk(KERN_ERR "mISDN %s: no ENTITY id\n",
++                              __FUNCTION__);
++                      test_and_clear_bit(PLCI_STATE_ACTIV, &plci->state);
++                      return(-EINVAL); //FIXME
++              }
++              plci->l3id = (contr->entity << 16) | plci->addr;
++      } else {
++              plci = getPlci4L3id(contr, l3id);
++              if (plci) {
++                      printk(KERN_WARNING "mISDN %s: PLCI(%x) allready has l3id(%x)\n",
++                              __FUNCTION__, plci->addr, l3id);
++                      test_and_clear_bit(PLCI_STATE_ACTIV, &(*plci_p)->state);
++                      return(-EBUSY); 
++              }
++              plci = *plci_p;
++              plci->l3id = l3id;
++      }
++      contrDebug(contr, CAPI_DBG_PLCI, "%s: PLCI(%x) plci(%p,%d) id(%x)",
++              __FUNCTION__, plci->addr, plci, sizeof(*plci), plci->l3id);
++      return(0);
++}
++
++int
++ControllerReleasePlci(Plci_t *plci)
++{
++      if (!plci->contr) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (plci->nAppl) {
++              contrDebug(plci->contr, CAPI_DBG_PLCI, "%s: PLCI(%x) still has %d Applications",
++                      __FUNCTION__, plci->addr, plci->nAppl);
++              return(-EBUSY);
++      }
++      if (!list_empty(&plci->AppPlcis)) {
++              int_errtxt("PLCI(%x) AppPlcis list not empty", plci->addr);
++              return(-EBUSY);
++      }
++      test_and_clear_bit(PLCI_STATE_ALERTING, &plci->state);
++      test_and_clear_bit(PLCI_STATE_OUTGOING, &plci->state);
++      plci->l3id = MISDN_ID_NONE;
++      if (!test_and_clear_bit(PLCI_STATE_ACTIV, &plci->state))
++              int_errtxt("PLCI(%x) was not activ", plci->addr);
++      return(0);
++}
++
++void
++ControllerAddSSProcess(Controller_t *contr, SSProcess_t *sp)
++{
++      u_long  flags;
++
++      INIT_LIST_HEAD(&sp->head);
++      sp->contr = contr;
++      sp->addr = contr->addr;
++      spin_lock_irqsave(&contr->list_lock, flags);
++      contr->LastInvokeId++;
++      sp->invokeId = contr->LastInvokeId;
++      list_add(&sp->head, &contr->SSProcesse);
++      spin_unlock_irqrestore(&contr->list_lock, flags);
++}
++
++SSProcess_t
++*getSSProcess4Id(Controller_t *contr, __u16 id)
++{
++      struct list_head        *item;
++      SSProcess_t             *sp = NULL;
++
++      list_for_each(item, &contr->SSProcesse) {
++              sp = (SSProcess_t *)item;
++              if (sp->invokeId == id)
++                      break;
++              sp = NULL;
++      }
++      return(sp);
++}
++
++int
++ControllerL3L4(mISDNif_t *hif, struct sk_buff *skb)
++{
++      Controller_t    *contr;
++      Plci_t          *plci;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      contr = hif->fdata;
++      contrDebug(contr, CAPI_DBG_CONTR_INFO, "%s: prim(%x) id(%x)",
++              __FUNCTION__, hh->prim, hh->dinfo);
++      if (hh->prim == (CC_NEW_CR | INDICATION)) {
++              ret = ControllerNewPlci(contr, &plci, hh->dinfo);
++              if(!ret)
++                      dev_kfree_skb(skb);
++      } else if (hh->dinfo == MISDN_ID_DUMMY) {
++              ret = Supplementary_l3l4(contr, hh->prim, skb);
++      } else {
++              if (!(plci = getPlci4L3id(contr, hh->dinfo))) {
++                      contrDebug(contr, CAPI_DBG_WARN, "%s: unknown plci prim(%x) id(%x)",
++                              __FUNCTION__, hh->prim, hh->dinfo);
++                      return(-ENODEV);
++              }
++              contrDebug(contr, CAPI_DBG_PLCI, "%s: PLCI(%x) plci(%p)", __FUNCTION__, plci->addr, plci);
++              ret = plci_l3l4(plci, hh->prim, skb);
++      }
++      return(ret);
++}
++
++int
++ControllerL4L3(Controller_t *contr, u_int prim, int dinfo, struct sk_buff *skb)
++{
++      return(if_newhead(&contr->inst.down, prim, dinfo, skb));
++}
++
++void
++ControllerPutStatus(Controller_t *contr, char *msg)
++{
++      contrDebug(contr, CAPI_DBG_CONTR, "%s: %s", __FUNCTION__, msg);
++}
++
++int
++ControllerConstr(Controller_t **contr_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *ocapi)
++{
++      struct list_head        *head;
++      Controller_t            *contr;
++      int                     retval;
++      mISDNstack_t            *cst;
++      PLInst_t                *plink;
++
++      if (!st)
++              return(-EINVAL);
++      if (list_empty(&st->childlist)) {
++              if ((st->id & FLG_CLONE_STACK) &&
++                      (st->childlist.prev != &st->childlist)) {
++                      head = st->childlist.prev;
++              } else {
++                      printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n",
++                              __FUNCTION__, st->id, st->childlist.prev, &st->childlist, st->childlist.next);
++                      return(-EINVAL);
++              }
++      } else
++              head = &st->childlist;
++      if (!pid)
++              return(-EINVAL);
++      contr = kmalloc(sizeof(Controller_t), GFP_KERNEL);
++      if (!contr)
++              return(-ENOMEM);
++      memset(contr, 0, sizeof(Controller_t));
++      INIT_LIST_HEAD(&contr->Applications);
++      INIT_LIST_HEAD(&contr->SSProcesse);
++      INIT_LIST_HEAD(&contr->linklist);
++      spin_lock_init(&contr->list_lock);
++      spin_lock_init(&contr->id_lock);
++      contr->next_id = 1;
++      memcpy(&contr->inst.pid, pid, sizeof(mISDN_pid_t));
++#ifndef OLDCAPI_DRIVER_INTERFACE
++      if (!(contr->ctrl = kmalloc(sizeof(struct capi_ctr), GFP_KERNEL))) {
++              printk(KERN_ERR "no mem for contr->ctrl\n");
++              int_error();
++              ControllerDestr(contr);
++              return -ENOMEM;
++      }
++      memset(contr->ctrl, 0, sizeof(struct capi_ctr));
++#endif
++      list_for_each_entry(cst, head, list)
++              contr->nr_bc++;
++      if (!contr->nr_bc) {
++              printk(KERN_ERR "no bchannels\n");
++              ControllerDestr(contr);
++              return(-EINVAL); // FIXME
++      }
++      if (contr->nr_bc <= 2)
++              contr->maxplci = CAPI_MAXPLCI_BRI;
++      else if (contr->nr_bc <= 8)
++              contr->maxplci = contr->nr_bc * 2 + 4;
++      else
++              contr->maxplci = CAPI_MAXPLCI_PRI;
++      contr->plcis = kmalloc(contr->maxplci*sizeof(Plci_t), GFP_KERNEL);
++      if (!contr->plcis) {
++              printk(KERN_ERR "no mem for contr->plcis\n");
++              int_error();
++              contr->maxplci = 0;
++              ControllerDestr(contr);
++              return -ENOMEM;
++      }
++      // FIXME ???
++      contr->addr = st->id;
++      sprintf(contr->inst.name, "CAPI %d", st->id);
++      mISDN_init_instance(&contr->inst, ocapi, contr);
++      if (!mISDN_SetHandledPID(ocapi, &contr->inst.pid)) {
++              int_error();
++              ControllerDestr(contr);
++              return(-ENOPROTOOPT);
++      }
++      list_for_each_entry(cst, head, list) {
++              if (!(plink = kmalloc(sizeof(PLInst_t), GFP_KERNEL))) {
++                      printk(KERN_ERR "no mem for PLinst\n");
++                      int_error();
++                      ControllerDestr(contr);
++                      return -ENOMEM;
++              }
++              memset(plink, 0, sizeof(PLInst_t));
++              plink->st = cst;
++              plink->inst.st = cst;
++              mISDN_init_instance(&plink->inst, ocapi, plink);
++              plink->inst.pid.layermask |= ISDN_LAYER(4);
++              plink->inst.down.stat = IF_NOACTIV;
++              list_add_tail(&plink->list, &contr->linklist);
++      }
++      list_add_tail(&contr->list, &ocapi->ilist);
++      contr->entity = MISDN_ENTITY_NONE;
++      retval = ocapi->ctrl(&contr->inst, MGR_NEWENTITY | REQUEST, NULL);
++      if (retval) {
++              printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n",
++                      __FUNCTION__, retval);
++      }
++      retval = 0;
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      {
++              char    tmp[10];
++
++              sprintf(tmp, "mISDN%d", st->id);
++              contr->ctrl = cdrv_if->attach_ctr(&mISDN_driver, tmp, contr);
++              if (!contr->ctrl)
++                      retval = -ENODEV;
++      }
++#else
++      contr->ctrl->owner = THIS_MODULE;
++      sprintf(contr->ctrl->name, "mISDN%d", st->id);
++      contr->ctrl->driver_name = "mISDN";
++      contr->ctrl->driverdata = contr;
++      contr->ctrl->register_appl = RegisterApplication;
++      contr->ctrl->release_appl = ReleaseApplication;
++      contr->ctrl->send_message = SendMessage;
++      contr->ctrl->load_firmware = LoadFirmware;
++      contr->ctrl->reset_ctr = ResetController;
++      contr->ctrl->procinfo = procinfo;
++      contr->ctrl->ctr_read_proc = read_proc;
++      retval = attach_capi_ctr(contr->ctrl);
++#endif
++      if (!retval) {
++              contr->addr = contr->ctrl->cnr;
++              plciInit(contr);
++              ocapi->ctrl(st, MGR_REGLAYER | INDICATION, &contr->inst);
++              contr->inst.up.stat = IF_DOWN;
++              *contr_p = contr;
++      } else {
++              ControllerDestr(contr);
++      }
++      return retval;
++}
++
++PLInst_t *
++ControllerSelChannel(Controller_t *contr, u_int channel)
++{ 
++      mISDNstack_t    *cst;
++      PLInst_t        *plink;
++      channel_info_t  ci;
++      int             ret;
++
++      if (list_empty(&contr->linklist)) {
++              int_errtxt("no linklist for controller(%x)", contr->addr);
++              return(NULL);
++      }
++      ci.channel = channel;
++      ci.st.p = NULL;
++      ret = contr->inst.obj->ctrl(contr->inst.st, MGR_SELCHANNEL | REQUEST, &ci);
++      if (ret) {
++              int_errtxt("MGR_SELCHANNEL ret(%d)", ret);
++              return(NULL);
++      }
++      cst = ci.st.p;
++      list_for_each_entry(plink, &contr->linklist, list) {
++              if (cst == plink->st)
++                      return(plink);
++      }
++      return(NULL);
++}
++
++int
++ControllerNextId(Controller_t *contr)
++{
++      u_long  flags;
++      int     id;
++
++      spin_lock_irqsave(&contr->id_lock, flags);
++      id = contr->next_id++;
++      if (id == 0x7fff)
++              contr->next_id = 1;
++      spin_unlock_irqrestore(&contr->id_lock, flags);
++      id |= (contr->entity << 16);
++      return(id);
++}
++
++#if 0
++static void
++d2_listener(struct IsdnCardState *cs, u_char *buf, int len)
++{
++      Controller_t *contr = cs->contr;
++
++      if (!contr) {
++              int_error();
++              return;
++      }
++
++      ControllerD2Trace(contr, buf, len);
++}
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.c   2004-11-22 09:33:37.944775432 +0000
+@@ -0,0 +1,670 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/stddef.h>
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/spinlock.h>
++#include "core.h"
++#ifdef CONFIG_KMOD
++#include <linux/kmod.h>
++#endif
++#ifdef CONFIG_SMP
++#include <linux/smp_lock.h>
++#endif
++
++static char           *mISDN_core_revision = "$Revision$";
++
++LIST_HEAD(mISDN_objectlist);
++rwlock_t              mISDN_objects_lock = RW_LOCK_UNLOCKED;
++
++int core_debug;
++
++static u_char         entityarray[MISDN_MAX_ENTITY/8];
++static spinlock_t     entity_lock = SPIN_LOCK_UNLOCKED;
++
++static int debug;
++static int obj_id;
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++#endif
++
++typedef struct _mISDN_thread {
++      /* thread */
++      struct task_struct      *thread;
++      wait_queue_head_t       waitq;
++      struct semaphore        *notify;
++      u_long                  Flags;
++      struct sk_buff_head     workq;
++} mISDN_thread_t;
++
++#define       mISDN_TFLAGS_STARTED    1
++#define mISDN_TFLAGS_RMMOD    2
++#define mISDN_TFLAGS_ACTIV    3
++#define mISDN_TFLAGS_TEST     4
++
++static mISDN_thread_t mISDN_thread;
++
++static moditem_t modlist[] = {
++      {"mISDN_l1", ISDN_PID_L1_TE_S0},
++      {"mISDN_l2", ISDN_PID_L2_LAPD},
++      {"mISDN_l2", ISDN_PID_L2_B_X75SLP},
++      {"l3udss1", ISDN_PID_L3_DSS1USER},
++      {"mISDN_dtmf", ISDN_PID_L2_B_TRANSDTMF},
++      {NULL, ISDN_PID_NONE}
++};
++
++/* 
++ * kernel thread to do work which cannot be done
++ *in interrupt context 
++ */
++
++static int
++mISDNd(void *data)
++{
++      mISDN_thread_t  *hkt = data;
++
++#ifdef CONFIG_SMP
++      lock_kernel();
++#endif
++      MAKEDAEMON("mISDNd");
++      sigfillset(&current->blocked);
++      hkt->thread = current;
++#ifdef CONFIG_SMP
++      unlock_kernel();
++#endif
++      printk(KERN_DEBUG "mISDNd: kernel daemon started\n");
++
++      test_and_set_bit(mISDN_TFLAGS_STARTED, &hkt->Flags);
++
++      for (;;) {
++              int             err;
++              struct sk_buff  *skb;
++              mISDN_headext_t *hhe;
++
++              if (test_and_clear_bit(mISDN_TFLAGS_RMMOD, &hkt->Flags))
++                      break;
++              if (hkt->notify != NULL)
++                      up(hkt->notify);
++              interruptible_sleep_on(&hkt->waitq);
++              if (test_and_clear_bit(mISDN_TFLAGS_RMMOD, &hkt->Flags))
++                      break;
++              while ((skb = skb_dequeue(&hkt->workq))) {
++                      test_and_set_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags);
++                      err = -EINVAL;
++                      hhe=mISDN_HEADEXT_P(skb);
++                      switch (hhe->addr) {
++                              case MGR_FUNCTION:
++                                      err=hhe->func.ctrl(hhe->data[0], hhe->prim, skb->data);
++                                      if (err) {
++                                              printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) failed err(%x)\n",
++                                                      hhe->addr, hhe->prim, err);
++                                      } else {
++                                              if (debug)
++                                                      printk(KERN_DEBUG "mISDNd: addr(%x) prim(%x) success\n",
++                                                              hhe->addr, hhe->prim);
++                                              err--; /* to free skb */
++                                      }
++                                      break;
++                              case MGR_QUEUEIF:
++                                      err = hhe->func.iff(hhe->data[0], skb);
++                                      if (err) {
++                                              printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) failed err(%x)\n",
++                                                      hhe->addr, hhe->prim, err);
++                                      }
++                                      break;
++                              default:
++                                      int_error();
++                                      printk(KERN_WARNING "mISDNd: addr(%x) prim(%x) unknown\n",
++                                              hhe->addr, hhe->prim);
++                                      err = -EINVAL;
++                                      break;
++                      }
++                      if (err)
++                              kfree_skb(skb);
++                      test_and_clear_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags);
++              }
++              if (test_and_clear_bit(mISDN_TFLAGS_TEST, &hkt->Flags))
++                      printk(KERN_DEBUG "mISDNd: test event done\n");
++      }
++      
++      printk(KERN_DEBUG "mISDNd: daemon exit now\n");
++      test_and_clear_bit(mISDN_TFLAGS_STARTED, &hkt->Flags);
++      test_and_clear_bit(mISDN_TFLAGS_ACTIV, &hkt->Flags);
++      discard_queue(&hkt->workq);
++      hkt->thread = NULL;
++      if (hkt->notify != NULL)
++              up(hkt->notify);
++      return(0);
++}
++
++mISDNobject_t *
++get_object(int id) {
++      mISDNobject_t   *obj;
++
++      read_lock(&mISDN_objects_lock);
++      list_for_each_entry(obj, &mISDN_objectlist, list)
++              if (obj->id == id) {
++                      read_unlock(&mISDN_objects_lock);
++                      return(obj);
++              }
++      read_unlock(&mISDN_objects_lock);
++      return(NULL);
++}
++
++static mISDNobject_t *
++find_object(int protocol) {
++      mISDNobject_t   *obj;
++      int             err;
++
++      read_lock(&mISDN_objects_lock);
++      list_for_each_entry(obj, &mISDN_objectlist, list) {
++              err = obj->own_ctrl(NULL, MGR_HASPROTOCOL | REQUEST, &protocol);
++              if (!err)
++                      goto unlock;
++              if (err != -ENOPROTOOPT) {
++                      if (0 == mISDN_HasProtocol(obj, protocol))
++                              goto unlock;
++              }       
++      }
++      obj = NULL;
++unlock:
++      read_unlock(&mISDN_objects_lock);
++      return(obj);
++}
++
++static mISDNobject_t *
++find_object_module(int protocol) {
++#ifdef CONFIG_KMOD
++      int             err;
++#endif
++      moditem_t       *m = modlist;
++      mISDNobject_t   *obj;
++
++      while (m->name != NULL) {
++              if (m->protocol == protocol) {
++#ifdef CONFIG_KMOD
++                      if (debug)
++                              printk(KERN_DEBUG
++                                      "find_object_module %s - trying to load\n",
++                                      m->name);
++                      err=request_module(m->name);
++                      if (debug)
++                              printk(KERN_DEBUG "find_object_module: request_module(%s) returns(%d)\n",
++                                      m->name, err);
++#else
++                      printk(KERN_WARNING "not possible to autoload %s please try to load manually\n",
++                              m->name);
++#endif
++                      if ((obj = find_object(protocol)))
++                              return(obj);
++              }
++              m++;
++      }
++      if (debug)
++              printk(KERN_DEBUG "%s: no module for protocol %x found\n",
++                      __FUNCTION__, protocol);
++      return(NULL);
++}
++
++static void
++remove_object(mISDNobject_t *obj) {
++      mISDNstack_t *st, *nst;
++      mISDNlayer_t *layer, *nl;
++      mISDNinstance_t *inst;
++
++      list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) {
++              list_for_each_entry_safe(layer, nl, &st->layerlist, list) {
++                      inst = layer->inst;
++                      if (inst && inst->obj == obj)
++                              inst->obj->own_ctrl(st, MGR_RELEASE | INDICATION, inst);
++              }
++      }
++}
++
++static int
++dummy_if(mISDNif_t *hif, struct sk_buff *skb)
++{
++      if (!skb) {
++              printk(KERN_WARNING "%s: hif(%p) without skb\n",
++                      __FUNCTION__, hif);
++              return(-EINVAL);
++      }
++      if (debug & DEBUG_DUMMY_FUNC) {
++              mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++
++              printk(KERN_DEBUG "%s: hif(%p) skb(%p) len(%d) prim(%x)\n",
++                      __FUNCTION__, hif, skb, skb->len, hh->prim);
++      }
++      dev_kfree_skb_any(skb);
++      return(0);
++}
++
++mISDNinstance_t *
++get_next_instance(mISDNstack_t *st, mISDN_pid_t *pid)
++{
++      int             err;
++      mISDNinstance_t *next;
++      int             layer, proto;
++      mISDNobject_t   *obj;
++
++      layer = mISDN_get_lowlayer(pid->layermask);
++      proto = pid->protocol[layer];
++      next = get_instance(st, layer, proto);
++      if (!next) {
++              obj = find_object(proto);
++              if (!obj)
++                      obj = find_object_module(proto);
++              if (!obj) {
++                      if (debug)
++                              printk(KERN_WARNING "%s: no object found\n",
++                                      __FUNCTION__);
++                      return(NULL);
++              }
++              err = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, pid);
++              if (err) {
++                      printk(KERN_WARNING "%s: newlayer err(%d)\n",
++                              __FUNCTION__, err);
++                      return(NULL);
++              }
++              next = get_instance(st, layer, proto);
++      }
++      return(next);
++}
++
++static int
++sel_channel(mISDNstack_t *st, channel_info_t *ci)
++{
++      int     err = -EINVAL;
++
++      if (!ci)
++              return(err);
++      if (debug)
++              printk(KERN_DEBUG "%s: st(%p) st->mgr(%p)\n",
++                      __FUNCTION__, st, st->mgr);
++      if (st->mgr) {
++              if (st->mgr->obj && st->mgr->obj->own_ctrl) {
++                      err = st->mgr->obj->own_ctrl(st->mgr, MGR_SELCHANNEL | REQUEST, ci);
++                      if (debug)
++                              printk(KERN_DEBUG "%s: MGR_SELCHANNEL(%d)\n", __FUNCTION__, err);
++              } else
++                      int_error();
++      } else {
++              printk(KERN_WARNING "%s: no mgr st(%p)\n", __FUNCTION__, st);
++      }
++      if (err) {
++              mISDNstack_t    *cst;
++              u_int           nr = 0;
++
++              ci->st.p = NULL;
++              if (!(ci->channel & (~CHANNEL_NUMBER))) {
++                      /* only number is set */
++                      struct list_head        *head;
++                      if (list_empty(&st->childlist)) {
++                              if ((st->id & FLG_CLONE_STACK) &&
++                                      (st->childlist.prev != &st->childlist)) {
++                                      head = st->childlist.prev;
++                              } else {
++                                      printk(KERN_WARNING "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n",
++                                              __FUNCTION__, st->id, st->childlist.prev, &st->childlist, st->childlist.next);
++                                      return(err);
++                              }
++                      } else
++                              head = &st->childlist;
++                      list_for_each_entry(cst, head, list) {
++                              nr++;
++                              if (nr == (ci->channel & 3)) {
++                                      ci->st.p = cst;
++                                      return(0);
++                              }
++                      }
++              }
++      }
++      return(err);
++}
++
++static int
++disconnect_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) {
++      int     err = 0;
++
++      if (hif) {
++              hif->stat = IF_NOACTIV;
++              hif->func = dummy_if;
++              hif->peer = NULL;
++              hif->fdata = NULL;
++      }
++      if (inst)
++              err = inst->obj->own_ctrl(inst, prim, hif);
++      return(err);
++}
++
++static int
++add_if(mISDNinstance_t *inst, u_int prim, mISDNif_t *hif) {
++      mISDNif_t *myif;
++
++      if (!inst)
++              return(-EINVAL);
++      if (!hif)
++              return(-EINVAL);
++      if (hif->stat & IF_UP) {
++              myif = &inst->down;
++      } else if (hif->stat & IF_DOWN) {
++              myif = &inst->up;
++      } else
++              return(-EINVAL);
++      while(myif->clone)
++              myif = myif->clone;
++      myif->clone = hif;
++      hif->predecessor = myif;
++      inst->obj->own_ctrl(inst, prim, hif);
++      return(0);
++}
++
++static char tmpbuf[4096];
++static int
++debugout(mISDNinstance_t *inst, logdata_t *log)
++{
++      char *p = tmpbuf;
++
++      if (log->head && *log->head)
++              p += sprintf(p,"%s ", log->head);
++      else
++              p += sprintf(p,"%s ", inst->obj->name);
++      p += vsprintf(p, log->fmt, log->args);
++      printk(KERN_DEBUG "%s\n", tmpbuf);
++      return(0);
++}
++
++static int
++get_hdevice(mISDNdevice_t **dev, int *typ)
++{
++      if (!dev)
++              return(-EINVAL);
++      if (!typ)
++              return(-EINVAL);
++      if (*typ == mISDN_RAW_DEVICE) {
++              *dev = get_free_rawdevice();
++              if (!(*dev))
++                      return(-ENODEV);
++              return(0);
++      }
++      return(-EINVAL);
++}
++
++static int
++mgr_queue(void *data, u_int prim, struct sk_buff *skb)
++{
++      mISDN_headext_t *hhe = mISDN_HEADEXT_P(skb);
++
++      hhe->addr = prim;
++      skb_queue_tail(&mISDN_thread.workq, skb);
++      wake_up_interruptible(&mISDN_thread.waitq);
++      return(0);
++}
++
++static int central_manager(void *, u_int, void *);
++
++static int
++set_stack_req(mISDNstack_t *st, mISDN_pid_t *pid)
++{
++      struct sk_buff  *skb;
++      mISDN_headext_t *hhe;
++      mISDN_pid_t     *npid;
++      u_char          *pbuf = NULL;
++
++      skb = alloc_skb(sizeof(mISDN_pid_t) + pid->maxplen, GFP_ATOMIC);
++      hhe = mISDN_HEADEXT_P(skb);
++      hhe->prim = MGR_SETSTACK_NW | REQUEST;
++      hhe->addr = MGR_FUNCTION;
++      hhe->data[0] = st;
++      npid = (mISDN_pid_t *)skb_put(skb, sizeof(mISDN_pid_t));
++      if (pid->pbuf)
++              pbuf = skb_put(skb, pid->maxplen);
++      copy_pid(npid, pid, pbuf);
++      hhe->func.ctrl = central_manager;
++      skb_queue_tail(&mISDN_thread.workq, skb);
++      wake_up_interruptible(&mISDN_thread.waitq);
++      return(0);
++}
++
++int
++mISDN_alloc_entity(int *entity)
++{
++      u_long  flags;
++
++      spin_lock_irqsave(&entity_lock, flags);
++      *entity = 1;
++      while(*entity < MISDN_MAX_ENTITY) {
++              if (!test_and_set_bit(*entity, (u_long *)&entityarray[0]))
++                      break;
++              (*entity)++;
++      }
++      spin_unlock_irqrestore(&entity_lock, flags);
++      if (*entity == MISDN_MAX_ENTITY)
++              return(-EBUSY);
++      return(0);
++}
++
++int
++mISDN_delete_entity(int entity)
++{
++      u_long  flags;
++      int     ret = 0;
++
++      spin_lock_irqsave(&entity_lock, flags);
++      if (!test_and_clear_bit(entity, (u_long *)&entityarray[0])) {
++              printk(KERN_WARNING "mISDN: del_entity(%d) but entity not allocated\n", entity);
++              ret = -ENODEV;
++      }
++      spin_unlock_irqrestore(&entity_lock, flags);
++      return(ret);
++}
++
++static int
++new_entity(mISDNinstance_t *inst)
++{
++      int     entity;
++      int     ret;
++
++      if (!inst)
++              return(-EINVAL);
++      ret = mISDN_alloc_entity(&entity);
++      if (ret) {
++              printk(KERN_WARNING "mISDN: no more entity available(max %d)\n", MISDN_MAX_ENTITY);
++              return(ret);
++      }
++      ret = inst->obj->own_ctrl(inst, MGR_NEWENTITY | CONFIRM, (void *)entity);
++      if (ret)
++              mISDN_delete_entity(entity);
++      return(ret);
++}
++
++static int central_manager(void *data, u_int prim, void *arg) {
++      mISDNstack_t *st = data;
++
++      switch(prim) {
++          case MGR_NEWSTACK | REQUEST:
++              if (!(st = new_stack(data, arg)))
++                      return(-EINVAL);
++              return(0);
++          case MGR_NEWENTITY | REQUEST:
++              return(new_entity(data));
++          case MGR_DELENTITY | REQUEST:
++          case MGR_DELENTITY | INDICATION:
++              return(mISDN_delete_entity((int)arg));
++          case MGR_REGLAYER | INDICATION:
++              return(register_layer(st, arg));
++          case MGR_REGLAYER | REQUEST:
++              if (!register_layer(st, arg)) {
++                      mISDNinstance_t *inst = arg;
++                      return(inst->obj->own_ctrl(arg, MGR_REGLAYER | CONFIRM, NULL));
++              }
++              return(-EINVAL);
++          case MGR_UNREGLAYER | REQUEST:
++              return(unregister_instance(data));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(disconnect_if(data, prim, arg));
++          case MGR_GETDEVICE | REQUEST:
++              return(get_hdevice(data, arg));
++          case MGR_DELDEVICE | REQUEST:
++              return(free_device(data));
++          case MGR_QUEUEIF | REQUEST:
++              return(mgr_queue(data, MGR_QUEUEIF, arg));
++      }
++      if (!data)
++              return(-EINVAL);
++      switch(prim) {
++          case MGR_SETSTACK | REQUEST:
++              /* can sleep in case of module reload */
++              return(set_stack_req(st, arg));
++          case MGR_SETSTACK_NW | REQUEST:
++              return(set_stack(st, arg));
++          case MGR_CLEARSTACK | REQUEST:
++              return(clear_stack(st));
++          case MGR_DELSTACK | REQUEST:
++              return(release_stack(st));
++          case MGR_SELCHANNEL | REQUEST:
++              return(sel_channel(st, arg));
++          case MGR_ADDIF | REQUEST:
++              return(add_if(data, prim, arg));
++          case MGR_CTRLREADY | INDICATION:
++              return(do_for_all_layers(st, prim, arg));
++          case MGR_ADDSTPARA | REQUEST:
++          case MGR_CLRSTPARA | REQUEST:
++              return(change_stack_para(st, prim, arg));
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(data, arg));
++          case MGR_EVALSTACK  | REQUEST:
++              return(evaluate_stack_pids(data, arg));
++          case MGR_GLOBALOPT | REQUEST:
++          case MGR_LOADFIRM | REQUEST:
++              if (st->mgr && st->mgr->obj && st->mgr->obj->own_ctrl)
++                      return(st->mgr->obj->own_ctrl(st->mgr, prim, arg));
++              break;
++          case MGR_DEBUGDATA | REQUEST:
++              return(debugout(data, arg));
++          default:
++              if (debug)
++                      printk(KERN_WARNING "manager prim %x not handled\n", prim);
++              break;
++      }
++      return(-EINVAL);
++}
++
++int mISDN_register(mISDNobject_t *obj) {
++      u_long  flags;
++
++      if (!obj)
++              return(-EINVAL);
++      write_lock_irqsave(&mISDN_objects_lock, flags);
++      obj->id = obj_id++;
++      list_add_tail(&obj->list, &mISDN_objectlist);
++      write_unlock_irqrestore(&mISDN_objects_lock, flags);
++      obj->ctrl = central_manager;
++      // register_prop
++      if (debug)
++              printk(KERN_DEBUG "mISDN_register %s id %x\n", obj->name,
++                      obj->id);
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "mISDN_register: obj(%p)\n", obj);
++      return(0);
++}
++
++int mISDN_unregister(mISDNobject_t *obj) {
++      u_long  flags;
++
++      if (!obj)
++              return(-EINVAL);
++      if (debug)
++              printk(KERN_DEBUG "mISDN_unregister %s %d refs\n",
++                      obj->name, obj->refcnt);
++      if (obj->DPROTO.protocol[0])
++              release_stacks(obj);
++      else
++              remove_object(obj);
++      write_lock_irqsave(&mISDN_objects_lock, flags);
++      list_del(&obj->list);
++      write_unlock_irqrestore(&mISDN_objects_lock, flags);
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "mISDN_unregister: mISDN_objectlist(%p<-%p->%p)\n",
++                      mISDN_objectlist.prev, &mISDN_objectlist, mISDN_objectlist.next);
++      return(0);
++}
++
++int
++mISDNInit(void)
++{
++      DECLARE_MUTEX_LOCKED(sem);
++      int err;
++
++      printk(KERN_INFO "Modular ISDN Stack core %s\n", mISDN_core_revision);
++      core_debug = debug;
++#ifdef MISDN_MEMDEBUG
++      err = __mid_init();
++      if (err)
++              return(err);
++#endif
++      err = init_mISDNdev(debug);
++      if (err)
++              return(err);
++      init_waitqueue_head(&mISDN_thread.waitq);
++      skb_queue_head_init(&mISDN_thread.workq);
++      mISDN_thread.notify = &sem;
++      kernel_thread(mISDNd, (void *)&mISDN_thread, 0);
++      down(&sem);
++      mISDN_thread.notify = NULL;
++      test_and_set_bit(mISDN_TFLAGS_TEST, &mISDN_thread.Flags);
++      wake_up_interruptible(&mISDN_thread.waitq);
++      return(err);
++}
++
++void mISDN_cleanup(void) {
++      DECLARE_MUTEX_LOCKED(sem);
++      mISDNstack_t *st, *nst;
++
++      free_mISDNdev();
++      if (!list_empty(&mISDN_objectlist)) {
++              printk(KERN_WARNING "mISDNcore mISDN_objects not empty\n");
++      }
++      if (!list_empty(&mISDN_stacklist)) {
++              printk(KERN_WARNING "mISDNcore mISDN_stacklist not empty\n");
++              list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) {
++                      printk(KERN_WARNING "mISDNcore st %x still in list\n",
++                              st->id);
++                      if (list_empty(&st->list)) {
++                              printk(KERN_WARNING "mISDNcore st == next\n");
++                              break;
++                      }
++              }
++      }
++      if (mISDN_thread.thread) {
++              /* abort mISDNd kernel thread */
++              mISDN_thread.notify = &sem;
++              test_and_set_bit(mISDN_TFLAGS_RMMOD, &mISDN_thread.Flags);
++              wake_up_interruptible(&mISDN_thread.waitq);
++              down(&sem);
++              mISDN_thread.notify = NULL;
++      }
++#ifdef MISDN_MEMDEBUG
++      __mid_cleanup();
++#endif
++      printk(KERN_DEBUG "mISDNcore unloaded\n");
++}
++
++module_init(mISDNInit);
++module_exit(mISDN_cleanup);
++
++EXPORT_SYMBOL(mISDN_register);
++EXPORT_SYMBOL(mISDN_unregister);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/core.h       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/core.h   2004-11-22 09:33:37.954773912 +0000
+@@ -0,0 +1,67 @@
++/* $Id$
++ * 
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/mISDNif.h>
++#include "helper.h"
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++#define       mISDN_MAJOR             46
++#define mISDN_MINOR_CORE      0
++#define mISDN_MINOR_RAW_MIN   128
++#define mISDN_MINOR_RAW_MAX   255
++
++/* debugging */
++#define DEBUG_CORE_FUNC               0x0001
++#define DEBUG_DUMMY_FUNC      0x0002
++#define DEBUG_DEV_OP          0x0100
++#define DEBUG_MGR_FUNC                0x0200
++#define DEBUG_DEV_TIMER               0x0400
++#define DEBUG_RDATA           0x1000
++#define DEBUG_WDATA           0x2000
++
++/* from mISDN_dev.c */
++
++extern int            init_mISDNdev(int);
++extern int            free_mISDNdev(void);
++extern mISDNdevice_t  *get_free_rawdevice(void);
++extern int            free_device(mISDNdevice_t *dev);
++
++/* from mISDN_stack.c */
++
++extern struct list_head       mISDN_stacklist;
++extern struct list_head       mISDN_instlist;
++
++extern void           get_stack_info(struct sk_buff *);
++extern int            get_stack_cnt(void);
++extern mISDNstack_t   *get_stack4id(u_int);
++extern mISDNstack_t   *new_stack(mISDNstack_t *, mISDNinstance_t *);
++extern int            release_stack(mISDNstack_t *);
++extern int            do_for_all_layers(mISDNstack_t *, u_int, void *);
++extern int            change_stack_para(mISDNstack_t *, u_int, mISDN_stPara_t *);
++extern void           release_stacks(mISDNobject_t *);
++extern int            copy_pid(mISDN_pid_t *, mISDN_pid_t *, u_char *);
++extern int            set_stack(mISDNstack_t *, mISDN_pid_t *);
++extern int            clear_stack(mISDNstack_t *);
++extern int            evaluate_stack_pids(mISDNstack_t *, mISDN_pid_t *);
++extern mISDNlayer_t   *getlayer4lay(mISDNstack_t *, int);
++extern mISDNinstance_t        *get_instance(mISDNstack_t *, int, int);
++
++/* from mISDN_core.c */
++
++extern struct list_head       mISDN_objectlist;
++extern int core_debug;
++
++extern int            register_layer(mISDNstack_t *, mISDNinstance_t *);
++extern int            unregister_instance(mISDNinstance_t *);
++extern mISDNinstance_t        *get_next_instance(mISDNstack_t *, mISDN_pid_t *);
++extern mISDNobject_t  *get_object(int);
++extern mISDNinstance_t        *get_instance4id(u_int);
++extern int            mISDN_alloc_entity(int *);
++extern int            mISDN_delete_entity(int);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.c       2004-11-22 09:33:37.964772392 +0000
+@@ -0,0 +1,116 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mISDNif.h>
++#include "layer1.h"
++#include "helper.h"
++#include "dchannel.h"
++
++static void
++dchannel_bh(dchannel_t *dch)
++{
++      struct sk_buff  *skb;
++      int             err;
++      mISDN_head_t    *hh;
++
++      if (!dch)
++              return;
++      if (dch->debug)
++              printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event);
++#if 0
++      if (test_and_clear_bit(D_CLEARBUSY, &dch->event)) {
++              if (dch->debug)
++                      mISDN_debugprint(&dch->inst, "D-Channel Busy cleared");
++              stptr = dch->stlist;
++              while (stptr != NULL) {
++                      stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
++                      stptr = stptr->next;
++              }
++      }
++#endif
++      if (test_and_clear_bit(D_XMTBUFREADY, &dch->event)) {
++              if ((skb = dch->next_skb)) {
++                      hh = mISDN_HEAD_P(skb);
++                      dch->next_skb = NULL;
++                      skb_trim(skb, 0);
++                      if (if_newhead(&dch->inst.up, PH_DATA_CNF, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++
++      if (test_and_clear_bit(D_RCVBUFREADY, &dch->event)) {
++              while ((skb = skb_dequeue(&dch->rqueue))) {
++                      err = if_newhead(&dch->inst.up, PH_DATA_IND, MISDN_ID_ANY, skb);
++                      if (err < 0) {
++                              printk(KERN_WARNING "%s: deliver err %d\n", __FUNCTION__, err);
++                              dev_kfree_skb(skb);
++                      }
++              }
++      }
++
++      if (dch->hw_bh)
++              dch->hw_bh(dch);
++}
++
++int
++mISDN_init_dch(dchannel_t *dch) {
++      if (!(dch->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
++              printk(KERN_WARNING
++                      "mISDN: No memory for dlog\n");
++              return(-ENOMEM);
++      }
++      if (!(dch->tx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
++              printk(KERN_WARNING
++                      "mISDN: No memory for dchannel tx_buf\n");
++              kfree(dch->dlog);
++              dch->dlog = NULL;
++              return(-ENOMEM);
++      }
++      dch->hw = NULL;
++      dch->rx_skb = NULL;
++      dch->tx_idx = 0;
++      dch->next_skb = NULL;
++      dch->event = 0;
++      INIT_WORK(&dch->work, (void *)(void *)dchannel_bh, dch);
++      dch->hw_bh = NULL;
++      skb_queue_head_init(&dch->rqueue);
++      return(0);
++}
++
++int
++mISDN_free_dch(dchannel_t *dch) {
++#ifdef HAS_WORKQUEUE
++      if (dch->work.pending)
++              printk(KERN_ERR "mISDN_free_dch work:(%lx)\n", dch->work.pending);
++#else
++      if (dch->work.sync)
++              printk(KERN_ERR "mISDN_free_dch work:(%lx)\n", dch->work.sync);
++#endif
++      discard_queue(&dch->rqueue);
++      if (dch->rx_skb) {
++              dev_kfree_skb(dch->rx_skb);
++              dch->rx_skb = NULL;
++      }
++      if (dch->tx_buf) {
++              kfree(dch->tx_buf);
++              dch->tx_buf = NULL;
++      }
++      if (dch->next_skb) {
++              dev_kfree_skb(dch->next_skb);
++              dch->next_skb = NULL;
++      }
++      if (dch->dlog) {
++              kfree(dch->dlog);
++              dch->dlog = NULL;
++      }
++      return(0);
++}
++
++EXPORT_SYMBOL(mISDN_init_dch);
++EXPORT_SYMBOL(mISDN_free_dch);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dchannel.h   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dchannel.h       2004-11-22 09:33:37.974770872 +0000
+@@ -0,0 +1,92 @@
++/* $Id$
++ *
++ *   Basic declarations for dchannel HW
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/mISDNif.h>
++#ifdef HAS_WORKQUEUE
++#include <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#endif
++#include <linux/smp.h>
++#include <linux/ptrace.h>
++#include <linux/interrupt.h>
++#include <linux/timer.h>
++#include <asm/io.h>
++#include <linux/ioport.h>
++#include <linux/skbuff.h>
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++#define MAX_DFRAME_LEN_L1     300
++#define MAX_MON_FRAME         32
++#define MAX_DLOG_SPACE                2048
++
++#define FLG_TWO_DCHAN         4
++#define FLG_TX_BUSY           5
++#define FLG_TX_NEXT           6
++#define FLG_L1_DBUSY          7
++#define FLG_DBUSY_TIMER       8
++#define FLG_LOCK_ATOMIC       9
++#define FLG_ARCOFI_TIMER      10
++#define FLG_ARCOFI_ERROR      11
++#define FLG_HW_L1_UINT                12
++#define FLG_HW_INIT           13
++
++typedef struct _dchannel_t {
++      int                     channel;
++      mISDNinstance_t         inst;
++      u_long                  DFlags;
++      u_int                   type;
++      u_int                   ph_state;
++      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);
++      char                    *dlog;
++      int                     debug;
++      struct sk_buff          *rx_skb;
++      struct sk_buff          *next_skb;
++      u_char                  *tx_buf;
++      int                     tx_idx;
++      int                     tx_len;
++      int                     up_headerlen;
++      int                     err_crc;
++      int                     err_tx;
++      int                     err_rx;
++      void                    *hw;
++      struct timer_list       dbusytimer;
++      u_long                  event;
++      struct sk_buff_head     rqueue; /* D-channel receive queue */
++      struct work_struct      work;
++      void                    (*hw_bh) (struct _dchannel_t *);
++} dchannel_t;
++
++#define MON0_RX       1
++#define MON1_RX       2
++#define MON0_TX       4
++#define MON1_TX       8
++
++extern int mISDN_init_dch(dchannel_t *);
++extern int mISDN_free_dch(dchannel_t *);
++
++static inline void
++dch_set_para(dchannel_t *dch, mISDN_stPara_t *stp)
++{
++      if (stp)
++              dch->up_headerlen = stp->up_headerlen;
++      else
++              dch->up_headerlen = 0;
++}
++
++static inline void
++dchannel_sched_event(dchannel_t *dch, int event)
++{
++      test_and_set_bit(event, &dch->event);
++      schedule_work(&dch->work);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.c      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.c  2004-11-22 09:33:37.984769352 +0000
+@@ -0,0 +1,98 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mISDNif.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include "debug.h"
++
++#define mISDN_STATUS_BUFSIZE 4096
++
++static char tmpbuf[mISDN_STATUS_BUFSIZE];
++
++void
++vmISDN_debug(int id, char *head, char *fmt, va_list args)
++{
++/* if head == NULL the fmt contains the full info */
++      char *p = tmpbuf;
++
++      p += sprintf(p,"%d ", id);
++      if (head)
++              p += sprintf(p, "%s ", head);
++      p += vsprintf(p, fmt, args);
++      printk(KERN_DEBUG "%s\n", tmpbuf);
++} 
++
++void
++mISDN_debug(int id, char *head, char *fmt, ...)
++{
++      va_list args;
++
++      va_start(args, fmt);
++      vmISDN_debug(id, head, fmt, args);
++      va_end(args);
++}
++
++void
++mISDN_debugprint(mISDNinstance_t *inst, char *fmt, ...)
++{
++      logdata_t log;
++
++      va_start(log.args, fmt);
++      log.head = inst->name;
++      log.fmt = fmt;
++      inst->obj->ctrl(inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++char *
++mISDN_getrev(const char *revision)
++{
++      char *rev;
++      char *p;
++
++      if ((p = strchr(revision, ':'))) {
++              rev = p + 2;
++              p = strchr(rev, '$');
++              if (p)
++                      *--p = 0;
++      } else
++              rev = "???";
++      return rev;
++}
++
++int
++mISDN_QuickHex(char *txt, u_char * p, int cnt)
++{
++      register int i;
++      register char *t = txt;
++      register u_char w;
++
++      for (i = 0; i < cnt; i++) {
++              *t++ = ' ';
++              w = (p[i] >> 4) & 0x0f;
++              if (w < 10)
++                      *t++ = '0' + w;
++              else
++                      *t++ = 'A' - 10 + w;
++              w = p[i] & 0x0f;
++              if (w < 10)
++                      *t++ = '0' + w;
++              else
++                      *t++ = 'A' - 10 + w;
++      }
++      *t++ = 0;
++      return (t - txt);
++}
++
++EXPORT_SYMBOL(vmISDN_debug);
++EXPORT_SYMBOL(mISDN_debug);
++EXPORT_SYMBOL(mISDN_getrev);
++EXPORT_SYMBOL(mISDN_debugprint);
++EXPORT_SYMBOL(mISDN_QuickHex);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/debug.h      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/debug.h  2004-11-22 09:33:37.994767832 +0000
+@@ -0,0 +1,13 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++extern void vmISDN_debug(int id, char *head, char *fmt, va_list args);
++extern void mISDN_debug(int id, char *head, char *fmt, ...);
++extern char * mISDN_getrev(const char *revision);
++extern void mISDN_debugprint(mISDNinstance_t *inst, char *fmt, ...);
++extern int mISDN_QuickHex(char *, u_char *, int);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp.h        1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp.h    2004-11-22 09:33:38.005766160 +0000
+@@ -0,0 +1,231 @@
++/* $Id$
++ *
++ * Audio support data for ISDN4Linux.
++ *
++ * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++//#define AUTOJITTER
++
++#define DEBUG_DSP_MGR         0x0001
++#define DEBUG_DSP_CORE                0x0002
++#define DEBUG_DSP_DTMF                0x0004
++#define DEBUG_DSP_DTMFCOEFF   0x0008
++#define DEBUG_DSP_CMX         0x0010
++#define DEBUG_DSP_TONE                0x0020
++#define DEBUG_DSP_BLOWFISH    0x0040
++#define DEBUG_DSP_DELAY               0x0080
++
++/* 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)
++
++#ifdef HAS_WORKQUEUE
++#include <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#endif
++#include <linux/timer.h>
++
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++extern int dsp_options;
++extern int dsp_debug;
++
++/***************
++ * 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_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 silence;
++
++
++/*************
++ * cmx stuff *
++ *************/
++
++#define CMX_BUFF_SIZE 0x4000  /* must be 2**n */
++#define CMX_BUFF_HALF 0x2000  /* CMX_BUFF_SIZE / 2 */
++#define CMX_BUFF_MASK 0x3fff  /* CMX_BUFF_SIZE - 1 */
++
++/* 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;
++typedef struct _conf_member {
++      struct list_head        list;
++      struct _dsp             *dsp;
++} conf_member_t;
++
++/* the list of all conferences */
++typedef struct _conference {
++      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 */
++//#ifndef AUTOJITTER
++      int                     largest; /* largest frame received in conf's life. */
++//#endif
++      int                     W_min, W_max; /* min/maximum rx-write pointer of members */
++      s32                     conf_buff[CMX_BUFF_SIZE];
++} conference_t;
++
++extern mISDNobject_t dsp_obj;
++
++
++/**************
++ * DTMF stuff *
++ **************/
++
++#define DSP_DTMF_NPOINTS 102
++
++typedef struct _dtmf_t {
++      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 */
++} dtmf_t;
++
++
++/***************
++ * tones stuff *
++ ***************/
++
++typedef struct _tone_t {
++      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;
++} tone_t;
++
++/*****************
++ * general stuff *
++ *****************/
++
++#define DELAY_CHECK 8000
++
++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             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 */
++};            
++
++typedef struct _dsp {
++      struct list_head list;
++      mISDNinstance_t inst;
++      int             b_active;
++      int             echo; /* echo is done by software */
++      int             rx_disabled;
++      int             tx_mix;
++      tone_t          tone;
++      dtmf_t          dtmf;
++      int             tx_volume, rx_volume;
++      struct work_struct sendwork; /* event for sending data */
++      int             tx_pending;
++
++      /* conference stuff */
++      u32             conf_id;
++      conference_t    *conf;
++      conf_member_t   *member;
++
++      /* buffer stuff */
++      int             largest; /* largest frame received in dsp's life. */
++      int             R_tx, W_tx; /* pointers of transmit buffer */
++      int             R_rx, W_rx; /* pointers of receive buffer and conference buffer */
++      u8              tx_buff[CMX_BUFF_SIZE];
++      u8              rx_buff[CMX_BUFF_SIZE];
++#ifdef AUTOJITTER
++      int             tx_delay; /* used to find the delay of tx buffer */
++      int             tx_delay_count;
++      int             rx_delay; /* used to find the delay of rx buffer */
++      int             rx_delay_count;
++#endif
++
++      /* hardware stuff */
++      struct dsp_features features; /* features */
++      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;
++} dsp_t;
++
++/* functions */
++
++extern void dsp_change_volume(struct sk_buff *skb, int volume);
++
++extern struct list_head Conf_list;
++extern void dsp_cmx_debug(dsp_t *dsp);
++extern void dsp_cmx_hardware(conference_t *conf, dsp_t *dsp);
++extern int dsp_cmx_conf(dsp_t *dsp, u32 conf_id);
++extern void dsp_cmx_receive(dsp_t *dsp, struct sk_buff *skb);
++extern struct sk_buff *dsp_cmx_send(dsp_t *dsp, int len, int dinfo);
++extern void dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb);
++extern int dsp_cmx_del_conf_member(dsp_t *dsp);
++extern int dsp_cmx_del_conf(conference_t *conf);
++
++extern void dsp_dtmf_goertzel_init(dsp_t *dsp);
++extern u8 *dsp_dtmf_goertzel_decode(dsp_t *dsp, u8 *data, int len, int fmt);
++
++extern int dsp_tone(dsp_t *dsp, int tone);
++extern void dsp_tone_copy(dsp_t *dsp, u8 *data, int len);
++extern void dsp_tone_timeout(void *arg);
++
++extern void dsp_bf_encrypt(dsp_t *dsp, u8 *data, int len);
++extern void dsp_bf_decrypt(dsp_t *dsp, u8 *data, int len);
++extern int dsp_bf_init(dsp_t *dsp, const u8 *key, unsigned int keylen);
++extern void dsp_bf_cleanup(dsp_t *dsp);
++
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_audio.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_audio.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_audio.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_audio.c      2004-11-22 09:33:38.015764640 +0000
+@@ -0,0 +1,583 @@
++/* $Id$
++ *
++ * Audio support data for mISDN_dsp.
++ *
++ * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++#include "dsp.h"
++
++/* ulaw[unsigned char] -> signed 16-bit */
++s32 dsp_audio_ulaw_to_s32[256] =
++{
++      0xffff8284, 0xffff8684, 0xffff8a84, 0xffff8e84,
++      0xffff9284, 0xffff9684, 0xffff9a84, 0xffff9e84,
++      0xffffa284, 0xffffa684, 0xffffaa84, 0xffffae84,
++      0xffffb284, 0xffffb684, 0xffffba84, 0xffffbe84,
++      0xffffc184, 0xffffc384, 0xffffc584, 0xffffc784,
++      0xffffc984, 0xffffcb84, 0xffffcd84, 0xffffcf84,
++      0xffffd184, 0xffffd384, 0xffffd584, 0xffffd784,
++      0xffffd984, 0xffffdb84, 0xffffdd84, 0xffffdf84,
++      0xffffe104, 0xffffe204, 0xffffe304, 0xffffe404,
++      0xffffe504, 0xffffe604, 0xffffe704, 0xffffe804,
++      0xffffe904, 0xffffea04, 0xffffeb04, 0xffffec04,
++      0xffffed04, 0xffffee04, 0xffffef04, 0xfffff004,
++      0xfffff0c4, 0xfffff144, 0xfffff1c4, 0xfffff244,
++      0xfffff2c4, 0xfffff344, 0xfffff3c4, 0xfffff444,
++      0xfffff4c4, 0xfffff544, 0xfffff5c4, 0xfffff644,
++      0xfffff6c4, 0xfffff744, 0xfffff7c4, 0xfffff844,
++      0xfffff8a4, 0xfffff8e4, 0xfffff924, 0xfffff964,
++      0xfffff9a4, 0xfffff9e4, 0xfffffa24, 0xfffffa64,
++      0xfffffaa4, 0xfffffae4, 0xfffffb24, 0xfffffb64,
++      0xfffffba4, 0xfffffbe4, 0xfffffc24, 0xfffffc64,
++      0xfffffc94, 0xfffffcb4, 0xfffffcd4, 0xfffffcf4,
++      0xfffffd14, 0xfffffd34, 0xfffffd54, 0xfffffd74,
++      0xfffffd94, 0xfffffdb4, 0xfffffdd4, 0xfffffdf4,
++      0xfffffe14, 0xfffffe34, 0xfffffe54, 0xfffffe74,
++      0xfffffe8c, 0xfffffe9c, 0xfffffeac, 0xfffffebc,
++      0xfffffecc, 0xfffffedc, 0xfffffeec, 0xfffffefc,
++      0xffffff0c, 0xffffff1c, 0xffffff2c, 0xffffff3c,
++      0xffffff4c, 0xffffff5c, 0xffffff6c, 0xffffff7c,
++      0xffffff88, 0xffffff90, 0xffffff98, 0xffffffa0,
++      0xffffffa8, 0xffffffb0, 0xffffffb8, 0xffffffc0,
++      0xffffffc8, 0xffffffd0, 0xffffffd8, 0xffffffe0,
++      0xffffffe8, 0xfffffff0, 0xfffffff8, 0xffffffff,
++      0x00007d7c, 0x0000797c, 0x0000757c, 0x0000717c,
++      0x00006d7c, 0x0000697c, 0x0000657c, 0x0000617c,
++      0x00005d7c, 0x0000597c, 0x0000557c, 0x0000517c,
++      0x00004d7c, 0x0000497c, 0x0000457c, 0x0000417c,
++      0x00003e7c, 0x00003c7c, 0x00003a7c, 0x0000387c,
++      0x0000367c, 0x0000347c, 0x0000327c, 0x0000307c,
++      0x00002e7c, 0x00002c7c, 0x00002a7c, 0x0000287c,
++      0x0000267c, 0x0000247c, 0x0000227c, 0x0000207c,
++      0x00001efc, 0x00001dfc, 0x00001cfc, 0x00001bfc,
++      0x00001afc, 0x000019fc, 0x000018fc, 0x000017fc,
++      0x000016fc, 0x000015fc, 0x000014fc, 0x000013fc,
++      0x000012fc, 0x000011fc, 0x000010fc, 0x00000ffc,
++      0x00000f3c, 0x00000ebc, 0x00000e3c, 0x00000dbc,
++      0x00000d3c, 0x00000cbc, 0x00000c3c, 0x00000bbc,
++      0x00000b3c, 0x00000abc, 0x00000a3c, 0x000009bc,
++      0x0000093c, 0x000008bc, 0x0000083c, 0x000007bc,
++      0x0000075c, 0x0000071c, 0x000006dc, 0x0000069c,
++      0x0000065c, 0x0000061c, 0x000005dc, 0x0000059c,
++      0x0000055c, 0x0000051c, 0x000004dc, 0x0000049c,
++      0x0000045c, 0x0000041c, 0x000003dc, 0x0000039c,
++      0x0000036c, 0x0000034c, 0x0000032c, 0x0000030c,
++      0x000002ec, 0x000002cc, 0x000002ac, 0x0000028c,
++      0x0000026c, 0x0000024c, 0x0000022c, 0x0000020c,
++      0x000001ec, 0x000001cc, 0x000001ac, 0x0000018c,
++      0x00000174, 0x00000164, 0x00000154, 0x00000144,
++      0x00000134, 0x00000124, 0x00000114, 0x00000104,
++      0x000000f4, 0x000000e4, 0x000000d4, 0x000000c4,
++      0x000000b4, 0x000000a4, 0x00000094, 0x00000084,
++      0x00000078, 0x00000070, 0x00000068, 0x00000060,
++      0x00000058, 0x00000050, 0x00000048, 0x00000040,
++      0x00000038, 0x00000030, 0x00000028, 0x00000020,
++      0x00000018, 0x00000010, 0x00000008, 0x00000000
++};
++
++/* alaw[unsigned char] -> signed 16-bit */
++s32 dsp_audio_alaw_to_s32[256] =
++{
++      0x000013fc, 0xffffec04, 0x00000144, 0xfffffebc,
++      0x0000517c, 0xffffae84, 0x0000051c, 0xfffffae4,
++      0x00000a3c, 0xfffff5c4, 0x00000048, 0xffffffb8,
++      0x0000287c, 0xffffd784, 0x0000028c, 0xfffffd74,
++      0x00001bfc, 0xffffe404, 0x000001cc, 0xfffffe34,
++      0x0000717c, 0xffff8e84, 0x0000071c, 0xfffff8e4,
++      0x00000e3c, 0xfffff1c4, 0x000000c4, 0xffffff3c,
++      0x0000387c, 0xffffc784, 0x0000039c, 0xfffffc64,
++      0x00000ffc, 0xfffff004, 0x00000104, 0xfffffefc,
++      0x0000417c, 0xffffbe84, 0x0000041c, 0xfffffbe4,
++      0x0000083c, 0xfffff7c4, 0x00000008, 0xfffffff8,
++      0x0000207c, 0xffffdf84, 0x0000020c, 0xfffffdf4,
++      0x000017fc, 0xffffe804, 0x0000018c, 0xfffffe74,
++      0x0000617c, 0xffff9e84, 0x0000061c, 0xfffff9e4,
++      0x00000c3c, 0xfffff3c4, 0x00000084, 0xffffff7c,
++      0x0000307c, 0xffffcf84, 0x0000030c, 0xfffffcf4,
++      0x000015fc, 0xffffea04, 0x00000164, 0xfffffe9c,
++      0x0000597c, 0xffffa684, 0x0000059c, 0xfffffa64,
++      0x00000b3c, 0xfffff4c4, 0x00000068, 0xffffff98,
++      0x00002c7c, 0xffffd384, 0x000002cc, 0xfffffd34,
++      0x00001dfc, 0xffffe204, 0x000001ec, 0xfffffe14,
++      0x0000797c, 0xffff8684, 0x000007bc, 0xfffff844,
++      0x00000f3c, 0xfffff0c4, 0x000000e4, 0xffffff1c,
++      0x00003c7c, 0xffffc384, 0x000003dc, 0xfffffc24,
++      0x000011fc, 0xffffee04, 0x00000124, 0xfffffedc,
++      0x0000497c, 0xffffb684, 0x0000049c, 0xfffffb64,
++      0x0000093c, 0xfffff6c4, 0x00000028, 0xffffffd8,
++      0x0000247c, 0xffffdb84, 0x0000024c, 0xfffffdb4,
++      0x000019fc, 0xffffe604, 0x000001ac, 0xfffffe54,
++      0x0000697c, 0xffff9684, 0x0000069c, 0xfffff964,
++      0x00000d3c, 0xfffff2c4, 0x000000a4, 0xffffff5c,
++      0x0000347c, 0xffffcb84, 0x0000034c, 0xfffffcb4,
++      0x000012fc, 0xffffed04, 0x00000134, 0xfffffecc,
++      0x00004d7c, 0xffffb284, 0x000004dc, 0xfffffb24,
++      0x000009bc, 0xfffff644, 0x00000038, 0xffffffc8,
++      0x0000267c, 0xffffd984, 0x0000026c, 0xfffffd94,
++      0x00001afc, 0xffffe504, 0x000001ac, 0xfffffe54,
++      0x00006d7c, 0xffff9284, 0x000006dc, 0xfffff924,
++      0x00000dbc, 0xfffff244, 0x000000b4, 0xffffff4c,
++      0x0000367c, 0xffffc984, 0x0000036c, 0xfffffc94,
++      0x00000f3c, 0xfffff0c4, 0x000000f4, 0xffffff0c,
++      0x00003e7c, 0xffffc184, 0x000003dc, 0xfffffc24,
++      0x000007bc, 0xfffff844, 0x00000008, 0xfffffff8,
++      0x00001efc, 0xffffe104, 0x000001ec, 0xfffffe14,
++      0x000016fc, 0xffffe904, 0x00000174, 0xfffffe8c,
++      0x00005d7c, 0xffffa284, 0x000005dc, 0xfffffa24,
++      0x00000bbc, 0xfffff444, 0x00000078, 0xffffff88,
++      0x00002e7c, 0xffffd184, 0x000002ec, 0xfffffd14,
++      0x000014fc, 0xffffeb04, 0x00000154, 0xfffffeac,
++      0x0000557c, 0xffffaa84, 0x0000055c, 0xfffffaa4,
++      0x00000abc, 0xfffff544, 0x00000058, 0xffffffa8,
++      0x00002a7c, 0xffffd584, 0x000002ac, 0xfffffd54,
++      0x00001cfc, 0xffffe304, 0x000001cc, 0xfffffe34,
++      0x0000757c, 0xffff8a84, 0x0000075c, 0xfffff8a4,
++      0x00000ebc, 0xfffff144, 0x000000d4, 0xffffff2c,
++      0x00003a7c, 0xffffc584, 0x0000039c, 0xfffffc64,
++      0x000010fc, 0xffffef04, 0x00000114, 0xfffffeec,
++      0x0000457c, 0xffffba84, 0x0000045c, 0xfffffba4,
++      0x000008bc, 0xfffff744, 0x00000018, 0xffffffe8,
++      0x0000227c, 0xffffdd84, 0x0000022c, 0xfffffdd4,
++      0x000018fc, 0xffffe704, 0x0000018c, 0xfffffe74,
++      0x0000657c, 0xffff9a84, 0x0000065c, 0xfffff9a4,
++      0x00000cbc, 0xfffff344, 0x00000094, 0xffffff6c,
++      0x0000327c, 0xffffcd84, 0x0000032c, 0xfffffcd4
++};
++
++s32 *dsp_audio_law_to_s32;
++
++/* signed 16-bit -> law */
++u8 dsp_audio_s16_to_law[65536];
++
++/* table is used to generate s16_to_alaw */
++static short dsp_audio_alaw_relations[512] =
++{
++      0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95,
++      0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5,
++      0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85,
++      0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5,
++      0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d,
++      0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd,
++      0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d,
++      0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad,
++      0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91,
++      0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1,
++      0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81,
++      0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59,
++      0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99,
++      0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9,
++      0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89,
++      0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57,
++      0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97,
++      0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7,
++      0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87,
++      0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f,
++      0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f,
++      0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf,
++      0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f,
++      0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53,
++      0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73,
++      0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3,
++      0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83,
++      0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3,
++      0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b,
++      0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb,
++      0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b,
++      0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab,
++      0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a,
++      0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a,
++      0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a,
++      0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a,
++      0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62,
++      0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42,
++      0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72,
++      0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52,
++      0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e,
++      0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e,
++      0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e,
++      0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e,
++      0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66,
++      0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46,
++      0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76,
++      0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56,
++      0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68,
++      0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48,
++      0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78,
++      0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58,
++      0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60,
++      0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40,
++      0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70,
++      0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50,
++      0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c,
++      0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c,
++      0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c,
++      0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c,
++      0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64,
++      0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44,
++      0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74,
++      0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54
++};
++
++
++/* alaw -> ulaw */
++u8 dsp_audio_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 */
++u8 dsp_audio_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
++};
++
++u8 silence;
++
++
++/*****************************************************
++ * generate table for conversion of s16 to alaw/ulaw *
++ *****************************************************/
++
++void
++dsp_audio_generate_s2law_table(void)
++{
++      int i, j;
++
++      if (dsp_options & DSP_OPT_ULAW) {
++              /* generating ulaw-table */
++              i = j = 0;
++              while(i < 32768) {
++                      if (i-32768 > dsp_audio_law_to_s32[j])
++                              j++;
++                      dsp_audio_s16_to_law[(i-32768) & 0xffff] = j;
++                      i++;
++              }
++              j = 255;
++              while(i < 65536) {
++                      if (i-0x32768 > dsp_audio_law_to_s32[j])
++                              j--;
++                      dsp_audio_s16_to_law[(i-32768) & 0xffff] = j;
++                      i++;
++              }
++      } else {
++              /* generating alaw-table */
++              i = j = 0;
++              while(i < 65536) {
++                      if (i-32768 > dsp_audio_alaw_relations[j<<1])
++                              j++;
++                      if (j>255)
++                              j=255;
++                      dsp_audio_s16_to_law[(i-32768) & 0xffff]
++                               = dsp_audio_alaw_relations[(j<<1)|1];
++                      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;
++      u8 spl;
++
++      /* conversion from law to seven bit audio */
++      i = 0;
++      while(i < 256) {
++              /* 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 */
++              j = 0;
++              while(j < 256) {
++                      if (dsp_audio_alaw_relations[(j<<1)|1] == spl)
++                              break;
++                      j++;
++              }
++              if (j == 256) {
++                      printk(KERN_WARNING "fatal error in %s: alaw-sample '0x%2x' not found in relations-table.\n", __FUNCTION__, spl);
++              }
++              /* write 7-bit audio value */
++              dsp_audio_law2seven[i] = j >> 1;
++              i++;
++      }
++
++      /* conversion from seven bit audio to law */
++      i = 0;
++      while(i < 128) {
++              /* find alaw-spl */
++              spl = dsp_audio_alaw_relations[(i<<2)|1];
++              /* convert to ulaw, if required */
++              if (dsp_options & DSP_OPT_ULAW)
++                      spl = dsp_audio_alaw_to_ulaw[spl];
++              /* write 8-bit law sample */
++              dsp_audio_seven2law[i] = spl;
++              i++;
++      }
++}
++
++
++/* 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;
++
++      i = 0;
++      while(i < 256) {
++              dsp_audio_reduce8[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>8) & 0xffff];
++              dsp_audio_reduce7[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>7) & 0xffff];
++              dsp_audio_reduce6[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>6) & 0xffff];
++              dsp_audio_reduce5[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>5) & 0xffff];
++              dsp_audio_reduce4[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>4) & 0xffff];
++              dsp_audio_reduce3[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>3) & 0xffff];
++              dsp_audio_reduce2[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>2) & 0xffff];
++              dsp_audio_reduce1[i] = dsp_audio_s16_to_law[(dsp_audio_law_to_s32[i]>>1) & 0xffff];
++              sample = dsp_audio_law_to_s32[i] << 1;
++              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] << 2;
++              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] << 3;
++              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] << 4;
++              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] << 5;
++              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] << 6;
++              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] << 7;
++              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] << 8;
++              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 -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_blowfish.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_blowfish.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_blowfish.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_blowfish.c   2004-11-22 09:33:38.025763120 +0000
+@@ -0,0 +1,658 @@
++/* $Id$
++ *
++ * Blowfish encryption/decryption for mISDN_dsp.
++ *
++ * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++#include "layer1.h"
++#include "helper.h"
++#include "debug.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)  b ^= P[n]; a ^= bf_F (b)
++#define DROUND(a, b, n)  a ^= bf_F (b); b ^= P[n]
++
++
++/*
++ * encrypt isdn data frame
++ * every block with 9 samples is encrypted
++ */
++void
++dsp_bf_encrypt(dsp_t *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]];
++              yr = nibble = dsp_audio_law2seven[bf_data_in[4]];
++              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(dsp_t *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 */
++              yr = nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
++              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(dsp_t *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] = 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(dsp_t *dsp)
++{
++      dsp->bf_enable = 0;
++}
++
++
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_cmx.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_cmx.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_cmx.c    1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_cmx.c        2004-11-22 09:33:38.035761600 +0000
+@@ -0,0 +1,1374 @@
++/* $Id$
++ *
++ * Audio crossconnecting/conferrencing (hardware level).
++ *
++ * Copyright 2002 by Andreas Eversberg (jolly@jolly.de)
++ *
++ * 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 conference_t which has one or more members in a chain
++ * of conf_member_t.
++ *
++ * 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 conference_t entry.
++ */
++
++/* HOW THE CMX WORKS:
++ *
++ * There are 3 types of interaction: One member is alone, in this case only
++ * data flow 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.
++ *
++ * There are 3 buffers:
++ *
++ * The conference buffer
++ *
++ *  R-3     R-2   R-1    W-2    W-1 W-3
++ *   |       |     |      |      |   |
++ * --+-------+-----+------+------+---+---------------
++ *                        |          |
++ *                      W-min      W-max
++ *
++ * The conference buffer is a ring buffer used to mix all data from all members.
++ * To compensate the echo, data of individual member will later be substracted
++ * before it is sent to that member. Each conference has a W-min and a W-max
++ * pointer. Each individual member has a write pointer (W-x) and a read pointer
++ * (R-x). W-min shows the lowest value of all W-x. The W-max shows the highest
++ * value of all W-x. Whenever data is written, it is mixed by adding to the
++ * existing sample value in the buffer. If W-max would increase, the additional
++ * range is cleared so old data will be erased in the ring buffer.
++ *
++ *
++ * RX-Buffer
++ *                R-1           W-1
++ *                 |             |
++ * ----------------+-------------+-------------------
++ * 
++ * The rx-buffer is a ring buffer used to store the received data for each
++ * individual member. To compensate echo, this data will later be substracted
++ * from the conference's data before it is sent to that member. If only two
++ * members are in one conference, this data is used to get the queued data from
++ * the other member.
++ *
++ *
++ * 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.
++ *
++ *
++ * If a member joins a conference:
++ *
++ * - If a member joins, its rx_buff is set to silence.
++ * - If the conference reaches three members, the conf-buffer is cleared.
++ * - When a member is joined, it will set its write and read pointer to W_max.
++ *
++ * 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.
++ *
++ *
++ * LIMITS:
++ *
++ * The max_queue value is 2* the samples of largest packet ever received by any
++ * conference member from her card. It also changes during life of conference.
++ *
++ *
++ * AUDIO PROCESS:
++ *
++ * Writing data to conference's and member's buffer is done by adding the sample
++ * value to the existing ring buffer. Writing user space data to the member's
++ * buffer is done by substracting the sample value from the existing ring
++ * buffer.
++ *
++ *
++ * 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/vmalloc.h>
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++#include "dsp.h"
++#include "hw_lock.h"
++
++//#define CMX_CONF_DEBUG /* debugging of multi party conference, by using conference even with two members */
++//#define CMX_DEBUG /* massive read/write pointer output */
++
++extern mISDN_HWlock_t dsp_lock;
++LIST_HEAD(Conf_list);
++
++/*
++ * debug cmx memory structure
++ */
++void
++dsp_cmx_debug(dsp_t *dsp)
++{
++      conference_t *conf;
++      conf_member_t *member;
++      dsp_t *odsp;
++
++      printk(KERN_DEBUG "-----Current DSP\n");
++      list_for_each_entry(odsp, &dsp_obj.ilist, list)
++      {
++              printk(KERN_DEBUG "* %s echo=%d txmix=%d", odsp->inst.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_list, list)
++      {
++              printk(KERN_DEBUG "* Conf %d (0x%x)\n", conf->id, (u32)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->inst.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 conference_t 
++*dsp_cmx_search_conf(u32 id)
++{
++      conference_t *conf;
++
++      if (!id) {
++              printk(KERN_WARNING "%s: conference ID is 0.\n", 
++                      __FUNCTION__);
++              return(NULL);
++      }
++
++      /* search conference */
++      list_for_each_entry(conf, &Conf_list, list)
++              if (conf->id == id)
++                      return(conf);
++
++      return(NULL);
++}
++
++
++/*
++ * add member to conference
++ */
++static int
++dsp_cmx_add_conf_member(dsp_t *dsp, conference_t *conf)
++{
++      conf_member_t *member;
++
++      if (!conf || !dsp) {
++              printk(KERN_WARNING "%s: conf or dsp is 0.\n", __FUNCTION__);
++              return(-EINVAL);
++      }
++      if (dsp->member) {
++              printk(KERN_WARNING "%s: dsp is already member in a conf.\n", 
++                      __FUNCTION__);
++              return(-EINVAL);
++      }
++
++      if (dsp->conf) {
++              printk(KERN_WARNING "%s: dsp is already in a conf.\n", 
++                      __FUNCTION__);
++              return(-EINVAL);
++      }
++
++      unlock_HW(&dsp_lock);
++      if (!(member = vmalloc(sizeof(conf_member_t)))) {
++              lock_HW(&dsp_lock, 0);
++              printk(KERN_ERR "vmalloc conf_member_t failed\n");
++              return(-ENOMEM);
++      }
++      lock_HW(&dsp_lock, 0);
++      memset(member, 0, sizeof(conf_member_t));
++      memset(dsp->rx_buff, silence, sizeof(dsp->rx_buff));
++      member->dsp = dsp;
++      /* set initial values */
++      dsp->W_rx = conf->W_max;
++      dsp->R_rx = conf->W_max;
++
++      list_add_tail(&member->list, &conf->mlist);
++
++      /* zero conf-buffer if we change from 2 to 3 members */
++      if (3 == count_list_member(&conf->mlist))
++              memset(conf->conf_buff, 0, sizeof(conf->conf_buff));
++
++      dsp->conf = conf;
++      dsp->member = member;
++
++      return(0);
++}
++
++
++/*
++ * del member from conference
++ */
++int
++dsp_cmx_del_conf_member(dsp_t *dsp)
++{
++      conf_member_t *member;
++
++      if (!dsp) {
++              printk(KERN_WARNING "%s: dsp is 0.\n", 
++                      __FUNCTION__);
++              return(-EINVAL);
++      }
++
++      if (!dsp->conf) {
++              printk(KERN_WARNING "%s: dsp is not in a conf.\n", 
++                      __FUNCTION__);
++              return(-EINVAL);
++      }
++
++      if (list_empty(&dsp->conf->mlist)) {
++              printk(KERN_WARNING "%s: dsp has linked an empty conf.\n", 
++                      __FUNCTION__);
++              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;
++                      unlock_HW(&dsp_lock);
++                      vfree(member);
++                      lock_HW(&dsp_lock, 0);
++                      return(0);
++              }
++      }
++      printk(KERN_WARNING "%s: dsp is not present in its own conf_meber list.\n", 
++              __FUNCTION__);
++
++      return(-EINVAL);
++}
++
++
++/*
++ * new conference
++ */
++static conference_t
++*dsp_cmx_new_conf(u32 id)
++{
++      conference_t *conf;
++
++      if (!id) {
++              printk(KERN_WARNING "%s: id is 0.\n", 
++                      __FUNCTION__);
++              return(NULL);
++      }
++
++      unlock_HW(&dsp_lock);
++      if (!(conf = vmalloc(sizeof(conference_t)))) {
++              lock_HW(&dsp_lock, 0);
++              printk(KERN_ERR "vmalloc conference_t failed\n");
++              return(NULL);
++      }
++      lock_HW(&dsp_lock, 0);
++      memset(conf, 0, sizeof(conference_t));
++      INIT_LIST_HEAD(&conf->mlist);
++      conf->id = id;
++
++      list_add_tail(&conf->list, &Conf_list);
++
++      return(conf);
++}
++
++
++/*
++ * del conference
++ */
++int
++dsp_cmx_del_conf(conference_t *conf)
++{
++      if (!conf) {
++              printk(KERN_WARNING "%s: conf is null.\n", 
++                      __FUNCTION__);
++              return(-EINVAL);
++      }
++
++      if (!list_empty(&conf->mlist)) {
++              printk(KERN_WARNING "%s: conf not empty.\n", 
++                      __FUNCTION__);
++              return(-EINVAL);
++      }
++      list_del(&conf->list);
++      unlock_HW(&dsp_lock);
++      vfree(conf);
++      lock_HW(&dsp_lock, 0);
++
++      return(0);
++}
++
++
++/*
++ * send HW message to hfc card
++ */
++static void
++dsp_cmx_hw_message(dsp_t *dsp, u32 message, u32 param1, u32 param2, u32 param3, u32 param4)
++{
++      struct sk_buff *nskb;
++      u32 param[4];
++
++      param[0] = param1;
++      param[1] = param2;
++      param[2] = param3;
++      param[3] = param4;
++      nskb = create_link_skb(PH_CONTROL | REQUEST, message, sizeof(param), param, 0);
++      if (!nskb) {
++              printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__);
++              return;
++      }
++      /* unlocking is not required, because we don't expect a response */
++      if (dsp->inst.down.func(&dsp->inst.down, nskb))
++              dev_kfree_skb(nskb);
++}
++
++
++/*
++ * 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(conference_t *conf, dsp_t *dsp)
++{
++      conf_member_t *member, *nextm;
++      dsp_t *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) */
++//printk("-----1\n");
++      if (!conf) {
++//printk("-----2\n");
++              if (!dsp)
++                      return;
++//printk("-----3\n");
++              if (dsp_debug & DEBUG_DSP_CMX)
++                      printk(KERN_DEBUG "%s checking dsp %s\n", __FUNCTION__, dsp->inst.name);
++//printk("-----a\n");
++              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", __FUNCTION__, dsp->inst.name, dsp->hfc_conf);
++                      dsp_cmx_hw_message(dsp, HW_CONF_SPLIT, 0, 0, 0, 0);
++                      dsp->hfc_conf = -1;
++              }
++              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", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx, dsp->pcm_slot_rx);
++                              dsp_cmx_hw_message(dsp, HW_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; /* loop */
++                      dsp->pcm_bank_rx = 2;
++                      if (dsp_debug & DEBUG_DSP_CMX)
++                              printk(KERN_DEBUG "%s refresh %s for echo using slot %d\n", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx);
++                      dsp_cmx_hw_message(dsp, HW_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_obj.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", __FUNCTION__);
++                      /* 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", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx);
++              dsp_cmx_hw_message(dsp, HW_PCM_CONN, dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
++              return;
++      }
++
++//printk("-----4\n");
++      /* conf gets updated (all members) */
++      if (dsp_debug & DEBUG_DSP_CMX)
++              printk(KERN_DEBUG "%s checking conference %d\n", __FUNCTION__, conf->id);
++//printk("-----5\n");
++      
++      if (list_empty(&conf->mlist)) {
++              printk(KERN_ERR "%s: conference whithout members\n", __FUNCTION__);
++              return;
++      }
++      member = list_entry(conf->mlist.next, conf_member_t, 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", __FUNCTION__, member->dsp->inst.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", __FUNCTION__, dsp->inst.name, dsp->hfc_conf);
++                                      dsp_cmx_hw_message(dsp, HW_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", __FUNCTION__, dsp->inst.name, dsp->pcm_slot_tx, dsp->pcm_slot_rx);
++                                      dsp_cmx_hw_message(dsp, HW_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", __FUNCTION__, member->dsp->inst.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", __FUNCTION__, member->dsp->inst.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", __FUNCTION__, member->dsp->inst.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", __FUNCTION__, member->dsp->inst.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", __FUNCTION__, member->dsp->inst.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", __FUNCTION__, member->dsp->inst.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",
++                                      __FUNCTION__, member->dsp->inst.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", __FUNCTION__, conf->id);
++              conf->hardware = 0;
++              conf->software = 1;
++              member = list_entry(conf->mlist.next, conf_member_t, 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, conf_member_t, list);
++              nextm = list_entry(member->list.next, conf_member_t, 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", __FUNCTION__, member->dsp->inst.name, member->dsp->hfc_conf);
++                      dsp_cmx_hw_message(member->dsp, HW_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", __FUNCTION__, nextm->dsp->inst.name, nextm->dsp->hfc_conf);
++                      dsp_cmx_hw_message(nextm->dsp, HW_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.pcm_id!=nextm->dsp->features.pcm_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", __FUNCTION__,
++                                              member->dsp->inst.name, nextm->dsp->inst.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_obj.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", __FUNCTION__,
++                                              member->dsp->inst.name, nextm->dsp->inst.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", __FUNCTION__,
++                                      member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx);
++                      dsp_cmx_hw_message(member->dsp, HW_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, HW_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", __FUNCTION__,
++                                              member->dsp->inst.name, nextm->dsp->inst.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_obj.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", __FUNCTION__,
++                                              member->dsp->inst.name, nextm->dsp->inst.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", __FUNCTION__,
++                                              member->dsp->inst.name, nextm->dsp->inst.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", __FUNCTION__,
++                                      member->dsp->inst.name, nextm->dsp->inst.name, member->dsp->pcm_slot_tx,
++                                      member->dsp->pcm_slot_rx);
++                      dsp_cmx_hw_message(member->dsp, HW_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, HW_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",
++                              __FUNCTION__, conf->id);
++              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_obj.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", __FUNCTION__, 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", __FUNCTION__, member->dsp->inst.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, HW_PCM_CONN, i, 2, i, 2);
++                      dsp_cmx_hw_message(member->dsp, HW_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_obj.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", __FUNCTION__, 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(dsp_t *dsp, u32 conf_id)
++{
++      int err;
++      conference_t *conf;
++
++      /* 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);
++      }
++      /* 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(dsp_t *dsp, struct sk_buff *skb)
++{
++      conference_t *conf = dsp->conf;
++      conf_member_t *member;
++      s32 *c;
++      u8 *d, *p;
++      int len = skb->len;
++      int w, ww, i, ii;
++      int W_min, W_max;
++
++      /* check if we have sompen */
++      if (len < 1)
++              return;
++
++//#ifndef AUTOJITTER
++      /* -> if length*2 is greater largest */
++      if (dsp->largest < (len<<1))
++              dsp->largest = (len<<1);
++//#endif
++
++      /* half of the buffer should be 4 time larger than maximum packet size */
++      if (len >= (CMX_BUFF_HALF>>2)) {
++              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;
++      }
++
++      /* STEP 1: WRITE DOWN WHAT WE GOT (into the buffer(s)) */
++
++      /* -> new W-min & W-max is calculated:
++       * W_min will be the write pointer of this dsp (after writing 'len'
++       * of bytes).
++       * If there are other members in a conference, W_min will be the
++       * lowest of all member's writer pointers.
++       * W_max respectively
++       */
++      W_max = W_min = (dsp->W_rx + len) & CMX_BUFF_MASK;
++      if (conf) {
++              /* -> who is larger? dsp or conf */
++              if (conf->largest < dsp->largest)
++                      conf->largest = dsp->largest;
++              else if (conf->largest > dsp->largest)
++                      dsp->largest = conf->largest;
++
++              list_for_each_entry(member, &conf->mlist, list) {
++                      if (member != dsp->member) {
++                              /* if W_rx is lower */
++                              if (((member->dsp->W_rx - W_min) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
++                                      W_min = member->dsp->W_rx;
++                              /* if W_rx is higher */
++                              if (((W_max - member->dsp->W_rx) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
++                                      W_max = member->dsp->W_rx;
++                      }
++              }
++      }
++
++#ifdef CMX_DEBUG
++      printk(KERN_DEBUG "cmx_receive(dsp=%lx): W_rx(dsp)=%05x W_min=%05x W_max=%05x largest=%05x %s\n", dsp, dsp->W_rx, W_min, W_max, dsp->largest, dsp->inst.name);
++#endif
++
++      /* -> if data is not too fast (exceed maximum queue):
++       * data is written if 'new W_rx' is not too far behind W_min.
++       */
++      if (((dsp->W_rx + len - W_min) & CMX_BUFF_MASK) <= dsp->largest) {
++              /* -> received data is written to rx-buffer */
++              p = skb->data;
++              d = dsp->rx_buff;
++              w = dsp->W_rx;
++              i = 0;
++              ii = len;
++              while(i < ii) {
++                      d[w++ & CMX_BUFF_MASK] = *p++;
++                      i++;
++              }
++              /* -> if conference has three or more members */
++              if (conf) {
++#ifdef CMX_CONF_DEBUG
++#warning CMX_CONF_DEBUG is enabled, it causes performance loss with normal 2-party crossconnects
++                      if (2 <= count_list_member(&conf->mlist)) {
++#else
++                      if (3 <= count_list_member(&conf->mlist)) {
++#endif
++//printk(KERN_DEBUG "cmxing dsp:%s dsp->W_rx=%04x conf->W_max=%04x\n", dsp->inst.name, dsp->W_rx, conf->W_max);
++                              /* -> received data is added to conf-buffer
++                               *    new space is overwritten */
++                              p = skb->data;
++                              c = conf->conf_buff;
++                              w = dsp->W_rx;
++                              ww = conf->W_max;
++                              i = 0;
++                              ii = len;
++                              /* loop until done or old W_max is reached */
++                              while(i<ii && w!=ww) {
++                                      c[w] += dsp_audio_law_to_s32[*p++]; /* add to existing */
++                                      w = (w+1) & CMX_BUFF_MASK; /* must be always masked, for loop condition */
++                                      i++;
++                              }
++                              /* loop the rest */
++                              while(i < ii) {
++                                      c[w++ & CMX_BUFF_MASK] = dsp_audio_law_to_s32[*p++]; /* write to new */
++                                      i++;
++                              }
++                      }
++                      /* if W_max is lower new dsp->W_rx */
++                      if (((W_max - (dsp->W_rx+len)) & CMX_BUFF_MASK) >= CMX_BUFF_HALF)
++                              W_max = (dsp->W_rx + len) & CMX_BUFF_MASK;
++                      /* store for dsp_cmx_send */
++                      conf->W_min = W_min;
++                      /* -> write new W_max */
++                      conf->W_max = W_max;
++              }
++              /* -> write new W_rx */
++              dsp->W_rx = (dsp->W_rx + len) & CMX_BUFF_MASK;
++      } else {
++              if (dsp_debug & DEBUG_DSP_CMX)
++                      printk(KERN_DEBUG "CMX: receiving too fast (rx_buff) dsp=%x\n", (u32)dsp);
++#ifdef CMX_CONF_DEBUG
++              printk(KERN_DEBUG "W_max=%x-W_min=%x = %d, largest = %d\n", W_max, W_min, (W_max - W_min) & CMX_BUFF_MASK, dsp->largest);
++#endif
++      }
++}
++
++/*
++ * send mixed audio data to card
++ */
++
++struct sk_buff 
++*dsp_cmx_send(dsp_t *dsp, int len, int dinfo)
++{
++      conference_t *conf = dsp->conf;
++      dsp_t *member, *other;
++      register s32 sample;
++      s32 *c;
++      u8 *d, *o, *p, *q;
++      struct sk_buff *nskb;
++      int r, rr, t, tt;
++
++      /* PREPARE RESULT */
++      nskb = alloc_skb(len, GFP_ATOMIC);
++      if (!nskb) {
++              printk(KERN_ERR "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n", len);
++              return(NULL);
++      }
++      mISDN_sethead(PH_DATA | REQUEST, dinfo, nskb);
++      /* set pointers, indexes and stuff */
++      member = dsp;
++      p = dsp->tx_buff; /* transmit data */
++      q = dsp->rx_buff; /* received data */
++      d = skb_put(nskb, len); /* result */
++      t = dsp->R_tx; /* tx-pointers */
++      tt = dsp->W_tx;
++      r = dsp->R_rx; /* rx-pointers */
++      if (conf) {
++              /* special hardware access */
++              if (conf->hardware) {
++                      if (dsp->tone.tone && dsp->tone.software) {
++                              /* -> copy tone */
++                              dsp_tone_copy(dsp, d, len);
++                              dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */
++                              return(nskb);
++                      }
++                      if (t != tt) {
++                              while(len && t!=tt) {
++                                      *d++ = p[t]; /* write tx_buff */
++                                      t = (t+1) & CMX_BUFF_MASK;
++                                      len--;
++                              }
++                      }
++                      if (len)
++                              memset(d, silence, len);
++                      dsp->R_tx = t;
++                      return(nskb);
++              }
++              /* W_min is also limit for read */
++              rr = conf->W_min;
++      } else
++              rr = dsp->W_rx;
++
++      /* calculate actual r (if r+len would overrun rr) */
++      if (((rr - r - len) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
++#ifdef CMX_CONF_DEBUG
++              printk(KERN_DEBUG "r+len=%04x overruns rr=%04x\n", (r+len) & CMX_BUFF_MASK, rr);
++#endif
++              /* r is set "len" bytes before W_min */
++              r = (rr - len) & CMX_BUFF_MASK;
++              if (dsp_debug & DEBUG_DSP_CMX)
++                      printk(KERN_DEBUG "CMX: sending too fast (tx_buff) dsp=%x\n", (u32)dsp);
++      } else
++              /* rr is set "len" bytes after R_rx */
++              rr = (r + len) & CMX_BUFF_MASK;
++      dsp->R_rx = rr;
++      /* now: rr is exactly "len" bytes after r now */
++#ifdef CMX_DEBUG
++      printk(KERN_DEBUG "CMX_SEND(dsp=%lx) %d bytes from tx:0x%05x-0x%05x rx:0x%05x-0x%05x echo=%d %s\n", dsp, len, t, tt, r, rr, dsp->echo, dsp->inst.name);
++#endif
++
++      /* STEP 2.0: PROCESS TONES/TX-DATA ONLY */
++      if (dsp->tone.tone && dsp->tone.software) {
++              /* -> copy tone */
++              dsp_tone_copy(dsp, d, len);
++              dsp->R_tx = dsp->W_tx = 0; /* clear tx buffer */
++              return(nskb);
++      }
++      /* if we have tx-data but do not use mixing */
++      if (!dsp->tx_mix && t!=tt) {
++              /* -> send tx-data and continue when not enough */
++              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) {
++                      dsp->R_tx = t;
++                      return(nskb);
++              }
++      }
++
++      /* STEP 2.1: PROCESS DATA (one member / no conf) */
++      if (!conf) {
++              single:
++              /* -> 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, 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->R_tx = t;
++              return(nskb);
++      }
++      if (1 == count_list_member(&conf->mlist)) {
++              goto single;
++      }
++      /* STEP 2.2: PROCESS DATA (two members) */
++#ifdef CMX_CONF_DEBUG
++      if (0) {
++#else
++      if (2 == count_list_member(&conf->mlist)) {
++#endif
++              /* "other" becomes other party */
++              other = (list_entry(conf->mlist.next, conf_member_t, list))->dsp;
++              if (other == member)
++                      other = (list_entry(conf->mlist.prev, conf_member_t, list))->dsp;
++              o = other->rx_buff; /* received data */
++              /* -> if echo is NOT enabled */
++              if (!dsp->echo) {
++                      /* -> copy other member's rx-data, if tx-data is available, mix */
++                      while(r!=rr && t!=tt) {
++                              *d++ = dsp_audio_mix_law[(p[t]<<8)|o[r]];
++                              t = (t+1) & CMX_BUFF_MASK;
++                              r = (r+1) & CMX_BUFF_MASK;
++                      }
++                      while(r != rr) {
++                              *d++ = o[r];
++                              r = (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[o[r]] + 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]; /* tx-data + rx_data + echo */
++                              t = (t+1) & CMX_BUFF_MASK;
++                              r = (r+1) & CMX_BUFF_MASK;
++                      }
++                      while(r != rr) {
++                              *d++ = dsp_audio_mix_law[(o[r]<<8)|q[r]];
++                              r = (r+1) & CMX_BUFF_MASK;
++                      }
++              }
++              dsp->R_tx = t;
++              return(nskb);
++      }
++      /* STEP 2.3: PROCESS DATA (three or more members) */
++      c = conf->conf_buff;
++      /* -> 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[r] - 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[r] - 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[r];
++                      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[r];
++                      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->R_tx = t;
++      return(nskb);
++}
++
++/*
++ * audio data is transmitted from upper layer to the dsp
++ */
++void 
++dsp_cmx_transmit(dsp_t *dsp, struct sk_buff *skb)
++{
++      u_int w, ww;
++      u8 *d, *p;
++      int space, l;
++#ifdef AUTOJITTER
++      int use;
++#endif
++
++      /* check if we have sompen */
++      l = skb->len;
++      w = dsp->W_tx;
++      ww = dsp->R_tx;
++      if (l < 1)
++              return;
++
++#ifdef AUTOJITTER
++      /* check the delay */
++      use = w-ww;
++      if (use < 0)
++              use += CMX_BUFF_SIZE;
++      if (!dsp->tx_delay || dsp->tx_delay>use)
++              dsp->tx_delay = use;
++      dsp->tx_delay_count += l;
++      if (dsp->tx_delay_count >= DELAY_CHECK) {
++              /* now remove the delay */
++              if (dsp_debug & DEBUG_DSP_DELAY)
++                      printk(KERN_DEBUG "%s(dsp=0x%x) removing delay of %d bytes\n", __FUNCTION__, (u32)dsp, dsp->tx_delay);
++              dsp->tx_delay_count = 0;
++              dsp->R_tx = ww = (ww + dsp->tx_delay) & CMX_BUFF_MASK;
++              dsp->tx_delay = 0;
++      }
++#endif
++
++      /* check if there is enough space, and then copy */
++      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->W_tx = ww;
++
++#ifdef CMX_DEBUG
++      printk(KERN_DEBUG "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n", dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->inst.name);
++#endif
++
++      /* copy transmit data to tx-buffer */
++      while(w != ww) {
++              p[w]= *d++;
++              w = (w+1) & CMX_BUFF_MASK;
++      }
++
++      return;
++}
++
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_core.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_core.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_core.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_core.c       2004-11-22 09:33:38.046759928 +0000
+@@ -0,0 +1,964 @@
++/* $Id$
++ *
++ * Author       Andreas Eversberg (jolly@jolly.de)
++ * 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
++ * - (4) echo generation for delay test
++ * - (5) volume control
++ * - (6) disable receive data
++ * - (7) encryption/decryption
++ *
++ * Look:
++ *             TX            RX
++ *         ------upper layer------
++ *             |             ^
++ *             |             |(6)
++ *             v             |
++ *       +-----+-------------+-----+
++ *       |(3)(4)                   |
++ *       |                         |
++ *       |                         |
++ *       |           CMX           |
++ *       |                         |
++ *       |                         |
++ *       |                         |
++ *       |                         |
++ *       |           +-------------+
++ *       |           |       ^
++ *       |           |       |
++ *       |           |       |
++ *       |+---------+|  +----+----+
++ *       ||(1)      ||  |(5)      |
++ *       ||         ||  |         |
++ *       ||  Tones  ||  |RX Volume|
++ *       ||         ||  |         |
++ *       ||         ||  |         |
++ *       |+----+----+|  +----+----+
++ *       +-----+-----+       ^
++ *             |             | 
++ *             |             |
++ *             v             |
++ *        +----+----+   +----+----+
++ *        |(5)      |   |(2)      |
++ *        |         |   |         |
++ *        |TX Volume|   |  DTMF   |
++ *        |         |   |         |
++ *        |         |   |         |
++ *        +----+----+   +----+----+
++ *             |             ^ 
++ *             |             |
++ *             v             |
++ *        +----+----+   +----+----+
++ *        |(7)      |   |(7)      |
++ *        |         |   |         |
++ *        | 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.
++ *
++ * 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.  When data is ready to be transmitted
++ * to a different layer, the module is unlocked. It is not allowed to hold a
++ * lock outside own layer.
++ * Reasons: Multiple threads must not process cmx at the same time, if threads
++ * serve instances, that are connected in same conference.
++ * PH_CONTROL must not change any settings, join or split conference members
++ * during process of data.
++ * 
++ *
++ * TRANSMISSION:
++ *
++
++TBD
++
++There are three things that need to receive data from card:
++ - software DTMF decoder
++ - software cmx (if conference exists)
++ - upper layer, if rx-data not disabled
++
++Whenever dtmf decoder is turned on or off, software cmx changes, rx-data is disabled or enabled, or card becomes activated, then rx-data is disabled or enabled using a special command to the card.
++
++There are three things that need to transmit data to card:
++ - software tone generation (part of cmx)
++ - software cmx
++ - upper layer, if tx-data is written to tx-buffer
++
++Whenever cmx is changed, or data is sent from upper layer, the transmission is triggered by an silence freame (if not already tx_pending==1). When the confirm is received from card, next frame is
++sent using software cmx, if tx-data is still available, or if software tone generation is used,
++or if cmx is currently using software.
++
++
++ 
++ */
++
++const char *dsp_revision = "$Revision$";
++
++#include <linux/delay.h>
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++#include "dsp.h"
++#include "hw_lock.h"
++
++static char DSPName[] = "DSP";
++mISDNobject_t dsp_obj;
++mISDN_HWlock_t dsp_lock;
++
++static int debug = 0;
++int dsp_debug;
++static int options = 0;
++int dsp_options;
++#ifndef AUTOJITTER
++int poll = 0;
++#endif
++
++#ifdef MODULE
++MODULE_AUTHOR("Andreas Eversberg");
++MODULE_PARM(debug, "1i");
++MODULE_PARM(options, "1i");
++#ifndef AUTOJITTER
++MODULE_PARM(poll, "1i");
++#endif
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++#endif
++
++/*
++ * sending next frame to card (triggered by PH_DATA_IND)
++ */
++static void
++sendevent(dsp_t *dsp)
++{
++      struct sk_buff *nskb;
++
++      lock_HW(&dsp_lock, 0);
++      if (dsp->b_active && dsp->tx_pending) {
++              /* get data from cmx */
++              nskb = dsp_cmx_send(dsp, dsp->tx_pending, 0);
++              dsp->tx_pending = 0;
++              if (!nskb) {
++                      unlock_HW(&dsp_lock);
++                      printk(KERN_ERR "%s: failed to create tx packet\n", __FUNCTION__);
++                      return;
++              }
++              /* crypt if enabled */
++              if (dsp->bf_enable)
++                      dsp_bf_encrypt(dsp, nskb->data, nskb->len);
++              /* change volume if requested */
++              if (dsp->tx_volume)
++                      dsp_change_volume(nskb, dsp->tx_volume);
++              /* send subsequent requests to card */
++              unlock_HW(&dsp_lock);
++              if (dsp->inst.down.func(&dsp->inst.down, nskb)) {
++                      dev_kfree_skb(nskb);
++                      printk(KERN_ERR "%s: failed to send tx packet\n", __FUNCTION__);
++              }
++      } else {
++              dsp->tx_pending = 0;
++              unlock_HW(&dsp_lock);
++      }
++}
++
++
++/*
++ * special message process for DL_CONTROL | REQUEST
++ */
++static int
++dsp_control_req(dsp_t *dsp, mISDN_head_t *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", __FUNCTION__);
++      }
++      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_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: start dtmf\n", __FUNCTION__);
++                      dsp_dtmf_goertzel_init(dsp);
++                      /* checking for hardware capability */
++                      if (dsp->features.hfc_dtmf) {
++                              dsp->dtmf.hardware = 1;
++                              dsp->dtmf.software = 0;
++                      } else {
++                              dsp->dtmf.hardware = 0;
++                              dsp->dtmf.software = 1;
++                      }
++                      break;
++              case DTMF_TONE_STOP: /* turn off DTMF */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: stop dtmf\n", __FUNCTION__);
++                      dsp->dtmf.hardware = 0;
++                      dsp->dtmf.software = 0;
++                      break;
++              case CMX_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", __FUNCTION__, *((u32 *)data));
++                      ret = dsp_cmx_conf(dsp, *((u32 *)data));
++                      if (dsp_debug & DEBUG_DSP_CMX)
++                              dsp_cmx_debug(dsp);
++                      break;
++              case CMX_CONF_SPLIT: /* remove from conference */
++                      conf_split:
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: release conference\n", __FUNCTION__);
++                      ret = dsp_cmx_conf(dsp, 0);
++                      if (dsp_debug & DEBUG_DSP_CMX)
++                              dsp_cmx_debug(dsp);
++                      break;
++              case TONE_PATT_ON: /* play tone */
++                      if (len != sizeof(int)) {
++                              ret = -EINVAL;
++                              break;
++                      }
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: turn tone 0x%x on\n", __FUNCTION__, *((int *)skb->data));
++                      ret = dsp_tone(dsp, *((int *)data));
++                      if (!ret)
++                              dsp_cmx_hardware(dsp->conf, dsp);
++                      if (!dsp->tone.tone)
++                              goto tone_off;
++                      break;
++              case TONE_PATT_OFF: /* stop tone */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: turn tone off\n", __FUNCTION__);
++                      dsp_tone(dsp, 0);
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      /* reset tx buffers (user space data) */
++                      tone_off:
++                      dsp->R_tx = dsp->W_tx = 0;
++                      break;
++              case VOL_CHANGE_TX: /* change volume */
++                      if (len != sizeof(int)) {
++                              ret = -EINVAL;
++                              break;
++                      }
++                      dsp->tx_volume = *((int *)data);
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume);
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      break;
++              case VOL_CHANGE_RX: /* change volume */
++                      if (len != sizeof(int)) {
++                              ret = -EINVAL;
++                              break;
++                      }
++                      dsp->rx_volume = *((int *)data);
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: change rx volume to %d\n", __FUNCTION__, dsp->tx_volume);
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      break;
++              case CMX_ECHO_ON: /* enable echo */
++                      dsp->echo = 1; /* soft echo */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: enable cmx-echo\n", __FUNCTION__);
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      if (dsp_debug & DEBUG_DSP_CMX)
++                              dsp_cmx_debug(dsp);
++                      break;
++              case CMX_ECHO_OFF: /* disable echo */
++                      dsp->echo = 0;
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: disable cmx-echo\n", __FUNCTION__);
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      if (dsp_debug & DEBUG_DSP_CMX)
++                              dsp_cmx_debug(dsp);
++                      break;
++              case CMX_RECEIVE_ON: /* enable receive to user space */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: enable receive to user space\n", __FUNCTION__);
++                      dsp->rx_disabled = 0;
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      break;
++              case CMX_RECEIVE_OFF: /* disable receive to user space */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: disable receive to user space\n", __FUNCTION__);
++                      dsp->rx_disabled = 1;
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      break;
++              case CMX_MIX_ON: /* enable mixing of transmit data with conference members */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: enable mixing of tx-data with conf mebers\n", __FUNCTION__);
++                      dsp->tx_mix = 1;
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      if (dsp_debug & DEBUG_DSP_CMX)
++                              dsp_cmx_debug(dsp);
++                      break;
++              case CMX_MIX_OFF: /* disable mixing of transmit data with conference members */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: disable mixing of tx-data with conf mebers\n", __FUNCTION__);
++                      dsp->tx_mix = 0;
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      if (dsp_debug & DEBUG_DSP_CMX)
++                              dsp_cmx_debug(dsp);
++                      break;
++              case BF_ENABLE_KEY: /* turn blowfish on */
++                      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", __FUNCTION__);
++                      ret = dsp_bf_init(dsp, (u8*)data, len);
++                      /* set new cont */
++                      if (!ret)
++                              cont = BF_ACCEPT;
++                      else
++                              cont = BF_REJECT;
++                      /* send indication if it worked to set it */
++                      nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0);
++                      unlock_HW(&dsp_lock);
++                      if (nskb) {
++                              if (dsp->inst.up.func(&dsp->inst.up, nskb))
++                                      dev_kfree_skb(nskb);
++                      }
++                      lock_HW(&dsp_lock, 0);
++                      if (!ret)
++                              dsp_cmx_hardware(dsp->conf, dsp);
++                      break;
++              case BF_DISABLE: /* turn blowfish off */
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: turn blowfish off\n", __FUNCTION__);
++                      dsp_bf_cleanup(dsp);
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      break;
++              default:
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: ctrl req %x unhandled\n", __FUNCTION__, cont);
++                      ret = -EINVAL;
++      }
++      return(ret);
++}
++
++
++/*
++ * messages from upper layers
++ */
++static int
++dsp_from_up(mISDNif_t *hif, struct sk_buff *skb)
++{
++      dsp_t                   *dsp;
++      mISDN_head_t            *hh;
++      int                     ret = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      dsp = hif->fdata;
++      if (!dsp->inst.down.func)
++              return(-ENXIO);
++
++      hh = mISDN_HEAD_P(skb);
++      switch(hh->prim) {
++              case DL_DATA | RESPONSE:
++              case PH_DATA | RESPONSE:
++                      /* ignore response */
++                      break;
++              case DL_DATA | REQUEST:
++              case PH_DATA | REQUEST:
++                      lock_HW(&dsp_lock, 0);
++                      /* send data to tx-buffer (if no tone is played) */
++                      if (!dsp->tone.tone)
++                              dsp_cmx_transmit(dsp, skb);
++                      unlock_HW(&dsp_lock);
++                      break;
++              case PH_CONTROL | REQUEST:
++                      lock_HW(&dsp_lock, 0);
++                      ret = dsp_control_req(dsp, hh, skb);
++                      unlock_HW(&dsp_lock);
++                      break;
++              case DL_ESTABLISH | REQUEST:
++              case PH_ACTIVATE | REQUEST:
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: activating b_channel %s\n", __FUNCTION__, dsp->inst.name);
++                      lock_HW(&dsp_lock, 0);
++                      dsp->tx_pending = 0;
++                      if (dsp->dtmf.hardware || dsp->dtmf.software)
++                              dsp_dtmf_goertzel_init(dsp);
++                      unlock_HW(&dsp_lock);
++                      hh->prim = PH_ACTIVATE | REQUEST;
++                      return(dsp->inst.down.func(&dsp->inst.down, skb));
++              case DL_RELEASE | REQUEST:
++              case PH_DEACTIVATE | REQUEST:
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: releasing b_channel %s\n", __FUNCTION__, dsp->inst.name);
++                      lock_HW(&dsp_lock, 0);
++                      dsp->tx_pending = 0;
++                      dsp->tone.tone = dsp->tone.hardware = dsp->tone.software = 0;
++                      if (timer_pending(&dsp->tone.tl))
++                              del_timer(&dsp->tone.tl);
++                      unlock_HW(&dsp_lock);
++                      hh->prim = PH_DEACTIVATE | REQUEST;
++                      return(dsp->inst.down.func(&dsp->inst.down, skb));
++              default:
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name);
++                      ret = -EINVAL;
++                      break;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++
++/*
++ * messages from lower layers
++ */
++static int
++dsp_from_down(mISDNif_t *hif,  struct sk_buff *skb)
++{
++      dsp_t           *dsp;
++      mISDN_head_t    *hh;
++      int             ret = 0;
++      u8              *digits;
++      int             cont;
++      struct          sk_buff *nskb;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      dsp = hif->fdata;
++      if (!dsp->inst.up.func)
++              return(-ENXIO);
++
++      hh = mISDN_HEAD_P(skb);
++      switch(hh->prim)
++      {
++              case PH_DATA | CONFIRM:
++              case DL_DATA | CONFIRM:
++                      break;
++              case PH_DATA | INDICATION:
++              case DL_DATA | INDICATION:
++                      if (skb->len < 1)
++                              break;
++                      lock_HW(&dsp_lock, 0);
++                      /* decrypt if enabled */
++                      if (dsp->bf_enable)
++                              dsp_bf_decrypt(dsp, skb->data, skb->len);
++                      /* 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);
++                              if (digits) while(*digits) {
++                                      if (dsp_debug & DEBUG_DSP_DTMF)
++                                              printk(KERN_DEBUG "%s: sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name);
++                                      cont = DTMF_TONE_VAL | *digits;
++                                      nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &cont, 0);
++                                      unlock_HW(&dsp_lock);
++                                      if (!nskb) {
++                                              lock_HW(&dsp_lock, 0);
++                                              break;
++                                      }
++                                      if (dsp->inst.up.func(&dsp->inst.up, nskb))
++                                              dev_kfree_skb(nskb);
++                                      lock_HW(&dsp_lock, 0);
++                                      digits++;
++                              }
++                      }
++                      /* change volume if requested */
++                      if (dsp->rx_volume)
++                              dsp_change_volume(skb, dsp->rx_volume);
++                      /* we need to process receive data if software */
++                      if (dsp->pcm_slot_tx<0 && dsp->pcm_slot_rx<0) {
++                              /* process data from card at cmx */
++                              dsp_cmx_receive(dsp, skb);
++                      }
++                      /* we send data only if software or if we have some
++                       * or if we cannot do tones with hardware
++                       */
++                      if ((dsp->pcm_slot_tx<0 && !dsp->features.hfc_loops) /* software crossconnects OR software loops */
++                       || dsp->R_tx != dsp->W_tx /* data in buffer */
++                       || (dsp->echo==1 && dsp->pcm_slot_tx<0) /* software echo */
++                       || (dsp->tone.tone && dsp->tone.software)) { /* software loops */
++                              /* schedule sending skb->len bytes */
++                              dsp->tx_pending = skb->len;
++                              schedule_work(&dsp->sendwork);
++                      }
++                      if (dsp->rx_disabled) {
++                              /* if receive is not allowed */
++                              dev_kfree_skb(skb);
++                              unlock_HW(&dsp_lock);
++                              return(0);
++                      }
++                      unlock_HW(&dsp_lock);
++                      hh->prim = DL_DATA | INDICATION;
++                      return(dsp->inst.up.func(&dsp->inst.up, skb));
++              case PH_CONTROL | INDICATION:
++                      lock_HW(&dsp_lock, 0);
++                      if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
++                              printk(KERN_DEBUG "%s: PH_CONTROL received: %x (len %d) %s\n", __FUNCTION__, hh->dinfo, skb->len, dsp->inst.name);
++                      switch (hh->dinfo) {
++                              case HW_HFC_COEFF: /* getting coefficients */
++                              if (!dsp->dtmf.hardware) {
++                                      if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
++                                              printk(KERN_DEBUG "%s: ignoring DTMF coefficients from HFC\n", __FUNCTION__);
++                                      break;
++                              }
++                              if (dsp->inst.up.func) {
++                                      digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, 2);
++                                      if (digits) while(*digits) {
++                                              int k;
++                                              struct sk_buff *nskb;
++                                              if (dsp_debug & DEBUG_DSP_DTMF)
++                                                      printk(KERN_DEBUG "%s: now sending software decoded digit(%c) to upper layer %s\n", __FUNCTION__, *digits, dsp->inst.name);
++                                              k = *digits | DTMF_TONE_VAL;
++                                              nskb = create_link_skb(PH_CONTROL | INDICATION, 0, sizeof(int), &k, 0);
++                                              unlock_HW(&dsp_lock);
++                                              if (!nskb) {
++                                                      lock_HW(&dsp_lock, 0);
++                                                      break;
++                                              }
++                                              if (dsp->inst.up.func(&dsp->inst.up, nskb))
++                                                      dev_kfree_skb(nskb);
++                                              lock_HW(&dsp_lock, 0);
++                                              digits++;
++                                      }
++                              }
++                              break;
++
++                              default:
++                              if (dsp_debug & DEBUG_DSP_CORE)
++                                      printk(KERN_DEBUG "%s: ctrl ind %x unhandled %s\n", __FUNCTION__, hh->dinfo, dsp->inst.name);
++                              ret = -EINVAL;
++                      }
++                      unlock_HW(&dsp_lock);
++                      break;
++              case PH_ACTIVATE | CONFIRM:
++                      lock_HW(&dsp_lock, 0);
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: b_channel is now active %s\n", __FUNCTION__, dsp->inst.name);
++                      /* bchannel now active */
++                      dsp->b_active = 1;
++                      dsp->W_tx = dsp->R_tx = 0; /* clear TX buffer */
++                      dsp->W_rx = dsp->R_rx = 0; /* clear RX buffer */
++                      if (dsp->conf)
++                              dsp->W_rx = dsp->R_rx = dsp->conf->W_max;
++                      memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
++                      dsp_cmx_hardware(dsp->conf, dsp);
++//                    /* now trigger transmission */
++//#ifndef AUTOJITTER
++//                    dsp->tx_pending = 1;
++//                    schedule_work(&dsp->sendwork);
++//#endif
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: done with activation, sending confirm to user space. %s\n", __FUNCTION__, dsp->inst.name);
++                      /* send activation to upper layer */
++                      hh->prim = DL_ESTABLISH | CONFIRM;
++                      unlock_HW(&dsp_lock);
++                      return(dsp->inst.up.func(&dsp->inst.up, skb));
++              case PH_DEACTIVATE | CONFIRM:
++                      lock_HW(&dsp_lock, 0);
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: b_channel is now inactive %s\n", __FUNCTION__, dsp->inst.name);
++                      /* bchannel now inactive */
++                      dsp->b_active = 0;
++                      dsp_cmx_hardware(dsp->conf, dsp);
++                      hh->prim = DL_RELEASE | CONFIRM;
++                      unlock_HW(&dsp_lock);
++                      return(dsp->inst.up.func(&dsp->inst.up, skb));
++              default:
++                      if (dsp_debug & DEBUG_DSP_CORE)
++                              printk(KERN_DEBUG "%s: msg %x unhandled %s\n", __FUNCTION__, hh->prim, dsp->inst.name);
++                      ret = -EINVAL;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++
++/*
++ * desroy DSP instances
++ */
++static void
++release_dsp(dsp_t *dsp)
++{
++      mISDNinstance_t *inst = &dsp->inst;
++      conference_t *conf;
++
++      lock_HW(&dsp_lock, 0);
++      if (timer_pending(&dsp->tone.tl))
++              del_timer(&dsp->tone.tl);
++#ifdef HAS_WORKQUEUE
++      if (dsp->sendwork.pending)
++              printk(KERN_ERR "%s: pending sendwork: %lx %s\n", __FUNCTION__, dsp->sendwork.pending, dsp->inst.name);
++#else
++      if (dsp->sendwork.sync)
++              printk(KERN_ERR "%s: pending sendwork: %lx %s\n", __FUNCTION__, dsp->sendwork.sync, dsp->inst.name);
++#endif
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: removing conferences %s\n", __FUNCTION__, dsp->inst.name);
++      conf = dsp->conf;
++      if (conf) {
++              dsp_cmx_del_conf_member(dsp);
++              if (!list_empty(&conf->mlist)) {
++                      dsp_cmx_del_conf(conf);
++              }
++      }
++
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: disconnecting instances %s\n", __FUNCTION__, dsp->inst.name);
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: remove & destroy object %s\n", __FUNCTION__, dsp->inst.name);
++      list_del(&dsp->list);
++      dsp_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      unlock_HW(&dsp_lock);
++      vfree(dsp);
++
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: dsp instance released\n", __FUNCTION__);
++}
++
++
++/*
++ * create new DSP instances
++ */
++static int
++new_dsp(mISDNstack_t *st, mISDN_pid_t *pid) 
++{
++      int err = 0;
++      dsp_t *ndsp;
++
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: creating new dsp instance\n", __FUNCTION__);
++
++      if (!st || !pid)
++              return(-EINVAL);
++      if (!(ndsp = vmalloc(sizeof(dsp_t)))) {
++              printk(KERN_ERR "%s: vmalloc dsp_t failed\n", __FUNCTION__);
++              return(-ENOMEM);
++      }
++      memset(ndsp, 0, sizeof(dsp_t));
++      memcpy(&ndsp->inst.pid, pid, sizeof(mISDN_pid_t));
++      ndsp->inst.obj = &dsp_obj;
++      ndsp->inst.data = ndsp;
++      if (!mISDN_SetHandledPID(&dsp_obj, &ndsp->inst.pid)) {
++              int_error();
++              err = -ENOPROTOOPT;
++              free_mem:
++              vfree(ndsp);
++              return(err);
++      }
++      sprintf(ndsp->inst.name, "DSP_S%x/C%x",
++              (st->id&0xff), (st->id&0xff00)>>8);
++      ndsp->inst.up.owner = &ndsp->inst;
++      ndsp->inst.down.owner = &ndsp->inst;
++//#ifndef AUTOJITTER
++      /* set frame size to start */
++      ndsp->largest = 64 << 1;
++//#endif
++      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 timer */
++      ndsp->tone.tl.function = (void *)dsp_tone_timeout;
++      ndsp->tone.tl.data = (long) ndsp;
++      init_timer(&ndsp->tone.tl);
++      /* init send que */
++      INIT_WORK(&ndsp->sendwork, (void *)(void *)sendevent, ndsp);
++      lock_HW(&dsp_lock, 0);
++      /* append and register */
++      list_add_tail(&ndsp->list, &dsp_obj.ilist);
++      err = dsp_obj.ctrl(st, MGR_REGLAYER | INDICATION, &ndsp->inst);
++      if (err) {
++              printk(KERN_ERR "%s: failed to register layer %s\n", __FUNCTION__, ndsp->inst.name);
++              list_del(&ndsp->list);
++              unlock_HW(&dsp_lock);
++              goto free_mem;
++      }
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: dsp instance created %s\n", __FUNCTION__, ndsp->inst.name);
++      unlock_HW(&dsp_lock);
++      return(err);
++}
++
++
++/*
++ * ask for hardware features
++ */
++static void
++dsp_feat(dsp_t *dsp)
++{
++      struct sk_buff *nskb;
++      void *feat;
++
++      if (!(dsp_options & DSP_OPT_NOHARDWARE)) {
++              feat = &dsp->features;
++              nskb = create_link_skb(PH_CONTROL | REQUEST, HW_FEATURES, sizeof(feat), &feat, 0);
++              if (nskb) {
++                      if (dsp->inst.down.func(&dsp->inst.down, nskb)) {
++                              dev_kfree_skb(nskb);
++                              if (dsp_debug & DEBUG_DSP_MGR)
++                                      printk(KERN_DEBUG "%s: no features supported by %s\n", __FUNCTION__, dsp->inst.name);
++                      } else {
++                              if (dsp_debug & DEBUG_DSP_MGR)
++                                      printk(KERN_DEBUG "%s: features of %s: hfc_id=%d hfc_dtmf=%d hfc_loops=%d pcm_id=%d pcm_slots=%d pcm_banks=%d\n",
++                                       __FUNCTION__, dsp->inst.name,
++                                       dsp->features.hfc_id,
++                                       dsp->features.hfc_dtmf,
++                                       dsp->features.hfc_loops,
++                                       dsp->features.pcm_id,
++                                       dsp->features.pcm_slots,
++                                       dsp->features.pcm_banks);
++                      }
++              }
++      }
++}
++
++
++/*
++ * manager for DSP instances
++ */
++static int
++dsp_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      dsp_t *dspl;
++      int ret = -EINVAL;
++
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__, data, prim, arg);
++      if (!data)
++              return(ret);
++      list_for_each_entry(dspl, &dsp_obj.ilist, list) {
++              if (&dspl->inst == inst) {
++                      ret = 0;
++                      break;
++              }
++      }
++      if (ret && (prim != (MGR_NEWLAYER | REQUEST))) {
++              printk(KERN_WARNING "%s: given instance(%p) not in ilist.\n", __FUNCTION__, data);
++              return(ret);
++      }
++
++      switch(prim) {
++          case MGR_NEWLAYER | REQUEST:
++              ret = new_dsp(data, arg);
++              break;
++          case MGR_CONNECT | REQUEST:
++              ret = mISDN_ConnectIF(inst, arg);
++              dsp_feat(dspl);
++              break;
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              ret = mISDN_SetIF(inst, arg, prim, dsp_from_up, dsp_from_down, dspl);
++              break;
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              ret = mISDN_DisConnectIF(inst, arg);
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++          case MGR_RELEASE | INDICATION:
++              if (dsp_debug & DEBUG_DSP_MGR)
++                      printk(KERN_DEBUG "%s: release_dsp id %x\n", __FUNCTION__, dspl->inst.st->id);
++
++              release_dsp(dspl);
++              break;
++          default:
++              printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim);
++              ret = -EINVAL;
++              break;
++      }
++      return(ret);
++}
++
++
++/*
++ * initialize DSP object
++ */
++static int dsp_init(void)
++{
++      int err;
++
++      /* copy variables */
++      dsp_options = options;
++      dsp_debug = debug;
++
++      /* display revision */
++      printk(KERN_INFO "mISDN_dsp: Audio DSP  Rev. %s (debug=0x%x)\n", mISDN_getrev(dsp_revision), debug);
++
++#ifndef AUTOJITTER
++      /* set packet size */
++      switch(poll) {
++              case 8:
++              break;
++              case 16:
++              break;
++              case 32:
++              break;
++              case 64: case 0:
++              poll = 64;
++              break;
++              case 128:
++              break;
++              case 256:
++              break;
++              default:
++              printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll);
++              err = -EINVAL;
++              return(err);
++              
++      }
++#endif
++
++      /* fill mISDN object (dsp_obj) */
++      memset(&dsp_obj, 0, sizeof(dsp_obj));
++#ifdef MODULE
++      SET_MODULE_OWNER(&dsp_obj);
++#endif
++      dsp_obj.name = DSPName;
++      dsp_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_DSP;
++      dsp_obj.own_ctrl = dsp_manager;
++      INIT_LIST_HEAD(&dsp_obj.ilist);
++
++      /* initialize audio tables */
++      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();
++
++      /* init global lock */
++      lock_HW_init(&dsp_lock);
++
++      /* register object */
++      if ((err = mISDN_register(&dsp_obj))) {
++              printk(KERN_ERR "mISDN_dsp: Can't register %s error(%d)\n", DSPName, err);
++              return(err);
++      }
++
++      return(0);
++}
++
++
++/*
++ * cleanup DSP object during module removal
++ */
++static void dsp_cleanup(void)
++{
++      dsp_t   *dspl, *nd;     
++      int     err;
++
++      if (dsp_debug & DEBUG_DSP_MGR)
++              printk(KERN_DEBUG "%s: removing module\n", __FUNCTION__);
++
++      if ((err = mISDN_unregister(&dsp_obj))) {
++              printk(KERN_ERR "mISDN_dsp: Can't unregister Audio DSP error(%d)\n", 
++                      err);
++      }
++      if (!list_empty(&dsp_obj.ilist)) {
++              printk(KERN_WARNING "mISDN_dsp: Audio DSP object inst list not empty.\n");
++              list_for_each_entry_safe(dspl, nd, &dsp_obj.ilist, list)
++                      release_dsp(dspl);
++      }
++      if (!list_empty(&Conf_list)) {
++              printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not all memory freed.\n");
++      }
++}
++
++#ifdef MODULE
++module_init(dsp_init);
++module_exit(dsp_cleanup);
++#endif
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_dtmf.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_dtmf.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_dtmf.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_dtmf.c       2004-11-22 09:33:38.056758408 +0000
+@@ -0,0 +1,250 @@
++/* $Id$
++ *
++ * DTMF decoder.
++ *
++ * Copyright 2003       by Andreas Eversberg (jolly@jolly.de)
++ *                    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 "layer1.h"
++#include "helper.h"
++#include "debug.h"
++#include "dsp.h"
++
++#define NCOEFF            8     /* number of frequencies to be analyzed */
++#define DTMF_TRESH     200000L /* above this is dtmf (square of)*/
++
++/* 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(dsp_t *dsp)
++{
++      dsp->dtmf.size = 0;
++      dsp->dtmf.lastwhat = '\0';
++      dsp->dtmf.lastdigit = '\0';
++      dsp->dtmf.count = 0;
++}
++
++
++/*************************************************************
++ * 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(dsp_t *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 are 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",
++                                      __FUNCTION__, 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 = sk1 = 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] > DTMF_TRESH) {
++                      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 = 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 = 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 -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_tones.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_tones.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dsp_tones.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dsp_tones.c      2004-11-22 09:33:38.066756888 +0000
+@@ -0,0 +1,546 @@
++/* $Id$ 
++ *
++ * Audio support data for ISDN4Linux.
++ *
++ * Copyright 2002/2003 by Andreas Eversberg (jolly@jolly.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++#include "layer1.h"
++#include "helper.h"
++#include "debug.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(dsp_t *dsp, u8 *data, int len)
++{
++      int index, count, start, num;
++      struct pattern *pat;
++      tone_t *tone = &dsp->tone;
++ 
++      /* if we have no tone, we copy silence */
++      if (!tone->tone) {
++              memset(data, 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", __FUNCTION__, 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(dsp_t *dsp, u8 *sample, int len)
++{
++      struct sk_buff *nskb;
++
++      nskb = create_link_skb(PH_CONTROL | REQUEST, (len)?HW_SPL_LOOP_ON:HW_SPL_LOOP_OFF, len, sample, 0);
++      if (!nskb) {
++              printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__);
++              return;
++      }
++      /* unlocking is not required, because we don't expect a response */
++      if (dsp->inst.down.func(&dsp->inst.down, nskb))
++              dev_kfree_skb(nskb);
++}
++
++
++/*****************
++ * timer expires *
++ *****************/
++void
++dsp_tone_timeout(void *arg)
++{
++      dsp_t *dsp = arg;
++      tone_t *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(dsp_t *dsp, int tone)
++{
++      struct pattern *pat;
++      int i;
++      tone_t *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", __FUNCTION__, 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 -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dss1.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/dss1.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dss1.h       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dss1.h   2004-11-22 09:33:38.076755368 +0000
+@@ -0,0 +1,158 @@
++/* $Id$
++ *
++ *  DSS1 (Euro) D-channel protocol defines
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#ifndef l3dss1_process
++
++#define T302  15000
++#define T303  4000
++#define T304  30000
++#define T305  30000
++#define T308  4000
++/* for layer 1 certification T309 < layer1 T3 (e.g. 4000) */
++/* This makes some tests easier and quicker */
++#define T309  40000
++#define T310  30000
++#define T313  4000
++#define T318  4000
++#define T319  4000
++#define N303  1
++#define T_CTRL        180000
++
++/* private TIMER events */
++#define CC_T302               0x030201
++#define CC_T303               0x030301
++#define CC_T304               0x030401
++#define CC_T305               0x030501
++#define CC_T308_1     0x030801
++#define CC_T308_2     0x030802
++#define CC_T309         0x030901
++#define CC_T310               0x031001
++#define CC_T313               0x031301
++#define CC_T318               0x031801
++#define CC_T319               0x031901
++#define CC_TCTRL      0x031f01
++/*
++ * Message-Types
++ */
++
++#define MT_ALERTING           0x01
++#define MT_CALL_PROCEEDING    0x02
++#define MT_CONNECT            0x07
++#define MT_CONNECT_ACKNOWLEDGE        0x0f
++#define MT_PROGRESS           0x03
++#define MT_SETUP              0x05
++#define MT_SETUP_ACKNOWLEDGE  0x0d
++#define MT_RESUME             0x26
++#define MT_RESUME_ACKNOWLEDGE 0x2e
++#define MT_RESUME_REJECT      0x22
++#define MT_SUSPEND            0x25
++#define MT_SUSPEND_ACKNOWLEDGE        0x2d
++#define MT_SUSPEND_REJECT     0x21
++#define MT_USER_INFORMATION   0x20
++#define MT_DISCONNECT         0x45
++#define MT_RELEASE            0x4d
++#define MT_RELEASE_COMPLETE   0x5a
++#define MT_RESTART            0x46
++#define MT_RESTART_ACKNOWLEDGE        0x4e
++#define MT_SEGMENT            0x60
++#define MT_CONGESTION_CONTROL 0x79
++#define MT_INFORMATION                0x7b
++#define MT_FACILITY           0x62
++#define MT_NOTIFY             0x6e
++#define MT_STATUS             0x7d
++#define MT_STATUS_ENQUIRY     0x75
++
++#define IE_SEGMENT    0x00
++#define IE_BEARER     0x04
++#define IE_CAUSE      0x08
++#define IE_CALL_ID    0x10
++#define IE_CALL_STATE 0x14
++#define IE_CHANNEL_ID 0x18
++#define IE_FACILITY   0x1c
++#define IE_PROGRESS   0x1e
++#define IE_NET_FAC    0x20
++#define IE_NOTIFY     0x27
++#define IE_DISPLAY    0x28
++#define IE_DATE               0x29
++#define IE_KEYPAD     0x2c
++#define IE_SIGNAL     0x34
++#define IE_INFORATE   0x40
++#define IE_E2E_TDELAY 0x42
++#define IE_TDELAY_SEL 0x43
++#define IE_PACK_BINPARA       0x44
++#define IE_PACK_WINSIZE       0x45
++#define IE_PACK_SIZE  0x46
++#define IE_CUG                0x47
++#define       IE_REV_CHARGE   0x4a
++#define IE_CONNECT_PN 0x4c
++#define IE_CONNECT_SUB        0x4d
++#define IE_CALLING_PN 0x6c
++#define IE_CALLING_SUB        0x6d
++#define IE_CALLED_PN  0x70
++#define IE_CALLED_SUB 0x71
++#define IE_REDIR_NR   0x74
++#define IE_TRANS_SEL  0x78
++#define IE_RESTART_IND        0x79
++#define IE_LLC                0x7c
++#define IE_HLC                0x7d
++#define IE_USER_USER  0x7e
++#define IE_ESCAPE     0x7f
++#define IE_SHIFT      0x90
++#define IE_MORE_DATA  0xa0
++#define IE_COMPLETE   0xa1
++#define IE_CONGESTION 0xb0
++#define IE_REPEAT     0xd0
++
++#define IE_MANDATORY  0x0100
++/* mandatory not in every case */
++#define IE_MANDATORY_1        0x0200
++
++#define ERR_IE_COMPREHENSION   1
++#define ERR_IE_UNRECOGNIZED   -1
++#define ERR_IE_LENGTH         -2
++#define ERR_IE_SEQUENCE               -3
++
++#define CAUSE_LOC_USER                0
++
++#define CAUSE_NORMAL_CLEARING 16
++#define CAUSE_CALL_REJECTED   21
++#define CAUSE_INVALID_NUMBER  28
++#define CAUSE_STATUS_RESPONSE 30
++#define CAUSE_NORMALUNSPECIFIED       31
++#define CAUSE_TEMPORARY_FAILURE       41
++#define CAUSE_RESOURCES_UNAVAIL 47
++#define CAUSE_INVALID_CALLREF 81
++#define CAUSE_MANDATORY_IE_MISS       96
++#define CAUSE_MT_NOTIMPLEMENTED       97
++#define CAUSE_IE_NOTIMPLEMENTED       99
++#define CAUSE_INVALID_CONTENTS        100
++#define CAUSE_NOTCOMPAT_STATE 101
++#define CAUSE_TIMER_EXPIRED   102
++#define CAUSE_PROTOCOL_ERROR  111
++
++#define NO_CAUSE              254
++
++#else /* only l3dss1_process */
++
++/* l3dss1 specific data in l3 process */
++typedef struct
++  { unsigned char invoke_id; /* used invoke id in remote ops, 0 = not active */
++    ulong ll_id; /* remebered ll id */
++    u_char remote_operation; /* handled remote operation, 0 = not active */ 
++    int proc; /* rememered procedure */  
++    ulong remote_result; /* result of remote operation for statcallb */
++    char uus1_data[35]; /* data send during alerting or disconnect */
++  } dss1_proc_priv;
++
++/* l3dss1 specific data in protocol stack */
++typedef struct
++  { unsigned char last_invoke_id; /* last used value for invoking */
++    unsigned char invoke_used[32]; /* 256 bits for 256 values */
++  } dss1_stk_priv;        
++
++#endif /* only l3dss1_process */
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dtmf.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/dtmf.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/dtmf.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/dtmf.c   2004-11-22 09:33:38.086753848 +0000
+@@ -0,0 +1,643 @@
++/* $Id$
++ *
++ * Linux ISDN subsystem, DTMF tone module
++ *
++ * Author     Karsten Keil (kkeil@suse.de)
++ *
++ * based on I4L isdn_audio code
++ * Copyright 2003 by Karsten Keil (kkeil@suse.de)
++ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
++ * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
++ * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++
++#define DTMF_NPOINTS 205        /* Number of samples for DTMF recognition */
++
++typedef struct _dtmf {
++      struct list_head        list;
++      u_long                  Flags;
++      int                     debug;
++      char                    last;
++      int                     idx;
++      int                     buf[DTMF_NPOINTS];
++      mISDNinstance_t         inst;
++} dtmf_t;
++
++#define       FLG_DTMF_ULAW   1
++#define FLG_DTMF_ACTIV        2
++
++static int debug = 0;
++
++#define DEBUG_DTMF_MGR                0x001
++#define DEBUG_DTMF_TONE               0x010
++#define DEBUG_DTMF_CTRL               0x020
++#define DEBUG_DTMF_DETECT     0x100
++#define DEBUG_DTMF_KOEFF      0x200
++
++static mISDNobject_t dtmf_obj;
++
++static char *mISDN_dtmf_revision = "$Revision$";
++
++/*
++ * Misc. lookup-tables.
++ */
++
++/* ulaw -> signed 16-bit */
++static short isdn_audio_ulaw_to_s16[] =
++{
++      0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
++      0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
++      0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
++      0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
++      0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
++      0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
++      0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
++      0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
++      0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
++      0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
++      0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
++      0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
++      0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
++      0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
++      0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
++      0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000,
++      0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
++      0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
++      0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
++      0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
++      0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
++      0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
++      0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
++      0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
++      0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
++      0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
++      0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
++      0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
++      0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
++      0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
++      0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
++      0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
++};
++
++/* alaw -> signed 16-bit */
++static short isdn_audio_alaw_to_s16[] =
++{
++      0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
++      0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
++      0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
++      0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
++      0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
++      0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
++      0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
++      0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
++      0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
++      0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
++      0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
++      0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
++      0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
++      0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
++      0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
++      0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
++      0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
++      0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
++      0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
++      0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
++      0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
++      0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
++      0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
++      0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
++      0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
++      0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
++      0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
++      0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
++      0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
++      0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
++      0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
++      0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
++};
++
++/* alaw -> ulaw */
++static char isdn_audio_alaw_to_ulaw[] =
++{
++      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 char isdn_audio_ulaw_to_alaw[] =
++{
++      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
++};
++
++#define NCOEFF            8     /* number of frequencies to be analyzed       */
++#define DTMF_TRESH     4000     /* above this is dtmf                         */
++#define SILENCE_TRESH   200     /* below this is silence                      */
++#define AMP_BITS          9     /* bits per sample, reduced to avoid overflow */
++#define LOGRP             0
++#define HIGRP             1
++
++/* For DTMF recognition:
++ * 2 * cos(2 * PI * k / N) precalculated for all k
++ */
++static int cos2pik[NCOEFF] =
++{
++      55813, 53604, 51193, 48591, 38114, 33057, 25889, 18332
++};
++
++static char dtmf_matrix[4][4] =
++{
++      {'1', '2', '3', 'A'},
++      {'4', '5', '6', 'B'},
++      {'7', '8', '9', 'C'},
++      {'*', '0', '#', 'D'}
++};
++
++static inline void
++isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n)
++{
++#ifdef __i386__
++      unsigned long d0, d1, d2, d3;
++      __asm__ __volatile__(
++              "cld\n"
++              "1:\tlodsb\n\t"
++              "xlatb\n\t"
++              "stosb\n\t"
++              "loop 1b\n\t"
++      :       "=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3)
++      :       "0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff)
++      :       "memory", "ax");
++#else
++      while (n--)
++              *buff = table[*(unsigned char *)buff], buff++;
++#endif
++}
++
++void
++isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len)
++{
++      isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len);
++}
++
++void
++isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len)
++{
++      isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len);
++}
++
++/*
++ * Goertzel algorithm.
++ * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/
++ * for more info.
++ */
++
++static void
++isdn_audio_goertzel(dtmf_t *dtmf)
++{
++      int             sk[NCOEFF], sk1[NCOEFF], sk2[NCOEFF];
++      register int    sample; 
++      int             k, n;
++      int             thresh, silence;
++      int             lgrp,hgrp;
++      char            what;
++
++      memset(sk, 0, NCOEFF*sizeof(int));
++      memset(sk1, 0, NCOEFF*sizeof(int));
++      memset(sk2, 0, NCOEFF*sizeof(int));
++      for (n = 0; n < DTMF_NPOINTS; n++) {
++              sample = dtmf->buf[n];
++              for (k = 0; k < NCOEFF; k++)
++                      sk[k] = sample + ((cos2pik[k] * sk1[k]) >> 15) - sk2[k];
++              memcpy(sk2, sk1, NCOEFF*sizeof(int));
++              memcpy(sk1, sk, NCOEFF*sizeof(int));
++      }       
++      thresh = silence = 0;
++      lgrp = hgrp = -1;
++      for (k = 0; k < NCOEFF; k++) {
++              sk[k] >>= 1;
++              sk2[k] >>= 1;
++              /* compute |X(k)|**2 */
++              /* report overflows. This should not happen. */
++              /* Comment this out if desired */
++              if (sk[k] < -32768 || sk[k] > 32767)
++                      printk(KERN_DEBUG
++                              "dtmf goertzel overflow, sk[%d]=%d\n", k, sk[k]);
++              if (sk2[k] < -32768 || sk2[k] > 32767)
++                      printk(KERN_DEBUG
++                              "isdn_audio: dtmf goertzel overflow, sk2[%d]=%d\n", k, sk2[k]);
++              sk1[k] = ((sk[k] * sk[k]) >> AMP_BITS) -
++                      ((((cos2pik[k] * sk[k]) >> 15) * sk2[k]) >> AMP_BITS) +
++                      ((sk2[k] * sk2[k]) >> AMP_BITS);
++              if (sk1[k] > DTMF_TRESH) {
++                      if (sk1[k] > thresh)
++                              thresh = sk1[k];
++              } else if (sk1[k] < SILENCE_TRESH)
++                      silence++;
++      }
++      if (dtmf->debug & DEBUG_DTMF_KOEFF)
++              printk(KERN_DEBUG "DTMF koeff(%d,%d,%d,%d,%d,%d,%d,%d) range(%d-%d)\n",
++                      sk1[0], sk1[1], sk1[2], sk1[3], sk1[4], sk1[5],
++                      sk1[6], sk1[7], SILENCE_TRESH, DTMF_TRESH);
++      if (silence == NCOEFF)
++              what = ' ';
++      else {
++              if (thresh > 0) {
++                      thresh = thresh >> 4;  /* touchtones must match within 12 dB */
++                      for (k = 0; k < NCOEFF; k++) {
++                              if (sk1[k] < thresh)
++                                      continue;  /* ignore */
++                              /* good level found. This is allowed only one time per group */
++                              if (k < NCOEFF / 2) {
++                                      /* lowgroup*/
++                                      if (lgrp >= 0) {
++                                              // Bad. Another tone found. */
++                                              lgrp = -1;
++                                              break;
++                                      } else
++                                              lgrp = k;
++                              } else { /* higroup */
++                                      if (hgrp >= 0) {
++                                              // Bad. Another tone found. */
++                                              hgrp = -1;
++                                              break;
++                                      } else
++                                              hgrp = k - NCOEFF/2;
++                              }
++                      }
++                      if ((lgrp >= 0) && (hgrp >= 0)) {
++                              what = dtmf_matrix[lgrp][hgrp];
++                              if (dtmf->last != ' ' && dtmf->last != '.')
++                                      dtmf->last = what;      /* min. 1 non-DTMF between DTMF */
++                      } else
++                                      what = '.';
++              } else
++                      what = '.';
++      }
++      if (dtmf->debug & DEBUG_DTMF_DETECT)
++              printk(KERN_DEBUG "DTMF: last(%c) what(%c)\n",
++                      dtmf->last, what);
++      if ((what != dtmf->last) && (what != ' ') && (what != '.')) {
++              if (dtmf->debug & DEBUG_DTMF_TONE)
++                      printk(KERN_DEBUG "DTMF: tone='%c'\n", what);
++              k = what | DTMF_TONE_VAL;
++              if_link(&dtmf->inst.up, PH_CONTROL | INDICATION,
++                      0, sizeof(int), &k, 0);
++      }
++      dtmf->last = what;
++}
++
++/*
++ * Decode audio stream into signed u16 
++ * start detection if enough data was sampled
++ */
++static void
++isdn_audio_calc_dtmf(dtmf_t *dtmf, struct sk_buff *skb)
++{
++      int len = skb->len;
++      u_char  *p = skb->data;
++      int i;
++      int c;
++
++      while (len) {
++              c = DTMF_NPOINTS - dtmf->idx;
++              if (c > len)
++                      c = len;
++              if (c <= 0)
++                      break;
++              for (i = 0; i < c; i++) {
++                      if (test_bit(FLG_DTMF_ULAW, &dtmf->Flags))
++                              dtmf->buf[dtmf->idx++] =
++                                      isdn_audio_ulaw_to_s16[*p++] >> (15 - AMP_BITS);
++                      else
++                              dtmf->buf[dtmf->idx++] =
++                                      isdn_audio_alaw_to_s16[*p++] >> (15 - AMP_BITS);
++              }
++              if (dtmf->idx == DTMF_NPOINTS) {
++                      isdn_audio_goertzel(dtmf);
++                      dtmf->idx = 0;
++              }
++              len -= c;
++      }
++}
++
++static void
++dtmf_reset(dtmf_t *dtmf)
++{
++      dtmf->last = ' ';
++      dtmf->idx = 0;
++}
++
++static int
++dtmf_from_up(mISDNif_t *hif, struct sk_buff *skb)
++{
++      dtmf_t          *dtmf;
++      mISDN_head_t    *hh;
++      int             *data;
++      int             err = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      dtmf = hif->fdata;
++      if (!dtmf->inst.down.func) {
++              return(-ENXIO);
++      }
++      hh = mISDN_HEAD_P(skb);
++      switch(hh->prim) {
++              case (PH_CONTROL | REQUEST):
++                      if ((hh->dinfo == 0) && (skb->len >= sizeof(int))) {
++                              data = (int *)skb->data;
++                              if (dtmf->debug & DEBUG_DTMF_CTRL)
++                                      printk(KERN_DEBUG "DTMF: PH_CONTROL REQ data %04x\n",
++                                              *data);
++                              if (*data == DTMF_TONE_START) {
++                                      test_and_set_bit(FLG_DTMF_ACTIV, &dtmf->Flags);
++                                      dtmf_reset(dtmf);
++                                      break;
++                              } else if (*data == DTMF_TONE_STOP) {
++                                      test_and_clear_bit(FLG_DTMF_ACTIV, &dtmf->Flags);
++                                      dtmf_reset(dtmf);
++                                      break;
++                              }
++                      }
++                      /* Fall trough in case of not handled function */
++              default:
++                      return(dtmf->inst.down.func(&dtmf->inst.down, skb));
++      }
++      if (!err)
++              dev_kfree_skb(skb);
++      return(err);
++}
++
++static int
++dtmf_from_down(mISDNif_t *hif,  struct sk_buff *skb)
++{
++      dtmf_t          *dtmf;
++      mISDN_head_t    *hh;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      dtmf = hif->fdata;
++      if (!dtmf->inst.up.func) {
++              return(-ENXIO);
++      }
++      hh = mISDN_HEAD_P(skb);
++      switch(hh->prim) {
++              case (PH_DATA | CONFIRM):
++                      hh->prim = DL_DATA | CONFIRM;
++                      break;
++              case (DL_DATA_IND):
++              case (PH_DATA_IND):
++                      if (test_bit(FLG_DTMF_ACTIV, &dtmf->Flags))
++                              isdn_audio_calc_dtmf(dtmf, skb);
++                      hh->prim = DL_DATA_IND;
++                      break;
++              case (PH_ACTIVATE | CONFIRM):
++                      hh->prim = DL_ESTABLISH | CONFIRM;
++                      break;
++              case (PH_ACTIVATE | INDICATION):
++                      hh->prim = DL_ESTABLISH | INDICATION;
++                      break;
++              case (PH_DEACTIVATE | CONFIRM):
++                      hh->prim = DL_RELEASE | CONFIRM;
++                      break;
++              case (PH_DEACTIVATE | INDICATION):
++                      hh->prim = DL_RELEASE | INDICATION;
++                      break;
++      }
++      return(dtmf->inst.up.func(&dtmf->inst.up, skb));
++}
++
++static void
++release_dtmf(dtmf_t *dtmf) {
++      mISDNinstance_t *inst = &dtmf->inst;
++
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      list_del(&dtmf->list);
++      dtmf_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      kfree(dtmf);
++}
++
++static int
++new_dtmf(mISDNstack_t *st, mISDN_pid_t *pid) {
++      dtmf_t *n_dtmf;
++      int err;
++
++      if (!st || !pid)
++              return(-EINVAL);
++      if (!(n_dtmf = kmalloc(sizeof(dtmf_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc dtmf_t failed\n");
++              return(-ENOMEM);
++      }
++      memset(n_dtmf, 0, sizeof(dtmf_t));
++      memcpy(&n_dtmf->inst.pid, pid, sizeof(mISDN_pid_t));
++      mISDN_init_instance(&n_dtmf->inst, &dtmf_obj, n_dtmf);
++      if (!mISDN_SetHandledPID(&dtmf_obj, &n_dtmf->inst.pid)) {
++              int_error();
++              kfree(n_dtmf);
++              return(-ENOPROTOOPT);
++      }
++      n_dtmf->debug = debug;
++      list_add_tail(&n_dtmf->list, &dtmf_obj.ilist);
++      err = dtmf_obj.ctrl(st, MGR_REGLAYER | INDICATION, &n_dtmf->inst);
++      if (err) {
++              list_del(&n_dtmf->list);
++              kfree(n_dtmf);
++      }
++      return(err);
++}
++
++#if 0
++static int
++dtmf_status(dtmf_t *dtmf, status_info_dtmf_t *si)
++{
++
++      if (!si)
++              return(-EINVAL);
++      memset(si, 0, sizeof(status_info_dtmf_t));
++      si->len = sizeof(status_info_dtmf_t) - 2*sizeof(int);
++      si->typ = STATUS_INFO_L1;
++      si->protocol = dtmf->inst.pid.protocol[1];
++      if (test_bit(FLG_L1_ACTIVATED, &dtmf->Flags))
++              si->status = 1;
++      si->state = dtmf->dtmfm.state;
++      si->Flags = dtmf->Flags;
++      si->T3 = TIMER3_VALUE;
++      si->debug = dtmf->delay;
++      si->debug = dtmf->debug;
++      return(0);
++}
++
++#endif
++
++static char MName[] = "DTMF";
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++MODULE_PARM(debug, "1i");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++#endif
++
++static int
++dtmf_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      dtmf_t          *dtmf_l;
++      int             ret = -EINVAL;
++
++      if (debug & DEBUG_DTMF_MGR)
++              printk(KERN_DEBUG "dtmf_manager data:%p prim:%x arg:%p\n", data, prim, arg);
++      if (!data)
++              return(ret);
++      list_for_each_entry(dtmf_l, &dtmf_obj.ilist, list) {
++              if (&dtmf_l->inst == inst) {
++                      ret = 0;
++                      break;
++              }
++      }
++      if (prim == (MGR_NEWLAYER | REQUEST))
++              return(new_dtmf(data, arg));
++      if (ret) {
++              printk(KERN_WARNING "dtmf_manager prim(%x) no instance\n", prim);
++              return(ret);
++      }
++      switch(prim) {
++          case MGR_CLRSTPARA | INDICATION:
++          case MGR_CLONELAYER | REQUEST:
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              return(mISDN_SetIF(inst, arg, prim, dtmf_from_up, dtmf_from_down, dtmf_l));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_UNREGLAYER | REQUEST:
++          case MGR_RELEASE | INDICATION:
++              if (debug & DEBUG_DTMF_MGR)
++                      printk(KERN_DEBUG "release_dtmf id %x\n", dtmf_l->inst.st->id);
++              release_dtmf(dtmf_l);
++              break;
++//        case MGR_STATUS | REQUEST:
++//            return(dtmf_status(dtmf_l, arg));
++          default:
++              if (debug & DEBUG_DTMF_MGR)
++                      printk(KERN_WARNING "dtmf_manager prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static int dtmf_init(void)
++{
++      int err;
++
++      printk(KERN_INFO "DTMF modul version %s\n", mISDN_getrev(mISDN_dtmf_revision));
++#ifdef MODULE
++      dtmf_obj.owner = THIS_MODULE;
++#endif
++      dtmf_obj.name = MName;
++      dtmf_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANSDTMF;
++      dtmf_obj.own_ctrl = dtmf_manager;
++      INIT_LIST_HEAD(&dtmf_obj.ilist);
++      if ((err = mISDN_register(&dtmf_obj))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++      }
++      return(err);
++}
++
++static void dtmf_cleanup(void)
++{
++      int     err;
++      dtmf_t  *dtmf, *nd;
++
++      if ((err = mISDN_unregister(&dtmf_obj))) {
++              printk(KERN_ERR "Can't unregister DTMF error(%d)\n", err);
++      }
++      if (!list_empty(&dtmf_obj.ilist)) {
++              printk(KERN_WARNING "dtmf inst list not empty\n");
++              list_for_each_entry_safe(dtmf, nd, &dtmf_obj.ilist, list)
++                      release_dtmf(dtmf);
++      }
++}
++
++module_init(dtmf_init);
++module_exit(dtmf_cleanup);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/faxl3.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/faxl3.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/faxl3.c      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/faxl3.c  2004-11-22 09:33:38.096752328 +0000
+@@ -0,0 +1,2185 @@
++/* $Id$
++ *
++ * Linux ISDN subsystem, Fax Layer 3
++ *
++ * Author     Karsten Keil (kkeil@suse.de)
++ *
++ * Copyright 2003 by Karsten Keil (kkeil@suse.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include "layer1.h"
++#include "m_capi.h"
++#include "helper.h"
++#include "debug.h"
++
++static int ttt=180;
++
++typedef struct _faxl3 {
++      struct list_head        list;
++      spinlock_t              lock;
++      u_long                  state;
++      int                     debug;
++      mISDNinstance_t         inst;
++      u16                     options;
++      u16                     format;
++      u8                      stationID[24];
++      u8                      headline[64];
++      u8                      DIS[12];
++      u8                      CIS[24];  // only max 20 are used
++      u8                      NSF[12];
++      u8                      DTC[12];
++      u8                      DCS[12];
++      u8                      CIG[24];
++      u8                      NSC[12];
++      u_int                   peer_rate_mask;
++      u_int                   own_rate_mask;
++      int                     current_rate_idx;
++      int                     current_mod;
++      int                     current_rate;
++      int                     pending_mod;
++      int                     pending_rate;
++      int                     result;
++      struct FsmInst          main;
++      struct FsmTimer         deltimer;
++      struct FsmTimer         timer1;
++      struct FsmInst          mod;
++      struct FsmTimer         modtimer;
++      struct sk_buff_head     downq;
++      struct sk_buff_head     dataq;
++      struct sk_buff_head     pageq;
++      struct sk_buff_head     saveq;
++      struct sk_buff          *data_skb;
++      struct sk_buff          *page_skb;
++      int                     entity;
++      int                     next_id;
++      int                     maxdatalen;
++      int                     up_headerlen;
++      int                     down_headerlen;
++      u32                     ncci;
++      // SFFHEADER
++      int                     pages;
++      u32                     offset_lpage;
++      u32                     offset_dend;
++      int                     current_page;
++      // SFF page header
++      u8                      page_vres;
++      u8                      page_hres;
++      u8                      page_code;
++      u8                      page_rsv1;
++      u16                     page_llen;
++      u16                     page_plen;
++      u32                     page_oprv;
++      u32                     page_onxt;
++      u8                      lasttyp;
++      int                     lastlen;
++      int                     line_cnt;
++      int                     page_retry;
++} faxl3_t;
++
++#define       FAXL3_STATE_OUTGOING    1
++#define FAXL3_STATE_SENT_TIS  2
++#define FAXL3_STATE_CAPICONNECT       3
++#define FAXL3_STATE_GOT_DIS   8
++#define FAXL3_STATE_GOT_CIS   9
++#define FAXL3_STATE_GOT_NSF   10
++#define FAXL3_STATE_SFFHEADER 16
++#define FAXL3_STATE_PAGEHEADER        17
++#define FAXL3_STATE_NEWPAGE   18
++#define FAXL3_STATE_LASTPAGE  19
++#define FAXL3_STATE_CONTINUE  20
++#define FAXL3_STATE_HAVEDATA  21
++#define FAXL3_STATE_DATABUSY  24
++#define FAXL3_STATE_DATALAST  25
++#define FAXL3_STATE_DATAREADY 26
++
++#define FAXL3_RESULT_NONE     0
++#define FAXL3_RESULT_CFR      1
++#define FAXL3_RESULT_FTT      2
++#define FAXL3_RESULT_MCF      3
++#define FAXL3_RESULT_RTP      4
++#define FAXL3_RESULT_RTN      5
++
++static char logbuf[2000];
++static int debug = 0;
++#define DEBUG_FAXL3_FUNC      0x0001
++#define DEBUG_FAXL3_MGR               0x0010
++#define DEBUG_FAXL3_CFG               0x0020
++#define DEBUG_FAXL3_MSG               0x0100
++#define DEBUG_FAXL3_SIG         0x0200
++#define DEBUG_FAXL3_PAGEPREPARE       0x1000
++
++static mISDNobject_t faxl3_obj;
++
++static char *mISDN_faxl3_revision = "$Revision$";
++
++static u_char FaxModulation[] = {24,48,72,74,96,98,122,146};
++static u_char FaxModulationTrain[] = {24,48,72,73,96,97,121,145};
++static int FaxModulationBaud[] = {2400,4800,7200,7200,9600,9600,12000,14400};
++
++#define MAX_FAXMODULATION_INDEX       7
++#define FAXMODULATION_MASK    0xff
++
++#define FAXMODM_UNDEF         0x00
++#define FAXMODM_V27           0x03
++#define FAXMODM_V27_V29               0x17
++#define FAXMODM_V27_V29_V33   0x17 //We don't have V.33 definition yet
++#define FAXMODM_V27_V29_V33_V17       0xff
++
++static u_int FaxModulationRates[16] = {
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF,
++      FAXMODM_V27,
++      FAXMODM_V27_V29,
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF,
++      FAXMODM_V27_V29_V33,
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF,
++      FAXMODM_V27_V29_V33_V17,
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF,
++      FAXMODM_UNDEF
++};
++
++static u8 FaxModulationRates_DCS[8] = {
++      0x0,
++      0x2,
++      0x3,
++      0xb,
++      0x1,
++      0x9,
++      0xa,
++      0x8
++};
++
++#define Dxx_TYPE_DIS  0
++#define Dxx_TYPE_DTC  1
++#define Dxx_TYPE_DCS  2
++
++static void   l3m_debug(struct FsmInst *fi, char *fmt, ...);
++static int    send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len);
++static int    sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb);
++static int    send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info);
++static int    prepare_page_data(faxl3_t *fl3);
++static int    copy_page_data4retry(faxl3_t *fl3);
++
++static
++struct Fsm faxl3fsm = {NULL, 0, 0, NULL, NULL};
++
++enum {
++      ST_L3_IDLE,
++      ST_L3_WAIT_RECVDIS,
++      ST_L3_RECV_DIS,
++      ST_L3_WAIT_SENDDCS,
++      ST_L3_SEND_DCS,
++      ST_L3_WAIT_SENDTRAIN, 
++      ST_L3_SEND_TRAIN,
++      ST_L3_WAIT_TRAINSTATE,
++      ST_L3_RECV_TRAINSTATE,
++      ST_L3_WAIT_SENDPAGE,
++      ST_L3_SEND_PAGE,
++      ST_L3_WAIT_PAGESTATE,
++      ST_L3_RECV_PAGESTATE,
++      ST_L3_WAIT_SENDEOP,
++      ST_L3_SEND_EOP,
++      ST_L3_WAIT_RECVMCF,
++      ST_L3_RECV_MCF,
++      ST_L3_WAIT_SENDDCN,
++      ST_L3_SEND_DCN,
++      ST_L3_CLEARING,
++};
++
++#define FAXL3_STATE_COUNT (ST_L3_CLEARING+1)
++
++static char *strfaxl3State[] =
++{
++      "ST_L3_IDLE",
++      "ST_L3_WAIT_RECVDIS",
++      "ST_L3_RECV_DIS",
++      "ST_L3_WAIT_SENDDCS",
++      "ST_L3_SEND_DCS",
++      "ST_L3_WAIT_SENDTRAIN", 
++      "ST_L3_SEND_TRAIN",
++      "ST_L3_WAIT_TRAINSTATE",
++      "ST_L3_RECV_TRAINSTATE",
++      "ST_L3_WAIT_SENDPAGE",
++      "ST_L3_SEND_PAGE",
++      "ST_L3_WAIT_PAGESTATE",
++      "ST_L3_RECV_PAGESTATE",
++      "ST_L3_WAIT_SENDEOP",
++      "ST_L3_SEND_EOP",
++      "ST_L3_WAIT_RECVMCF",
++      "ST_L3_RECV_MCF",
++      "ST_L3_WAIT_SENDDCN",
++      "ST_L3_SEND_DCN",
++      "ST_L3_CLEARING",
++};
++
++enum {
++      EV_CALL_OUT,
++      EV_MODEM_ACTIV,
++      EV_MODEM_IDLE,
++      EV_MODEM_ERROR,
++      EV_DATA,
++      EV_NEXT_DATA,
++      EV_DELAYTIMER,
++      EV_CLEARING,
++};
++
++#define FAXL3_EVENT_COUNT (EV_CLEARING + 1)
++
++static char *strfaxl3Event[] =
++{
++      "EV_CALL_OUT",
++      "EV_MODEM_ACTIV",
++      "EV_MODEM_IDLE",
++      "EV_MODEM_ERROR",
++      "EV_DATA",
++      "EV_NEXT_DATA",
++      "EV_DELAYTIMER",
++      "EV_CLEARING",
++};
++
++static
++struct Fsm modfsm = {NULL, 0, 0, NULL, NULL};
++
++enum {
++      ST_MOD_NULL,
++      ST_MOD_IDLE,
++      ST_MOD_WAITCONNECT,
++      ST_MOD_CONNECTED,
++      ST_MOD_WAITDISCONNECT,
++};
++
++#define MOD_STATE_COUNT (ST_MOD_WAITDISCONNECT + 1)
++
++static char *strmodState[] =
++{
++      "ST_MOD_NULL",
++      "ST_MOD_IDLE",
++      "ST_MOD_WAITCONNECT",
++      "ST_MOD_CONNECTED",
++      "ST_MOD_WAITDISCONNECT",
++};
++
++enum {
++      EV_MOD_READY,
++      EV_MOD_NEW,
++      EV_MOD_CONNECT,
++      EV_MOD_DISCONNECT,
++      EV_MOD_NOCARRIER,
++      EV_MOD_ERROR,
++      EV_MOD_TIMEOUT,
++};
++
++#define MOD_EVENT_COUNT (EV_MOD_TIMEOUT + 1)
++
++static char *strmodEvent[] =
++{
++      "EV_MOD_READY",
++      "EV_MOD_NEW",
++      "EV_MOD_CONNECT",
++      "EV_MOD_DISCONNECT",
++      "EV_MOD_NOCARRIER",
++      "EV_MOD_ERROR",
++      "EV_MOD_TIMEOUT",
++};
++
++static int
++data_next_id(faxl3_t *fl3)
++{
++      u_long  flags;
++      int     id;
++
++      spin_lock_irqsave(&fl3->lock, flags);
++      id = fl3->next_id++;
++      if (id == 0x0fff)
++              fl3->next_id = 1;
++      spin_unlock_irqrestore(&fl3->lock, flags);
++      id |= (fl3->entity << 16);
++      return(id);
++}
++
++static void
++print_hexdata(faxl3_t *fl3, char *head, int len, char *data)
++{
++      char    *t = logbuf;
++
++      t += sprintf(logbuf, "%s", head);
++      if (len > 650)
++              len = 650;
++      mISDN_QuickHex(t, data, len);
++      printk(KERN_DEBUG "%s\n", logbuf);
++}
++
++static char *rate_1[16] = {
++      "undef",
++      "undef",
++      "2400,4800 V.27ter",
++      "9600,7200,4800,2400 V.27ter/V.29",
++      "undef",
++      "undef",
++      "undef",
++      "14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33",
++      "undef",
++      "undef",
++      "undef",
++      "14400,12000,9600,7200,4800,2400 V.27ter/V.29/V.33/V.17",
++      "undef",
++      "undef",
++      "undef",
++      "undef"
++};
++
++static char *rate_2[16] = {
++      "2400(V.27ter)",
++      "9600(V.29)",
++      "4800(V.27ter)",
++      "7200(V.29)",
++      "14400(V.33)",
++      "undef",
++      "12000(V.33)",
++      "undef",
++      "14400(V.17)",
++      "9600(V.17)",
++      "12000(V.17)",
++      "7200(V.17)",
++      "undef",
++      "undef",
++      "undef",
++      "undef"
++};
++
++static char *pwidth_1[4] = {
++      "1728/A4",
++      "1728/A4 2048/B4",
++      "1728/A4 2048/B4 2432/A3",
++      "undef"
++};
++
++static char *pwidth_2[4] = {
++      "1728 A4",
++      "2048 B4",
++      "2432 A3",
++      "undef"
++};
++
++static char *plength[4] = {
++      "297/A4",
++      "364/B4",
++      "unlimited",
++      "undef"
++};
++
++static char *minrowtime_1[8] = {
++      "20ms",
++      "5ms",
++      "10ms",
++      "20ms*",
++      "40ms",
++      "40ms*",
++      "10ms*",
++      "0ms"
++};
++
++static char *minrowtime_2[8] = {
++      "20ms",
++      "5ms",
++      "10ms",
++      " ",
++      "40ms",
++      " ",
++      " ",
++      "0ms"
++};
++
++static void
++print_Dxx(faxl3_t *fl3, int typ)
++{
++      char    *ts;
++      u8      *p, v1,v2,v3;
++
++      switch (typ) {
++              case Dxx_TYPE_DIS:
++                      ts = "DIS";
++                      p = fl3->DIS;
++                      break;
++              case Dxx_TYPE_DTC:
++                      ts = "DTC";
++                      p = fl3->DTC;
++                      break;
++              case Dxx_TYPE_DCS:
++                      ts = "DCS";
++                      p = fl3->DCS;
++                      break;
++              default:
++                      int_error();
++                      return;
++      }
++      /* OK byte one is only for group 1/2 compatibility */
++      printk(KERN_DEBUG "%s: byte1 %02X\n", ts, *p);
++      v1 = (p[1] >> 2) & 0xf;
++      printk(KERN_DEBUG "%s:%s%s %s%s%s\n", ts,
++              (test_bit(8, (u_long *)p) && (typ != Dxx_TYPE_DCS)) ? " SendG3" : "",
++              (test_bit(9, (u_long *)p)) ? " RecvG3" : "",
++              (typ == Dxx_TYPE_DCS) ? rate_2[v1] : rate_1[v1],
++              (test_bit(14, (u_long *)p)) ? " 7,7Row/mm" : "",
++              (test_bit(15, (u_long *)p)) ? " 2-Dim" : "");
++
++      v1 = p[2] & 3;
++      v2 = (p[2] >> 2) & 3;
++      v3 = (p[2] >> 4) & 7;
++      printk(KERN_DEBUG "%s: width(%s) plength(%s) MinRow(%s)\n", ts,
++              (typ == Dxx_TYPE_DCS) ? pwidth_2[v1] : pwidth_1[v1],
++              plength[v2],
++              (typ == Dxx_TYPE_DCS) ? minrowtime_2[v3] : minrowtime_1[v3]);
++
++      if (!test_bit(23, (u_long *)p))
++              return;
++
++      if (typ == Dxx_TYPE_DCS)
++              printk(KERN_DEBUG "%s:%s%s%s BS(%s)%s\n", ts,
++                      (test_bit(24, (u_long *)p)) ? " 2400" : "",
++                      (test_bit(25, (u_long *)p)) ? " uncompressed" : "",
++                      (test_bit(26, (u_long *)p)) ? " ECM" : "",
++                      (test_bit(27, (u_long *)p)) ? "64" : "256",
++                      (test_bit(30, (u_long *)p)) ? " MMR" : "");
++      else
++              printk(KERN_DEBUG "%s:%s%s%s%s\n", ts,
++                      (test_bit(24, (u_long *)p)) ? " 2400" : "",
++                      (test_bit(25, (u_long *)p)) ? " uncompressed" : "",
++                      (test_bit(26, (u_long *)p)) ? " ECM" : "",
++                      (test_bit(30, (u_long *)p)) ? " MMR" : "");
++      if (!test_bit(31, (u_long *)p))
++              return;
++      /* byte is reseved */
++      if (!test_bit(39, (u_long *)p))
++              return;
++// TODO
++      if (!test_bit(47, (u_long *)p))
++              return;
++// TODO
++      if (!test_bit(55, (u_long *)p))
++              return;
++// TODO
++      if (!test_bit(63, (u_long *)p))
++              return;
++// TODO
++}
++
++static u8
++calc_dtcrate(faxl3_t *fl3)
++{
++      if ((FAXMODM_V27_V29_V33_V17 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33_V17)
++              return(11);
++      if ((FAXMODM_V27_V29_V33 & fl3->own_rate_mask) == FAXMODM_V27_V29_V33)
++              return(7);
++      if ((FAXMODM_V27_V29 & fl3->own_rate_mask) == FAXMODM_V27_V29)
++              return(3);
++      if ((FAXMODM_V27& fl3->own_rate_mask) == FAXMODM_V27)
++              return(2);
++      return(0);
++}
++
++static u8
++calc_dcsrate(faxl3_t *fl3)
++{
++      if ((fl3->current_rate_idx > MAX_FAXMODULATION_INDEX) ||
++              (fl3->current_rate_idx < 0)) {
++              int_errtxt("current_rate_idx(%d)", fl3->current_rate_idx);
++              return(0xf);
++      } 
++      return(FaxModulationRates_DCS[fl3->current_rate_idx]);
++}
++
++static void
++fill_Dxx(faxl3_t *fl3, int typ)
++{
++      u8      *p, v1,v2,v3;
++
++      switch (typ) {
++              case Dxx_TYPE_DIS:
++                      p = fl3->DIS;
++                      break;
++              case Dxx_TYPE_DTC:
++                      p = fl3->DTC;
++                      break;
++              case Dxx_TYPE_DCS:
++                      p = fl3->DCS;
++                      break;
++              default:
++                      int_error();
++                      return;
++      }
++      memset(p, 0, 12); // clear all bits
++      /* OK byte one is only for group 1/2 compatibility, skipped */
++      if (typ == Dxx_TYPE_DCS)
++              v1 = calc_dcsrate(fl3);
++      else
++              v1 = calc_dtcrate(fl3);
++      p[1] = v1 << 2;
++      if (typ == Dxx_TYPE_DCS)
++              test_and_set_bit(9, (u_long *)p);
++      else
++              test_and_set_bit(8, (u_long *)p);
++      if (fl3->options & 1)
++              test_and_set_bit(14, (u_long *)p);
++// TODO: calc
++      test_and_set_bit(14, (u_long *)p);
++      v1 = 0; // A4, TODO: calc 
++      v2 = 2; // unlimited, TODO: calc
++      v3 = 7; // 0 ms, TODO: calc
++      p[2] = v1 | (v2 << 2) | (v3 << 4);
++      test_and_set_bit(23, (u_long *)p); // next byte exist
++      p[3] = 0; // TODO: calc
++}
++
++static int
++send_Dxx(faxl3_t *fl3, int typ, int last)
++{
++      u8      *p, fcf, hdlc_cf = last ? 0x13 : 3;
++      int     len;
++
++      switch (typ) {
++              case Dxx_TYPE_DIS:
++                      p = fl3->DIS;
++                      fcf = 80;
++                      break;
++              case Dxx_TYPE_DTC:
++                      p = fl3->DTC;
++                      fcf = 0x81;
++                      break;
++              case Dxx_TYPE_DCS:
++                      p = fl3->DCS;
++                      fcf = 0x83;
++                      break;
++              default:
++                      int_error();
++                      return(-EINVAL);
++      }
++      if (!test_bit(23, (u_long *)p))
++              len = 3;
++      else if (!test_bit(31, (u_long *)p))
++              len = 4;
++      else if (!test_bit(39, (u_long *)p))
++              len = 5;
++      else if (!test_bit(47, (u_long *)p))
++              len = 6;
++      else if (!test_bit(55, (u_long *)p))
++              len = 7;
++      else if (!test_bit(63, (u_long *)p))
++              len = 8;
++      else
++              len = 9;
++      return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, p, len));
++}
++
++static int
++send_char20(faxl3_t *fl3, u8 *p, int fcf, int last)
++{
++      u8      buf[20], *s, hdlc_cf = last ? 0x13 : 3;
++      int     len, i;
++
++      memset(buf, ' ', 20);
++      len = strlen(p);
++      if (len > 20)
++              len = 20;
++      s = buf;
++      for (i=len; i>0; i--)
++              *s++ = p[i-1];
++      return(send_hdlc_data(fl3, 0xff, hdlc_cf, fcf, buf, 20));
++}
++
++static int
++is_valid_rate_idx(faxl3_t *fl3, int ridx)
++{
++      if ((ridx > MAX_FAXMODULATION_INDEX) || (ridx < 0))
++              return(0);
++      if (((1<<ridx) & fl3->own_rate_mask & fl3->peer_rate_mask) == 0)
++              return(0);
++      else
++              return(1);
++}
++
++static int
++fallback_rate(faxl3_t *fl3)
++{
++      fl3->current_rate_idx--;
++      while ((fl3->current_rate_idx >= 0) &&
++              !is_valid_rate_idx(fl3, fl3->current_rate_idx)) {
++              fl3->current_rate_idx--;
++      }
++      return(is_valid_rate_idx(fl3, fl3->current_rate_idx));
++}
++
++static int
++calc_max_rate(faxl3_t *fl3)
++{
++      int i;
++
++      for (i = MAX_FAXMODULATION_INDEX; i >= 0; i--) {
++              if (is_valid_rate_idx(fl3, i))
++                      return(i);
++      }
++      return(-1);
++}
++
++static int
++send_data_down(faxl3_t *fl3, struct sk_buff *skb) {
++      int             ret = 0;
++      mISDNif_t       *down = &fl3->inst.down;
++
++      if (test_and_set_bit(FAXL3_STATE_DATABUSY, &fl3->state)) {
++              skb_queue_tail(&fl3->downq, skb);
++      } else {
++              mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb);
++              ret = down->func(down, skb);
++              if (ret) {
++                      int_errtxt("down: error(%d)", ret);
++              }
++      }
++      return(ret);
++}
++
++static int
++send_hdlc_data(faxl3_t *fl3, u8 adr, u8 hcf, u8 fcf, u8 *para, int len)
++{
++      struct sk_buff  *skb;
++      u_char          *p;
++      int             ret;
++
++      if (!(skb = alloc_stack_skb(3 + len, 1)))
++              return(-ENOMEM);
++      p = skb_put(skb, 3);
++      *p++ = adr;
++      *p++ = hcf;
++      *p++ = fcf;
++      if (len)
++              memcpy(skb_put(skb, len), para, len);
++      ret = send_data_down(fl3, skb);
++      if (ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++static void
++mod_init(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++      int err;
++
++      err = if_link(&fl3->inst.down, PH_ACTIVATE | REQUEST, 0, 0, NULL, 0);
++      if (err) {
++              int_error();
++              return;
++      }
++}
++
++static void
++set_new_modulation(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++      int err;
++
++      if ((fl3->pending_mod < 0) || (fl3->pending_rate <0)) {
++              if (event == EV_MOD_READY)
++                      mISDN_FsmChangeState(fi, ST_MOD_IDLE);
++              else
++                      int_error();
++              return;
++      }
++      mISDN_FsmChangeState(fi, ST_MOD_WAITCONNECT);
++      err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, fl3->pending_mod, sizeof(int), &fl3->pending_rate, 0);
++      if (err) {
++              int_error();
++              return;
++      }
++}
++
++static void
++mod_activ(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_MOD_CONNECTED);
++      fl3->current_mod = fl3->pending_mod;
++      fl3->pending_mod = -1;
++      fl3->current_rate = fl3->pending_rate;
++      fl3->pending_rate = -1;
++      mISDN_FsmEvent(&fl3->main, EV_MODEM_ACTIV, NULL);       
++}
++
++static void
++mod_disconnect(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_MOD_WAITDISCONNECT);
++}
++
++static void
++mod_error(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_MOD_IDLE);
++      mISDN_FsmEvent(&fl3->main, EV_MODEM_ERROR, NULL);       
++}
++
++static void
++mod_nocarrier(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_MOD_IDLE);
++      mISDN_FsmEvent(&fl3->main, EV_MODEM_IDLE, NULL);        
++}
++
++static struct FsmNode ModFnList[] =
++{
++      {ST_MOD_NULL,           EV_MOD_NEW,             mod_init},
++      {ST_MOD_NULL,           EV_MOD_READY,           set_new_modulation},
++      {ST_MOD_IDLE,           EV_MOD_READY,           set_new_modulation},
++      {ST_MOD_IDLE,           EV_MOD_NEW,             set_new_modulation},
++      {ST_MOD_WAITCONNECT,    EV_MOD_CONNECT,         mod_activ},
++      {ST_MOD_WAITCONNECT,    EV_MOD_ERROR,           mod_error},
++      {ST_MOD_CONNECTED,      EV_MOD_NOCARRIER,       mod_nocarrier},
++      {ST_MOD_CONNECTED,      EV_MOD_DISCONNECT,      mod_disconnect},
++      {ST_MOD_WAITDISCONNECT, EV_MOD_NOCARRIER,       mod_nocarrier},
++};
++
++#define MOD_FN_COUNT (sizeof(ModFnList)/sizeof(struct FsmNode))
++
++
++static void
++l3m_callout(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVDIS);
++}
++
++static void
++l3m_activ_dis(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_L3_RECV_DIS);
++}
++
++static int
++get_Dxx(u8 *dst, u8 *src, int len) {
++      if (len < 3) {
++              int_errtxt("Dxx too short %d", len);
++              return(-1);
++      }
++      if (len > 12)
++              len = 12; // normally max 9 bytes
++      memcpy(dst, src, len);
++      return(0);
++}
++
++static int
++get_CHAR20(u8 *dst, u8 *src, int len) {
++      int i;
++
++      if (len <= 0) {
++              int_errtxt("string too short %d", len);
++              return(-1);
++      }
++      if (len > 20) {
++              int_errtxt("string too big (%d) rest ignored", len);
++              len = 20;
++      }
++      for (i = 20; i > len; i--)
++              dst[20-i] = ' ';
++      for (; i > 0; i--)
++              dst[20-i] = src[i-1];
++      dst[20] = 0;
++      return(0);
++}
++
++static int
++get_Nxx(u8 *dst, u8 *src, int len) {
++      if (len < 2) {
++              int_errtxt("Nxx too short %d", len);
++              return(-1);
++      }
++      if (len > 12) {
++              int_errtxt("Nxx too big (%d) ignored", len);
++              return(-2);
++      }
++      memcpy(dst, src, len);
++      return(0);
++}
++
++static void
++init_newpage(faxl3_t *fl3)
++{
++      fl3->page_retry = 0;
++      fl3->line_cnt = 0;
++      fl3->result = 0;
++      discard_queue(&fl3->pageq);
++      discard_queue(&fl3->saveq);
++      test_and_clear_bit(FAXL3_STATE_NEWPAGE, &fl3->state);
++}
++
++static void
++l3m_receive_dis(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t         *fl3 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      u8              end, *p = skb->data;
++
++      if (skb->len < 3) {
++              int_errtxt("HDLC too short %d", skb->len);
++              return;
++      }
++      if (*p != 0xff) {
++              int_errtxt("HDLC addr not FF (%02X)", *p);
++      }
++      p++;
++      if (*p == 0x03) {
++              end = 0;
++      } else if (*p == 0x13) {
++              end = 1;
++      } else {
++              int_errtxt("wrong HDLC CTRL (%02X)", *p);
++      }
++      p++;
++      skb_pull(skb, 3);
++      switch(*p) {
++              case 0x80: // DIS
++                      if (0 == get_Dxx(fl3->DIS, p+1, skb->len))
++                              test_and_set_bit(FAXL3_STATE_GOT_DIS, &fl3->state);
++                      break;
++              case 0x40: // CIS
++                      if (0 == get_CHAR20(fl3->CIS, p+1, skb->len))
++                              test_and_set_bit(FAXL3_STATE_GOT_CIS, &fl3->state);
++                      break;
++              case 0x20: // NSF
++                      if (0 == get_Nxx(fl3->NSF, p+1, skb->len))
++                              test_and_set_bit(FAXL3_STATE_GOT_NSF, &fl3->state);
++                      break;
++              default:
++                      int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len);
++                      break;
++      }
++}
++
++static void
++l3m_finish_dis(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      if (fl3->debug & DEBUG_FAXL3_SIG) {
++              if (test_bit(FAXL3_STATE_GOT_DIS, &fl3->state))
++                      print_Dxx(fl3, Dxx_TYPE_DIS);
++              if (test_bit(FAXL3_STATE_GOT_CIS, &fl3->state))
++                      printk(KERN_DEBUG "CIS: %s\n", fl3->CIS);
++              if (test_bit(FAXL3_STATE_GOT_NSF, &fl3->state))
++                      print_hexdata(fl3, "NSF: ", 12, fl3->NSF);
++      }
++      fl3->peer_rate_mask = FaxModulationRates[(fl3->DIS[1]>>2) &0xf];
++      fl3->current_rate_idx = calc_max_rate(fl3);
++      if (fl3->current_rate_idx > 0) {
++              fill_Dxx(fl3, Dxx_TYPE_DCS);
++              if (fl3->debug & DEBUG_FAXL3_SIG)
++                      print_Dxx(fl3, Dxx_TYPE_DCS);
++              fl3->pending_mod = HW_MOD_FTH;
++              fl3->pending_rate = 3;
++              mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS);
++              mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++      } else // ABORT
++              mISDN_FsmChangeState(fi, ST_L3_IDLE);
++}
++
++static void
++l3m_send_dcs(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L3_SEND_DCS);
++      if (!test_and_set_bit(FAXL3_STATE_SENT_TIS, &fl3->state))
++              send_char20(fl3, &fl3->stationID[1], 0x43, 0);
++      send_Dxx(fl3, Dxx_TYPE_DCS, 1);
++}
++
++static void
++l3m_send_lastdata(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++      int     err;
++
++      if (test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state)) {
++              err = 0;
++              err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0);
++              if (err) {
++                      int_error();
++                      return;
++              }
++      }
++}
++
++static void
++l3m_finish_dcs(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDTRAIN);
++      /* wait 75 ms */
++      mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2);
++}
++
++static void
++l3m_setup_train(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) {
++              fl3->pending_mod = HW_MOD_FTM;
++              fl3->pending_rate = FaxModulationTrain[fl3->current_rate_idx];
++              mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++      } else // ABORT
++              mISDN_FsmChangeState(fi, ST_L3_IDLE);
++}
++
++static void
++l3m_send_train(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t         *fl3 = fi->userdata;
++      struct sk_buff  *skb;
++      int             len, ret;
++
++      mISDN_FsmChangeState(fi, ST_L3_SEND_TRAIN);
++      len = 3*(FaxModulationBaud[fl3->current_rate_idx]/16); // 1,5 sec
++      if (!(skb = alloc_stack_skb(len, 1)))
++              return;
++      memset(skb_put(skb, len), 0, len);
++      test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
++      ret = send_data_down(fl3, skb);
++      if (ret) {
++              dev_kfree_skb(skb);
++      }
++}
++
++static void
++l3m_finish_train(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      fl3->pending_mod = HW_MOD_FRH;
++      fl3->pending_rate = 3;
++      mISDN_FsmChangeState(fi, ST_L3_WAIT_TRAINSTATE);
++      mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++}
++
++static void
++l3m_activ_trainstate(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_L3_RECV_TRAINSTATE);
++}
++
++static void
++l3m_receive_trainstate(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t         *fl3 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      u8              end, *p = skb->data;
++
++      if (skb->len < 3) {
++              int_errtxt("HDLC too short %d", skb->len);
++              return;
++      }
++      if (*p != 0xff) {
++              int_errtxt("HDLC addr not FF (%02X)", *p);
++      }
++      p++;
++      if (*p == 0x03) {
++              end = 0;
++      } else if (*p == 0x13) {
++              end = 1;
++      } else {
++              int_errtxt("wrong HDLC CTRL (%02X)", *p);
++      }
++      p++;
++      skb_pull(skb, 3);
++      switch(*p) {
++              case 0x84: // CFR
++                      fl3->result = FAXL3_RESULT_CFR;
++                      printk(KERN_DEBUG "training successfull\n");
++                      if (!test_and_set_bit(FAXL3_STATE_CAPICONNECT, &fl3->state))
++                              send_capi_msg_ncpi(fl3, CAPI_CONNECT_B3_ACTIVE_IND, 0);
++                      break;
++              case 0x44: // FTT
++                      fl3->result = FAXL3_RESULT_FTT;
++                      printk(KERN_DEBUG "training failed\n");
++                      break;
++              default:
++                      fl3->result = FAXL3_RESULT_NONE;
++                      int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len);
++                      break;
++      }
++}
++
++static void
++l3m_finish_trainstate(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      if (fl3->result == FAXL3_RESULT_FTT) {
++              fl3->pending_mod = HW_MOD_FTH;
++              fl3->pending_rate = 3;
++              if (fallback_rate(fl3)) {
++                      fill_Dxx(fl3, Dxx_TYPE_DCS);
++                      mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCS);
++              } else {
++                      mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDDCN);
++              }
++      } else {
++              mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE);
++              mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2);
++      }
++}
++
++static void
++l3m_setup_sendpage(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      if (is_valid_rate_idx(fl3, fl3->current_rate_idx)) {
++              fl3->pending_mod = HW_MOD_FTM;
++              fl3->pending_rate = FaxModulation[fl3->current_rate_idx];
++              mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++      } else // ABORT
++              mISDN_FsmChangeState(fi, ST_L3_IDLE);
++}
++
++static void
++l3m_ready_sendpage(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++      struct sk_buff  *skb;
++      int             ret;
++
++      mISDN_FsmChangeState(fi, ST_L3_SEND_PAGE);
++      if (!(skb = alloc_stack_skb(4000, 1)))
++              return;
++//    memset(skb_put(skb, 1000), 0xff, 1000);
++//    memset(skb_put(skb, 1000), 0, 1000);
++//    memset(skb_put(skb, 100), 0xff, 100);
++      memset(skb_put(skb, ttt), 0, ttt);
++      test_and_set_bit(FAXL3_STATE_DATAREADY, &fl3->state);
++      ret = send_data_down(fl3, skb);
++      if (ret) {
++              dev_kfree_skb(skb);
++      }
++}
++
++static void
++l3m_send_next_pagedata(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t         *fl3 = fi->userdata;
++      struct sk_buff  *skb;
++      int             err;
++
++start:
++      if (test_bit(FAXL3_STATE_DATALAST, &fl3->state)) {
++              if (skb_queue_empty(&fl3->pageq)) {
++                      err = 0;
++                      err = if_link(&fl3->inst.down, PH_CONTROL | REQUEST, HW_MOD_LASTDATA, sizeof(int), &err, 0);
++                      if (err) {
++                              int_error();
++                              return;
++                      }
++                      test_and_clear_bit(FAXL3_STATE_DATALAST, &fl3->state);
++              } else {
++                      while((skb = skb_dequeue(&fl3->pageq)))
++                              send_data_down(fl3, skb);
++              }
++      } else {
++              if (!fl3->page_retry) {
++                      prepare_page_data(fl3);
++              } else if (skb_queue_empty(&fl3->pageq)) {
++                      test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
++                      goto start;
++              }
++              if ((skb = skb_dequeue(&fl3->pageq)))
++                      send_data_down(fl3, skb);
++      }
++}
++
++static void
++l3m_finish_page(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      test_and_clear_bit(FAXL3_STATE_DATAREADY, &fl3->state);
++      fl3->pending_mod = HW_MOD_FTH;
++      fl3->pending_rate = 3;
++      mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDEOP);
++      mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++}
++
++static void
++l3m_send_endofpage(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++      u8      fcf;
++
++      mISDN_FsmChangeState(fi, ST_L3_SEND_EOP);
++      if (test_bit(FAXL3_STATE_LASTPAGE, &fl3->state))
++              fcf = 0x2f; // EOP
++      else
++              fcf = 0x4f; // MPS
++      send_hdlc_data(fl3, 0xff, 0x13, fcf, NULL, 0);
++}
++
++static void
++l3m_finish_eop(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      fl3->pending_mod = HW_MOD_FRH;
++      fl3->pending_rate = 3;
++      mISDN_FsmChangeState(fi, ST_L3_WAIT_RECVMCF);
++      mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++}
++
++static void
++l3m_activ_mcf(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_L3_RECV_MCF);
++}
++
++static void
++l3m_receive_mcf(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t         *fl3 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      u8              end, *p = skb->data;
++
++      if (skb->len < 3) {
++              int_errtxt("HDLC too short %d", skb->len);
++              return;
++      }
++      if (*p != 0xff) {
++              int_errtxt("HDLC addr not FF (%02X)", *p);
++      }
++      p++;
++      if (*p == 0x03) {
++              end = 0;
++      } else if (*p == 0x13) {
++              end = 1;
++      } else {
++              int_errtxt("wrong HDLC CTRL (%02X)", *p);
++      }
++      p++;
++      skb_pull(skb, 3);
++      switch(*p) {
++              case 0x8C: // MCF
++                      fl3->result = FAXL3_RESULT_MCF;
++                      printk(KERN_DEBUG "got MCF\n");
++                      break;
++              case 0xCC: // RTP
++                      fl3->result = FAXL3_RESULT_RTP;
++                      printk(KERN_DEBUG "got RTP\n");
++                      break;
++              case 0x4C: // RTN
++                      fl3->result = FAXL3_RESULT_RTN;
++                      printk(KERN_DEBUG "got RTN\n");
++                      break;
++              default:
++                      fl3->result = FAXL3_RESULT_NONE;
++                      int_errtxt("unhandled FCF (%02X) len %d", *p, skb->len);
++                      break;
++      }
++}
++
++static void
++l3m_finish_mcf(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++      int     newstate = ST_L3_WAIT_SENDDCN;
++
++      if (fl3->result == FAXL3_RESULT_RTN) {
++              fl3->page_retry++;
++              if ((fl3->page_retry < 5) && fallback_rate(fl3)) {
++                      newstate = ST_L3_WAIT_SENDDCS;
++                      fill_Dxx(fl3, Dxx_TYPE_DCS);
++                      copy_page_data4retry(fl3);
++              }
++      } else if (fl3->result == FAXL3_RESULT_MCF) {
++              if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) {
++                      init_newpage(fl3);
++                      prepare_page_data(fl3);
++                      mISDN_FsmChangeState(fi, ST_L3_WAIT_SENDPAGE);
++                      mISDN_FsmRestartTimer(&fl3->deltimer, 75, EV_DELAYTIMER, NULL, 2);
++                      return;
++              }
++      } else if (fl3->result == FAXL3_RESULT_RTP) {
++              if (!test_bit(FAXL3_STATE_LASTPAGE, &fl3->state)) {
++                      init_newpage(fl3);
++                      prepare_page_data(fl3);
++                      newstate = ST_L3_WAIT_SENDDCS;
++                      fill_Dxx(fl3, Dxx_TYPE_DCS);
++              }
++      } else {
++              int_errtxt("unhandled result %d abort", fl3->result);
++      }
++      fl3->pending_mod = HW_MOD_FTH;
++      fl3->pending_rate = 3;
++      mISDN_FsmChangeState(fi, newstate);
++      mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++}
++
++static void
++
++l3m_send_dcn(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      send_hdlc_data(fl3, 0xff, 0x13, 0xfb, NULL, 0);
++}
++
++static void
++l3m_finish_dcn(struct FsmInst *fi, int event, void *arg)
++{
++      faxl3_t *fl3 = fi->userdata;
++
++      
++      send_capi_msg_ncpi(fl3, CAPI_DISCONNECT_B3_IND, 0);
++      mISDN_FsmChangeState(fi, ST_L3_CLEARING);
++}
++
++static struct FsmNode FaxL3FnList[] =
++{
++      {ST_L3_IDLE,            EV_CALL_OUT,            l3m_callout},
++      {ST_L3_WAIT_RECVDIS,    EV_MODEM_ACTIV,         l3m_activ_dis},
++      {ST_L3_RECV_DIS,        EV_DATA,                l3m_receive_dis},
++      {ST_L3_RECV_DIS,        EV_MODEM_IDLE,          l3m_finish_dis},
++      {ST_L3_WAIT_SENDDCS,    EV_MODEM_ACTIV,         l3m_send_dcs},
++      {ST_L3_SEND_DCS,        EV_NEXT_DATA,           l3m_send_lastdata},
++      {ST_L3_SEND_DCS,        EV_MODEM_IDLE,          l3m_finish_dcs},
++      {ST_L3_WAIT_SENDTRAIN,  EV_DELAYTIMER,          l3m_setup_train},
++      {ST_L3_WAIT_SENDTRAIN,  EV_MODEM_ACTIV,         l3m_send_train},
++      {ST_L3_SEND_TRAIN,      EV_MODEM_IDLE,          l3m_finish_train},
++      {ST_L3_SEND_TRAIN,      EV_NEXT_DATA,           l3m_send_lastdata},
++      {ST_L3_WAIT_TRAINSTATE, EV_MODEM_ACTIV,         l3m_activ_trainstate},
++      {ST_L3_RECV_TRAINSTATE, EV_DATA,                l3m_receive_trainstate},
++      {ST_L3_RECV_TRAINSTATE, EV_MODEM_IDLE,          l3m_finish_trainstate},
++      {ST_L3_WAIT_SENDPAGE,   EV_DELAYTIMER,          l3m_setup_sendpage},
++      {ST_L3_WAIT_SENDPAGE,   EV_MODEM_ACTIV,         l3m_ready_sendpage},
++      {ST_L3_SEND_PAGE,       EV_NEXT_DATA,           l3m_send_next_pagedata},
++      {ST_L3_SEND_PAGE,       EV_MODEM_IDLE,          l3m_finish_page},
++      {ST_L3_WAIT_SENDEOP,    EV_MODEM_ACTIV,         l3m_send_endofpage},
++      {ST_L3_SEND_EOP,        EV_NEXT_DATA,           l3m_send_lastdata},
++      {ST_L3_SEND_EOP,        EV_MODEM_IDLE,          l3m_finish_eop},
++      {ST_L3_WAIT_RECVMCF,    EV_MODEM_ACTIV,         l3m_activ_mcf},
++      {ST_L3_RECV_MCF,        EV_DATA,                l3m_receive_mcf},
++      {ST_L3_RECV_MCF,        EV_MODEM_IDLE,          l3m_finish_mcf},
++      {ST_L3_WAIT_SENDDCN,    EV_MODEM_ACTIV,         l3m_send_dcn},
++      {ST_L3_SEND_DCN,        EV_NEXT_DATA,           l3m_send_lastdata},
++      {ST_L3_SEND_DCN,        EV_MODEM_IDLE,          l3m_finish_dcn},
++};
++
++#define FAXL3_FN_COUNT (sizeof(FaxL3FnList)/sizeof(struct FsmNode))
++
++static int
++data_b3_conf(faxl3_t *fl3, struct sk_buff *skb)
++{
++      mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++      u8              buf[4];
++
++      hh++;
++      capimsg_setu16(buf, 0, hh->prim); // datahandle 
++      hh--;
++      capimsg_setu16(buf, 2, 0); // Info
++      sendL4frame(fl3, CAPI_DATA_B3_CONF, hh->dinfo, 4, buf, NULL);
++      dev_kfree_skb(skb);
++      return(0);
++}
++
++static int
++copy_page_data4retry(faxl3_t *fl3) {
++      struct sk_buff_head     tmpq;
++      struct sk_buff          *skb, *nskb;
++      int                     err = 0;
++
++      skb_queue_head_init(&tmpq);
++      discard_queue(&fl3->pageq);
++      while((skb = skb_dequeue(&fl3->saveq))) {
++              nskb = skb_clone(skb, GFP_ATOMIC);
++              skb_queue_tail(&fl3->pageq, skb);
++              if (!nskb) {
++                      int_error();
++                      err = -ENOMEM;
++              } else
++                      skb_queue_tail(&tmpq, nskb);
++              
++      }
++      if (err) {
++              discard_queue(&tmpq);
++      } else {
++              while((skb = skb_dequeue(&tmpq)))
++                      skb_queue_tail(&fl3->saveq, skb);
++      }
++      return(err);
++}
++
++#define       PAGE_SKB_LEN    1024
++
++static int
++collect_page_data(faxl3_t *fl3, int len, u8 *buf, int flush)
++{
++      int             l = len;
++      struct sk_buff  *skb;
++
++      if (!fl3->page_skb) {
++              fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1);
++              if (!fl3->page_skb)
++                      return(-ENOMEM);
++      }
++      if ((fl3->page_skb->len + len) >= PAGE_SKB_LEN) {
++              l = PAGE_SKB_LEN - fl3->page_skb->len;
++              memcpy(skb_put(fl3->page_skb, l), buf, l);
++              buf += l;
++              skb = skb_clone(fl3->page_skb, GFP_ATOMIC);
++              if (!skb) {
++                      int_error();
++              } else {
++                      // for resend pages
++                      skb_queue_tail(&fl3->saveq, skb);
++              }
++              skb_queue_tail(&fl3->pageq, fl3->page_skb);
++              fl3->page_skb = alloc_stack_skb(PAGE_SKB_LEN, 1);
++              if (!fl3->page_skb) {
++                      int_error();
++                      return(-ENOMEM);
++              }
++              l = len - l;
++      }
++      if (l) {
++              memcpy(skb_put(fl3->page_skb, l), buf, l);
++      }
++      if (flush) {
++              skb = skb_clone(fl3->page_skb, GFP_ATOMIC);
++              if (!skb) {
++                      int_error();
++              } else {
++                      // for resend pages
++                      skb_queue_tail(&fl3->saveq, skb);
++              }
++              skb_queue_tail(&fl3->pageq, fl3->page_skb);
++              fl3->page_skb = NULL;
++      }
++      return(0);
++}
++
++static int
++fill_empty_lines(faxl3_t *fl3, int cnt) {
++      u8              buf[4], *p;
++      int             l,ret = 0;
++
++      if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
++              printk(KERN_DEBUG "%s %d\n", __FUNCTION__, cnt);
++      p = buf;
++      if (fl3->page_llen == 1728) {
++              *p++ = 0x00;
++              *p++ = 0x40;
++              *p++ = 0xd9;
++              *p++ = 0xa4;
++              l = 4;
++      } else {
++              int_error();
++              return(-EINVAL);
++      }
++      while(cnt) {
++              ret = collect_page_data(fl3, l, buf, 0);
++              fl3->line_cnt++;
++              cnt--;
++      }
++      return(ret);    
++}
++
++static int
++fill_rtc(faxl3_t *fl3) {
++      u8              buf[8] = {0, 0x08, 0x80,};
++      int             cnt = 3, ret = 0;
++
++      if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
++              printk(KERN_DEBUG "%s\n", __FUNCTION__);
++      while(cnt) {
++              ret = collect_page_data(fl3, 3, buf, 0);
++              cnt--;
++      }
++      memset(buf, 0 ,8);
++      ret = collect_page_data(fl3, 8, buf, 1);
++      return(ret);    
++}
++
++static int
++fill_line(faxl3_t *fl3, int cnt1, u8 *data1, int cnt2, u8 *data2) {
++      u8              eol[2] = {0x00, 0x80};
++      int             ret = 0;
++
++      if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
++              printk(KERN_DEBUG "%s: %d/%d\n", __FUNCTION__, cnt1, cnt2);
++      ret = collect_page_data(fl3, 2, eol, 0);
++      if (cnt1)
++              ret = collect_page_data(fl3, cnt1, data1, 0);
++      if (cnt2)
++              ret = collect_page_data(fl3, cnt2, data2, 0);
++      fl3->line_cnt++;
++      return(ret);    
++}
++
++static int
++next_data_skb(faxl3_t *fl3) {
++      struct sk_buff  *skb;
++      int             cnt = 3, ret = 0;
++
++      skb = skb_dequeue(&fl3->dataq);
++      if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
++              printk(KERN_DEBUG "%s: %p/%p %d %d %lx\n", __FUNCTION__, fl3->data_skb, skb,
++                      fl3->lasttyp, fl3->lastlen, fl3->state);
++      if (!skb) {
++              if (fl3->data_skb && (fl3->data_skb->len == 0)) {
++                      data_b3_conf(fl3, fl3->data_skb);
++                      fl3->data_skb = NULL;
++                      test_and_clear_bit(FAXL3_STATE_HAVEDATA, &fl3->state);
++              }
++              return(-EAGAIN);
++      }
++      if (fl3->data_skb) {
++              if (fl3->debug & DEBUG_FAXL3_PAGEPREPARE)
++                      printk(KERN_DEBUG "%s: len(%d) hl(%d)\n", __FUNCTION__, 
++                              fl3->data_skb->len, skb_headroom(skb));
++              if (fl3->data_skb->len) {
++                      if (fl3->data_skb->len <= skb_headroom(skb)) {
++                              memcpy(skb_push(skb, fl3->data_skb->len), fl3->data_skb->data, fl3->data_skb->len);
++                              skb_pull(fl3->data_skb, fl3->data_skb->len);
++                      }
++                      if (test_and_clear_bit(FAXL3_STATE_CONTINUE, &fl3->state)) {
++                              cnt = fl3->lastlen - fl3->data_skb->len;
++                              fill_line(fl3, fl3->data_skb->len, fl3->data_skb->data, cnt, skb->data);
++                              skb_pull(fl3->data_skb, fl3->data_skb->len);
++                              skb_pull(skb, cnt);
++                      }
++                      if (fl3->data_skb->len) {
++                              int_error();
++                              return(-EINVAL);
++                      }
++              }
++              data_b3_conf(fl3, fl3->data_skb);
++      }
++      fl3->data_skb = skb;
++      return(ret);    
++}
++
++static int
++prepare_page_data(faxl3_t *fl3)
++{
++      u32     tmp32;
++      u16     tmp16;
++      u8      ver, len8;
++
++      if (!fl3->data_skb) {
++              fl3->data_skb = skb_dequeue(&fl3->dataq);
++              if (!fl3->data_skb)
++                      return(-EAGAIN);
++      }
++      if (test_bit(FAXL3_STATE_CONTINUE, &fl3->state)) {
++              if (next_data_skb(fl3))
++                      return(-EAGAIN);
++      }
++      if (!test_and_set_bit(FAXL3_STATE_SFFHEADER, &fl3->state)) {
++              if (fl3->data_skb->len < 0x14) {
++                      int_error();
++                      return(-EINVAL);
++              }
++              tmp32 = CAPIMSG_U32(fl3->data_skb->data, 0);
++              ver = CAPIMSG_U8(fl3->data_skb->data, 4);
++              len8 = CAPIMSG_U8(fl3->data_skb->data, 5);
++              tmp16 = CAPIMSG_U16(fl3->data_skb->data, 6);
++              printk(KERN_DEBUG "SFFHEADER(%x,%x,%x,%x)\n", tmp32, ver, len8, tmp16);
++              if (tmp32 != 0x66666653) { // SFFF
++                      int_error();
++                      return(-EINVAL);
++              }
++              if (ver != 1) { // only ver 1 supported
++                      int_error();
++                      return(-EINVAL);
++              }
++              fl3->pages = CAPIMSG_U16(fl3->data_skb->data, 8);
++              tmp16 = CAPIMSG_U16(fl3->data_skb->data, 10);
++              fl3->offset_lpage = CAPIMSG_U32(fl3->data_skb->data, 12);
++              fl3->offset_dend = CAPIMSG_U32(fl3->data_skb->data, 16);
++              printk(KERN_DEBUG "SFFHEADER pages %d ofP(%x) o_lpage(%x) o_dend(%x)\n",
++                      fl3->pages, tmp16, fl3->offset_lpage, fl3->offset_dend);
++              if (tmp16 != 0x14) {
++                      int_error();
++                      return(-EINVAL);
++              }
++              skb_pull(fl3->data_skb, 0x14);
++      }
++      if (fl3->data_skb->len < 2) {
++              if (next_data_skb(fl3))
++                      return(-EAGAIN);
++      }
++      while (fl3->data_skb->len > 1) {
++              fl3->lasttyp = CAPIMSG_U8(fl3->data_skb->data, 0);
++              fl3->lastlen = 0;
++              if (fl3->lasttyp == 255) {
++                      int_error();
++                      return(-EINVAL);
++              } else if (fl3->lasttyp == 254) {
++                      // page header
++                      len8 = CAPIMSG_U8(fl3->data_skb->data, 1);
++                      printk(KERN_DEBUG "current page end: %d lines\n", fl3->line_cnt); 
++                      if (len8 == 0) {
++                              // doc end
++                              printk(KERN_DEBUG "SFF doc end found\n");
++                              test_and_set_bit(FAXL3_STATE_LASTPAGE, &fl3->state);
++                              test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
++                              skb_pull(fl3->data_skb, 2);
++                              fill_rtc(fl3);
++                              // TODO clean up skb
++                              return(0);
++                      }
++                      if (test_and_set_bit(FAXL3_STATE_NEWPAGE, &fl3->state)) {
++                              // next page
++                              fill_rtc(fl3);
++                              test_and_set_bit(FAXL3_STATE_DATALAST, &fl3->state);
++                              return(0);
++                      }
++                      if (fl3->data_skb->len < (2 + len8)) {
++                              if (next_data_skb(fl3))
++                                      return(-EAGAIN);
++                      }
++                      printk(KERN_DEBUG "SFF page header len %d\n", len8);
++                      if (len8 < 10) {
++                              int_error();
++                              return(-EINVAL);
++                      }
++                      fl3->page_vres = CAPIMSG_U8(fl3->data_skb->data, 2);
++                      fl3->page_hres = CAPIMSG_U8(fl3->data_skb->data, 3);
++                      fl3->page_code = CAPIMSG_U8(fl3->data_skb->data, 4);
++                      fl3->page_rsv1 = CAPIMSG_U8(fl3->data_skb->data, 5);
++                      fl3->page_llen = CAPIMSG_U16(fl3->data_skb->data, 6);
++                      fl3->page_plen = CAPIMSG_U16(fl3->data_skb->data, 8);
++                      fl3->page_oprv = CAPIMSG_U32(fl3->data_skb->data, 10);
++                      fl3->page_onxt = CAPIMSG_U32(fl3->data_skb->data, 14);
++                      skb_pull(fl3->data_skb, len8 +2);
++                      printk(KERN_DEBUG "SFF page header: vres(%x) hres(%x) code(%x) resrv(%x)\n",
++                              fl3->page_vres, fl3->page_hres, fl3->page_code, fl3->page_rsv1);
++                      printk(KERN_DEBUG "SFF page header: llen(%d) plen(%d) op(%x) on(%x)\n",
++                              fl3->page_llen, fl3->page_plen, fl3->page_oprv, fl3->page_onxt);
++                      continue;
++              } else if (fl3->lasttyp == 0) {
++                      if (fl3->data_skb->len < 3) {
++                              if (next_data_skb(fl3))
++                                      return(-EAGAIN);
++                      }
++                      fl3->lastlen = CAPIMSG_U16(fl3->data_skb->data, 1);
++                      skb_pull(fl3->data_skb, 3);
++              } else if (fl3->lasttyp < 216) {
++                      fl3->lastlen = fl3->lasttyp;
++                      skb_pull(fl3->data_skb, 1);
++              } else if (fl3->lasttyp < 253) {
++                      // white lines
++                      skb_pull(fl3->data_skb, 1);
++                      fill_empty_lines(fl3, fl3->lasttyp - 216);
++                      continue;
++              }
++              if (fl3->data_skb->len < fl3->lastlen) {
++                      test_and_set_bit(FAXL3_STATE_CONTINUE, &fl3->state);
++                      break;
++              }
++              fill_line(fl3, fl3->lastlen, fl3->data_skb->data, 0, NULL);
++              skb_pull(fl3->data_skb, fl3->lastlen);
++      }
++      return(0);
++}
++
++static u16
++data_b3_req(faxl3_t *fl3, struct sk_buff *skb)
++{
++      __u16           size;
++      mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++
++      if (skb->len < 10) {
++              int_errtxt("skb too short");
++              return(0x2007);
++      }
++
++      size =  CAPIMSG_U16(skb->data, 4);
++
++      /* we save DataHandle and Flags in a area after normal mISDN_HEAD */ 
++      hh++;
++      hh->prim = CAPIMSG_U16(skb->data, 6);
++      hh->dinfo = CAPIMSG_U16(skb->data, 8);
++      /* the data begins behind the header, we don't use Data32/Data64 here */
++      if ((skb->len - size) == 18)
++              skb_pull(skb, 18);
++      else if ((skb->len - size) == 10) // old format
++              skb_pull(skb, 10);
++      else {
++              int_errtxt("skb data_b3 header len mismatch len %d", skb->len - size);
++              return(0x2007);
++      }
++      if (test_and_set_bit(FAXL3_STATE_HAVEDATA, &fl3->state)) {
++              skb_queue_tail(&fl3->dataq, skb);
++      } else {
++              fl3->data_skb = skb;
++              prepare_page_data(fl3);
++      }
++      return(0);
++}
++
++static int
++data_b3_resp(faxl3_t *faxl3, u_int di, struct sk_buff *skb)
++{
++      dev_kfree_skb(skb);
++      return(0);
++}
++
++static int
++connect_b3_req(faxl3_t *fl3, u_int di, u32 addr, struct sk_buff *skb)
++{
++      u16     info = 0;
++
++      print_hexdata(fl3, "NCPI: ", skb->len, skb->data);
++      fl3->pending_mod = HW_MOD_FRH;
++      fl3->pending_rate = 3;
++      mISDN_FsmEvent(&fl3->mod, EV_MOD_NEW, NULL);
++      mISDN_FsmEvent(&fl3->main, EV_CALL_OUT, NULL);
++      fl3->ncci = 0x10000 | addr;
++      skb_push(skb, 4);
++      sendL4frame(fl3, CAPI_CONNECT_B3_CONF, di, 2, &info, skb);
++      return(0);
++}
++
++static int
++sendL4frame(faxl3_t *fl3, int prim, int di, int len, void *arg, struct sk_buff *skb)
++{
++      u_char          *p;
++      int             ret;
++
++      if (!skb) {
++              skb = alloc_stack_skb(len +  20, fl3->up_headerlen);
++              if (!skb)
++                      return(-ENOMEM);
++      } else {
++              skb_trim(skb, 0);
++      }
++      capimsg_setu32(skb_put(skb, 4), 0, fl3->ncci);
++      switch(prim) {
++              case CAPI_CONNECT_B3_CONF:
++                      capimsg_setu16(skb_put(skb, 2), 0, *((u16 *)arg));
++                      break;
++              case CAPI_DISCONNECT_B3_IND:
++//                    capimsg_setu16(skb_put(skb, 2), 0, flags & 0xffff);
++              case CAPI_CONNECT_B3_IND:
++              case CAPI_RESET_B3_IND:
++              case CAPI_CONNECT_B3_ACTIVE_IND:
++              case CAPI_DATA_B3_CONF:
++                      if (len) {
++                              p = skb_put(skb, len);
++                              memcpy(p, arg, len);
++                      } else {
++                              p = skb_put(skb, 1);
++                              *p = 0;
++                      }
++                      break;
++              default:
++                      int_error();
++                      dev_kfree_skb(skb);
++                      return(-EINVAL);
++      }
++      ret = if_newhead(&fl3->inst.up, prim, di, skb);
++      if (ret) {
++              printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret);
++              dev_kfree_skb(skb);
++      }
++      return(ret);
++}
++
++static int
++send_capi_msg_ncpi(faxl3_t *fl3, int prim, u16 Info)
++{
++      u8      ncpi[36], *p, off=0, len = 0;
++      u16     lastmod = 0;
++
++      memset(ncpi, 0, 36);
++      switch(prim) {
++              case CAPI_CONNECT_B3_ACTIVE_IND:
++                      if (is_valid_rate_idx(fl3, fl3->current_rate_idx))
++                              lastmod = FaxModulationBaud[fl3->current_rate_idx];
++                      p = fl3->CIS;
++                      break;
++              case CAPI_DISCONNECT_B3_IND:
++                      if (is_valid_rate_idx(fl3, fl3->current_rate_idx))
++                              lastmod = FaxModulationBaud[fl3->current_rate_idx];
++                      p = fl3->CIS;
++                      capimsg_setu16(ncpi, 0, Info);
++                      off = 2;
++                      break;
++              default:
++                      int_error();
++                      return(-EINVAL);
++      }
++      capimsg_setu16(ncpi, off+1, lastmod);
++      capimsg_setu16(ncpi, off+3, 0x8000); // FIXME no ECM
++      capimsg_setu16(ncpi, off+5, 0);
++      capimsg_setu16(ncpi, off+7, 0);
++      len = strlen(p);
++      if (len > 20)
++              len = 20;
++      capimsg_setu8(ncpi, off+9, len);
++      if (len)
++              memcpy(&ncpi[off+10], p, len);
++      len += 9; // 8*u16 + lenfield
++      capimsg_setu8(ncpi, off, len);
++      return(sendL4frame(fl3, prim, 0, len + off, ncpi, NULL));
++}
++
++static int
++faxl3_from_up(mISDNif_t *hif, struct sk_buff *skb)
++{
++      faxl3_t         *faxl3;
++      mISDN_head_t    *hh;
++      __u32           addr;
++      __u16           info = 0;
++      int             err = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      faxl3 = hif->fdata;
++      if (!faxl3->inst.down.func) {
++              return(-ENXIO);
++      }
++      hh = mISDN_HEAD_P(skb);
++      if (faxl3->debug & DEBUG_FAXL3_FUNC)
++              printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len);
++      if (skb->len < 4) {
++              printk(KERN_WARNING "%s: skb too short (%d)\n", __FUNCTION__, skb->len);
++              return(-EINVAL);
++      } else {
++              addr = CAPIMSG_U32(skb->data, 0);
++              skb_pull(skb, 4);
++      }
++      if (faxl3->debug & DEBUG_FAXL3_FUNC)
++              printk(KERN_DEBUG "%s: addr(%x)\n", __FUNCTION__, addr);
++      switch(hh->prim) {
++              case CAPI_DATA_B3_REQ:
++                      info = data_b3_req(faxl3, skb);
++                      if (info) {
++                      } else
++                              err = 0;
++                      break;
++              case CAPI_DATA_B3_RESP:
++                      return(data_b3_resp(faxl3, hh->dinfo, skb));
++              case CAPI_CONNECT_B3_REQ:
++                      return(connect_b3_req(faxl3, hh->dinfo, addr, skb));
++              case CAPI_RESET_B3_REQ:
++                      break;
++              case CAPI_DISCONNECT_B3_REQ:
++                      break;
++              case CAPI_CONNECT_B3_RESP:
++                      if (skb->len <= 2) {
++                              printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP skb too short (%d)\n",
++                                      __FUNCTION__, skb->len);
++                              skb_push(skb, 4);
++                              return(-EINVAL);
++                      }
++                      info = CAPIMSG_U16(skb->data, 0);
++                      skb_pull(skb, 2);
++                      if (info == 0)
++                              ;
++                      if (skb->len <= 4) { // default NCPI
++                      } else {
++                      }
++                      dev_kfree_skb(skb);
++                      err = 0;
++                      break;
++              case CAPI_CONNECT_B3_ACTIVE_RESP:
++                      // nothing to do
++                      dev_kfree_skb(skb);
++                      err = 0;
++                      break;
++              case CAPI_RESET_B3_RESP:
++                      dev_kfree_skb(skb);
++                      err = 0;
++                      break;
++              case CAPI_DISCONNECT_B3_RESP:
++                      dev_kfree_skb(skb);
++                      err = 0;
++                      break;
++              default:
++                      printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n",
++                              __FUNCTION__, hh->prim, hh->dinfo);
++                      err = -EINVAL;
++      }
++      return(err);
++}
++
++static void
++ph_status_ind(faxl3_t *fl3, int status)
++{
++      switch(status) {
++              case HW_MOD_READY:
++                      mISDN_FsmEvent(&fl3->mod, EV_MOD_READY, NULL);
++                      break;
++              case HW_MOD_CONNECT:
++                      mISDN_FsmEvent(&fl3->mod, EV_MOD_CONNECT, NULL);
++                      break;
++              case HW_MOD_OK:
++              case HW_MOD_NOCARR:
++                      mISDN_FsmEvent(&fl3->mod, EV_MOD_NOCARRIER, NULL);
++                      break;
++              default:
++                      int_errtxt("unhandled status(%x)", status);
++                      break;
++      }
++}
++
++static void
++ph_data_cnf(faxl3_t *fl3, int status)
++{
++      struct sk_buff  *skb ;
++      mISDNif_t       *down = &fl3->inst.down;
++      int             ret;
++
++      if (!test_bit(FAXL3_STATE_DATABUSY, &fl3->state)) {
++              int_errtxt("PH_DATA | CONFIRM without DATABUSY");
++              return;
++      }
++      skb = skb_dequeue(&fl3->downq);
++      if ((skb == NULL) && test_bit(FAXL3_STATE_DATAREADY, &fl3->state)) {
++              skb = skb_dequeue(&fl3->pageq);
++      }
++      if (skb)  {
++              mISDN_sethead(PH_DATA_REQ, data_next_id(fl3), skb);
++              ret = down->func(down, skb);
++              if (ret) {
++                      dev_kfree_skb(skb);
++                      int_errtxt("down: error(%d)", ret);
++                      return;
++              }
++      } else {
++              test_and_clear_bit(FAXL3_STATE_DATABUSY, &fl3->state);
++              mISDN_FsmEvent(&fl3->main, EV_NEXT_DATA, NULL);
++      }
++}
++
++static int
++faxl3_from_down(mISDNif_t *hif,  struct sk_buff *skb)
++{
++      faxl3_t         *faxl3;
++      mISDN_head_t    *hh;
++      int             err = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      faxl3 = hif->fdata;
++      if (!faxl3->inst.up.func) {
++              return(-ENXIO);
++      }
++      hh = mISDN_HEAD_P(skb);
++      if (faxl3->debug & DEBUG_FAXL3_FUNC)
++              printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len);
++      switch(hh->prim) {
++              case PH_ACTIVATE | INDICATION:
++              case PH_ACTIVATE | CONFIRM:
++                      break;
++              case PH_STATUS | INDICATION:
++                      ph_status_ind(faxl3, hh->dinfo);
++                      break;
++              case PH_DATA | INDICATION:
++                      mISDN_FsmEvent(&faxl3->main, EV_DATA, skb);
++                      break;
++              case PH_DATA | CONFIRM:
++                      ph_data_cnf(faxl3, hh->dinfo);
++                      break;
++              default:
++                      printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n",
++                              __FUNCTION__, hh->prim, hh->dinfo);
++                      err = -EINVAL;
++                      break;
++      }
++      if (!err)
++              dev_kfree_skb(skb);
++      return(err);
++}
++
++static char MName[] = "FAXL3";
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++MODULE_PARM(debug, "1i");
++MODULE_PARM(ttt, "1i");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++#endif
++
++static void
++l3m_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      faxl3_t *fl3 = fi->userdata;
++      logdata_t log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = fl3->inst.name;
++      fl3->inst.obj->ctrl(&fl3->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++static void
++release_faxl3(faxl3_t *faxl3) {
++      mISDNinstance_t *inst = &faxl3->inst;
++
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      list_del(&faxl3->list);
++      discard_queue(&faxl3->downq);
++      discard_queue(&faxl3->dataq);
++      discard_queue(&faxl3->pageq);
++      discard_queue(&faxl3->saveq);
++      mISDN_FsmDelTimer(&faxl3->deltimer, 99);
++      mISDN_FsmDelTimer(&faxl3->timer1, 99);
++      mISDN_FsmDelTimer(&faxl3->modtimer, 99);
++      if (faxl3->entity != MISDN_ENTITY_NONE)
++              faxl3_obj.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)faxl3->entity);
++      faxl3_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      kfree(faxl3);
++}
++
++static int
++new_faxl3(mISDNstack_t *st, mISDN_pid_t *pid) {
++      faxl3_t *n_faxl3;
++      u8      *p;
++      int     err;
++
++      if (!st || !pid)
++              return(-EINVAL);
++      if (!(n_faxl3 = kmalloc(sizeof(faxl3_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc faxl3_t failed\n");
++              return(-ENOMEM);
++      }
++      memset(n_faxl3, 0, sizeof(faxl3_t));
++      n_faxl3->entity = MISDN_ENTITY_NONE;
++      n_faxl3->next_id = 1;
++      spin_lock_init(&n_faxl3->lock);
++      memcpy(&n_faxl3->inst.pid, pid, sizeof(mISDN_pid_t));
++      if (n_faxl3->inst.pid.global == 1)
++              test_and_set_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state);
++      p = n_faxl3->inst.pid.param[3];
++      if (p) {
++              if (*p < 6) {
++                      int_errtxt("B3cfg too shoort(%d)", *p);
++              } else {
++                      n_faxl3->options = CAPIMSG_U16(p, 1);
++                      n_faxl3->format = CAPIMSG_U16(p, 3);
++                      p += 5;
++                      if (*p && (*p <= 20))
++                              memcpy(n_faxl3->stationID, p, *p + 1);
++                      p += (*p +1);
++                      if (*p && (*p <= 62))
++                              memcpy(n_faxl3->headline, p, *p + 1);
++              }
++      }
++      if (debug & DEBUG_FAXL3_CFG) {
++              printk(KERN_DEBUG "%s opt(%x) fmt(%x) id=%s head=%s\n",
++                      test_bit(FAXL3_STATE_OUTGOING, &n_faxl3->state) ? "out" : "in",
++                      n_faxl3->options,
++                      n_faxl3->format,
++                      &n_faxl3->stationID[1],
++                      &n_faxl3->headline[1]);
++              if (n_faxl3->inst.pid.param[1]) {
++                      p = n_faxl3->inst.pid.param[1];
++                      printk(KERN_DEBUG "B1 param len %d rate %d\n", *p,
++                              (*p > 1) ? *((u16 *)(p+1)) : -1);
++              }
++      }
++      mISDN_init_instance(&n_faxl3->inst, &faxl3_obj, n_faxl3);
++      if (!mISDN_SetHandledPID(&faxl3_obj, &n_faxl3->inst.pid)) {
++              int_error();
++              kfree(n_faxl3);
++              return(-ENOPROTOOPT);
++      }
++      n_faxl3->own_rate_mask = FAXMODM_V27_V29_V33_V17; 
++      n_faxl3->current_rate_idx = -1;
++      n_faxl3->current_mod = -1;
++      n_faxl3->current_rate = -1;
++      n_faxl3->pending_mod = -1;
++      n_faxl3->pending_rate = -1;
++      n_faxl3->debug = debug;
++      n_faxl3->main.fsm = &faxl3fsm;
++      n_faxl3->main.state = ST_L3_IDLE;
++      n_faxl3->main.debug = debug;
++      n_faxl3->main.userdata = n_faxl3;
++      n_faxl3->main.userint = 0;
++      n_faxl3->main.printdebug = l3m_debug;
++      mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->deltimer);
++      mISDN_FsmInitTimer(&n_faxl3->main, &n_faxl3->timer1);
++
++      n_faxl3->mod.fsm = &modfsm;
++      n_faxl3->mod.state = ST_MOD_NULL;
++      n_faxl3->mod.debug = debug;
++      n_faxl3->mod.userdata = n_faxl3;
++      n_faxl3->mod.userint = 0;
++      n_faxl3->mod.printdebug = l3m_debug;
++      mISDN_FsmInitTimer(&n_faxl3->mod, &n_faxl3->modtimer);
++      skb_queue_head_init(&n_faxl3->downq);
++      skb_queue_head_init(&n_faxl3->dataq);
++      skb_queue_head_init(&n_faxl3->pageq);
++      skb_queue_head_init(&n_faxl3->saveq);
++
++      list_add_tail(&n_faxl3->list, &faxl3_obj.ilist);
++      n_faxl3->entity = MISDN_ENTITY_NONE;
++      err = faxl3_obj.ctrl(&n_faxl3->inst, MGR_NEWENTITY | REQUEST, NULL);
++      if (err) {
++              printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n",
++                      __FUNCTION__, err);
++      }
++      err = faxl3_obj.ctrl(st, MGR_REGLAYER | INDICATION, &n_faxl3->inst);
++      if (err) {
++              list_del(&n_faxl3->list);
++              kfree(n_faxl3);
++      } else {
++              if (st->para.maxdatalen)
++                      n_faxl3->maxdatalen = st->para.maxdatalen;
++              if (st->para.up_headerlen)
++                      n_faxl3->up_headerlen = st->para.up_headerlen;
++              if (st->para.down_headerlen)
++                      n_faxl3->down_headerlen = st->para.down_headerlen;
++              if (debug)
++                      printk(KERN_DEBUG "%s:mlen(%d) hup(%d) hdown(%d)\n", __FUNCTION__,
++                              n_faxl3->maxdatalen, n_faxl3->up_headerlen, n_faxl3->down_headerlen);
++      }
++      return(err);
++}
++
++static int
++faxl3_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      faxl3_t         *faxl3_l;
++      int             err = -EINVAL;
++
++      if (debug & DEBUG_FAXL3_MGR)
++              printk(KERN_DEBUG "faxl3_manager data:%p prim:%x arg:%p\n", data, prim, arg);
++      if (!data)
++              return(err);
++      list_for_each_entry(faxl3_l, &faxl3_obj.ilist, list) {
++              if (&faxl3_l->inst == inst) {
++                      err = 0;
++                      break;
++              }
++      }
++      if (prim == (MGR_NEWLAYER | REQUEST))
++              return(new_faxl3(data, arg));
++      if (err) {
++              printk(KERN_WARNING "faxl3_manager prim(%x) no instance\n", prim);
++              return(err);
++      }
++      switch(prim) {
++          case MGR_CLRSTPARA | INDICATION:
++          case MGR_CLONELAYER | REQUEST:
++              break;
++          case MGR_ADDSTPARA | INDICATION:
++              {
++                      mISDN_stPara_t *stp = arg;
++
++                      if (stp->down_headerlen)
++                              faxl3_l->down_headerlen = stp->down_headerlen;
++                      if (stp->up_headerlen)
++                              faxl3_l->up_headerlen = stp->up_headerlen;
++                      printk(KERN_DEBUG "MGR_ADDSTPARA: (%d/%d/%d)\n",
++                              stp->maxdatalen, stp->down_headerlen, stp->up_headerlen);
++              }
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_NEWENTITY | CONFIRM:
++              faxl3_l->entity = (int)arg;
++              break;
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              return(mISDN_SetIF(inst, arg, prim, faxl3_from_up, faxl3_from_down, faxl3_l));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_UNREGLAYER | REQUEST:
++          case MGR_RELEASE | INDICATION:
++              if (debug & DEBUG_FAXL3_MGR)
++                      printk(KERN_DEBUG "release_faxl3 id %x\n", faxl3_l->inst.st->id);
++              release_faxl3(faxl3_l);
++              break;
++          default:
++              if (debug & DEBUG_FAXL3_MGR)
++                      printk(KERN_WARNING "faxl3_manager prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static int faxl3_init(void)
++{
++      int err;
++
++      printk(KERN_INFO "%s modul version %s\n", MName, mISDN_getrev(mISDN_faxl3_revision));
++#ifdef MODULE
++      faxl3_obj.owner = THIS_MODULE;
++#endif
++      faxl3_obj.name = MName;
++      faxl3_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_T30;
++      faxl3_obj.own_ctrl = faxl3_manager;
++      INIT_LIST_HEAD(&faxl3_obj.ilist);
++      if ((err = mISDN_register(&faxl3_obj))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++              return(err);
++      }
++      faxl3fsm.state_count = FAXL3_STATE_COUNT;
++      faxl3fsm.event_count = FAXL3_EVENT_COUNT;
++      faxl3fsm.strEvent = strfaxl3Event;
++      faxl3fsm.strState = strfaxl3State;
++      mISDN_FsmNew(&faxl3fsm, FaxL3FnList, FAXL3_FN_COUNT);
++      modfsm.state_count = MOD_STATE_COUNT;
++      modfsm.event_count = MOD_EVENT_COUNT;
++      modfsm.strEvent = strmodEvent;
++      modfsm.strState = strmodState;
++      mISDN_FsmNew(&modfsm, ModFnList, MOD_FN_COUNT);
++      return(err);
++}
++
++static void faxl3_cleanup(void)
++{
++      faxl3_t *l3, *nl3;
++      int     err;
++
++      if ((err = mISDN_unregister(&faxl3_obj))) {
++              printk(KERN_ERR "Can't unregister DTMF error(%d)\n", err);
++      }
++      if(!list_empty(&faxl3_obj.ilist)) {
++              printk(KERN_WARNING "faxl3 inst list not empty\n");
++              list_for_each_entry_safe(l3, nl3, &faxl3_obj.ilist, list)
++                      release_faxl3(l3);
++      }
++      mISDN_FsmFree(&faxl3fsm);
++      mISDN_FsmFree(&modfsm);
++}
++
++module_init(faxl3_init);
++module_exit(faxl3_cleanup);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.c        1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.c    2004-11-22 09:33:38.107750656 +0000
+@@ -0,0 +1,169 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * Thanks to    Jan den Ouden
++ *              Fritz Elfert
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/isdn_compat.h>
++#ifdef NEED_JIFFIES_INCLUDE
++#include <linux/jiffies.h>
++#endif
++#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 = (FSMFNPTR *)
++              kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
++      memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count);
++
++      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 line %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;
++}
++
++void
++mISDN_FsmFree(struct Fsm *fsm)
++{
++      kfree((void *) fsm->jumpmatrix);
++}
++
++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 (!0);
++      }
++}
++
++void
++mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
++{
++      fi->state = newstate;
++      if (fi->debug)
++              fi->printdebug(fi, "ChangeState %s",
++                      fi->fsm->strState[newstate]);
++}
++
++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);
++}
++
++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);
++}
++
++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)) {
++              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;
++}
++
++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_FsmNew);
++EXPORT_SYMBOL(mISDN_FsmFree);
++EXPORT_SYMBOL(mISDN_FsmEvent);
++EXPORT_SYMBOL(mISDN_FsmChangeState);
++EXPORT_SYMBOL(mISDN_FsmInitTimer);
++EXPORT_SYMBOL(mISDN_FsmAddTimer);
++EXPORT_SYMBOL(mISDN_FsmRestartTimer);
++EXPORT_SYMBOL(mISDN_FsmDelTimer);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/fsm.h        1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/fsm.h    2004-11-22 09:33:38.117749136 +0000
+@@ -0,0 +1,54 @@
++/* $Id$
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#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 -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.c 2004-11-22 09:33:38.127747616 +0000
+@@ -0,0 +1,379 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mISDNif.h>
++#include "helper.h"
++
++#undef        DEBUG_LM
++#undef        DEBUG_IF
++
++void
++mISDN_set_dchannel_pid(mISDN_pid_t *pid, int protocol, int layermask)
++{
++      if (!layermask)
++              layermask = ISDN_LAYER(0)| ISDN_LAYER(1) | ISDN_LAYER(2) |
++                      ISDN_LAYER(3) | ISDN_LAYER(4);
++      
++      memset(pid, 0, sizeof(mISDN_pid_t));
++      pid->layermask = layermask;
++      if (layermask & ISDN_LAYER(0))
++              pid->protocol[0] = ISDN_PID_L0_TE_S0;
++      if (layermask & ISDN_LAYER(1))
++              pid->protocol[1] = ISDN_PID_L1_TE_S0;
++      if (layermask & ISDN_LAYER(2)) {
++              pid->protocol[2] = ISDN_PID_L2_LAPD;
++              if (protocol & 0x20)
++                      pid->protocol[2] |= ISDN_PID_L2_DF_PTP;
++      }
++      if (layermask & ISDN_LAYER(3)) {
++              if ((protocol & 0xf) == 2)
++                      pid->protocol[3] = ISDN_PID_L3_DSS1USER;
++              if (protocol & 0x20)
++                      pid->protocol[3] |= ISDN_PID_L3_DF_PTP;
++      }
++      if (layermask & ISDN_LAYER(4))
++              pid->protocol[4] = ISDN_PID_L4_CAPI20;
++}
++
++int
++mISDN_bprotocol2pid(void *bp, mISDN_pid_t *pid)
++{
++      __u8    *p = bp;
++      __u16   *w = bp;
++      int     i;
++      
++
++      p += 6;
++      for (i=1; i<=3; i++) {
++              if (*w > 23) {
++                      int_errtxt("L%d pid %x\n",i,*w);
++                      return(-EINVAL);
++              }
++              pid->protocol[i] = (1 <<*w) | ISDN_PID_LAYER(i) |
++                      ISDN_PID_BCHANNEL_BIT;
++              if (*p)
++                      pid->param[i] = p;
++              else
++                      pid->param[i] = NULL;
++              w++;
++              p += *p;
++              p++;
++      }
++      pid->global = 0;
++      if (*p == 2) { // len of 1 word
++              p++;
++              w = (__u16 *)p;
++              pid->global = *w;
++      }
++      return(0);
++}
++
++int
++mISDN_HasProtocol(mISDNobject_t *obj, u_int protocol)
++{
++      int layer;
++      u_int pmask;
++
++      if (!obj) {
++              int_error();
++              return(-EINVAL);
++      }
++      layer = (protocol & ISDN_PID_LAYER_MASK)>>24;
++      if (layer > MAX_LAYER_NR) {
++              int_errtxt("layer %d", layer);
++              return(-EINVAL);
++      }
++      if (protocol & ISDN_PID_BCHANNEL_BIT)
++              pmask = obj->BPROTO.protocol[layer];
++      else
++              pmask = obj->DPROTO.protocol[layer];
++      if (pmask == ISDN_PID_ANY)
++              return(-EPROTO);
++      if (protocol == (protocol & pmask))
++              return(0);
++      else
++              return(-ENOPROTOOPT); 
++}
++
++int
++mISDN_SetHandledPID(mISDNobject_t *obj, mISDN_pid_t *pid)
++{
++      int layer;
++      int ret = 0;
++      mISDN_pid_t sav;
++
++      if (!obj || !pid) {
++              int_error();
++              return(0);
++      }
++#ifdef DEBUG_LM
++      printk(KERN_DEBUG "%s: %s LM(%x)\n", __FUNCTION__, obj->name, pid->layermask);
++#endif
++      memcpy(&sav, pid, sizeof(mISDN_pid_t));
++      memset(pid, 0, sizeof(mISDN_pid_t));
++      pid->global = sav.global;
++      if (!sav.layermask) {
++              printk(KERN_WARNING "%s: no layermask in pid\n", __FUNCTION__);
++              return(0);
++      }
++      for (layer=0; layer<=MAX_LAYER_NR; layer++) {
++              if (!(ISDN_LAYER(layer) & sav.layermask)) {
++                      if (ret)
++                              break;
++                      else
++                              continue;
++              }
++              if (0 == mISDN_HasProtocol(obj, sav.protocol[layer])) {
++                      ret++;
++                      pid->protocol[layer] = sav.protocol[layer];
++                      pid->param[layer] = sav.param[layer];
++                      pid->layermask |= ISDN_LAYER(layer);
++              } else
++                      break;
++      }
++      return(ret);
++}
++
++void
++mISDN_RemoveUsedPID(mISDN_pid_t *pid, mISDN_pid_t *used)
++{
++      int layer;
++
++      if (!used || !pid) {
++              int_error();
++              return;
++      }
++      if (!used->layermask)
++              return;
++      
++      for (layer=0; layer<=MAX_LAYER_NR; layer++) {
++              if (!(ISDN_LAYER(layer) & used->layermask))
++                              continue;
++              pid->protocol[layer] = ISDN_PID_NONE;
++              pid->param[layer] = NULL;
++              pid->layermask &= ~(ISDN_LAYER(layer));
++      }
++}
++
++int
++mISDN_layermask2layer(int layermask) {
++      switch(layermask) {
++              case ISDN_LAYER(0): return(0);
++              case ISDN_LAYER(1): return(1);
++              case ISDN_LAYER(2): return(2);
++              case ISDN_LAYER(3): return(3);
++              case ISDN_LAYER(4): return(4);
++              case ISDN_LAYER(5): return(5);
++              case ISDN_LAYER(6): return(6);
++              case ISDN_LAYER(7): return(7);
++              case 0: return(-1);
++      }
++      return(-2);
++}
++
++int
++mISDN_get_protocol(mISDNstack_t *st, int layer)
++{
++
++      if (!st){
++              int_error();
++              return(-EINVAL);
++      }
++      if (LAYER_OUTRANGE(layer)) {
++              int_errtxt("L%d st(%x)", layer, st->id);
++              return(-EINVAL);
++      }
++      return(st->pid.protocol[layer]);
++}
++
++int
++mISDN_get_lowlayer(int layermask)
++{
++      int layer;
++
++      if (!layermask)
++              return(0);
++      for (layer=0; layer <= MAX_LAYER_NR; layer++)
++              if (layermask & ISDN_LAYER(layer))
++                      return(layer);
++      return(0);
++}
++
++int
++mISDN_get_down_layer(int layermask)
++{
++      int downlayer = 1;
++      
++      if (layermask>255 || (layermask & 1)) {
++              int_errtxt("lmask %x out of range", layermask);
++              return(0);
++      }
++      while(downlayer <= MAX_LAYER_NR) {
++              if (ISDN_LAYER(downlayer) & layermask)
++                      break;
++              downlayer++;
++      }
++      downlayer--;
++      return(downlayer);
++}
++
++int
++mISDN_get_up_layer(int layermask) {
++      int uplayer = MAX_LAYER_NR;
++      
++      if (layermask>=128) {
++              int_errtxt("lmask %x out of range", layermask);
++              return(0);
++      }
++      while(uplayer>=0) {
++              if (ISDN_LAYER(uplayer) & layermask)
++                      break;
++              uplayer--;
++      }
++      uplayer++;
++      return(uplayer);
++}
++
++int
++mISDN_SetIF(mISDNinstance_t *owner, mISDNif_t *hif, u_int prim, void *upfunc,
++      void *downfunc, void *data)
++{
++      mISDNif_t *own_hif;
++
++      if (!owner) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (!hif) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (IF_TYPE(hif) == IF_UP) {
++              hif->func = upfunc;
++              own_hif = &owner->up;
++#ifdef DEBUG_IF
++              printk(KERN_DEBUG "%s: IF_UP: func:%p(%p)\n", __FUNCTION__, hif->func, owner->data);
++#endif
++      } else if (IF_TYPE(hif) == IF_DOWN) {
++              hif->func = downfunc;
++              own_hif = &owner->down;
++#ifdef DEBUG_IF
++              printk(KERN_DEBUG "%s: IF_DOWN: func:%p(%p)\n", __FUNCTION__, hif->func, owner->data);
++#endif
++      } else {
++              int_errtxt("stat(%x)", hif->stat);
++              return(-EINVAL);
++      }
++      hif->peer = owner;
++      hif->fdata = data;
++      if ((prim & SUBCOMMAND_MASK) == REQUEST) {
++              if (own_hif->stat == IF_NOACTIV) {
++                      if (IF_TYPE(hif) == IF_UP)
++                              own_hif->stat = IF_DOWN;
++                      else
++                              own_hif->stat = IF_UP;
++                      own_hif->owner = owner;
++                      return(hif->owner->obj->own_ctrl(hif->owner,
++                              MGR_SETIF | INDICATION, own_hif));
++              } else {
++                      int_errtxt("REQ own stat(%x)", own_hif->stat);
++                      return(-EBUSY);
++              }
++      }
++      return(0);
++}
++
++int
++mISDN_ConnectIF(mISDNinstance_t *owner, mISDNinstance_t *peer)
++{
++      mISDNif_t *hif;
++
++      if (!owner) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (!peer) {
++              int_error();
++              return(-EINVAL);
++      }
++      
++      if (owner->pid.layermask < peer->pid.layermask) {
++              hif = &owner->up;
++              hif->owner = owner;
++              hif->stat = IF_DOWN;
++      } else if (owner->pid.layermask > peer->pid.layermask) {
++              hif = &owner->down;
++              hif->owner = owner;
++              hif->stat = IF_UP;
++      } else {
++              int_errtxt("OLM == PLM: %x", owner->pid.layermask);
++              return(-EINVAL);
++      }
++      return(peer->obj->own_ctrl(peer, MGR_SETIF | REQUEST, hif));
++}
++
++int
++mISDN_DisConnectIF(mISDNinstance_t *inst, mISDNif_t *hif) {
++      if (hif) {
++              if (hif->clone) {
++                      if (hif->clone->owner)
++                              hif->clone->owner->obj->ctrl(hif->clone->owner,
++                                      MGR_DISCONNECT | REQUEST, hif->clone);
++              }       
++              if (inst->up.peer) {
++                      if (inst->up.peer == hif->owner)
++                              inst->up.peer->obj->ctrl(inst->up.peer,
++                                      MGR_DISCONNECT | INDICATION, &inst->up);
++              }
++              if (inst->down.peer) {
++                      if (inst->down.peer == hif->owner)
++                              inst->down.peer->obj->ctrl(inst->down.peer,
++                                      MGR_DISCONNECT | INDICATION, &inst->down);
++              }
++      }
++      return(0);
++}
++
++void
++mISDN_init_instance(mISDNinstance_t *inst, mISDNobject_t *obj, void *data)
++{
++      if (!obj) {
++              int_error();
++              return;
++      }
++      if (!obj->ctrl) {
++              int_error();
++              return;
++      }
++      inst->obj = obj;
++      inst->data = data;
++      inst->up.owner = inst;
++      inst->down.owner = inst;
++      inst->up.clone = NULL;
++      inst->up.predecessor = NULL;
++      inst->down.clone = NULL;
++      inst->down.predecessor = NULL;
++      obj->ctrl(NULL, MGR_DISCONNECT | REQUEST, &inst->down);
++      obj->ctrl(NULL, MGR_DISCONNECT | REQUEST, &inst->up);
++}
++
++EXPORT_SYMBOL(mISDN_set_dchannel_pid);
++EXPORT_SYMBOL(mISDN_get_lowlayer);
++EXPORT_SYMBOL(mISDN_get_up_layer);
++EXPORT_SYMBOL(mISDN_get_down_layer);
++EXPORT_SYMBOL(mISDN_layermask2layer);
++EXPORT_SYMBOL(mISDN_get_protocol);
++EXPORT_SYMBOL(mISDN_HasProtocol);
++EXPORT_SYMBOL(mISDN_SetHandledPID);
++EXPORT_SYMBOL(mISDN_RemoveUsedPID);
++EXPORT_SYMBOL(mISDN_init_instance);
++EXPORT_SYMBOL(mISDN_SetIF);
++EXPORT_SYMBOL(mISDN_ConnectIF);
++EXPORT_SYMBOL(mISDN_DisConnectIF);
++EXPORT_SYMBOL(mISDN_bprotocol2pid);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/helper.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/helper.h 2004-11-22 09:33:38.137746096 +0000
+@@ -0,0 +1,192 @@
++/* $Id$
++ *
++ *   Basic declarations, defines and prototypes
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++#ifndef _mISDN_HELPER_H
++#define       _mISDN_HELPER_H
++#include <linux/kernel.h>
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++#define int_error() \
++        printk(KERN_ERR "mISDN: INTERNAL ERROR in %s:%d\n", \
++                       __FILE__, __LINE__)
++                       
++#define int_errtxt(fmt, arg...) \
++        printk(KERN_ERR "mISDN: INTERNAL ERROR in %s:%d " fmt "\n", \
++                       __FILE__, __LINE__, ## arg)
++                       
++static inline int
++discard_queue(struct sk_buff_head *q)
++{
++      struct sk_buff *skb;
++      int ret=0;
++
++      while ((skb = skb_dequeue(q))) {
++              dev_kfree_skb(skb);
++              ret++;
++      }
++      return(ret);
++}
++
++#ifdef MISDN_MEMDEBUG
++#define alloc_stack_skb(s, r) __mid_alloc_stack_skb(s, r, __FILE__, __LINE__)
++static inline struct sk_buff *
++__mid_alloc_stack_skb(size_t size, size_t reserve, char *fn, int line)
++{
++      struct sk_buff *skb;
++
++      if (!(skb = __mid_alloc_skb(size + reserve, GFP_ATOMIC, fn, line)))
++#else
++static inline struct sk_buff *
++alloc_stack_skb(size_t size, size_t reserve)
++{
++      struct sk_buff *skb;
++
++      if (!(skb = alloc_skb(size + reserve, GFP_ATOMIC)))
++#endif
++              printk(KERN_WARNING "%s(%d,%d): no skb size\n", __FUNCTION__,
++                      size, reserve);
++      else
++              skb_reserve(skb, reserve);
++      return(skb);
++}
++
++extern void   mISDN_set_dchannel_pid(mISDN_pid_t *, int, int);
++extern int    mISDN_get_lowlayer(int);
++extern int    mISDN_get_up_layer(int);
++extern int    mISDN_get_down_layer(int);
++extern int    mISDN_layermask2layer(int);
++extern int    mISDN_get_protocol(mISDNstack_t *, int);
++extern int    mISDN_HasProtocol(mISDNobject_t *, u_int);
++extern int    mISDN_SetHandledPID(mISDNobject_t *, mISDN_pid_t *);
++extern void   mISDN_RemoveUsedPID(mISDN_pid_t *, mISDN_pid_t *);
++extern void   mISDN_init_instance(mISDNinstance_t *, mISDNobject_t *, void *);
++
++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);
++}
++
++static inline int
++mISDN_HasProtocolP(mISDNobject_t *obj, int *PP)
++{
++      if (!PP) {
++              int_error();
++              return(-EINVAL);
++      }
++      return(mISDN_HasProtocol(obj, *PP));
++}
++
++static inline void
++mISDN_sethead(u_int prim, int dinfo, struct sk_buff *skb)
++{
++      mISDN_head_t *hh = mISDN_HEAD_P(skb);
++
++      hh->prim = prim;
++      hh->dinfo = dinfo;
++}
++
++static inline int
++if_newhead(mISDNif_t *i, u_int prim, int dinfo, struct sk_buff *skb)
++{
++      if (!i->func || !skb)
++              return(-ENXIO);
++      mISDN_sethead(prim, dinfo, skb);
++      return(i->func(i, skb));
++}
++
++#ifdef MISDN_MEMDEBUG
++#define create_link_skb(p, d, l, a, r)        __mid_create_link_skb(p, d, l, a, r, __FILE__, __LINE__)
++static inline struct sk_buff *
++__mid_create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve, char *fn, int line)
++{
++      struct sk_buff  *skb;
++
++      if (!(skb = __mid_alloc_skb(len + reserve, GFP_ATOMIC, fn, line))) {
++#else
++static inline struct sk_buff *
++create_link_skb(u_int prim, int dinfo, int len, void *arg, int reserve)
++{
++      struct sk_buff  *skb;
++
++      if (!(skb = alloc_skb(len + reserve, GFP_ATOMIC))) {
++#endif
++              printk(KERN_WARNING "%s: no skb size %d+%d\n",
++                      __FUNCTION__, len, reserve);
++              return(NULL);
++      } else
++              skb_reserve(skb, reserve);
++      if (len)
++              memcpy(skb_put(skb, len), arg, len);
++      mISDN_sethead(prim, dinfo, skb);
++      return(skb);
++}
++
++#ifdef MISDN_MEMDEBUG
++#define if_link(i, p, d, l, a, r)     __mid_if_link(i, p, d, l, a, r, __FILE__, __LINE__)
++static inline int
++__mid_if_link(mISDNif_t *i, u_int prim, int dinfo, int len, void *arg, int reserve, char *fn, int line)
++{
++      struct sk_buff  *skb;
++      int             err;
++
++      if (!(skb = __mid_create_link_skb(prim, dinfo, len, arg, reserve, fn, line)))
++#else
++static inline int
++if_link(mISDNif_t *i, u_int prim, int dinfo, int len, void *arg, int reserve)
++{
++      struct sk_buff  *skb;
++      int             err;
++
++      if (!(skb = create_link_skb(prim, dinfo, len, arg, reserve)))
++#endif
++              return(-ENOMEM);
++      if (!i)
++              err = -ENXIO;
++      else
++              err = i->func(i, skb);
++      if (err)
++              kfree_skb(skb);
++      return(err);
++}
++
++/* L3 data struct helper functions */
++
++extern        signed int      mISDN_l3_ie2pos(u_char);
++extern        unsigned char   mISDN_l3_pos2ie(int);
++extern        void            mISDN_initQ931_info(Q931_info_t *);
++#ifdef MISDN_MEMDEBUG
++#define mISDN_alloc_l3msg(a, b)       __mid_alloc_l3msg(a, b, __FILE__, __LINE__)
++extern        struct sk_buff  *__mid_alloc_l3msg(int, u_char, char *, int);
++#else
++extern        struct sk_buff  *mISDN_alloc_l3msg(int, u_char);
++#endif
++extern        void            mISDN_AddvarIE(struct sk_buff *, u_char *);
++extern        void            mISDN_AddIE(struct sk_buff *, u_char, u_char *);
++extern        void            mISDN_LogL3Msg(struct sk_buff *);
++
++/* manager default handler helper macros */
++
++#define PRIM_NOT_HANDLED(p)   case p: break
++
++#define MGR_HASPROTOCOL_HANDLER(p,a,o)        \
++      if ((MGR_HASPROTOCOL | REQUEST) == p) {\
++              if (a) {\
++                      int prot = *((int *)a);\
++                      return(mISDN_HasProtocol(o, prot));\
++              } else \
++                      return(-EINVAL);\
++      }
++
++#endif /* _mISDN_HELPER_H */
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.c      2004-11-22 09:33:38.148744424 +0000
+@@ -0,0 +1,3607 @@
++/* 
++
++ * hfc_multi.c  low level driver for hfc-4s/hfc-8s/hfc-e1 based cards
++ *
++ * Author     Andreas Eversberg (jolly@jolly.de)
++ *
++ * inspired by existing hfc-pci driver:
++ * Copyright 1999  by Werner Cornelius (werner@isdn-development.de)
++ * Copyright 2001  by Karsten Keil (keil@isdn4linux.de)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License 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.
++ *
++ *
++ * Thanx to Cologne Chip AG for this great controller!
++ */
++
++/* module parameters:
++ * type:
++      Value 1 = HFC-E1 (1 port) 0x01
++      Value 4 = HFC-4S (4 ports) 0x04
++      Value 8 = HFC-8S (8 ports) 0x08
++      Bit 8   = uLaw (instead of aLaw)
++      Bit 9   = Enable DTMF detection on all B-channels
++      Bit 10  = spare 
++      Bit 11  = Set PCM bus into slave mode.
++      Bit 14  = Use external ram (128K)
++      Bit 15  = Use external ram (512K)
++      Bit 16  = Use 64 timeslots instead of 32
++      Bit 17  = Use 128 timeslots instead of anything else
++
++ * protocol:
++      NOTE: Must be given for all ports, not for the number of cards.
++      HFC-4S/HFC-8S/HFC-E1 bits:
++      Bit 0-3 = protocol
++      Bit 4   = NT-Mode
++      Bit 5   = PTP (instead of multipoint)
++
++      HFC-4S/HFC-8S only bits:
++      Bit 16  = Use master clock for this S/T interface (ony once per chip).
++      Bit 17  = transmitter line setup (non capacitive mode) DONT CARE!
++      Bit 18  = Disable E-channel. (No E-channel processing)
++      Bit 19  = Register E-channel as D-stack (TE-mode only)
++
++      HFC-E1 only bits:
++      Bit 16  = interface: 0=copper, 1=optical
++      Bit 17  = reserved (later for 32 B-channels transparent mode)
++      Bit 18  = Report LOS
++      Bit 19  = Report AIS
++      Bit 20  = Report SLIP
++      Bit 21-22 = elastic jitter buffer (1-3), Use 0 for default.
++(all other bits are reserved and shall be 0)
++
++ * layermask:
++      NOTE: Must be given for all ports, not for the number of cards.
++      mask of layers to be used for D-channel stack
++
++ * 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 all cards
++      Give the id of the PCM bus. All PCM busses with the same ID
++      are expected to be connected and have equal slots.
++      Only one chip of the PCM bus must be master, the others slave.
++      -1 means no support of PCM bus.
++ */
++
++/* debug using register map (never use this, it will flood your system log) */
++//#define HFC_REGISTER_MAP
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++
++#include "dchannel.h"
++#include "bchannel.h"
++#include "layer1.h"
++#include "dsp.h"
++#include "helper.h"
++#include "debug.h"
++#include <linux/isdn_compat.h>
++
++#define SPIN_DEBUG
++#define LOCK_STATISTIC
++#include "hw_lock.h"
++#include "hfc_multi.h"
++
++extern const char *CardType[];
++
++static const char *hfcmulti_revision = "$Revision$";
++
++static int HFC_cnt;
++
++static mISDNobject_t  HFCM_obj;
++
++static char HFCName[] = "HFC_multi";
++
++/* table entry in the PCI devices list */
++typedef struct {
++      int vendor_id;
++      int vendor_sub;
++      int device_id;
++      int device_sub;
++      char *vendor_name;
++      char *card_name;
++      int type;
++      int clock2;
++      int leds;
++} PCI_ENTRY;
++
++static int poll_timer = 6;    /* default = 128 samples = 16ms */
++/* number of POLL_TIMER interrupts for G2 timeout (min 120ms) */
++static int nt_t1_count[] = { 480, 240, 120, 60, 30, 15, 8, 4 };
++#define CLKDEL_TE     0x0f    /* CLKDEL in TE mode */
++#define CLKDEL_NT     0x0c    /* CLKDEL in NT mode (0x60 MUST not be included!) */
++static u_char silence =       0xff;   /* silence by LAW */
++
++/* enable 32 bit fifo access (PC usage) */
++#define FIFO_32BIT_ACCESS
++
++#define VENDOR_CCD "Cologne Chip AG"
++
++static const PCI_ENTRY id_list[] =
++{
++      {0x1397, 0x1397, 0x08B4, 0x08B4, VENDOR_CCD,
++       "HFC-4S Eval", 4, 0, 0},
++      {0x1397, 0x1397, 0x16B8, 0x16B8, VENDOR_CCD,
++       "HFC-8S Eval", 8, 0, 0},
++      {0x1397, 0x1397, 0x30B1, 0x30B1, VENDOR_CCD,
++       "HFC-E1 Eval", 1, 0, 0},
++      {0x1397, 0x1397, 0x08B4, 0xB520, VENDOR_CCD,
++       "HFC-4S IOB4ST", 4, 1, 2},
++      {0x1397, 0x1397, 0x08B4, 0xB620, VENDOR_CCD,
++       "HFC-4S", 4, 1, 2},
++      {0x1397, 0x1397, 0x08B4, 0xB560, VENDOR_CCD,
++       "HFC-4S Beronet Card", 4, 1, 2},
++      {0x1397, 0x1397, 0x16B8, 0xB521, VENDOR_CCD,
++       "HFC-8S IOB4ST Recording", 8, 1, 0},
++      {0x1397, 0x1397, 0x16B8, 0xB522, VENDOR_CCD,
++       "HFC-8S IOB8ST", 8, 1, 0},
++      {0x1397, 0x1397, 0x16B8, 0xB622, VENDOR_CCD,
++       "HFC-8S", 8, 1, 0},
++      {0x1397, 0x1397, 0x16B8, 0xB562, VENDOR_CCD,
++       "HFC-8S Beronet Card", 8, 1, 0},
++      {0x1397, 0x1397, 0x30B1, 0xB523, VENDOR_CCD,
++       "HFC-E1 IOB1E1", 1, 0, 1}, /* E1 only supports single clock */
++      {0x1397, 0x1397, 0x30B1, 0xC523, VENDOR_CCD,
++       "HFC-E1", 1, 0, 1}, /* E1 only supports single clock */
++      {0x1397, 0x1397, 0x30B1, 0xB563, VENDOR_CCD,
++       "HFC-E1 Beronet Card", 1, 0, 1}, /* E1 only supports single clock */
++      {0, 0, 0, 0, NULL, NULL, 0, 0, 0},
++};
++
++
++/****************/
++/* module stuff */
++/****************/
++
++/* NOTE: MAX_PORTS must be 8*MAX_CARDS */
++#define MAX_CARDS     16
++#define MAX_PORTS     128
++#define MODULE_CARDS_T        "1-16i"
++#define MODULE_PORTS_T        "1-128i" /* 16 cards can have 128 ports */
++static u_int type[MAX_CARDS];
++static int pcm[MAX_PORTS];
++static u_int protocol[MAX_PORTS];
++static int layermask[MAX_PORTS];
++static int debug;
++static int poll;
++
++#ifdef MODULE
++MODULE_AUTHOR("Andreas Eversberg");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++MODULE_PARM(poll, "1i");
++MODULE_PARM(type, MODULE_CARDS_T);
++MODULE_PARM(pcm, MODULE_CARDS_T);
++MODULE_PARM(protocol, MODULE_PORTS_T);
++MODULE_PARM(layermask, MODULE_PORTS_T);
++#endif
++
++
++/*************************/
++/* lock and unlock stuff */
++/*************************/
++
++static int
++lock_dev(void *data, int nowait)
++{
++      register mISDN_HWlock_t *lock = &((hfc_multi_t *)data)->lock;
++      if (debug & DEBUG_HFCMULTI_LOCK)
++              printk(KERN_DEBUG "%s\n", __FUNCTION__);
++      return(lock_HW(lock, nowait));
++}
++static void
++unlock_dev(void *data)
++{
++      register mISDN_HWlock_t *lock = &((hfc_multi_t *)data)->lock;
++      if (debug & DEBUG_HFCMULTI_LOCK)
++              printk(KERN_DEBUG "%s\n", __FUNCTION__);
++      unlock_HW(lock);
++}
++
++/******************************************/
++/* free hardware resources used by driver */
++/******************************************/
++
++static void
++release_io_hfcmulti(hfc_multi_t *hc)
++{
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
++
++      /* irq off */
++      HFC_outb(hc, R_IRQ_CTRL, 0);
++
++      /* soft reset */
++      hc->hw.r_cirm |= V_SRES;
++      HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
++      udelay(10);
++      hc->hw.r_cirm &= ~V_SRES;
++      HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
++      udelay(10); /* instead of 'wait' that may cause locking */
++
++      /* disable memory mapped ports / io ports */
++      pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
++#ifdef CONFIG_HFCMULTI_PCIMEM
++      if (hc->pci_membase)
++              iounmap((void *)hc->pci_membase);
++#else
++      if (hc->pci_iobase)
++              release_region(hc->pci_iobase, 8);
++#endif
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: done\n", __FUNCTION__);
++}
++
++/****************************************************************************/
++/* 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(hfc_multi_t *hc)
++{
++      u_long val, val2 = 0, rev;
++      int cnt = 0;
++      int i;
++      u_char r_conf_en;
++
++      /* reset all registers */
++      memset(&hc->hw, 0, sizeof(hfcmulti_hw_t));
++
++      /* revision check */
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
++      val = HFC_inb(hc, R_CHIP_ID);
++      rev = HFC_inb(hc, R_CHIP_RV);
++      printk(KERN_INFO "HFC_multi: resetting HFC with chip ID=0x%lx revision=%ld%s\n", val>>4, 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", __FUNCTION__);
++              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", __FUNCTION__);
++              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;
++      }
++
++      /* 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;
++
++      /* 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(10);
++      hc->hw.r_cirm = 0;
++      HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
++      udelay(10);
++      HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
++
++      /* set pcm mode & reset */
++      if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: setting PCM into slave mode\n", __FUNCTION__);
++      } else {
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: setting PCM into master mode\n", __FUNCTION__);
++              hc->hw.r_pcm_mo0 |= V_PCM_MO;
++      }
++      i = 0;
++      HFC_outb(hc, R_PCM_MO0, hc->hw.r_pcm_mo0 | 0x90);
++      if (hc->slots == 32)
++              HFC_outb(hc, R_PCM_MO1, 0x00);
++      if (hc->slots == 64)
++              HFC_outb(hc, R_PCM_MO1, 0x10);
++      if (hc->slots == 128)
++              HFC_outb(hc, R_PCM_MO1, 0x20);
++      HFC_outb(hc, R_PCM_MO0, hc->hw.r_pcm_mo0 | 0x00);
++      while (i < 256) {
++              HFC_outb_(hc, R_SLOT, i);
++              HFC_outb_(hc, A_SL_CFG, 0);
++              HFC_outb_(hc, A_CONF, 0);
++              hc->slot_owner[i] = -1;
++              i++;
++      }
++
++      /* set clock speed */
++      if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) {
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: setting double clock\n", __FUNCTION__);
++              HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
++      }
++
++      /* check if R_F0_CNT counts */
++      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 status ok\n", val);
++      unlock_dev(hc);
++      set_current_state(TASK_UNINTERRUPTIBLE);
++      while (cnt < 50) { /* max 50 ms */
++              schedule_timeout((HZ*10)/1000); /* Timeout 10ms */
++              cnt+=10;
++              val2 = HFC_inb(hc, R_F0_CNTL);
++              val2 += HFC_inb(hc, R_F0_CNTH) << 8;
++              if (val2 >= val+4) /* wait 4 pulses */
++                      break;
++      }
++      lock_dev(hc, 0);
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "HFC_multi F0_CNT %ld after %dms\n", val2, cnt);
++      if (val2 < val+4) {
++              printk(KERN_ERR "HFC_multi ERROR 125us pulse not counting.\n");
++              if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
++                      printk(KERN_ERR "HFC_multi This happens in PCM slave mode without connected master.\n");
++              }
++              return(-EIO);
++      }
++
++      /* set up timer */
++      HFC_outb(hc, R_TI_WD, poll_timer);
++      hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
++
++      /* 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", __FUNCTION__);
++              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 */
++              break;
++
++              case 2: /* HFC-4S OEM */
++              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", __FUNCTION__, hc->masterclk, hc->type-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 "%s: done\n", __FUNCTION__);
++      return(0);
++}
++
++
++/***************/
++/* output leds */
++/***************/
++static void
++hfcmulti_leds(hfc_multi_t *hc)
++{
++      int i, state, active;
++      dchannel_t *dch;
++      int led[4];
++
++      hc->ledcount += poll;
++      if (hc->ledcount > 4096)
++              hc->ledcount -= 4096;
++
++      switch(hc->leds) {
++              case 1: /* HFC-E1 OEM */
++              break;
++
++              case 2: /* HFC-4S OEM */
++              i = 0;
++              while(i < 4) {
++                      state = 0;
++                      active = -1;
++                      if ((dch = hc->chan[(i<<2)|2].dch)) {
++                              state = dch->ph_state;
++                              active = test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)?3:7;
++                      }
++                      if (state) {
++                              if (state==active) {
++                                      if (hc->activity[i]) {
++                                              led[i] = 1; /* led green */
++                                              hc->activity[i] = 0;
++                                      } else
++                                              led[i] = 2; /* led red */
++                              } else if (hc->ledcount>>11)
++                                      led[i] = 2; /* led red */
++                              else
++                                      led[i] = 0; /* led off */
++                      } else
++                              led[i] = 0; /* led off */
++                      i++;
++              }
++//printk("leds %d %d %d %d\n", led[0], led[1], led[2], led[3]);
++              HFC_outb(hc, R_GPIO_EN1,
++                      ((led[0]>0)<<0) | ((led[1]>0)<<1) |
++                      ((led[2]>0)<<2) | ((led[3]>0)<<3));
++              HFC_outb(hc, R_GPIO_OUT1,
++                      ((led[0]&1)<<0) | ((led[1]&1)<<1) |
++                      ((led[2]&1)<<2) | ((led[3]&1)<<3));
++              break;
++      }
++}
++/**************************/
++/* read dtmf coefficients */
++/**************************/
++
++static void
++hfcmulti_dtmf(hfc_multi_t *hc)
++{
++      signed long coeff[16];
++      unsigned long mantissa;
++      int co, ch;
++      bchannel_t *bch = NULL;
++      unsigned char exponent;
++      int dtmf = 0;
++      int addr;
++      unsigned short w_float;
++      struct sk_buff *skb;
++
++      if (debug & DEBUG_HFCMULTI_DTMF)
++              printk(KERN_DEBUG "%s: dtmf detection irq\n", __FUNCTION__);
++      ch = 0;
++      while(ch < 32) {
++              /* only process enabled B-channels */
++              if (!(bch = hc->chan[ch].bch)) {
++                      ch++;
++                      continue;
++              }
++              if (!hc->created[hc->chan[ch].port]) {
++                      ch++;
++                      continue;
++              }
++              if (bch->protocol != ISDN_PID_L1_B_64TRANS) {
++                      ch++;
++                      continue;
++              }
++              if (debug & DEBUG_HFCMULTI_DTMF)
++                      printk(KERN_DEBUG "%s: dtmf channel %d:", __FUNCTION__, ch);
++              dtmf = 1;
++              co = 0;
++              while(co < 8) {
++                      /* read W(n-1) coefficient */
++                      addr = hc->DTMFbase + ((co<<7) | (ch<<2));
++                      HFC_outb_(hc, R_RAM_ADDR0, addr);
++                      HFC_outb_(hc, R_RAM_ADDR1, addr>>8);
++                      HFC_outb_(hc, R_RAM_ADDR2, (addr>>16) | V_ADDR_INC);
++                      w_float = HFC_inb_(hc, R_RAM_DATA);
++#ifdef CONFIG_HFCMULTI_PCIMEM
++                      w_float |= (HFC_inb_(hc, R_RAM_DATA) << 8);
++#else
++                      w_float |= (HFC_getb(hc) << 8);
++#endif
++                      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_(hc, R_RAM_DATA);
++#ifdef CONFIG_HFCMULTI_PCIMEM
++                      w_float |= (HFC_inb_(hc, R_RAM_DATA) << 8);
++#else
++                      w_float |= (HFC_getb(hc) << 8);
++#endif
++                      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;
++                      co++;
++              }
++              if (debug & DEBUG_HFCMULTI_DTMF)
++                      printk("\n");
++              skb = create_link_skb(PH_CONTROL | INDICATION, HW_HFC_COEFF, sizeof(coeff), coeff, 0);
++              if (!skb) {
++                      printk(KERN_WARNING "%s: No memory for skb\n", __FUNCTION__);
++                      ch++;
++                      continue;
++              }
++              skb_queue_tail(&hc->chan[ch].dtmfque, skb);
++              bch_sched_event(bch, B_DTMFREADY);
++              ch++;
++      }
++
++      /* restart DTMF processing */
++      hc->dtmf = dtmf;
++      if (dtmf)
++              HFC_outb_(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
++}
++
++
++/*********************************/
++/* fill fifo as much as possible */
++/*********************************/
++
++static void
++hfcmulti_tx(hfc_multi_t *hc, int ch, dchannel_t *dch, bchannel_t *bch)
++{
++      int i, ii, temp;
++      int Zspace, z1, z2;
++      int Fspace, f1, f2;
++      unsigned char *d, *dd, *buf = NULL;
++      int *len = NULL, *idx = NULL; /* = NULL, to make GCC happy */
++      int txpending, slot_tx;
++      int hdlc = 0;
++
++      /* get skb, fifo & mode */
++      if (dch) {
++              buf = dch->tx_buf;
++              len = &dch->tx_len;
++              idx = &dch->tx_idx;
++              hdlc = 1;
++      }
++      if (bch) {
++              buf = bch->tx_buf;
++              len = &bch->tx_len;
++              idx = &bch->tx_idx;
++              if (bch->protocol == ISDN_PID_L1_B_64HDLC)
++                      hdlc = 1;
++      }
++      txpending = hc->chan[ch].txpending;
++      slot_tx = hc->chan[ch].slot_tx;
++      if ((!(*len)) && txpending!=1)
++              return; /* no data */
++
++//printk("debug: data: len = %d, txpending = %d!!!!\n", *len, txpending);
++      /* lets see how much data we will have left in buffer */
++      HFC_outb_(hc, R_FIFO, ch<<1);
++      HFC_wait_(hc);
++      if (txpending == 2) {
++              /* reset fifo */
++              HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F);
++              HFC_wait_(hc);
++              HFC_outb(hc, A_SUBCH_CFG, 0);
++              txpending = 1;
++      }
++next_frame:
++      if (hdlc) {
++              f1 = HFC_inb_(hc, A_F1);
++              f2 = HFC_inb_(hc, A_F2);
++              while (f2 != (temp=HFC_inb_(hc, A_F2))) {
++                      if (debug & DEBUG_HFCMULTI_FIFO)
++                              printk(KERN_DEBUG "%s: reread f2 because %d!=%d\n", __FUNCTION__, 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_(hc, A_Z1) - hc->Zmin;
++      z2 = HFC_inw_(hc, A_Z2) - hc->Zmin;
++      while(z2 != (temp=(HFC_inw_(hc, A_Z2) - hc->Zmin))) {
++              if (debug & DEBUG_HFCMULTI_FIFO)
++                      printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2);
++              z2 = temp; /* repeat unti Z2 is equal */
++      }
++      Zspace = z2-z1-1;
++      if (Zspace < 0)
++              Zspace += hc->Zlen;
++      /* buffer too full, there must be at least one more byte for 0-volume */
++      if (Zspace < 4) /* just to be safe */
++              return;
++
++      /* if no data */
++      if (!(*len)) {
++              if (z1 == z2) { /* empty */
++                      /* if done with FIFO audio data during PCM connection */
++                      if (!hdlc && 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", __FUNCTION__, ch, slot_tx);
++                              
++                              /* connect slot */
++                              HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF);
++                              HFC_outb_(hc, R_FIFO, ch<<1 | 1);
++                              HFC_wait_(hc);
++                              HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF);
++                              HFC_outb_(hc, R_FIFO, ch<<1);
++                              HFC_wait_(hc);
++                      }
++                      txpending = hc->chan[ch].txpending = 0;
++              }
++              return; /* no data */
++      }
++
++      /* if audio data */
++      if (!hdlc && !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", __FUNCTION__, ch, slot_tx);
++              /* disconnect slot */
++              HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
++              HFC_outb_(hc, R_FIFO, ch<<1 | 1);
++              HFC_wait_(hc);
++              HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
++              HFC_outb_(hc, R_FIFO, ch<<1);
++              HFC_wait_(hc);
++      }
++      txpending = hc->chan[ch].txpending = 1;
++
++      /* show activity */
++      hc->activity[hc->chan[ch].port] = 1;
++
++      /* fill fifo to what we have left */
++      i = *idx;
++      ii = *len; 
++      d = buf + i;
++      if (ii-i > Zspace)
++              ii = Zspace+i;
++      if (debug & DEBUG_HFCMULTI_FIFO) {
++              printk(KERN_DEBUG "%s: fifo(%d) has %d bytes space left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n",
++                      __FUNCTION__, ch, Zspace, z1, z2, ii-i, (*len)-i, hdlc?"HDLC":"TRANS");
++      }
++#ifdef FIFO_32BIT_ACCESS
++#ifndef CONFIG_HFCMULTI_PCIMEM
++      HFC_set(hc, A_FIFO_DATA0);
++#endif
++      dd = d + ((ii-i)&0xfffc);
++      i += (ii-i) & 0xfffc;
++      while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++              HFC_outl_(hc, A_FIFO_DATA0, *((unsigned long *)d));
++#else
++              HFC_putl(hc, *((unsigned long *)d));
++#endif
++//            if (debug & DEBUG_HFCMULTI_FIFO)
++//                    printk("%02x %02x %02x %02x ", d[0], d[1], d[2], d[3]);
++              d+=4;
++      }
++#endif
++      dd = d + (ii-i);
++      while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++              HFC_outb_(hc, A_FIFO_DATA0, *d);
++#else
++              HFC_putb(hc, *d);
++#endif
++//            if (debug & DEBUG_HFCMULTI_FIFO)
++//                    printk("%02x ", d[0]);
++              d++;
++      }
++//    if (debug & DEBUG_HFCMULTI_FIFO)
++//            printk("\n");
++      *idx = 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 */
++      if (hdlc) {
++              /* increment f-counter */
++              HFC_outb_(hc, R_INC_RES_FIFO, V_INC_F);
++              HFC_wait_(hc);
++      }
++      if (dch) {
++              /* check for next frame */
++              if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
++                      if (dch->next_skb) {
++                              dch->tx_idx = 0;
++                              dch->tx_len = dch->next_skb->len;
++                              memcpy(dch->tx_buf, dch->next_skb->data, dch->tx_len);
++                              dchannel_sched_event(dch, D_XMTBUFREADY);
++                              goto next_frame;
++                      } else
++                              printk(KERN_WARNING "%s: tx irq TX_NEXT without skb (dch ch=%d)\n", __FUNCTION__, ch);
++              }
++              test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++              dch->tx_idx = dch->tx_len = 0;
++      }
++      if (bch) {
++              /* check for next frame */
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      if (bch->next_skb) {
++                              bch->tx_idx = 0;
++                              bch->tx_len = bch->next_skb->len;
++                              memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len);
++                              bch_sched_event(bch, B_XMTBUFREADY);
++                              goto next_frame;
++                      } else
++                              printk(KERN_WARNING "%s: tx irq TX_NEXT without skb (bch ch=%d)\n", __FUNCTION__, ch);
++              }
++              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              bch->tx_idx = bch->tx_len = 0;
++      }
++      /* 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 (!hdlc)
++              HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence);
++}
++
++
++/**************/
++/* empty fifo */
++/**************/
++
++static void
++hfcmulti_rx(hfc_multi_t *hc, int ch, dchannel_t *dch, bchannel_t *bch)
++{
++      int ii, temp;
++      int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */
++      int f1 = 0, f2 = 0; /* = 0, to make GCC happy */
++      unsigned char *d, *dd, *buf = NULL;
++      int *idx = NULL, max = 0; /* = 0, to make GCC happy */
++      int hdlc = 0;
++      struct sk_buff *skb;
++
++      /* get skb, fifo & mode */
++      if (dch) {
++              buf = hc->chan[ch].rx_buf;
++              idx = &hc->chan[ch].rx_idx;
++              max = MAX_DFRAME_LEN_L1;
++              hdlc = 1;
++      }
++      if (bch) {
++              buf = bch->rx_buf;
++              idx = &bch->rx_idx;
++              max = MAX_DATA_MEM;
++              if (bch->protocol == ISDN_PID_L1_B_64HDLC)
++                      hdlc = 1;
++      }
++
++      /* lets see how much data we received */
++      HFC_outb_(hc, R_FIFO, (ch<<1)|1);
++      HFC_wait_(hc);
++next_frame:
++#if 0
++      /* set Z2(F1) */
++      HFC_outb_(hc, R_RAM_SZ, hc->hw.r_ram_sz & ~V_FZ_MD);
++#endif
++      if (hdlc) {
++              f1 = HFC_inb_(hc, A_F1);
++              while (f1 != (temp=HFC_inb_(hc, A_F1))) {
++                      if (debug & DEBUG_HFCMULTI_FIFO)
++                              printk(KERN_DEBUG "%s: reread f1 because %d!=%d\n", __FUNCTION__, temp, f1);
++                      f1 = temp; /* repeat until F1 is equal */
++              }
++              f2 = HFC_inb_(hc, A_F2);
++      }
++      z1 = HFC_inw_(hc, A_Z1) - hc->Zmin;
++      while(z1 != (temp=(HFC_inw_(hc, A_Z1) - hc->Zmin))) {
++              if (debug & DEBUG_HFCMULTI_FIFO)
++                      printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2);
++              z1 = temp; /* repeat unti Z1 is equal */
++      }
++      z2 = HFC_inw_(hc, A_Z2) - hc->Zmin;
++      Zsize = z1-z2;
++      if (hdlc && f1!=f2) /* complete hdlc frame */
++              Zsize++;
++      if (Zsize < 0)
++              Zsize += hc->Zlen;
++      /* if buffer is empty */
++      if (Zsize <= 0)
++              return;
++      
++      /* show activity */
++      hc->activity[hc->chan[ch].port] = 1;
++
++      /* empty fifo with what we have */
++      if (hdlc) {
++              if (debug & DEBUG_HFCMULTI_FIFO)
++                      printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) got=%d\n",
++                              __FUNCTION__, ch, Zsize, z1, z2, (f1==f2)?"fragment":"COMPLETE", f1, f2, Zsize+*idx);
++              /* HDLC */
++              ii = Zsize;
++              if ((ii + *idx) > max) {
++                      if (debug & DEBUG_HFCMULTI_FIFO)
++                              printk(KERN_DEBUG "%s: hdlc-frame too large.\n", __FUNCTION__);
++                      *idx = 0;
++                      HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F);
++                      HFC_wait_(hc);
++                      return;
++              }
++              d = buf + *idx;
++#ifdef FIFO_32BIT_ACCESS
++#ifndef CONFIG_HFCMULTI_PCIMEM
++              HFC_set(hc, A_FIFO_DATA0);
++#endif
++              dd = d + (ii&0xfffc);
++              while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++                      *((unsigned long *)d) = HFC_inl_(hc, A_FIFO_DATA0);
++#else
++                      *((unsigned long *)d) = HFC_getl(hc);
++#endif
++                      d+=4;
++              }
++              dd = d + (ii&0x0003);
++#else
++              dd = d + ii;
++#endif
++              while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++                      *d++ = HFC_inb_(hc, A_FIFO_DATA0);
++#else
++                      *d++ = HFC_getb(hc);
++#endif
++              }
++              *idx += ii;
++              if (f1 != f2) {
++                      /* increment Z2,F2-counter */
++                      HFC_outb_(hc, R_INC_RES_FIFO, V_INC_F);
++                      HFC_wait_(hc);
++                      /* check size */
++                      if (*idx < 4) {
++                              if (debug & DEBUG_HFCMULTI_FIFO)
++                                      printk(KERN_DEBUG "%s: Frame below minimum size\n", __FUNCTION__);
++                              return;
++                      }
++                      /* there is at least one complete frame, check crc */
++                      if (buf[(*idx)-1]) {
++                              if (debug & DEBUG_HFCMULTI_CRC)
++                                      printk(KERN_DEBUG "%s: CRC-error\n", __FUNCTION__);
++                              return;
++                      } 
++                      /* only send dchannel if in active state */
++                      if (dch && hc->type==1 && hc->chan[ch].e1_state!=1)
++                              return;
++                      if (!(skb = alloc_stack_skb((*idx)-3, (bch)?bch->up_headerlen:dch->up_headerlen))) {
++                              printk(KERN_DEBUG "%s: No mem for skb\n", __FUNCTION__);
++                              return;
++                      }
++                      memcpy(skb_put(skb, (*idx)-3), buf, (*idx)-3);
++                      if (debug & DEBUG_HFCMULTI_FIFO) {
++                              temp = 0;
++                              while(temp < (*idx)-3)
++                                      printk("%02x ", skb->data[temp++]);
++                              printk("\n");
++                      }
++                      if (dch) {
++                              if (debug & DEBUG_HFCMULTI_FIFO)
++                                      printk(KERN_DEBUG "%s: sending D-channel frame to user space.\n", __FUNCTION__); 
++                              /* schedule D-channel event */
++                              skb_queue_tail(&dch->rqueue, skb);
++                              dchannel_sched_event(dch, D_RCVBUFREADY);
++                      }
++                      if (bch) {
++                              /* schedule B-channel event */
++                              skb_queue_tail(&bch->rqueue, skb);
++                              bch_sched_event(bch, B_RCVBUFREADY);
++                      }
++                      *idx = 0;
++                      goto next_frame;
++              }
++              /* there is an incomplete frame */
++      } else {
++              /* transparent */
++              ii = Zsize;
++              if (ii > MAX_DATA_MEM)
++                      ii = MAX_DATA_MEM;
++              if (!(skb = alloc_stack_skb(ii, bch->up_headerlen))) {
++                      printk(KERN_DEBUG "%s: No mem for skb\n", __FUNCTION__);
++                      HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F);
++                      HFC_wait_(hc);
++                      return;
++              }
++              if (debug & DEBUG_HFCMULTI_FIFO)
++                      printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) TRANS\n",
++                              __FUNCTION__, ch, ii, z1, z2);
++              d = skb_put(skb, ii);
++#ifdef FIFO_32BIT_ACCESS
++#ifndef CONFIG_HFCMULTI_PCIMEM
++              HFC_set(hc, A_FIFO_DATA0);
++#endif
++              dd = d + (ii&0xfffc);
++              while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++                      *((unsigned long *)d) = HFC_inl_(hc, A_FIFO_DATA0);
++#else
++                      *((unsigned long *)d) = HFC_getl(hc);
++#endif
++                      d+=4;
++              }
++#endif
++              dd = d + (ii&0x0003);
++              while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++                      *d++ = HFC_inb_(hc, A_FIFO_DATA0);
++#else
++                      *d++ = HFC_getb(hc);
++#endif
++              }
++              if (dch) {
++                      /* schedule D-channel event */
++                      skb_queue_tail(&dch->rqueue, skb);
++                      dchannel_sched_event(dch, D_RCVBUFREADY);
++              }
++              if (bch) {
++                      /* schedule B-channel event */
++                      skb_queue_tail(&bch->rqueue, skb);
++                      bch_sched_event(bch, B_RCVBUFREADY);
++              }
++      }
++}
++
++
++/*********************/
++/* Interrupt handler */
++/*********************/
++
++static irqreturn_t
++hfcmulti_interrupt(int intno, void *dev_id, struct pt_regs *regs)
++{
++      hfc_multi_t *hc = dev_id;
++      bchannel_t *bch;
++      dchannel_t *dch;
++      u_long flags;
++      u_char r_irq_statech, status, r_irq_misc, r_irq_oview, r_irq_fifo_bl;
++      int ch;
++      int i, j;
++      int temp;
++
++      spin_lock_irqsave(&hc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      hc->lock.spin_adr = (void *)0x3001;
++#endif
++
++      if (!hc) {
++              printk(KERN_WARNING "HFC-multi: Spurious interrupt!\n");
++              irq_notforus:
++#ifdef SPIN_DEBUG
++              hc->lock.spin_adr = NULL;
++#endif
++              spin_unlock_irqrestore(&hc->lock.lock, flags);
++              return(IRQ_NONE);
++      }
++
++      status = HFC_inb_(hc, R_STATUS);
++      r_irq_statech = HFC_inb_(hc, R_IRQ_STATECH);
++      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 (test_and_set_bit(STATE_FLAG_BUSY, &hc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
++                      __FUNCTION__, hc->lock.state);
++#ifdef SPIN_DEBUG
++              printk(KERN_ERR "%s: previous lock:%p\n",
++                      __FUNCTION__, hc->lock.busy_adr);
++#endif
++#ifdef LOCK_STATISTIC
++              hc->lock.irq_fail++;
++#endif
++      } else {
++#ifdef LOCK_STATISTIC
++              hc->lock.irq_ok++;
++#endif
++#ifdef SPIN_DEBUG
++              hc->lock.busy_adr = hfcmulti_interrupt;
++#endif
++      }
++
++      test_and_set_bit(STATE_FLAG_INIRQ, &hc->lock.state);
++#ifdef SPIN_DEBUG
++        hc->lock.spin_adr= NULL;
++#endif
++      spin_unlock_irqrestore(&hc->lock.lock, flags);
++
++      if (r_irq_statech) {
++              if (hc->type != 1) {
++                      /* state machine */
++                      ch = 0;
++                      while(ch < 32) {
++                              if ((dch = hc->chan[ch].dch)) {
++                                      if (r_irq_statech & 1) {
++                                              HFC_outb_(hc, R_ST_SEL, hc->chan[ch].port);
++                                              dch->ph_state = HFC_inb(hc, A_ST_RD_STATE) & 0x0f;
++                                              dchannel_sched_event(dch, D_L1STATECHANGE);
++                                              if (debug & DEBUG_HFCMULTI_STATE)
++                                                      printk(KERN_DEBUG "%s: S/T newstate %x port %d\n", __FUNCTION__, dch->ph_state, hc->chan[ch].port);
++                                      }
++                                      r_irq_statech >>= 1;
++                              }
++                              ch++;
++                      }
++              }
++      }
++      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_(hc, R_IRQ_MISC);
++              if (r_irq_misc & V_STA_IRQ) {
++                      if (hc->type == 1) {
++                              /* state machine */
++                              dch = hc->chan[16].dch;
++                              dch->ph_state = HFC_inb_(hc, R_E1_RD_STA) & 0x7;
++                              dchannel_sched_event(dch, D_L1STATECHANGE);
++                              if (debug & DEBUG_HFCMULTI_STATE)
++                                      printk(KERN_DEBUG "%s: E1 newstate %x\n", __FUNCTION__, dch->ph_state);
++                      }
++              }
++              if (r_irq_misc & V_TI_IRQ) {
++                      /* -> timer IRQ */
++                      ch = 0;
++                      while(ch < 32) {
++                              if ((dch = hc->chan[ch].dch))
++                              if (hc->created[hc->chan[ch].port]) {
++                                      hfcmulti_tx(hc, ch, dch, NULL);
++                                      /* fifo is started when switching to rx-fifo */
++                                      hfcmulti_rx(hc, ch, dch, NULL);
++                                      if (hc->chan[ch].nt_timer > -1) {
++                                              if (!(--hc->chan[ch].nt_timer)) {
++                                                      dchannel_sched_event(dch, D_L1STATECHANGE);
++                                                      if (debug & DEBUG_HFCMULTI_STATE)
++                                                              printk(KERN_DEBUG "%s: nt_timer at state %x\n", __FUNCTION__, dch->ph_state);
++                                              }
++                                      }
++                              }
++                              if ((bch = hc->chan[ch].bch))
++                              if (hc->created[hc->chan[ch].port]) {
++                                      if (bch->protocol != ISDN_PID_NONE) {
++                                              hfcmulti_tx(hc, ch, NULL, bch);
++                                              /* fifo is started when switching to rx-fifo */
++                                              hfcmulti_rx(hc, ch, NULL, bch);
++                                      }
++                              }
++                              ch++;
++                      }
++                      if (hc->type == 1)
++                      if (hc->created[0]) {
++                              if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].cfg)) {
++                                      /* LOS */
++                                      temp = HFC_inb_(hc, R_RX_STA0) & V_SIG_LOS;
++                                      if (!temp && hc->chan[16].los)
++                                              dchannel_sched_event(dch, D_LOS);
++                                      if (temp && !hc->chan[16].los)
++                                              dchannel_sched_event(dch, D_LOS_OFF);
++                                      hc->chan[16].los = temp;
++                              }
++                              if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[16].cfg)) {
++                                      /* AIS */
++                                      temp = HFC_inb_(hc, R_RX_STA0) & V_AIS;
++                                      if (!temp && hc->chan[16].ais)
++                                              dchannel_sched_event(dch, D_AIS);
++                                      if (!temp && hc->chan[16].ais)
++                                              dchannel_sched_event(dch, D_AIS_OFF);
++                                      hc->chan[16].ais = temp;
++                              }
++                              if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[16].cfg)) {
++                                      /* SLIP */
++                                      temp = HFC_inb_(hc, R_SLIP) & V_FOSLIP_RX;
++                                      if (!temp && hc->chan[16].slip_rx)
++                                              dchannel_sched_event(dch, D_SLIP_RX);
++                                      hc->chan[16].slip_rx = temp;
++                                      temp = HFC_inb_(hc, R_SLIP) & V_FOSLIP_TX;
++                                      if (!temp && hc->chan[16].slip_tx)
++                                              dchannel_sched_event(dch, D_SLIP_TX);
++                                      hc->chan[16].slip_tx = temp;
++                              }
++                              temp = HFC_inb_(hc, R_JATT_DIR);
++                              switch(!hc->chan[16].sync) {
++                                      case 0:
++                                      if ((temp&0x60) == 0x60) {
++                                              if (debug & DEBUG_HFCMULTI_SYNC)
++                                                      printk(KERN_DEBUG "%s: E1 now in clock sync\n", __FUNCTION__);
++                                              HFC_outb(hc, R_RX_OFF, hc->chan[16].jitter | V_RX_INIT);
++                                              HFC_outb(hc, R_TX_OFF, hc->chan[16].jitter | V_RX_INIT);
++                                              hc->chan[16].sync = 1;
++                                              goto check_framesync;
++                                      }
++                                      break;
++
++                                      case 1:
++                                      if ((temp&0x60) != 0x60) {
++                                              if (debug & DEBUG_HFCMULTI_SYNC)
++                                                      printk(KERN_DEBUG "%s: E1 lost clock sync\n", __FUNCTION__);
++                                              hc->chan[16].sync = 0;
++                                              break;
++                                      }
++                                      check_framesync:
++                                      temp = HFC_inb_(hc, R_RX_STA0);
++                                      if (temp == 0x27) {
++                                              if (debug & DEBUG_HFCMULTI_SYNC)
++                                                      printk(KERN_DEBUG "%s: E1 now in frame sync\n", __FUNCTION__);
++                                              hc->chan[16].sync = 2;
++                                      }
++                                      break;
++
++                                      case 2:
++                                      if ((temp&0x60) != 0x60) {
++                                              if (debug & DEBUG_HFCMULTI_SYNC)
++                                                      printk(KERN_DEBUG "%s: E1 lost clock & frame sync\n", __FUNCTION__);
++                                              hc->chan[16].sync = 0;
++                                              break;
++                                      }
++                                      temp = HFC_inb_(hc, R_RX_STA0);
++                                      if (temp != 0x27) {
++                                              if (debug & DEBUG_HFCMULTI_SYNC)
++                                                      printk(KERN_DEBUG "%s: E1 lost frame sync\n", __FUNCTION__);
++                                              hc->chan[16].sync = 1;
++                                      }
++                                      break;
++                              }
++                      }
++                      if (hc->leds)
++                              hfcmulti_leds(hc);
++              }
++              if (r_irq_misc & V_DTMF_IRQ) {
++                      /* -> DTMF IRQ */
++                      hfcmulti_dtmf(hc);
++              }
++      }
++      if (status & V_FR_IRQSTA) {
++              /* FIFO IRQ */
++              r_irq_oview = HFC_inb_(hc, R_IRQ_OVIEW);
++              i = 0;
++              while(i < 8) {
++                      if (r_irq_oview & (1 << i)) {
++                              r_irq_fifo_bl = HFC_inb_(hc, R_IRQ_FIFO_BL0 + i);
++                              j = 0;
++                              while(j < 8) {
++                                      ch = (i<<2) + (j>>1);
++                                      if (r_irq_fifo_bl & (1 << j)) {
++                                              if ((dch = hc->chan[ch].dch))
++                                              if (hc->created[hc->chan[ch].port]) {
++                                                      hfcmulti_tx(hc, ch, dch, NULL);
++                                                      /* start fifo */
++                                                      HFC_outb_(hc, R_FIFO, 0);
++                                                      HFC_wait_(hc);
++                                              }
++                                              if ((bch = hc->chan[ch].bch))
++                                              if (hc->created[hc->chan[ch].port]) {
++                                                      if (bch->protocol != ISDN_PID_NONE) {
++                                                              hfcmulti_tx(hc, ch, NULL, bch);
++                                                              /* start fifo */
++                                                              HFC_outb_(hc, R_FIFO, 0);
++                                                              HFC_wait_(hc);
++                                                      }
++                                              }
++                                      }
++                                      j++;
++                                      if (r_irq_fifo_bl & (1 << j)) {
++                                              if ((dch = hc->chan[ch].dch))
++                                              if (hc->created[hc->chan[ch].port]) {
++                                                      hfcmulti_rx(hc, ch, dch, NULL);
++                                              }
++                                              if ((bch = hc->chan[ch].bch))
++                                              if (hc->created[hc->chan[ch].port]) {
++                                                      if (bch->protocol != ISDN_PID_NONE) {
++                                                              hfcmulti_rx(hc, ch, NULL, bch);
++                                                      }
++                                              }
++                                      }
++                                      j++;
++                              }
++                      }
++                      i++;
++              }
++      }
++
++      spin_lock_irqsave(&hc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      hc->lock.spin_adr = (void *)0x3002;
++#endif
++      if (!test_and_clear_bit(STATE_FLAG_INIRQ, &hc->lock.state)) {
++      }
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &hc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      __FUNCTION__, hc->lock.state);
++      }
++#ifdef SPIN_DEBUG
++      hc->lock.busy_adr = NULL;
++      hc->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&hc->lock.lock, flags);
++      return IRQ_HANDLED;
++}
++
++
++/********************************************************************/
++/* timer callback for D-chan busy resolution. Currently no function */
++/********************************************************************/
++
++static void
++hfcmulti_dbusy_timer(hfc_multi_t *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(hfc_multi_t *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 = hc->chan[ch].slot_tx;
++      int oslot_rx = hc->chan[ch].slot_rx;
++      int conf = hc->chan[ch].conf;
++
++      if (debug & DEBUG_HFCMULTI_MODE)
++              printk(KERN_DEBUG "%s: channel %d protocol %x slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, ch, protocol, slot_tx, bank_tx, 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", __FUNCTION__, 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 slot anymore, channel %d is.\n", __FUNCTION__, 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", __FUNCTION__, 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 slot anymore, channel %d is.\n", __FUNCTION__, 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 to slot %d bank %d flow %02x routing %02x conf %d (TX)\n", __FUNCTION__, 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 to slot %d bank %d flow %02x routing %02x conf %d (RX)\n", __FUNCTION__, 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_PID_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->type == 1) {
++              } else if ((ch&0x3)<2) {
++                      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);
++                      HFC_outb(hc, A_ST_CTRL0,  hc->hw.a_st_ctrl0[hc->chan[ch].port]);
++              }
++              break;
++
++              case (ISDN_PID_L1_B_64TRANS): /* B-channel */
++              /* 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);
++              HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx 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);
++                      HFC_outb(hc, A_ST_CTRL0,  hc->hw.a_st_ctrl0[hc->chan[ch].port]);
++              }
++              break;
++
++              case (ISDN_PID_L1_B_64HDLC): /* B-channel */
++              case (ISDN_PID_L1_TE_E1): /* D-channel E1 */
++              case (ISDN_PID_L1_NT_E1):
++              /* enable TX fifo */
++              HFC_outb(hc, R_FIFO, ch<<1);
++              HFC_wait(hc);
++              HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04);
++              HFC_outb(hc, A_SUBCH_CFG, 0);
++              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);
++              HFC_outb(hc, A_SUBCH_CFG, 0);
++              HFC_outb(hc, A_IRQ_MSK, V_IRQ);
++              HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
++              HFC_wait(hc);
++              if (hc->type == 1) {
++              } else {
++                      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);
++                      HFC_outb(hc, A_ST_CTRL0,  hc->hw.a_st_ctrl0[hc->chan[ch].port]);
++              }
++              break;
++
++              case (ISDN_PID_L1_TE_S0): /* D-channel S0 */
++              case (ISDN_PID_L1_NT_S0):
++              /* enable TX fifo */
++              HFC_outb(hc, R_FIFO, ch<<1);
++              HFC_wait(hc);
++              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);
++              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);
++              break;
++
++              default:
++              printk(KERN_DEBUG "%s: protocol not known %x\n", __FUNCTION__, protocol);
++              hc->chan[ch].protocol = ISDN_PID_NONE;
++              return(-ENOPROTOOPT);
++      }
++      hc->chan[ch].protocol = protocol;
++      return(0);
++}
++
++
++/**************************/
++/* connect/disconnect PCM */
++/**************************/
++
++static void
++hfcmulti_pcm(hfc_multi_t *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(hfc_multi_t *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
++static void
++hfcmulti_splloop(hfc_multi_t *hc, int ch, u_char *data, int len)
++{
++      u_char *d, *dd;
++      bchannel_t *bch = hc->chan[ch].bch;
++
++      /* flush pending TX data */
++      if (bch->next_skb) {
++              test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag); 
++              dev_kfree_skb(bch->next_skb);
++              bch->next_skb = NULL;
++      }
++      bch->tx_idx = bch->tx_len = bch->rx_idx = 0;
++
++      /* prevent overflow */
++      if (len > hc->Zlen-1)
++              len = hc->Zlen-1;
++
++      /* select fifo */
++      HFC_outb_(hc, R_FIFO, ch<<1);
++      HFC_wait_(hc);
++
++      /* reset fifo */
++      HFC_outb(hc, A_SUBCH_CFG, 0);
++      udelay(500);
++      HFC_outb_(hc, R_INC_RES_FIFO, V_RES_F);
++      HFC_wait_(hc);
++      udelay(500);
++
++      /* if off */
++      if (len <= 0) {
++              HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence);
++              if (hc->chan[ch].slot_tx>=0) {
++                      if (debug & DEBUG_HFCMULTI_MODE)
++                              printk(KERN_DEBUG "%s: connecting PCM due to no more TONE: channel %d slot_tx %d\n", __FUNCTION__, ch, hc->chan[ch].slot_tx);
++                      /* connect slot */
++                      HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF);
++                      HFC_outb(hc, R_FIFO, ch<<1 | 1);
++                      HFC_wait(hc);
++                      HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | V_HDLC_TRP | V_IFF);
++              }
++              hc->chan[ch].txpending = 0;
++              return;
++      }
++
++      /* loop fifo */
++
++      /* set mode */
++      hc->chan[ch].txpending = 2;
++
++//printk("len=%d %02x %02x %02x\n", len, data[0], data[1], data[2]);
++      /* write loop data */
++      d = data;
++#ifdef FIFO_32BIT_ACCESS
++#ifndef CONFIG_HFCMULTI_PCIMEM
++      HFC_set(hc, A_FIFO_DATA0);
++#endif
++      dd = d + (len & 0xfffc);
++      while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++              HFC_outl_(hc, A_FIFO_DATA0, *((unsigned long *)d));
++#else
++              HFC_putl(hc, *((unsigned long *)d));
++#endif
++              d+=4;
++              
++      }
++      dd = d + (len & 0x0003);
++#else
++      dd = d + len;
++#endif
++      while(d != dd) {
++#ifdef CONFIG_HFCMULTI_PCIMEM
++              HFC_outb_(hc, A_FIFO_DATA0, *d);
++#else
++              HFC_putb(hc, *d);
++#endif
++              d++;
++      }
++
++      udelay(500);
++      HFC_outb(hc, A_SUBCH_CFG, V_LOOP_FIFO);
++      udelay(500);
++
++      /* disconnect slot */
++      if (hc->chan[ch].slot_tx>=0) {
++              if (debug & DEBUG_HFCMULTI_MODE)
++                      printk(KERN_DEBUG "%s: disconnecting PCM due to TONE: channel %d slot_tx %d\n", __FUNCTION__, ch, hc->chan[ch].slot_tx);
++              HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
++              HFC_outb(hc, R_FIFO, ch<<1 | 1);
++              HFC_wait(hc);
++              HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
++              HFC_outb(hc, R_FIFO, ch<<1);
++              HFC_wait(hc);
++      } else {
++              /* change fifo */
++              HFC_outb(hc, R_FIFO, ch<<1);
++              HFC_wait(hc);
++      }
++
++//udelay(300);
++}
++
++
++/*************************************/
++/* Layer 1 D-channel hardware access */
++/*************************************/
++
++/* message transfer from layer 1 to hardware.
++ */
++static int
++hfcmulti_l1hw(mISDNif_t *hif, struct sk_buff *skb)
++{
++      dchannel_t      *dch;
++      int             slot_tx, slot_rx, bank_tx, bank_rx;
++      hfc_multi_t     *hc;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      dch = hif->fdata;
++      hc = dch->inst.data;
++      ret = 0;
++      if (hh->prim == PH_DATA_REQ) {
++              /* check oversize */
++              if (skb->len == 0) {
++                      printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__);
++                      return(-EINVAL);
++              }
++              if (skb->len > MAX_DFRAME_LEN_L1) {
++                      printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__);
++                      return(-EINVAL);
++              }
++              /* check for pending next_skb */
++              if (dch->next_skb) {
++                      printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, dch->next_skb->len);
++                      return(-EBUSY);
++              }
++              /* if we have currently a pending tx skb */
++              dch->inst.lock(hc, 0);
++              if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) {
++                      test_and_set_bit(FLG_TX_NEXT, &dch->DFlags);
++                      dch->next_skb = skb;
++                      dch->inst.unlock(hc);
++                      return(0);
++              }
++              /* write to fifo */
++              dch->tx_len = skb->len;
++              memcpy(dch->tx_buf, skb->data, dch->tx_len);
++//            if (debug & DEBUG_HFCMULTI_FIFO) {
++//                    printk(KERN_DEBUG "%s:from user space:\n", __FUNCTION__);
++//                    i = 0;
++//                    while(i < dch->tx_len)
++//                            printk(" %02x", dch->tx_buf[i++]);
++//                    printk("\n");
++//            }
++              dch->tx_idx = 0;
++              hfcmulti_tx(hc, dch->channel, dch, NULL);
++              /* start fifo */
++              HFC_outb(hc, R_FIFO, 0);
++              HFC_wait(hc);
++              dch->inst.unlock(hc);
++              skb_trim(skb, 0);
++              return(if_newhead(&dch->inst.up, PH_DATA_CNF,
++                      hh->dinfo, skb));
++      } else 
++      if (hh->prim == (PH_SIGNAL | REQUEST)) {
++              dch->inst.lock(hc, 0);
++              switch (hh->dinfo) {
++#if 0
++                      case INFO3_P8:
++                      case INFO3_P10: 
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: INFO3_P%d\n", __FUNCTION__, (hh->dinfo==INFO3_P8)?8:10);
++                      if (test_bit(HFC_CHIP_MASTER, &hc->chip))
++                              hc->hw.mst_m |= HFCPCI_MASTER;
++                      Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
++                      break;
++#endif
++                      default:
++                      printk(KERN_DEBUG "%s: unknown PH_SIGNAL info %x\n", __FUNCTION__, hh->dinfo);
++                      ret = -EINVAL;
++              }
++              dch->inst.unlock(hc);
++      } else 
++      if (hh->prim == (PH_CONTROL | REQUEST)) {
++              dch->inst.lock(hc, 0);
++              switch (hh->dinfo) {
++                      case HW_RESET:
++                      /* start activation */
++                      if (hc->type == 1) {
++                              HFC_outb(hc, R_E1_WR_STA, V_E1_LD_STA | 1);
++                              udelay(6); /* wait at least 5,21us */
++                              HFC_outb(hc, R_E1_WR_STA, 1);
++                      } else {
++                              HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port);
++                              HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* G1 */
++                              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 */
++                      }
++                      break;
++
++                      case HW_DEACTIVATE:
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_DEACTIVATE\n", __FUNCTION__);
++                      goto hw_deactivate; /* after lock */
++
++                      /* connect interface to pcm timeslot (0..N) */
++                      case HW_PCM_CONN:
++                      if (skb->len < 4*sizeof(u_long)) {
++                              printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__);
++                              break;
++                      }
++                      slot_tx = ((int *)skb->data)[0];
++                      bank_tx = ((int *)skb->data)[1];
++                      slot_rx = ((int *)skb->data)[2];
++                      bank_rx = ((int *)skb->data)[3];
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, 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, dch->channel, slot_tx, bank_tx, slot_rx, bank_rx);
++                      else
++                              printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx);
++                      break;
++
++                      /* release interface from pcm timeslot */
++                      case HW_PCM_DISC:
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__);
++                      hfcmulti_pcm(hc, dch->channel, -1, -1, -1, -1); 
++                      break;
++
++                      default:
++                      printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo);
++                      ret = -EINVAL;
++              }
++              dch->inst.unlock(hc);
++      } else
++      if (hh->prim == (PH_ACTIVATE | REQUEST)) {
++              if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) {
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: PH_ACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
++                      dch->inst.lock(hc, 0);
++                      /* start activation */
++                      if (hc->type == 1) {
++                              HFC_outb(hc, R_E1_WR_STA, V_E1_LD_STA | 1);
++                              udelay(6); /* wait at least 5,21us */
++                              HFC_outb(hc, R_E1_WR_STA, 1);
++                      } else {
++                              HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port);
++                              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->ph_state = 1;
++                      dch->inst.unlock(hc);
++              } else {
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: PH_ACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
++                      ret = -EINVAL;
++              }
++      } else 
++      if (hh->prim == (PH_DEACTIVATE | REQUEST)) {
++              if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) {
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: PH_DEACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
++                      dch->inst.lock(hc, 0);
++                      hw_deactivate: /* after lock */
++                      dch->ph_state = 0;
++                      /* start deactivation */
++                      if (hc->type == 1) {
++                      } else {
++                              HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port);
++                              HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); /* deactivate */
++                      }
++                      discard_queue(&dch->rqueue);
++                      if (dch->next_skb) {
++                              dev_kfree_skb(dch->next_skb);
++                              dch->next_skb = NULL;
++                      }
++                      dch->tx_idx = dch->tx_len = hc->chan[dch->channel].rx_idx = 0;
++                      test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags);
++                      test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++                      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++                              del_timer(&dch->dbusytimer);
++                      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++                              dchannel_sched_event(dch, D_CLEARBUSY);
++                      dch->inst.unlock(hc);
++              } else {
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: PH_DEACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
++                      ret = -EINVAL;
++              }
++      } else {
++              if (debug & DEBUG_HFCMULTI_MSG)
++                      printk(KERN_DEBUG "%s: unknown prim %x\n", __FUNCTION__, hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret) {
++//            printk("1\n");
++              dev_kfree_skb(skb);
++//            printk("2\n");
++      }
++      return(ret);
++}
++
++
++/******************************/
++/* Layer2 -> Layer 1 Transfer */
++/******************************/
++
++/* messages from layer 2 to layer 1 are processed here.
++ */
++static int
++hfcmulti_l2l1(mISDNif_t *hif, struct sk_buff *skb)
++{
++      u_long          num;
++      int             slot_tx, slot_rx, bank_tx, bank_rx;
++      bchannel_t      *bch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++      hfc_multi_t     *hc;
++      struct          dsp_features *features;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      bch = hif->fdata;
++      hc = bch->inst.data;
++
++      if ((hh->prim == PH_DATA_REQ)
++       || (hh->prim == (DL_DATA | REQUEST))) {
++              if (skb->len == 0) {
++                      printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__);
++                      return(-EINVAL);
++              }
++              if (skb->len > MAX_DATA_MEM) {
++                      printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__);
++                      return(-EINVAL);
++              }
++              /* check for pending next_skb */
++              if (bch->next_skb) {
++                      printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n", __FUNCTION__, skb->len, bch->next_skb->len);
++                      return(-EBUSY);
++              }
++              /* if we have currently a pending tx skb */
++              bch->inst.lock(hc, 0);
++              if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) {
++                      test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag);
++                      bch->next_skb = skb;
++                      bch->inst.unlock(hc);
++                      return(0);
++              }
++              /* write to fifo */
++              bch->tx_len = skb->len;
++              memcpy(bch->tx_buf, skb->data, bch->tx_len);
++              bch->tx_idx = 0;
++              hfcmulti_tx(hc, bch->channel, NULL, bch);
++              /* start fifo */
++              HFC_outb_(hc, R_FIFO, 0);
++              HFC_wait_(hc);
++              bch->inst.unlock(hc);
++              if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                      && bch->dev)
++                      hif = &bch->dev->rport.pif;
++              else
++                      hif = &bch->inst.up;
++              skb_trim(skb, 0);
++              return(if_newhead(hif, hh->prim | CONFIRM,
++                      hh->dinfo, skb));
++      } else 
++      if ((hh->prim == (PH_ACTIVATE | REQUEST))
++       || (hh->prim == (DL_ESTABLISH  | REQUEST))) {
++              /* activate B-channel if not already activated */
++              if (debug & DEBUG_HFCMULTI_MSG)
++                      printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel);
++              if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
++                      ret = 0;
++              else {
++                      bch->inst.lock(hc, 0);
++                      ret = mode_hfcmulti(hc, bch->channel, bch->inst.pid.protocol[1], hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx);
++                      if (!ret) {
++                              bch->protocol = bch->inst.pid.protocol[1];
++                              if (bch->protocol)
++                                      bch_sched_event(bch, B_XMTBUFREADY);
++                              if (bch->protocol==ISDN_PID_L1_B_64TRANS && !hc->dtmf) {
++                                      /* start decoder */
++                                      hc->dtmf = 1;
++                                      if (debug & DEBUG_HFCMULTI_DTMF)
++                                              printk(KERN_DEBUG "%s: start dtmf decoder\n", __FUNCTION__);
++                                      HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
++                              }
++                      }
++                      bch->inst.unlock(hc);
++              }
++              if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                      if (bch->dev)
++                              if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0);
++              skb_trim(skb, 0);
++              return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
++      } else 
++      if ((hh->prim == (PH_DEACTIVATE | REQUEST))
++       || (hh->prim == (DL_RELEASE | REQUEST))
++       || (hh->prim == (MGR_DISCONNECT | REQUEST))) {
++              if (debug & DEBUG_HFCMULTI_MSG)
++                      printk(KERN_DEBUG "%s: PH_DEACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel);
++              /* deactivate B-channel if not already deactivated */
++              bch->inst.lock(hc, 0);
++              if (bch->next_skb) {
++                      test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag); 
++                      dev_kfree_skb(bch->next_skb);
++                      bch->next_skb = NULL;
++              }
++              bch->tx_idx = bch->tx_len = bch->rx_idx = 0;
++              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              hc->chan[bch->channel].slot_tx = -1;
++              hc->chan[bch->channel].slot_rx = -1;
++              hc->chan[bch->channel].conf = -1;
++              mode_hfcmulti(hc, bch->channel, ISDN_PID_NONE, hc->chan[bch->channel].slot_tx, hc->chan[bch->channel].bank_tx, hc->chan[bch->channel].slot_rx, hc->chan[bch->channel].bank_rx);
++              bch->protocol = ISDN_PID_NONE;
++              test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
++              bch->inst.unlock(hc);
++              skb_trim(skb, 0);
++//printk("5\n");
++              if (hh->prim != (MGR_DISCONNECT | REQUEST)) {
++                      if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                              if (bch->dev)
++                                      if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0);
++                      if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
++                              return(0);
++//printk("6\n");
++              }
++//printk("7\n");
++              ret = 0;
++      } else
++      if (hh->prim == (PH_CONTROL | REQUEST)) {
++              bch->inst.lock(hc, 0);
++              switch (hh->dinfo) {
++                      /* fill features structure */
++                      case HW_FEATURES:
++                      if (skb->len != sizeof(void *)) {
++                              printk(KERN_WARNING "%s: HW_FEATURES lacks parameters\n", __FUNCTION__);
++                              break;
++                      }
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_FEATURE request\n", __FUNCTION__);
++                      features = *((struct dsp_features **)skb->data);
++                      features->hfc_id = hc->id;
++                      if (test_bit(HFC_CHIP_DTMF, &hc->chip))
++                              features->hfc_dtmf = 1;
++                      features->hfc_loops = 0;
++                      features->pcm_id = hc->pcm;
++                      features->pcm_slots = hc->slots;
++                      features->pcm_banks = 2;
++                      ret = 0;
++                      break;
++
++                      /* connect interface to pcm timeslot (0..N) */
++                      case HW_PCM_CONN:
++                      if (skb->len < 4*sizeof(u_long)) {
++                              printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__);
++                              break;
++                      }
++                      slot_tx = ((int *)skb->data)[0];
++                      bank_tx = ((int *)skb->data)[1];
++                      slot_rx = ((int *)skb->data)[2];
++                      bank_rx = ((int *)skb->data)[3];
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX)\n", __FUNCTION__, 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->channel, slot_tx, bank_tx, slot_rx, bank_rx);
++                      else
++                              printk(KERN_WARNING "%s: HW_PCM_CONN slot %d bank %d (TX) slot %d bank %d (RX) out of range\n", __FUNCTION__, slot_tx, bank_tx, slot_rx, bank_rx);
++                      ret = 0;
++                      break;
++
++                      /* release interface from pcm timeslot */
++                      case HW_PCM_DISC:
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__);
++                      hfcmulti_pcm(hc, bch->channel, -1, -1, -1, -1); 
++                      ret = 0;
++                      break;
++
++                      /* join conference (0..7) */
++                      case HW_CONF_JOIN:
++                      if (skb->len < sizeof(u_long)) {
++                              printk(KERN_WARNING "%s: HW_CONF_JOIN lacks parameters\n", __FUNCTION__);
++                              break;
++                      }
++                      num = ((u_long *)skb->data)[0];
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_CONF_JOIN conf %ld\n", __FUNCTION__, num);
++                      if (num <= 7) {
++                              hfcmulti_conf(hc, bch->channel, num); 
++                              ret = 0;
++                      } else
++                              printk(KERN_WARNING "%s: HW_CONF_JOIN conf %ld out of range\n", __FUNCTION__, num);
++                      break;
++
++                      /* split conference */
++                      case HW_CONF_SPLIT:
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_CONF_SPLIT\n", __FUNCTION__);
++                      hfcmulti_conf(hc, bch->channel, -1); 
++                      ret = 0;
++                      break;
++
++                      /* set sample loop */
++                      case HW_SPL_LOOP_ON:
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_SPL_LOOP_ON (len = %d)\n", __FUNCTION__, skb->len);
++                      hfcmulti_splloop(hc, bch->channel, skb->data, skb->len);
++                      ret = 0;
++                      break;
++
++                      /* set silence */
++                      case HW_SPL_LOOP_OFF:
++                      if (debug & DEBUG_HFCMULTI_MSG)
++                              printk(KERN_DEBUG "%s: HW_SPL_LOOP_OFF\n", __FUNCTION__);
++                      hfcmulti_splloop(hc, bch->channel, NULL, 0);
++                      ret = 0;
++                      break;
++
++                      default:
++                      printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo);
++                      ret = -EINVAL;
++              }
++              bch->inst.unlock(hc);
++      } else {
++              printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret) {
++//            printk("3\n");
++              dev_kfree_skb(skb);
++//            printk("4\n");
++      }
++      return(ret);
++}
++
++
++/***************************/
++/* handle D-channel events */
++/***************************/
++
++/* handle state change event
++ */
++static void
++hfcmulti_dch_bh(dchannel_t *dch)
++{
++      hfc_multi_t *hc = dch->inst.data;
++      u_int prim = PH_SIGNAL | INDICATION;
++      u_int para = 0;
++      mISDNif_t *upif = &dch->inst.up;
++      mISDN_head_t *hh;
++      int ch;
++      struct sk_buff *skb;
++
++      if (!dch) {
++              printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __FUNCTION__);
++              return;
++      }
++      ch = dch->channel;
++
++      /* Loss Of Signal */
++      if (test_and_clear_bit(D_LOS, &dch->event)) {
++              if (debug & DEBUG_HFCMULTI_STATE)
++                      printk(KERN_DEBUG "%s: LOS detected\n", __FUNCTION__);
++              skb = create_link_skb(PH_CONTROL | INDICATION, HW_LOS, 0, NULL, 0);
++              if (skb) {
++                      hh = mISDN_HEAD_P(skb);
++                      if (if_newhead(upif, hh->prim, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++      /* Loss Of Signal OFF */
++      if (test_and_clear_bit(D_LOS_OFF, &dch->event)) {
++              if (debug & DEBUG_HFCMULTI_STATE)
++                      printk(KERN_DEBUG "%s: LOS gone\n", __FUNCTION__);
++              skb = create_link_skb(PH_CONTROL | INDICATION, HW_LOS_OFF, 0, NULL, 0);
++              if (skb) {
++                      hh = mISDN_HEAD_P(skb);
++                      if (if_newhead(upif, hh->prim, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++      /* Alarm Indication Signal */
++      if (test_and_clear_bit(D_AIS, &dch->event)) {
++              if (debug & DEBUG_HFCMULTI_STATE)
++                      printk(KERN_DEBUG "%s: AIS detected\n", __FUNCTION__);
++              skb = create_link_skb(PH_CONTROL | INDICATION, HW_AIS, 0, NULL, 0);
++              if (skb) {
++                      hh = mISDN_HEAD_P(skb);
++                      if (if_newhead(upif, hh->prim, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++      /* Alarm Indication Signal OFF */
++      if (test_and_clear_bit(D_AIS_OFF, &dch->event)) {
++              if (debug & DEBUG_HFCMULTI_STATE)
++                      printk(KERN_DEBUG "%s: AIS gone\n", __FUNCTION__);
++              skb = create_link_skb(PH_CONTROL | INDICATION, HW_AIS_OFF, 0, NULL, 0);
++              if (skb) {
++                      hh = mISDN_HEAD_P(skb);
++                      if (if_newhead(upif, hh->prim, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++      /* SLIP detected */
++      if (test_and_clear_bit(D_SLIP_TX, &dch->event)) {
++              if (debug & DEBUG_HFCMULTI_STATE)
++                      printk(KERN_DEBUG "%s: bit SLIP detected TX\n", __FUNCTION__);
++              skb = create_link_skb(PH_CONTROL | INDICATION, HW_SLIP_TX, 0, NULL, 0);
++              if (skb) {
++                      hh = mISDN_HEAD_P(skb);
++                      if (if_newhead(upif, hh->prim, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++      if (test_and_clear_bit(D_SLIP_RX, &dch->event)) {
++              if (debug & DEBUG_HFCMULTI_STATE)
++                      printk(KERN_DEBUG "%s: bit SLIP detected RX\n", __FUNCTION__);
++              skb = create_link_skb(PH_CONTROL | INDICATION, HW_SLIP_RX, 0, NULL, 0);
++              if (skb) {
++                      hh = mISDN_HEAD_P(skb);
++                      if (if_newhead(upif, hh->prim, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++
++      if (test_and_clear_bit(D_L1STATECHANGE, &dch->event)) {
++              if (hc->type == 1) {
++                      if (!test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) {
++                              if (debug & DEBUG_HFCMULTI_STATE)
++                                      printk(KERN_DEBUG "%s: E1 TE newstate %x\n", __FUNCTION__, dch->ph_state);
++                      } else { 
++                              if (debug & DEBUG_HFCMULTI_STATE)
++                                      printk(KERN_DEBUG "%s: E1 NT newstate %x\n", __FUNCTION__, dch->ph_state);
++                      }
++                      switch (dch->ph_state) {
++                              case (1):
++                              prim = PH_ACTIVATE | INDICATION;
++                              para = 0;
++                              break;
++
++                              default:
++                              if (hc->chan[ch].e1_state != 1)
++                                      return;
++                              prim = PH_DEACTIVATE | INDICATION;
++                              para = 0;
++                      }
++                      hc->chan[ch].e1_state = dch->ph_state;
++              } else {
++                      if (!test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) {
++                              if (debug & DEBUG_HFCMULTI_STATE)
++                                      printk(KERN_DEBUG "%s: S/T TE newstate %x\n", __FUNCTION__, dch->ph_state);
++                              switch (dch->ph_state) {
++                                      case (0):
++                                      prim = PH_CONTROL | INDICATION;
++                                      para = HW_RESET;
++                                      break;
++
++                                      case (3):
++                                      prim = PH_CONTROL | INDICATION;
++                                      para = HW_DEACTIVATE;
++                                      break;
++
++                                      case (5):
++                                      case (8):
++                                      para = ANYSIGNAL;
++                                      break;
++
++                                      case (6):
++                                      para = INFO2;
++                                      break;
++
++                                      case (7):
++                                      para = INFO4_P8;
++                                      break;
++
++                                      default:
++                                      return;
++                              }
++                      } else { 
++                              if (debug & DEBUG_HFCMULTI_STATE)
++                                      printk(KERN_DEBUG "%s: S/T NT newstate %x\n", __FUNCTION__, dch->ph_state);
++                              dch->inst.lock(hc, 0);
++                              switch (dch->ph_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);
++                                              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->ph_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);
++                                              HFC_outb(hc, A_ST_WR_STATE, 2 | V_SET_G2_G3); /* allow G2 -> G3 transition */
++                                      }
++                                      upif = NULL;
++                                      break;
++
++                                      case (1):
++                                      prim = PH_DEACTIVATE | INDICATION;
++                                      para = 0;
++                                      hc->chan[ch].nt_timer = -1;
++                                      break;
++
++                                      case (4):
++                                      hc->chan[ch].nt_timer = -1;
++                                      upif = NULL;
++                                      break;
++
++                                      case (3):
++                                      prim = PH_ACTIVATE | INDICATION;
++                                      para = 0;
++                                      hc->chan[ch].nt_timer = -1;
++                                      break;
++
++                                      default:
++                                      break;
++                              }
++                              dch->inst.unlock(hc);
++                      }
++              }
++              /* transmit new state to upper layer if available */
++              if (hc->created[hc->chan[ch].port]) {
++                      while(upif) {
++                              if_link(upif, prim, para, 0, NULL, 0);
++                              upif = upif->clone;
++                      }
++              }
++      }
++}
++
++/***************************/
++/* handle D-channel events */
++/***************************/
++
++/* handle DTMF event
++ */
++static void
++hfcmulti_bch_bh(bchannel_t *bch)
++{
++      hfc_multi_t     *hc = bch->inst.data;
++      struct sk_buff  *skb;
++        mISDN_head_t    *hh;
++        mISDNif_t       *hif = 0; /* make gcc happy */
++      u_long          *coeff;
++
++      if (!bch) {
++              printk(KERN_WARNING "%s: ERROR given bch is NULL\n", __FUNCTION__);
++              return;
++      }
++
++      /* DTMF event */
++      if (test_and_clear_bit(B_DTMFREADY, &bch->event)) {
++              while ((skb = skb_dequeue(&hc->chan[bch->channel].dtmfque))) {
++                      if (debug & DEBUG_HFCMULTI_DTMF) {
++                              coeff = (u_long *)skb->data;
++                              printk("%s: DTMF ready %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx len=%d\n", __FUNCTION__,
++                              coeff[0], coeff[1], coeff[2], coeff[3], coeff[4], coeff[5], coeff[6], coeff[7], skb->len);
++                      }
++                      hh = mISDN_HEAD_P(skb);
++                      if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) && bch->dev)
++                              hif = &bch->dev->rport.pif;
++                      else
++                              hif = &bch->inst.up;
++                      if (if_newhead(hif, hh->prim, hh->dinfo, skb))
++                              dev_kfree_skb(skb);
++              }
++      }
++
++}
++
++
++/*************************************/
++/* called for card mode init message */
++/*************************************/
++
++static void
++hfcmulti_initmode(hfc_multi_t *hc)
++{
++      int nt_mode;
++      unsigned char r_sci_msk, a_st_wr_state, r_e1_wr_sta;
++      int i, port;
++      dchannel_t *dch;
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk("%s: entered\n", __FUNCTION__);
++
++      lock_dev(hc, 0);
++
++      if (hc->type == 1) {
++              nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[16].cfg);
++              hc->chan[16].slot_tx = -1;
++              hc->chan[16].slot_rx = -1;
++              hc->chan[16].conf = -1;
++              mode_hfcmulti(hc, 16, nt_mode?ISDN_PID_L1_NT_E1:ISDN_PID_L1_TE_E1, -1, 0, -1, 0);
++              hc->chan[16].dch->hw_bh = hfcmulti_dch_bh;
++              hc->chan[16].dch->dbusytimer.function = (void *) hfcmulti_dbusy_timer;
++              hc->chan[16].dch->dbusytimer.data = (long) &hc->chan[16].dch;
++              init_timer(&hc->chan[16].dch->dbusytimer);
++
++              i = 0;
++              while (i < 30) {
++                      hc->chan[i+1+(i>=15)].slot_tx = -1;
++                      hc->chan[i+1+(i>=15)].slot_rx = -1;
++                      hc->chan[i+1+(i>=15)].conf = -1;
++                      hc->chan[i+1+(i>=15)].bch->hw_bh = hfcmulti_bch_bh;
++                      mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0, -1, 0);
++                      hc->chan[i+1+(i>=15)].bch->protocol = ISDN_PID_NONE;
++                      i++;
++              }
++      } else {
++              i = 0;
++              while (i < hc->type) {
++                      nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg);
++                      hc->chan[(i<<2)+2].slot_tx = -1;
++                      hc->chan[(i<<2)+2].slot_rx = -1;
++                      hc->chan[(i<<2)+2].conf = -1;
++                      mode_hfcmulti(hc, (i<<2)+2, nt_mode?ISDN_PID_L1_NT_S0:ISDN_PID_L1_TE_S0, -1, 0, -1, 0);
++                      hc->chan[(i<<2)+2].dch->hw_bh = hfcmulti_dch_bh;
++                      hc->chan[(i<<2)+2].dch->dbusytimer.function = (void *) hfcmulti_dbusy_timer;
++                      hc->chan[(i<<2)+2].dch->dbusytimer.data = (long) &hc->chan[(i<<2)+2].dch;
++                      init_timer(&hc->chan[(i<<2)+2].dch->dbusytimer);
++      
++                      hc->chan[i<<2].bch->hw_bh = hfcmulti_bch_bh;
++                      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_PID_NONE, -1, 0, -1, 0);
++                      hc->chan[i<<2].bch->protocol = ISDN_PID_NONE;
++                      hc->chan[(i<<2)+1].bch->hw_bh = hfcmulti_bch_bh;
++                      hc->chan[(i<<2)+1].slot_tx = -1;
++                      hc->chan[(i<<2)+1].slot_rx = -1;
++                      hc->chan[(i<<2)+1].conf = -1;
++                      mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0, -1, 0);
++                      hc->chan[(i<<2)+1].bch->protocol = ISDN_PID_NONE;
++                      i++;
++              }
++      }
++
++      /* set up interface */
++      if (hc->type != 1) {
++              /* ST */
++              r_sci_msk = 0;
++              i = 0;
++              while(i < 32) {
++                      if (!(dch = hc->chan[i].dch)) {
++                              i++;
++                              continue;
++                      }
++                      port = hc->chan[i].port;
++                      /* select interface */
++                      HFC_outb(hc, R_ST_SEL, port);
++                      if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg)) {
++                              if (debug & DEBUG_HFCMULTI_INIT)
++                                      printk(KERN_DEBUG "%s: ST port %d is NT-mode\n", __FUNCTION__, port);
++                              /* clock delay */
++                              HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_NT | 0x60);
++                              a_st_wr_state = 1; /* G1 */
++                              hc->hw.a_st_ctrl0[port] = V_ST_MD;
++                      } else {
++                              if (debug & DEBUG_HFCMULTI_INIT)
++                                      printk(KERN_DEBUG "%s: ST port %d is TE-mode\n", __FUNCTION__, port);
++                              /* clock delay */
++                              HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_TE);
++                              a_st_wr_state = 2; /* F2 */
++                              hc->hw.a_st_ctrl0[port] = 0;
++                      }
++                      if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) {
++                              hc->hw.a_st_ctrl0[port] |= V_TX_LI;
++                      }
++                      /* line setup */
++                      HFC_outb(hc, A_ST_CTRL0,  hc->hw.a_st_ctrl0[port]);
++                      /* disable E-channel */
++                      if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg)
++                       || test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg))
++                              HFC_outb(hc, A_ST_CTRL1, V_E_IGNO);
++                      /* 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);
++                      r_sci_msk |= 1 << port;
++                      i++;
++              }
++              /* state machine interrupts */
++              HFC_outb(hc, R_SCI_MSK, r_sci_msk);
++      } else {
++              /* E1 */
++              if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].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[16].cfg)) {
++                      HFC_outb(hc, R_RX0, 0);
++                      HFC_outb(hc, R_TX0, 0 | V_OUT_EN);
++              } else {
++                      HFC_outb(hc, R_RX0, 1);
++                      HFC_outb(hc, R_TX0, 1 | V_OUT_EN);
++              }
++              HFC_outb(hc, R_TX1, V_ATX | V_NTRI);
++              HFC_outb(hc, R_TX_FR0, 0x00);
++              HFC_outb(hc, R_TX_FR1, 0xf8);
++              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);
++              HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC);
++              if (test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg)) {
++                      /* NT mode */
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: E1 port LT-mode\n", __FUNCTION__);
++                      r_e1_wr_sta = 1; /* G1 */
++                      HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS | V_PCM_SYNC);
++              } else {
++                      /* TE mode */
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: E1 port is TE-mode\n", __FUNCTION__);
++                      r_e1_wr_sta = 2; /* F2 */
++                      HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
++              }
++              HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */
++              HFC_outb(hc, R_SYNC_OUT, V_IPATS0 | V_IPATS1 | V_IPATS2);
++              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);
++              
++      }
++
++      /* set interrupts & global interrupt */
++      hc->hw.r_irq_ctrl = V_FIFO_IRQ | V_GLOB_IRQ_EN;
++
++      unlock_dev(hc);
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk("%s: done\n", __FUNCTION__);
++}
++
++
++/***********************/
++/* 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(hfc_multi_t *hc)
++{
++      int cnt = 1; /* as long as there is no trouble */
++      int err = -EIO;
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
++
++      lock_dev(hc, 0);
++      if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, SA_SHIRQ, "HFC-multi", hc)) {
++              printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", hc->pci_dev->irq);
++              unlock_dev(hc);
++              return(-EIO);
++      }
++      hc->irq = hc->pci_dev->irq;
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt);
++      while (cnt) {
++              if ((err = init_chip(hc)))
++                      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
++               */
++              unlock_dev(hc);
++              HFC_outb(hc, R_IRQ_CTRL, V_GLOB_IRQ_EN);
++              set_current_state(TASK_UNINTERRUPTIBLE);
++              schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
++              /* turn IRQ off until chip is completely initialized */
++              HFC_outb(hc, R_IRQ_CTRL, 0);
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt);
++              if (hc->irqcnt) {
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: done\n", __FUNCTION__);
++                      return(0);
++              }
++              lock_dev(hc, 0);
++              printk(KERN_WARNING "HFC PCI: IRQ(%d) getting no interrupts during init (try %d)\n", hc->irq, cnt);
++              cnt--;
++              err = -EIO;
++      }
++
++      error:
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq);
++      free_irq(hc->irq, hc);
++      hc->irq = 0;
++      unlock_dev(hc);
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: done (err=%d)\n", __FUNCTION__, err);
++      return(err);
++}
++
++
++/*********************************************************/
++/* select free channel and return OK(0), -EBUSY, -EINVAL */
++/*********************************************************/
++
++static int
++SelFreeBChannel(hfc_multi_t *hc, int ch, channel_info_t *ci)
++{
++      bchannel_t              *bch;
++      hfc_multi_t             *hfc;
++      mISDNstack_t            *bst;
++      struct list_head        *head;
++      int                     cnr;
++      int                     port = hc->chan[ch].port;
++
++      if (port < 0 || port>=hc->type) {
++              printk(KERN_WARNING "%s: port(%d) out of range", __FUNCTION__, port);
++              return(-EINVAL);
++      }
++      
++      if (!ci)
++              return(-EINVAL);
++      ci->st.p = NULL;
++      cnr=0;
++      bst = hc->chan[ch].dch->inst.st;
++      if (list_empty(&bst->childlist)) {
++              if ((bst->id & FLG_CLONE_STACK) &&
++                      (bst->childlist.prev != &bst->childlist)) {
++                      head = bst->childlist.prev;
++              } else {
++                      printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n",
++                              __FUNCTION__, bst->id, bst->childlist.prev, &bst->childlist, bst->childlist.next);
++                      return(-EINVAL);
++              }
++      } else
++              head = &bst->childlist;
++      list_for_each_entry(bst, head, list) {
++              if (cnr == ((hc->type==1)?30:2)) /* 30 or 2 B-channels */ {
++                      printk(KERN_WARNING "%s: fatal error: more b-stacks than ports", __FUNCTION__);
++                      return(-EINVAL);
++              }
++              if(!bst->mgr) {
++                      int_errtxt("no mgr st(%p)", bst);
++                      return(-EINVAL);
++              }
++              hfc = bst->mgr->data;
++              if (!hfc) {
++                      int_errtxt("no mgr->data st(%p)", bst);
++                      return(-EINVAL);
++              }
++              if (hc->type==1)
++                      bch = hc->chan[cnr + 1 + (cnr>=15)].bch;
++              else
++                      bch = hc->chan[(port<<2) + cnr].bch;
++              if (!(ci->channel & (~CHANNEL_NUMBER))) {
++                      /* only number is set */
++                      if ((ci->channel & 0x3) == (cnr + 1)) {
++                              if (bch->protocol != ISDN_PID_NONE)
++                                      return(-EBUSY);
++                              ci->st.p = bst;
++                              return(0);
++                      }
++              }
++              if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) {
++                      if (bch->protocol == ISDN_PID_NONE) {
++                              ci->st.p = bst;
++                              return(0);
++                      }
++              }
++              cnr++;
++      }
++      return(-EBUSY);
++}
++
++
++/*********************************/
++/* find pci device and set it up */
++/*********************************/
++
++/* this variable is used as card index when more than one cards are present */
++static struct pci_dev *dev_hfcmulti = NULL;
++
++static int
++setup_pci(hfc_multi_t *hc)
++{
++      int i;
++      struct pci_dev *tmp_hfcmulti = NULL;
++
++      /* go into 0-state (we might already be due to zero-filled-object */
++      i = 0;
++      while(i < 32) {
++              if (hc->chan[i].dch)
++                      hc->chan[i].dch->ph_state = 0;
++              i++;
++      }
++
++      /* loop all card ids */
++      i = 0;
++      while (id_list[i].vendor_id) {
++//            printk(KERN_DEBUG "setup_pci(): investigating card entry %d\n", i);
++              /* only the given type is searched */
++              if (id_list[i].type != hc->type) {
++                      i++;
++                      continue;
++              }
++              tmp_hfcmulti = pci_find_subsys(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_sub, id_list[i].device_sub, dev_hfcmulti);
++              if (tmp_hfcmulti) {
++                      break;
++              }
++              i++;
++      }
++      if (!tmp_hfcmulti) {
++              printk(KERN_WARNING "HFC-multi: No PCI card found\n");
++              return (-ENODEV);
++      }
++
++      /* found a card */
++      printk(KERN_INFO "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", id_list[i].vendor_name, id_list[i].card_name, (id_list[i].clock2)?"double":"normal");
++      dev_hfcmulti = tmp_hfcmulti;    /* old device */
++      hc->pci_dev = dev_hfcmulti;
++      if (id_list[i].clock2)
++              test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
++      if (hc->pci_dev->irq <= 0) {
++              printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n");
++              return (-EIO);
++      }
++      if (pci_enable_device(dev_hfcmulti)) {
++              printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n");
++              return (-EIO);
++      }
++      hc->leds = id_list[i].leds;
++#ifdef CONFIG_HFCMULTI_PCIMEM
++      hc->pci_membase = (char *) get_pcibase(dev_hfcmulti, 1);
++      if (!hc->pci_membase) {
++              printk(KERN_WARNING "HFC-multi: No IO-Memory for PCI card found\n");
++              return (-EIO);
++      }
++
++      if (!(hc->pci_membase = ioremap((ulong) hc->pci_membase, 256))) {
++              printk(KERN_WARNING "HFC-multi: failed to remap io address space. (internal error)\n");
++              hc->pci_membase = NULL;
++              return (-EIO);
++      }
++      printk(KERN_INFO "%s: defined at MEMBASE %#x IRQ %d HZ %d leds-type %d\n", hc->name, (u_int) hc->pci_membase, hc->pci_dev->irq, HZ, hc->leds);
++      pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
++#else
++      hc->pci_iobase = (u_int) get_pcibase(dev_hfcmulti, 0);
++      if (!hc->pci_iobase) {
++              printk(KERN_WARNING "HFC-multi: No IO for PCI card found\n");
++              return (-EIO);
++      }
++      if (!request_region(hc->pci_iobase, 8, "hfcmulti")) {
++              printk(KERN_WARNING "HFC-multi: failed to rquest address space at 0x%04x (internal error)\n", hc->pci_iobase);
++              hc->pci_iobase = 0;
++              return (-EIO);
++      }
++
++      printk(KERN_INFO "%s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", hc->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);
++#endif
++
++      /* At this point the needed PCI config is done */
++      /* fifos are still not enabled */
++        lock_dev(hc, 0);
++#ifdef SPIN_DEBUG
++      printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &hc->lock.spin_adr, hc->lock.spin_adr);
++      printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &hc->lock.busy_adr, hc->lock.busy_adr);
++#endif
++      unlock_dev(hc);
++      return (0);
++}
++
++
++/*******************************************
++ * remove port or complete card from stack *
++ *******************************************/
++
++static void
++release_port(hfc_multi_t *hc, int port)
++{
++      int i = 0;
++      int all = 1, any = 0;
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
++
++#ifdef LOCK_STATISTIC
++        printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",                hc->lock.try_ok, hc->lock.try_wait, hc->lock.try_mult, hc->lock.try_inirq);
++        printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
++                hc->lock.irq_ok, hc->lock.irq_fail);
++#endif
++
++      if (port >= hc->type) {
++              printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", __FUNCTION__, port);
++              return;
++      }
++
++//    if (debug & DEBUG_HFCMULTI_INIT)
++//            printk(KERN_DEBUG "%s: before lock_dev\n", __FUNCTION__);
++      lock_dev(hc, 0);
++//    if (debug & DEBUG_HFCMULTI_INIT)
++//            printk(KERN_DEBUG "%s: after lock_dev\n", __FUNCTION__);
++
++      if (port > -1) {
++              i = 0;
++              while(i < hc->type) {
++                      if (hc->created[i] && i!=port)
++                              all = 0;
++                      if (hc->created[i])
++                              any = 1;
++                      i++;
++              }
++              if (!any) {
++                      printk(KERN_WARNING "%s: ERROR card has no used stacks anymore.\n", __FUNCTION__);
++                      unlock_dev(hc);
++                      return;
++              }
++      }
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: releasing port=%d all=%d any=%d\n", __FUNCTION__, port, all, any);
++
++      if (port>-1 && !hc->created[port]) {
++              printk(KERN_WARNING "%s: ERROR given stack is not used by card (port=%d).\n", __FUNCTION__, port);
++              unlock_dev(hc);
++              return;
++      }
++
++      if (all) {
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_WARNING "%s: card has no more used stacks, so we release hardware.\n", __FUNCTION__);
++              if (hc->irq) {
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq);
++                      free_irq(hc->irq, hc);
++                      hc->irq = 0;
++              }
++      }
++
++      /* disable D-channels & B-channels */
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: disable all channels (d and b)\n", __FUNCTION__);
++      if (hc->type == 1) {
++              hc->chan[16].slot_tx = -1;
++              hc->chan[16].slot_rx = -1;
++              hc->chan[16].conf = -1;
++              mode_hfcmulti(hc, 16, ISDN_PID_NONE, -1, 0, -1, 0);//d
++              i = 0;
++              while(i < 30) {
++                      hc->chan[i+1+(i>=15)].slot_tx = -1;
++                      hc->chan[i+1+(i>=15)].slot_rx = -1;
++                      hc->chan[i+1+(i>=15)].conf = -1;
++                      mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0, -1, 0); //b
++                      i++;
++              }
++      } else {
++              i = 0;
++              while(i < hc->type) {
++                      if (all || port==i)
++                      if (hc->created[i]) {
++                              hc->chan[(i<<2)+2].slot_tx = -1;
++                              hc->chan[(i<<2)+2].slot_rx = -1;
++                              hc->chan[(i<<2)+2].conf = -1;
++                              mode_hfcmulti(hc, (i<<2)+2, ISDN_PID_NONE, -1, 0, -1, 0); //d
++                              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_PID_NONE, -1, 0, -1, 0); //b1
++                              hc->chan[(i<<2)+1].slot_tx = -1;
++                              hc->chan[(i<<2)+1].slot_rx = -1;
++                              hc->chan[(i<<2)+1].conf = -1;
++                              mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0, -1, 0); //b2
++                      }
++                      i++;
++              }
++      }
++
++      i = 0;
++      while(i < 32) {
++              if (hc->chan[i].dch)
++              if (hc->created[hc->chan[i].port])
++              if (hc->chan[i].dch->dbusytimer.function != NULL && (all || port==i)) {
++                      del_timer(&hc->chan[i].dch->dbusytimer);
++                      hc->chan[i].dch->dbusytimer.function = NULL;
++              }
++              i++;
++      }
++
++      /* free channels */
++      i = 0;
++      while(i < 32) {
++              if (hc->created[hc->chan[i].port])
++              if (hc->chan[i].port==port || all) {
++                      if (hc->chan[i].dch) {
++                              if (debug & DEBUG_HFCMULTI_INIT)
++                                      printk(KERN_DEBUG "%s: free port %d D-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i);
++                              mISDN_free_dch(hc->chan[i].dch);
++                              HFCM_obj.ctrl(hc->chan[i].dch->inst.up.peer, MGR_DISCONNECT | REQUEST, &hc->chan[i].dch->inst.up);
++                              HFCM_obj.ctrl(&hc->chan[i].dch->inst, MGR_UNREGLAYER | REQUEST, NULL);
++                              kfree(hc->chan[i].dch);
++                              hc->chan[i].dch = NULL;
++                      }
++                      if (hc->chan[i].rx_buf) {
++                              kfree(hc->chan[i].rx_buf);
++                              hc->chan[i].rx_buf = NULL;
++                      }
++                      if (hc->chan[i].bch) {
++                              if (debug & DEBUG_HFCMULTI_INIT)
++                                      printk(KERN_DEBUG "%s: free port %d B-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i);
++                              discard_queue(&hc->chan[i].dtmfque);
++                              mISDN_free_bch(hc->chan[i].bch);
++                              kfree(hc->chan[i].bch);
++                              hc->chan[i].bch = NULL;
++                      }
++              }
++              i++;
++      }
++      i = 0;
++      while(i < 8) {
++              if (i==port || all)
++                      hc->created[i] = 0;
++              i++;
++      }
++
++      /* dimm leds */
++      if (hc->leds)
++              hfcmulti_leds(hc);
++
++      /* release IO & remove card */
++      if (all) {
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: do release_io_hfcmulti\n", __FUNCTION__);
++              release_io_hfcmulti(hc);
++
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: removing object from listbase\n", __FUNCTION__);
++              list_del(&hc->list);
++              unlock_dev(hc);
++              kfree(hc);
++      } else
++              unlock_dev(hc);
++}
++
++static int
++HFC_manager(void *data, u_int prim, void *arg)
++{
++      hfc_multi_t *hc;
++      mISDNinstance_t *inst = data;
++      struct sk_buff *skb;
++      dchannel_t *dch = NULL;
++      bchannel_t *bch = NULL;
++      int ch = -1;
++      int i;
++
++      if (!data) {
++              MGR_HASPROTOCOL_HANDLER(prim,arg,&HFCM_obj)
++              printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg);
++              return(-EINVAL);
++      }
++
++      /* find channel and card */
++      list_for_each_entry(hc, &HFCM_obj.ilist, list) {
++              i = 0;
++              while(i < 32) {
++//printk(KERN_DEBUG "comparing (D-channel) card=%08x inst=%08x with inst=%08x\n", hc, &hc->dch[i].inst, inst);
++                      if (hc->chan[i].dch)
++                      if (&hc->chan[i].dch->inst == inst) {
++                              ch = i;
++                              dch = hc->chan[i].dch;
++                              break;
++                      }
++                      if (hc->chan[i].bch)
++                      if (&hc->chan[i].bch->inst == inst) {
++                              ch = i;
++                              bch = hc->chan[i].bch;
++                              break;
++                      }
++                      i++;
++              }
++              if (ch >= 0)
++                      break;
++      }
++      if (ch < 0) {
++              printk(KERN_ERR "%s: no card/channel found  data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg);
++              return(-EINVAL);
++      }
++      if (debug & DEBUG_HFCMULTI_MGR)
++              printk(KERN_DEBUG "%s: channel %d (0..31)  data %p prim %x arg %p\n", __FUNCTION__, ch, data, prim, arg);
++
++      switch(prim) {
++              case MGR_REGLAYER | CONFIRM:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__);
++              if (dch)
++                      dch_set_para(dch, &inst->st->para);
++              if (bch)
++                      bch_set_para(bch, &inst->st->para);
++              break;
++
++              case MGR_UNREGLAYER | REQUEST:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__);
++              if (dch) {
++                      inst->down.fdata = dch;
++                      if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) {
++                              if (hfcmulti_l1hw(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              } else
++              if (bch) {
++                      inst->down.fdata = bch;
++                      if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST, 0, 0, NULL, 0))) {
++                              if (hfcmulti_l2l1(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              }
++              HFCM_obj.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
++              HFCM_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++              break;
++
++              case MGR_CLRSTPARA | INDICATION:
++              arg = NULL;
++              // fall through
++              case MGR_ADDSTPARA | INDICATION:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__);
++              if (dch)
++                      dch_set_para(dch, arg);
++              if (bch)
++                      bch_set_para(bch, arg);
++              break;
++
++              case MGR_RELEASE | INDICATION:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__);
++              if (dch) {
++                      release_port(hc, hc->chan[ch].port);
++                      HFCM_obj.refcnt--;
++              }
++              if (bch)
++                      HFCM_obj.refcnt--;
++              break;
++
++              case MGR_CONNECT | REQUEST:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_CONNECT\n", __FUNCTION__);
++              return(mISDN_ConnectIF(inst, arg));
++              //break;
++
++              case MGR_SETIF | REQUEST:
++              case MGR_SETIF | INDICATION:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__);
++              if (dch)
++                      return(mISDN_SetIF(inst, arg, prim, hfcmulti_l1hw, NULL, dch));
++              if (bch)
++                      return(mISDN_SetIF(inst, arg, prim, hfcmulti_l2l1, NULL, bch));
++              //break;
++
++              case MGR_DISCONNECT | REQUEST:
++              case MGR_DISCONNECT | INDICATION:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__);
++              return(mISDN_DisConnectIF(inst, arg));
++              //break;
++
++              case MGR_SELCHANNEL | REQUEST:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__);
++              if (!dch) {
++                      printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__);
++                      return(-EINVAL);
++              }
++              return(SelFreeBChannel(hc, ch, arg));
++              //break;
++
++              case MGR_SETSTACK | CONFIRM:
++              if (debug & DEBUG_HFCMULTI_MGR)
++                      printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__);
++              if (bch && inst->pid.global==2) {
++                      inst->down.fdata = bch;
++                      if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) {
++                              if (hfcmulti_l2l1(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++                      if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
++                              if_link(&inst->up, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0);
++                      else
++                              if_link(&inst->up, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0);
++              }
++              break;
++
++              PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
++              PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST);
++              default:
++              printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++
++static int __init
++HFCmulti_init(void)
++{
++      int err, err2, i;
++      hfc_multi_t *hc,*next;
++      mISDN_pid_t pid, pids[MAX_CARDS];
++      mISDNstack_t *dst = NULL; /* make gcc happy */
++      int port_cnt;
++      int bchperport, pt;
++      int ch, ch2;
++      dchannel_t *dch;
++      bchannel_t *bch;
++      char tmp[64];
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__);
++
++#ifdef __BIG_ENDIAN
++#error "not running on big endian machines now"
++#endif
++      strcpy(tmp, hfcmulti_revision);
++      printk(KERN_INFO "mISDN: HFC-multi driver Rev. %s\n", mISDN_getrev(tmp));
++
++      switch(poll) {
++              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: case 0:
++              poll_timer = 6;
++              poll = 128;
++              break;
++              case 256:
++              poll_timer = 7;
++              break;
++              default:
++              printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll);
++              err = -EINVAL;
++              return(err);
++              
++      }
++
++      memset(&HFCM_obj, 0, sizeof(HFCM_obj));
++#ifdef MODULE
++      HFCM_obj.owner = THIS_MODULE;
++#endif
++      INIT_LIST_HEAD(&HFCM_obj.ilist);
++      HFCM_obj.name = HFCName;
++      HFCM_obj.own_ctrl = HFC_manager;
++      HFCM_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0
++                              | ISDN_PID_L0_TE_E1 | ISDN_PID_L0_NT_E1;
++      HFCM_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0
++                              | ISDN_PID_L1_TE_E1 | ISDN_PID_L1_NT_E1;
++      HFCM_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC;
++      HFCM_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV;
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: registering HFCM_obj\n", __FUNCTION__);
++      if ((err = mISDN_register(&HFCM_obj))) {
++              printk(KERN_ERR "Can't register HFC-Multi cards error(%d)\n", err);
++              return(err);
++      }
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++
++      /* Note: ALL ports are one "card" object */
++
++      port_cnt = HFC_cnt = 0;
++      while (HFC_cnt < MAX_CARDS  &&  type[HFC_cnt] > 0) {
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: Registering chip type %d (0x%x)\n", __FUNCTION__, type[HFC_cnt] & 0xff, type[HFC_cnt]);
++
++              /* check card type */
++              switch (type[HFC_cnt] & 0xff) {
++                      case 1:
++                      bchperport = 30;
++                      break;
++
++                      case 4:
++                      bchperport = 2;
++                      break;
++
++                      case 8:
++                      bchperport = 2;
++                      break;
++
++                      default:
++                      printk(KERN_ERR "Card type(%d) not supported.\n", type[HFC_cnt] & 0xff);
++                      err = -EINVAL;
++                      goto free_object;
++              }
++
++
++              /* allocate card+fifo structure */
++              if (!(hc = kmalloc(sizeof(hfc_multi_t), GFP_ATOMIC))) {
++                      printk(KERN_ERR "No kmem for HFC-Multi card\n");
++                      err = -ENOMEM;
++                      goto free_object;
++              }
++              memset(hc, 0, sizeof(hfc_multi_t));
++              hc->id = HFC_cnt + 1;
++              hc->pcm = pcm[HFC_cnt];
++
++              /* set chip specific features */
++              hc->masterclk = -1;
++              hc->type = type[HFC_cnt] & 0xff;
++              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]&0x400) && hc->type==4)
++//                    test_and_set_bit(HFC_CHIP_LEDS, &hc->chip);
++              if (type[HFC_cnt] & 0x800)
++                      test_and_set_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 (hc->type == 1)
++                      sprintf(hc->name, "HFC-E1#%d", HFC_cnt+1);
++              else
++                      sprintf(hc->name, "HFC-%dS#%d", hc->type, HFC_cnt+1);
++
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__);
++              list_add_tail(&hc->list, &HFCM_obj.ilist);
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__);
++              
++              lock_HW_init(&hc->lock);
++
++              pt = 0;
++              while (pt < hc->type) {
++                      if (protocol[port_cnt] == 0) {
++                              printk(KERN_ERR "Not enough 'protocol' values given.\n");
++                              err = -EINVAL;
++                              goto free_channels;
++                      }
++                      if (hc->type == 1)
++                              ch = 16;
++                      else
++                              ch = (pt<<2)+2;
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: Registering D-channel, card(%d) ch(%d) port(%d) protocol(%x)\n", __FUNCTION__, HFC_cnt+1, ch, pt+1, protocol[port_cnt]);
++                      hc->chan[ch].port = pt;
++                      hc->chan[ch].nt_timer = -1;
++                      dch = kmalloc(sizeof(dchannel_t), GFP_ATOMIC);
++                      if (!dch) {
++                              err = -ENOMEM;
++                              goto free_channels;
++                      }
++                      memset(dch, 0, sizeof(dchannel_t));
++                      dch->channel = ch;
++                      //dch->debug = debug;
++                      dch->inst.obj = &HFCM_obj;
++                      dch->inst.lock = lock_dev;
++                      dch->inst.unlock = unlock_dev;
++                      mISDN_init_instance(&dch->inst, &HFCM_obj, hc);
++                      dch->inst.pid.layermask = ISDN_LAYER(0);
++                      sprintf(dch->inst.name, "HFCm%d/%d", HFC_cnt+1, pt+1);
++                      if (!(hc->chan[ch].rx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
++                              err = -ENOMEM;
++                              goto free_channels;
++                      }
++                      if (mISDN_init_dch(dch)) {
++                              err = -ENOMEM;
++                              goto free_channels;
++                      }
++                      hc->chan[ch].dch = dch;
++
++                      i=0;
++                      while(i < bchperport) {
++                              if (hc->type == 1)
++                                      ch2 = i + 1 + (i>=15);
++                              else
++                                      ch2 = (pt<<2)+i;
++                              if (debug & DEBUG_HFCMULTI_INIT)
++                                      printk(KERN_DEBUG "%s: Registering B-channel, card(%d) ch(%d) port(%d) channel(%d)\n", __FUNCTION__, HFC_cnt+1, ch2, pt+1, i);
++                              hc->chan[ch2].port = pt;
++                              bch = kmalloc(sizeof(bchannel_t), GFP_ATOMIC);
++                              if (!bch) {
++                                      err = -ENOMEM;
++                                      goto free_channels;
++                              }
++                              memset(bch, 0, sizeof(bchannel_t));
++                              bch->channel = ch2;
++                              mISDN_init_instance(&bch->inst, &HFCM_obj, hc);
++                              bch->inst.pid.layermask = ISDN_LAYER(0);
++                              bch->inst.lock = lock_dev;
++                              bch->inst.unlock = unlock_dev;
++                              //bch->debug = debug;
++                              sprintf(bch->inst.name, "%s B%d",
++                                      dch->inst.name, i+1);
++                              if (mISDN_init_bch(bch)) {
++                                      kfree(bch);
++                                      err = -ENOMEM;
++                                      goto free_channels;
++                              }
++                              skb_queue_head_init(&hc->chan[ch2].dtmfque);
++                              hc->chan[ch2].bch = bch;
++                              if (bch->dev) {
++                                      bch->dev->wport.pif.func =
++                                              hfcmulti_l2l1;
++                                      bch->dev->wport.pif.fdata =
++                                              bch;
++                              }
++                              i++;
++                      }
++
++                      /* set D-channel */
++                      mISDN_set_dchannel_pid(&pid, protocol[port_cnt], layermask[port_cnt]);
++
++                      /* set PRI */
++                      if (hc->type == 1) {
++                              if (layermask[port_cnt] & ISDN_LAYER(2)) {
++                                      pid.protocol[2] |= ISDN_PID_L2_DF_PTP;
++                              }
++                              if (layermask[port_cnt] & ISDN_LAYER(3)) {
++                                      pid.protocol[3] |= ISDN_PID_L3_DF_PTP;
++                                      pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID;
++                                      pid.protocol[3] |= ISDN_PID_L3_DF_CRLEN2;
++                              }
++                      }
++
++                      /* set protocol type */
++                      if (protocol[port_cnt] & 0x10) {
++                              /* NT-mode */
++                              dch->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0;
++                              dch->inst.pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0;
++                              pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0;
++                              pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0;
++                              dch->inst.pid.layermask |= ISDN_LAYER(1);
++                              pid.layermask |= ISDN_LAYER(1);
++                              if (layermask[port_cnt] & ISDN_LAYER(2))
++                                      pid.protocol[2] = ISDN_PID_L2_LAPD_NET;
++                              test_and_set_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg);
++                      } else {
++                              /* TE-mode */
++                              dch->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0;
++                              pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0;
++                              if (hc->type == 1) {
++                                      /* own E1 for E1 */
++                                      dch->inst.pid.protocol[1] = ISDN_PID_L1_TE_E1;
++                                      pid.protocol[1] = ISDN_PID_L1_TE_E1;
++                                      dch->inst.pid.layermask |= ISDN_LAYER(1);
++                                      pid.layermask |= ISDN_LAYER(1);
++                              }
++                      }
++
++
++                      if (hc->type != 1) {
++                              /* S/T */
++                              /* set master clock */
++                              if (protocol[port_cnt] & 0x10000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL set master clock: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      if (test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) {
++                                              printk(KERN_ERR "Error: Master clock for port(%d) of card(%d) is only possible with TE-mode\n", pt+1, HFC_cnt+1);
++                                              err = -EINVAL;
++                                              goto free_channels;
++                                      }
++                                      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);
++                                              err = -EINVAL;
++                                              goto free_channels;
++                                      }
++                                      hc->masterclk = pt;
++                              }
++
++                              /* set transmitter line to non capacitive */
++                              if (protocol[port_cnt] & 0x20000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL set non capacitive transmitter: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      test_and_set_bit(HFC_CFG_NONCAP_TX, &hc->chan[ch].cfg);
++                              }
++
++                              /* disable E-channel */
++                              if (protocol[port_cnt] & 0x40000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL disable E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      test_and_set_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[ch].cfg);
++                              }
++                              /* register E-channel */
++                              if (protocol[port_cnt] & 0x80000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL register E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      test_and_set_bit(HFC_CFG_REG_ECHANNEL, &hc->chan[ch].cfg);
++                              }
++                      } else {
++                              /* E1 */
++                              /* set optical line type */
++                              if (protocol[port_cnt] & 0x10000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL set optical interfacs: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      test_and_set_bit(HFC_CFG_OPTICAL, &hc->chan[ch].cfg);
++                              }
++
++                              /* set LOS report */
++                              if (protocol[port_cnt] & 0x40000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL set LOS report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      test_and_set_bit(HFC_CFG_REPORT_LOS, &hc->chan[ch].cfg);
++                              }
++
++                              /* set AIS report */
++                              if (protocol[port_cnt] & 0x80000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL set AIS report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      test_and_set_bit(HFC_CFG_REPORT_AIS, &hc->chan[ch].cfg);
++                              }
++
++                              /* set SLIP report */
++                              if (protocol[port_cnt] & 0x100000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL set SLIP report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
++                                      test_and_set_bit(HFC_CFG_REPORT_SLIP, &hc->chan[ch].cfg);
++                              }
++
++                              /* set elastic jitter buffer */
++                              if (protocol[port_cnt] & 0x600000) {
++                                      if (debug & DEBUG_HFCMULTI_INIT)
++                                              printk(KERN_DEBUG "%s: PROTOCOL set elastic buffer to %d: card(%d) port(%d)\n", __FUNCTION__, hc->chan[ch].jitter, HFC_cnt+1, pt);
++                                      hc->chan[ch].jitter = (protocol[port_cnt]>>21) & 0x3;
++                              } else
++                                      hc->chan[ch].jitter = 2; /* default */
++                      }
++
++                      memcpy(&pids[pt], &pid, sizeof(pid));
++
++                      pt++;
++                      port_cnt++;
++              }
++
++              /* run card setup */
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: Setting up card(%d)\n", __FUNCTION__, HFC_cnt+1);
++              if ((err = setup_pci(hc))) {
++                      goto free_channels;
++              }
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: Initializing card(%d)\n", __FUNCTION__, HFC_cnt+1);
++              if ((err = init_card(hc))) {
++                      if (debug & DEBUG_HFCMULTI_INIT) {
++                              printk(KERN_DEBUG "%s: do release_io_hfcmulti\n", __FUNCTION__);
++                              release_io_hfcmulti(hc);
++                      }
++                      goto free_channels;
++              }
++
++              if (debug & DEBUG_HFCMULTI_INIT)
++                      printk(KERN_DEBUG "%s: Init modes card(%d)\n", __FUNCTION__, HFC_cnt+1);
++              hfcmulti_initmode(hc);
++
++              /* add stacks */
++              pt = 0;
++              while(pt < hc->type) {
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: Adding d-stack: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt+1);
++                      if (hc->type == 1)
++                              dch = hc->chan[16].dch;
++                      else
++                              dch = hc->chan[(pt<<2)+2].dch;
++                      if ((err = HFCM_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &dch->inst))) {
++                              printk(KERN_ERR  "MGR_ADDSTACK REQUEST dch err(%d)\n", err);
++                              free_release:
++                              release_port(hc, -1); /* all ports */
++                              goto free_object;
++                      }
++                      /* indicate that this stack is created */
++                      hc->created[pt] = 1;
++
++                      dst = dch->inst.st;
++
++                      i = 0;
++                      while(i < bchperport) {
++                              if (debug & DEBUG_HFCMULTI_INIT)
++                                      printk(KERN_DEBUG "%s: Adding b-stack: card(%d) port(%d) B-channel(%d)\n", __FUNCTION__, HFC_cnt+1, pt+1, i+1);
++                              if (hc->type == 1)
++                                      bch = hc->chan[i + 1 + (i>=15)].bch;
++                              else
++                                      bch = hc->chan[(pt<<2) + i].bch;
++                              if ((err = HFCM_obj.ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) {
++                                      printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
++                                      free_delstack:
++                                      HFCM_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL);
++                                      goto free_release;
++                              }
++                              bch->st = bch->inst.st;
++                              i++;
++                      }
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pids[pt].layermask);
++
++                      if ((err = HFCM_obj.ctrl(dst, MGR_SETSTACK | REQUEST, &pids[pt]))) {
++                              printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err);
++                              goto free_delstack;
++                      }
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: (after MGR_SETSTACK REQUEST)\n", __FUNCTION__);
++
++                      /* delay some time */
++                      set_current_state(TASK_UNINTERRUPTIBLE);
++                      schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
++
++                      /* tell stack, that we are ready */
++                      HFCM_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL);
++
++                      pt++;
++              }
++
++              /* now turning on irq */
++              HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
++
++              HFC_cnt++;
++      }
++
++      if (HFC_cnt == 0) {
++              printk(KERN_INFO "hfc_multi: No cards defined, read the documentation.\n");
++              err = -EINVAL;
++              goto free_object;
++      }
++
++      printk(KERN_INFO "hfc_multi driver: %d cards with total of %d ports installed.\n", HFC_cnt, port_cnt);
++      return(0);
++
++      /* DONE */
++
++      /* if an error ocurred */
++      free_channels:
++      i = 0;
++      while(i < 32) {
++              if (hc->chan[i].dch) {
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: free D-channel %d (1..32)\n", __FUNCTION__, i);
++                      mISDN_free_dch(hc->chan[i].dch);
++                      kfree(hc->chan[i].dch);
++                      hc->chan[i].dch = NULL;
++              }
++              if (hc->chan[i].rx_buf) {
++                      kfree(hc->chan[i].rx_buf);
++                      hc->chan[i].rx_buf = NULL;
++              }
++              if (hc->chan[i].bch) {
++                      if (debug & DEBUG_HFCMULTI_INIT)
++                              printk(KERN_DEBUG "%s: free B-channel %d (1..32)\n", __FUNCTION__, i);
++                      discard_queue(&hc->chan[i].dtmfque);
++                      mISDN_free_bch(hc->chan[i].bch);
++                      kfree(hc->chan[i].bch);
++                      hc->chan[i].bch = NULL;
++              }
++              i++;
++      }
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++      list_del(&hc->list);
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++      kfree(hc);
++
++      free_object:
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: brefore mISDN_unregister (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++      if ((err2 = mISDN_unregister(&HFCM_obj))) {
++              printk(KERN_ERR "Can't unregister HFC-Multi cards error(%d)\n", err);
++      }
++      list_for_each_entry_safe(hc, next, &HFCM_obj.ilist, list) {
++              printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFCM_obj.refcnt);
++              release_port(hc, -1); /* all ports */
++      }
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: after mISDN_unregister (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: exitting with error %d\n", __FUNCTION__, err);
++      return(err);
++}
++
++
++#ifdef MODULE
++static void __exit
++HFCmulti_cleanup(void)
++{
++      hfc_multi_t *hc,*next;
++      int err;
++
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: entered (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++      if ((err = mISDN_unregister(&HFCM_obj))) {
++              printk(KERN_ERR "Can't unregister HFC-Multi cards error(%d)\n", err);
++      }
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++      
++      list_for_each_entry_safe(hc, next, &HFCM_obj.ilist, list) {
++              printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFCM_obj.refcnt);
++              release_port(hc, -1); /* all ports */
++      }
++      if (debug & DEBUG_HFCMULTI_INIT)
++              printk(KERN_DEBUG "%s: done (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
++      return;
++}
++module_init(HFCmulti_init);
++module_exit(HFCmulti_cleanup);
++#endif
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_multi.h  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_multi.h      2004-11-22 09:33:38.158742904 +0000
+@@ -0,0 +1,1255 @@
++/*
++
++ * see notice in hfc_multi.c
++ */
++
++#define DEBUG_HFCMULTI_FIFO   0x0001
++#define DEBUG_HFCMULTI_CRC    0x0002
++#define DEBUG_HFCMULTI_INIT     0x0004
++#define DEBUG_HFCMULTI_MGR    0x0008
++#define DEBUG_HFCMULTI_MODE   0x0010
++#define DEBUG_HFCMULTI_MSG    0x0020
++#define DEBUG_HFCMULTI_STATE  0x0040
++#define DEBUG_HFCMULTI_SYNC   0x0100
++#define DEBUG_HFCMULTI_DTMF   0x0200
++#define DEBUG_HFCMULTI_LOCK   0x8000
++
++#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 {
++      /* dch or bch is set, otherwhise this channel is not used */
++      dchannel_t      *dch; /* link if channel is a D-channel */
++      bchannel_t      *bch; /* link if channel is a B-channel */
++      int             rx_idx; /* for D-channel */
++      unsigned char   *rx_buf; /* for D-channel */
++      int             port; /* the interface port this channel is associated with */
++      int             nt_mode;
++      int             nt_timer; /* -1 if off, 0 if elapsed, >0 if running */
++      int             los, ais, slip_tx, slip_rx; /* current alarms */
++      int             jitter;
++      u_long          cfg; /* port configuration */
++      int             sync; /* sync state (used by E1) */
++      struct sk_buff_head dtmfque;
++      unsigned long   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             e1_state; /* keep track of last state */
++};
++
++typedef struct hfc_chan               hfc_chan_t;
++
++struct hfcmulti_hw {
++      unsigned char   r_ctrl; 
++      unsigned char   r_irq_ctrl;     
++      unsigned char   r_cirm; 
++      unsigned char   r_ram_sz;
++      unsigned char   r_pcm_mo0;
++      unsigned char   r_irqmsk_misc;
++      unsigned char   r_dtmf;
++      unsigned char   r_st_sync;
++      unsigned char   r_sci_msk;
++      unsigned char   a_st_ctrl0[8];
++      timer_t timer;
++};
++
++typedef struct hfcmulti_hw    hfcmulti_hw_t;
++
++/* for each stack these flags are used (cfg) */
++#define HFC_CFG_NTMODE                0 /* NT-mode */
++#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  2 /* register E-channel */
++#define HFC_CFG_OPTICAL               3 /* the E1 interface is optical */
++#define HFC_CFG_REPORT_LOS    4 /* the card should report loss of signal */
++#define HFC_CFG_REPORT_AIS    5 /* the card should report alarm ind. sign. */
++#define HFC_CFG_REPORT_SLIP   6 /* the card should report bit slips */
++#define HFC_CFG_DTMF          7 /* enable DTMF-detection */
++
++#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_DTMF         4 /* DTMF decoding is enabled */
++#define HFC_CHIP_ULAW         5 /* ULAW mode */
++#define HFC_CHIP_LEDS         5 /* used LEDs */
++#define HFC_CHIP_CLOCK2               6 /* double clock mode */
++
++struct hfc_multi {
++      struct list_head        list;
++      char            name[32];
++      int             id;     /* chip number starting with 1 */
++      int             pcm;    /* id of pcm bus */
++      int             type;
++
++      u_int           irq;    /* irq used by card */
++      u_int           irqcnt;
++      struct pci_dev  *pci_dev;
++#ifdef CONFIG_HFCMULTI_PCIMEM
++      unsigned char   *pci_membase;/* PCI memory (MUST BE BYTE POINTER) */
++#else
++      u_int           pci_iobase;/* PCI IO (MUST BE BYTE POINTER) */
++#endif
++      hfcmulti_hw_t   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 (not maximum) */
++      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_int           activity[8]; /* if there is any action on this port (will be cleared after showing led-states) */
++
++      mISDN_HWlock_t  lock;   /* the lock */
++#ifdef SPIN_DEBUG
++      void            *lock_adr;
++#endif
++
++      /* 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
++       */
++      hfc_chan_t      chan[32];
++      u_char          created[8]; /* what port is created */
++      signed char     slot_owner[256]; /* owner channel of slot */
++};
++
++typedef struct hfc_multi      hfc_multi_t;
++
++
++/*********************************************\
++|* 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_MO0             0x14
++#define R_PCM_MO1             0x15
++#define R_PCM_MO2             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            0x21
++#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_RX_STA0             0x24
++#define R_RX_STA1             0x25
++#define R_RX_STA2             0x26
++#define R_RX_STA3             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_EN             0x80
++/* R_STATE */
++#define V_E1_STA              0x01
++#define V_ALT_FR_RX           0x40
++#define V_ALT_FR_TX           0x80
++/* R_RX_STA0 */
++#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_STA1 */
++#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_MO0 */
++#define V_PCM_MO              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_SYCN_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_MAP
++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",          0x31},
++      {"R_SYNC_OUT",          0x21},
++      {"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_RX_STA0",           0x24},
++      {"R_RX_STA1",           0x25},
++      {"R_RX_STA2",           0x26},
++      {"R_RX_STA3",           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_MAP */
++
++/* ACCESS TO PCI MEMORY MAPPED REGISTERS */
++
++#ifdef CONFIG_HFCMULTI_PCIMEM
++
++#define HFC_outl(a,b,c) (*((volatile u_long *)((a->pci_membase)+b)) = c)
++#define HFC_inl(a,b) (*((volatile u_long *)((a->pci_membase)+b)))
++
++/* no debug */
++#define HFC_outl_(a,b,c) (*((volatile u_long *)((a->pci_membase)+b)) = c)
++#define HFC_inl_(a,b) (*((volatile u_long *)((a->pci_membase)+b)))
++#define HFC_inw_(a,b) (*((volatile u_short *)((a->pci_membase)+b)))
++#define HFC_outb_(a,b,c) (*((volatile u_char *)((a->pci_membase)+b)) = c)
++#define HFC_inb_(a,b) (*((volatile u_char *)((a->pci_membase)+b)))
++#define HFC_wait_(a) while((*((volatile u_char *)((a->pci_membase)+R_STATUS))) & V_BUSY)
++
++/* macros */
++#ifndef HFC_REGISTER_MAP
++
++/* usage: HFC_outX(card,register,value); */
++#define HFC_outb(a,b,c) (*((volatile u_char *)((a->pci_membase)+b)) = c)
++/* usage: register=HFC_inX(card,register); */
++#define HFC_inb(a,b) (*((volatile u_char *)((a->pci_membase)+b)))
++#define HFC_inw(a,b) (*((volatile u_short *)((a->pci_membase)+b)))
++/* usage: HFC_wait(card); */
++#define HFC_wait(a) while((*((volatile u_char *)((a->pci_membase)+R_STATUS))) & V_BUSY)
++
++#else /* HFC_REGISTER_MAP */
++
++#define HFC_outb(a,b,c) _HFC_outb(a, b, c, __FUNCTION__, __LINE__)
++static unsigned char _HFC_outb(hfc_multi_t *a, unsigned char b, unsigned char c, 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 == b)
++                      strcat(regname, hfc_register_names[i].name);
++      }
++      if (regname[0] == '\0')
++              strcpy(regname, "register");
++
++      bits[7] = '0'+(!!(c&1));
++      bits[6] = '0'+(!!(c&2));
++      bits[5] = '0'+(!!(c&4));
++      bits[4] = '0'+(!!(c&8));
++      bits[3] = '0'+(!!(c&16));
++      bits[2] = '0'+(!!(c&32));
++      bits[1] = '0'+(!!(c&64));
++      bits[0] = '0'+(!!(c&128));
++      printk(KERN_DEBUG "HFC_outb(\"%s\", %02x=%s, 0x%02x=%s); in %s() line %d\n", a->name, b, regname, c, bits, function, line);
++      return(*(((volatile u_char *)a->pci_membase)+b) = c);
++}
++#define HFC_inb(a,b) _HFC_inb(a, b, __FUNCTION__, __LINE__)
++static unsigned char _HFC_inb(hfc_multi_t *a, unsigned char b, char *function, int line)
++{
++      char regname[256]="", bits[9]="xxxxxxxx";
++      u_char c = (*(((volatile u_char *)a->pci_membase)+b));
++      int i;
++
++      i = 0;
++      while(hfc_register_names[i++].name)
++              ;
++      while(hfc_register_names[++i].name) {
++              if (hfc_register_names[i].reg == b)
++                      strcat(regname, hfc_register_names[i].name);
++      }
++      if (regname[0] == '\0')
++              strcpy(regname, "register");
++
++      bits[7] = '0'+(!!(c&1));
++      bits[6] = '0'+(!!(c&2));
++      bits[5] = '0'+(!!(c&4));
++      bits[4] = '0'+(!!(c&8));
++      bits[3] = '0'+(!!(c&16));
++      bits[2] = '0'+(!!(c&32));
++      bits[1] = '0'+(!!(c&64));
++      bits[0] = '0'+(!!(c&128));
++      printk(KERN_DEBUG "HFC_inb(\"%s\", %02x=%s) = 0x%02x=%s; in %s() line %d\n", a->name, b, regname, c, bits, function, line);
++      return(c);
++}
++#define HFC_inw(a,b) _HFC_inw(a, b, __FUNCTION__, __LINE__)
++static unsigned short _HFC_inw(hfc_multi_t *a, unsigned char b, char *function, int line)
++{
++      char regname[256]="";
++      u_short c = (*(((volatile u_short *)a->pci_membase)+b));
++      int i;
++
++      i = 0;
++      while(hfc_register_names[i++].name)
++              ;
++      while(hfc_register_names[++i].name) {
++              if (hfc_register_names[i].reg == b)
++                      strcat(regname, hfc_register_names[i].name);
++      }
++      if (regname[0] == '\0')
++              strcpy(regname, "register");
++
++      printk(KERN_DEBUG "HFC_inw(\"%s\", %02x=%s) = 0x%04x; in %s() line %d\n", a->name, b, regname, c, function, line);
++      return(c);
++}
++#define HFC_wait(a) _HFC_wait(a, __FUNCTION__, __LINE__)
++static void _HFC_wait(hfc_multi_t *a, char *function, int line)
++{
++      printk(KERN_DEBUG "HFC_wait(\"%s\"); in %s() line %d\n", a->name, function, line);
++      while((*(((volatile u_char *)a->pci_membase)+R_STATUS)) & V_BUSY);
++}
++
++#endif /* else HFC_REGISTER_MAP */
++
++#else /* CONFIG_HFCMULTI_PCIMEM */
++
++/* ACCESS TO PCI IO REGISTERS */
++
++#ifdef HFC_REGISTER_MAP
++#error Please use "HFC_REGISTER_MAP" debugging only in conjuction with PCIMEM access.
++#endif
++
++/* usage: HFC_outX(card,register,value); */
++static inline void HFC_outb(hfc_multi_t *a, u_char b, u_char c)
++{
++      outb(b,(a->pci_iobase)+4);
++      outb(c,a->pci_iobase);
++}
++static inline void HFC_outl(hfc_multi_t *a, u_char b, u_long c)
++{
++      outb(b,(a->pci_iobase)+4);
++      outl(c,a->pci_iobase);
++}
++
++/* usage: value=HFC_inX(card,register); */
++static inline u_char HFC_inb(hfc_multi_t *a, u_char b)
++{
++      outb(b,(a->pci_iobase)+4);
++      return (inb((volatile u_int)a->pci_iobase));
++}
++static inline u_short HFC_inw(hfc_multi_t *a, u_char b)
++{
++      outb(b,(a->pci_iobase)+4);
++      return (inw((volatile u_int)a->pci_iobase));
++}
++static inline u_long HFC_inl(hfc_multi_t *a, u_char b)
++{
++      outb(b,(a->pci_iobase)+4);
++      return (inl((volatile u_int)a->pci_iobase));
++}
++
++/* usage: HFC_wait(card); */
++static inline void HFC_wait(hfc_multi_t *a)
++{
++      outb(R_STATUS,(a->pci_iobase)+4);
++      while(inb((volatile u_int)a->pci_iobase) & V_BUSY);
++}
++
++/* usage: HFC_set(card,register); */
++#define HFC_set(a, b) outb(b,(a->pci_iobase)+4)
++
++/* usage: HFC_putX(card,value); */
++#define HFC_putb(a,b) outb(b,a->pci_iobase)
++#define HFC_putl(a,b) outl(b,a->pci_iobase)
++
++/* usage: value=HFC_getX(card); */
++#define HFC_getb(a) inb((volatile u_int)a->pci_iobase)
++#define HFC_getl(a) inl((volatile u_int)a->pci_iobase)
++
++/* no debug */
++#define HFC_outl_(a,b,c) HFC_outl(a,b,c)
++#define HFC_inl_(a,b) HFC_inl(a,b)
++#define HFC_inw_(a,b) HFC_inw(a,b)
++#define HFC_outb_(a,b,c) HFC_outb(a,b,c)
++#define HFC_inb_(a,b) HFC_inb(a,b)
++#define HFC_wait_(a) HFC_wait(a)
++
++#endif /* else CONFIG_HFCMULTI_PCIMEM */
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.c    1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.c        2004-11-22 09:33:38.168741384 +0000
+@@ -0,0 +1,2575 @@
++/* $Id$
++
++ * hfc_pci.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 2001  by Karsten Keil (keil@isdn4linux.de)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License 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/config.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++
++#include "dchannel.h"
++#include "bchannel.h"
++#include "hfc_pci.h"
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++#include <linux/isdn_compat.h>
++
++#define SPIN_DEBUG
++#define LOCK_STATISTIC
++#include "hw_lock.h"
++
++#define HFC_INFO(txt) printk(KERN_DEBUG txt)
++
++extern const char *CardType[];
++
++static const char *hfcpci_revision = "$Revision$";
++
++/* table entry in the PCI devices list */
++typedef struct {
++      int vendor_id;
++      int device_id;
++      char *vendor_name;
++      char *card_name;
++} PCI_ENTRY;
++
++#define NT_T1_COUNT   20      /* number of 3.125ms interrupts for G2 timeout */
++#define CLKDEL_TE     0x0e    /* CLKDEL in TE mode */
++#define CLKDEL_NT     0x6c    /* CLKDEL in NT mode */
++
++#ifndef PCI_VENDOR_ID_CCD
++#define PCI_VENDOR_ID_CCD             0x1397
++#define PCI_DEVICE_ID_CCD_2BD0                0x2BD0
++#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_B008                0xB008
++#define PCI_DEVICE_ID_CCD_B009                0xB009
++#define PCI_DEVICE_ID_CCD_B00A                0xB00A
++#define PCI_DEVICE_ID_CCD_B00B                0xB00B
++#define PCI_DEVICE_ID_CCD_B00C                0xB00C
++#define PCI_DEVICE_ID_CCD_B100                0xB100
++
++#define PCI_VENDOR_ID_ASUSTEK         0x1043
++#define PCI_DEVICE_ID_ASUSTEK_0675    0x0675
++
++#define PCI_VENDOR_ID_BERKOM          0x0871
++#define PCI_DEVICE_ID_BERKOM_A1T      0xFFA1
++#define PCI_DEVICE_ID_BERKOM_T_CONCEPT        0xFFA2
++
++#define PCI_VENDOR_ID_ANIGMA          0x1051
++#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100
++
++#define PCI_VENDOR_ID_ZOLTRIX         0x15b0
++#define PCI_DEVICE_ID_ZOLTRIX_2BD0    0x2BD0
++
++#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E        0x0070
++#define PCI_DEVICE_ID_DIGI_DF_M_E     0x0071
++#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A        0x0072
++#define PCI_DEVICE_ID_DIGI_DF_M_A     0x0073
++
++#define PCI_VENDOR_ID_ABOCOM          0x13D1
++#define PCI_DEVICE_ID_ABOCOM_2BD1     0x2BD1
++#endif
++
++static const PCI_ENTRY id_list[] =
++{
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"},
++      {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"},
++      {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"},
++      {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"},
++      {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"},
++      {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"},
++      {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"},
++      {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"},
++      {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"},
++      {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"},
++      {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"},
++      {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"},
++      {0, 0, NULL, NULL},
++};
++
++
++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           nt_mode;
++      int                     nt_timer;
++      struct pci_dev          *dev;
++      unsigned char           *pci_io; /* start of PCI IO memory */
++      void                    *share_start; /* shared memory for Fifos start */
++      void                    *fifos; /* FIFO memory */ 
++      int                     last_bfifo_cnt[2]; /* marker saving last b-fifo frame count */
++      struct timer_list       timer;
++};
++
++typedef struct hfcPCI_hw      hfcPCI_hw_t;
++
++#define SPIN_DEBUG
++#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
++
++typedef struct _hfc_pci {
++      struct list_head        list;
++      u_char                  subtyp;
++      u_char                  chanlimit;
++      u_long                  cfg;
++      u_int                   irq;
++      u_int                   irqcnt;
++      hfcPCI_hw_t             hw;
++      mISDN_HWlock_t          lock;
++      dchannel_t              dch;
++      bchannel_t              bch[2];
++} hfc_pci_t;
++
++
++static int lock_dev(void *data, int nowait)
++{
++      register mISDN_HWlock_t *lock = &((hfc_pci_t *)data)->lock;
++      
++      return(lock_HW(lock, nowait));
++} 
++
++static void unlock_dev(void *data)
++{
++      register mISDN_HWlock_t *lock = &((hfc_pci_t *)data)->lock;
++
++      unlock_HW(lock);
++}
++
++/* Interface functions */
++
++/******************************************/
++/* free hardware resources used by driver */
++/******************************************/
++void
++release_io_hfcpci(hfc_pci_t *hc)
++{
++      hc->hw.int_m2 = 0;      /* interrupt output off ! */
++      Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
++      Write_hfc(hc, HFCPCI_CIRM, HFCPCI_RESET);               /* Reset On */
++      mdelay(10);                                             /* Timeout 10ms */
++      hc->hw.cirm = 0; /* Reset Off */
++      Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
++      pci_write_config_word(hc->hw.dev, PCI_COMMAND, 0);      /* disable memory mapped ports + busmaster */
++      del_timer(&hc->hw.timer);
++      kfree(hc->hw.share_start);
++      hc->hw.share_start = NULL;
++      iounmap((void *)hc->hw.pci_io);
++}
++
++
++/********************************************************************************/
++/* function called to reset the HFC PCI chip. A complete software reset of chip */
++/* and fifos is done.                                                           */
++/********************************************************************************/
++static void
++reset_hfcpci(hfc_pci_t *hc)
++{
++      u_char  val;
++      int     cnt = 0;
++
++      HFC_INFO("reset_hfcpci: entered\n");
++      val = Read_hfc(hc, HFCPCI_CHIP_ID);
++      printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val);
++      pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO);  /* enable memory mapped ports, disable busmaster */
++      hc->hw.int_m2 = 0;              /* interrupt output off ! */
++      Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
++      pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */
++      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;
++      if (hc->hw.nt_mode) {
++              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.states = 2;              /* F2 */
++      }
++      Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
++      Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
++      Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel);
++      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));
++
++      Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states);
++      udelay(10);
++      Write_hfc(hc, HFCPCI_STATES, hc->hw.states);
++
++      Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
++      Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
++      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(hfc_pci_t *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
++bchannel_t *
++Sel_BCS(hfc_pci_t *hc, int channel)
++{
++      if (hc->bch[0].protocol && (hc->bch[0].channel & channel))
++              return (&hc->bch[0]);
++      else if (hc->bch[1].protocol && (hc->bch[1].channel & channel))
++              return (&hc->bch[1]);
++      else
++              return (NULL);
++}
++
++/***************************************/
++/* clear the desired B-channel rx fifo */
++/***************************************/
++static void hfcpci_clear_fifo_rx(hfc_pci_t *hc, int fifo)
++{       u_char fifo_state;
++        bzfifo_type *bzr;
++
++      if (fifo) {
++              bzr = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2;
++              fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX;
++      } else {
++              bzr = &((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 = B_FIFO_SIZE + B_SUB_VAL - 1;
++      bzr->za[MAX_B_FRAMES].z2 = 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(hfc_pci_t *hc, int fifo)
++{       u_char fifo_state;
++        bzfifo_type *bzt;
++
++      if (fifo) {
++              bzt = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2;
++              fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX;
++      } else {
++              bzt = &((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 & L1_DEB_HSCX)
++              mISDN_debugprint(&hc->bch[fifo].inst, "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x) state(%x)",
++                              fifo, bzt->f1, bzt->f2, bzt->za[MAX_B_FRAMES].z1, 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 = B_FIFO_SIZE + B_SUB_VAL - 1;
++      bzt->za[MAX_B_FRAMES].z2 = 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 & L1_DEB_HSCX)
++              mISDN_debugprint(&hc->bch[fifo].inst, "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)",
++                              fifo, bzt->f1, bzt->f2, bzt->za[MAX_B_FRAMES].z1, bzt->za[MAX_B_FRAMES].z2);
++}   
++
++/*********************************************/
++/* read a complete B-frame out of the buffer */
++/*********************************************/
++static struct sk_buff
++*
++hfcpci_empty_fifo(bchannel_t *bch, bzfifo_type * bz, u_char * bdata, int count)
++{
++      u_char          *ptr, *ptr1, new_f2;
++      struct sk_buff  *skb;
++      int             total, maxlen, new_z2;
++      z_type          *zp;
++
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo");
++      zp = &bz->za[bz->f2];   /* point to Z-Regs */
++      new_z2 = 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 + (zp->z1 - B_SUB_VAL)))) {
++              if (bch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count);
++#ifdef ERROR_STATISTIC
++              bch->err_inv++;
++#endif
++              bz->za[new_f2].z2 = new_z2;
++              bz->f2 = new_f2;        /* next buffer */
++              skb = NULL;
++      } else if (!(skb = alloc_stack_skb(count - 3, bch->up_headerlen)))
++              printk(KERN_WARNING "HFCPCI: receive out of memory\n");
++      else {
++              total = count;
++              count -= 3;
++              ptr = skb_put(skb, count);
++
++              if (zp->z2 + count <= B_FIFO_SIZE + B_SUB_VAL)
++                      maxlen = count;         /* complete transfer */
++              else
++                      maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;      /* maximum */
++
++              ptr1 = bdata + (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 = new_z2;
++              bz->f2 = new_f2;        /* next buffer */
++
++      }
++      return (skb);
++}
++
++/*******************************/
++/* D-channel receive procedure */
++/*******************************/
++static
++int
++receive_dmsg(hfc_pci_t *hc)
++{
++      struct sk_buff  *skb;
++      dchannel_t      *dch = &hc->dch;
++      int             maxlen;
++      int             rcnt, total;
++      int             count = 5;
++      u_char          *ptr, *ptr1;
++      dfifo_type      *df;
++      z_type          *zp;
++
++      df = &((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 = zp->z1 - zp->z2;
++              if (rcnt < 0)
++                      rcnt += D_FIFO_SIZE;
++              rcnt++;
++              if (dch->debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&dch->inst, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
++                              df->f1, df->f2, zp->z1, zp->z2, rcnt);
++
++              if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) ||
++                  (df->data[zp->z1])) {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[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 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1);
++              } else if ((skb = alloc_stack_skb(rcnt - 3, dch->up_headerlen))) {
++                      total = rcnt;
++                      rcnt -= 3;
++                      ptr = skb_put(skb, rcnt);
++
++                      if (zp->z2 + rcnt <= D_FIFO_SIZE)
++                              maxlen = rcnt;  /* complete transfer */
++                      else
++                              maxlen = D_FIFO_SIZE - zp->z2;  /* maximum */
++
++                      ptr1 = df->data + 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 = (zp->z2 + total) & (D_FIFO_SIZE - 1);
++
++                      if (dch->debug & L1_DEB_ISAC_FIFO) {
++                              char *t = dch->dlog;
++
++                              count = skb->len;
++                              ptr = skb->data;
++                              t += sprintf(t, "hfcD_empty_fifo cnt %d", count);
++                              mISDN_QuickHex(t, ptr, count);
++                              mISDN_debugprint(&dch->inst, dch->dlog);
++                      }
++                      skb_queue_tail(&dch->rqueue, skb);
++                      dchannel_sched_event(dch, D_RCVBUFREADY);
++              } else
++                      printk(KERN_WARNING "HFC-PCI: D receive out of memory\n");
++      }
++      return (1);
++}
++
++/*******************************************************************************/
++/* check for transparent receive data and read max one threshold size if avail */
++/*******************************************************************************/
++int
++hfcpci_empty_fifo_trans(bchannel_t *bch, bzfifo_type * bz, u_char * bdata)
++{
++      unsigned short  *z1r, *z2r;
++      int             new_z2, fcnt, maxlen;
++      struct sk_buff  *skb;
++      u_char          *ptr, *ptr1;
++
++      z1r = &bz->za[MAX_B_FRAMES].z1;         /* pointer to z reg */
++      z2r = z1r + 1;
++
++      if (!(fcnt = *z1r - *z2r))
++              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 = *z2r + fcnt;   /* new position in fifo */
++      if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
++              new_z2 -= B_FIFO_SIZE;  /* buffer wrap */
++
++      if (!(skb = alloc_stack_skb(fcnt, bch->up_headerlen)))
++              printk(KERN_WARNING "HFCPCI: receive out of memory\n");
++      else {
++              ptr = skb_put(skb, fcnt);
++              if (*z2r + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
++                      maxlen = fcnt;  /* complete transfer */
++              else
++                      maxlen = B_FIFO_SIZE + B_SUB_VAL - *z2r;        /* maximum */
++
++              ptr1 = bdata + (*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 */
++              }
++              skb_queue_tail(&bch->rqueue, skb);
++              bch_sched_event(bch, B_RCVBUFREADY);
++      }
++
++      *z2r = new_z2;          /* new position */
++      return (1);
++}                             /* hfcpci_empty_fifo_trans */
++
++/**********************************/
++/* B-channel main receive routine */
++/**********************************/
++void
++main_rec_hfcpci(bchannel_t *bch)
++{
++      hfc_pci_t       *hc = bch->inst.data;
++      int             rcnt, real_fifo;
++      int             receive, count = 5;
++      struct sk_buff  *skb;
++      bzfifo_type     *bz;
++      u_char          *bdata;
++      z_type          *zp;
++
++
++      if ((bch->channel & 2) && (!hc->hw.bswapped)) {
++              bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2;
++              bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b2;
++              real_fifo = 1;
++      } else {
++              bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b1;
++              bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b1;
++              real_fifo = 0;
++      }
++      Begin:
++      count--;
++      if (bz->f1 != bz->f2) {
++              if (bch->debug & L1_DEB_HSCX)
++                      mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) f1(%d) f2(%d)",
++                              bch->channel, bz->f1, bz->f2);
++              zp = &bz->za[bz->f2];
++
++              rcnt = zp->z1 - zp->z2;
++              if (rcnt < 0)
++                      rcnt += B_FIFO_SIZE;
++              rcnt++;
++              if (bch->debug & L1_DEB_HSCX)
++                      mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)",
++                              bch->channel, zp->z1, zp->z2, rcnt);
++              if ((skb = hfcpci_empty_fifo(bch, bz, bdata, rcnt))) {
++                      skb_queue_tail(&bch->rqueue, skb);
++                      bch_sched_event(bch, B_RCVBUFREADY);
++              }
++              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 (bch->protocol == ISDN_PID_L1_B_64TRANS)
++              receive = hfcpci_empty_fifo_trans(bch, bz, bdata);
++      else
++              receive = 0;
++      if (count && receive)
++              goto Begin;
++      return;
++}
++
++/**************************/
++/* D-channel send routine */
++/**************************/
++static void
++hfcpci_fill_dfifo(hfc_pci_t *hc)
++{
++      dchannel_t      *dch = &hc->dch;
++      int             fcnt;
++      int             count, new_z1, maxlen;
++      dfifo_type      *df;
++      u_char          *src, *dst, new_f1;
++
++      if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
++              mISDN_debugprint(&dch->inst, "hfcpci_fill_dfifo");
++
++      count = dch->tx_len - dch->tx_idx;
++      if (count <= 0)
++              return;
++      df = &((fifo_area *) (hc->hw.fifos))->d_chan.d_tx;
++
++      if (dch->debug & L1_DEB_ISAC_FIFO)
++              mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)",
++                      df->f1, df->f2,
++                      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 & L1_DEB_ISAC)
++                      mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo more as 14 frames");
++#ifdef ERROR_STATISTIC
++              cs->err_tx++;
++#endif
++              return;
++      }
++      /* now determine free bytes in FIFO buffer */
++      maxlen = df->za[df->f2 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1 - 1;
++      if (maxlen <= 0)
++              maxlen += D_FIFO_SIZE;  /* count now contains available bytes */
++
++      if (dch->debug & L1_DEB_ISAC)
++              mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo count(%ld/%d)",
++                      count, maxlen);
++      if (count > maxlen) {
++              if (dch->debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo no fifo mem");
++              return;
++      }
++      new_z1 = (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_buf + dch->tx_idx;        /* source pointer */
++      dst = df->data + df->za[df->f1 & D_FREG_MASK].z1;
++      maxlen = D_FIFO_SIZE - 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 = new_z1;       /* for next buffer */
++      df->za[df->f1 & D_FREG_MASK].z1 = new_z1;       /* new pos actual buffer */
++      df->f1 = new_f1;        /* next frame */
++      if (dch->debug & L1_DEB_ISAC_FIFO) {
++              char *t = dch->dlog;
++
++              count = dch->tx_len - dch->tx_idx;
++              src = dch->tx_buf + dch->tx_idx;
++              t += sprintf(t, "hfcD_fill_fifo cnt %d", count);
++              mISDN_QuickHex(t, src, count);
++              mISDN_debugprint(&dch->inst, dch->dlog);
++      }
++      dch->tx_idx = dch->tx_len;
++      return;
++}
++
++/**************************/
++/* B-channel send routine */
++/**************************/
++static void
++hfcpci_fill_fifo(bchannel_t *bch)
++{
++      hfc_pci_t       *hc = bch->inst.data;
++      int             maxlen, fcnt;
++      int             count, new_z1;
++      bzfifo_type     *bz;
++      u_char          *bdata;
++      u_char          new_f1, *src, *dst;
++      unsigned short  *z1t, *z2t;
++
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "%s", __FUNCTION__);
++      count = bch->tx_len - bch->tx_idx;
++      if (bch->tx_len <= 0)
++              return;
++      
++      if ((bch->channel & 2) && (!hc->hw.bswapped)) {
++              bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2;
++              bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b2;
++      } else {
++              bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b1;
++              bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b1;
++      }
++
++      if (bch->protocol == ISDN_PID_L1_B_64TRANS) {
++              z1t = &bz->za[MAX_B_FRAMES].z1;
++              z2t = z1t + 1;
++              if (bch->debug & L1_DEB_HSCX)
++                      mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo_trans ch(%x) cnt(%d) z1(%x) z2(%x)",
++                              bch->channel, count, *z1t, *z2t);
++              fcnt = *z2t - *z1t;
++              if (fcnt <= 0)
++                      fcnt += B_FIFO_SIZE;    /* fcnt contains available bytes in fifo */
++              fcnt = B_FIFO_SIZE - fcnt;      /* remaining bytes to send */
++next_t_frame:
++              if (fcnt < (2 * HFCPCI_BTRANS_THRESHOLD)) {
++                      count = bch->tx_len - bch->tx_idx;
++                      if (count < B_FIFO_SIZE - fcnt) {
++                              /* data is suitable for fifo */
++                              new_z1 = *z1t + count;  /* new buffer Position */
++                              if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
++                                      new_z1 -= B_FIFO_SIZE;  /* buffer wrap */
++                              src = bch->tx_buf;      /* source pointer */
++                              dst = bdata + (*z1t - B_SUB_VAL);
++                              maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t;      /* end of fifo */
++                              if (bch->debug & L1_DEB_HSCX_FIFO)
++                                      mISDN_debugprint(&bch->inst, "hfcpci_FFt fcnt(%d) maxl(%d) nz1(%x) dst(%p)",
++                                              fcnt, maxlen, new_z1, dst);
++                              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);
++                              }
++                              fcnt += bch->tx_len;
++                              *z1t = new_z1;  /* now send data */
++                      } else if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo_trans ch(%x) frame length %d discarded",
++                                      bch->channel, bch->tx_len);
++                      if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                              if (bch->next_skb) {
++                                      bch->tx_idx = 0;
++                                      bch->tx_len = bch->next_skb->len;
++                                      memcpy(bch->tx_buf,
++                                              bch->next_skb->data,
++                                              bch->tx_len);
++                                      bch_sched_event(bch, B_XMTBUFREADY);
++                                      goto next_t_frame;
++                              } else
++                                      printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n");
++                      }
++                      bch->tx_len = 0;
++                      test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                      bch->tx_idx = bch->tx_len;
++              }
++              return;
++      }
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)",
++                      __FUNCTION__, bch->channel, 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 & L1_DEB_HSCX)
++                      mISDN_debugprint(&bch->inst, "hfcpci_fill_Bfifo more as 14 frames");
++              return;
++      }
++      /* now determine free bytes in FIFO buffer */
++      maxlen = bz->za[bz->f2].z2 - bz->za[bz->f1].z1 - 1;
++      if (maxlen <= 0)
++              maxlen += B_FIFO_SIZE;  /* count now contains available bytes */
++
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo ch(%x) count(%ld/%d),%lx",
++                      bch->channel, count,
++                      maxlen, current->state);
++
++      if (maxlen < count) {
++              if (bch->debug & L1_DEB_HSCX)
++                      mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo no fifo mem");
++              return;
++      }
++      new_z1 = 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_buf + bch->tx_idx;        /* source pointer */
++      dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL);
++      maxlen = (B_FIFO_SIZE + B_SUB_VAL) - 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 = new_z1;     /* for next buffer */
++      bz->f1 = new_f1;        /* next frame */
++      bch->tx_idx = bch->tx_len;
++      test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++      return;
++}
++
++
++#if 0
++/**********************************************/
++/* D-channel l1 state call for leased NT-mode */
++/**********************************************/
++static void
++dch_nt_l2l1(struct PStack *st, int pr, void *arg)
++{
++      hfc_pci_t *hc = (struct IsdnCardState *) st->l1.hardware;
++
++      switch (pr) {
++              case (PH_DATA | REQUEST):
++              case (PH_PULL | REQUEST):
++              case (PH_PULL | INDICATION):
++                      st->l1.l1hw(st, pr, arg);
++                      break;
++              case (PH_ACTIVATE | REQUEST):
++                      st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
++                      break;
++              case (PH_TESTLOOP | REQUEST):
++                      if (1 & (long) arg)
++                              debugl1(hc, "PH_TEST_LOOP B1");
++                      if (2 & (long) arg)
++                              debugl1(hc, "PH_TEST_LOOP B2");
++                      if (!(3 & (long) arg))
++                              debugl1(hc, "PH_TEST_LOOP DISABLED");
++                      st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
++                      break;
++              default:
++                      if (hc->debug)
++                              debugl1(hc, "dch_nt_l2l1 msg %04X unhandled", pr);
++                      break;
++      }
++}
++
++
++
++/***********************/
++/* set/reset echo mode */
++/***********************/
++static int
++hfcpci_auxcmd(hfc_pci_t *hc, isdn_ctrl * ic)
++{
++      int i = *(unsigned int *) ic->parm.num;
++
++      if ((ic->arg == 98) &&
++          (!(hc->hw.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) {
++              hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */
++              Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel);
++              Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0);    /* HFC ST G0 */
++              udelay(10);
++              hc->hw.sctrl |= SCTRL_MODE_NT;
++              Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);      /* set NT-mode */
++              udelay(10);
++              Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1);    /* HFC ST G1 */
++              udelay(10);
++              Write_hfc(hc, HFCPCI_STATES, 1 | HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
++              cs->dc.hfcpci.ph_state = 1;
++              hc->hw.nt_mode = 1;
++              hc->hw.nt_timer = 0;
++              cs->stlist->l2.l2l1 = dch_nt_l2l1;
++              debugl1(hc, "NT mode activated");
++              return (0);
++      }
++      if ((hc->chanlimit > 1) || (hc->hw.bswapped) ||
++          (hc->hw.nt_mode) || (ic->arg != 12))
++              return (-EINVAL);
++
++      if (i) {
++              cs->logecho = 1;
++              hc->hw.trm |= 0x20;     /* enable echo chan */
++              hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
++              hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
++      } else {
++              cs->logecho = 0;
++              hc->hw.trm &= ~0x20;    /* disable echo chan */
++              hc->hw.int_m1 &= ~HFCPCI_INTS_B2REC;
++              hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2RX;
++      }
++      hc->hw.sctrl_r &= ~SCTRL_B2_ENA;
++      hc->hw.sctrl &= ~SCTRL_B2_ENA;
++      hc->hw.conn |= 0x10;    /* B2-IOM -> B2-ST */
++      hc->hw.ctmt &= ~2;
++      Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
++      Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
++      Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
++      Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
++      Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
++      Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
++      Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
++      return (0);
++}                             /* hfcpci_auxcmd */
++
++/*****************************/
++/* E-channel receive routine */
++/*****************************/
++static void
++receive_emsg(hfc_pci_t *hc)
++{
++      int rcnt;
++      int receive, count = 5;
++      bzfifo_type *bz;
++      u_char *bdata;
++      z_type *zp;
++      u_char *ptr, *ptr1, new_f2;
++      int total, maxlen, new_z2;
++      u_char e_buffer[256];
++
++      bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2;
++      bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b2;
++      Begin:
++      count--;
++      if (bz->f1 != bz->f2) {
++              if (hc->debug & L1_DEB_ISAC)
++                      debugl1(hc, "hfcpci e_rec f1(%d) f2(%d)",
++                              bz->f1, bz->f2);
++              zp = &bz->za[bz->f2];
++
++              rcnt = zp->z1 - zp->z2;
++              if (rcnt < 0)
++                      rcnt += B_FIFO_SIZE;
++              rcnt++;
++              if (hc->debug & L1_DEB_ISAC)
++                      debugl1(hc, "hfcpci e_rec z1(%x) z2(%x) cnt(%d)",
++                              zp->z1, zp->z2, rcnt);
++              new_z2 = zp->z2 + rcnt;         /* 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 ((rcnt > 256 + 3) || (count < 4) ||
++                  (*(bdata + (zp->z1 - B_SUB_VAL)))) {
++                      if (hc->debug & L1_DEB_WARN)
++                              debugl1(hc, "hfcpci_empty_echan: incoming packet invalid length %d or crc", rcnt);
++                      bz->za[new_f2].z2 = new_z2;
++                      bz->f2 = new_f2;        /* next buffer */
++              } else {
++                      total = rcnt;
++                      rcnt -= 3;
++                      ptr = e_buffer;
++
++                      if (zp->z2 <= B_FIFO_SIZE + B_SUB_VAL)
++                              maxlen = rcnt;  /* complete transfer */
++                      else
++                              maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2;      /* maximum */
++
++                      ptr1 = bdata + (zp->z2 - B_SUB_VAL);    /* start of data */
++                      memcpy(ptr, ptr1, maxlen);      /* copy data */
++                      rcnt -= maxlen;
++
++                      if (rcnt) {     /* rest remaining */
++                              ptr += maxlen;
++                              ptr1 = bdata;   /* start of buffer */
++                              memcpy(ptr, ptr1, rcnt);        /* rest */
++                      }
++                      bz->za[new_f2].z2 = new_z2;
++                      bz->f2 = new_f2;        /* next buffer */
++                      if (hc->debug & DEB_DLOG_HEX) {
++                              ptr = cs->dlog;
++                              if ((total - 3) < MAX_DLOG_SPACE / 3 - 10) {
++                                      *ptr++ = 'E';
++                                      *ptr++ = 'C';
++                                      *ptr++ = 'H';
++                                      *ptr++ = 'O';
++                                      *ptr++ = ':';
++                                      ptr += mISDN_QuickHex(ptr, e_buffer, total - 3);
++                                      ptr--;
++                                      *ptr++ = '\n';
++                                      *ptr = 0;
++                                      mISDN_putstatus(hc, NULL, cs->dlog);
++                              } else
++                                      mISDN_putstatus(hc, "LogEcho: ", "warning Frame too big (%d)", total - 3);
++                      }
++              }
++
++              rcnt = bz->f1 - bz->f2;
++              if (rcnt < 0)
++                      rcnt += MAX_B_FRAMES + 1;
++              if (rcnt > 1)
++                      receive = 1;
++              else
++                      receive = 0;
++      } else
++              receive = 0;
++      if (count && receive)
++              goto Begin;
++      return;
++}                             /* receive_emsg */
++
++#endif
++
++/*********************/
++/* Interrupt handler */
++/*********************/
++static irqreturn_t
++hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
++{
++      hfc_pci_t       *hc = dev_id;
++      u_char          exval;
++      bchannel_t      *bch;
++      u_long          flags;
++      u_char          val, stat;
++
++      spin_lock_irqsave(&hc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      hc->lock.spin_adr = (void *)0x3001;
++#endif
++      if (!(hc->hw.int_m2 & 0x08)) {
++#ifdef SPIN_DEBUG
++              hc->lock.spin_adr = NULL;
++#endif
++              spin_unlock_irqrestore(&hc->lock.lock, flags);
++              return IRQ_NONE; /* not initialised */
++      }
++
++      if (HFCPCI_ANYINT & (stat = Read_hfc(hc, HFCPCI_STATUS))) {
++              val = Read_hfc(hc, HFCPCI_INT_S1);
++              if (hc->dch.debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&hc->dch.inst, "HFC-PCI: stat(%02x) s1(%02x)",
++                              stat, val);
++      } else {
++              /* shared */
++#ifdef SPIN_DEBUG
++              hc->lock.spin_adr = NULL;
++#endif
++              spin_unlock_irqrestore(&hc->lock.lock, flags);
++              return IRQ_NONE;
++      }
++      hc->irqcnt++;
++      if (test_and_set_bit(STATE_FLAG_BUSY, &hc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
++                      __FUNCTION__, hc->lock.state);
++#ifdef SPIN_DEBUG
++              printk(KERN_ERR "%s: previous lock:%p\n",
++                      __FUNCTION__, hc->lock.busy_adr);
++#endif
++#ifdef LOCK_STATISTIC
++              hc->lock.irq_fail++;
++#endif
++      } else {
++#ifdef LOCK_STATISTIC
++              hc->lock.irq_ok++;
++#endif
++#ifdef SPIN_DEBUG
++              hc->lock.busy_adr = hfcpci_interrupt;
++#endif
++      }
++
++      test_and_set_bit(STATE_FLAG_INIRQ, &hc->lock.state);
++#ifdef SPIN_DEBUG
++      hc->lock.spin_adr= NULL;
++#endif
++      spin_unlock_irqrestore(&hc->lock.lock, flags);
++
++      if (hc->dch.debug & L1_DEB_ISAC)
++              mISDN_debugprint(&hc->dch.inst, "HFC-PCI irq %x", val);
++      val &= hc->hw.int_m1;
++      if (val & 0x40) {       /* state machine irq */
++              exval = Read_hfc(hc, HFCPCI_STATES) & 0xf;
++              if (hc->dch.debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&hc->dch.inst, "ph_state chg %d->%d",
++                              hc->dch.ph_state, exval);
++              hc->dch.ph_state = exval;
++              dchannel_sched_event(&hc->dch, D_L1STATECHANGE);
++              val &= ~0x40;
++      }
++      if (val & 0x80) {       /* timer irq */
++              if (hc->hw.nt_mode) {
++                      if ((--hc->hw.nt_timer) < 0)
++                              dchannel_sched_event(&hc->dch, D_L1STATECHANGE);
++              }
++              val &= ~0x80;
++              Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
++      }
++      if (val & 0x08) {
++              if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) {
++                      if (hc->dch.debug)
++                              mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x08 IRQ");
++              } else
++                      main_rec_hfcpci(bch);
++      }
++      if (val & 0x10) {
++//            if (hc->logecho)
++//                    receive_emsg(hc);
++//            else 
++              if (!(bch = Sel_BCS(hc, 2))) {
++                      if (hc->dch.debug)
++                              mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x10 IRQ");
++              } else
++                      main_rec_hfcpci(bch);
++      }
++      if (val & 0x01) {
++              if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) {
++                      if (hc->dch.debug)
++                              mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x01 IRQ");
++              } else {
++                      if (bch->tx_idx < bch->tx_len) {
++                              hfcpci_fill_fifo(bch);
++                      } else {
++                              bch->tx_idx = 0;
++                              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                                      if (bch->next_skb) {
++                                              bch->tx_len = bch->next_skb->len;
++                                              memcpy(bch->tx_buf,
++                                                      bch->next_skb->data,
++                                                      bch->tx_len);
++                                              hfcpci_fill_fifo(bch);
++                                              bch_sched_event(bch, B_XMTBUFREADY);
++                                      } else {
++                                              printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n");
++                                              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                                              bch->tx_len = 0;
++                                      }
++                              } else {
++                                      test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                                      bch_sched_event(bch, B_XMTBUFREADY);
++                                      bch->tx_len = 0;
++                              }
++                      }
++              }
++      }
++      if (val & 0x02) {
++              if (!(bch = Sel_BCS(hc, 2))) {
++                      if (hc->dch.debug)
++                              mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x02 IRQ");
++              } else {
++                      if (bch->tx_idx < bch->tx_len) {
++                              hfcpci_fill_fifo(bch);
++                      } else {
++                              bch->tx_idx = 0;
++                              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                                      if (bch->next_skb) {
++                                              bch->tx_len = bch->next_skb->len;
++                                              memcpy(bch->tx_buf,
++                                                      bch->next_skb->data,
++                                                      bch->tx_len);
++                                              hfcpci_fill_fifo(bch);
++                                              bch_sched_event(bch, B_XMTBUFREADY);
++                                      } else {
++                                              printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n");
++                                              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                                              bch->tx_len = 0;
++                                      }
++                              } else {
++                                      test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                                      bch_sched_event(bch, B_XMTBUFREADY);
++                                      bch->tx_len = 0;
++                              }
++                      }
++              }
++      }
++      if (val & 0x20) {       /* receive dframe */
++              receive_dmsg(hc);
++      }
++      if (val & 0x04) {       /* dframe transmitted */
++              if (test_and_clear_bit(FLG_DBUSY_TIMER, &hc->dch.DFlags))
++                      del_timer(&hc->dch.dbusytimer);
++              if (test_and_clear_bit(FLG_L1_DBUSY, &hc->dch.DFlags))
++                      dchannel_sched_event(&hc->dch, D_CLEARBUSY);
++              if (hc->dch.tx_idx < hc->dch.tx_len) {
++                      hfcpci_fill_dfifo(hc);
++              } else {
++                      if (test_and_clear_bit(FLG_TX_NEXT, &hc->dch.DFlags)) {
++                              if (hc->dch.next_skb) {
++                                      hc->dch.tx_len = hc->dch.next_skb->len;
++                                      memcpy(hc->dch.tx_buf,
++                                              hc->dch.next_skb->data,
++                                              hc->dch.tx_len);
++                                      hc->dch.tx_idx = 0;
++                                      hfcpci_fill_dfifo(hc);
++                                      dchannel_sched_event(&hc->dch, D_XMTBUFREADY);
++                              } else {
++                                      printk(KERN_WARNING "hfcd tx irq TX_NEXT without skb\n");
++                                      test_and_clear_bit(FLG_TX_BUSY, &hc->dch.DFlags);
++                              }
++                      } else
++                              test_and_clear_bit(FLG_TX_BUSY, &hc->dch.DFlags);
++              }
++      }
++      spin_lock_irqsave(&hc->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      hc->lock.spin_adr = (void *)0x3002;
++#endif
++      if (!test_and_clear_bit(STATE_FLAG_INIRQ, &hc->lock.state)) {
++      }
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &hc->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      __FUNCTION__, hc->lock.state);
++      }
++#ifdef SPIN_DEBUG
++      hc->lock.busy_adr = NULL;
++      hc->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&hc->lock.lock, flags);
++      return IRQ_HANDLED;
++}
++
++/********************************************************************/
++/* timer callback for D-chan busy resolution. Currently no function */
++/********************************************************************/
++static void
++hfcpci_dbusy_timer(hfc_pci_t *hc)
++{
++}
++
++/*************************************/
++/* Layer 1 D-channel hardware access */
++/*************************************/
++static int
++HFCD_l1hw(mISDNif_t *hif, struct sk_buff *skb)
++{
++      dchannel_t      *dch;
++      hfc_pci_t       *hc;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      dch = hif->fdata;
++      hc = dch->inst.data;
++      ret = 0;
++      if (hh->prim == PH_DATA_REQ) {
++              if (dch->next_skb) {
++                      printk(KERN_WARNING "%s: next_skb exist ERROR\n", __FUNCTION__);
++                      return(-EBUSY);
++              }
++              dch->inst.lock(dch->inst.data, 0);
++              if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) {
++                      test_and_set_bit(FLG_TX_NEXT, &dch->DFlags);
++                      dch->next_skb = skb;
++                      dch->inst.unlock(dch->inst.data);
++                      return(0);
++              } else {
++                      dch->tx_len = skb->len;
++                      memcpy(dch->tx_buf, skb->data, dch->tx_len);
++                      dch->tx_idx = 0;
++                      hfcpci_fill_dfifo(dch->inst.data);
++                      dch->inst.unlock(dch->inst.data);
++                      skb_trim(skb, 0);
++                      return(if_newhead(&dch->inst.up, PH_DATA_CNF,
++                              hh->dinfo, skb));
++              }
++      } else if (hh->prim == (PH_SIGNAL | REQUEST)) {
++              dch->inst.lock(dch->inst.data, 0);
++              if ((hh->dinfo == INFO3_P8) || (hh->dinfo == 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);
++              } else
++                      ret = -EINVAL;
++              dch->inst.unlock(dch->inst.data);
++      } else if (hh->prim == (PH_CONTROL | REQUEST)) {
++              dch->inst.lock(dch->inst.data, 0);
++              if (hh->dinfo == HW_RESET) {
++                      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_msg(hc, HW_POWERUP | CONFIRM, NULL);
++              } else if (hh->dinfo == HW_DEACTIVATE) {
++                      hc->hw.mst_m &= ~HFCPCI_MASTER;
++                      Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
++                      discard_queue(&dch->rqueue);
++                      if (dch->next_skb) {
++                              dev_kfree_skb(dch->next_skb);
++                              dch->next_skb = NULL;
++                      }
++                      test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags);
++                      test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++                      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++                              del_timer(&dch->dbusytimer);
++                      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++                              dchannel_sched_event(&hc->dch, D_CLEARBUSY);
++              } else if (hh->dinfo == HW_POWERUP) {
++                      Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION);
++              } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) {
++                      u_char  slot;
++                      if (1 & hh->dinfo) {
++                              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",
++                                      __FUNCTION__, slot);
++                              Write_hfc(hc, HFCPCI_B1_SSL, slot);
++                              Write_hfc(hc, HFCPCI_B1_RSL, slot);
++                              hc->hw.conn = (hc->hw.conn & ~7) | 1;
++                              Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
++                      }
++                      if (2 & hh->dinfo) {
++                              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",
++                                      __FUNCTION__, slot);
++                              Write_hfc(hc, HFCPCI_B2_SSL, slot);
++                              Write_hfc(hc, HFCPCI_B2_RSL, slot);
++                              hc->hw.conn = (hc->hw.conn & ~0x38) | 0x08;
++                              Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
++                      }
++                      if (3 & hh->dinfo)
++                              hc->hw.trm |= 0x80;     /* enable IOM-loop */
++                      else
++                              hc->hw.trm &= 0x7f;     /* disable IOM-loop */
++                      Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
++              } else {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "%s: unknown ctrl %x",
++                                      __FUNCTION__, hh->dinfo);
++                      ret = -EINVAL;
++              }
++              dch->inst.unlock(dch->inst.data);
++      } else if (hh->prim == (PH_ACTIVATE | REQUEST)) {
++              if (hc->hw.nt_mode) {
++                      dch->inst.lock(dch->inst.data, 0);
++                      Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0); /* G0 */
++                      udelay(6);
++                      Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1); /* G1 */
++                      udelay(6);
++                      if (test_bit(HFC_CFG_MASTER, &hc->cfg))
++                              hc->hw.mst_m |= HFCPCI_MASTER;
++                      Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
++                      udelay(6);
++                      Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION | 1);
++                      dch->inst.unlock(dch->inst.data);
++              } else {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "%s: PH_ACTIVATE none NT mode",
++                                      __FUNCTION__);
++                      ret = -EINVAL;
++              }
++      } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) {
++              if (hc->hw.nt_mode) {
++                      dch->inst.lock(dch->inst.data, 0);
++                      hc->hw.mst_m &= ~HFCPCI_MASTER;
++                      Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
++                      discard_queue(&dch->rqueue);
++                      if (dch->next_skb) {
++                              dev_kfree_skb(dch->next_skb);
++                              dch->next_skb = NULL;
++                      }
++                      test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags);
++                      test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++                      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++                              del_timer(&dch->dbusytimer);
++                      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++                              dchannel_sched_event(&hc->dch, D_CLEARBUSY);
++                      dch->inst.unlock(dch->inst.data);
++              } else {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "%s: PH_DEACTIVATE none NT mode",
++                                      __FUNCTION__);
++                      ret = -EINVAL;
++              }
++      } else {
++              if (dch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&dch->inst, "%s: unknown prim %x",
++                              __FUNCTION__, hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++/***************************************************************/
++/* activate/deactivate hardware for selected channels and mode */
++/***************************************************************/
++static int
++mode_hfcpci(bchannel_t *bch, int bc, int protocol)
++{
++      hfc_pci_t       *hc = bch->inst.data;
++      int             fifo2;
++      u_char          rx_slot = 0, tx_slot = 0, pcm_mode;
++
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "HFCPCI bchannel protocol %x-->%x ch %x-->%x",
++                      bch->protocol, protocol, bch->channel, 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",
++                              __FUNCTION__);
++              rx_slot = (bc>>8) & 0xff;
++              tx_slot = (bc>>16) & 0xff;
++              bc = bc & 0xff;
++      } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_PID_NONE))
++              printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n",
++                              __FUNCTION__);
++      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_PID_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->protocol = -1;
++                      bch->channel = bc;
++              case (ISDN_PID_NONE):
++                      if (bch->protocol == ISDN_PID_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->channel & 2)
++                              hc->hw.cirm &= 0x7f;
++                      else
++                              hc->hw.cirm &= 0xbf;
++#endif
++                      bch->protocol = ISDN_PID_NONE;
++                      bch->channel = bc;
++                      break;
++              case (ISDN_PID_L1_B_64TRANS):
++                      bch->protocol = protocol;
++                      bch->channel = 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;
++                      }
++                      break;
++              case (ISDN_PID_L1_B_64HDLC):
++                      bch->protocol = protocol;
++                      bch->channel = 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;
++                      }
++                      break;
++#if 0
++              case (L1_MODE_EXTRN):
++                      if (bc) {
++                              hc->hw.conn |= 0x10;
++                              hc->hw.sctrl |= SCTRL_B2_ENA;
++                              hc->hw.sctrl_r |= SCTRL_B2_ENA;
++                              hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2;
++                              hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
++                      } else {
++                              hc->hw.conn |= 0x02;
++                              hc->hw.sctrl |= SCTRL_B1_ENA;
++                              hc->hw.sctrl_r |= SCTRL_B1_ENA;
++                              hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1;
++                              hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
++                      }
++                      break;
++#endif
++              default:
++                      mISDN_debugprint(&bch->inst, "prot not known %x", protocol);
++                      return(-ENOPROTOOPT);
++      }
++      if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
++              if ((protocol == ISDN_PID_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",
++                              __FUNCTION__, tx_slot);
++                      printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n",
++                              __FUNCTION__, 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",
++                              __FUNCTION__, tx_slot);
++                      printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n",
++                              __FUNCTION__, 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
++      if (bch->protocol)
++              bch_sched_event(bch, B_XMTBUFREADY);
++      return(0);
++}
++
++static int
++set_hfcpci_rxtest(bchannel_t *bch, int protocol, struct sk_buff *skb)
++{
++      hfc_pci_t       *hc = bch->inst.data;
++      int             *chan = (int *)skb->data;
++
++      if (skb->len <4) {
++              mISDN_debugprint(&bch->inst, "HFCPCI rxtest no channel parameter");
++              return(-EINVAL);
++      }
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x",
++                      bch->protocol, protocol, bch->channel, *chan);
++      if (bch->channel != *chan) {
++              mISDN_debugprint(&bch->inst, "HFCPCI rxtest wrong channel parameter %x/%x",
++                      bch->channel, *chan);
++              return(-EINVAL);
++      }
++      switch (protocol) {
++              case (ISDN_PID_L1_B_64TRANS):
++                      bch->protocol = 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_PID_L1_B_64HDLC):
++                      bch->protocol = 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:
++                      mISDN_debugprint(&bch->inst, "prot not known %x", 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);
++}
++
++/******************************/
++/* Layer2 -> Layer 1 Transfer */
++/******************************/
++static int
++hfcpci_l2l1(mISDNif_t *hif, struct sk_buff *skb)
++{
++      bchannel_t      *bch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      bch = hif->fdata;
++      if ((hh->prim == PH_DATA_REQ) ||
++              (hh->prim == (DL_DATA | REQUEST))) {
++#warning TODO: hier muss abgefragt werden, ob skb->len <= 0 ist, und ggf. ein -EINVAL zurückliefern, sonst wird zwar einmal confirmed, aber es regt sich nichts mehr. dies bitte auch für den d-kanal Ã¼berdenken, sowie für alle andere kartentreiber.
++              if (bch->next_skb) {
++                      printk(KERN_WARNING "%s: next_skb exist ERROR\n",
++                              __FUNCTION__);
++                      return(-EBUSY);
++              }
++              bch->inst.lock(bch->inst.data, 0);
++              if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) {
++                      test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag);
++                      bch->next_skb = skb;
++                      bch->inst.unlock(bch->inst.data);
++                      return(0);
++              } else {
++                      bch->tx_len = skb->len;
++                      memcpy(bch->tx_buf, skb->data, bch->tx_len);
++                      bch->tx_idx = 0;
++                      hfcpci_fill_fifo(bch);
++                      bch->inst.unlock(bch->inst.data);
++                      if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                              && bch->dev)
++                              hif = &bch->dev->rport.pif;
++                      else
++                              hif = &bch->inst.up;
++                      skb_trim(skb, 0);
++                      return(if_newhead(hif, hh->prim | CONFIRM,
++                              hh->dinfo, skb));
++              }
++      } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_ESTABLISH  | REQUEST))) {
++              if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
++                      ret = 0;
++              else {
++                      bch->inst.lock(bch->inst.data, 0);
++                      ret = mode_hfcpci(bch, bch->channel,
++                              bch->inst.pid.protocol[1]);
++                      bch->inst.unlock(bch->inst.data);
++              }
++              if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                      if (bch->dev)
++                              if_link(&bch->dev->rport.pif,
++                                      hh->prim | CONFIRM, 0, 0, NULL, 0);
++              skb_trim(skb, 0);
++              return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
++      } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_RELEASE | REQUEST)) ||
++              (hh->prim == (MGR_DISCONNECT | REQUEST))) {
++              bch->inst.lock(bch->inst.data, 0);
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      dev_kfree_skb(bch->next_skb);
++                      bch->next_skb = NULL;
++              }
++              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              mode_hfcpci(bch, bch->channel, ISDN_PID_NONE);
++              test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
++              bch->inst.unlock(bch->inst.data);
++              skb_trim(skb, 0);
++              if (hh->prim != (MGR_DISCONNECT | REQUEST)) {
++                      if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                              if (bch->dev)
++                                      if_link(&bch->dev->rport.pif,
++                                              hh->prim | CONFIRM, 0, 0, NULL, 0);
++                      if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
++                              return(0);
++              }
++              ret = 0;
++      } else if (hh->prim == (PH_CONTROL | REQUEST)) {
++              bch->inst.lock(bch->inst.data, 0);
++              if (hh->dinfo == HW_TESTRX_RAW) {
++                      ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64TRANS, skb);
++              } else if (hh->dinfo == HW_TESTRX_HDLC) {
++                      ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64HDLC, skb);
++              } else if (hh->dinfo == HW_TESTRX_OFF) {
++                      mode_hfcpci(bch, bch->channel, ISDN_PID_NONE);
++                      ret = 0;
++              } else
++                      ret = -EINVAL;
++              bch->inst.unlock(bch->inst.data);
++              if (!ret) {
++                      skb_trim(skb, 0);
++                      if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, hh->dinfo, skb))
++                              return(0);
++              }
++      } else {
++              printk(KERN_WARNING "%s: unknown prim(%x)\n",
++                      __FUNCTION__, hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++/***************************/
++/* handle L1 state changes */
++/***************************/
++
++static void
++HW_hfcD_bh(dchannel_t *dch)
++{
++      hfc_pci_t       *hc = dch->inst.data;
++      u_int           prim = PH_SIGNAL | INDICATION;
++      u_int           para = 0;
++      mISDNif_t       *upif = &dch->inst.up;
++
++      if (test_and_clear_bit(D_L1STATECHANGE, &dch->event)) {
++              if (!hc->hw.nt_mode) {
++                      if (dch->debug)
++                              printk(KERN_DEBUG "%s: TE newstate %x\n",
++                                      __FUNCTION__, dch->ph_state);
++                      switch (dch->ph_state) {
++                              case (0):
++                                      prim = PH_CONTROL | INDICATION;
++                                      para = HW_RESET;
++                                      break;
++                              case (3):
++                                      prim = PH_CONTROL | INDICATION;
++                                      para = HW_DEACTIVATE;
++                                      break;
++                              case (5):
++                              case (8):
++                                      para = ANYSIGNAL;
++                                      break;
++                              case (6):
++                                      para = INFO2;
++                                      break;
++                              case (7):
++                                      para = INFO4_P8;
++                                      break;
++                              default:
++                                      return;
++                      }
++              } else {
++                      if (dch->debug)
++                              printk(KERN_DEBUG "%s: NT newstate %x\n",
++                                      __FUNCTION__, dch->ph_state);
++                      dch->inst.lock(dch->inst.data, 0);
++                      switch (dch->ph_state) {
++                              case (2):
++                                      if (hc->hw.nt_timer < 0) {
++                                              hc->hw.nt_timer = 0;
++                                              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->ph_state = 4;
++                                      } else {
++                                              hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
++                                              Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
++                                              hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
++                                              hc->hw.ctmt |= HFCPCI_TIM3_125;
++                                              Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
++                                              Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
++                                              hc->hw.nt_timer = NT_T1_COUNT;
++                                              Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);      /* allow G2 -> G3 transition */
++                                      }
++                                      upif = NULL;
++                                      break;
++                              case (1):
++                                      prim = PH_DEACTIVATE | INDICATION;
++                                      para = 0;
++                                      hc->hw.nt_timer = 0;
++                                      hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
++                                      Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
++                                      break;
++                              case (4):
++                                      hc->hw.nt_timer = 0;
++                                      hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
++                                      Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
++                                      upif = NULL;
++                                      break;
++                              case (3):
++                                      prim = PH_ACTIVATE | INDICATION;
++                                      para = 0;
++                                      hc->hw.nt_timer = 0;
++                                      hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
++                                      Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
++                                      break;
++                              default:
++                                      break;
++                      }
++                      dch->inst.unlock(dch->inst.data);
++              }
++              while(upif) {
++                      if_link(upif, prim, para, 0, NULL, 0);
++                      upif = upif->clone;
++              }
++      }
++}
++
++/********************************/
++/* called for card init message */
++/********************************/
++
++void
++inithfcpci(hfc_pci_t *hc)
++{
++      HFC_INFO("inithfcpci: entered\n");
++      hc->dch.hw_bh = HW_hfcD_bh;
++      hc->dch.dbusytimer.function = (void *) hfcpci_dbusy_timer;
++      hc->dch.dbusytimer.data = (long) &hc->dch;
++      init_timer(&hc->dch.dbusytimer);
++      hc->chanlimit = 2;
++      mode_hfcpci(&hc->bch[0], 1, -1);
++      mode_hfcpci(&hc->bch[1], 2, -1);
++}
++
++#if 0
++/*******************************************/
++/* handle card messages from control layer */
++/*******************************************/
++static int
++hfcpci_card_msg(hfc_pci_t *hc, int mt, void *arg)
++{
++      long flags;
++
++      if (hc->debug & L1_DEB_ISAC)
++              debugl1(hc, "HFCPCI: card_msg %x", mt);
++      switch (mt) {
++              case CARD_RESET:
++                      HFC_INFO("hfcpci_card_msg: CARD_RESET\n");
++                      reset_hfcpci(hc);
++                      return (0);
++              case CARD_RELEASE:
++                      HFC_INFO("hfcpci_card_msg: CARD_RELEASE\n");
++                      release_io_hfcpci(hc);
++                      return (0);
++              case CARD_INIT:
++                      HFC_INFO("hfcpci_card_msg: CARD_INIT\n");
++                      inithfcpci(hc);
++                      save_flags(flags);
++                      sti();
++                      set_current_state(TASK_UNINTERRUPTIBLE);
++                      schedule_timeout((80 * HZ) / 1000);     /* Timeout 80ms */
++                      /* now switch timer interrupt off */
++                      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);
++                      restore_flags(flags);
++                      return (0);
++              case CARD_TEST:
++                      HFC_INFO("hfcpci_card_msg: CARD_TEST\n");
++                      return (0);
++      }
++      return (0);
++}
++
++#endif
++
++static int init_card(hfc_pci_t *hc)
++{
++      int     cnt = 3;
++
++      HFC_INFO("init_card: entered\n");
++
++      lock_dev(hc, 0);
++      if (request_irq(hc->irq, hfcpci_interrupt, SA_SHIRQ, "HFC PCI", hc)) {
++              printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
++                      hc->irq);
++              unlock_dev(hc);
++              return(-EIO);
++      }
++      while (cnt) {
++              inithfcpci(hc);
++              unlock_dev(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
++               */
++              hc->hw.int_m2 = HFCPCI_IRQ_ENABLE;
++              Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
++              /* 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 */
++              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) {
++                              return (-EIO);
++                      } else {
++                              reset_hfcpci(hc);
++                              cnt--;
++                      }
++              } else {
++                      return(0);
++              }
++              lock_dev(hc, 0);
++      }
++      unlock_dev(hc);
++      return(-EIO);
++}
++
++static int
++SelFreeBChannel(hfc_pci_t *hc, channel_info_t *ci)
++{
++      bchannel_t              *bch;
++      hfc_pci_t               *hfc;
++      mISDNstack_t            *bst;
++      u_int                   cnr;
++      struct list_head        *head;
++      
++      if (!ci)
++              return(-EINVAL);
++      ci->st.p = NULL;
++      cnr=0;
++      bst = hc->dch.inst.st;
++      if (list_empty(&bst->childlist)) {
++              if ((bst->id & FLG_CLONE_STACK) &&
++                      (bst->childlist.prev != &bst->childlist)) {
++                      head = bst->childlist.prev;
++              } else {
++                      printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n",
++                              __FUNCTION__, bst->id, bst->childlist.prev, &bst->childlist, bst->childlist.next);
++                      return(-EINVAL);
++              }
++      } else
++              head = &bst->childlist;
++      list_for_each_entry(bst, head, list) {
++              if(!bst->mgr) {
++                      int_errtxt("no mgr st(%p)", bst);
++                      return(-EINVAL);
++              }
++              hfc = bst->mgr->data;
++              if (!hfc) {
++                      int_errtxt("no mgr->data st(%p)", bst);
++                      return(-EINVAL);
++              }
++              bch = &hfc->bch[cnr & 1];
++              if (!(ci->channel & (~CHANNEL_NUMBER))) {
++                      /* only number is set */
++                      if ((ci->channel & 0x3) == (cnr + 1)) {
++                              if (bch->protocol != ISDN_PID_NONE)
++                                      return(-EBUSY);
++                              bch->channel = (cnr & 1) ? 2 : 1;
++                              ci->st.p = bst;
++                              return(0);
++                      }
++              } else if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) {
++                      if (bch->protocol == ISDN_PID_NONE) {
++                              ci->st.p = bst;
++                              bch->channel = (cnr & 1) ? 2 : 1;
++                              bch->channel |= CHANNEL_EXT_PCM;
++                              bch->channel |= (ci->channel & 0x1f) << 16;
++                              bch->channel |= (ci->channel & 0x1f) << 8;
++                              ci->st.p = bst;
++                              return(0);
++                      }
++              }
++              cnr++;
++      }
++      return(-EBUSY);
++}
++
++#define MAX_CARDS     8
++#define MODULE_PARM_T "1-8i"
++static int HFC_cnt;
++static u_int protocol[MAX_CARDS];
++static int layermask[MAX_CARDS];
++
++static mISDNobject_t  HFC_obj;
++static int debug;
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++MODULE_PARM(protocol, MODULE_PARM_T);
++
++/* short description of protocol
++ * protocol=<p1>[,p2,p3...]
++ *
++ * Values:
++ * the value has following structure
++ * <bit  3 -  0>  D-channel protocol id
++ * <bit 15 -  4>  Flags for special features
++ * <bit 31 - 16>  Spare (set to 0)
++ *
++ * D-channel protocol ids
++ * 1       1TR6 (not released yet)
++ * 2       DSS1
++ *
++ * Feature Flags
++ * bit 4   0x0010  Net side stack (NT mode)
++ * bit 5   0x0020  point to point line
++ * bit 6   0x0040  PCM slave mode
++ * bit 7   0x0080  use negativ frame pulse
++ * bit 8   0x0100  use setting from the previous HFC driver and add channels to
++ *                 the previous stack, used for the second chip in 2 chip setups
++ * bit 9   0x0200  switch DD/DU interface
++ * bit 10 - 15     reserved
++ */
++ 
++MODULE_PARM(layermask, MODULE_PARM_T);
++#endif
++
++static char HFCName[] = "HFC_PCI";
++
++/* this variable is used as card index when more than one cards are present */
++static struct pci_dev *dev_hfcpci = NULL;
++
++
++static int
++setup_hfcpci(hfc_pci_t *hc)
++{
++      char tmp[64];
++      int i=0;
++      struct pci_dev *tmp_hfcpci = NULL;
++
++#ifdef __BIG_ENDIAN
++#error "not running on big endian machines now"
++#endif
++      strcpy(tmp, hfcpci_revision);
++      printk(KERN_INFO "mISDN: HFC-PCI driver Rev. %s\n", mISDN_getrev(tmp));
++      hc->hw.cirm = 0;
++      hc->dch.ph_state = 0;
++      while (id_list[i].vendor_id) {
++              tmp_hfcpci = pci_find_device(id_list[i].vendor_id,
++                              id_list[i].device_id, dev_hfcpci);
++              i++;
++              if (tmp_hfcpci) {
++                      if (pci_enable_device(tmp_hfcpci))
++                              continue;
++                      pci_set_master(tmp_hfcpci);
++                      break;
++              }
++      }
++      if (tmp_hfcpci) {
++              i--;
++              dev_hfcpci = tmp_hfcpci;        /* old device */
++              hc->hw.dev = dev_hfcpci;
++              hc->irq = dev_hfcpci->irq;
++              if (!hc->irq) {
++                      printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
++                      return (1);
++              }
++              hc->hw.pci_io = (char *) get_pcibase(dev_hfcpci, 1);
++              printk(KERN_INFO "mISDN: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name);
++      } else {
++              printk(KERN_WARNING "HFC-PCI: No more PCI cards found\n");
++              return (1);
++      }
++      if (!hc->hw.pci_io) {
++              printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
++              return (1);
++      }
++      /* Allocate memory for FIFOS */
++      /* Because the HFC-PCI needs a 32K physical alignment, we */
++      /* need to allocate the double mem and align the address */
++      if (!(hc->hw.share_start = kmalloc(65536, GFP_KERNEL))) {
++              printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n");
++              return 1;
++      }
++      hc->hw.fifos = (void *)
++              ((((ulong) hc->hw.share_start) & ~0x7FFF) + 0x8000);
++      pci_write_config_dword(hc->hw.dev, 0x80, (u_int) virt_to_bus(hc->hw.fifos));
++      hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256);
++      printk(KERN_INFO
++              "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n",
++              (u_int) hc->hw.pci_io, (u_int) hc->hw.fifos,
++              (u_int) virt_to_bus(hc->hw.fifos),
++              hc->irq, HZ);
++      pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO);  /* enable memory mapped ports, disable busmaster */
++      hc->hw.int_m2 = 0;      /* disable alle interrupts */
++      Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
++      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);
++      lock_dev(hc, 0);
++#ifdef SPIN_DEBUG
++      printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &hc->lock.spin_adr, hc->lock.spin_adr);
++      printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &hc->lock.busy_adr, hc->lock.busy_adr);
++#endif
++      unlock_dev(hc);
++      reset_hfcpci(hc);
++      return (0);
++}
++
++static void
++release_card(hfc_pci_t *hc) {
++
++#ifdef LOCK_STATISTIC
++      printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",
++              hc->lock.try_ok, hc->lock.try_wait, hc->lock.try_mult, hc->lock.try_inirq);
++      printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
++              hc->lock.irq_ok, hc->lock.irq_fail);
++#endif
++      lock_dev(hc, 0);
++      free_irq(hc->irq, hc);
++      mode_hfcpci(&hc->bch[0], 1, ISDN_PID_NONE);
++      mode_hfcpci(&hc->bch[1], 2, ISDN_PID_NONE);
++      if (hc->dch.dbusytimer.function != NULL) {
++              del_timer(&hc->dch.dbusytimer);
++              hc->dch.dbusytimer.function = NULL;
++      }
++      release_io_hfcpci(hc);
++      mISDN_free_bch(&hc->bch[1]);
++      mISDN_free_bch(&hc->bch[0]);
++      mISDN_free_dch(&hc->dch);
++      HFC_obj.ctrl(hc->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &hc->dch.inst.up);
++      HFC_obj.ctrl(&hc->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
++      list_del(&hc->list);
++      unlock_dev(hc);
++      kfree(hc);
++}
++
++static int
++HFC_manager(void *data, u_int prim, void *arg) {
++      hfc_pci_t       *card;
++      mISDNinstance_t *inst = data;
++      struct sk_buff  *skb;
++      int             channel = -1;
++
++      if (!data) {
++              MGR_HASPROTOCOL_HANDLER(prim,arg,&HFC_obj)
++              printk(KERN_ERR "%s: no data prim %x arg %p\n",
++                      __FUNCTION__, prim, arg);
++              return(-EINVAL);
++      }
++      list_for_each_entry(card, &HFC_obj.ilist, list) {
++              if (&card->dch.inst == inst) {
++                      channel = 2;
++                      break;
++              }
++              if (&card->bch[0].inst == inst) {
++                      channel = 0;
++                      break;
++              }
++              if (&card->bch[1].inst == inst) {
++                      inst = &card->bch[1].inst;
++                      channel = 1;
++                      break;
++              }
++      }
++      if (channel<0) {
++              printk(KERN_ERR "%s: no channel data %p prim %x arg %p\n",
++                      __FUNCTION__, data, prim, arg);
++              return(-EINVAL);
++      }
++
++      switch(prim) {
++          case MGR_REGLAYER | CONFIRM:
++              if (channel == 2)
++                      dch_set_para(&card->dch, &inst->st->para);
++              else
++                      bch_set_para(&card->bch[channel], &inst->st->para);
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++              if (channel == 2) {
++                      inst->down.fdata = &card->dch;
++                      if ((skb = create_link_skb(PH_CONTROL | REQUEST,
++                              HW_DEACTIVATE, 0, NULL, 0))) {
++                              if (HFCD_l1hw(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              } else {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (hfcpci_l2l1(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              }
++              HFC_obj.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
++              HFC_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++              break;
++          case MGR_CLRSTPARA | INDICATION:
++              arg = NULL;
++          case MGR_ADDSTPARA | INDICATION:
++              if (channel == 2)
++                      dch_set_para(&card->dch, arg);
++              else
++                      bch_set_para(&card->bch[channel], arg);
++              break;
++          case MGR_RELEASE | INDICATION:
++              if (channel == 2) {
++                      release_card(card);
++              } else {
++                      HFC_obj.refcnt--;
++              }
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              if (channel==2)
++                      return(mISDN_SetIF(inst, arg, prim, HFCD_l1hw, NULL,
++                              &card->dch));
++              else
++                      return(mISDN_SetIF(inst, arg, prim, hfcpci_l2l1, NULL,
++                              &card->bch[channel]));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_SELCHANNEL | REQUEST:
++              if (channel != 2) {
++                      printk(KERN_WARNING "%s: selchannel not dinst\n",
++                              __FUNCTION__);
++                      return(-EINVAL);
++              }
++              return(SelFreeBChannel(card, arg));
++          case MGR_SETSTACK | CONFIRM:
++              if ((channel!=2) && (inst->pid.global == 2)) {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(PH_ACTIVATE | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (hfcpci_l2l1(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++                      if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
++                              if_link(&inst->up, DL_ESTABLISH | INDICATION,
++                                      0, 0, NULL, 0);
++                      else
++                              if_link(&inst->up, PH_ACTIVATE | INDICATION,
++                                      0, 0, NULL, 0);
++              }
++              break;
++          PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
++          PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST);
++          default:
++              printk(KERN_WARNING "%s: prim %x not handled\n",
++                      __FUNCTION__, prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static int __init HFC_init(void)
++{
++      int             err,i;
++      hfc_pci_t       *card, *prev;
++      mISDN_pid_t     pid;
++      mISDNstack_t    *dst;
++
++#ifdef MODULE
++      HFC_obj.owner = THIS_MODULE;
++#endif
++      INIT_LIST_HEAD(&HFC_obj.ilist);
++      HFC_obj.name = HFCName;
++      HFC_obj.own_ctrl = HFC_manager;
++      HFC_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 |
++                                   ISDN_PID_L0_NT_S0;
++      HFC_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0;
++      HFC_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS |
++                                   ISDN_PID_L1_B_64HDLC;
++      HFC_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS |
++                                   ISDN_PID_L2_B_RAWDEV;
++      if ((err = mISDN_register(&HFC_obj))) {
++              printk(KERN_ERR "Can't register HFC PCI error(%d)\n", err);
++              return(err);
++      }
++      while (HFC_cnt < MAX_CARDS) {
++              if (!(card = kmalloc(sizeof(hfc_pci_t), GFP_ATOMIC))) {
++                      printk(KERN_ERR "No kmem for HFCcard\n");
++                      mISDN_unregister(&HFC_obj);
++                      return(-ENOMEM);
++              }
++              memset(card, 0, sizeof(hfc_pci_t));
++              list_add_tail(&card->list, &HFC_obj.ilist);
++              card->dch.debug = debug;
++              lock_HW_init(&card->lock);
++              card->dch.inst.lock = lock_dev;
++              card->dch.inst.unlock = unlock_dev;
++              mISDN_init_instance(&card->dch.inst, &HFC_obj, card);
++              card->dch.inst.pid.layermask = ISDN_LAYER(0);
++              sprintf(card->dch.inst.name, "HFC%d", HFC_cnt+1);
++              mISDN_init_dch(&card->dch);
++              for (i=0; i<2; i++) {
++                      card->bch[i].channel = i + 1;
++                      mISDN_init_instance(&card->bch[i].inst, &HFC_obj, card);
++                      card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
++                      card->bch[i].inst.lock = lock_dev;
++                      card->bch[i].inst.unlock = unlock_dev;
++                      card->bch[i].debug = debug;
++                      sprintf(card->bch[i].inst.name, "%s B%d",
++                              card->dch.inst.name, i+1);
++                      mISDN_init_bch(&card->bch[i]);
++                      if (card->bch[i].dev) {
++                              card->bch[i].dev->wport.pif.func =
++                                      hfcpci_l2l1;
++                              card->bch[i].dev->wport.pif.fdata =
++                                      &card->bch[i];
++                      }
++              }
++              if (protocol[HFC_cnt] == 0x100) {
++                      if (card->list.prev == &HFC_obj.ilist)
++                              prev = NULL;
++                      else
++                              prev = list_entry(card->list.prev, hfc_pci_t, list);
++
++                      if (!prev) {
++                              int_errtxt("card(%d) no previous HFC",
++                                      HFC_cnt);
++                              if (!HFC_cnt)
++                                      mISDN_unregister(&HFC_obj);
++                              else
++                                      err = 0;
++                              return(err);
++                      }
++                      i = HFC_cnt - 1;
++                      test_and_set_bit(HFC_CFG_2HFC, &prev->cfg);
++                      test_and_set_bit(HFC_CFG_2HFC, &card->cfg);
++                      test_and_set_bit(HFC_CFG_SLAVEHFC, &card->cfg);
++              } else {
++                      prev = NULL;
++                      i = HFC_cnt;
++              }
++              mISDN_set_dchannel_pid(&pid, protocol[i], layermask[i]);
++              test_and_set_bit(HFC_CFG_MASTER, &card->cfg);
++              if (protocol[i] & 0x10) {
++                      card->dch.inst.pid.protocol[0] = ISDN_PID_L0_NT_S0;
++                      card->dch.inst.pid.protocol[1] = ISDN_PID_L1_NT_S0;
++                      pid.protocol[0] = ISDN_PID_L0_NT_S0;
++                      pid.protocol[1] = ISDN_PID_L1_NT_S0;
++                      card->dch.inst.pid.layermask |= ISDN_LAYER(1);
++                      pid.layermask |= ISDN_LAYER(1);
++                      if (layermask[i] & ISDN_LAYER(2))
++                              pid.protocol[2] = ISDN_PID_L2_LAPD_NET;
++                      card->hw.nt_mode = 1;
++              } else {
++                      card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
++                      card->hw.nt_mode = 0;
++              }
++              if (protocol[i] & 0x40) {
++                      if (pid.layermask & ISDN_LAYER(3))
++                              pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID;
++                      test_and_set_bit(HFC_CFG_PCM, &card->cfg);
++                      test_and_set_bit(HFC_CFG_SLAVE, &card->cfg);
++                      test_and_clear_bit(HFC_CFG_MASTER, &card->cfg);
++              }
++              if (protocol[i] & 0x80) {
++                      test_and_set_bit(HFC_CFG_NEG_F0, &card->cfg);
++              }
++              if (protocol[i] & 0x200) {
++                      test_and_set_bit(HFC_CFG_SW_DD_DU, &card->cfg);
++              }
++              printk(KERN_DEBUG "HFC card %p dch %p bch1 %p bch2 %p\n",
++                      card, &card->dch, &card->bch[0], &card->bch[1]);
++              if (setup_hfcpci(card)) {
++                      err = 0;
++                      mISDN_free_dch(&card->dch);
++                      mISDN_free_bch(&card->bch[1]);
++                      mISDN_free_bch(&card->bch[0]);
++                      list_del(&card->list);
++                      kfree(card);
++                      if (!HFC_cnt) {
++                              mISDN_unregister(&HFC_obj);
++                              err = -ENODEV;
++                      } else
++                              printk(KERN_INFO "HFC %d cards installed\n",
++                                      HFC_cnt);
++                      return(err);
++              }
++              HFC_cnt++;
++              if (prev) {
++                      dst = prev->dch.inst.st;
++              } else {
++                      if ((err = HFC_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST,
++                              &card->dch.inst))) {
++                              printk(KERN_ERR  "MGR_ADDSTACK REQUEST dch err(%d)\n", err);
++                              release_card(card);
++                              if (!HFC_cnt)
++                                      mISDN_unregister(&HFC_obj);
++                              else
++                                      err = 0;
++                              return(err);
++                      }
++                      dst = card->dch.inst.st;
++              }
++              for (i = 0; i < 2; i++) {
++                      if ((err = HFC_obj.ctrl(dst,
++                              MGR_NEWSTACK | REQUEST, &card->bch[i].inst))) {
++                              printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
++                              HFC_obj.ctrl(card->dch.inst.st,
++                                      MGR_DELSTACK | REQUEST, NULL);
++                              if (!HFC_cnt)
++                                      mISDN_unregister(&HFC_obj);
++                              else
++                                      err = 0;
++                              return(err);
++                      }
++                      card->bch[i].st = card->bch[i].inst.st;
++              }
++              if (protocol[HFC_cnt] != 0x100) { /* next not second HFC */
++                      if ((err = HFC_obj.ctrl(dst, MGR_SETSTACK | REQUEST,
++                              &pid))) {
++                              printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n",
++                                      err);
++                              HFC_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL);
++                              if (!HFC_cnt)
++                                      mISDN_unregister(&HFC_obj);
++                              else
++                                      err = 0;
++                              return(err);
++                      }
++              }
++              if ((err = init_card(card))) {
++                      HFC_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL);
++                      if (!HFC_cnt)
++                              mISDN_unregister(&HFC_obj);
++                      else
++                              err = 0;
++                      return(err);
++              }
++              HFC_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL);
++      }
++      printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt);
++      return(0);
++}
++
++#ifdef MODULE
++static void __exit HFC_cleanup(void)
++{
++      hfc_pci_t       *card, *next;
++      int             err;
++
++      if ((err = mISDN_unregister(&HFC_obj))) {
++              printk(KERN_ERR "Can't unregister HFC PCI error(%d)\n", err);
++      }
++      list_for_each_entry_safe(card, next, &HFC_obj.ilist, list) {
++              printk(KERN_ERR "HFC PCI card struct not empty refs %d\n",
++                      HFC_obj.refcnt);
++              release_card(card);
++      }
++      return;
++}
++
++module_init(HFC_init);
++module_exit(HFC_cleanup);
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hfc_pci.h    1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hfc_pci.h        2004-11-22 09:33:38.179739712 +0000
+@@ -0,0 +1,243 @@
++/* $Id$
++ *
++ *  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_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
++
++typedef struct {
++      unsigned short z1;  /* Z1 pointer 16 Bit */
++      unsigned short z2;  /* Z2 pointer 16 Bit */
++} z_type;
++
++typedef struct {
++      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 */
++      z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */
++      u_char fill3[0x4000-0x2100]; /* align 16K */  
++} dfifo_type;
++
++typedef struct {
++      z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ 
++      u_char f1,f2; /* f pointers */
++      u_char fill[0x2100-0x2082]; /* alignment */
++} bzfifo_type;
++
++
++typedef union {
++      struct { 
++              dfifo_type d_tx; /* D-send channel */
++              dfifo_type d_rx; /* D-receive channel */
++      } d_chan; 
++      struct {
++              u_char fill1[0x200];
++              u_char txdat_b1[B_FIFO_SIZE];
++              bzfifo_type txbz_b1;
++              bzfifo_type txbz_b2;
++              u_char txdat_b2[B_FIFO_SIZE];
++              u_char fill2[D_FIFO_SIZE];
++              u_char rxdat_b1[B_FIFO_SIZE];
++              bzfifo_type rxbz_b1;
++              bzfifo_type rxbz_b2;
++              u_char rxdat_b2[B_FIFO_SIZE];
++      } b_chans;  
++      u_char fill[32768]; 
++} fifo_area;
++
++#define Write_hfc(a,b,c) (*(((u_char *)a->hw.pci_io)+b) = c) 
++#define Read_hfc(a,b) (*(((u_char *)a->hw.pci_io)+b))
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hw_lock.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/hw_lock.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/hw_lock.h    1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/hw_lock.h        2004-11-22 09:33:38.189738192 +0000
+@@ -0,0 +1,182 @@
++/* $Id$
++ *
++ * hw_lock.h  Hardware locking inline routines
++ *
++ * Author Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++*/
++
++/* Description of the locking mechanism
++ * 
++ * The locking must grant serialisized and atomic
++ * access to the ISDN hardware registers, if the lock
++ * is aquired no other process or IRQ is alloed to
++ * access ISDN hardware registers.
++ *
++ * In general here are 3 possible entry points:
++ *    1. the ISDN interrupt routine
++ *    2. ISDN timer routines in the hardware module
++ *    3. messages that came from upper layers
++ *
++ * Since most work must be do in the interrupt routine
++ * (to grant minimum IRQ latency) and only few things with
++ * need direct HW access must be done for messages from upper
++ * layers, we should allow other IRQs in our IRQ routines and
++ * only block our own routines in this case. Since the common IRQ
++ * routines allready mask the same IRQ, we only need to protect us
++ * from timer and uper layers. The disadvantage is, that we need to
++ * disable local IRQ for the 2. and 3. points, but since the routines
++ * which need hardware access are well known and small, the impact
++ * is very small.
++ *
++ * We have a two stage locking to make this working:
++ * A spinlock which protect the state LOCK Flag (STATE_FLAG_BUSY) and
++ * also protect us from local IRQs from the entry points 2 and 3.
++ *
++ * In the hardware IRQ we aquire the spinlock, set the STATE_FLAG_BUSY
++ * LOCK Flag and then release the spinlock. It can never happen that
++ * the STATE_FLAG_BUSY is allready set in this case, see later.
++ *
++ * In the other cases (from timer or upper layers) we aquire the spinlock
++ * test_and_set the STATE_FLAG_BUSY LOCK Flag, if it was allready set
++ * (a ISDN IRQ is running on the other CPU) we schedule timeout or add a other
++ * small timeout.
++ * If it was not set, we have the lock and we don't release the spinlock until we have
++ * done the harware work.
++ *
++ * To avoid any kind of deadlocking, it is important that we release the lock
++ * before we call functions that deliver to upper layers.
++ * To leave the impact of disabled local IRQ small, it is important to only protect
++ * small areas where hardware is accessed.
++ *
++ * The following routines handle the lock in the entry point from upper layers and other
++ * none IRQ cases (module init/exit stuff).
++ *
++ * They never called directly, but via the wrappers assigned to the instance
++ * inst.lock / inst.unlock pointers.
++ *
++ * Here are two defines which can be used for DEBUGING and PROFILING
++ * SPIN_DEBUG and LOCK_STATISTIC
++ *
++ */
++#ifndef __hw_lock__
++#define __hw_lock__
++
++typedef struct _mISDN_HWlock {
++      u_long                  flags;
++      spinlock_t              lock;
++#ifdef SPIN_DEBUG
++      void                    *spin_adr;
++      void                    *busy_adr;
++#endif
++      volatile u_long         state;
++#ifdef LOCK_STATISTIC
++      u_int                   try_ok;
++      u_int                   try_wait;
++      u_int                   try_inirq;
++      u_int                   try_mult;
++      u_int                   irq_ok;
++      u_int                   irq_fail;
++#endif
++} mISDN_HWlock_t;
++
++#define STATE_FLAG_BUSY               1
++#define STATE_FLAG_INIRQ      2
++
++
++/*
++ * returns 0 if the lock was aquired
++ * returns 1 if nowait != 0 and the lock is not aquired 
++ */
++static inline int lock_HW(mISDN_HWlock_t *lock, int nowait)
++{
++      register u_long flags;
++#ifdef LOCK_STATISTIC
++      int     wait = 0;
++#endif
++
++      spin_lock_irqsave(&lock->lock, flags);
++#ifdef SPIN_DEBUG
++      lock->spin_adr = __builtin_return_address(0);
++#endif
++      while (test_and_set_bit(STATE_FLAG_BUSY, &lock->state)) {
++              /* allready busy so we delay */
++              spin_unlock_irqrestore(&lock->lock, flags);
++#ifdef SPIN_DEBUG
++              lock->spin_adr = NULL;
++#endif
++              if (nowait) {
++#ifdef LOCK_STATISTIC
++                      lock->try_wait++;
++#endif 
++                      return(1);
++              }
++              /* delay 1 jiffie is enought */
++              if (in_interrupt()) {
++                      /* Should never happen */
++#ifdef LOCK_STATISTIC
++                      lock->try_inirq++;
++#endif
++                      printk(KERN_ERR "lock_HW: try to schedule in IRQ state(%lx)\n",
++                              lock->state);
++                      mdelay(1);
++              } else {
++#ifdef LOCK_STATISTIC
++                      if (wait++)
++                              lock->try_mult++;
++                      else
++                              lock->try_wait++;
++#endif
++                      schedule_timeout(1);
++              }
++              spin_lock_irqsave(&lock->lock, flags);
++#ifdef SPIN_DEBUG
++              lock->spin_adr = __builtin_return_address(0);
++#endif
++      }       
++      /* get the LOCK */
++      lock->flags = flags;
++#ifdef SPIN_DEBUG
++      lock->busy_adr = __builtin_return_address(0);
++#endif
++#ifdef LOCK_STATISTIC
++      if (!wait)
++              lock->try_ok++;
++#endif
++      return(0);
++} 
++
++static inline void unlock_HW(mISDN_HWlock_t *lock)
++{
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &lock->state)) {
++              printk(KERN_ERR "lock_HW: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      lock->state);
++      }
++#ifdef SPIN_DEBUG
++      lock->busy_adr = NULL;
++      lock->spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&lock->lock, lock->flags);
++}
++
++static inline void lock_HW_init(mISDN_HWlock_t *lock)
++{
++      spin_lock_init(&lock->lock);
++      lock->state = 0;
++#ifdef SPIN_DEBUG
++      lock->busy_adr = NULL;
++      lock->spin_adr = NULL;
++#endif
++#ifdef LOCK_STATISTIC
++      lock->try_ok = 0;
++      lock->try_wait = 0;
++      lock->try_inirq = 0;
++      lock->try_mult = 0;
++      lock->irq_ok = 0;
++      lock->irq_fail = 0;
++#endif
++}
++
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/i4l_mISDN.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/i4l_mISDN.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/i4l_mISDN.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/i4l_mISDN.c      2004-11-22 09:33:38.199736672 +0000
+@@ -0,0 +1,1587 @@
++/* $Id$
++ *
++ * interface for old I4L hardware drivers to the CAPI driver
++ *
++ * Copyright  (C) 2003 Karsten Keil (kkeil@suse.de)
++ *
++ * Author     Karsten Keil (kkeil@suse.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/isdnif.h>
++#include <linux/delay.h>
++#include <asm/semaphore.h>
++#include <linux/mISDNif.h>
++#include "fsm.h"
++#include "helper.h"
++#include "dss1.h"
++#include "debug.h"
++
++static char *i4lcapi_revision = "$Revision$";
++
++/* data struct */
++typedef struct _i4l_channel   i4l_channel_t;
++typedef struct _i4l_capi      i4l_capi_t;
++
++struct _i4l_channel {
++      mISDNinstance_t         inst;
++      i4l_capi_t              *drv;
++      int                     nr;
++      u_int                   Flags;
++      int                     cause_loc;
++      int                     cause_val;
++      u_int                   l4id;
++      struct FsmInst          i4lm;
++      struct sk_buff_head     sendq;
++      struct sk_buff_head     ackq;
++};
++
++struct _i4l_capi {
++      i4l_capi_t              *prev;
++      i4l_capi_t              *next;
++      isdn_if                 *interface;
++      mISDNinstance_t         inst;
++      mISDN_pid_t             pid;
++      int                     idx;
++      int                     locks;
++      int                     debug;
++      int                     nr_ch;
++      i4l_channel_t           *ch;
++};
++
++#define I4L_FLG_LOCK          0
++#define       I4L_FLG_L1TRANS         1
++#define I4L_FLG_L1HDLC                2
++#define I4L_FLG_LAYER1                3
++#define I4L_FLG_BREADY                4
++#define I4L_FLG_BCONN         5
++#define       I4L_FLG_HANGUP          6
++
++static
++struct Fsm i4lfsm_s =
++{NULL, 0, 0, NULL, NULL};
++
++enum {
++      ST_NULL,
++      ST_ICALL,
++      ST_OCALL,
++      ST_PROCEED,
++      ST_ALERT,
++      ST_WAITDCONN,
++      ST_ACTIVD,
++      ST_BREADY,
++      ST_ACTIVB,
++      ST_HANGUP,
++};
++
++#define STATE_COUNT (ST_HANGUP+1)
++
++static char *strI4LState[] =
++{
++      "ST_NULL",
++      "ST_ICALL",
++      "ST_OCALL",
++      "ST_PROCEED",
++      "ST_ALERT",
++      "ST_WAITDCONN",
++      "ST_ACTIVD",
++      "ST_BREADY",
++      "ST_ACTIVB",
++      "ST_HANGUP",
++};
++
++enum {
++      EV_I4L_ICALL,
++      EV_I4L_DCONN,
++      EV_I4L_BCONN,
++      EV_I4L_DHUP,
++      EV_I4L_BHUP,
++      EV_I4L_L1ERR,
++      EV_STACKREADY,
++      EV_CAPI_OCALL,
++      EV_CAPI_ALERT,
++      EV_CAPI_PROCEED,
++      EV_CAPI_DCONNECT,
++      EV_CAPI_ESTABLISHB,
++      EV_CAPI_RELEASEB,
++      EV_CAPI_DISCONNECT,
++      EV_CAPI_RELEASE,
++};
++
++#define EVENT_COUNT (EV_CAPI_RELEASE + 1)
++
++static char *strI4LEvent[] =
++{
++      "EV_I4L_ICALL",
++      "EV_I4L_DCONN",
++      "EV_I4L_BCONN",
++      "EV_I4L_DHUP",
++      "EV_I4L_BHUP",
++      "EV_I4L_L1ERR",
++      "EV_STACKREADY",
++      "EV_CAPI_OCALL",
++      "EV_CAPI_ALERT",
++      "EV_CAPI_PROCEED",
++      "EV_CAPI_DCONNECT",
++      "EV_CAPI_ESTABLISHB",
++      "EV_CAPI_RELEASEB",
++      "EV_CAPI_DISCONNECT",
++      "EV_CAPI_RELEASE",
++};
++
++static void
++i4lm_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      logdata_t       log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = ch->inst.name;
++      ch->inst.obj->ctrl(&ch->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++#define MAX_CARDS     8
++static i4l_capi_t     *drvmap[MAX_CARDS];
++static mISDNobject_t  I4Lcapi;
++
++static int debug;
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++#endif
++
++static void
++i4l_lock_drv(i4l_capi_t *ic)
++{
++      isdn_ctrl cmd;
++
++      cmd.driver = ic->idx;
++      cmd.arg = 0;
++      cmd.command = ISDN_CMD_LOCK;
++      ic->interface->command(&cmd);
++      ic->locks++;
++}
++
++static void
++i4l_unlock_drv(i4l_capi_t *ic)
++{
++      isdn_ctrl cmd;
++
++      cmd.driver = ic->idx;
++      cmd.arg = 0;
++      cmd.command = ISDN_CMD_UNLOCK;
++      ic->interface->command(&cmd);
++      ic->locks--;
++}
++
++static int
++i4l_cmd(i4l_capi_t *ic, int arg, int cmd)
++{
++      isdn_ctrl ctrl;
++
++      ctrl.driver = ic->idx;
++      ctrl.arg = arg;
++      ctrl.command = cmd;
++      return(ic->interface->command(&ctrl));
++}
++
++static void
++init_channel(i4l_capi_t *ic, int nr)
++{
++      i4l_channel_t   *ch;
++
++      ch = ic->ch + nr;
++      memset(ch, 0, sizeof(i4l_channel_t));
++      ch->nr = nr;
++      ch->drv = ic;
++      ch->i4lm.debug = debug & 0x8;
++      ch->i4lm.userdata = ch;
++      ch->i4lm.userint = 0;
++      ch->i4lm.printdebug = i4lm_debug;
++      ch->i4lm.fsm = &i4lfsm_s;
++      ch->i4lm.state = ST_NULL;
++      skb_queue_head_init(&ch->sendq);
++      skb_queue_head_init(&ch->ackq);
++      ch->inst.obj = &I4Lcapi;
++      ch->inst.data = ch;
++      ch->inst.pid.layermask = ISDN_LAYER(0);
++      ch->inst.up.owner = &ch->inst;
++      ch->inst.down.owner = &ch->inst;
++      I4Lcapi.ctrl(NULL, MGR_DISCONNECT | REQUEST, &ch->inst.down);
++      sprintf(ch->inst.name, "%s B%d", ic->inst.name, nr+1);
++}
++
++static void
++reset_channel(i4l_channel_t *ch)
++{
++      ch->cause_loc = 0;
++      ch->cause_val = 0;
++      ch->l4id = 0;
++      skb_queue_purge(&ch->sendq);
++      skb_queue_purge(&ch->ackq);
++      if (test_and_clear_bit(I4L_FLG_LOCK, &ch->Flags))
++              i4l_unlock_drv(ch->drv);
++      ch->Flags = 0;
++}
++
++static void
++release_card(int idx) {
++      i4l_capi_t      *ic = drvmap[idx];
++      i4l_channel_t   *ch;
++      int             i;
++      mISDNinstance_t *inst;
++
++      if (!ic)
++              return;
++      drvmap[idx] = NULL;
++      ch = ic->ch;
++      for (i=0; i<ic->nr_ch; i++) {
++              inst = &ch->inst;
++              if (inst->up.peer)
++                      inst->up.peer->obj->ctrl(inst->up.peer,
++                              MGR_DISCONNECT | REQUEST, &inst->up);
++              reset_channel(ch);
++              ch++;
++      }
++      inst = &ic->inst;
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      REMOVE_FROM_LISTBASE(ic, ((i4l_capi_t *)I4Lcapi.ilist));
++      while (ic->locks > 0)
++              i4l_unlock_drv(ic);
++      kfree(ic->ch);
++      ic->ch = NULL;
++      kfree(ic);
++      I4Lcapi.refcnt--;
++}
++
++static int
++sendup(i4l_channel_t *ch, int Dchannel, int prim, struct sk_buff *skb)
++{
++      int             ret;
++      mISDN_headext_t *hhe;
++      mISDNinstance_t *I;
++
++      if (!skb) {
++              skb = alloc_skb(8, GFP_ATOMIC);
++              if (!skb)
++                      return(-ENOMEM);
++      }
++      hhe = mISDN_HEADEXT_P(skb);
++      hhe->prim = prim;
++      hhe->dinfo = ch->l4id;
++      if (ch->drv->debug & 0x4)
++              mISDN_LogL3Msg(skb);
++      if (Dchannel)
++              I = &ch->drv->inst;
++      else
++              I = &ch->inst;
++      if (!I->up.func) {
++              int_error();
++              dev_kfree_skb(skb);
++              return(-EUNATCH);
++      }
++      if (in_interrupt()) {
++              hhe->func.iff = I->up.func;
++              hhe->data[0] = &I->up;
++              ret = I->obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb);
++      } else
++              ret = I->up.func(&I->up, skb);
++      if (ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++static int
++sendqueued(i4l_channel_t *ch)
++{
++      struct sk_buff  *skb, *s_skb;
++      int             len, ret;
++
++      if (!test_bit(I4L_FLG_BCONN, &ch->Flags)) {
++              if (ch->drv->debug & 0x40)
++                      printk(KERN_DEBUG "%s: bc%d not ready\n", __FUNCTION__, ch->nr);
++              return(0);
++      }
++      while ((skb = skb_dequeue(&ch->sendq))) {
++              s_skb = skb_clone(skb, GFP_ATOMIC);
++              len = s_skb->len;
++              skb_queue_tail(&ch->ackq, skb);
++              ret = ch->drv->interface->writebuf_skb(ch->drv->idx, ch->nr, 1, s_skb);
++              if (ch->drv->debug & 0x800)
++                      printk(KERN_DEBUG "bc%d sent skb(%p) %d(%d)\n", ch->nr, skb, ret, len);
++              if (ret == len) {
++                      continue;
++              } else if (ret > 0) {
++                      skb_queue_head(&ch->sendq, s_skb);
++                      break;
++              } else {
++                      skb_unlink(skb);
++                      skb_queue_head(&ch->sendq, skb);
++                      if (!ret)
++                              dev_kfree_skb(s_skb);
++                      break;
++              }
++      }
++      return(0);
++}
++
++static u_char *
++EncodeASyncParams(u_char * p, u_char si2)
++{                             // 7c 06 88  90 21 42 00 bb
++      p[0] = 0;
++      p[1] = 0x40;            // Intermediate rate: 16 kbit/s jj 2000.02.19
++      p[2] = 0x80;
++      if (si2 & 32)           // 7 data bits
++              p[2] += 16;
++      else                    // 8 data bits
++              p[2] += 24;
++
++      if (si2 & 16)           // 2 stop bits
++              p[2] += 96;
++      else                    // 1 stop bit
++              p[2] += 32;
++      if (si2 & 8)            // even parity
++              p[2] += 2;
++      else                    // no parity
++              p[2] += 3;
++      switch (si2 & 0x07) {
++              case 0:
++                      p[0] = 66;      // 1200 bit/s
++                      break;
++              case 1:
++                      p[0] = 88;      // 1200/75 bit/s
++                      break;
++              case 2:
++                      p[0] = 87;      // 75/1200 bit/s
++                      break;
++              case 3:
++                      p[0] = 67;      // 2400 bit/s
++                      break;
++              case 4:
++                      p[0] = 69;      // 4800 bit/s
++                      break;
++              case 5:
++                      p[0] = 72;      // 9600 bit/s
++                      break;
++              case 6:
++                      p[0] = 73;      // 14400 bit/s
++                      break;
++              case 7:
++                      p[0] = 75;      // 19200 bit/s
++                      break;
++      }
++      return p + 3;
++}
++
++static  u_char
++EncodeSyncParams(u_char si2, u_char ai)
++{
++      switch (si2) {
++              case 0:
++                      return ai + 2;  // 1200 bit/s
++              case 1:
++                      return ai + 24; // 1200/75 bit/s
++              case 2:
++                      return ai + 23; // 75/1200 bit/s
++              case 3:
++                      return ai + 3;  // 2400 bit/s
++              case 4:
++                      return ai + 5;  // 4800 bit/s
++              case 5:
++                      return ai + 8;  // 9600 bit/s
++              case 6:
++                      return ai + 9;  // 14400 bit/s
++              case 7:
++                      return ai + 11; // 19200 bit/s
++              case 8:
++                      return ai + 14; // 48000 bit/s
++              case 9:
++                      return ai + 15; // 56000 bit/s
++              case 15:
++                      return ai + 40; // negotiate bit/s
++              default:
++                      break;
++      }
++      return ai;
++}
++
++static u_char
++DecodeASyncParams(u_char si2, u_char * p)
++{
++      u_char info;
++
++      switch (p[5]) {
++              case 66:        // 1200 bit/s
++                      break;  // si2 don't change
++              case 88:        // 1200/75 bit/s
++                      si2 += 1;
++                      break;
++              case 87:        // 75/1200 bit/s
++                      si2 += 2;
++                      break;
++              case 67:        // 2400 bit/s
++                      si2 += 3;
++                      break;
++              case 69:        // 4800 bit/s
++                      si2 += 4;
++                      break;
++              case 72:        // 9600 bit/s
++                      si2 += 5;
++                      break;
++              case 73:        // 14400 bit/s
++                      si2 += 6;
++                      break;
++              case 75:        // 19200 bit/s
++                      si2 += 7;
++                      break;
++      }
++      info = p[7] & 0x7f;
++      if ((info & 16) && (!(info & 8)))       // 7 data bits
++              si2 += 32;      // else 8 data bits
++      if ((info & 96) == 96)  // 2 stop bits
++              si2 += 16;      // else 1 stop bit
++      if ((info & 2) && (!(info & 1)))        // even parity
++              si2 += 8;       // else no parity
++      return si2;
++}
++
++
++static u_char
++DecodeSyncParams(u_char si2, u_char info)
++{
++      switch (info & 0x7f) {
++              case 40:        // bit/s negotiation failed  ai := 165 not 175!
++                      return si2 + 15;
++              case 15:        // 56000 bit/s failed, ai := 0 not 169 !
++                      return si2 + 9;
++              case 14:        // 48000 bit/s
++                      return si2 + 8;
++              case 11:        // 19200 bit/s
++                      return si2 + 7;
++              case 9: // 14400 bit/s
++                      return si2 + 6;
++              case 8: // 9600  bit/s
++                      return si2 + 5;
++              case 5: // 4800  bit/s
++                      return si2 + 4;
++              case 3: // 2400  bit/s
++                      return si2 + 3;
++              case 23:        // 75/1200 bit/s
++                      return si2 + 2;
++              case 24:        // 1200/75 bit/s
++                      return si2 + 1;
++              default:        // 1200 bit/s
++                      return si2;
++      }
++}
++
++static u_char
++DecodeSI2(u_char *p)
++{
++
++      if (p) {
++              switch (p[4] & 0x0f) {
++                      case 0x01:
++                              if (p[1] == 0x04)       // sync. Bitratenadaption
++                                      return DecodeSyncParams(160, p[5]);     // V.110/X.30
++                              else if (p[1] == 0x06)  // async. Bitratenadaption
++                                      return DecodeASyncParams(192, p);       // V.110/X.30
++                              break;
++                      case 0x08:      // if (p[5] == 0x02) // sync. Bitratenadaption
++                              if (p[1] > 3) 
++                                      return DecodeSyncParams(176, p[5]);     // V.120
++                              break;
++              }
++      }
++      return 0;
++}
++
++static void
++i4l_l1err(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++
++      sendup(ch, 1, DL_RELEASE | INDICATION, NULL);
++      reset_channel(ch);
++      mISDN_FsmChangeState(fi, ST_NULL);
++}
++
++static void
++i4l_dhup(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      struct sk_buff  *skb;
++      u_char          tmp[8];
++
++      skb = mISDN_alloc_l3msg(8, MT_RELEASE);
++      if (!skb)
++              return;
++
++      tmp[0] = IE_CAUSE;
++      tmp[1] = 2;
++      if (ch->cause_val) {
++              tmp[2] = ch->cause_loc;
++              tmp[3] = ch->cause_val;
++      } else {
++              tmp[2] = 0x80;
++              tmp[3] = 0x9f; /* normal, unspecified */
++      }
++      mISDN_AddvarIE(skb, tmp);
++      sendup(ch, 1, CC_RELEASE | INDICATION, skb);
++      reset_channel(ch);
++      mISDN_FsmChangeState(fi, ST_NULL);
++}
++
++static void
++i4l_icall(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      setup_parm      *setup = arg;
++      u_char          tmp[36], *p;
++      struct sk_buff  *skb;
++      int             i,j;
++
++      test_and_set_bit(I4L_FLG_LOCK, &ch->Flags);
++      i4l_lock_drv(ch->drv);
++      if ((skb = alloc_skb(sizeof(int *) + 8, GFP_ATOMIC))) {
++              int     **idp = (int **)skb_put(skb, sizeof(idp));
++              mISDN_head_t *hh = mISDN_HEAD_P(skb);
++
++              *idp = &ch->l4id;
++              hh->prim = CC_NEW_CR | INDICATION;
++              i = ch->drv->inst.up.func(&ch->drv->inst.up, skb);
++              if (i) {
++                      int_error();
++                      dev_kfree_skb(skb);
++                      return;
++              }
++              if (ch->drv->debug & 0x2)
++                      printk(KERN_DEBUG "%s: l4id(%x) ch(%p)->nr %d\n", __FUNCTION__, ch->l4id, ch, ch->nr);
++      } else
++              return;
++      skb = mISDN_alloc_l3msg(260, MT_SETUP);
++      if (!skb)
++              return;
++      p = tmp;
++        switch (setup->si1) {
++              case 1:                 /* Telephony                        */
++                      *p++ = IE_BEARER;
++                      *p++ = 0x3;     /* Length                           */
++                      *p++ = 0x90;    /* Coding Std. CCITT, 3.1 kHz audio */
++                      *p++ = 0x90;    /* Circuit-Mode 64kbps              */
++                      *p++ = 0xa3;    /* A-Law Audio                      */
++                      break;
++              case 5:                 /* Datatransmission 64k, BTX        */
++              case 7:                 /* Datatransmission 64k             */
++              default:
++                      *p++ = IE_BEARER;
++                      *p++ = 0x2;     /* Length                           */
++                      *p++ = 0x88;    /* Coding CCITT, unrestr. dig. Info.*/
++                      *p++ = 0x90;    /* Circuit-Mode 64kbps              */
++                      break;
++      }
++      mISDN_AddvarIE(skb, tmp);
++      tmp[0] = IE_CHANNEL_ID;
++      tmp[1] = 1;
++      tmp[2] = 0x85 + ch->nr;
++      mISDN_AddvarIE(skb, tmp);
++      if (setup->phone[0]) {
++              i = 1;
++              if (setup->plan) {
++                      tmp[i++] = setup->plan;
++                      if (!(setup->plan & 0x80))
++                              tmp[i++] = setup->screen;
++              } else
++                      tmp[i++] = 0x81;
++              j = 0;
++              while (setup->phone[j]) {
++                      if (setup->phone[j] == '.') /* subaddress */
++                              break;
++                      tmp[i++] = setup->phone[j++];
++              }
++              tmp[0] = i-1;
++              mISDN_AddIE(skb, IE_CALLING_PN, tmp);
++              if (setup->phone[j] == '.') {
++                      i = 1;
++                      tmp[i++] = 0x80;
++                      j++;
++                      while (setup->phone[j])
++                              tmp[i++] = setup->phone[j++];
++                      tmp[0] = i-1;
++                      mISDN_AddIE(skb, IE_CALLING_SUB, tmp);
++              }
++      }
++      if (setup->eazmsn[0]) {
++              i = 1;
++              tmp[i++] = 0x81;
++              j = 0;
++              while (setup->eazmsn[j]) {
++                      if (setup->eazmsn[j] == '.') /* subaddress */
++                              break;
++                      tmp[i++] = setup->eazmsn[j++];
++              }
++              tmp[0] = i-1;
++              mISDN_AddIE(skb, IE_CALLED_PN, tmp);
++              if (setup->eazmsn[j] == '.') {
++                      i = 1;
++                      tmp[i++] = 0x80;
++                      j++;
++                      while (setup->eazmsn[j])
++                              tmp[i++] = setup->eazmsn[j++];
++                      tmp[0] = i-1;
++                      mISDN_AddIE(skb, IE_CALLED_SUB, tmp);
++              }
++      }
++      p = tmp;
++      *p++ = IE_LLC;
++      if ((setup->si2 >= 160) && (setup->si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30
++              *p++ = 0x04;
++              *p++ = 0x88;
++              *p++ = 0x90;
++              *p++ = 0x21;
++              *p++ = EncodeSyncParams(setup->si2 - 160, 0x80);
++              test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags);
++      } else if ((setup->si2 >= 176) && (setup->si2 <= 191)) { // sync. Bitratenadaption, V.120
++              *p++ = 0x05;
++              *p++ = 0x88;
++              *p++ = 0x90;
++              *p++ = 0x28;
++              *p++ = EncodeSyncParams(setup->si2 - 176, 0);
++              *p++ = 0x82;
++              test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags);
++      } else if (setup->si2 >= 192) { // async. Bitratenadaption, V.110/X.30
++              *p++ = 0x06;
++              *p++ = 0x88;
++              *p++ = 0x90;
++              *p++ = 0x21;
++              p = EncodeASyncParams(p, setup->si2 - 192);
++              test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags);
++      } else {
++              switch(setup->si1) {
++                      case 1:
++                              *p++ = 0x3;
++                              *p++ = 0x90;
++                              *p++ = 0x90;
++                              *p++ = 0xa3;
++                              test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags);
++                              break;
++                      case 5:
++                      case 7:
++                      default:
++                              *p++ = 0x2;
++                              *p++ = 0x88;
++                              *p++ = 0x90;
++                              test_and_set_bit(I4L_FLG_L1HDLC, &ch->Flags);
++                              break;
++              }
++      }
++      mISDN_AddvarIE(skb, tmp);
++      mISDN_FsmChangeState(fi, ST_ICALL);
++      sendup(ch, 1, CC_SETUP | INDICATION, skb);
++}
++
++static void
++i4l_dconn_out(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      struct sk_buff  *skb;
++      u_char          tmp[4];
++
++      skb = mISDN_alloc_l3msg(4, MT_CONNECT);
++      if (!skb)
++              return;
++
++      tmp[0] = IE_CHANNEL_ID;
++      tmp[1] = 1;
++      tmp[2] = 0x85 + ch->nr;
++      mISDN_AddvarIE(skb, tmp);
++      sendup(ch, 1, CC_CONNECT | INDICATION, skb);
++      mISDN_FsmChangeState(fi, ST_ACTIVD);
++}
++
++static void
++i4l_dconn_in(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++
++      sendup(ch, 1, CC_CONNECT_ACKNOWLEDGE | INDICATION, NULL);
++      mISDN_FsmChangeState(fi, ST_ACTIVD);
++}
++
++static void
++i4l_bconn_notready(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++
++      test_and_set_bit(I4L_FLG_BCONN, &ch->Flags);
++}
++
++static void
++i4l_bconn(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      int             prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_ACTIVATE : DL_ESTABLISH;
++
++      sendup(ch, 0, prim | INDICATION, NULL);
++      test_and_set_bit(I4L_FLG_BCONN, &ch->Flags);
++      mISDN_FsmChangeState(fi, ST_ACTIVB);
++      if (skb_queue_len(&ch->sendq))
++              sendqueued(ch);
++}
++
++static void
++i4l_bhup(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      int             prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_DEACTIVATE : DL_RELEASE;
++
++      mISDN_FsmChangeState(fi, ST_ACTIVD);
++      sendup(ch, 0, prim | INDICATION, NULL);
++}
++
++static void
++stackready(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_BREADY);
++      test_and_set_bit(I4L_FLG_BREADY, &ch->Flags);
++      if (test_bit(I4L_FLG_BCONN, &ch->Flags))
++              mISDN_FsmEvent(&ch->i4lm, EV_I4L_BCONN, NULL);
++}
++
++static void
++capi_ocall(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      u_char          *p, *ps = skb->data;
++      isdn_ctrl       ctrl;
++      int             i,l;
++
++      mISDN_FsmChangeState(fi, ST_OCALL);
++      test_and_set_bit(I4L_FLG_LOCK, &ch->Flags);
++      i4l_lock_drv(ch->drv);
++      ps += L3_EXTRA_SIZE;
++      ctrl.parm.setup.si1 = 0;
++      ctrl.parm.setup.si2 = 0;
++      if (qi->bearer_capability) {
++              p = ps + qi->bearer_capability;
++              if ((p[1] > 1) && (p[1] < 11)) {
++                      switch (p[2] & 0x7f) {
++                              case 0x00: /* Speech */
++                              case 0x10: /* 3.1 Khz audio */
++                                      ctrl.parm.setup.si1 = 1;
++                                      break;
++                              case 0x08: /* Unrestricted digital information */
++                                      ctrl.parm.setup.si1 = 7;
++                                      if (qi->llc) 
++                                              ctrl.parm.setup.si2 = DecodeSI2(ps + qi->llc);
++                                      break;
++                              case 0x09: /* Restricted digital information */
++                                      ctrl.parm.setup.si1 = 2;
++                                      break;
++                              case 0x11:
++                                      /* Unrestr. digital information  with 
++                                       * tones/announcements ( or 7 kHz audio)
++                                       */
++                                      ctrl.parm.setup.si1 = 3;
++                                      break;
++                              case 0x18: /* Video */
++                                      ctrl.parm.setup.si1 = 4;
++                                      break;
++                      }
++                      switch (p[3] & 0x7f) {
++                              case 0x40: /* packed mode */
++                                      ctrl.parm.setup.si1 = 8;
++                                      break;
++                      }
++              }
++      }
++      if ((ctrl.parm.setup.si1 == 7) && (ctrl.parm.setup.si2 < 160))
++              test_and_set_bit(I4L_FLG_L1HDLC, &ch->Flags);
++      else
++              test_and_set_bit(I4L_FLG_L1TRANS, &ch->Flags);
++      i = 0;
++      if (qi->calling_nr) {
++              p = ps + qi->calling_nr + 1;
++              l = *p++;
++              ctrl.parm.setup.plan = *p;
++              l--;
++              if (!(*p & 0x80)) {
++                      p++;
++                      ctrl.parm.setup.screen = *p;
++                      l--;
++              } else
++                      ctrl.parm.setup.screen = 0;
++              p++;
++              while(i<l)
++                      ctrl.parm.setup.eazmsn[i++] = *p++;
++              ctrl.parm.setup.eazmsn[i] = 0;
++      } else
++              ctrl.parm.setup.eazmsn[0] = 0;
++      if (qi->calling_sub) {
++              p = ps + qi->calling_sub + 1;
++              l = *p++;
++              l--;
++              p++;
++              if (l>0)
++                      ctrl.parm.setup.eazmsn[i++] = '.';
++              while(l>0) {
++                      ctrl.parm.setup.eazmsn[i++] = *p++;
++                      l--;
++              }
++              ctrl.parm.setup.eazmsn[i] = 0;
++      }
++      i = 0;
++      if (qi->called_nr) {
++              p = ps + qi->called_nr + 1;
++              l = *p++;
++              p++;
++              l--;
++              while(i<l)
++                      ctrl.parm.setup.phone[i++] = *p++;
++              ctrl.parm.setup.phone[i] = 0;
++      } else
++              ctrl.parm.setup.phone[0] = 0;
++      if (qi->called_sub) {
++              p = ps + qi->called_sub + 1;
++              l = *p++;
++              l--;
++              p++;
++              if (l>0)
++                      ctrl.parm.setup.phone[i++] = '.';
++              while(l>0) {
++                      ctrl.parm.setup.phone[i++] = *p++;
++                      l--;
++              }
++              ctrl.parm.setup.phone[i] = 0;
++      }
++      if (test_bit(I4L_FLG_L1TRANS, &ch->Flags)) {
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_TRANS << 8), ISDN_CMD_SETL2);
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3);
++      } else {
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_HDLC << 8), ISDN_CMD_SETL2);
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3);
++      }
++      if (ch->drv->debug & 0x4)
++              printk(KERN_DEBUG "ocall from %s, si(%d/%d) -> %s\n", ctrl.parm.setup.eazmsn,
++                      ctrl.parm.setup.si1, ctrl.parm.setup.si2, ctrl.parm.setup.phone);
++      ctrl.driver = ch->drv->idx;
++      ctrl.arg = ch->nr;
++      ctrl.command = ISDN_CMD_DIAL;
++      ch->drv->interface->command(&ctrl);
++      dev_kfree_skb(skb);
++}
++
++static void
++capi_alert(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      mISDN_FsmChangeState(fi, ST_ALERT);
++      i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ALERT);
++      if (skb)
++              dev_kfree_skb(skb);
++}
++
++static void
++capi_connect(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      if (test_bit(I4L_FLG_L1TRANS, &ch->Flags)) {
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_TRANS << 8), ISDN_CMD_SETL2);
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3);
++      } else {
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L2_HDLC << 8), ISDN_CMD_SETL2);
++              i4l_cmd(ch->drv, ch->nr | (ISDN_PROTO_L3_TRANS << 8), ISDN_CMD_SETL3);
++      }
++      mISDN_FsmChangeState(fi, ST_WAITDCONN);
++      i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ACCEPTD);
++      if (skb)
++              dev_kfree_skb(skb);
++}
++
++static void
++capi_disconnect(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      mISDN_FsmChangeState(fi, ST_HANGUP);
++      test_and_set_bit(I4L_FLG_HANGUP, &ch->Flags);
++      i4l_cmd(ch->drv, ch->nr, ISDN_CMD_HANGUP);
++      if (skb)
++              dev_kfree_skb(skb);
++}
++
++static void
++capi_release(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      if (!test_and_clear_bit(I4L_FLG_HANGUP, &ch->Flags))
++              i4l_cmd(ch->drv, ch->nr, ISDN_CMD_HANGUP);
++      if (skb)
++              dev_kfree_skb(skb);
++      reset_channel(ch);
++      mISDN_FsmChangeState(fi, ST_NULL);
++}
++
++static void
++capi_establishb(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++
++      i4l_cmd(ch->drv, ch->nr, ISDN_CMD_ACCEPTB);
++}
++
++static void
++capi_releaseb(struct FsmInst *fi, int event, void *arg)
++{
++      i4l_channel_t   *ch = fi->userdata;
++
++      test_and_clear_bit(I4L_FLG_BREADY, &ch->Flags);
++      mISDN_FsmChangeState(fi, ST_ACTIVD);
++}
++
++static int
++Dchannel_i4l(mISDNif_t *hif, struct sk_buff *skb)
++{
++      int             i, ret = -EINVAL;
++      mISDN_head_t    *hh;
++      i4l_capi_t      *ic;
++      i4l_channel_t   *ch;
++
++      if (!hif || !skb)
++              return(ret);
++      ic = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      if (ic->debug & 0x2)
++              printk(KERN_DEBUG "%s: prim(%x) id(%x)\n", __FUNCTION__, hh->prim, hh->dinfo);
++      if (!ic)
++              return(ret);
++      if ((DL_ESTABLISH | REQUEST) == hh->prim) {
++              // FIXME
++              dev_kfree_skb(skb);
++              return(0);
++      }
++      ch = ic->ch;
++      for (i=0; i < ic->nr_ch; i++) {
++              if (ch->l4id == hh->dinfo)
++                      break;
++              ch++;
++      }
++      if (i == ic->nr_ch)
++              ch = NULL;
++      if ((CC_NEW_CR | REQUEST) == hh->prim) {
++              if (ch) {
++                      printk(KERN_WARNING "%s: ch%x in use\n", __FUNCTION__, ch->nr);
++                      ret = -EBUSY;
++              } else {
++                      ch = ic->ch;
++                      for (i=0; i < ic->nr_ch; i++) {
++                              if (ch->l4id == 0)
++                                      break;
++                              ch++;
++                      }
++                      if (i == ic->nr_ch) {
++                              ret = -EBUSY;
++                      } else {
++                              ch->l4id = hh->dinfo;
++                              ret = 0;
++                              dev_kfree_skb(skb);
++                      }
++              }
++              return(ret);
++      }
++      if (!ch) {
++              printk(KERN_WARNING "%s: no channel prim(%x) id(%x)\n", __FUNCTION__, hh->prim, hh->dinfo);
++              return(ret);
++      }
++      if (ch->drv->debug & 0x4)
++              mISDN_LogL3Msg(skb);
++      switch(hh->prim) {
++              case CC_SETUP | REQUEST:
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_OCALL, skb);
++                      break;
++              case CC_ALERTING | REQUEST:
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_ALERT, skb);
++                      break;
++              case CC_CONNECT | REQUEST:
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_DCONNECT, skb);
++                      break;
++              case CC_DISCONNECT | REQUEST:
++              case CC_RELEASE | REQUEST:
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_DISCONNECT, skb);
++                      break;
++              case CC_RELEASE_COMPLETE | REQUEST:
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_CAPI_RELEASE, skb);
++                      break;
++              default:
++                      if (debug)
++                              printk(KERN_DEBUG "%s: ch%x prim(%x) id(%x) not handled\n",
++                                      __FUNCTION__, ch->nr, hh->prim, hh->dinfo);
++                      break;
++      }
++      return(ret);
++}
++
++static int
++Bchannel_i4l(mISDNif_t *hif, struct sk_buff *skb)
++{
++      i4l_channel_t   *ch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      ch = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      if (ch->drv->debug & 0x20)
++              printk(KERN_DEBUG  "%s: prim(%x)\n", __FUNCTION__, hh->prim);
++      switch(hh->prim) {
++              case PH_ACTIVATE | REQUEST:
++              case DL_ESTABLISH | REQUEST:
++                      mISDN_FsmEvent(&ch->i4lm, EV_CAPI_ESTABLISHB, NULL);
++                      skb_trim(skb, 0);
++                      ret = if_newhead(&ch->inst.up, hh->prim | CONFIRM, 0, skb);
++                      break;
++              case PH_DEACTIVATE | REQUEST:
++              case DL_RELEASE | REQUEST:
++                      mISDN_FsmEvent(&ch->i4lm, EV_CAPI_RELEASEB, NULL);
++                      skb_trim(skb, 0);
++                      ret = if_newhead(&ch->inst.up, hh->prim | CONFIRM, 0, skb);
++                      break;
++              case PH_DATA | REQUEST:
++              case DL_DATA | REQUEST:
++                      skb_queue_tail(&ch->sendq, skb);
++                      ret = sendqueued(ch);
++                      break;
++              default:
++                      if (debug)
++                              printk(KERN_DEBUG "%s: ch%x prim(%x) id(%x) not handled\n",
++                                      __FUNCTION__, ch->nr, hh->prim, hh->dinfo);
++                      break;
++      }
++      return(ret);
++}
++
++
++/*
++ * Receive a packet from B-Channel. (Called from low-level-module)
++ */
++static void
++I4Lcapi_receive_skb_callback(int drvidx, int channel, struct sk_buff *skb)
++{
++      i4l_capi_t      *ic = drvmap[drvidx];
++      i4l_channel_t   *ch;
++      mISDN_headext_t *hhe = mISDN_HEADEXT_P(skb);
++      int             ret;
++
++      if (!ic) {
++              int_error();
++              return;
++      }
++      ch = ic->ch + channel;
++      if (!test_bit(I4L_FLG_BREADY, &ch->Flags)) {
++              if (ic->debug & 0x10)
++                      printk(KERN_WARNING "I4Lcapi_receive_skb_callback: bc%d/%d not ready\n", channel, ch->nr);
++              dev_kfree_skb(skb);
++              return;
++      }
++      hhe->prim = test_bit(I4L_FLG_LAYER1, &ch->Flags) ? PH_DATA_IND : DL_DATA_IND;
++      if (!ch->inst.up.func) {
++              dev_kfree_skb(skb);
++              int_error();
++              return;
++      }
++      if (in_interrupt()) {
++              hhe->func.iff = ch->inst.up.func;
++              hhe->data[0] = &ch->inst.up;
++              ret = ch->inst.obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb);
++      } else
++              ret = ch->inst.up.func(&ch->inst.up, skb);
++      if (ret)
++              dev_kfree_skb(skb);
++}
++
++static int
++i4l_stat_run(i4l_capi_t *ic) {
++      int     err;
++
++      err = I4Lcapi.ctrl(ic->inst.st, MGR_SETSTACK | REQUEST, &ic->pid);
++      if (err) {
++              printk(KERN_ERR  "MGR_SETSTACK REQUEST dch err(%d)\n", err);
++              I4Lcapi.ctrl(ic->inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(err);
++      }
++      return(0);
++}
++
++static int
++i4l_sent_pkt(i4l_capi_t *drv, isdn_ctrl *c)
++{
++      i4l_channel_t   *ch = drv->ch;
++      struct sk_buff  *skb;
++      int             ret;
++      mISDN_headext_t *hhe;
++
++      if (c->arg < 0)
++              return -1;
++      ch += c->arg;
++      skb = skb_dequeue(&ch->ackq);
++      if (!skb) {
++              int_error();
++              return(-1);
++      }
++      if (drv->debug & 0x800)
++              printk(KERN_DEBUG "bc%ld ack skb(%p)\n", c->arg, skb);
++      if (skb_queue_len(&ch->sendq))
++              sendqueued(ch);
++      skb_trim(skb, 0);
++      hhe = mISDN_HEADEXT_P(skb);
++      hhe->prim |= CONFIRM;
++      if (in_interrupt()) {
++              hhe->func.iff = ch->inst.up.func;
++              hhe->data[0] = &ch->inst.up;
++              ret = ch->inst.obj->ctrl(NULL, MGR_QUEUEIF | REQUEST, skb);
++      } else
++              ret = ch->inst.up.func(&ch->inst.up, skb);
++      if (ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++#define I4L_LOGBUF_SIZE       256
++static char   logbuf[I4L_LOGBUF_SIZE];
++
++static int
++i4l_stavail(i4l_capi_t *drv, isdn_ctrl *c)
++{
++      int             len = c->arg;
++
++      if (drv->interface->readstat) {
++              while(len>0) {
++                      if (len < I4L_LOGBUF_SIZE) {
++                              drv->interface->readstat(logbuf, len, 0, drv->idx, 0);
++                              logbuf[len] = 0;
++                      } else {
++                              drv->interface->readstat(logbuf, I4L_LOGBUF_SIZE - 1, 0, drv->idx, 0);
++                              logbuf[I4L_LOGBUF_SIZE] = 0;
++                      }
++                      if (drv->debug & 0x1)
++                              printk(KERN_DEBUG "%s", logbuf);
++                      len -= (I4L_LOGBUF_SIZE -1);
++              }
++      }
++      return(0);
++}
++
++static int
++I4Lcapi_status_callback(isdn_ctrl *c)
++{
++      i4l_capi_t      *drv = drvmap[c->driver];
++      i4l_channel_t   *ch;
++      int             i, ret = -1;
++
++      if (!drv)
++              return(-1);
++      if (c->command == ISDN_STAT_BSENT)
++              return(i4l_sent_pkt(drv, c));
++      if (c->command == ISDN_STAT_STAVAIL)
++              return(i4l_stavail(drv, c));
++      ch = drv->ch;
++      if (drv->debug & 0x8)
++              printk(KERN_DEBUG "drv%d cmd(%d) arg(%ld)\n",
++                      c->driver, c->command, c->arg);
++      switch (c->command) {
++              case ISDN_STAT_RUN:
++                      ret = i4l_stat_run(drv);
++                      break;
++              case ISDN_STAT_STOP:
++                      // FIXME
++                      ret = 0;
++                      break;
++              case ISDN_STAT_ICALL:
++                      if (c->arg < 0)
++                              return -1;
++                      ch += c->arg;
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_ICALL, &c->parm.setup);
++                      break;
++              case ISDN_STAT_CINF:
++                      if (c->arg < 0)
++                              return -1;
++                      // FIXME
++                      ret = 0;
++                      break;
++              case ISDN_STAT_CAUSE:
++                      if (c->arg < 0)
++                              return -1;
++                      ch += c->arg;
++                      if ((c->parm.num[0] == 'E') || (c->parm.num[0] == 'L'))
++                              i = 1;
++                      else
++                              i = 0;
++                      sscanf(&c->parm.num[i], "%2X%2X", &ch->cause_loc, &ch->cause_val);
++                      ch->cause_loc |= 0x80;
++                      ch->cause_val |= 0x80;
++                      if (drv->debug & 0x1)
++                              printk(KERN_DEBUG "isdn: ch%ld cause: %s %02x%02x\n",
++                                      c->arg, c->parm.num, ch->cause_loc, ch->cause_val);
++                      ret = 0;
++                      break;
++              case ISDN_STAT_DISPLAY:
++                      // FIXME
++                      ret = 0;
++                      break;
++              case ISDN_STAT_DCONN:
++                      if (c->arg < 0)
++                              return -1;
++                      ch += c->arg;
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_DCONN, NULL);
++                      break;
++              case ISDN_STAT_DHUP:
++                      if (c->arg < 0)
++                              return -1;
++                      ch += c->arg;
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_DHUP, NULL);
++                      break;
++              case ISDN_STAT_BCONN:
++                      if (c->arg < 0)
++                              return -1;
++                      ch += c->arg;
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_BCONN, NULL);
++                      break;
++              case ISDN_STAT_BHUP:
++                      if (c->arg < 0)
++                              return -1;
++                      ch += c->arg;
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_BHUP, NULL);
++                      break;
++              case ISDN_STAT_NODCH:
++              case ISDN_STAT_L1ERR:
++                      if (c->arg < 0)
++                              return -1;
++                      ch += c->arg;
++                      ret = mISDN_FsmEvent(&ch->i4lm, EV_I4L_L1ERR, NULL);
++                      break;
++              case ISDN_STAT_ADDCH:
++              case ISDN_STAT_DISCH:
++                      // FIXME
++                      ret = 0;
++                      break;
++              case ISDN_STAT_UNLOAD:
++                      ret = I4Lcapi.ctrl(drv->inst.st, MGR_DELSTACK | REQUEST, NULL);
++                      MOD_DEC_USE_COUNT;
++                      break;
++              case CAPI_PUT_MESSAGE:
++                      // FIXME
++                      break;
++              case ISDN_STAT_FAXIND:
++                      // FIXME
++                      break;
++              case ISDN_STAT_AUDIO:
++                      // FIXME
++                      break;
++              case ISDN_STAT_PROT:
++              case ISDN_STAT_REDIR:
++                      // FIXME
++                      break;                  
++              default:
++                      break;
++      }
++      return(ret);
++}  
++
++static int
++I4Lcapi_manager(void *data, u_int prim, void *arg) {
++      i4l_capi_t      *card = I4Lcapi.ilist;
++      mISDNinstance_t *inst = data;
++      i4l_channel_t   *channel = NULL;
++      int             nr_ch = -2;
++
++      if (debug & 0x100)
++              printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n",
++                      __FUNCTION__, data, prim, arg);
++      if (prim == (MGR_HASPROTOCOL | REQUEST))
++              return(mISDN_HasProtocolP(&I4Lcapi, arg));
++      if (!data) {
++              printk(KERN_ERR "I4Lcapi_manager no data prim %x arg %p\n",
++                      prim, arg);
++              return(-EINVAL);
++      }
++      while(card) {
++              if (&card->inst == inst) {
++                      nr_ch = -1;
++                      break;
++              }
++              channel = card->ch;
++              for (nr_ch = 0; nr_ch < card->nr_ch; nr_ch++) {
++                      if (&channel->inst == inst)
++                              break;
++                      channel++;
++              }
++              if (nr_ch != card->nr_ch)
++                      break;
++              card = card->next;
++              channel = NULL;
++              nr_ch = -2;
++      }
++      if (nr_ch == -2) {
++              printk(KERN_ERR "I4Lcapi_manager no channel data %p prim %x arg %p\n",
++                      data, prim, arg);
++              return(-EINVAL);
++      }
++      switch(prim) {
++          case MGR_REGLAYER | CONFIRM:
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++              I4Lcapi.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
++              I4Lcapi.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++              break;
++          case MGR_RELEASE | INDICATION:
++              if (nr_ch == -1) {
++                      release_card(card->idx);
++              } else {
++                      I4Lcapi.refcnt--;
++              }
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              if (nr_ch == -1)
++                      return(mISDN_SetIF(inst, arg, prim, Dchannel_i4l, NULL, card));
++              else
++                      return(mISDN_SetIF(inst, arg, prim, Bchannel_i4l, NULL, channel));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_SETSTACK | CONFIRM:
++              if (nr_ch >= 0) {
++                      if (inst->pid.protocol[2] != ISDN_PID_L2_B_TRANS)
++                              test_and_set_bit(I4L_FLG_LAYER1, &channel->Flags);
++                      mISDN_FsmEvent(&channel->i4lm, EV_STACKREADY, NULL);
++              }
++              break;
++          default:
++              if (debug)
++                      printk(KERN_DEBUG "I4Lcapi_manager prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static i4l_capi_reg_t I4Lcapireg;
++
++static int
++I4Lcapi_register(isdn_if *iif)
++{
++      int             drvidx = 0;
++      int             i, err;
++      i4l_channel_t   *ch;
++
++      if (!iif->writebuf_skb) {
++              printk(KERN_ERR "I4Lcapi_register: No write routine given.\n");
++              return 0;
++      }
++      for (drvidx=0; drvidx<MAX_CARDS; drvidx++) {
++              if (drvmap[drvidx] == NULL)
++                      break;
++      }
++      if (drvidx == MAX_CARDS) {
++              printk(KERN_ERR "I4Lcapi_register: no driver slot this card\n");
++              return(0);
++      }
++      drvmap[drvidx] = kmalloc(sizeof(i4l_capi_t), GFP_KERNEL);
++      if (!drvmap[drvidx]) {
++              printk(KERN_ERR "I4Lcapi_register: no memory for i4l_capi_t\n");
++              return(0);
++      }
++      memset(drvmap[drvidx], 0, sizeof(i4l_capi_t));
++      drvmap[drvidx]->ch = kmalloc(iif->channels * sizeof(i4l_channel_t), GFP_KERNEL);
++      if (!drvmap[drvidx]->ch) {
++              printk(KERN_ERR "I4Lcapi_register: no memory for i4l_channel_t\n");
++              kfree(drvmap[drvidx]);
++              drvmap[drvidx] = NULL;
++              return(0);
++      }
++      drvmap[drvidx]->idx = drvidx;
++      drvmap[drvidx]->interface = iif;
++      drvmap[drvidx]->nr_ch = iif->channels;
++      iif->channels = drvidx;
++
++      iif->rcvcallb_skb = I4Lcapi_receive_skb_callback;
++      iif->statcallb = I4Lcapi_status_callback;
++
++      APPEND_TO_LIST(drvmap[drvidx], ((i4l_capi_t *)I4Lcapi.ilist));
++      drvmap[drvidx]->debug = debug;
++      drvmap[drvidx]->inst.pid.layermask = ISDN_LAYER(0) | ISDN_LAYER(1) | ISDN_LAYER(2) | ISDN_LAYER(3);
++      drvmap[drvidx]->inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
++      drvmap[drvidx]->inst.pid.protocol[1] = ISDN_PID_L1_TE_S0;
++      drvmap[drvidx]->inst.pid.protocol[2] = ISDN_PID_L2_LAPD;
++      drvmap[drvidx]->inst.pid.protocol[3] = ISDN_PID_L3_DSS1USER;
++      mISDN_init_instance(&drvmap[drvidx]->inst, &I4Lcapi, drvmap[drvidx]);
++      sprintf(drvmap[drvidx]->inst.name, "Fritz%d", drvidx+1);
++      mISDN_set_dchannel_pid(&drvmap[drvidx]->pid, 2, 0);
++      for (i=0; i < drvmap[drvidx]->nr_ch; i++) {
++              init_channel(drvmap[drvidx], i);
++      }
++      err = I4Lcapi.ctrl(NULL, MGR_NEWSTACK | REQUEST, &drvmap[drvidx]->inst);
++      if (err) {
++              release_card(drvidx);
++              return(err);
++      }
++      ch = drvmap[drvidx]->ch;
++      for (i=0; i < drvmap[drvidx]->nr_ch; i++) {
++              err = I4Lcapi.ctrl(drvmap[drvidx]->inst.st, MGR_NEWSTACK | REQUEST, &ch->inst);
++              if (err) {
++                      printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
++                      I4Lcapi.ctrl(drvmap[drvidx]->inst.st, MGR_DELSTACK | REQUEST, NULL);
++                      return(err);
++              }
++              ch++;
++      }
++      MOD_INC_USE_COUNT;
++      return(1);
++}
++
++static struct FsmNode I4LFnList[] =
++{
++      {ST_NULL,       EV_I4L_ICALL,           i4l_icall},
++      {ST_NULL,       EV_CAPI_OCALL,          capi_ocall},
++      {ST_ICALL,      EV_I4L_DHUP,            i4l_dhup},
++      {ST_ICALL,      EV_I4L_L1ERR,           i4l_l1err},
++      {ST_ICALL,      EV_CAPI_ALERT,          capi_alert},
++      {ST_ICALL,      EV_CAPI_DCONNECT,       capi_connect},
++      {ST_ICALL,      EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_ICALL,      EV_CAPI_RELEASE,        capi_release},
++      {ST_OCALL,      EV_I4L_DHUP,            i4l_dhup},
++      {ST_OCALL,      EV_I4L_L1ERR,           i4l_l1err},
++      {ST_OCALL,      EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_OCALL,      EV_CAPI_RELEASE,        capi_release},
++      {ST_OCALL,      EV_I4L_DCONN,           i4l_dconn_out},
++      {ST_PROCEED,    EV_I4L_DHUP,            i4l_dhup},
++      {ST_PROCEED,    EV_I4L_L1ERR,           i4l_l1err},
++      {ST_PROCEED,    EV_CAPI_ALERT,          capi_alert},
++      {ST_PROCEED,    EV_CAPI_DCONNECT,       capi_connect},
++      {ST_PROCEED,    EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_PROCEED,    EV_CAPI_RELEASE,        capi_release},
++      {ST_ALERT,      EV_I4L_DHUP,            i4l_dhup},
++      {ST_ALERT,      EV_I4L_L1ERR,           i4l_l1err},
++      {ST_ALERT,      EV_CAPI_DCONNECT,       capi_connect},
++      {ST_ALERT,      EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_ALERT,      EV_CAPI_RELEASE,        capi_release},
++      {ST_WAITDCONN,  EV_I4L_DCONN,           i4l_dconn_in},
++      {ST_WAITDCONN,  EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_WAITDCONN,  EV_CAPI_RELEASE,        capi_release},
++      {ST_WAITDCONN,  EV_I4L_DHUP,            i4l_dhup},
++      {ST_ACTIVD,     EV_I4L_DHUP,            i4l_dhup},
++      {ST_ACTIVD,     EV_I4L_L1ERR,           i4l_l1err},
++      {ST_ACTIVD,     EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_ACTIVD,     EV_CAPI_RELEASE,        capi_release},
++      {ST_ACTIVD,     EV_I4L_BCONN,           i4l_bconn_notready},
++      {ST_ACTIVD,     EV_STACKREADY,          stackready},
++      {ST_BREADY,     EV_CAPI_ESTABLISHB,     capi_establishb},
++      {ST_BREADY,     EV_I4L_BCONN,           i4l_bconn},
++      {ST_BREADY,     EV_I4L_DHUP,            i4l_dhup},
++      {ST_BREADY,     EV_I4L_BHUP,            i4l_bhup},
++      {ST_BREADY,     EV_I4L_L1ERR,           i4l_l1err},
++      {ST_BREADY,     EV_CAPI_RELEASEB,       capi_releaseb},
++      {ST_BREADY,     EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_BREADY,     EV_CAPI_RELEASE,        capi_release},
++      {ST_ACTIVB,     EV_I4L_DHUP,            i4l_dhup},
++      {ST_ACTIVB,     EV_I4L_BHUP,            i4l_bhup},
++      {ST_ACTIVB,     EV_I4L_L1ERR,           i4l_l1err},
++      {ST_ACTIVB,     EV_CAPI_DISCONNECT,     capi_disconnect},
++      {ST_ACTIVB,     EV_CAPI_RELEASE,        capi_release},
++      {ST_ACTIVB,     EV_CAPI_RELEASEB,       capi_releaseb},
++      {ST_HANGUP,     EV_I4L_DHUP,            i4l_dhup},
++      {ST_HANGUP,     EV_I4L_L1ERR,           i4l_l1err},
++      {ST_HANGUP,     EV_CAPI_RELEASE,        capi_release},
++};
++
++#define I4L_FN_COUNT  (sizeof(I4LFnList)/sizeof(struct FsmNode))
++
++static char   *I4L_capi_name = "I4L CAPI";
++
++int
++I4Lcapi_init(void)
++{
++      int err;
++
++      printk(KERN_INFO "I4L CAPI interface modul version %s\n", mISDN_getrev(i4lcapi_revision));
++#ifdef MODULE
++      I4Lcapi.owner = THIS_MODULE;
++#endif
++      I4Lcapi.name = I4L_capi_name;
++      I4Lcapi.own_ctrl = I4Lcapi_manager;
++      I4Lcapi.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0;
++      I4Lcapi.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0;
++      I4Lcapi.DPROTO.protocol[2] = ISDN_PID_L2_LAPD | ISDN_PID_L2_DF_PTP;
++      I4Lcapi.DPROTO.protocol[3] = ISDN_PID_L3_DSS1USER | ISDN_PID_L3_DF_PTP;
++      I4Lcapi.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC;
++      I4Lcapi.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS;
++      I4Lcapi.prev = NULL;
++      I4Lcapi.next = NULL;
++      
++      i4lfsm_s.state_count = STATE_COUNT;
++      i4lfsm_s.event_count = EVENT_COUNT;
++      i4lfsm_s.strEvent = strI4LEvent;
++      i4lfsm_s.strState = strI4LState;
++      mISDN_FsmNew(&i4lfsm_s, I4LFnList, I4L_FN_COUNT);
++      if ((err = mISDN_register(&I4Lcapi))) {
++              printk(KERN_ERR "Can't register I4L CAPI error(%d)\n", err);
++              mISDN_FsmFree(&i4lfsm_s);
++              return(err);
++      }
++      I4Lcapireg.register_func = I4Lcapi_register;
++      strcpy(I4Lcapireg.name, "I4L CAPI");
++      err = register_i4lcapi(&I4Lcapireg);
++      printk(KERN_INFO "registered I4L CAPI %s err(%d)\n", i4lcapi_revision, err);
++      return(0);
++}
++
++#ifdef MODULE
++void
++I4Lcapi_cleanup(void)
++{
++      
++      int err;
++      if ((err = mISDN_unregister(&I4Lcapi))) {
++              printk(KERN_ERR "Can't unregister I4Lcapi error(%d)\n", err);
++              return;
++      }
++      while(I4Lcapi.ilist) {
++              printk(KERN_ERR "I4Lcapi card struct not empty refs %d\n",
++                      I4Lcapi.refcnt);
++              release_card(((i4l_capi_t *)I4Lcapi.ilist)->idx);
++      }
++      mISDN_FsmFree(&i4lfsm_s);
++      unregister_i4lcapi();
++      return;
++}
++
++module_init(I4Lcapi_init);
++module_exit(I4Lcapi_cleanup);
++
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.c   2004-11-22 09:33:38.209735152 +0000
+@@ -0,0 +1,848 @@
++/* $Id$
++ *
++ * isac.c   ISAC specific routines
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ *            This file is (c) under GNU PUBLIC LICENSE
++ */
++
++#include <linux/module.h>
++#include "dchannel.h"
++#include "isac.h"
++#include "arcofi.h"
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++#ifdef CONFIG_KMOD
++#include <linux/kmod.h>
++#endif
++
++
++#define DBUSY_TIMER_VALUE 80
++#define ARCOFI_USE 1
++
++const char *isac_revision = "$Revision$";
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++EXPORT_SYMBOL(mISDN_isac_init);
++EXPORT_SYMBOL(mISDN_isac_free);
++EXPORT_SYMBOL(mISDN_isac_interrupt);
++EXPORT_SYMBOL(mISDN_clear_isac);
++EXPORT_SYMBOL(mISDN_ISAC_l1hw);
++#endif
++
++static inline void
++ph_command(dchannel_t *dch, unsigned int command)
++{
++      if (dch->debug & L1_DEB_ISAC)
++              mISDN_debugprint(&dch->inst, "ph_command %x", command);
++      if (dch->type & ISAC_TYPE_ISACSX)
++              dch->write_reg(dch->inst.data, ISACSX_CIX0, (command << 4) | 0xE);
++      else
++              dch->write_reg(dch->inst.data, ISAC_CIX0, (command << 2) | 3);
++}
++
++static void
++isac_new_ph(dchannel_t *dch)
++{
++      u_int           prim = PH_SIGNAL | INDICATION;
++      u_int           para = 0;
++      mISDNif_t       *upif = &dch->inst.up;
++
++      switch (dch->ph_state) {
++              case (ISAC_IND_RS):
++              case (ISAC_IND_EI):
++                      dch->inst.lock(dch->inst.data,0);
++                      ph_command(dch, ISAC_CMD_DUI);
++                      dch->inst.unlock(dch->inst.data);
++                      prim = PH_CONTROL | INDICATION;
++                      para = HW_RESET;
++                      break;
++              case (ISAC_IND_DID):
++                      prim = PH_CONTROL | CONFIRM;
++                      para = HW_DEACTIVATE;
++                      break;
++              case (ISAC_IND_DR):
++                      prim = PH_CONTROL | INDICATION;
++                      para = HW_DEACTIVATE;
++                      break;
++              case (ISAC_IND_PU):
++                      prim = PH_CONTROL | INDICATION;
++                      para = HW_POWERUP;
++                      break;
++              case (ISAC_IND_RSY):
++                      para = ANYSIGNAL;
++                      break;
++              case (ISAC_IND_ARD):
++                      para = INFO2;
++                      break;
++              case (ISAC_IND_AI8):
++                      para = INFO4_P8;
++                      break;
++              case (ISAC_IND_AI10):
++                      para = INFO4_P10;
++                      break;
++              default:
++                      return;
++      }
++      while(upif) {
++              if_link(upif, prim, para, 0, NULL, 0);
++              upif = upif->clone;
++      }
++}
++
++static void
++isac_hwbh(dchannel_t *dch)
++{
++      if (dch->debug)
++              printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event);
++#if 0
++      if (test_and_clear_bit(D_CLEARBUSY, &dch->event)) {
++              if (dch->debug)
++                      mISDN_debugprint(&dch->inst, "D-Channel Busy cleared");
++              stptr = dch->stlist;
++              while (stptr != NULL) {
++                      stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
++                      stptr = stptr->next;
++              }
++      }
++#endif
++      if (test_and_clear_bit(D_L1STATECHANGE, &dch->event))
++              isac_new_ph(dch);               
++#if ARCOFI_USE
++      if (!(ISAC_TYPE_ARCOFI & dch->type))
++              return;
++      if (test_and_clear_bit(D_RX_MON1, &dch->event))
++              arcofi_fsm(dch, ARCOFI_RX_END, NULL);
++      if (test_and_clear_bit(D_TX_MON1, &dch->event))
++              arcofi_fsm(dch, ARCOFI_TX_END, NULL);
++#endif
++}
++
++void
++isac_empty_fifo(dchannel_t *dch, int count)
++{
++      u_char *ptr;
++
++      if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
++              mISDN_debugprint(&dch->inst, "isac_empty_fifo");
++
++      if (!dch->rx_skb) {
++              if (!(dch->rx_skb = alloc_stack_skb(MAX_DFRAME_LEN_L1, dch->up_headerlen))) {
++                      printk(KERN_WARNING "mISDN: D receive out of memory\n");
++                      dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
++                      return;
++              }
++      }
++      if ((dch->rx_skb->len + count) >= MAX_DFRAME_LEN_L1) {
++              if (dch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&dch->inst, "isac_empty_fifo overrun %d",
++                              dch->rx_skb->len + count);
++              dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
++              return;
++      }
++      ptr = skb_put(dch->rx_skb, count);
++      dch->read_fifo(dch->inst.data, ptr, count);
++      dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
++      if (dch->debug & L1_DEB_ISAC_FIFO) {
++              char *t = dch->dlog;
++
++              t += sprintf(t, "isac_empty_fifo cnt %d", count);
++              mISDN_QuickHex(t, ptr, count);
++              mISDN_debugprint(&dch->inst, dch->dlog);
++      }
++}
++
++static void
++isac_fill_fifo(dchannel_t *dch)
++{
++      int count, more;
++      u_char *ptr;
++
++      if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
++              mISDN_debugprint(&dch->inst, "isac_fill_fifo");
++
++      count = dch->tx_len - dch->tx_idx;
++      if (count <= 0)
++              return;
++
++      more = 0;
++      if (count > 32) {
++              more = !0;
++              count = 32;
++      }
++      ptr = dch->tx_buf + dch->tx_idx;
++      dch->tx_idx += count;
++      dch->write_fifo(dch->inst.data, ptr, count);
++      dch->write_reg(dch->inst.data, ISAC_CMDR, more ? 0x8 : 0xa);
++      if (test_and_set_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
++              mISDN_debugprint(&dch->inst, "isac_fill_fifo dbusytimer running");
++              del_timer(&dch->dbusytimer);
++      }
++      init_timer(&dch->dbusytimer);
++      dch->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
++      add_timer(&dch->dbusytimer);
++      if (dch->debug & L1_DEB_ISAC_FIFO) {
++              char *t = dch->dlog;
++
++              t += sprintf(t, "isac_fill_fifo cnt %d", count);
++              mISDN_QuickHex(t, ptr, count);
++              mISDN_debugprint(&dch->inst, dch->dlog);
++      }
++}
++
++static void
++isac_rme_irq(dchannel_t *dch)
++{
++      u_char  val;
++      u_int   count;
++
++      val = dch->read_reg(dch->inst.data, ISAC_RSTA);
++      if ((val & 0x70) != 0x20) {
++              if (val & 0x40) {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "ISAC RDO");
++#ifdef ERROR_STATISTIC
++                      dch->err_rx++;
++#endif
++              }
++              if (!(val & 0x20)) {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "ISAC CRC error");
++#ifdef ERROR_STATISTIC
++                      dch->err_crc++;
++#endif
++              }
++              dch->write_reg(dch->inst.data, ISAC_CMDR, 0x80);
++              if (dch->rx_skb)
++                      dev_kfree_skb(dch->rx_skb);
++      } else {
++              count = dch->read_reg(dch->inst.data, ISAC_RBCL) & 0x1f;
++              if (count == 0)
++                      count = 32;
++              isac_empty_fifo(dch, count);
++              if (dch->rx_skb) {
++                      skb_queue_tail(&dch->rqueue, dch->rx_skb);
++              }
++      }
++      dch->rx_skb = NULL;
++      dchannel_sched_event(dch, D_RCVBUFREADY);
++}
++
++static void
++isac_xpr_irq(dchannel_t *dch)
++{
++      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++              del_timer(&dch->dbusytimer);
++      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++              dchannel_sched_event(dch, D_CLEARBUSY);
++      if (dch->tx_idx < dch->tx_len) {
++              isac_fill_fifo(dch);
++      } else {
++              if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
++                      if (dch->next_skb) {
++                              dch->tx_len = dch->next_skb->len;
++                              memcpy(dch->tx_buf,
++                                      dch->next_skb->data, dch->tx_len);
++                              dch->tx_idx = 0;
++                              isac_fill_fifo(dch);
++                              dchannel_sched_event(dch, D_XMTBUFREADY);
++                      } else {
++                              printk(KERN_WARNING "isac tx irq TX_NEXT without skb\n");
++                              test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++                      }
++              } else
++                      test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++      }
++}
++
++static void
++isac_retransmit(dchannel_t *dch)
++{
++      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++              del_timer(&dch->dbusytimer);
++      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++              dchannel_sched_event(dch, D_CLEARBUSY);
++      if (test_bit(FLG_TX_BUSY, &dch->DFlags)) {
++              /* Restart frame */
++              dch->tx_idx = 0;
++              isac_fill_fifo(dch);
++      } else {
++              printk(KERN_WARNING "mISDN: ISAC XDU no TX_BUSY\n");
++              mISDN_debugprint(&dch->inst, "ISAC XDU no TX_BUSY");
++              if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
++                      if (dch->next_skb) {
++                              dch->tx_len = dch->next_skb->len;
++                              memcpy(dch->tx_buf,
++                              dch->next_skb->data,
++                              dch->tx_len);
++                              dch->tx_idx = 0;
++                              isac_fill_fifo(dch);
++                              dchannel_sched_event(dch, D_XMTBUFREADY);
++                      } else {
++                              printk(KERN_WARNING "isac xdu irq TX_NEXT without skb\n");
++                      }
++              }
++      }
++}
++
++static void
++isac_mos_irq(dchannel_t *dch)
++{
++      u_char          val;
++      isac_chip_t     *isac = dch->hw;
++
++      val = dch->read_reg(dch->inst.data, ISAC_MOSR);
++      if (dch->debug & L1_DEB_MONITOR)
++              mISDN_debugprint(&dch->inst, "ISAC MOSR %02x", val);
++#if ARCOFI_USE
++      if (val & 0x08) {
++              if (!isac->mon_rx) {
++                      if (!(isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
++                              if (dch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!");
++                              isac->mocr &= 0xf0;
++                              isac->mocr |= 0x0a;
++                              dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                              goto afterMONR0;
++                      } else
++                              isac->mon_rxp = 0;
++              }
++              if (isac->mon_rxp >= MAX_MON_FRAME) {
++                      isac->mocr &= 0xf0;
++                      isac->mocr |= 0x0a;
++                      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                      isac->mon_rxp = 0;
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "ISAC MON RX overflow!");
++                      goto afterMONR0;
++              }
++              isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.data, ISAC_MOR0);
++              if (dch->debug & L1_DEB_MONITOR)
++                      mISDN_debugprint(&dch->inst, "ISAC MOR0 %02x", isac->mon_rx[isac->mon_rxp -1]);
++              if (isac->mon_rxp == 1) {
++                      isac->mocr |= 0x04;
++                      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++              }
++      }
++afterMONR0:
++      if (val & 0x80) {
++              if (!isac->mon_rx) {
++                      if (!(isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
++                              if (dch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&dch->inst, "ISAC MON RX out of memory!");
++                              isac->mocr &= 0x0f;
++                              isac->mocr |= 0xa0;
++                              dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                              goto afterMONR1;
++                      } else
++                              isac->mon_rxp = 0;
++              }
++              if (isac->mon_rxp >= MAX_MON_FRAME) {
++                      isac->mocr &= 0x0f;
++                      isac->mocr |= 0xa0;
++                      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                      isac->mon_rxp = 0;
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "ISAC MON RX overflow!");
++                      goto afterMONR1;
++              }
++              isac->mon_rx[isac->mon_rxp++] = dch->read_reg(dch->inst.data, ISAC_MOR1);
++              if (dch->debug & L1_DEB_MONITOR)
++                      mISDN_debugprint(&dch->inst, "ISAC MOR1 %02x", isac->mon_rx[isac->mon_rxp -1]);
++              isac->mocr |= 0x40;
++              dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++      }
++afterMONR1:
++      if (val & 0x04) {
++              isac->mocr &= 0xf0;
++              dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++              isac->mocr |= 0x0a;
++              dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++              dchannel_sched_event(dch, D_RX_MON0);
++      }
++      if (val & 0x40) {
++              isac->mocr &= 0x0f;
++              dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++              isac->mocr |= 0xa0;
++              dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++              dchannel_sched_event(dch, D_RX_MON1);
++      }
++      if (val & 0x02) {
++              if ((!isac->mon_tx) || (isac->mon_txc && 
++                      (isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {
++                      isac->mocr &= 0xf0;
++                      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                      isac->mocr |= 0x0a;
++                      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                      if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc))
++                              dchannel_sched_event(dch, D_TX_MON0);
++                      goto AfterMOX0;
++              }
++              if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
++                      dchannel_sched_event(dch, D_TX_MON0);
++                      goto AfterMOX0;
++              }
++              dch->write_reg(dch->inst.data, ISAC_MOX0,
++              isac->mon_tx[isac->mon_txp++]);
++              if (dch->debug & L1_DEB_MONITOR)
++                      mISDN_debugprint(&dch->inst, "ISAC %02x -> MOX0", isac->mon_tx[isac->mon_txp -1]);
++      }
++AfterMOX0:
++      if (val & 0x20) {
++              if ((!isac->mon_tx) || (isac->mon_txc && 
++                      (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {
++                      isac->mocr &= 0x0f;
++                      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                      isac->mocr |= 0xa0;
++                      dch->write_reg(dch->inst.data, ISAC_MOCR, isac->mocr);
++                      if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc))
++                              dchannel_sched_event(dch, D_TX_MON1);
++                      goto AfterMOX1;
++              }
++              if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
++                      dchannel_sched_event(dch, D_TX_MON1);
++                      goto AfterMOX1;
++              }
++              dch->write_reg(dch->inst.data, ISAC_MOX1,
++              isac->mon_tx[isac->mon_txp++]);
++              if (dch->debug & L1_DEB_MONITOR)
++                      mISDN_debugprint(&dch->inst, "ISAC %02x -> MOX1", isac->mon_tx[isac->mon_txp -1]);
++      }
++AfterMOX1:
++      val = 0; /* dummy to avoid warning */
++#endif
++}
++
++static void
++isac_cisq_irq(dchannel_t *dch) {
++      unsigned char val;
++
++      val = dch->read_reg(dch->inst.data, ISAC_CIR0);
++      if (dch->debug & L1_DEB_ISAC)
++              mISDN_debugprint(&dch->inst, "ISAC CIR0 %02X", val);
++      if (val & 2) {
++              if (dch->debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&dch->inst, "ph_state change %x->%x",
++                              dch->ph_state, (val >> 2) & 0xf);
++              dch->ph_state = (val >> 2) & 0xf;
++              dchannel_sched_event(dch, D_L1STATECHANGE);
++      }
++      if (val & 1) {
++              val = dch->read_reg(dch->inst.data, ISAC_CIR1);
++              if (dch->debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&dch->inst, "ISAC CIR1 %02X", val );
++      }
++}
++
++static void
++isacsx_cic_irq(dchannel_t *dch)
++{
++      unsigned char val;
++
++      val = dch->read_reg(dch->inst.data, ISACSX_CIR0);
++      if (dch->debug & L1_DEB_ISAC)
++              mISDN_debugprint(&dch->inst, "ISACSX CIR0 %02X", val);
++      if (val & ISACSX_CIR0_CIC0) {
++              if (dch->debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&dch->inst, "ph_state change %x->%x",
++                              dch->ph_state, val >> 4);
++              dch->ph_state = val >> 4;
++              dchannel_sched_event(dch, D_L1STATECHANGE);
++      }
++}
++
++static void
++isacsx_rme_irq(dchannel_t *dch)
++{
++      int count;
++      unsigned char val;
++
++      val = dch->read_reg(dch->inst.data, ISACSX_RSTAD);
++      if ((val & (ISACSX_RSTAD_VFR | 
++                  ISACSX_RSTAD_RDO | 
++                  ISACSX_RSTAD_CRC | 
++                  ISACSX_RSTAD_RAB)) 
++          != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) {
++              if (dch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&dch->inst, "RSTAD %#x, dropped", val);
++#ifdef ERROR_STATISTIC
++              if (val & ISACSX_RSTAD_CRC)
++                      dch->err_rx++;
++              else
++                      dch->err_crc++;
++#endif
++              dch->write_reg(dch->inst.data, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
++              if (dch->rx_skb)
++                      dev_kfree_skb(dch->rx_skb);
++      } else {
++              count = dch->read_reg(dch->inst.data, ISACSX_RBCLD) & 0x1f;
++              if (count == 0)
++                      count = 32;
++              isac_empty_fifo(dch, count);
++              if (dch->rx_skb) {
++                      skb_trim(dch->rx_skb, dch->rx_skb->len - 1);
++                      skb_queue_tail(&dch->rqueue, dch->rx_skb);
++              }
++      }
++      dch->rx_skb = NULL;
++      dchannel_sched_event(dch, D_RCVBUFREADY);
++}
++
++void
++mISDN_isac_interrupt(dchannel_t *dch, u_char val)
++{
++      if (dch->debug & L1_DEB_ISAC)
++              mISDN_debugprint(&dch->inst, "ISAC interrupt %02x", val);
++      if (dch->type & ISAC_TYPE_ISACSX) {
++              if (val & ISACSX_ISTA_CIC)
++                      isacsx_cic_irq(dch);
++              if (val & ISACSX_ISTA_ICD) {
++                      val = dch->read_reg(dch->inst.data, ISACSX_ISTAD);
++                      if (dch->debug & L1_DEB_ISAC)
++                              mISDN_debugprint(&dch->inst, "ISTAD %02x", val);
++                      if (val & ISACSX_ISTAD_XDU) {
++                              if (dch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&dch->inst, "ISAC XDU");
++#ifdef ERROR_STATISTIC
++                              dch->err_tx++;
++#endif
++                              isac_retransmit(dch);
++                      }
++                      if (val & ISACSX_ISTAD_XMR) {
++                              if (dch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&dch->inst, "ISAC XMR");
++#ifdef ERROR_STATISTIC
++                              dch->err_tx++;
++#endif
++                              isac_retransmit(dch);
++                      }
++                      if (val & ISACSX_ISTAD_XPR)
++                              isac_xpr_irq(dch);
++                      if (val & ISACSX_ISTAD_RFO) {
++                              if (dch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&dch->inst, "ISAC RFO");
++                              dch->write_reg(dch->inst.data, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
++                      }
++                      if (val & ISACSX_ISTAD_RME)
++                              isacsx_rme_irq(dch);
++                      if (val & ISACSX_ISTAD_RPF)
++                              isac_empty_fifo(dch, 0x20);
++              }
++      } else {
++              if (val & 0x80) /* RME */
++                      isac_rme_irq(dch);
++              if (val & 0x40) /* RPF */
++                      isac_empty_fifo(dch, 32);
++              if (val & 0x10) /* XPR */
++                      isac_xpr_irq(dch);
++              if (val & 0x04) /* CISQ */
++                      isac_cisq_irq(dch);
++              if (val & 0x20) /* RSC - never */
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "ISAC RSC interrupt");
++              if (val & 0x02) /* SIN - never */
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "ISAC SIN interrupt");
++              if (val & 0x01) {       /* EXI */
++                      val = dch->read_reg(dch->inst.data, ISAC_EXIR);
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "ISAC EXIR %02x", val);
++                      if (val & 0x80) /* XMR */
++                              mISDN_debugprint(&dch->inst, "ISAC XMR");
++                      if (val & 0x40) { /* XDU */
++                              if (dch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&dch->inst, "ISAC XDU");
++#ifdef ERROR_STATISTIC
++                              dch->err_tx++;
++#endif
++                              isac_retransmit(dch);
++                      }
++                      if (val & 0x04) /* MOS */
++                              isac_mos_irq(dch);
++              }
++      }
++}
++
++int
++mISDN_ISAC_l1hw(mISDNif_t *hif, struct sk_buff *skb)
++{
++      dchannel_t      *dch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      dch = hif->fdata;
++      ret = 0;
++      if (hh->prim == PH_DATA_REQ) {
++              if (dch->next_skb) {
++                      mISDN_debugprint(&dch->inst, " l2l1 next_skb exist this shouldn't happen");
++                      return(-EBUSY);
++              }
++              dch->inst.lock(dch->inst.data,0);
++              if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) {
++                      test_and_set_bit(FLG_TX_NEXT, &dch->DFlags);
++                      dch->next_skb = skb;
++                      dch->inst.unlock(dch->inst.data);
++                      return(0);
++              } else {
++                      dch->tx_len = skb->len;
++                      memcpy(dch->tx_buf, skb->data, dch->tx_len);
++                      dch->tx_idx = 0;
++                      isac_fill_fifo(dch);
++                      dch->inst.unlock(dch->inst.data);
++                      return(if_newhead(&dch->inst.up, PH_DATA_CNF,
++                              hh->dinfo, skb));
++              }
++      } else if (hh->prim == (PH_SIGNAL | REQUEST)) {
++              dch->inst.lock(dch->inst.data,0);
++              if (hh->dinfo == INFO3_P8)
++                      ph_command(dch, ISAC_CMD_AR8);
++              else if (hh->dinfo == INFO3_P10)
++                      ph_command(dch, ISAC_CMD_AR10);
++              else
++                      ret = -EINVAL;
++              dch->inst.unlock(dch->inst.data);
++      } else if (hh->prim == (PH_CONTROL | REQUEST)) {
++              dch->inst.lock(dch->inst.data,0);
++              if (hh->dinfo == HW_RESET) {
++                      if ((dch->ph_state == ISAC_IND_EI) ||
++                              (dch->ph_state == ISAC_IND_DR) ||
++                              (dch->ph_state == ISAC_IND_RS))
++                              ph_command(dch, ISAC_CMD_TIM);
++                      else
++                              ph_command(dch, ISAC_CMD_RS);
++              } else if (hh->dinfo == HW_POWERUP) {
++                      ph_command(dch, ISAC_CMD_TIM);
++              } else if (hh->dinfo == HW_DEACTIVATE) {
++                      discard_queue(&dch->rqueue);
++                      if (dch->next_skb) {
++                              dev_kfree_skb(dch->next_skb);
++                              dch->next_skb = NULL;
++                      }
++                      test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags);
++                      test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++                      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++                              del_timer(&dch->dbusytimer);
++                      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++                              dchannel_sched_event(dch, D_CLEARBUSY);
++              } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) {
++                      u_char          tl;
++                      if (dch->type & ISAC_TYPE_ISACSX) {
++                      /* TODO */
++                      } else {
++                              tl = 0;
++                              if (1 & hh->dinfo)
++                                      tl |= 0x0c;
++                              if (2 & hh->dinfo)
++                                      tl |= 0x3;
++                              if (ISAC_TYPE_IOM1 & dch->type) {
++                                      /* IOM 1 Mode */
++                                      if (!tl) {
++                                              dch->write_reg(dch->inst.data, ISAC_SPCR, 0xa);
++                                              dch->write_reg(dch->inst.data, ISAC_ADF1, 0x2);
++                                      } else {
++                                              dch->write_reg(dch->inst.data, ISAC_SPCR, tl);
++                                              dch->write_reg(dch->inst.data, ISAC_ADF1, 0xa);
++                                      }
++                              } else {
++                                      /* IOM 2 Mode */
++                                      dch->write_reg(dch->inst.data, ISAC_SPCR, tl);
++                                      if (tl)
++                                              dch->write_reg(dch->inst.data, ISAC_ADF1, 0x8);
++                                      else
++                                              dch->write_reg(dch->inst.data, ISAC_ADF1, 0x0);
++                              }
++                      }
++              } else {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "isac_l1hw unknown ctrl %x",
++                                      hh->dinfo);
++                      ret = -EINVAL;
++              }
++              dch->inst.unlock(dch->inst.data);
++      } else {
++              if (dch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&dch->inst, "isac_l1hw unknown prim %x",
++                              hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++void 
++mISDN_isac_free(dchannel_t *dch) {
++      isac_chip_t     *isac = dch->hw;
++
++      if (dch->dbusytimer.function != NULL) {
++              del_timer(&dch->dbusytimer);
++              dch->dbusytimer.function = NULL;
++      }
++      if (!isac)
++              return;
++      if (isac->mon_rx) {
++              kfree(isac->mon_rx);
++              isac->mon_rx = NULL;
++      }
++      if (isac->mon_tx) {
++              kfree(isac->mon_tx);
++              isac->mon_tx = NULL;
++      }
++}
++
++static void
++dbusy_timer_handler(dchannel_t *dch)
++{
++      int     rbch, star;
++
++      if (test_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
++              if (dch->inst.lock(dch->inst.data, 1)) {
++                      dch->dbusytimer.expires = jiffies + 1;
++                      add_timer(&dch->dbusytimer);
++                      return;
++              }
++              rbch = dch->read_reg(dch->inst.data, ISAC_RBCH);
++              star = dch->read_reg(dch->inst.data, ISAC_STAR);
++              if (dch->debug) 
++                      mISDN_debugprint(&dch->inst, "D-Channel Busy RBCH %02x STAR %02x",
++                              rbch, star);
++              if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */
++                      test_and_set_bit(FLG_L1_DBUSY, &dch->DFlags);
++#if 0
++                      stptr = dch->stlist;
++                      while (stptr != NULL) {
++                              stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
++                              stptr = stptr->next;
++                      }
++#endif
++              } else {
++                      /* discard frame; reset transceiver */
++                      test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags);
++                      if (dch->tx_idx) {
++                              dch->tx_idx = 0;
++                      } else {
++                              printk(KERN_WARNING "mISDN: ISAC D-Channel Busy no tx_idx\n");
++                              mISDN_debugprint(&dch->inst, "D-Channel Busy no tx_idx");
++                      }
++                      /* Transmitter reset */
++                      dch->write_reg(dch->inst.data, ISAC_CMDR, 0x01);
++              }
++              dch->inst.unlock(dch->inst.data);
++      }
++}
++
++static char *ISACVer[] =
++{"2086/2186 V1.1", "2085 B1", "2085 B2",
++ "2085 V2.3"};
++
++int
++mISDN_isac_init(dchannel_t *dch)
++{
++      isac_chip_t     *isac = dch->hw;
++      u_char          val;
++
++
++      if (!isac)
++              return(-EINVAL);
++      dch->hw_bh = isac_hwbh;
++      isac->mon_tx = NULL;
++      isac->mon_rx = NULL;
++      dch->dbusytimer.function = (void *) dbusy_timer_handler;
++      dch->dbusytimer.data = (long) dch;
++      init_timer(&dch->dbusytimer);
++      isac->mocr = 0xaa;
++      if (dch->type & ISAC_TYPE_ISACSX) {
++              // clear LDD
++              dch->write_reg(dch->inst.data, ISACSX_TR_CONF0, 0x00);
++              // enable transmitter
++              dch->write_reg(dch->inst.data, ISACSX_TR_CONF2, 0x00);
++              // transparent mode 0, RAC, stop/go
++              dch->write_reg(dch->inst.data, ISACSX_MODED, 0xc9);
++              // all HDLC IRQ unmasked
++              dch->write_reg(dch->inst.data, ISACSX_MASKD, 0x03);
++              // unmask ICD, CID IRQs
++              dch->write_reg(dch->inst.data, ISACSX_MASK, ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
++              printk(KERN_INFO "mISDN_isac_init: ISACSX\n");
++              dchannel_sched_event(dch, D_L1STATECHANGE);
++              ph_command(dch, ISAC_CMD_RS);
++      } else { /* old isac */
++              dch->write_reg(dch->inst.data, ISAC_MASK, 0xff);
++              val = dch->read_reg(dch->inst.data, ISAC_RBCH);
++              printk(KERN_INFO "mISDN_isac_init: ISAC version (%x): %s\n", val, ISACVer[(val >> 5) & 3]);
++              dch->type |= ((val >> 5) & 3);
++              if (ISAC_TYPE_IOM1 & dch->type) {
++                      /* IOM 1 Mode */
++                      dch->write_reg(dch->inst.data, ISAC_ADF2, 0x0);
++                      dch->write_reg(dch->inst.data, ISAC_SPCR, 0xa);
++                      dch->write_reg(dch->inst.data, ISAC_ADF1, 0x2);
++                      dch->write_reg(dch->inst.data, ISAC_STCR, 0x70);
++                      dch->write_reg(dch->inst.data, ISAC_MODE, 0xc9);
++              } else {
++                      /* IOM 2 Mode */
++                      if (!isac->adf2)
++                              isac->adf2 = 0x80;
++                      dch->write_reg(dch->inst.data, ISAC_ADF2, isac->adf2);
++                      dch->write_reg(dch->inst.data, ISAC_SQXR, 0x2f);
++                      dch->write_reg(dch->inst.data, ISAC_SPCR, 0x00);
++                      dch->write_reg(dch->inst.data, ISAC_STCR, 0x70);
++                      dch->write_reg(dch->inst.data, ISAC_MODE, 0xc9);
++                      dch->write_reg(dch->inst.data, ISAC_TIMR, 0x00);
++                      dch->write_reg(dch->inst.data, ISAC_ADF1, 0x00);
++              }
++              dchannel_sched_event(dch, D_L1STATECHANGE);
++              ph_command(dch, ISAC_CMD_RS);
++              dch->write_reg(dch->inst.data, ISAC_MASK, 0x0);
++      }
++      return 0;
++}
++
++void
++mISDN_clear_isac(dchannel_t *dch)
++{
++      isac_chip_t     *isac = dch->hw;
++      u_int           val, eval;
++
++      if (!isac)
++              return;
++      /* Disable all IRQ */
++      dch->write_reg(dch->inst.data, ISAC_MASK, 0xFF);
++      val = dch->read_reg(dch->inst.data, ISAC_STAR);
++      mISDN_debugprint(&dch->inst, "ISAC STAR %x", val);
++      val = dch->read_reg(dch->inst.data, ISAC_MODE);
++      mISDN_debugprint(&dch->inst, "ISAC MODE %x", val);
++      val = dch->read_reg(dch->inst.data, ISAC_ADF2);
++      mISDN_debugprint(&dch->inst, "ISAC ADF2 %x", val);
++      val = dch->read_reg(dch->inst.data, ISAC_ISTA);
++      mISDN_debugprint(&dch->inst, "ISAC ISTA %x", val);
++      if (val & 0x01) {
++              eval = dch->read_reg(dch->inst.data, ISAC_EXIR);
++              mISDN_debugprint(&dch->inst, "ISAC EXIR %x", eval);
++      }
++      val = dch->read_reg(dch->inst.data, ISAC_CIR0);
++      mISDN_debugprint(&dch->inst, "ISAC CIR0 %x", val);
++      dch->ph_state = (val >> 2) & 0xf;
++}
++
++#ifdef MODULE
++static int isac_mod_init(void)
++{
++      printk(KERN_INFO "ISAC module %s\n", isac_revision);
++      return(0);
++}
++
++static void isac_mod_cleanup(void)
++{
++      printk(KERN_INFO "ISAC module unloaded\n");
++}
++module_init(isac_mod_init);
++module_exit(isac_mod_cleanup);
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isac.h       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isac.h   2004-11-22 09:33:38.219733632 +0000
+@@ -0,0 +1,136 @@
++/* $Id$
++ *
++ * isac.h   ISAC specific defines
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++/* privat isac data */
++
++typedef struct isac_chip {
++      u_char                  *mon_tx;
++      u_char                  *mon_rx;
++      int                     mon_txp;
++      int                     mon_txc;
++      int                     mon_rxp;
++      struct arcofi_msg       *arcofi_list;
++      struct timer_list       arcofitimer;
++      wait_queue_head_t       arcofi_wait;
++      u_char                  arcofi_bc;
++      u_char                  arcofi_state;
++      u_char                  mocr;
++      u_char                  adf2;
++} isac_chip_t;
++
++#define ISAC_TYPE_ISAC                0x0010
++#define ISAC_TYPE_IPAC                0x0020
++#define ISAC_TYPE_ISACSX      0x0040
++#define ISAC_TYPE_IPACSX      0x0080
++#define ISAC_TYPE_IOM1                0x0100
++#define ISAC_TYPE_ARCOFI      0x1000
++
++
++/* All Registers original Siemens Spec  */
++
++#define ISAC_MASK 0x20
++#define ISAC_ISTA 0x20
++#define ISAC_STAR 0x21
++#define ISAC_CMDR 0x21
++#define ISAC_EXIR 0x24
++#define ISAC_ADF2 0x39
++#define ISAC_SPCR 0x30
++#define ISAC_ADF1 0x38
++#define ISAC_CIR0 0x31
++#define ISAC_CIX0 0x31
++#define ISAC_CIR1 0x33
++#define ISAC_CIX1 0x33
++#define ISAC_STCR 0x37
++#define ISAC_MODE 0x22
++#define ISAC_RSTA 0x27
++#define ISAC_RBCL 0x25
++#define ISAC_RBCH 0x2A
++#define ISAC_TIMR 0x23
++#define ISAC_SQXR 0x3b
++#define ISAC_MOSR 0x3a
++#define ISAC_MOCR 0x3a
++#define ISAC_MOR0 0x32
++#define ISAC_MOX0 0x32
++#define ISAC_MOR1 0x34
++#define ISAC_MOX1 0x34
++
++#define ISAC_RBCH_XAC 0x80
++
++#define ISAC_CMD_TIM  0x0
++#define ISAC_CMD_RS   0x1
++#define ISAC_CMD_SCZ  0x4
++#define ISAC_CMD_SSZ  0x2
++#define ISAC_CMD_AR8  0x8
++#define ISAC_CMD_AR10 0x9
++#define ISAC_CMD_ARL  0xA
++#define ISAC_CMD_DUI  0xF
++
++#define ISAC_IND_RS   0x1
++#define ISAC_IND_PU   0x7
++#define ISAC_IND_DR   0x0
++#define ISAC_IND_SD   0x2
++#define ISAC_IND_DIS  0x3
++#define ISAC_IND_EI   0x6
++#define ISAC_IND_RSY  0x4
++#define ISAC_IND_ARD  0x8
++#define ISAC_IND_TI   0xA
++#define ISAC_IND_ATI  0xB
++#define ISAC_IND_AI8  0xC
++#define ISAC_IND_AI10 0xD
++#define ISAC_IND_DID  0xF
++
++/* the new ISACX */
++#define ISACSX_MASK       0x60
++#define ISACSX_ISTA       0x60
++#define ISACSX_ISTA_ICD   0x01
++#define ISACSX_ISTA_CIC   0x10
++
++#define ISACSX_MASKD      0x20
++#define ISACSX_ISTAD      0x20
++#define ISACSX_ISTAD_XDU  0x04
++#define ISACSX_ISTAD_XMR  0x08
++#define ISACSX_ISTAD_XPR  0x10
++#define ISACSX_ISTAD_RFO  0x20
++#define ISACSX_ISTAD_RPF  0x40
++#define ISACSX_ISTAD_RME  0x80
++
++#define ISACSX_CMDRD      0x21
++#define ISACSX_CMDRD_XRES 0x01
++#define ISACSX_CMDRD_XME  0x02
++#define ISACSX_CMDRD_XTF  0x08
++#define ISACSX_CMDRD_RRES 0x40
++#define ISACSX_CMDRD_RMC  0x80
++
++#define ISACSX_MODED      0x22
++
++#define ISACSX_RBCLD      0x26
++
++#define ISACSX_RSTAD      0x28
++#define ISACSX_RSTAD_RAB  0x10
++#define ISACSX_RSTAD_CRC  0x20
++#define ISACSX_RSTAD_RDO  0x40
++#define ISACSX_RSTAD_VFR  0x80
++
++#define ISACSX_CIR0       0x2e
++#define ISACSX_CIR0_CIC0  0x08
++#define ISACSX_CIX0       0x2e
++
++#define ISACSX_TR_CONF0   0x30
++
++#define ISACSX_TR_CONF2   0x32
++
++/* interface for the isac module */
++
++extern int mISDN_isac_init(dchannel_t *);
++extern void mISDN_isac_free(dchannel_t *);
++
++extern void mISDN_isac_interrupt(dchannel_t *, u_char);
++extern void mISDN_clear_isac(dchannel_t *);
++extern int mISDN_ISAC_l1hw(mISDNif_t *, struct sk_buff *);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.c   2004-11-22 09:33:38.229732112 +0000
+@@ -0,0 +1,1727 @@
++/* $Id$
++ *
++ * isar.c   ISAR (Siemens PSB 7110) specific routines
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/delay.h>
++#include "layer1.h"
++#include "helper.h"
++#include "bchannel.h"
++#include "isar.h"
++#include "debug.h"
++
++#define DBG_LOADFIRM  0
++#define DUMP_MBOXFRAME        2
++
++#define MIN(a,b) ((a<b)?a:b)
++
++static char *ISAR_revision = "$Revision$";
++
++const u_char faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146";
++const u_char faxmodulation[] = {3,24,48,72,73,74,96,97,98,121,122,145,146};
++#define FAXMODCNT 13
++
++void isar_setup(bchannel_t *);
++static void isar_pump_cmd(bchannel_t *, int, u_char);
++
++static int firmwaresize = 0;
++static u_char *firmware;
++static u_char *fw_p;
++
++static inline int
++waitforHIA(bchannel_t *bch, int timeout)
++{
++
++      while ((bch->Read_Reg(bch->inst.data, 0, ISAR_HIA) & 1) && timeout) {
++              udelay(1);
++              timeout--;
++      }
++      if (!timeout)
++              printk(KERN_WARNING "mISDN: ISAR waitforHIA timeout\n");
++      return(timeout);
++}
++
++
++int
++sendmsg(bchannel_t *bch, u_char his, u_char creg, u_char len,
++      u_char *msg)
++{
++      int i;
++      
++      if (!waitforHIA(bch, 4000))
++              return(0);
++#if DUMP_MBOXFRAME
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "sendmsg(%02x,%02x,%d)", his, creg, len);
++#endif
++      bch->Write_Reg(bch->inst.data, 0, ISAR_CTRL_H, creg);
++      bch->Write_Reg(bch->inst.data, 0, ISAR_CTRL_L, len);
++      bch->Write_Reg(bch->inst.data, 0, ISAR_WADR, 0);
++      if (msg && len) {
++              bch->Write_Reg(bch->inst.data, 1, ISAR_MBOX, msg[0]);
++              for (i=1; i<len; i++)
++                      bch->Write_Reg(bch->inst.data, 2, ISAR_MBOX, msg[i]);
++#if DUMP_MBOXFRAME>1
++              if (bch->debug & L1_DEB_HSCX_FIFO) {
++                      char *t;
++                      
++                      i = len;
++                      while (i>0) {
++                              t = bch->blog;
++                              t += sprintf(t, "sendmbox cnt %d", len);
++                              mISDN_QuickHex(t, &msg[len-i], (i>64) ? 64:i);
++                              mISDN_debugprint(&bch->inst, bch->blog);
++                              i -= 64;
++                      }
++              }
++#endif
++      }
++      bch->Write_Reg(bch->inst.data, 1, ISAR_HIS, his);
++      waitforHIA(bch, 10000);
++      return(1);
++}
++
++/* Call only with IRQ disabled !!! */
++inline void
++rcv_mbox(bchannel_t *bch, isar_reg_t *ireg, u_char *msg)
++{
++      int i;
++
++      bch->Write_Reg(bch->inst.data, 1, ISAR_RADR, 0);
++      if (msg && ireg->clsb) {
++              msg[0] = bch->Read_Reg(bch->inst.data, 1, ISAR_MBOX);
++              for (i=1; i < ireg->clsb; i++)
++                       msg[i] = bch->Read_Reg(bch->inst.data, 2, ISAR_MBOX);
++#if DUMP_MBOXFRAME>1
++              if (bch->debug & L1_DEB_HSCX_FIFO) {
++                      char *t;
++                      
++                      i = ireg->clsb;
++                      while (i>0) {
++                              t = bch->blog;
++                              t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb);
++                              mISDN_QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i);
++                              mISDN_debugprint(&bch->inst, bch->blog);
++                              i -= 64;
++                      }
++              }
++#endif
++      }
++      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++}
++
++/* Call only with IRQ disabled !!! */
++inline void
++get_irq_infos(bchannel_t *bch, isar_reg_t *ireg)
++{
++      ireg->iis = bch->Read_Reg(bch->inst.data, 1, ISAR_IIS);
++      ireg->cmsb = bch->Read_Reg(bch->inst.data, 1, ISAR_CTRL_H);
++      ireg->clsb = bch->Read_Reg(bch->inst.data, 1, ISAR_CTRL_L);
++#if DUMP_MBOXFRAME
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb,
++                      ireg->clsb);
++#endif
++}
++
++int
++waitrecmsg(bchannel_t *bch, u_char *len,
++      u_char *msg, int maxdelay)
++{
++      int timeout = 0;
++      isar_hw_t *ih = bch->hw;
++      
++      
++      while((!(bch->Read_Reg(bch->inst.data, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) &&
++              (timeout++ < maxdelay))
++              udelay(1);
++      if (timeout >= maxdelay) {
++              printk(KERN_WARNING"isar recmsg IRQSTA timeout\n");
++              return(0);
++      }
++      get_irq_infos(bch, ih->reg);
++      rcv_mbox(bch, ih->reg, msg);
++      *len = ih->reg->clsb;
++      return(1);
++}
++
++int
++ISARVersion(bchannel_t *bch, char *s)
++{
++      int ver;
++      u_char msg[] = ISAR_MSG_HWVER;
++      u_char tmp[64];
++      u_char len;
++      isar_hw_t *ih = bch->hw;
++      int debug;
++
++//    bch->cardmsg(bch->inst.data, CARD_RESET,  NULL);
++      /* disable ISAR IRQ */
++      bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, 0);
++      debug = bch->debug;
++      bch->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
++      if (!sendmsg(bch, ISAR_HIS_VNR, 0, 3, msg))
++              return(-1);
++      if (!waitrecmsg(bch, &len, tmp, 100000))
++               return(-2);
++      bch->debug = debug;
++      if (ih->reg->iis == ISAR_IIS_VNR) {
++              if (len == 1) {
++                      ver = tmp[0] & 0xf;
++                      printk(KERN_INFO "%s ISAR version %d\n", s, ver);
++                      return(ver);
++              }
++              return(-3);
++      }
++      return(-4);
++}
++
++int
++isar_load_firmware(bchannel_t *bch, u_char *buf, int size)
++{
++      int ret, cnt, debug;
++      u_char len, nom, noc;
++      u_short sadr, left, *sp;
++      u_char *p = buf;
++      u_char *msg, *tmpmsg, *mp, tmp[64];
++      isar_hw_t *ih = bch->hw;
++      
++      struct {u_short sadr;
++              u_short len;
++              u_short d_key;
++      } *blk_head;
++              
++      bch->inst.lock(bch->inst.data, 0);
++#define       BLK_HEAD_SIZE 6
++      if (1 != (ret = ISARVersion(bch, "Testing"))) {
++              printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret);
++              bch->inst.unlock(bch->inst.data);
++              return(1);
++      }
++      debug = bch->debug;
++#if DBG_LOADFIRM<2
++      bch->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
++#endif
++      printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf);
++      printk(KERN_DEBUG"isar_load_firmware size: %d\n", size);
++      cnt = 0;
++      /* disable ISAR IRQ */
++      bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, 0);
++      if (!(msg = kmalloc(256, GFP_ATOMIC))) {
++              printk(KERN_ERR"isar_load_firmware no buffer\n");
++              bch->inst.unlock(bch->inst.data);
++              return (1);
++      }
++      while (cnt < size) {
++              blk_head = (void *)p;
++#ifdef __BIG_ENDIAN
++              sadr = (blk_head->sadr & 0xff)*256 + blk_head->sadr/256;
++              blk_head->sadr = sadr;
++              sadr = (blk_head->len & 0xff)*256 + blk_head->len/256;
++              blk_head->len = sadr;
++              sadr = (blk_head->d_key & 0xff)*256 + blk_head->d_key/256;
++              blk_head->d_key = sadr;
++#endif /* __BIG_ENDIAN */
++              cnt += BLK_HEAD_SIZE;
++              p += BLK_HEAD_SIZE;
++              printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n",
++                      blk_head->sadr, blk_head->len, blk_head->d_key & 0xff);
++              sadr = blk_head->sadr;
++              left = blk_head->len;
++              if (cnt+left > size) {
++                      printk(KERN_ERR"isar: firmware size error have %d need %d bytes\n",
++                              size, cnt+left);
++                      ret = 1;goto reterror;
++              }
++              if (!sendmsg(bch, ISAR_HIS_DKEY, blk_head->d_key & 0xff, 0, NULL)) {
++                      printk(KERN_ERR"isar sendmsg dkey failed\n");
++                      ret = 1;goto reterror;
++              }
++              if (!waitrecmsg(bch, &len, tmp, 100000)) {
++                      printk(KERN_ERR"isar waitrecmsg dkey failed\n");
++                      ret = 1;goto reterror;
++              }
++              if ((ih->reg->iis != ISAR_IIS_DKEY) || ih->reg->cmsb || len) {
++                      printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n",
++                              ih->reg->iis, ih->reg->cmsb, len);
++                      ret = 1;goto reterror;
++              }
++              while (left>0) {
++                      noc = MIN(126, left);
++                      nom = 2*noc;
++                      mp  = msg;
++                      *mp++ = sadr / 256;
++                      *mp++ = sadr % 256;
++                      left -= noc;
++                      *mp++ = noc;
++                      tmpmsg = p;
++                      p += nom;
++                      cnt += nom;
++                      nom += 3;
++                      sp = (u_short *)tmpmsg;
++#if DBG_LOADFIRM
++                      printk(KERN_DEBUG"isar: load %3d words at %04x\n",
++                               noc, sadr);
++#endif
++                      sadr += noc;
++                      while(noc) {
++#ifdef __BIG_ENDIAN
++                              *mp++ = *sp % 256;
++                              *mp++ = *sp / 256;
++#else
++                              *mp++ = *sp / 256;
++                              *mp++ = *sp % 256;
++#endif /* __BIG_ENDIAN */
++                              sp++;
++                              noc--;
++                      }
++                      if (!sendmsg(bch, ISAR_HIS_FIRM, 0, nom, msg)) {
++                              printk(KERN_ERR"isar sendmsg prog failed\n");
++                              ret = 1;goto reterror;
++                      }
++                      if (!waitrecmsg(bch, &len, tmp, 100000)) {
++                              printk(KERN_ERR"isar waitrecmsg prog failed\n");
++                              ret = 1;goto reterror;
++                      }
++                      if ((ih->reg->iis != ISAR_IIS_FIRM) || ih->reg->cmsb || len) {
++                              printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n",
++                                      ih->reg->iis, ih->reg->cmsb, len);
++                              ret = 1;goto reterror;
++                      }
++              }
++              printk(KERN_DEBUG"isar firmware block %5d words loaded\n",
++                      blk_head->len);
++      }
++      /* 10ms delay */
++      cnt = 10;
++      while (cnt--)
++              udelay(1000);
++      msg[0] = 0xff;
++      msg[1] = 0xfe;
++      ih->reg->bstat = 0;
++      if (!sendmsg(bch, ISAR_HIS_STDSP, 0, 2, msg)) {
++              printk(KERN_ERR"isar sendmsg start dsp failed\n");
++              ret = 1;goto reterror;
++      }
++      if (!waitrecmsg(bch, &len, tmp, 100000)) {
++              printk(KERN_ERR"isar waitrecmsg start dsp failed\n");
++              ret = 1;goto reterror;
++      }
++      if ((ih->reg->iis != ISAR_IIS_STDSP) || ih->reg->cmsb || len) {
++              printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n",
++                      ih->reg->iis, ih->reg->cmsb, len);
++              ret = 1;goto reterror;
++      } else
++              printk(KERN_DEBUG"isar start dsp success\n");
++      /* NORMAL mode entered */
++      /* Enable IRQs of ISAR */
++      bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, ISAR_IRQSTA);
++      bch->inst.unlock(bch->inst.data);
++      cnt = 1000; /* max 1s */
++      while ((!ih->reg->bstat) && cnt) {
++              mdelay(1);
++              cnt--;
++      }
++      if (!cnt) {
++              printk(KERN_ERR"isar no general status event received\n");
++              ret = 1;
++              goto reterrflg;
++      } else {
++              printk(KERN_DEBUG"isar general status event %x\n",
++                      ih->reg->bstat);
++      }
++      /* 10ms delay */
++      cnt = 10;
++      while (cnt--)
++              mdelay(1);
++      ih->reg->iis = 0;
++      bch->inst.lock(bch->inst.data, 0);
++      if (!sendmsg(bch, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
++              printk(KERN_ERR"isar sendmsg self tst failed\n");
++              ret = 1;goto reterror;
++      }
++      bch->inst.unlock(bch->inst.data);
++      cnt = 10000; /* max 100 ms */
++      while ((ih->reg->iis != ISAR_IIS_DIAG) && cnt) {
++              udelay(10);
++              cnt--;
++      }
++      mdelay(1);
++      if (!cnt) {
++              printk(KERN_ERR"isar no self tst response\n");
++              ret = 1;goto reterrflg;
++      }
++      if ((ih->reg->cmsb == ISAR_CTRL_STST) && (ih->reg->clsb == 1)
++              && (ih->reg->par[0] == 0)) {
++              printk(KERN_DEBUG"isar selftest OK\n");
++      } else {
++              printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n",
++                      ih->reg->cmsb, ih->reg->clsb, ih->reg->par[0]);
++              ret = 1;goto reterrflg;
++      }
++      bch->inst.lock(bch->inst.data, 0);
++      ih->reg->iis = 0;
++      if (!sendmsg(bch, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
++              printk(KERN_ERR"isar RQST SVN failed\n");
++              ret = 1;goto reterror;
++      }
++      bch->inst.unlock(bch->inst.data);
++      cnt = 30000; /* max 300 ms */
++      while ((ih->reg->iis != ISAR_IIS_DIAG) && cnt) {
++              udelay(10);
++              cnt--;
++      }
++      mdelay(1);
++      if (!cnt) {
++              printk(KERN_ERR"isar no SVN response\n");
++              ret = 1;goto reterrflg;
++      } else {
++              if ((ih->reg->cmsb == ISAR_CTRL_SWVER) && (ih->reg->clsb == 1))
++                      printk(KERN_DEBUG"isar software version %#x\n",
++                              ih->reg->par[0]);
++              else {
++                      
++                      printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n",
++                              ih->reg->cmsb, ih->reg->clsb, cnt);
++                      ret = 1;goto reterrflg;
++              }
++      }
++      bch->debug = debug;
++      bch->inst.lock(bch->inst.data, 0);
++      isar_setup(bch);
++      bch->inst.unlock(bch->inst.data);
++      bch->inst.obj->own_ctrl(&bch->inst, MGR_LOADFIRM | CONFIRM, NULL);
++      ret = 0;
++reterrflg:
++      bch->inst.lock(bch->inst.data, 0);
++reterror:
++      bch->debug = debug;
++      if (ret)
++              /* disable ISAR IRQ */
++              bch->Write_Reg(bch->inst.data, 0, ISAR_IRQBIT, 0);
++      bch->inst.unlock(bch->inst.data);
++      kfree(msg);
++      return(ret);
++}
++
++#define B_LL_READY    8
++#define B_LL_NOCARRIER        9
++#define B_LL_CONNECT  10
++#define B_LL_OK               11
++#define B_LL_FCERROR  12
++#define B_TOUCH_TONE  13
++
++static inline void
++deliver_status(bchannel_t *bch, int status)
++{
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "HL->LL FAXIND %x", status);
++      if_link(&bch->inst.up, PH_STATUS | INDICATION, status, 0, NULL, 0);
++}
++
++static void
++isar_bh(bchannel_t *bch)
++{
++      int     tt;
++
++      if (test_and_clear_bit(B_LL_READY, &bch->event))
++              deliver_status(bch, HW_MOD_READY);
++      if (test_and_clear_bit(B_LL_NOCARRIER, &bch->event))
++              deliver_status(bch, HW_MOD_NOCARR);
++      if (test_and_clear_bit(B_LL_CONNECT, &bch->event))
++              deliver_status(bch, HW_MOD_CONNECT);
++      if (test_and_clear_bit(B_LL_OK, &bch->event))
++              deliver_status(bch, HW_MOD_OK);
++      if (test_and_clear_bit(B_LL_FCERROR, &bch->event))
++              deliver_status(bch, HW_MOD_FCERROR);
++      if (test_and_clear_bit(B_TOUCH_TONE, &bch->event)) {
++              tt = bch->conmsg[0] | 0x30;
++              if (tt == 0x3e)
++                      tt = '*';
++              else if (tt == 0x3f)
++                      tt = '#';
++              else if (tt > '9')
++                      tt += 7;
++              tt |= DTMF_TONE_VAL;
++              if_link(&bch->inst.up, PH_CONTROL | INDICATION,
++                      0, sizeof(int), &tt, 0);
++      }
++}
++
++static inline void
++isar_rcv_frame(bchannel_t *bch)
++{
++      u_char          *ptr;
++      struct sk_buff  *skb;
++      isar_hw_t       *ih = bch->hw;
++      
++      if (!ih->reg->clsb) {
++              mISDN_debugprint(&bch->inst, "isar zero len frame");
++              bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++              return;
++      }
++      switch (bch->protocol) {
++          case ISDN_PID_NONE:
++              mISDN_debugprint(&bch->inst, "isar protocol 0 spurious IIS_RDATA %x/%x/%x",
++                      ih->reg->iis, ih->reg->cmsb, ih->reg->clsb);
++              printk(KERN_WARNING"isar protocol 0 spurious IIS_RDATA %x/%x/%x\n",
++                      ih->reg->iis, ih->reg->cmsb, ih->reg->clsb);
++              bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++              break;
++          case ISDN_PID_L1_B_64TRANS:
++          case ISDN_PID_L2_B_TRANSDTMF:
++          case ISDN_PID_L1_B_MODEM_ASYNC:
++              if ((skb = alloc_stack_skb(ih->reg->clsb, bch->up_headerlen))) {
++                      rcv_mbox(bch, ih->reg, (u_char *)skb_put(skb, ih->reg->clsb));
++                      skb_queue_tail(&bch->rqueue, skb);
++                      bch_sched_event(bch, B_RCVBUFREADY);
++              } else {
++                      printk(KERN_WARNING "mISDN: skb out of memory\n");
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++              }
++              break;
++          case ISDN_PID_L1_B_64HDLC:
++              if ((bch->rx_idx + ih->reg->clsb) > MAX_DATA_MEM) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "isar_rcv_frame: incoming packet too large");
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      bch->rx_idx = 0;
++              } else if (ih->reg->cmsb & HDLC_ERROR) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "isar frame error %x len %d",
++                                      ih->reg->cmsb, ih->reg->clsb);
++#ifdef ERROR_STATISTIC
++                      if (ih->reg->cmsb & HDLC_ERR_RER)
++                              bch->err_inv++;
++                      if (ih->reg->cmsb & HDLC_ERR_CER)
++                              bch->err_crc++;
++#endif
++                      bch->rx_idx = 0;
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++              } else {
++                      if (ih->reg->cmsb & HDLC_FSD)
++                              bch->rx_idx = 0;
++                      ptr = bch->rx_buf + bch->rx_idx;
++                      bch->rx_idx += ih->reg->clsb;
++                      rcv_mbox(bch, ih->reg, ptr);
++                      if (ih->reg->cmsb & HDLC_FED) {
++                              if (bch->rx_idx < 3) { /* last 2 bytes are the FCS */
++                                      if (bch->debug & L1_DEB_WARN)
++                                              mISDN_debugprint(&bch->inst, "isar frame to short %d",
++                                                      bch->rx_idx);
++                              } else if (!(skb = alloc_stack_skb(bch->rx_idx-2, bch->up_headerlen))) {
++                                      printk(KERN_WARNING "ISAR: receive out of memory\n");
++                              } else {
++                                      memcpy(skb_put(skb, bch->rx_idx-2),
++                                              bch->rx_buf, bch->rx_idx-2);
++                                      skb_queue_tail(&bch->rqueue, skb);
++                                      bch_sched_event(bch, B_RCVBUFREADY);
++                              }
++                              bch->rx_idx = 0;
++                      }
++              }
++              break;
++      case ISDN_PID_L1_B_T30FAX:
++              if (ih->state != STFAX_ACTIV) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "isar_rcv_frame: not ACTIV");
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      bch->rx_idx = 0;
++                      break;
++              }
++              if (ih->cmd == PCTRL_CMD_FRM) {
++                      rcv_mbox(bch, ih->reg, bch->rx_buf);
++                      bch->rx_idx = ih->reg->clsb;
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "isar_rcv_frame: %d", bch->rx_idx);
++                      if ((skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen))) {
++                              memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx);
++                              if (ih->reg->cmsb & SART_NMD) { /* ABORT */
++                                      if (bch->debug & L1_DEB_WARN)
++                                              mISDN_debugprint(&bch->inst, "isar_rcv_frame: no more data");
++                                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                                      bch->rx_idx = 0;
++                                      sendmsg(bch, SET_DPS(ih->dpath) |
++                                              ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
++                                              0, NULL);
++                                      ih->state = STFAX_ESCAPE;
++//                                    set_skb_flag(skb, DF_NOMOREDATA);
++                              }
++                              skb_queue_tail(&bch->rqueue, skb);
++                              bch_sched_event(bch, B_RCVBUFREADY);
++                              if (ih->reg->cmsb & SART_NMD)
++                                      bch_sched_event(bch, B_LL_NOCARRIER);
++                      } else {
++                              printk(KERN_WARNING "mISDN: skb out of memory\n");
++                              bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      }
++                      break;
++              }
++              if (ih->cmd != PCTRL_CMD_FRH) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "isar_rcv_frame: unknown fax mode %x",
++                                      ih->cmd);
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      bch->rx_idx = 0;
++                      break;
++              }
++              /* PCTRL_CMD_FRH */
++              if ((bch->rx_idx + ih->reg->clsb) > MAX_DATA_MEM) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "isar_rcv_frame: incoming packet too large");
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      bch->rx_idx = 0;
++              } else if (ih->reg->cmsb & HDLC_ERROR) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "isar frame error %x len %d",
++                                      ih->reg->cmsb, ih->reg->clsb);
++                      bch->rx_idx = 0;
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++              } else {
++                      if (ih->reg->cmsb & HDLC_FSD)
++                              bch->rx_idx = 0;
++                      ptr = bch->rx_buf + bch->rx_idx;
++                      bch->rx_idx += ih->reg->clsb;
++                      rcv_mbox(bch, ih->reg, ptr);
++                      if (ih->reg->cmsb & HDLC_FED) {
++                              if (bch->rx_idx < 3) { /* last 2 bytes are the FCS */
++                                      if (bch->debug & L1_DEB_WARN)
++                                              mISDN_debugprint(&bch->inst, "isar frame to short %d",
++                                                      bch->rx_idx);
++                              } else if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen))) {
++                                      printk(KERN_WARNING "ISAR: receive out of memory\n");
++                              } else {
++                                      memcpy(skb_put(skb, bch->rx_idx-2), bch->rx_buf, bch->rx_idx-2);
++//                                    if (ih->reg->cmsb & SART_NMD)
++                                               /* ABORT */
++//                                            set_skb_flag(skb, DF_NOMOREDATA);
++                                      skb_queue_tail(&bch->rqueue, skb);
++                                      bch_sched_event(bch, B_RCVBUFREADY);
++                              }
++                              bch->rx_idx = 0;
++                      }
++              }
++              if (ih->reg->cmsb & SART_NMD) { /* ABORT */
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "isar_rcv_frame: no more data");
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      bch->rx_idx = 0;
++                      sendmsg(bch, SET_DPS(ih->dpath) |
++                              ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
++                      ih->state = STFAX_ESCAPE;
++                      bch_sched_event(bch, B_LL_NOCARRIER);
++              }
++              break;
++      default:
++              printk(KERN_ERR"isar_rcv_frame protocol (%x)error\n", bch->protocol);
++              bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++              break;
++      }
++}
++
++void
++isar_fill_fifo(bchannel_t *bch)
++{
++      isar_hw_t       *ih = bch->hw;
++      int count;
++      u_char msb;
++      u_char *ptr;
++
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "%s", __FUNCTION__);
++      count = bch->tx_len - bch->tx_idx;
++      if (count <= 0)
++              return;
++      if (!(ih->reg->bstat &
++              (ih->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
++              return;
++      if (count > ih->mml) {
++              msb = 0;
++              count = ih->mml;
++      } else {
++              msb = HDLC_FED;
++      }
++      ptr = bch->tx_buf + bch->tx_idx;
++      if (!bch->tx_idx) {
++              if (bch->debug & L1_DEB_HSCX)
++                      mISDN_debugprint(&bch->inst, "frame start");
++              if ((bch->protocol == ISDN_PID_L1_B_T30FAX) &&
++                      (ih->cmd == PCTRL_CMD_FTH)) {
++                      if (count > 1) {
++                              if ((ptr[0]== 0xff) && (ptr[1] == 0x13)) {
++                                      /* last frame */
++                                      test_and_set_bit(BC_FLG_LASTDATA, &bch->Flag);
++                                      if (bch->debug & L1_DEB_HSCX)
++                                              mISDN_debugprint(&bch->inst, "set LASTDATA");
++                                      if (msb == HDLC_FED)
++                                              test_and_set_bit(BC_FLG_DLEETX, &bch->Flag);
++                              }
++                      }
++              }
++              msb |= HDLC_FST;
++      }
++      bch->tx_idx += count;
++      switch (bch->protocol) {
++              case ISDN_PID_NONE:
++                      printk(KERN_ERR "%s: wrong protocol 0\n", __FUNCTION__);
++                      break;
++              case ISDN_PID_L1_B_64TRANS:
++              case ISDN_PID_L2_B_TRANSDTMF:
++              case ISDN_PID_L1_B_MODEM_ASYNC:
++                      sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA,
++                              0, count, ptr);
++                      break;
++              case ISDN_PID_L1_B_64HDLC:
++                      sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA,
++                              msb, count, ptr);
++                      break;
++              case ISDN_PID_L1_B_T30FAX:
++                      if (ih->state != STFAX_ACTIV) {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "%s: not ACTIV",
++                                              __FUNCTION__);
++                      } else if (ih->cmd == PCTRL_CMD_FTH) {
++                              sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA,
++                                      msb, count, ptr);
++                      } else if (ih->cmd == PCTRL_CMD_FTM) {
++                              sendmsg(bch, SET_DPS(ih->dpath) | ISAR_HIS_SDATA,
++                                      0, count, ptr);
++                      } else {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "%s: not FTH/FTM",
++                                              __FUNCTION__);
++                      }
++                      break;
++              default:
++                      if (bch->debug)
++                              mISDN_debugprint(&bch->inst, "%s: protocol(%x) error",
++                                      __FUNCTION__, bch->protocol);
++                      printk(KERN_ERR "%s: protocol(%x) error\n",
++                              __FUNCTION__, bch->protocol);
++                      break;
++      }
++}
++
++inline
++bchannel_t *sel_bch_isar(bchannel_t *bch, u_char dpath)
++{
++
++      if ((!dpath) || (dpath == 3))
++              return(NULL);
++      
++      if (((isar_hw_t *)bch[0].hw)->dpath == dpath)
++              return(&bch[0]);
++      if (((isar_hw_t *)bch[1].hw)->dpath == dpath)
++              return(&bch[1]);
++      return(NULL);
++}
++
++inline void
++send_frames(bchannel_t *bch)
++{
++      isar_hw_t       *ih = bch->hw;
++
++      if (bch->tx_len - bch->tx_idx) {
++              isar_fill_fifo(bch);
++      } else {
++              bch->tx_idx = 0;
++              if (bch->protocol == ISDN_PID_L1_B_T30FAX) {
++                      if (ih->cmd == PCTRL_CMD_FTH) {
++                              if (test_bit(BC_FLG_LASTDATA, &bch->Flag)) {
++                                      printk(KERN_WARNING "set NMD_DATA\n");
++                                      test_and_set_bit(BC_FLG_NMD_DATA, &bch->Flag);
++                              }
++                      } else if (ih->cmd == PCTRL_CMD_FTM) {
++                              if (test_bit(BC_FLG_DLEETX, &bch->Flag)) {
++                                      test_and_set_bit(BC_FLG_LASTDATA, &bch->Flag);
++                                      test_and_set_bit(BC_FLG_NMD_DATA, &bch->Flag);
++                              }
++                      }
++              }
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      if (bch->next_skb) {
++                              bch->tx_len = bch->next_skb->len;
++                              memcpy(bch->tx_buf,
++                                      bch->next_skb->data, bch->tx_len);
++                              isar_fill_fifo(bch);
++                              bch_sched_event(bch, B_XMTBUFREADY);
++                      } else {
++                              bch->tx_len = 0;
++                              printk(KERN_WARNING "isar tx irq TX_NEXT without skb\n");
++                              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                      }
++              } else {
++                      bch->tx_len = 0;
++                      if (test_and_clear_bit(BC_FLG_DLEETX, &bch->Flag)) {
++                              if (test_and_clear_bit(BC_FLG_LASTDATA, &bch->Flag)) {
++                                      if (test_and_clear_bit(BC_FLG_NMD_DATA, &bch->Flag)) {
++                                              u_char dummy = 0;
++                                              sendmsg(bch, SET_DPS(ih->dpath) |
++                                                      ISAR_HIS_SDATA, 0x01, 1, &dummy);
++                                      }
++                                      test_and_set_bit(BC_FLG_LL_OK, &bch->Flag);
++                              } else {
++                                      bch_sched_event(bch, B_LL_CONNECT);
++                              }
++                      }
++                      test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                      bch_sched_event(bch, B_XMTBUFREADY);
++              }
++      }
++}
++
++inline void
++check_send(bchannel_t *bch, u_char rdm)
++{
++      bchannel_t *bc;
++      
++      if (rdm & BSTAT_RDM1) {
++              if ((bc = sel_bch_isar(bch, 1))) {
++                      if (bc->protocol) {
++                              send_frames(bc);
++                      }
++              }
++      }
++      if (rdm & BSTAT_RDM2) {
++              if ((bc = sel_bch_isar(bch, 2))) {
++                      if (bc->protocol) {
++                              send_frames(bc);
++                      }
++              }
++      }
++      
++}
++
++const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
++                      "300", "600", "1200", "2400", "4800", "7200",
++                      "9600nt", "9600t", "12000", "14400", "WRONG"};
++const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
++                      "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"};
++
++static void
++isar_pump_status_rsp(bchannel_t *bch, isar_reg_t *ireg) {
++      isar_hw_t       *ih = bch->hw;
++      u_char ril = ireg->par[0];
++      u_char rim;
++
++      if (!test_and_clear_bit(ISAR_RATE_REQ, &ireg->Flags))
++              return;
++      if (ril > 14) {
++              if (bch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&bch->inst, "wrong pstrsp ril=%d",ril);
++              ril = 15;
++      }
++      switch(ireg->par[1]) {
++              case 0:
++                      rim = 0;
++                      break;
++              case 0x20:
++                      rim = 2;
++                      break;
++              case 0x40:
++                      rim = 3;
++                      break;
++              case 0x41:
++                      rim = 4;
++                      break;
++              case 0x51:
++                      rim = 5;
++                      break;
++              case 0x61:
++                      rim = 6;
++                      break;
++              case 0x71:
++                      rim = 7;
++                      break;
++              case 0x82:
++                      rim = 8;
++                      break;
++              case 0x92:
++                      rim = 9;
++                      break;
++              case 0xa2:
++                      rim = 10;
++                      break;
++              default:
++                      rim = 1;
++                      break;
++      }
++      sprintf(ih->conmsg,"%s %s", dmril[ril], dmrim[rim]);
++      bch->conmsg = ih->conmsg;
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "pump strsp %s", bch->conmsg);
++}
++
++static void
++isar_pump_statev_modem(bchannel_t *bch, u_char devt) {
++      isar_hw_t       *ih = bch->hw;
++      u_char dps = SET_DPS(ih->dpath);
++
++      switch(devt) {
++              case PSEV_10MS_TIMER:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev TIMER");
++                      break;
++              case PSEV_CON_ON:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev CONNECT");
++                      bch_sched_event(bch, B_LL_CONNECT);
++                      break;
++              case PSEV_CON_OFF:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev NO CONNECT");
++                      sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
++                      bch_sched_event(bch, B_LL_NOCARRIER);
++                      break;
++              case PSEV_V24_OFF:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev V24 OFF");
++                      break;
++              case PSEV_CTS_ON:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev CTS ON");
++                      break;
++              case PSEV_CTS_OFF:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev CTS OFF");
++                      break;
++              case PSEV_DCD_ON:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev CARRIER ON");
++                      test_and_set_bit(ISAR_RATE_REQ, &ih->reg->Flags);
++                      sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
++                      break;
++              case PSEV_DCD_OFF:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev CARRIER OFF");
++                      break;
++              case PSEV_DSR_ON:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev DSR ON");
++                      break;
++              case PSEV_DSR_OFF:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev DSR_OFF");
++                      break;
++              case PSEV_REM_RET:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev REMOTE RETRAIN");
++                      break;
++              case PSEV_REM_REN:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev REMOTE RENEGOTIATE");
++                      break;
++              case PSEV_GSTN_CLR:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev GSTN CLEAR", devt);
++                      break;
++              default:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "unknown pump stev %x", devt);
++                      break;
++      }
++}
++
++static void
++isar_pump_statev_fax(bchannel_t *bch, u_char devt) {
++      isar_hw_t       *ih = bch->hw;
++      u_char dps = SET_DPS(ih->dpath);
++      u_char p1;
++
++      switch(devt) {
++              case PSEV_10MS_TIMER:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev TIMER");
++                      break;
++              case PSEV_RSP_READY:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev RSP_READY");
++                      ih->state = STFAX_READY;
++                      bch_sched_event(bch, B_LL_READY);
++//                    if (test_bit(BC_FLG_ORIG, &bch->Flag)) {
++//                            isar_pump_cmd(bch, HW_MOD_FRH, 3);
++//                    } else {
++//                            isar_pump_cmd(bch, HW_MOD_FTH, 3);
++//                    }
++                      break;
++              case PSEV_LINE_TX_H:
++                      if (ih->state == STFAX_LINE) {
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_TX_H");
++                              ih->state = STFAX_CONT;
++                              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
++                      } else {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_TX_H wrong st %x",
++                                              ih->state);
++                      }
++                      break;
++              case PSEV_LINE_RX_H:
++                      if (ih->state == STFAX_LINE) {
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_RX_H");
++                              ih->state = STFAX_CONT;
++                              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
++                      } else {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_RX_H wrong st %x",
++                                              ih->state);
++                      }
++                      break;
++              case PSEV_LINE_TX_B:
++                      if (ih->state == STFAX_LINE) {
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_TX_B");
++                              ih->state = STFAX_CONT;
++                              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
++                      } else {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_TX_B wrong st %x",
++                                              ih->state);
++                      }
++                      break;
++              case PSEV_LINE_RX_B:
++                      if (ih->state == STFAX_LINE) {
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_RX_B");
++                              ih->state = STFAX_CONT;
++                              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_CONT, 0, NULL);
++                      } else {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "pump stev LINE_RX_B wrong st %x",
++                                              ih->state);
++                      }
++                      break;
++              case PSEV_RSP_CONN:
++                      if (ih->state == STFAX_CONT) {
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "pump stev RSP_CONN");
++                              ih->state = STFAX_ACTIV;
++                              test_and_set_bit(ISAR_RATE_REQ, &ih->reg->Flags);
++                              sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
++                              if (ih->cmd == PCTRL_CMD_FTH) {
++                                      int delay = (ih->mod == 3) ? 1000 : 200;
++                                      /* 1s (200 ms) Flags before data */
++                                      if (test_and_set_bit(BC_FLG_FTI_RUN, &bch->Flag))
++                                              del_timer(&ih->ftimer);
++                                      ih->ftimer.expires =
++                                              jiffies + ((delay * HZ)/1000);
++                                      test_and_set_bit(BC_FLG_LL_CONN,
++                                              &bch->Flag);
++                                      add_timer(&ih->ftimer);
++                              } else {
++                                      bch_sched_event(bch, B_LL_CONNECT);
++                              }
++                      } else {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "pump stev RSP_CONN wrong st %x",
++                                              ih->state);
++                      }
++                      break;
++              case PSEV_FLAGS_DET:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev FLAGS_DET");
++                      break;
++              case PSEV_RSP_DISC:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev RSP_DISC state(%d)", ih->state);
++                      if (ih->state == STFAX_ESCAPE) {
++                              p1 = 5;
++                              switch(ih->newcmd) {
++                                      case 0:
++                                              ih->state = STFAX_READY;
++                                              break;
++                                      case PCTRL_CMD_FTM:
++                                              p1 = 2;
++                                      case PCTRL_CMD_FTH:
++                                              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL,
++                                                      PCTRL_CMD_SILON, 1, &p1);
++                                              ih->state = STFAX_SILDET;
++                                              break;
++                                      case PCTRL_CMD_FRH:
++                                      case PCTRL_CMD_FRM:
++                                              p1 = ih->mod = ih->newmod;
++                                              ih->newmod = 0;
++                                              ih->cmd = ih->newcmd;
++                                              ih->newcmd = 0;
++                                              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL,
++                                                      ih->cmd, 1, &p1);
++                                              ih->state = STFAX_LINE;
++                                              ih->try_mod = 3;
++                                              break;
++                                      default:
++                                              if (bch->debug & L1_DEB_HSCX)
++                                                      mISDN_debugprint(&bch->inst, "RSP_DISC unknown newcmd %x", ih->newcmd);
++                                              break;
++                              }
++                      } else if (ih->state == STFAX_ACTIV) {
++                              if (test_and_clear_bit(BC_FLG_LL_OK, &bch->Flag)) {
++                                      bch_sched_event(bch, B_LL_OK);
++                              } else if (ih->cmd == PCTRL_CMD_FRM) {
++                                      bch_sched_event(bch, B_LL_NOCARRIER);
++                              } else {
++                                      bch_sched_event(bch, B_LL_FCERROR);
++                              }
++                              ih->state = STFAX_READY;
++                      } else if (ih->state != STFAX_SILDET) { // ignore in STFAX_SILDET
++                              ih->state = STFAX_READY;
++                              bch_sched_event(bch, B_LL_FCERROR);
++                      }
++                      break;
++              case PSEV_RSP_SILDET:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev RSP_SILDET");
++                      if (ih->state == STFAX_SILDET) {
++                              p1 = ih->mod = ih->newmod;
++                              ih->newmod = 0;
++                              ih->cmd = ih->newcmd;
++                              ih->newcmd = 0;
++                              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL,
++                                      ih->cmd, 1, &p1);
++                              ih->state = STFAX_LINE;
++                              ih->try_mod = 3;
++                      }
++                      break;
++              case PSEV_RSP_SILOFF:
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev RSP_SILOFF");
++                      break;
++              case PSEV_RSP_FCERR:
++                      if (ih->state == STFAX_LINE) {
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "pump stev RSP_FCERR try %d",
++                                              ih->try_mod);
++                              if (ih->try_mod--) {
++                                      sendmsg(bch, dps | ISAR_HIS_PUMPCTRL,
++                                              ih->cmd, 1,
++                                              &ih->mod);
++                                      break;
++                              }
++                      }
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "pump stev RSP_FCERR");
++                      ih->state = STFAX_ESCAPE;
++                      sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
++                      bch_sched_event(bch, B_LL_FCERROR);
++                      break;
++              default:
++                      break;
++      }
++}
++
++static char debbuf[128];
++
++void
++isar_int_main(bchannel_t *bch)
++{
++      isar_hw_t       *ih = bch->hw;
++      bchannel_t *bc;
++
++      get_irq_infos(bch, ih->reg);
++      switch (ih->reg->iis & ISAR_IIS_MSCMSD) {
++              case ISAR_IIS_RDATA:
++                      if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) {
++                              isar_rcv_frame(bc);
++                      } else {
++                              mISDN_debugprint(&bch->inst, "isar spurious IIS_RDATA %x/%x/%x",
++                                      ih->reg->iis, ih->reg->cmsb, ih->reg->clsb);
++                              bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      }
++                      break;
++              case ISAR_IIS_GSTEV:
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      ih->reg->bstat |= ih->reg->cmsb;
++                      check_send(bch, ih->reg->cmsb);
++                      break;
++              case ISAR_IIS_BSTEV:
++#ifdef ERROR_STATISTIC
++                      if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) {
++                              if (ih->reg->cmsb == BSTEV_TBO)
++                                      bc->err_tx++;
++                              if (ih->reg->cmsb == BSTEV_RBO)
++                                      bc->err_rdo++;
++                      }
++#endif
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "Buffer STEV dpath%d msb(%x)",
++                                      ih->reg->iis>>6, ih->reg->cmsb);
++                      bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      break;
++              case ISAR_IIS_PSTEV:
++                      if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) {
++                              rcv_mbox(bc, ih->reg, (u_char *)ih->reg->par);
++                              if (bc->protocol == ISDN_PID_L1_B_MODEM_ASYNC) {
++                                      isar_pump_statev_modem(bc, ih->reg->cmsb);
++                              } else if (bc->protocol == ISDN_PID_L1_B_T30FAX) {
++                                      isar_pump_statev_fax(bc, ih->reg->cmsb);
++                              } else if (bc->protocol == ISDN_PID_L2_B_TRANSDTMF) {
++                                      ih->conmsg[0] = ih->reg->cmsb;
++                                      bch->conmsg = ih->conmsg;
++                                      bch_sched_event(bc, B_TOUCH_TONE);
++                              } else {
++                                      if (bch->debug & L1_DEB_WARN)
++                                              mISDN_debugprint(&bch->inst, "isar IIS_PSTEV pmode %d stat %x",
++                                                      bc->protocol, ih->reg->cmsb);
++                              }
++                      } else {
++                              mISDN_debugprint(&bch->inst, "isar spurious IIS_PSTEV %x/%x/%x",
++                                      ih->reg->iis, ih->reg->cmsb, ih->reg->clsb);
++                              bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      }
++                      break;
++              case ISAR_IIS_PSTRSP:
++                      if ((bc = sel_bch_isar(bch, ih->reg->iis >> 6))) {
++                              rcv_mbox(bc, ih->reg, (u_char *)ih->reg->par);
++                              isar_pump_status_rsp(bc, ih->reg);
++                      } else {
++                              mISDN_debugprint(&bch->inst, "isar spurious IIS_PSTRSP %x/%x/%x",
++                                      ih->reg->iis, ih->reg->cmsb, ih->reg->clsb);
++                              bch->Write_Reg(bch->inst.data, 1, ISAR_IIA, 0);
++                      }
++                      break;
++              case ISAR_IIS_DIAG:
++              case ISAR_IIS_BSTRSP:
++              case ISAR_IIS_IOM2RSP:
++                      rcv_mbox(bch, ih->reg, (u_char *)ih->reg->par);
++                      if ((bch->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO))
++                              == L1_DEB_HSCX) {
++                              u_char *tp=debbuf;
++
++                              tp += sprintf(debbuf, "msg iis(%x) msb(%x)",
++                                      ih->reg->iis, ih->reg->cmsb);
++                              mISDN_QuickHex(tp, (u_char *)ih->reg->par, ih->reg->clsb);
++                              mISDN_debugprint(&bch->inst, debbuf);
++                      }
++                      break;
++              case ISAR_IIS_INVMSG:
++                      rcv_mbox(bch, ih->reg, debbuf);
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "invalid msg his:%x",
++                                      ih->reg->cmsb);
++                      break;
++              default:
++                      rcv_mbox(bch, ih->reg, debbuf);
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "unhandled msg iis(%x) ctrl(%x/%x)",
++                                      ih->reg->iis, ih->reg->cmsb, ih->reg->clsb);
++                      break;
++      }
++}
++
++static void
++ftimer_handler(bchannel_t *bch) {
++      if (bch->debug)
++              mISDN_debugprint(&bch->inst, "ftimer flags %04x",
++                      bch->Flag);
++      test_and_clear_bit(BC_FLG_FTI_RUN, &bch->Flag);
++      if (test_and_clear_bit(BC_FLG_LL_CONN, &bch->Flag)) {
++              bch_sched_event(bch, B_LL_CONNECT);
++      }
++}
++
++static void
++setup_pump(bchannel_t *bch) {
++      isar_hw_t       *ih = bch->hw;
++      u_char dps = SET_DPS(ih->dpath);
++      u_char ctrl, param[6];
++
++      switch (bch->protocol) {
++              case ISDN_PID_NONE:
++              case ISDN_PID_L1_B_64TRANS:
++              case ISDN_PID_L1_B_64HDLC:
++                      sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
++                      break;
++              case ISDN_PID_L2_B_TRANSDTMF:
++                      if (test_bit(BC_FLG_DTMFSEND, &bch->Flag)) {
++                              param[0] = 5; /* TOA 5 db */
++                              sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_DTMF_TRANS, 1, param);
++                      } else {
++                              param[0] = 40; /* REL -46 dbm */
++                              sendmsg(bch, dps | ISAR_HIS_PUMPCFG, PMOD_DTMF, 1, param);
++                      }
++              case ISDN_PID_L1_B_MODEM_ASYNC:
++                      ctrl = PMOD_DATAMODEM;
++                      if (test_bit(BC_FLG_ORIG, &bch->Flag)) {
++                              ctrl |= PCTRL_ORIG;
++                              param[5] = PV32P6_CTN;
++                      } else {
++                              param[5] = PV32P6_ATN;
++                      }
++                      param[0] = 6; /* 6 db */
++                      param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B |
++                                 PV32P2_V22C | PV32P2_V21 | PV32P2_BEL;
++                      param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B;
++                      param[3] = PV32P4_UT144;
++                      param[4] = PV32P5_UT144;
++                      sendmsg(bch, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
++                      break;
++              case ISDN_PID_L1_B_T30FAX:
++                      ctrl = PMOD_FAX;
++                      if (test_bit(BC_FLG_ORIG, &bch->Flag)) {
++                              ctrl |= PCTRL_ORIG;
++                              param[1] = PFAXP2_CTN;
++                      } else {
++                              param[1] = PFAXP2_ATN;
++                      }
++                      param[0] = 6; /* 6 db */
++                      sendmsg(bch, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
++                      ih->state = STFAX_NULL;
++                      ih->newcmd = 0;
++                      ih->newmod = 0;
++                      test_and_set_bit(BC_FLG_FTI_RUN, &bch->Flag);
++                      break;
++      }
++      udelay(1000);
++      sendmsg(bch, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
++      udelay(1000);
++}
++
++static void
++setup_sart(bchannel_t *bch) {
++      isar_hw_t       *ih = bch->hw;
++      u_char dps = SET_DPS(ih->dpath);
++      u_char ctrl, param[2];
++      
++      switch (bch->protocol) {
++              case ISDN_PID_NONE:
++                      sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0,
++                              NULL);
++                      break;
++              case ISDN_PID_L1_B_64TRANS:
++              case ISDN_PID_L2_B_TRANSDTMF:
++                      sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2,
++                              "\0\0");
++                      break;
++              case ISDN_PID_L1_B_64HDLC:
++              case ISDN_PID_L1_B_T30FAX:
++                      param[0] = 0;
++                      sendmsg(bch, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1,
++                              param);
++                      break;
++              case ISDN_PID_L1_B_MODEM_ASYNC:
++                      ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
++                      param[0] = S_P1_CHS_8;
++                      param[1] = S_P2_BFT_DEF;
++                      sendmsg(bch, dps | ISAR_HIS_SARTCFG, ctrl, 2,
++                              param);
++                      break;
++      }
++      udelay(1000);
++      sendmsg(bch, dps | ISAR_HIS_BSTREQ, 0, 0, NULL);
++      udelay(1000);
++}
++
++static void
++setup_iom2(bchannel_t *bch) {
++      isar_hw_t       *ih = bch->hw;
++      u_char dps = SET_DPS(ih->dpath);
++      u_char cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD,0,0,0,0};
++      
++      if (bch->channel)
++              msg[1] = msg[3] = 1;
++      switch (bch->protocol) {
++              case ISDN_PID_NONE:
++                      cmsb = 0;
++                      /* dummy slot */
++                      msg[1] = msg[3] = ih->dpath + 2;
++                      break;
++              case ISDN_PID_L1_B_64TRANS:
++              case ISDN_PID_L1_B_64HDLC:
++                      break;
++              case ISDN_PID_L1_B_MODEM_ASYNC:
++              case ISDN_PID_L1_B_T30FAX:
++                      cmsb |= IOM_CTRL_RCV;
++              case ISDN_PID_L2_B_TRANSDTMF:
++                      if (test_bit(BC_FLG_DTMFSEND, &bch->Flag))
++                              cmsb |= IOM_CTRL_RCV;
++                      cmsb |= IOM_CTRL_ALAW;
++                      break;
++      }
++      sendmsg(bch, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg);
++      udelay(1000);
++      sendmsg(bch, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL);
++      udelay(1000);
++}
++
++static int
++modeisar(bchannel_t *bch, int channel, u_int bprotocol, u_char *param)
++{
++      isar_hw_t       *ih = bch->hw;
++
++      /* Here we are selecting the best datapath for requested protocol */
++      if(bch->protocol == ISDN_PID_NONE) { /* New Setup */
++              bch->channel = channel;
++              switch (bprotocol) {
++                      case ISDN_PID_NONE: /* init */
++                              if (!ih->dpath)
++                                      /* no init for dpath 0 */
++                                      return(0);
++                              break;
++                      case ISDN_PID_L1_B_64TRANS:
++                      case ISDN_PID_L1_B_64HDLC:
++                              /* best is datapath 2 */
++                              if (!test_and_set_bit(ISAR_DP2_USE, &ih->reg->Flags))
++                                      ih->dpath = 2;
++                              else if (!test_and_set_bit(ISAR_DP1_USE,
++                                      &ih->reg->Flags))
++                                      ih->dpath = 1;
++                              else {
++                                      printk(KERN_WARNING"isar modeisar both pathes in use\n");
++                                      return(-EINVAL);
++                              }
++                              break;
++                      case ISDN_PID_L1_B_MODEM_ASYNC:
++                      case ISDN_PID_L1_B_T30FAX:
++                      case ISDN_PID_L2_B_TRANSDTMF:
++                              /* only datapath 1 */
++                              if (!test_and_set_bit(ISAR_DP1_USE,
++                                      &ih->reg->Flags))
++                                      ih->dpath = 1;
++                              else {
++                                      printk(KERN_WARNING"isar modeisar analog funktions only with DP1\n");
++                                      mISDN_debugprint(&bch->inst, "isar modeisar analog funktions only with DP1");
++                                      return(-EBUSY);
++                              }
++                              break;
++              }
++      }
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "isar dp%d protocol %x->%x ichan %d",
++                      ih->dpath, bch->protocol, bprotocol, channel);
++      bch->protocol = bprotocol;
++      setup_pump(bch);
++      setup_iom2(bch);
++      setup_sart(bch);
++      if (bch->protocol == ISDN_PID_NONE) {
++              /* Clear resources */
++              if (ih->dpath == 1)
++                      test_and_clear_bit(ISAR_DP1_USE, &ih->reg->Flags);
++              else if (ih->dpath == 2)
++                      test_and_clear_bit(ISAR_DP2_USE, &ih->reg->Flags);
++              ih->dpath = 0;
++      }
++      return(0);
++}
++
++static void
++isar_pump_cmd(bchannel_t *bch, int cmd, u_char para)
++{
++      isar_hw_t       *ih = bch->hw;
++      u_char          dps = SET_DPS(ih->dpath);
++      u_char          ctrl = 0, nom = 0, p1 = 0;
++
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "isar_pump_cmd %x/%x state(%x)",
++                                      cmd, para, ih->state);
++      switch(cmd) {
++              case HW_MOD_FTM:
++                      if (ih->state == STFAX_READY) {
++                              p1 = para;
++                              ctrl = PCTRL_CMD_FTM;
++                              nom = 1;
++                              ih->state = STFAX_LINE;
++                              ih->cmd = ctrl;
++                              ih->mod = para;
++                              ih->newmod = 0;
++                              ih->newcmd = 0;
++                              ih->try_mod = 3;
++                      } else if ((ih->state == STFAX_ACTIV) &&
++                              (ih->cmd == PCTRL_CMD_FTM) &&
++                              (ih->mod == para)) {
++                              bch_sched_event(bch, B_LL_CONNECT);
++                      } else {
++                              ih->newmod = para;
++                              ih->newcmd = PCTRL_CMD_FTM;
++                              nom = 0;
++                              ctrl = PCTRL_CMD_ESC;
++                              ih->state = STFAX_ESCAPE;
++                      }
++                      break;
++              case HW_MOD_FTH:
++                      if (ih->state == STFAX_READY) {
++                              p1 = para;
++                              ctrl = PCTRL_CMD_FTH;
++                              nom = 1;
++                              ih->state = STFAX_LINE;
++                              ih->cmd = ctrl;
++                              ih->mod = para;
++                              ih->newmod = 0;
++                              ih->newcmd = 0;
++                              ih->try_mod = 3;
++                      } else if ((ih->state == STFAX_ACTIV) &&
++                              (ih->cmd == PCTRL_CMD_FTH) &&
++                              (ih->mod == para)) {
++                              bch_sched_event(bch, B_LL_CONNECT);
++                      } else {
++                              ih->newmod = para;
++                              ih->newcmd = PCTRL_CMD_FTH;
++                              nom = 0;
++                              ctrl = PCTRL_CMD_ESC;
++                              ih->state = STFAX_ESCAPE;
++                      }
++                      break;
++              case HW_MOD_FRM:
++                      if (ih->state == STFAX_READY) {
++                              p1 = para;
++                              ctrl = PCTRL_CMD_FRM;
++                              nom = 1;
++                              ih->state = STFAX_LINE;
++                              ih->cmd = ctrl;
++                              ih->mod = para;
++                              ih->newmod = 0;
++                              ih->newcmd = 0;
++                              ih->try_mod = 3;
++                      } else if ((ih->state == STFAX_ACTIV) &&
++                              (ih->cmd == PCTRL_CMD_FRM) &&
++                              (ih->mod == para)) {
++                              bch_sched_event(bch, B_LL_CONNECT);
++                      } else {
++                              ih->newmod = para;
++                              ih->newcmd = PCTRL_CMD_FRM;
++                              nom = 0;
++                              ctrl = PCTRL_CMD_ESC;
++                              ih->state = STFAX_ESCAPE;
++                      }
++                      break;
++              case HW_MOD_FRH:
++                      if (ih->state == STFAX_READY) {
++                              p1 = para;
++                              ctrl = PCTRL_CMD_FRH;
++                              nom = 1;
++                              ih->state = STFAX_LINE;
++                              ih->cmd = ctrl;
++                              ih->mod = para;
++                              ih->newmod = 0;
++                              ih->newcmd = 0;
++                              ih->try_mod = 3;
++                      } else if ((ih->state == STFAX_ACTIV) &&
++                              (ih->cmd == PCTRL_CMD_FRH) &&
++                              (ih->mod == para)) {
++                              bch_sched_event(bch, B_LL_CONNECT);
++                      } else {
++                              ih->newmod = para;
++                              ih->newcmd = PCTRL_CMD_FRH;
++                              nom = 0;
++                              ctrl = PCTRL_CMD_ESC;
++                              ih->state = STFAX_ESCAPE;
++                      }
++                      break;
++              case PCTRL_CMD_TDTMF:
++                      p1 = para;
++                      nom = 1;
++                      ctrl = PCTRL_CMD_TDTMF;
++                      break;
++      }
++      if (ctrl)
++              sendmsg(bch, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
++}
++
++void
++isar_setup(bchannel_t *bch)
++{
++      u_char msg;
++      int i;
++      
++      /* Dpath 1, 2 */
++      msg = 61;
++      for (i=0; i<2; i++) {
++              isar_hw_t       *ih = bch[i].hw;
++              /* Buffer Config */
++              sendmsg(bch, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
++                      ISAR_HIS_P12CFG, 4, 1, &msg);
++              ih->mml = msg;
++              bch[i].protocol = 0;
++              ih->dpath = i + 1;
++              modeisar(&bch[i], i, 0, NULL);
++      }
++}
++
++int
++isar_down(mISDNif_t *hif, struct sk_buff *skb)
++{
++      bchannel_t      *bch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      bch = hif->fdata;
++      ret = 0;
++      if ((hh->prim == PH_DATA_REQ) ||
++              (hh->prim == (DL_DATA | REQUEST))) {
++              if (bch->next_skb) {
++                      mISDN_debugprint(&bch->inst, " l2l1 next_skb exist this shouldn't happen");
++                      return(-EBUSY);
++              }
++              bch->inst.lock(bch->inst.data, 0);
++              if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) {
++                      test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag);
++                      bch->next_skb = skb;
++                      bch->inst.unlock(bch->inst.data);
++                      return(0);
++              } else {
++                      bch->tx_len = skb->len;
++                      memcpy(bch->tx_buf, skb->data, bch->tx_len);
++                      bch->tx_idx = 0;
++                      isar_fill_fifo(bch);
++                      bch->inst.unlock(bch->inst.data);
++                      skb_trim(skb, 0);
++                      return(if_newhead(&bch->inst.up, hh->prim | CONFIRM,
++                              hh->dinfo, skb));
++              }
++      } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_ESTABLISH  | REQUEST))) {
++              if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
++                      ret = 0;
++              else {
++                      u_int   bp = bch->inst.pid.protocol[1];
++
++                      if (bch->inst.pid.global == 1)
++                              test_and_set_bit(BC_FLG_ORIG, &bch->Flag);
++                      if ((bp == ISDN_PID_L1_B_64TRANS) &&
++                              (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANSDTMF))
++                              bp = ISDN_PID_L2_B_TRANSDTMF;
++                      bch->inst.lock(bch->inst.data, 0);
++                      ret = modeisar(bch, bch->channel, bp, NULL);
++                      bch->inst.unlock(bch->inst.data);
++              }
++              skb_trim(skb, 0);
++              return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
++      } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_RELEASE | REQUEST)) ||
++              (hh->prim == (MGR_DISCONNECT | REQUEST))) {
++              bch->inst.lock(bch->inst.data, 0);
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      dev_kfree_skb(bch->next_skb);
++                      bch->next_skb = NULL;
++              }
++              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              modeisar(bch, bch->channel, 0, NULL);
++              test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
++              bch->inst.unlock(bch->inst.data);
++              skb_trim(skb, 0);
++              if (hh->prim != (MGR_DISCONNECT | REQUEST))
++                      if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
++                              return(0);
++      } else if (hh->prim == (PH_CONTROL | REQUEST)) {
++              int  *val;
++              int  len;
++
++              val = (int *)skb->data;
++              if (bch->debug & L1_DEB_HSCX)
++                      mISDN_debugprint(&bch->inst, "PH_CONTROL | REQUEST %x/%x",
++                                      hh->dinfo, *val);
++              if ((hh->dinfo == 0) && ((*val & ~DTMF_TONE_MASK) == DTMF_TONE_VAL)) {
++                      if (bch->protocol == ISDN_PID_L2_B_TRANSDTMF) {
++                              char tt = *val & DTMF_TONE_MASK;
++                              
++                              if (tt == '*')
++                                      tt = 0x1e;
++                              else if (tt == '#')
++                                      tt = 0x1f;
++                              else if (tt > '9')
++                                      tt -= 7;
++                              tt &= 0x1f;
++                              bch->inst.lock(bch->inst.data, 0);
++                              isar_pump_cmd(bch, PCTRL_CMD_TDTMF, tt);
++                              bch->inst.unlock(bch->inst.data);
++                              skb_trim(skb, 0);
++                              if (!if_newhead(&bch->inst.up, PH_CONTROL |
++                                      CONFIRM, 0, skb))
++                                      return(0);
++                      } else {
++                              printk(KERN_WARNING "isar_down TOUCH_TONE_SEND wrong protocol %x\n",
++                                      bch->protocol);
++                              return(-EINVAL);
++                      }
++              } else if ((hh->dinfo == HW_MOD_FRM) || (hh->dinfo == HW_MOD_FRH) ||
++                      (hh->dinfo == HW_MOD_FTM) || (hh->dinfo == HW_MOD_FTH)) {
++                      u_int i;
++
++                      for (i=0; i<FAXMODCNT; i++)
++                              if (faxmodulation[i] == *val)
++                                      break;
++                      if ((FAXMODCNT > i) && test_bit(BC_FLG_INIT, &bch->Flag)) {
++                              printk(KERN_WARNING "isar: new mod\n");
++                              isar_pump_cmd(bch, hh->dinfo, *val);
++                              ret = 0;
++                      } else {
++                              int_errtxt("wrong modulation");
++                              /* wrong modulation or not activ */
++                              // TODO
++                      }
++              } else if (hh->dinfo == HW_MOD_LASTDATA) {
++                      test_and_set_bit(BC_FLG_DLEETX, &bch->Flag);
++              } else if (hh->dinfo == HW_FIRM_START) {
++                      firmwaresize = *val;
++                      if (!(firmware = vmalloc(firmwaresize))) {
++                              firmwaresize = 0;
++                              return(-ENOMEM);
++                      }
++                      fw_p = firmware;
++                      skb_trim(skb, 0);
++                      if(!if_newhead(&bch->inst.up, PH_CONTROL | CONFIRM,
++                              0, skb))
++                              return(0);
++              } else if (hh->dinfo == HW_FIRM_DATA) {
++                      len = *val++;
++                      memcpy(fw_p, val, len);
++                      fw_p += len;
++                      skb_trim(skb, 0);
++                      if(!if_newhead(&bch->inst.up, PH_CONTROL | CONFIRM,
++                              0, skb))
++                              return(0);
++              } else if (hh->dinfo == HW_FIRM_END) {
++                      if ((fw_p - firmware) == firmwaresize)
++                              ret = isar_load_firmware(bch, firmware, firmwaresize);
++                      else {
++                              printk(KERN_WARNING "wrong firmware size %d/%d\n",
++                                      fw_p - firmware, firmwaresize);
++                              ret = -EINVAL;
++                      }
++                      vfree(firmware);
++                      fw_p = firmware = NULL;
++                      firmwaresize = 0;
++                      skb_trim(skb, 0);
++                      if(!if_newhead(&bch->inst.up, PH_CONTROL | CONFIRM,
++                              0, skb))
++                              return(0);
++              }
++      } else {
++              printk(KERN_WARNING "isar_down unknown prim(%x)\n", hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++void
++free_isar(bchannel_t *bch)
++{
++      isar_hw_t *ih = bch->hw;
++
++      modeisar(bch, bch->channel, 0, NULL);
++      del_timer(&ih->ftimer);
++      test_and_clear_bit(BC_FLG_INIT, &bch->Flag);
++}
++
++
++int init_isar(bchannel_t *bch)
++{
++      isar_hw_t *ih = bch->hw;
++
++      printk(KERN_INFO "mISDN: ISAR driver Rev. %s\n", mISDN_getrev(ISAR_revision));
++      bch->hw_bh = isar_bh;
++      ih->ftimer.function = (void *) ftimer_handler;
++      ih->ftimer.data = (long) bch;
++      init_timer(&ih->ftimer);
++      test_and_set_bit(BC_FLG_INIT, &bch->Flag);
++      return (0);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/isar.h       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/isar.h   2004-11-22 09:33:38.240730440 +0000
+@@ -0,0 +1,238 @@
++/* $Id$
++ *
++ * isar.h   ISAR (Siemens PSB 7110) specific defines
++ *
++ * Author Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++ 
++typedef struct _isar_reg {
++      unsigned long   Flags;
++      volatile u_char bstat;
++      volatile u_char iis;
++      volatile u_char cmsb;
++      volatile u_char clsb;
++      volatile u_char par[8];
++} isar_reg_t;
++
++typedef struct _isar_hw {
++      int                     dpath;
++      int                     mml;
++      u_char                  state;
++      u_char                  cmd;
++      u_char                  mod;
++      u_char                  newcmd;
++      u_char                  newmod;
++      char                    try_mod;
++      struct timer_list       ftimer;
++      u_char                  conmsg[16];
++      isar_reg_t              *reg;
++} isar_hw_t;
++
++#define ISAR_IRQMSK   0x04
++#define ISAR_IRQSTA   0x04
++#define ISAR_IRQBIT   0x75
++#define ISAR_CTRL_H   0x61
++#define ISAR_CTRL_L   0x60
++#define ISAR_IIS      0x58
++#define ISAR_IIA      0x58
++#define ISAR_HIS      0x50
++#define ISAR_HIA      0x50
++#define ISAR_MBOX     0x4c
++#define ISAR_WADR     0x4a
++#define ISAR_RADR     0x48 
++
++#define ISAR_HIS_VNR          0x14
++#define ISAR_HIS_DKEY         0x02
++#define ISAR_HIS_FIRM         0x1e
++#define ISAR_HIS_STDSP                0x08
++#define ISAR_HIS_DIAG         0x05
++#define ISAR_HIS_P0CFG                0x3c
++#define ISAR_HIS_P12CFG               0x24
++#define ISAR_HIS_SARTCFG      0x25    
++#define ISAR_HIS_PUMPCFG      0x26    
++#define ISAR_HIS_PUMPCTRL     0x2a    
++#define ISAR_HIS_IOM2CFG      0x27
++#define ISAR_HIS_IOM2REQ      0x07
++#define ISAR_HIS_IOM2CTRL     0x2b
++#define ISAR_HIS_BSTREQ               0x0c
++#define ISAR_HIS_PSTREQ               0x0e
++#define ISAR_HIS_SDATA                0x20
++#define ISAR_HIS_DPS1         0x40
++#define ISAR_HIS_DPS2         0x80
++#define SET_DPS(x)            ((x<<6) & 0xc0)
++
++#define ISAR_IIS_MSCMSD               0x3f
++#define ISAR_IIS_VNR          0x15
++#define ISAR_IIS_DKEY         0x03
++#define ISAR_IIS_FIRM         0x1f
++#define ISAR_IIS_STDSP                0x09
++#define ISAR_IIS_DIAG         0x25
++#define ISAR_IIS_GSTEV                0x00
++#define ISAR_IIS_BSTEV                0x28
++#define ISAR_IIS_BSTRSP               0x2c
++#define ISAR_IIS_PSTRSP               0x2e
++#define ISAR_IIS_PSTEV                0x2a
++#define ISAR_IIS_IOM2RSP      0x27
++#define ISAR_IIS_RDATA                0x20
++#define ISAR_IIS_INVMSG               0x3f
++
++#define ISAR_CTRL_SWVER       0x10
++#define ISAR_CTRL_STST        0x40
++
++#define ISAR_MSG_HWVER        {0x20, 0, 1}
++
++#define ISAR_DP1_USE  1
++#define ISAR_DP2_USE  2
++#define ISAR_RATE_REQ 3
++
++#define PMOD_DISABLE  0
++#define PMOD_FAX      1
++#define PMOD_DATAMODEM        2
++#define PMOD_HALFDUPLEX       3
++#define PMOD_V110     4
++#define PMOD_DTMF     5
++#define PMOD_DTMF_TRANS       6
++#define PMOD_BYPASS   7
++
++#define PCTRL_ORIG    0x80
++#define PV32P2_V23R   0x40
++#define PV32P2_V22A   0x20
++#define PV32P2_V22B   0x10
++#define PV32P2_V22C   0x08
++#define PV32P2_V21    0x02
++#define PV32P2_BEL    0x01
++
++// LSB MSB in ISAR doc wrong !!! Arghhh
++#define PV32P3_AMOD   0x80
++#define PV32P3_V32B   0x02
++#define PV32P3_V23B   0x01
++#define PV32P4_48     0x11
++#define PV32P5_48     0x05
++#define PV32P4_UT48   0x11
++#define PV32P5_UT48   0x0d
++#define PV32P4_96     0x11
++#define PV32P5_96     0x03
++#define PV32P4_UT96   0x11
++#define PV32P5_UT96   0x0f
++#define PV32P4_B96    0x91
++#define PV32P5_B96    0x0b
++#define PV32P4_UTB96  0xd1
++#define PV32P5_UTB96  0x0f
++#define PV32P4_120    0xb1
++#define PV32P5_120    0x09
++#define PV32P4_UT120  0xf1
++#define PV32P5_UT120  0x0f
++#define PV32P4_144    0x99
++#define PV32P5_144    0x09
++#define PV32P4_UT144  0xf9
++#define PV32P5_UT144  0x0f
++#define PV32P6_CTN    0x01
++#define PV32P6_ATN    0x02
++
++#define PFAXP2_CTN    0x01
++#define PFAXP2_ATN    0x04
++
++#define PSEV_10MS_TIMER       0x02
++#define PSEV_CON_ON   0x18
++#define PSEV_CON_OFF  0x19
++#define PSEV_V24_OFF  0x20
++#define PSEV_CTS_ON   0x21
++#define PSEV_CTS_OFF  0x22
++#define PSEV_DCD_ON   0x23
++#define PSEV_DCD_OFF  0x24
++#define PSEV_DSR_ON   0x25
++#define PSEV_DSR_OFF  0x26
++#define PSEV_REM_RET  0xcc
++#define PSEV_REM_REN  0xcd
++#define PSEV_GSTN_CLR 0xd4
++
++#define PSEV_RSP_READY        0xbc
++#define PSEV_LINE_TX_H        0xb3
++#define PSEV_LINE_TX_B        0xb2
++#define PSEV_LINE_RX_H        0xb1
++#define PSEV_LINE_RX_B        0xb0
++#define PSEV_RSP_CONN 0xb5
++#define PSEV_RSP_DISC 0xb7
++#define PSEV_RSP_FCERR        0xb9
++#define PSEV_RSP_SILDET       0xbe
++#define PSEV_RSP_SILOFF       0xab
++#define PSEV_FLAGS_DET        0xba
++
++#define PCTRL_CMD_TDTMF       0x5a
++
++#define PCTRL_CMD_FTH 0xa7
++#define PCTRL_CMD_FRH 0xa5
++#define PCTRL_CMD_FTM 0xa8
++#define PCTRL_CMD_FRM 0xa6
++#define PCTRL_CMD_SILON       0xac
++#define PCTRL_CMD_CONT        0xa2
++#define PCTRL_CMD_ESC 0xa4
++#define PCTRL_CMD_SILOFF 0xab
++#define PCTRL_CMD_HALT        0xa9
++
++#define PCTRL_LOC_RET 0xcf
++#define PCTRL_LOC_REN 0xce
++
++#define SMODE_DISABLE 0
++#define SMODE_V14     2
++#define SMODE_HDLC    3
++#define SMODE_BINARY  4
++#define SMODE_FSK_V14 5
++
++#define SCTRL_HDMC_BOTH       0x00
++#define SCTRL_HDMC_DTX        0x80
++#define SCTRL_HDMC_DRX        0x40
++#define S_P1_OVSP     0x40
++#define S_P1_SNP      0x20
++#define S_P1_EOP      0x10
++#define S_P1_EDP      0x08
++#define S_P1_NSB      0x04
++#define S_P1_CHS_8    0x03
++#define S_P1_CHS_7    0x02
++#define S_P1_CHS_6    0x01
++#define S_P1_CHS_5    0x00
++
++#define S_P2_BFT_DEF  0x10
++
++#define IOM_CTRL_ENA  0x80
++#define IOM_CTRL_NOPCM        0x00
++#define IOM_CTRL_ALAW 0x02
++#define IOM_CTRL_ULAW 0x04
++#define IOM_CTRL_RCV  0x01
++
++#define IOM_P1_TXD    0x10
++
++#define HDLC_FED      0x40
++#define HDLC_FSD      0x20
++#define HDLC_FST      0x20
++#define HDLC_ERROR    0x1c
++#define HDLC_ERR_FAD  0x10
++#define HDLC_ERR_RER  0x08
++#define HDLC_ERR_CER  0x04
++#define SART_NMD      0x01
++
++#define BSTAT_RDM0    0x1
++#define BSTAT_RDM1    0x2
++#define BSTAT_RDM2    0x4
++#define BSTAT_RDM3    0x8
++#define BSTEV_TBO     0x1f
++#define BSTEV_RBO     0x2f
++
++/* FAX State Machine */
++#define STFAX_NULL    0
++#define STFAX_READY   1
++#define STFAX_LINE    2
++#define STFAX_CONT    3
++#define STFAX_ACTIV   4
++#define STFAX_ESCAPE  5
++#define STFAX_SILDET  6
++
++extern int ISARVersion(bchannel_t *bch, char *s);
++extern void isar_int_main(bchannel_t *bch);
++extern int init_isar(bchannel_t *bch);
++extern void free_isar(bchannel_t *bch);
++extern int isar_down(mISDNif_t *, struct sk_buff *);
++extern int isar_load_firmware(bchannel_t *bch, u_char *buf, int size);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3_udss1.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3_udss1.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3_udss1.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3_udss1.c       2004-11-22 09:33:38.250728920 +0000
+@@ -0,0 +1,2423 @@
++/* $Id$
++ *
++ * EURO/DSS1 D-channel protocol
++ *
++ * Author       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    Jan den Ouden
++ *              Fritz Elfert
++ *
++ */
++
++#include <linux/module.h>
++
++#include "layer3.h"
++#include "helper.h"
++#include "debug.h"
++#include "dss1.h"
++
++static int debug = 0;
++static mISDNobject_t u_dss1;
++
++
++const char *dss1_revision = "$Revision$";
++
++static int dss1man(l3_process_t *, u_int, void *);
++
++static int
++parseQ931(struct sk_buff *skb) {
++      Q931_info_t     *qi;
++      int             l, codeset, maincodeset;
++      int             len, iep, pos = 0, cnt = 0;
++      u16             *ie, cr;
++      u_char          t, *p = skb->data;
++
++      if (skb->len < 3)
++              return(-1);
++      p++;
++      l = (*p++) & 0xf;
++      if (l>2)
++              return(-2);
++      if (l)
++              cr = *p++;
++      else
++              cr = 0;
++      if (l == 2) {
++              cr <<= 8;
++              cr |= *p++;
++      } else if (l == 1)
++              if (cr & 0x80) {
++                      cr |= 0x8000;
++                      cr &= 0xFF7F;
++              }
++      t = *p;
++      if ((u_long)p & 1)
++              pos = 1;
++      else
++              pos = 0;
++      skb_pull(skb, (p - skb->data) - pos);
++      len = skb->len;
++      p = skb->data;
++      if (skb_headroom(skb) < (int)L3_EXTRA_SIZE) {
++              int_error();
++              return(-3);
++      }
++      qi = (Q931_info_t *)skb_push(skb, L3_EXTRA_SIZE);
++      mISDN_initQ931_info(qi);
++      qi->type = t;
++      qi->crlen = l;
++      qi->cr = cr;
++      pos++;
++      codeset = maincodeset = 0;
++      ie = &qi->bearer_capability;
++      while (pos < len) {
++              if ((p[pos] & 0xf0) == 0x90) {
++                      codeset = p[pos] & 0x07;
++                      if (!(p[pos] & 0x08))
++                              maincodeset = codeset;
++                      pos++;
++                      continue;
++              }
++              if (codeset == 0) {
++                      if (p[pos] & 0x80) { /* single octett IE */
++                              if (p[pos] == IE_MORE_DATA)
++                                      qi->more_data = pos;
++                              else if (p[pos] == IE_COMPLETE)
++                                      qi->sending_complete = pos;
++                              else if ((p[pos] & 0xf0) == IE_CONGESTION)
++                                      qi->congestion_level = pos;
++                              cnt++;
++                              pos++;
++                      } else {
++                              iep = mISDN_l3_ie2pos(p[pos]);
++                              if ((pos+1) >= len)
++                                      return(-4);
++                              l = p[pos+1];
++                              if ((pos+l+1) >= len)
++                                      return(-5);
++                              if (iep>=0) {
++                                      if (!ie[iep])
++                                              ie[iep] = pos;
++                              }
++                              pos += l + 2;
++                              cnt++;
++                      }
++              }
++              codeset = maincodeset;
++      }
++      return(cnt);
++}
++
++static int
++calc_msg_len(Q931_info_t *qi)
++{
++      int     i, cnt = 0;
++      u_char  *buf = (u_char *)qi;
++      u16     *v_ie;
++
++      buf += L3_EXTRA_SIZE;
++      if (qi->more_data)
++              cnt++;
++      if (qi->sending_complete)
++              cnt++;
++      if (qi->congestion_level)
++              cnt++;
++      v_ie = &qi->bearer_capability;
++      for (i=0; i<32; i++) {
++              if (v_ie[i])
++                      cnt += buf[v_ie[i] + 1] + 2;
++      }
++      return(cnt);
++}
++
++static int
++compose_msg(struct sk_buff *skb, Q931_info_t *qi)
++{
++      int     i, l;
++      u_char  *p, *buf = (u_char *)qi;
++      u16     *v_ie;
++
++      buf += L3_EXTRA_SIZE;
++      
++      if (qi->more_data) {
++              p = skb_put(skb, 1);
++              *p = buf[qi->more_data];
++      }
++      if (qi->sending_complete) {
++              p = skb_put(skb, 1);
++              *p = buf[qi->sending_complete];
++      }
++      if (qi->congestion_level) {
++              p = skb_put(skb, 1);
++              *p = buf[qi->congestion_level];
++      }
++      v_ie = &qi->bearer_capability;
++      for (i=0; i<32; i++) {
++              if (v_ie[i]) {
++                      l = buf[v_ie[i] + 1] +1;
++                      p = skb_put(skb, l + 1);
++                      *p++ = mISDN_l3_pos2ie(i);
++                      memcpy(p, &buf[v_ie[i] + 1], l);
++              }
++      }
++      return(0);
++}
++
++static struct sk_buff
++*MsgStart(l3_process_t *pc, u_char mt, int len) {
++      struct sk_buff  *skb;
++      int             lx = 4;
++      u_char          *p;
++
++      if (test_bit(FLG_CRLEN2, &pc->l3->Flag))
++              lx++;
++      if (pc->callref == -1) /* dummy cr */
++              lx = 3;
++      if (!(skb = alloc_stack_skb(len + lx, pc->l3->down_headerlen)))
++              return(NULL);
++      p = skb_put(skb, lx);
++      *p++ = 8;
++      if (lx == 3)
++              *p++ = 0;
++      else if (lx == 5) {
++              *p++ = 2;
++              *p++ = (pc->callref >> 8)  ^ 0x80;
++              *p++ = pc->callref & 0xff;
++      } else {
++              *p++ = 1;
++              *p = pc->callref & 0xff;
++              if (!(pc->callref & 0x8000))
++                      *p |= 0x80;
++              p++;
++      }
++      *p = mt;
++      return(skb);
++}
++
++static int SendMsg(l3_process_t *pc, struct sk_buff *skb, int state) {
++      int             l;
++      int             ret;
++      struct sk_buff  *nskb;
++      Q931_info_t     *qi;
++
++      if (!skb)
++              return(-EINVAL);
++      qi = (Q931_info_t *)skb->data;
++      l = calc_msg_len(qi);
++      if (!(nskb = MsgStart(pc, qi->type, l))) {
++              kfree_skb(skb);
++              return(-ENOMEM);
++      }
++      if (l)
++              compose_msg(nskb, qi);
++      kfree_skb(skb);
++      if (state != -1)
++              newl3state(pc, state);
++      if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, nskb)))
++              kfree_skb(nskb);
++      return(ret);
++}
++
++static int
++l3dss1_message(l3_process_t *pc, u_char mt)
++{
++      struct sk_buff  *skb;
++      int             ret;
++
++      if (!(skb = MsgStart(pc, mt, 0)))
++              return(-ENOMEM);
++      if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb)))
++              kfree_skb(skb);
++      return(ret);
++}
++
++static void
++l3dss1_message_cause(l3_process_t *pc, u_char mt, u_char cause)
++{
++      struct sk_buff  *skb;
++      u_char          *p;
++      int             ret;
++
++      if (!(skb = MsgStart(pc, mt, 4)))
++              return;
++      p = skb_put(skb, 4);
++      *p++ = IE_CAUSE;
++      *p++ = 0x2;
++      *p++ = 0x80 | CAUSE_LOC_USER;
++      *p++ = 0x80 | cause;
++      if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb)))
++              kfree_skb(skb);
++}
++
++static void
++l3dss1_status_send(l3_process_t *pc, u_char cause)
++{
++      struct sk_buff  *skb;
++      u_char          *p;
++      int             ret;
++
++      if (!(skb = MsgStart(pc, MT_STATUS, 7)))
++              return;
++      p = skb_put(skb, 7);
++      *p++ = IE_CAUSE;
++      *p++ = 2;
++      *p++ = 0x80 | CAUSE_LOC_USER;
++      *p++ = 0x80 | cause;
++
++      *p++ = IE_CALL_STATE;
++      *p++ = 1;
++      *p++ = pc->state & 0x3f;
++      if ((ret=l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb)))
++              kfree_skb(skb);
++}
++
++static void
++l3dss1_msg_without_setup(l3_process_t *pc, u_char cause)
++{
++      /* This routine is called if here was no SETUP made (checks in dss1up and in
++       * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
++       * MT_STATUS_ENQUIRE in the NULL state is handled too
++       */
++      switch (cause) {
++              case 81:        /* invalid callreference */
++              case 88:        /* incomp destination */
++              case 96:        /* mandory IE missing */
++              case 100:       /* invalid IE contents */
++              case 101:       /* incompatible Callstate */
++                      l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
++                      break;
++              default:
++                      printk(KERN_ERR "mISDN l3dss1_msg_without_setup wrong cause %d\n",
++                              cause);
++      }
++      release_l3_process(pc);
++}
++
++static int ie_ALERTING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
++              IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_HLC,
++              IE_USER_USER, -1};
++static int ie_CALL_PROCEEDING[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1,
++              IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_HLC, -1};
++static int ie_CONNECT[] = {IE_BEARER, IE_CHANNEL_ID | IE_MANDATORY_1, 
++              IE_FACILITY, IE_PROGRESS, IE_DISPLAY, IE_DATE, IE_SIGNAL,
++              IE_CONNECT_PN, IE_CONNECT_SUB, IE_LLC, IE_HLC, IE_USER_USER, -1};
++static int ie_CONNECT_ACKNOWLEDGE[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_SIGNAL, -1};
++static int ie_DISCONNECT[] = {IE_CAUSE | IE_MANDATORY, IE_FACILITY,
++              IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
++static int ie_INFORMATION[] = {IE_COMPLETE, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL,
++              IE_CALLED_PN, -1};
++static int ie_NOTIFY[] = {IE_BEARER, IE_NOTIFY | IE_MANDATORY, IE_DISPLAY, -1};
++static int ie_PROGRESS[] = {IE_BEARER, IE_CAUSE, IE_FACILITY, IE_PROGRESS |
++              IE_MANDATORY, IE_DISPLAY, IE_HLC, IE_USER_USER, -1};
++static int ie_RELEASE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY, IE_DISPLAY,
++              IE_SIGNAL, IE_USER_USER, -1};
++/* a RELEASE_COMPLETE with errors don't require special actions 
++static int ie_RELEASE_COMPLETE[] = {IE_CAUSE | IE_MANDATORY_1, IE_FACILITY,
++              IE_DISPLAY, IE_SIGNAL, IE_USER_USER, -1};
++*/
++static int ie_RESUME_ACKNOWLEDGE[] = {IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY,
++              IE_DISPLAY, -1};
++static int ie_RESUME_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
++static int ie_SETUP[] = {IE_COMPLETE, IE_BEARER  | IE_MANDATORY,
++              IE_CHANNEL_ID| IE_MANDATORY, IE_FACILITY, IE_PROGRESS,
++              IE_NET_FAC, IE_DISPLAY, IE_KEYPAD, IE_SIGNAL, IE_CALLING_PN,
++              IE_CALLING_SUB, IE_CALLED_PN, IE_CALLED_SUB, IE_REDIR_NR,
++              IE_LLC, IE_HLC, IE_USER_USER, -1};
++static int ie_SETUP_ACKNOWLEDGE[] = {IE_CHANNEL_ID | IE_MANDATORY, IE_FACILITY,
++              IE_PROGRESS, IE_DISPLAY, IE_SIGNAL, -1};
++static int ie_STATUS[] = {IE_CAUSE | IE_MANDATORY, IE_CALL_STATE |
++              IE_MANDATORY, IE_DISPLAY, -1};
++static int ie_STATUS_ENQUIRY[] = {IE_DISPLAY, -1};
++static int ie_SUSPEND_ACKNOWLEDGE[] = {IE_FACILITY, IE_DISPLAY, -1};
++static int ie_SUSPEND_REJECT[] = {IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
++/* not used 
++ * static int ie_CONGESTION_CONTROL[] = {IE_CONGESTION | IE_MANDATORY,
++ *            IE_CAUSE | IE_MANDATORY, IE_DISPLAY, -1};
++ * static int ie_USER_INFORMATION[] = {IE_MORE_DATA, IE_USER_USER | IE_MANDATORY, -1};
++ * static int ie_RESTART[] = {IE_CHANNEL_ID, IE_DISPLAY, IE_RESTART_IND |
++ *            IE_MANDATORY, -1};
++ */
++static int ie_FACILITY[] = {IE_FACILITY | IE_MANDATORY, IE_DISPLAY, -1};
++static int comp_required[] = {1,2,3,5,6,7,9,10,11,14,15,-1};
++static int l3_valid_states[] = {0,1,2,3,4,6,7,8,9,10,11,12,15,17,19,25,-1};
++
++struct ie_len {
++      int ie;
++      int len;
++};
++
++static
++struct ie_len max_ie_len[] = {
++      {IE_SEGMENT, 4},
++      {IE_BEARER, 12},
++      {IE_CAUSE, 32},
++      {IE_CALL_ID, 10},
++      {IE_CALL_STATE, 3},
++      {IE_CHANNEL_ID, 34},
++      {IE_FACILITY, 255},
++      {IE_PROGRESS, 4},
++      {IE_NET_FAC, 255},
++      {IE_NOTIFY, 3},
++      {IE_DISPLAY, 82},
++      {IE_DATE, 8},
++      {IE_KEYPAD, 34},
++      {IE_SIGNAL, 3},
++      {IE_INFORATE, 6},
++      {IE_E2E_TDELAY, 11},
++      {IE_TDELAY_SEL, 5},
++      {IE_PACK_BINPARA, 3},
++      {IE_PACK_WINSIZE, 4},
++      {IE_PACK_SIZE, 4},
++      {IE_CUG, 7},
++      {IE_REV_CHARGE, 3},
++      {IE_CALLING_PN, 24},
++      {IE_CALLING_SUB, 23},
++      {IE_CALLED_PN, 24},
++      {IE_CALLED_SUB, 23},
++      {IE_REDIR_NR, 255},
++      {IE_TRANS_SEL, 255},
++      {IE_RESTART_IND, 3},
++      {IE_LLC, 18},
++      {IE_HLC, 5},
++      {IE_USER_USER, 131},
++      {-1,0},
++};
++
++static int
++getmax_ie_len(u_char ie) {
++      int i = 0;
++      while (max_ie_len[i].ie != -1) {
++              if (max_ie_len[i].ie == ie)
++                      return(max_ie_len[i].len);
++              i++;
++      }
++      return(255);
++}
++
++static int
++ie_in_set(l3_process_t *pc, u_char ie, int *checklist) {
++      int ret = 1;
++
++      while (*checklist != -1) {
++              if ((*checklist & 0xff) == ie) {
++                      if (ie & 0x80)
++                              return(-ret);
++                      else
++                              return(ret);
++              }
++              ret++;
++              checklist++;
++      }
++      return(0);
++}
++
++static int
++check_infoelements(l3_process_t *pc, struct sk_buff *skb, int *checklist)
++{
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      int             *cl = checklist;
++      u_char          *p, ie;
++      u16             *iep;
++      int             i, l, newpos, oldpos;
++      int             err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
++      
++      p = skb->data;
++      p += L3_EXTRA_SIZE;
++      iep = &qi->bearer_capability;
++      oldpos = -1;
++      for (i=0; i<32; i++) {
++              if (iep[i]) {
++                      ie = mISDN_l3_pos2ie(i);
++                      if ((newpos = ie_in_set(pc, ie, cl))) {
++                              if (newpos > 0) {
++                                      if (newpos < oldpos)
++                                              err_seq++;
++                                      else
++                                              oldpos = newpos;
++                              }
++                      } else {
++                              if (ie_in_set(pc, ie, comp_required))
++                                      err_compr++;
++                              else
++                                      err_ureg++;
++                      }
++                      l = p[iep[i] +1];
++                      if (l > getmax_ie_len(ie))
++                              err_len++;
++              }
++      }
++      if (err_compr | err_ureg | err_len | err_seq) {
++              if (pc->l3->debug & L3_DEB_CHECK)
++                      l3_debug(pc->l3, "check IE MT(%x) %d/%d/%d/%d",
++                              qi->type, err_compr, err_ureg, err_len, err_seq);
++              if (err_compr)
++                      return(ERR_IE_COMPREHENSION);
++              if (err_ureg)
++                      return(ERR_IE_UNRECOGNIZED);
++              if (err_len)
++                      return(ERR_IE_LENGTH);
++              if (err_seq)
++                      return(ERR_IE_SEQUENCE);
++      } 
++      return(0);
++}
++
++/* verify if a message type exists and contain no IE error */
++static int
++l3dss1_check_messagetype_validity(l3_process_t *pc, int mt, void *arg)
++{
++      switch (mt) {
++              case MT_ALERTING:
++              case MT_CALL_PROCEEDING:
++              case MT_CONNECT:
++              case MT_CONNECT_ACKNOWLEDGE:
++              case MT_DISCONNECT:
++              case MT_INFORMATION:
++              case MT_FACILITY:
++              case MT_NOTIFY:
++              case MT_PROGRESS:
++              case MT_RELEASE:
++              case MT_RELEASE_COMPLETE:
++              case MT_SETUP:
++              case MT_SETUP_ACKNOWLEDGE:
++              case MT_RESUME_ACKNOWLEDGE:
++              case MT_RESUME_REJECT:
++              case MT_SUSPEND_ACKNOWLEDGE:
++              case MT_SUSPEND_REJECT:
++              case MT_USER_INFORMATION:
++              case MT_RESTART:
++              case MT_RESTART_ACKNOWLEDGE:
++              case MT_CONGESTION_CONTROL:
++              case MT_STATUS:
++              case MT_STATUS_ENQUIRY:
++                      if (pc->l3->debug & L3_DEB_CHECK)
++                              l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) OK", mt);
++                      break;
++              case MT_RESUME: /* RESUME only in user->net */
++              case MT_SUSPEND: /* SUSPEND only in user->net */
++              default:
++                      if (pc->l3->debug & (L3_DEB_CHECK | L3_DEB_WARN))
++                              l3_debug(pc->l3, "l3dss1_check_messagetype_validity mt(%x) fail", mt);
++                      l3dss1_status_send(pc, CAUSE_MT_NOTIMPLEMENTED);
++                      return(1);
++      }
++      return(0);
++}
++
++static void
++l3dss1_std_ie_err(l3_process_t *pc, int ret) {
++
++      if (pc->l3->debug & L3_DEB_CHECK)
++              l3_debug(pc->l3, "check_infoelements ret %d", ret);
++      switch(ret) {
++              case 0: 
++                      break;
++              case ERR_IE_COMPREHENSION:
++                      l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS);
++                      break;
++              case ERR_IE_UNRECOGNIZED:
++                      l3dss1_status_send(pc, CAUSE_IE_NOTIMPLEMENTED);
++                      break;
++              case ERR_IE_LENGTH:
++                      l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS);
++                      break;
++              case ERR_IE_SEQUENCE:
++              default:
++                      break;
++      }
++}
++
++static int
++l3dss1_get_channel_id(l3_process_t *pc, struct sk_buff *skb) {
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      u_char          *p;
++
++      if (qi->channel_id) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->channel_id;
++              p++;
++              if (test_bit(FLG_EXTCID, &pc->l3->Flag)) {
++                      if (*p != 1) {
++                              pc->bc = 1;
++                              return (0);
++                      }
++              }
++              if (*p != 1) { /* len for BRI = 1 */
++                      if (pc->l3->debug & L3_DEB_WARN)
++                              l3_debug(pc->l3, "wrong chid len %d", *p);
++                      return (-2);
++              }
++              p++;
++              if (*p & 0x60) { /* only base rate interface */
++                      if (pc->l3->debug & L3_DEB_WARN)
++                              l3_debug(pc->l3, "wrong chid %x", *p);
++                      return (-3);
++              }
++              pc->bc = *p & 3;
++      } else
++              return(-1);
++      return(0);
++}
++
++static int
++l3dss1_get_cause(l3_process_t *pc, struct sk_buff *skb) {
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      u_char          l;
++      u_char          *p;
++
++      if (qi->cause) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->cause;
++              p++;
++              l = *p++;
++              if (l>30) {
++                      return(-30);
++              }
++              if (l)
++                      l--;
++              else {
++                      return(-2);
++              }
++              if (l && !(*p & 0x80)) {
++                      l--;
++                      p++; /* skip recommendation */
++              }
++              p++;
++              if (l) {
++                      if (!(*p & 0x80)) {
++                              return(-3);
++                      }
++                      pc->err = *p & 0x7F;
++              } else {
++                      return(-4);
++              }
++      } else
++              return(-1);
++      return(0);
++}
++
++static void
++l3dss1_release_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      StopAllL3Timer(pc);
++      if (arg) {
++              SendMsg(pc, arg, 19);
++      } else {
++              newl3state(pc, 19);
++              l3dss1_message(pc, MT_RELEASE);
++      }
++      L3AddTimer(&pc->timer, T308, CC_T308_1);
++}
++
++static void
++l3dss1_setup_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = skb_clone(arg, GFP_ATOMIC);
++
++      if (!SendMsg(pc, arg, 1)) {
++              L3DelTimer(&pc->timer);
++              L3AddTimer(&pc->timer, T303, CC_T303);
++              pc->t303skb = skb;
++      } else
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_disconnect_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi;
++      u_char          *p;
++
++      StopAllL3Timer(pc);
++      if (arg) {
++              qi = (Q931_info_t *)skb->data;
++              if (!qi->cause) {
++                      qi->cause = skb->len - L3_EXTRA_SIZE;
++                      p = skb_put(skb, 4);
++                      *p++ = IE_CAUSE;
++                      *p++ = 2;
++                      *p++ = 0x80 | CAUSE_LOC_USER;
++                      *p++ = 0x80 | CAUSE_NORMALUNSPECIFIED;
++              }
++              SendMsg(pc, arg, 11);
++      } else {
++              newl3state(pc, 11);
++              l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_NORMALUNSPECIFIED);
++      }
++      L3AddTimer(&pc->timer, T305, CC_T305);
++}
++
++static void
++l3dss1_connect_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (!pc->bc) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "D-chan connect for waiting call");
++              l3dss1_disconnect_req(pc, pr, NULL);
++              return;
++      }
++      if (arg) {
++              SendMsg(pc, arg, 8);
++      } else {
++              newl3state(pc, 8);
++              l3dss1_message(pc, MT_CONNECT);
++      }
++      L3DelTimer(&pc->timer);
++      L3AddTimer(&pc->timer, T313, CC_T313);
++}
++
++static void
++l3dss1_release_cmpl_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      StopAllL3Timer(pc);
++      if (arg) {
++              SendMsg(pc, arg, 0);
++      } else {
++              newl3state(pc, 0);
++              l3dss1_message(pc, MT_RELEASE_COMPLETE);
++      }
++      mISDN_l3up(pc, CC_RELEASE_COMPLETE | CONFIRM, NULL);
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_alert_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (arg) {
++              SendMsg(pc, arg, 7);
++      } else {
++              newl3state(pc, 7);
++              l3dss1_message(pc, MT_ALERTING);
++      }
++      L3DelTimer(&pc->timer);
++}
++
++static void
++l3dss1_proceed_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (arg) {
++              SendMsg(pc, arg, 9);
++      } else {
++              newl3state(pc, 9);
++              l3dss1_message(pc, MT_CALL_PROCEEDING);
++      }
++      L3DelTimer(&pc->timer);
++}
++
++static void
++l3dss1_setup_ack_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (arg) {
++              SendMsg(pc, arg, 25);
++      } else {
++              newl3state(pc, 25);
++              l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE);
++      }
++      L3DelTimer(&pc->timer);
++      L3AddTimer(&pc->timer, T302, CC_T302);
++}
++
++static void
++l3dss1_suspend_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (arg) {
++              SendMsg(pc, arg, 15);
++      } else {
++              newl3state(pc, 15);
++              l3dss1_message(pc, MT_SUSPEND);
++      }
++      L3AddTimer(&pc->timer, T319, CC_T319);
++}
++
++static void
++l3dss1_resume_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (arg) {
++              SendMsg(pc, arg, 17);
++      } else {
++              newl3state(pc, 17);
++              l3dss1_message(pc, MT_RESUME);
++      }
++      L3AddTimer(&pc->timer, T318, CC_T318);
++}
++
++static void
++l3dss1_status_enq_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (arg)
++              dev_kfree_skb(arg);
++      l3dss1_message(pc, MT_STATUS_ENQUIRY);
++}
++
++static void
++l3dss1_information_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (pc->state == 2) {
++              L3DelTimer(&pc->timer);
++              L3AddTimer(&pc->timer, T304, CC_T304);
++      }
++
++      if (arg) {
++              SendMsg(pc, arg, 2);
++      }
++}
++
++static void
++l3dss1_progress_req(l3_process_t *pc, u_char pr, void *arg)
++{
++      if (arg) {
++              SendMsg(pc, arg, 10);
++      }
++}
++
++static void
++l3dss1_release_cmpl(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++
++      StopAllL3Timer(pc);
++      newl3state(pc, 0);
++      if (mISDN_l3up(pc, CC_RELEASE_COMPLETE | INDICATION, skb))
++              dev_kfree_skb(skb);
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_alerting(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff *skb = arg;
++      int ret;
++
++      ret = check_infoelements(pc, skb, ie_ALERTING);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      L3DelTimer(&pc->timer); /* T304 */
++      if (pc->t303skb) {
++              dev_kfree_skb(pc->t303skb);
++              pc->t303skb = NULL;
++      }
++      newl3state(pc, 4);
++      if (ret)
++              l3dss1_std_ie_err(pc, ret);
++      if (mISDN_l3up(pc, CC_ALERTING | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_call_proc(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++      u_char          cause;
++
++      if (!(ret = l3dss1_get_channel_id(pc, skb))) {
++              if ((0 == pc->bc) || (3 == pc->bc)) {
++                      if (pc->l3->debug & L3_DEB_WARN)
++                              l3_debug(pc->l3, "setup answer with wrong chid %x", pc->bc);
++                      l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS);
++                      return;
++              }
++      } else if (1 == pc->state) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "setup answer wrong chid (ret %d)", ret);
++              if (ret == -1)
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else
++                      cause = CAUSE_INVALID_CONTENTS;
++              l3dss1_status_send(pc, cause);
++              return;
++      }
++      /* Now we are on none mandatory IEs */
++      ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      L3DelTimer(&pc->timer);
++      if (pc->t303skb) {
++              dev_kfree_skb(pc->t303skb);
++              pc->t303skb = NULL;
++      }
++      newl3state(pc, 3);
++      L3AddTimer(&pc->timer, T310, CC_T310);
++      if (ret) /* STATUS for none mandatory IE errors after actions are taken */
++              l3dss1_std_ie_err(pc, ret);
++      if (mISDN_l3up(pc, CC_PROCEEDING | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_connect(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++
++      ret = check_infoelements(pc, skb, ie_CONNECT);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      L3DelTimer(&pc->timer); /* T310 */
++      if (pc->t303skb) {
++              dev_kfree_skb(pc->t303skb);
++              pc->t303skb = NULL;
++      }
++      l3dss1_message(pc, MT_CONNECT_ACKNOWLEDGE);
++      newl3state(pc, 10);
++      if (ret)
++              l3dss1_std_ie_err(pc, ret);
++      if (mISDN_l3up(pc, CC_CONNECT | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_connect_ack(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++
++      ret = check_infoelements(pc, skb, ie_CONNECT_ACKNOWLEDGE);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      newl3state(pc, 10);
++      L3DelTimer(&pc->timer);
++      if (pc->t303skb) {
++              dev_kfree_skb(pc->t303skb);
++              pc->t303skb = NULL;
++      }
++      if (ret)
++              l3dss1_std_ie_err(pc, ret);
++      if (mISDN_l3up(pc, CC_CONNECT_ACKNOWLEDGE | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_disconnect(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++      u_char          cause = 0;
++
++      StopAllL3Timer(pc);
++      if ((ret = l3dss1_get_cause(pc, skb))) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "DISC get_cause ret(%d)", ret);
++              if (ret == -1)
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else
++                      cause = CAUSE_INVALID_CONTENTS;
++      } 
++      ret = check_infoelements(pc, skb, ie_DISCONNECT);
++      if (ERR_IE_COMPREHENSION == ret)
++              cause = CAUSE_MANDATORY_IE_MISS;
++      else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
++              cause = CAUSE_IE_NOTIMPLEMENTED;
++      ret = pc->state;
++      if (cause)
++              newl3state(pc, 19);
++      else
++              newl3state(pc, 12);
++              if (11 != ret) {
++              if (mISDN_l3up(pc, CC_DISCONNECT | INDICATION, skb))
++                      dev_kfree_skb(skb);
++      } else if (!cause) {
++              l3dss1_release_req(pc, pr, NULL);
++              dev_kfree_skb(skb);
++      } else
++              dev_kfree_skb(skb);
++      if (cause) {
++              l3dss1_message_cause(pc, MT_RELEASE, cause);
++              L3AddTimer(&pc->timer, T308, CC_T308_1);
++      }
++}
++
++static void
++l3dss1_setup_ack(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++      u_char          cause;
++
++      if (!(ret = l3dss1_get_channel_id(pc, skb))) {
++              if ((0 == pc->bc) || (3 == pc->bc)) {
++                      if (pc->l3->debug & L3_DEB_WARN)
++                              l3_debug(pc->l3, "setup answer with wrong chid %x", pc->bc);
++                      l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS);
++                      dev_kfree_skb(skb);
++                      return;
++              }
++      } else {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "setup answer wrong chid (ret %d)", ret);
++              if (ret == -1)
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else
++                      cause = CAUSE_INVALID_CONTENTS;
++              l3dss1_status_send(pc, cause);
++              dev_kfree_skb(skb);
++              return;
++      }
++      /* Now we are on none mandatory IEs */
++      ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      L3DelTimer(&pc->timer);
++      if (pc->t303skb) {
++              dev_kfree_skb(pc->t303skb);
++              pc->t303skb = NULL;
++      }
++      newl3state(pc, 2);
++      L3AddTimer(&pc->timer, T304, CC_T304);
++      if (ret) /* STATUS for none mandatory IE errors after actions are taken */
++              l3dss1_std_ie_err(pc, ret);
++      if (mISDN_l3up(pc, CC_SETUP_ACKNOWLEDGE | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_setup(l3_process_t *pc, u_char pr, void *arg)
++{
++      u_char          *p, cause, bc2 = 0;
++      int             bcfound = 0;
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      int             err = 0;
++
++      /*
++       * Bearer Capabilities
++       */
++      /* only the first occurence 'll be detected ! */
++      p = skb->data;
++      if (qi->bearer_capability) {
++              p += L3_EXTRA_SIZE + qi->bearer_capability;
++              p++;
++              if ((p[0] < 2) || (p[0] > 11))
++                      err = 1;
++              else {
++                      bc2 = p[2] & 0x7f;
++                      switch (p[1] & 0x7f) {
++                              case 0x00: /* Speech */
++                              case 0x10: /* 3.1 Khz audio */
++                              case 0x08: /* Unrestricted digital information */
++                              case 0x09: /* Restricted digital information */
++                              case 0x11:
++                                      /* Unrestr. digital information  with 
++                                       * tones/announcements ( or 7 kHz audio
++                                       */
++                              case 0x18: /* Video */
++                                      break;
++                              default:
++                                      err = 2;
++                                      break;
++                      }
++                      switch (p[2] & 0x7f) {
++                              case 0x40: /* packed mode */
++                              case 0x10: /* 64 kbit */
++                              case 0x11: /* 2*64 kbit */
++                              case 0x13: /* 384 kbit */
++                              case 0x15: /* 1536 kbit */
++                              case 0x17: /* 1920 kbit */
++                                      break;
++                              default:
++                                      err = 3;
++                                      break;
++                      }
++              }
++              if (err) {
++                      if (pc->l3->debug & L3_DEB_WARN)
++                              l3_debug(pc->l3, "setup with wrong bearer(l=%d:%x,%x)",
++                                      p[0], p[1], p[2]);
++                      l3dss1_msg_without_setup(pc, CAUSE_INVALID_CONTENTS);
++                      dev_kfree_skb(skb);
++                      return;
++              } 
++      } else {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "setup without bearer capabilities");
++              /* ETS 300-104 1.3.3 */
++              l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS);
++              dev_kfree_skb(skb);
++              return;
++      }
++      /*
++       * Channel Identification
++       */
++      if (!(err = l3dss1_get_channel_id(pc, skb))) {
++              if (pc->bc) {
++                      if ((3 == pc->bc) && (0x10 == bc2)) {
++                              if (pc->l3->debug & L3_DEB_WARN)
++                                      l3_debug(pc->l3, "setup with wrong chid %x",
++                                              pc->bc);
++                              l3dss1_msg_without_setup(pc,
++                                      CAUSE_INVALID_CONTENTS);
++                              dev_kfree_skb(skb);
++                              return;
++                      }
++                      bcfound++;
++              } else {
++                      if (pc->l3->debug & L3_DEB_WARN)
++                              l3_debug(pc->l3, "setup without bchannel, call waiting");
++                      bcfound++;
++              } 
++      } else {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "setup with wrong chid ret %d", err);
++              if (err == -1)
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else
++                      cause = CAUSE_INVALID_CONTENTS;
++              l3dss1_msg_without_setup(pc, cause);
++              dev_kfree_skb(skb);
++              return;
++      }
++      /* Now we are on none mandatory IEs */
++      err = check_infoelements(pc, skb, ie_SETUP);
++      if (ERR_IE_COMPREHENSION == err) {
++              l3dss1_msg_without_setup(pc, CAUSE_MANDATORY_IE_MISS);
++              dev_kfree_skb(skb);
++              return;
++      }
++      newl3state(pc, 6);
++      L3DelTimer(&pc->timer);
++      L3AddTimer(&pc->timer, T_CTRL, CC_TCTRL);
++      if (err) /* STATUS for none mandatory IE errors after actions are taken */
++              l3dss1_std_ie_err(pc, err);
++// already done
++//    err = mISDN_l3up(pc, CC_NEW_CR | INDICATION, NULL);
++      if (mISDN_l3up(pc, CC_SETUP | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_reset(l3_process_t *pc, u_char pr, void *arg)
++{
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_release(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret, cause=0;
++
++      StopAllL3Timer(pc);
++      if ((ret = l3dss1_get_cause(pc, skb))) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "REL get_cause ret(%d)", ret);
++              if ((ret == -1) && (pc->state != 11))
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else if (ret != -1)
++                      cause = CAUSE_INVALID_CONTENTS;
++      }
++      ret = check_infoelements(pc, skb, ie_RELEASE);
++      if (ERR_IE_COMPREHENSION == ret)
++              cause = CAUSE_MANDATORY_IE_MISS;
++      else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
++              cause = CAUSE_IE_NOTIMPLEMENTED;
++      if (cause)
++              l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
++      else
++              l3dss1_message(pc, MT_RELEASE_COMPLETE);
++      if (mISDN_l3up(pc, CC_RELEASE | INDICATION, skb))
++              dev_kfree_skb(skb);
++      newl3state(pc, 0);
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_progress(l3_process_t *pc, u_char pr, void *arg) {
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      int             err = 0;
++      u_char          *p, cause = CAUSE_INVALID_CONTENTS;
++
++      if (qi->progress) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->progress;
++              p++;
++              if (p[0] != 2) {
++                      err = 1;
++              } else if (!(p[1] & 0x70)) {
++                      switch (p[1]) {
++                              case 0x80:
++                              case 0x81:
++                              case 0x82:
++                              case 0x84:
++                              case 0x85:
++                              case 0x87:
++                              case 0x8a:
++                                      switch (p[2]) {
++                                              case 0x81:
++                                              case 0x82:
++                                              case 0x83:
++                                              case 0x84:
++                                              case 0x88:
++                                                      break;
++                                              default:
++                                                      err = 2;
++                                                      break;
++                                      }
++                                      break;
++                              default:
++                                      err = 3;
++                                      break;
++                      }
++              }
++      } else {
++              cause = CAUSE_MANDATORY_IE_MISS;
++              err = 4;
++      }
++      if (err) {      
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "progress error %d", err);
++              l3dss1_status_send(pc, cause);
++              dev_kfree_skb(skb);
++              return;
++      }
++      /* Now we are on none mandatory IEs */
++      err = check_infoelements(pc, skb, ie_PROGRESS);
++      if (err)
++              l3dss1_std_ie_err(pc, err);
++      if (ERR_IE_COMPREHENSION != err) {
++              if (mISDN_l3up(pc, CC_PROGRESS | INDICATION, skb))
++                      dev_kfree_skb(skb);
++      } else
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_notify(l3_process_t *pc, u_char pr, void *arg) {
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      int             err = 0;
++      u_char          *p, cause = CAUSE_INVALID_CONTENTS;
++                        
++      if (qi->notify) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->notify;
++              p++;
++              if (p[0] != 1) {
++                      err = 1;
++              } else {
++                      switch (p[1]) {
++                              case 0x80:
++                              case 0x81:
++                              case 0x82:
++                                      break;
++                              default:
++                                      err = 2;
++                                      break;
++                      }
++              }
++      } else {
++              cause = CAUSE_MANDATORY_IE_MISS;
++              err = 3;
++      }
++      if (err) {      
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "notify error %d", err);
++              l3dss1_status_send(pc, cause);
++              dev_kfree_skb(skb);
++              return;
++      }
++      /* Now we are on none mandatory IEs */
++      err = check_infoelements(pc, skb, ie_NOTIFY);
++      if (err)
++              l3dss1_std_ie_err(pc, err);
++      if (ERR_IE_COMPREHENSION != err) {
++              if (mISDN_l3up(pc, CC_NOTIFY | INDICATION, skb))
++                      dev_kfree_skb(skb);
++      } else
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_status_enq(l3_process_t *pc, u_char pr, void *arg) {
++      int             ret;
++      struct sk_buff  *skb = arg;
++
++      ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
++      l3dss1_std_ie_err(pc, ret);
++      l3dss1_status_send(pc, CAUSE_STATUS_RESPONSE);
++      if (mISDN_l3up(pc, CC_STATUS_ENQUIRY | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_information(l3_process_t *pc, u_char pr, void *arg) {
++      int             ret;
++      struct sk_buff  *skb = arg;
++
++      ret = check_infoelements(pc, skb, ie_INFORMATION);
++      if (ret)
++              l3dss1_std_ie_err(pc, ret);
++      if (pc->state == 25) { /* overlap receiving */
++              L3DelTimer(&pc->timer);
++              L3AddTimer(&pc->timer, T302, CC_T302);
++      }
++      if (mISDN_l3up(pc, CC_INFORMATION | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_release_ind(l3_process_t *pc, u_char pr, void *arg)
++{
++      u_char          *p;
++      struct sk_buff  *skb = arg;
++      int             err, callState = -1;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++
++      if (qi->call_state) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->call_state;
++              p++;
++              if (1 == *p++)
++                      callState = *p;
++      }
++      if (callState == 0) {
++              /* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
++               * set down layer 3 without sending any message
++               */
++              newl3state(pc, 0);
++              err = mISDN_l3up(pc, CC_RELEASE | INDICATION, skb);
++              release_l3_process(pc);
++      } else {
++              err = mISDN_l3up(pc, CC_RELEASE | INDICATION, skb);
++      }
++      if (err)
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_restart(l3_process_t *pc, u_char pr, void *arg) {
++      struct sk_buff  *skb = arg;
++      L3DelTimer(&pc->timer);
++      mISDN_l3up(pc, CC_RELEASE | INDICATION, NULL);
++      release_l3_process(pc);
++      if (skb)
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_status(l3_process_t *pc, u_char pr, void *arg) {
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      int             ret = 0; 
++      u_char          *p, cause = 0, callState = 0xff;
++      
++      if ((ret = l3dss1_get_cause(pc, skb))) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "STATUS get_cause ret(%d)", ret);
++              if (ret == -1)
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else
++                      cause = CAUSE_INVALID_CONTENTS;
++      }
++      if (qi->call_state) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->call_state;
++              p++;
++              if (1 == *p++) {
++                      callState = *p;
++                      if (!ie_in_set(pc, callState, l3_valid_states))
++                              cause = CAUSE_INVALID_CONTENTS;
++              } else
++                      cause = CAUSE_INVALID_CONTENTS;
++      } else
++              cause = CAUSE_MANDATORY_IE_MISS;
++      if (!cause) { /*  no error before */
++              ret = check_infoelements(pc, skb, ie_STATUS);
++              if (ERR_IE_COMPREHENSION == ret)
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else if (ERR_IE_UNRECOGNIZED == ret)
++                      cause = CAUSE_IE_NOTIMPLEMENTED;
++      }
++      if (cause) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "STATUS error(%d/%d)", ret, cause);
++              l3dss1_status_send(pc, cause);
++              if (cause != CAUSE_IE_NOTIMPLEMENTED) {
++                      dev_kfree_skb(skb);
++                      return;
++              }
++      }
++      if (qi->cause)
++              cause = pc->err & 0x7f;
++      if ((cause == CAUSE_PROTOCOL_ERROR) && (callState == 0)) {
++              /* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
++               * if received MT_STATUS with cause == 111 and call
++               * state == 0, then we must set down layer 3
++               */
++              newl3state(pc, 0);
++              ret = mISDN_l3up(pc, CC_STATUS| INDICATION, skb);
++              release_l3_process(pc);
++      } else
++              ret = mISDN_l3up(pc, CC_STATUS | INDICATION, skb);
++      if (ret)
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_facility(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      int             ret;
++      
++      ret = check_infoelements(pc, skb, ie_FACILITY);
++      l3dss1_std_ie_err(pc, ret);
++      if (!qi->facility) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "FACILITY without IE_FACILITY");
++              dev_kfree_skb(skb);
++              return;
++      }               
++      if (mISDN_l3up(pc, CC_FACILITY | INDICATION, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l3dss1_suspend_ack(l3_process_t *pc, u_char pr, void *arg) {
++      struct sk_buff  *skb = arg;
++      int             ret;
++
++      L3DelTimer(&pc->timer);
++      newl3state(pc, 0);
++      /* We don't handle suspend_ack for IE errors now */
++      if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "SUSPACK check ie(%d)",ret);
++      if (mISDN_l3up(pc, CC_SUSPEND_ACKNOWLEDGE | INDICATION, skb))
++              dev_kfree_skb(skb);
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_suspend_rej(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++      u_char          cause;
++
++      if ((ret = l3dss1_get_cause(pc, skb))) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "SUSP_REJ get_cause err(%d)", ret);
++              if (ret == -1) 
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else
++                      cause = CAUSE_INVALID_CONTENTS;
++              l3dss1_status_send(pc, cause);
++              dev_kfree_skb(skb);
++              return;
++      }
++      ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      L3DelTimer(&pc->timer);
++      if (mISDN_l3up(pc, CC_SUSPEND_REJECT | INDICATION, skb))
++              dev_kfree_skb(skb);
++      newl3state(pc, 10);
++      if (ret) /* STATUS for none mandatory IE errors after actions are taken */
++              l3dss1_std_ie_err(pc, ret);
++}
++
++static void
++l3dss1_resume_ack(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++
++      if (!(ret = l3dss1_get_channel_id(pc, skb))) {
++              if ((0 == pc->bc) || (3 == pc->bc)) {
++                      if (pc->l3->debug & L3_DEB_WARN)
++                              l3_debug(pc->l3, "resume ack with wrong chid %x",
++                                      pc->bc);
++                      l3dss1_status_send(pc, CAUSE_INVALID_CONTENTS);
++                      dev_kfree_skb(skb);
++                      return;
++              }
++      } else if (1 == pc->state) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "resume ack without chid err(%d)",
++                              ret);
++              l3dss1_status_send(pc, CAUSE_MANDATORY_IE_MISS);
++              dev_kfree_skb(skb);
++              return;
++      }
++      ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      L3DelTimer(&pc->timer);
++      if (mISDN_l3up(pc, CC_RESUME_ACKNOWLEDGE | INDICATION, skb))
++              dev_kfree_skb(skb);
++      newl3state(pc, 10);
++      if (ret) /* STATUS for none mandatory IE errors after actions are taken */
++              l3dss1_std_ie_err(pc, ret);
++}
++
++static void
++l3dss1_resume_rej(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      int             ret;
++      u_char          cause;
++
++      if ((ret = l3dss1_get_cause(pc, skb))) {
++              if (pc->l3->debug & L3_DEB_WARN)
++                      l3_debug(pc->l3, "RES_REJ get_cause err(%d)", ret);
++              if (ret == -1) 
++                      cause = CAUSE_MANDATORY_IE_MISS;
++              else
++                      cause = CAUSE_INVALID_CONTENTS;
++              l3dss1_status_send(pc, cause);
++              dev_kfree_skb(skb);
++              return;
++      }
++      ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
++      if (ERR_IE_COMPREHENSION == ret) {
++              l3dss1_std_ie_err(pc, ret);
++              dev_kfree_skb(skb);
++              return;
++      }
++      L3DelTimer(&pc->timer);
++      if (mISDN_l3up(pc, CC_RESUME_REJECT | INDICATION, skb))
++              dev_kfree_skb(skb);
++      newl3state(pc, 0);
++      if (ret) /* STATUS for none mandatory IE errors after actions are taken */
++              l3dss1_std_ie_err(pc, ret);
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_global_restart(l3_process_t *pc, u_char pr, void *arg)
++{
++      u_char          *p, ri, ch = 0, chan = 0;
++      struct sk_buff  *skb = arg;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      l3_process_t    *up, *n;
++
++//    newl3state(pc, 2);
++      L3DelTimer(&pc->timer);
++      if (qi->restart_ind) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->restart_ind;
++              p++;
++              ri = p[1];
++              l3_debug(pc->l3, "Restart %x", ri);
++      } else {
++              l3_debug(pc->l3, "Restart without restart IE");
++              ri = 0x86;
++      }
++      if (qi->channel_id) {
++              p = skb->data;
++              p += L3_EXTRA_SIZE + qi->channel_id;
++              p++;
++              chan = p[1] & 3;
++              ch = p[1];
++              if (pc->l3->debug)
++                      l3_debug(pc->l3, "Restart for channel %d", chan);
++      }
++      list_for_each_entry_safe(up, n, &pc->l3->plist, list) {
++              if ((ri & 7) == 7)
++                      dss1man(up, CC_RESTART | REQUEST, NULL);
++              else if (up->bc == chan)
++                      mISDN_l3up(up, CC_RESTART | REQUEST, NULL);
++      }
++      dev_kfree_skb(skb);
++      skb = MsgStart(pc, MT_RESTART_ACKNOWLEDGE, chan ? 6 : 3);
++      p = skb_put(skb, chan ? 6 : 3);
++      if (chan) {
++              *p++ = IE_CHANNEL_ID;
++              *p++ = 1;
++              *p++ = ch | 0x80;
++      }
++      *p++ = IE_RESTART_IND;
++      *p++ = 1;
++      *p++ = ri;
++      if (l3_msg(pc->l3, DL_DATA | REQUEST, 0, 0, skb))
++              kfree_skb(skb);
++}
++
++static void
++l3dss1_dummy(l3_process_t *pc, u_char pr, void *arg)
++{
++}
++
++static void
++l3dss1_t302(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++      newl3state(pc, 11);
++      l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_INVALID_NUMBER);
++      mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL);
++}
++
++static void
++l3dss1_t303(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++      if (pc->n303 > 0) {
++              pc->n303--;
++              if (pc->t303skb) {
++                      struct sk_buff  *skb;
++                      if (pc->n303 > 0) {
++                              skb = skb_clone(pc->t303skb, GFP_ATOMIC);
++                      } else {
++                              skb = pc->t303skb;
++                              pc->t303skb = NULL;
++                      }
++                      if (skb)
++                              SendMsg(pc, skb, -1);
++              }
++              L3AddTimer(&pc->timer, T303, CC_T303);
++              return;
++      }
++      if (pc->t303skb)
++              kfree_skb(pc->t303skb);
++      pc->t303skb = NULL;
++      l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, CAUSE_TIMER_EXPIRED);
++      mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL);
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_t304(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++      newl3state(pc, 11);
++      l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED);
++      mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL);
++}
++
++static void
++l3dss1_t305(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++#if 0
++      if (pc->cause != NO_CAUSE)
++              cause = pc->cause;
++#endif
++      newl3state(pc, 19);
++      l3dss1_message_cause(pc, MT_RELEASE, CAUSE_NORMALUNSPECIFIED);
++      L3AddTimer(&pc->timer, T308, CC_T308_1);
++}
++
++static void
++l3dss1_t310(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++      newl3state(pc, 11);
++      l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED);
++      mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL);
++}
++
++static void
++l3dss1_t313(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++      newl3state(pc, 11);
++      l3dss1_message_cause(pc, MT_DISCONNECT, CAUSE_TIMER_EXPIRED);
++      mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL);
++}
++
++static void
++l3dss1_t308_1(l3_process_t *pc, u_char pr, void *arg)
++{
++      newl3state(pc, 19);
++      L3DelTimer(&pc->timer);
++      l3dss1_message(pc, MT_RELEASE);
++      L3AddTimer(&pc->timer, T308, CC_T308_2);
++}
++
++static void
++l3dss1_t308_2(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++      mISDN_l3up(pc, CC_TIMEOUT | INDICATION, NULL);
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_t318(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++#if 0
++      pc->cause = 102;        /* Timer expiry */
++      pc->para.loc = 0;       /* local */
++#endif
++      mISDN_l3up(pc, CC_RESUME_REJECT | INDICATION, NULL);
++      newl3state(pc, 19);
++      l3dss1_message(pc, MT_RELEASE);
++      L3AddTimer(&pc->timer, T308, CC_T308_1);
++}
++
++static void
++l3dss1_t319(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++#if 0
++      pc->cause = 102;        /* Timer expiry */
++      pc->para.loc = 0;       /* local */
++#endif
++      mISDN_l3up(pc, CC_SUSPEND_REJECT | INDICATION, NULL);
++      newl3state(pc, 10);
++}
++
++static void
++l3dss1_dl_reset(l3_process_t *pc, u_char pr, void *arg)
++{
++      struct sk_buff  *nskb, *skb = alloc_skb(L3_EXTRA_SIZE + 10, GFP_ATOMIC);
++      Q931_info_t     *qi;
++      u_char          *p;
++
++      if (!skb)
++              return;
++      qi = (Q931_info_t *)skb_put(skb, L3_EXTRA_SIZE);
++      mISDN_initQ931_info(qi);
++      qi->type = MT_DISCONNECT;
++      qi->cause = 1;
++      p = skb_put(skb, 5);
++      p++;
++      *p++ = IE_CAUSE;
++      *p++ = 2;
++      *p++ = 0x80 | CAUSE_LOC_USER;
++      *p++ = 0x80 | CAUSE_TEMPORARY_FAILURE;
++      nskb = skb_clone(skb, GFP_ATOMIC);
++      l3dss1_disconnect_req(pc, pr, skb);
++      if (nskb) {
++              if (mISDN_l3up(pc, CC_DISCONNECT | REQUEST, nskb))
++                      dev_kfree_skb(nskb);
++      }
++}
++
++static void
++l3dss1_dl_release(l3_process_t *pc, u_char pr, void *arg)
++{
++      newl3state(pc, 0);
++#if 0
++        pc->cause = 0x1b;          /* Destination out of order */
++        pc->para.loc = 0;
++#endif
++      release_l3_process(pc);
++}
++
++static void
++l3dss1_dl_reestablish(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++      L3AddTimer(&pc->timer, T309, CC_T309);
++      l3_msg(pc->l3, DL_ESTABLISH | REQUEST, 0, 0, NULL);
++}
++ 
++static void
++l3dss1_dl_reest_status(l3_process_t *pc, u_char pr, void *arg)
++{
++      L3DelTimer(&pc->timer);
++
++      l3dss1_status_send(pc, CAUSE_NORMALUNSPECIFIED);
++}
++
++/* *INDENT-OFF* */
++static struct stateentry downstatelist[] =
++{
++      {SBIT(0),
++       CC_SETUP | REQUEST, l3dss1_setup_req},
++      {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) |
++              SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(25),
++       CC_INFORMATION | REQUEST, l3dss1_information_req},
++      {SBIT(10),
++       CC_PROGRESS | REQUEST, l3dss1_progress_req},
++      {SBIT(0),
++       CC_RESUME | REQUEST, l3dss1_resume_req},
++      {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) |
++              SBIT(8) | SBIT(9) | SBIT(10) | SBIT(25),
++       CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
++      {SBIT(11) | SBIT(12),
++       CC_RELEASE | REQUEST, l3dss1_release_req},
++      {ALL_STATES,
++       CC_RESTART | REQUEST, l3dss1_restart},
++      {SBIT(6) | SBIT(25),
++       CC_SETUP | RESPONSE, l3dss1_release_cmpl_req},
++      {ALL_STATES,
++       CC_RELEASE_COMPLETE | REQUEST, l3dss1_release_cmpl_req},
++      {SBIT(6) | SBIT(25),
++       CC_PROCEEDING | REQUEST, l3dss1_proceed_req},
++      {SBIT(6),
++       CC_SETUP_ACKNOWLEDGE | REQUEST, l3dss1_setup_ack_req},
++      {SBIT(25),
++       CC_SETUP_ACKNOWLEDGE | REQUEST, l3dss1_dummy},
++      {SBIT(6) | SBIT(9) | SBIT(25),
++       CC_ALERTING | REQUEST, l3dss1_alert_req},
++      {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
++       CC_CONNECT | REQUEST, l3dss1_connect_req},
++      {SBIT(10),
++       CC_SUSPEND | REQUEST, l3dss1_suspend_req},
++      {ALL_STATES,
++       CC_STATUS_ENQUIRY | REQUEST, l3dss1_status_enq_req},
++};
++
++#define DOWNSLLEN \
++      (sizeof(downstatelist) / sizeof(struct stateentry))
++
++static struct stateentry datastatelist[] =
++{
++      {ALL_STATES,
++       MT_STATUS_ENQUIRY, l3dss1_status_enq},
++      {ALL_STATES,
++       MT_FACILITY, l3dss1_facility},
++      {SBIT(19),
++       MT_STATUS, l3dss1_release_ind},
++      {ALL_STATES,
++       MT_STATUS, l3dss1_status},
++      {SBIT(0),
++       MT_SETUP, l3dss1_setup},
++      {SBIT(6) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) |
++       SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
++       MT_SETUP, l3dss1_dummy},
++      {SBIT(1) | SBIT(2),
++       MT_CALL_PROCEEDING, l3dss1_call_proc},
++      {SBIT(1),
++       MT_SETUP_ACKNOWLEDGE, l3dss1_setup_ack},
++      {SBIT(2) | SBIT(3),
++       MT_ALERTING, l3dss1_alerting},
++      {SBIT(2) | SBIT(3),
++       MT_PROGRESS, l3dss1_progress},
++      {SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) |
++       SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
++       MT_INFORMATION, l3dss1_information},
++      {SBIT(10) | SBIT(11) | SBIT(15),
++       MT_NOTIFY, l3dss1_notify},
++      {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
++       SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19) | SBIT(25),
++       MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
++      {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(25),
++       MT_RELEASE, l3dss1_release},
++      {SBIT(19),  MT_RELEASE, l3dss1_release_ind},
++      {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(9) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(25),
++       MT_DISCONNECT, l3dss1_disconnect},
++      {SBIT(19),
++       MT_DISCONNECT, l3dss1_dummy},
++      {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
++       MT_CONNECT, l3dss1_connect},
++      {SBIT(8),
++       MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
++      {SBIT(15),
++       MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack},
++      {SBIT(15),
++       MT_SUSPEND_REJECT, l3dss1_suspend_rej},
++      {SBIT(17),
++       MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack},
++      {SBIT(17),
++       MT_RESUME_REJECT, l3dss1_resume_rej},
++};
++
++#define DATASLLEN \
++      (sizeof(datastatelist) / sizeof(struct stateentry))
++
++static struct stateentry globalmes_list[] =
++{
++      {ALL_STATES,
++       MT_STATUS, l3dss1_status},
++      {SBIT(0),
++       MT_RESTART, l3dss1_global_restart},
++/*    {SBIT(1),
++       MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
++*/
++};
++#define GLOBALM_LEN \
++      (sizeof(globalmes_list) / sizeof(struct stateentry))
++
++static struct stateentry manstatelist[] =
++{
++        {SBIT(2),
++         DL_ESTABLISH | INDICATION, l3dss1_dl_reset},
++        {SBIT(10),
++         DL_ESTABLISH | CONFIRM, l3dss1_dl_reest_status},
++        {SBIT(10),
++         DL_RELEASE | INDICATION, l3dss1_dl_reestablish},
++        {ALL_STATES,
++         DL_RELEASE | INDICATION, l3dss1_dl_release},
++      {SBIT(25),
++       CC_T302, l3dss1_t302},
++      {SBIT(1),
++       CC_T303, l3dss1_t303},
++      {SBIT(2),
++       CC_T304, l3dss1_t304},
++      {SBIT(3),
++       CC_T310, l3dss1_t310},
++      {SBIT(8),
++       CC_T313, l3dss1_t313},
++      {SBIT(11),
++       CC_T305, l3dss1_t305},
++      {SBIT(15),
++       CC_T319, l3dss1_t319},
++      {SBIT(17),
++       CC_T318, l3dss1_t318},
++      {SBIT(19),
++       CC_T308_1, l3dss1_t308_1},
++      {SBIT(19),
++       CC_T308_2, l3dss1_t308_2},
++      {SBIT(10),
++       CC_T309, l3dss1_dl_release},
++      {SBIT(6),
++       CC_TCTRL, l3dss1_reset},
++      {ALL_STATES,
++       CC_RESTART | REQUEST, l3dss1_restart},
++};
++
++#define MANSLLEN \
++        (sizeof(manstatelist) / sizeof(struct stateentry))
++/* *INDENT-ON* */
++
++
++static void
++global_handler(layer3_t *l3, u_int mt, struct sk_buff *skb)
++{
++      u_int           i;
++      l3_process_t    *proc = l3->global;
++
++      proc->callref = skb->data[2]; /* cr flag */
++      for (i = 0; i < GLOBALM_LEN; i++)
++              if ((mt == globalmes_list[i].primitive) &&
++                  ((1 << proc->state) & globalmes_list[i].state))
++                      break;
++      if (i == GLOBALM_LEN) {
++              if (l3->debug & L3_DEB_STATE) {
++                      l3_debug(l3, "dss1 global state %d mt %x unhandled",
++                              proc->state, mt);
++              }
++              l3dss1_status_send(proc, CAUSE_INVALID_CALLREF);
++              dev_kfree_skb(skb);
++      } else {
++              if (l3->debug & L3_DEB_STATE) {
++                      l3_debug(l3, "dss1 global %d mt %x",
++                              proc->state, mt);
++              }
++              globalmes_list[i].rout(proc, mt, skb);
++      }
++}
++
++static int
++dss1_fromdown(mISDNif_t *hif, struct sk_buff *skb)
++{
++      layer3_t        *l3;
++      u_int           i;
++      int             cause, callState, ret = -EINVAL;
++      char            *ptr;
++      l3_process_t    *proc;
++      mISDN_head_t    *hh;
++      Q931_info_t     *qi;
++      
++
++      if (!hif || !skb)
++              return(ret);
++      l3 = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      if (debug)
++              printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim);
++      if (!l3)
++              return(ret);
++      switch (hh->prim) {
++              case (DL_DATA | INDICATION):
++              case (DL_UNITDATA | INDICATION):
++                      break;
++              case (DL_ESTABLISH | CONFIRM):
++              case (DL_ESTABLISH | INDICATION):
++              case (DL_RELEASE | INDICATION):
++              case (DL_RELEASE | CONFIRM):
++                      l3_msg(l3, hh->prim, hh->dinfo, 0, NULL);
++                      dev_kfree_skb(skb);
++                      return(0);
++                      break;
++              case (DL_DATA | CONFIRM):
++              case (DL_UNITDATA | CONFIRM):
++                      dev_kfree_skb(skb);
++                      return(0);
++                      break;
++                default:
++                        printk(KERN_WARNING "%s: unknown pr=%04x\n",
++                              __FUNCTION__, hh->prim);
++                        return(-EINVAL);
++      }
++      if (skb->len < 3) {
++              l3_debug(l3, "dss1up frame too short(%d)", skb->len);
++              dev_kfree_skb(skb);
++              return(0);
++      }
++      if (skb->data[0] != PROTO_DIS_EURO) {
++              if (l3->debug & L3_DEB_PROTERR) {
++                      l3_debug(l3, "dss1up%sunexpected discriminator %x message len %d",
++                               (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
++                               skb->data[0], skb->len);
++              }
++              dev_kfree_skb(skb);
++              return(0);
++      }
++      ret = parseQ931(skb);
++      if (ret < 0) {
++              if (l3->debug & L3_DEB_PROTERR)
++                      l3_debug(l3, "dss1up: parse IE error %d", ret);
++              printk(KERN_WARNING "dss1up: parse IE error %d\n", ret);
++              dev_kfree_skb(skb);
++              return(0);
++      }
++      qi = (Q931_info_t *)skb->data;
++      ptr = skb->data;
++      ptr += L3_EXTRA_SIZE;
++      if (l3->debug & L3_DEB_STATE)
++              l3_debug(l3, "dss1up cr %d", qi->cr);
++      if (qi->crlen == 0) {   /* Dummy Callref */
++              if (qi->type == MT_FACILITY) {
++                      l3dss1_facility(l3->dummy, hh->prim, skb);
++                      return(0);
++              } else if (l3->debug & L3_DEB_WARN)
++                      l3_debug(l3, "dss1up dummy Callref (no facility msg or ie)");
++              dev_kfree_skb(skb);
++              return(0);
++      } else if ((qi->cr & 0x7fff) == 0) {    /* Global CallRef */
++              if (l3->debug & L3_DEB_STATE)
++                      l3_debug(l3, "dss1up Global CallRef");
++              global_handler(l3, qi->type, skb);
++              return(0);
++      } else if (!(proc = getl3proc(l3, qi->cr))) {
++              /* No transaction process exist, that means no call with
++               * this callreference is active
++               */
++              if (qi->type == MT_SETUP) {
++                      /* Setup creates a new l3 process */
++                      if (qi->cr & 0x8000) {
++                              /* Setup with wrong CREF flag */
++                              if (l3->debug & L3_DEB_STATE)
++                                      l3_debug(l3, "dss1up wrong CRef flag");
++                              dev_kfree_skb(skb);
++                              return(0);
++                      }
++                      if (!(proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) {
++                              /* May be to answer with RELEASE_COMPLETE and
++                               * CAUSE 0x2f "Resource unavailable", but this
++                               * need a new_l3_process too ... arghh
++                               */
++                              dev_kfree_skb(skb);
++                              return(0);
++                      }
++                      /* register this ID in L4 */
++                      ret = mISDN_l3up(proc, CC_NEW_CR | INDICATION, NULL);
++                      if (ret) {
++                              printk(KERN_WARNING "dss1up: cannot register ID(%x)\n",
++                                      proc->id);
++                              dev_kfree_skb(skb);
++                              release_l3_process(proc);
++                              return(0);
++                      }
++              } else if (qi->type == MT_STATUS) {
++                      cause = 0;
++                      if (qi->cause) {
++                              if (ptr[qi->cause +1] >= 2)
++                                      cause = ptr[qi->cause + 3] & 0x7f;
++                              else
++                                      cause = ptr[qi->cause + 2] & 0x7f;      
++                      }
++                      callState = 0;
++                      if (qi->call_state) {
++                              callState = ptr[qi->cause + 2];
++                      }
++                      /* ETS 300-104 part 2.4.1
++                       * if setup has not been made and a message type
++                       * MT_STATUS is received with call state == 0,
++                       * we must send nothing
++                       */
++                      if (callState != 0) {
++                              /* ETS 300-104 part 2.4.2
++                               * if setup has not been made and a message type
++                               * MT_STATUS is received with call state != 0,
++                               * we must send MT_RELEASE_COMPLETE cause 101
++                               */
++                              if ((proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) {
++                                      l3dss1_msg_without_setup(proc,
++                                              CAUSE_NOTCOMPAT_STATE);
++                              }
++                      }
++                      dev_kfree_skb(skb);
++                      return(0);
++              } else if (qi->type == MT_RELEASE_COMPLETE) {
++                      dev_kfree_skb(skb);
++                      return(0);
++              } else {
++                      /* ETS 300-104 part 2
++                       * if setup has not been made and a message type
++                       * (except MT_SETUP and RELEASE_COMPLETE) is received,
++                       * we must send MT_RELEASE_COMPLETE cause 81 */
++                      if ((proc = new_l3_process(l3, qi->cr, N303, MISDN_ID_ANY))) {
++                              l3dss1_msg_without_setup(proc,
++                                      CAUSE_INVALID_CALLREF);
++                      }
++                      dev_kfree_skb(skb);
++                      return(0);
++              }
++      }
++      if (l3dss1_check_messagetype_validity(proc, qi->type, skb)) {
++              dev_kfree_skb(skb);
++              return(0);
++      }
++      for (i = 0; i < DATASLLEN; i++)
++              if ((qi->type == datastatelist[i].primitive) &&
++                  ((1 << proc->state) & datastatelist[i].state))
++                      break;
++      if (i == DATASLLEN) {
++              if (l3->debug & L3_DEB_STATE) {
++                      l3_debug(l3, "dss1up%sstate %d mt %#x unhandled",
++                              (hh->prim == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
++                              proc->state, qi->type);
++              }
++              if ((MT_RELEASE_COMPLETE != qi->type) && (MT_RELEASE != qi->type)) {
++                      l3dss1_status_send(proc, CAUSE_NOTCOMPAT_STATE);
++              }
++              dev_kfree_skb(skb);
++      } else {
++              if (l3->debug & L3_DEB_STATE) {
++                      l3_debug(l3, "dss1up%sstate %d mt %x",
++                              (hh->prim == (DL_DATA | INDICATION)) ?
++                              " " : "(broadcast) ", proc->state, qi->type);
++              }
++              datastatelist[i].rout(proc, hh->prim, skb);
++      }
++      return(0);
++}
++
++static int
++dss1_fromup(mISDNif_t *hif, struct sk_buff *skb)
++{
++      layer3_t        *l3;
++      u_int           i;
++      int             cr, ret = -EINVAL;
++      l3_process_t    *proc;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      l3 = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      if (debug)
++              printk(KERN_DEBUG  "%s: prim(%x)\n", __FUNCTION__, hh->prim);
++      if (!l3)
++              return(ret);
++      if ((DL_ESTABLISH | REQUEST) == hh->prim) {
++              l3_msg(l3, hh->prim, 0, 0, NULL);
++              dev_kfree_skb(skb);
++              return(0);
++      }
++      proc = getl3proc4id(l3, hh->dinfo);
++      if ((CC_NEW_CR | REQUEST) == hh->prim) {
++              if (proc) {
++                      printk(KERN_WARNING "%s: proc(%x) allready exist\n",
++                              __FUNCTION__, hh->dinfo);
++                      ret = -EBUSY;
++              } else {
++                      cr = newcallref(l3);
++                      cr |= 0x8000;
++                      ret = -ENOMEM;
++                      if ((proc = new_l3_process(l3, cr, N303, hh->dinfo))) {
++                              ret = 0;
++                              dev_kfree_skb(skb);
++                      }
++              }
++              return(ret);
++      } 
++      if (!proc) {
++              printk(KERN_ERR "mISDN dss1 fromup without proc pr=%04x\n",
++                      hh->prim);
++                              return(-EINVAL);
++      }
++      for (i = 0; i < DOWNSLLEN; i++)
++              if ((hh->prim == downstatelist[i].primitive) &&
++                  ((1 << proc->state) & downstatelist[i].state))
++                      break;
++      if (i == DOWNSLLEN) {
++              if (l3->debug & L3_DEB_STATE) {
++                      l3_debug(l3, "dss1down state %d prim %#x unhandled",
++                              proc->state, hh->prim);
++              }
++              dev_kfree_skb(skb);
++      } else {
++              if (l3->debug & L3_DEB_STATE) {
++                      l3_debug(l3, "dss1down state %d prim %#x para len %d",
++                              proc->state, hh->prim, skb->len);
++              }
++              if (skb->len)
++                      downstatelist[i].rout(proc, hh->prim, skb);
++              else {
++                      downstatelist[i].rout(proc, hh->prim, NULL);
++                      dev_kfree_skb(skb);
++              }
++      }
++      return(0);
++}
++
++static int
++dss1man(l3_process_t *proc, u_int pr, void *arg)
++{
++      u_int   i;
++ 
++      if (!proc) {
++              printk(KERN_ERR "mISDN dss1man without proc pr=%04x\n", pr);
++              return(-EINVAL);
++      }
++      for (i = 0; i < MANSLLEN; i++)
++              if ((pr == manstatelist[i].primitive) &&
++                      ((1 << proc->state) & manstatelist[i].state))
++                      break;
++              if (i == MANSLLEN) {
++                      if (proc->l3->debug & L3_DEB_STATE) {
++                              l3_debug(proc->l3, "cr %d dss1man state %d prim %#x unhandled",
++                                      proc->callref & 0x7fff, proc->state, pr);
++                      }
++              } else {
++                      if (proc->l3->debug & L3_DEB_STATE) {
++                              l3_debug(proc->l3, "cr %d dss1man state %d prim %#x",
++                                      proc->callref & 0x7fff, proc->state, pr);
++                      }
++                      manstatelist[i].rout(proc, pr, arg);
++      }
++      return(0);
++}
++
++static void
++release_udss1(layer3_t *l3)
++{
++      mISDNinstance_t  *inst = &l3->inst;
++
++      printk(KERN_DEBUG "release_udss1 refcnt %d l3(%p) inst(%p)\n",
++              u_dss1.refcnt, l3, inst);
++      release_l3(l3);
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      list_del(&l3->list);
++      u_dss1.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      if (l3->entity != MISDN_ENTITY_NONE)
++              u_dss1.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l3->entity);
++      kfree(l3);
++}
++
++static int
++new_udss1(mISDNstack_t *st, mISDN_pid_t *pid)
++{
++      layer3_t        *nl3;
++      int             err;
++
++      if (!st || !pid)
++              return(-EINVAL);
++      if (!(nl3 = kmalloc(sizeof(layer3_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc layer3 failed\n");
++              return(-ENOMEM);
++      }
++      memset(nl3, 0, sizeof(layer3_t));
++      memcpy(&nl3->inst.pid, pid, sizeof(mISDN_pid_t));
++      nl3->debug = debug;
++      mISDN_init_instance(&nl3->inst, &u_dss1, nl3);
++      if (!mISDN_SetHandledPID(&u_dss1, &nl3->inst.pid)) {
++              int_error();
++              return(-ENOPROTOOPT);
++      }
++      if ((pid->protocol[3] & ~ISDN_PID_FEATURE_MASK) != ISDN_PID_L3_DSS1USER) {
++              printk(KERN_ERR "udss1 create failed prt %x\n",
++                      pid->protocol[3]);
++              kfree(nl3);
++              return(-ENOPROTOOPT);
++      }
++      init_l3(nl3);
++      if (pid->protocol[3] & ISDN_PID_L3_DF_PTP)
++              test_and_set_bit(FLG_PTP, &nl3->Flag);
++      if (pid->protocol[3] & ISDN_PID_L3_DF_EXTCID)
++              test_and_set_bit(FLG_EXTCID, &nl3->Flag);
++      if (pid->protocol[3] & ISDN_PID_L3_DF_CRLEN2)
++              test_and_set_bit(FLG_CRLEN2, &nl3->Flag);
++      if (!(nl3->global = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "mISDN can't get memory for dss1 global CR\n");
++              release_l3(nl3);
++              kfree(nl3);
++              return(-ENOMEM);
++      }
++      nl3->global->state = 0;
++      nl3->global->callref = 0;
++      nl3->global->id = MISDN_ID_GLOBAL;
++      INIT_LIST_HEAD(&nl3->global->list);
++      nl3->global->n303 = N303;
++      nl3->global->l3 = nl3;
++      nl3->global->t303skb = NULL;
++      L3InitTimer(nl3->global, &nl3->global->timer);
++      if (!(nl3->dummy = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "mISDN can't get memory for dss1 dummy CR\n");
++              release_l3(nl3);
++              kfree(nl3);
++              return(-ENOMEM);
++      }
++      nl3->dummy->state = 0;
++      nl3->dummy->callref = -1;
++      nl3->dummy->id = MISDN_ID_DUMMY;
++      INIT_LIST_HEAD(&nl3->dummy->list);
++      nl3->dummy->n303 = N303;
++      nl3->dummy->l3 = nl3;
++      nl3->dummy->t303skb = NULL;
++      L3InitTimer(nl3->dummy, &nl3->dummy->timer);
++      sprintf(nl3->inst.name, "DSS1 %d", st->id);
++      nl3->p_mgr = dss1man;
++      list_add_tail(&nl3->list, &u_dss1.ilist);
++      err = u_dss1.ctrl(&nl3->inst, MGR_NEWENTITY | REQUEST, NULL);
++      if (err) {
++              printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n",
++                      __FUNCTION__, err);
++      }
++      err = u_dss1.ctrl(st, MGR_REGLAYER | INDICATION, &nl3->inst);
++      if (err) {
++              release_l3(nl3);
++              list_del(&nl3->list);
++              kfree(nl3);
++      } else {
++              mISDN_stPara_t  stp;
++
++              if (st->para.down_headerlen)
++                      nl3->down_headerlen = st->para.down_headerlen;
++              stp.maxdatalen = 0;
++              stp.up_headerlen = L3_EXTRA_SIZE;
++              stp.down_headerlen = 0;
++              u_dss1.ctrl(st, MGR_ADDSTPARA | REQUEST, &stp);
++      }
++      return(err);
++}
++
++static char MName[] = "UDSS1";
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++#endif
++
++static int
++udss1_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      layer3_t *l3l;
++
++      if (debug & 0x1000)
++              printk(KERN_DEBUG "udss1_manager data:%p prim:%x arg:%p\n", data, prim, arg);
++      if (!data)
++              return(-EINVAL);
++      list_for_each_entry(l3l, &u_dss1.ilist, list) {
++              if (&l3l->inst == inst)
++                      break;
++      }
++      if (&l3l->list == &u_dss1.ilist)
++              l3l = NULL;
++      if (prim == (MGR_NEWLAYER | REQUEST))
++              return(new_udss1(data, arg));
++      if (!l3l) {
++              if (debug & 0x1)
++                      printk(KERN_WARNING "udss1_manager prim(%x) no instance\n", prim);
++              return(-EINVAL);
++      }
++      switch(prim) {
++          case MGR_NEWENTITY | CONFIRM:
++              l3l->entity = (int)arg;
++              break;
++          case MGR_ADDSTPARA | INDICATION:
++              l3l->down_headerlen = ((mISDN_stPara_t *)arg)->down_headerlen;
++          case MGR_CLRSTPARA | INDICATION:
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              return(mISDN_SetIF(inst, arg, prim, dss1_fromup, dss1_fromdown, l3l));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_RELEASE | INDICATION:
++          case MGR_UNREGLAYER | REQUEST:
++              if (debug & 0x1000)
++                      printk(KERN_DEBUG "release_udss1 id %x\n", l3l->inst.st->id);
++              release_udss1(l3l);
++              break;
++          PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
++          default:
++              if (debug & 0x1)
++                      printk(KERN_WARNING "udss1 prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++int UDSS1Init(void)
++{
++      int err;
++      char tmp[32];
++
++      strcpy(tmp, dss1_revision);
++      printk(KERN_INFO "mISDN: DSS1 Rev. %s\n", mISDN_getrev(tmp));
++#ifdef MODULE
++      u_dss1.owner = THIS_MODULE;
++#endif
++      INIT_LIST_HEAD(&u_dss1.ilist);
++      u_dss1.name = MName;
++      u_dss1.DPROTO.protocol[3] = ISDN_PID_L3_DSS1USER |
++              ISDN_PID_L3_DF_PTP |
++              ISDN_PID_L3_DF_EXTCID |
++              ISDN_PID_L3_DF_CRLEN2;
++      u_dss1.own_ctrl = udss1_manager;
++      mISDNl3New();
++      if ((err = mISDN_register(&u_dss1))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++              mISDNl3Free();
++      }
++      return(err);
++}
++
++#ifdef MODULE
++void UDSS1_cleanup(void)
++{
++      int err;
++      layer3_t        *l3, *next;
++
++      if ((err = mISDN_unregister(&u_dss1))) {
++              printk(KERN_ERR "Can't unregister User DSS1 error(%d)\n", err);
++      }
++      if (!list_empty(&u_dss1.ilist)) {
++              printk(KERN_WARNING "mISDNl3 u_dss1 list not empty\n");
++              list_for_each_entry_safe(l3, next, &u_dss1.ilist, list)
++                      release_udss1(l3);
++      }
++      mISDNl3Free();
++}
++
++module_init(UDSS1Init);
++module_exit(UDSS1_cleanup);
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3helper.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3helper.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/l3helper.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/l3helper.c       2004-11-22 09:33:38.260727400 +0000
+@@ -0,0 +1,197 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * L3 data struct helper functions
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mISDNif.h>
++#include "dss1.h"
++#include "helper.h"
++
++
++static signed char _mISDN_l3_ie2pos[128] = {
++                      -1,-1,-1,-1, 0,-1,-1,-1, 1,-1,-1,-1,-1,-1,-1,-1,
++                       2,-1,-1,-1, 3,-1,-1,-1, 4,-1,-1,-1, 5,-1, 6,-1,
++                       7,-1,-1,-1,-1,-1,-1, 8, 9,10,-1,-1,11,-1,-1,-1,
++                      -1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
++                      13,-1,14,15,16,17,18,19,-1,-1,-1,-1,20,21,-1,-1,
++                      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
++                      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,23,-1,-1,
++                      24,25,-1,-1,26,-1,-1,-1,27,28,-1,-1,29,30,31,-1
++};
++                      
++static unsigned char _mISDN_l3_pos2ie[32] = {
++                      0x04, 0x08, 0x10, 0x14, 0x18, 0x1c, 0x1e, 0x20,
++                      0x27, 0x28, 0x29, 0x2c, 0x34, 0x40, 0x42, 0x43,
++                      0x44, 0x45, 0x46, 0x47, 0x4c, 0x4d, 0x6c, 0x6d,
++                      0x70, 0x71, 0x74, 0x78, 0x79, 0x7c, 0x7d, 0x7e
++};
++
++signed int
++mISDN_l3_ie2pos(u_char c)
++{
++      if (c>0x7f)
++              return(-1);
++      return(_mISDN_l3_ie2pos[c]);
++}
++
++unsigned char
++mISDN_l3_pos2ie(int pos)
++{
++      return(_mISDN_l3_pos2ie[pos]);
++}
++
++void
++mISDN_initQ931_info(Q931_info_t *qi) {
++      memset(qi, 0, sizeof(Q931_info_t));
++};
++
++struct sk_buff *
++#ifdef MISDN_MEMDEBUG
++__mid_alloc_l3msg(int len, u_char type, char *fn, int line)
++#else
++mISDN_alloc_l3msg(int len, u_char type)
++#endif
++{
++      struct sk_buff  *skb;
++      Q931_info_t     *qi;
++
++#ifdef MISDN_MEMDEBUG
++      if (!(skb = __mid_alloc_skb(len + L3_EXTRA_SIZE +1, GFP_ATOMIC, fn, line))) {
++#else
++      if (!(skb = alloc_skb(len + L3_EXTRA_SIZE +1, GFP_ATOMIC))) {
++#endif
++              printk(KERN_WARNING "mISDN: No skb for L3\n");
++              return (NULL);
++      }
++      qi = (Q931_info_t *)skb_put(skb, L3_EXTRA_SIZE +1);
++      mISDN_initQ931_info(qi);
++      qi->type = type;
++      return (skb);
++}
++
++void mISDN_AddvarIE(struct sk_buff *skb, u_char *ie)
++{
++      u_char  *p, *ps;
++      u16     *ies;
++      int     l;
++      Q931_info_t *qi = (Q931_info_t *)skb->data;
++
++      ies = &qi->bearer_capability;
++      ps = (u_char *) qi;
++      ps += L3_EXTRA_SIZE;
++      if (*ie & 0x80) { /* one octett IE */
++              if (*ie == IE_MORE_DATA)
++                      ies = &qi->more_data;
++              else if (*ie == IE_COMPLETE)
++                      ies = &qi->sending_complete;
++              else if ((*ie & 0xf0) == IE_CONGESTION)
++                      ies = &qi->congestion_level;
++              else {
++                      int_error();
++                      return;
++              }
++              l = 1;
++      } else {
++              if (_mISDN_l3_ie2pos[*ie]<0) {
++                      int_error();
++                      return;
++              }
++              ies += _mISDN_l3_ie2pos[*ie];
++              l = ie[1] + 2;
++      }
++      p = skb_put(skb, l);
++      *ies = (u16)(p - ps);
++      memcpy(p, ie, l);
++}
++
++void mISDN_AddIE(struct sk_buff *skb, u_char ie, u_char *iep)
++{
++      u_char  *p, *ps;
++      u16     *ies;
++      int     l;
++      Q931_info_t *qi = (Q931_info_t *)skb->data;
++
++      if (ie & 0x80) { /* one octett IE */
++              if (ie == IE_MORE_DATA)
++                      ies = &qi->more_data;
++              else if (ie == IE_COMPLETE)
++                      ies = &qi->sending_complete;
++              else if ((ie & 0xf0) == IE_CONGESTION)
++                      ies = &qi->congestion_level;
++              else {
++                      int_error();
++                      return;
++              }
++              l = 0;
++      } else {
++              if (!iep || !iep[0])
++                      return;
++              ies = &qi->bearer_capability;
++              if (_mISDN_l3_ie2pos[ie]<0) {
++                      int_error();
++                      return;
++              }
++              ies += _mISDN_l3_ie2pos[ie];
++              l = iep[0] + 1;
++      }
++      ps = (u_char *) qi;
++      ps += L3_EXTRA_SIZE;
++      p = skb_put(skb, l+1);
++      *ies = (u16)(p - ps);
++      *p++ = ie;
++      if (l)
++              memcpy(p, iep, l);
++}
++
++void mISDN_LogL3Msg(struct sk_buff *skb)
++{
++      u_char          *p,*ps, *t, tmp[32];
++      u16             *ies;
++      int             i,j;
++      Q931_info_t     *qi = (Q931_info_t *)skb->data;
++      mISDN_head_t    *hh;
++
++      hh = mISDN_HEAD_P(skb);
++      printk(KERN_DEBUG "L3Msg prim(%x) id(%x) len(%d)\n",
++              hh->prim, hh->dinfo, skb->len);
++      if (skb->len < sizeof(Q931_info_t))
++              return;
++      ies = &qi->bearer_capability;
++      ps = (u_char *) qi;
++      ps += L3_EXTRA_SIZE;
++      printk(KERN_DEBUG "L3Msg type(%02x) qi(%p) ies(%p) ps(%p)\n",
++              qi->type, qi, ies, ps);
++      for (i=0;i<32;i++) {
++              if (ies[i]) {
++                      p = ps + ies[i];
++                      t = tmp;
++                      *t = 0;
++                      for (j=0; j<p[1]; j++) {
++                              if (j>9) {
++                                      sprintf(t, " ...");
++                              }
++                              t += sprintf(t, " %02x", p[j+2]);
++                      }
++                      printk(KERN_DEBUG "L3Msg ies[%d] off(%d) ie(%02x/%02x) len(%d) %s\n",
++                              i, ies[i], _mISDN_l3_pos2ie[i], *p, p[1], tmp);
++              }
++      }
++}
++
++EXPORT_SYMBOL(mISDN_l3_pos2ie);
++EXPORT_SYMBOL(mISDN_l3_ie2pos);
++EXPORT_SYMBOL(mISDN_initQ931_info);
++#ifdef MISDN_MEMDEBUG
++EXPORT_SYMBOL(__mid_alloc_l3msg);
++#else
++EXPORT_SYMBOL(mISDN_alloc_l3msg);
++#endif
++EXPORT_SYMBOL(mISDN_AddvarIE);
++EXPORT_SYMBOL(mISDN_AddIE);
++EXPORT_SYMBOL(mISDN_LogL3Msg);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.c 2004-11-22 09:33:38.270725880 +0000
+@@ -0,0 +1,815 @@
++/* $Id$
++ *
++ * mISDN_l1.c     common low level stuff for I.430 layer1
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ *            This file is (c) under GNU PUBLIC LICENSE
++ *            For changes and modifications please read
++ *            ../../../Documentation/isdn/mISDN.cert
++ *
++ */
++
++static char *l1_revision = "$Revision$";
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++
++typedef struct _layer1 {
++      struct list_head        list;
++      u_long                  Flags;
++      struct FsmInst          l1m;
++      struct FsmTimer         timer;
++      int                     debug;
++      int                     delay;
++      mISDNinstance_t         inst;
++} layer1_t;
++
++/* l1 status_info */
++typedef struct _status_info_l1 {
++      int     len;
++      int     typ;
++      int     protocol;
++      int     status;
++      int     state;
++      u_long  Flags;
++      int     T3;
++      int     delay;
++      int     debug;
++} status_info_l1_t;
++
++static int debug = 0;
++static mISDNobject_t isdnl1;
++
++#define TIMER3_VALUE 7000
++
++static
++struct Fsm l1fsm_b =
++{NULL, 0, 0, NULL, NULL};
++
++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",
++};
++
++#ifdef mISDN_UINTERFACE
++static
++struct Fsm l1fsm_u =
++{NULL, 0, 0, NULL, NULL};
++
++enum {
++      ST_L1_RESET,
++      ST_L1_DEACT,
++      ST_L1_SYNC2,
++      ST_L1_TRANS,
++};
++
++#define L1U_STATE_COUNT (ST_L1_TRANS+1)
++
++static char *strL1UState[] =
++{
++      "ST_L1_RESET",
++      "ST_L1_DEACT",
++      "ST_L1_SYNC2",
++      "ST_L1_TRANS",
++};
++#endif
++
++enum {
++      ST_L1_NULL,
++      ST_L1_WAIT_ACT,
++      ST_L1_WAIT_DEACT,
++      ST_L1_ACTIV,
++};
++
++#define L1B_STATE_COUNT (ST_L1_ACTIV+1)
++
++static char *strL1BState[] =
++{
++      "ST_L1_NULL",
++      "ST_L1_WAIT_ACT",
++      "ST_L1_WAIT_DEACT",
++      "ST_L1_ACTIV",
++};
++
++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, ...)
++{
++      layer1_t *l1 = fi->userdata;
++      logdata_t log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = l1->inst.name;
++      l1->inst.obj->ctrl(&l1->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++static int
++l1up(layer1_t *l1, u_int prim, int dinfo, int len, void *arg)
++{
++      return(if_link(&l1->inst.up, prim, dinfo, len, arg, 0));
++}
++
++static int
++l1down(layer1_t *l1, u_int prim, int dinfo, int len, void *arg)
++{
++      return(if_link(&l1->inst.down, prim, dinfo, len, arg, 0));
++}
++
++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)
++{
++      layer1_t *l1 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L1_F3);
++      if (test_bit(FLG_L1_ACTIVATING, &l1->Flags))
++              l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL);
++}
++
++static void
++l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *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)
++{
++      layer1_t *l1 = fi->userdata;
++
++      if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
++              mISDN_FsmChangeState(fi, ST_L1_F4);
++              l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL);
++              mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
++              test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
++      } 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)
++{
++      layer1_t *l1 = fi->userdata;
++
++#ifdef mISDN_UINTERFACE
++      if (test_bit(FLG_L1_UINT, &l1->Flags))
++              mISDN_FsmChangeState(fi, ST_L1_SYNC2);
++      else
++#endif
++              mISDN_FsmChangeState(fi, ST_L1_F6);
++      l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL);
++}
++
++static void
++l1_info4_ind(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++#ifdef mISDN_UINTERFACE
++      if (test_bit(FLG_L1_UINT, &l1->Flags))
++              mISDN_FsmChangeState(fi, ST_L1_TRANS);
++      else
++#endif
++              mISDN_FsmChangeState(fi, ST_L1_F7);
++      l1down(l1, PH_SIGNAL | REQUEST, INFO3_P8, 0, NULL);
++      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)
++{
++      layer1_t *l1 = fi->userdata;
++      u_int db = HW_D_NOBLOCKED;
++
++      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))
++                      l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db);
++              l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL);
++      }
++#ifdef mISDN_UINTERFACE
++      if (!test_bit(FLG_L1_UINT, &l1->Flags))
++#endif
++      if (l1->l1m.state != ST_L1_F6) {
++              mISDN_FsmChangeState(fi, ST_L1_F3);
++              l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL);
++      }
++}
++
++static void
++l1_timer_act(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags);
++      test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags);
++      if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags))
++              l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL);
++      else
++              l1up(l1, PH_ACTIVATE | INDICATION, 0, 0, NULL);
++}
++
++static void
++l1_timer_deact(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++      u_int db = HW_D_NOBLOCKED;
++
++      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))
++              l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db);
++      l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL);
++      l1down(l1, PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL);
++}
++
++static void
++l1_activate_s(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      l1down(l1, PH_CONTROL | REQUEST, HW_RESET, 0, NULL);
++}
++
++static void
++l1_activate_no(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++      u_int db = HW_D_NOBLOCKED;
++
++      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))
++                      l1up(l1, PH_CONTROL | INDICATION, 0, 4, &db);
++              l1up(l1, PH_DEACTIVATE | INDICATION, 0, 0, NULL);
++      }
++}
++
++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},
++};
++
++#define L1S_FN_COUNT (sizeof(L1SFnList)/sizeof(struct FsmNode))
++
++#ifdef mISDN_UINTERFACE
++static void
++l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L1_RESET);
++      mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2);
++      test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags);
++      l1down(l1, PH_CONTROL | REQUEST, HW_POWERUP, 0, NULL);
++}
++
++static void
++l1_power_up_u(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
++      test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
++}
++
++static void
++l1_info0_ind(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_L1_DEACT);
++}
++
++static void
++l1_activate_u(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      l1down(l1, PH_SIGNAL | REQUEST, INFO1, 0, NULL);
++}
++
++static struct FsmNode L1UFnList[] =
++{
++      {ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u},
++      {ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u},
++      {ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u},
++      {ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u},
++      {ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u},
++      {ST_L1_DEACT, EV_POWER_UP, l1_power_up_u},
++      {ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind},
++      {ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind},
++      {ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind},
++      {ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind},
++      {ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind},
++      {ST_L1_RESET, EV_INFO4_IND, l1_info4_ind},
++      {ST_L1_DEACT, EV_TIMER3, l1_timer3},
++      {ST_L1_SYNC2, EV_TIMER3, l1_timer3},
++      {ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act},
++      {ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact},
++      {ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact},
++      {ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact},
++};
++
++#define L1U_FN_COUNT (sizeof(L1UFnList)/sizeof(struct FsmNode))
++
++#endif
++
++static void
++l1b_activate(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L1_WAIT_ACT);
++      mISDN_FsmRestartTimer(&l1->timer, l1->delay, EV_TIMER_ACT, NULL, 2);
++}
++
++static void
++l1b_deactivate(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L1_WAIT_DEACT);
++      mISDN_FsmRestartTimer(&l1->timer, 10, EV_TIMER_DEACT, NULL, 2);
++}
++
++static void
++l1b_timer_act(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L1_ACTIV);
++      l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL);
++}
++
++static void
++l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
++{
++      layer1_t *l1 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L1_NULL);
++      l1up(l1, PH_DEACTIVATE | CONFIRM, 0, 0, NULL);
++}
++
++static struct FsmNode L1BFnList[] =
++{
++      {ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate},
++      {ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act},
++      {ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate},
++      {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact},
++};
++
++#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
++
++static int
++l1from_up(mISDNif_t *hif, struct sk_buff *skb)
++{
++      layer1_t        *l1;
++      mISDN_head_t    *hh;
++      int             err = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      l1 = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      switch(hh->prim) {
++              case (PH_DATA | REQUEST):
++              case (PH_CONTROL | REQUEST):
++                      if (l1->inst.down.func)
++                              return(l1->inst.down.func(&l1->inst.down,
++                                      skb));
++                      else
++                              err = -ENXIO;
++                      break;
++              case (PH_ACTIVATE | REQUEST):
++                      if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
++                              l1up(l1, PH_ACTIVATE | CONFIRM, 0, 0, NULL);
++                      else {
++                              test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags);
++                              mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL);
++                      }
++                      break;
++              case (MDL_FINDTEI | REQUEST):
++                      if (l1->inst.up.func)
++                              return(l1->inst.up.func(&l1->inst.up, skb));
++                      else
++                              err = -ENXIO;
++                      break;
++              default:
++                      if (l1->debug)
++                              mISDN_debug(l1->inst.st->id, NULL,
++                                      "l1from_up msg %x unhandled", hh->prim);
++                      err = -EINVAL;
++                      break;
++      }
++      if (!err)
++              dev_kfree_skb(skb);
++      return(err);
++}
++
++static int
++l1from_down(mISDNif_t *hif,  struct sk_buff *skb)
++{
++      layer1_t        *l1;
++      mISDN_head_t    *hh;
++      int             err = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      l1 = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      if (hh->prim == PH_DATA_IND) {
++              if (test_bit(FLG_L1_ACTTIMER, &l1->Flags))
++                      mISDN_FsmEvent(&l1->l1m, EV_TIMER_ACT, NULL);
++              if (l1->inst.up.func)
++                      return(l1->inst.up.func(&l1->inst.up, skb));
++              else
++                      err = -ENXIO;
++      } else if (hh->prim == PH_DATA_CNF) {
++              if (l1->inst.up.func)
++                      return(l1->inst.up.func(&l1->inst.up, skb));
++              else
++                      err = -ENXIO;
++      } else if (hh->prim == (PH_CONTROL | INDICATION)) {
++              if (hh->dinfo == HW_RESET)
++                      mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
++              else if (hh->dinfo == HW_DEACTIVATE)
++                      mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL);
++              else if (hh->dinfo == HW_POWERUP)
++                      mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL);
++              else if (l1->debug)
++                      mISDN_debug(l1->inst.st->id, NULL,
++                              "l1from_down ctrl ind %x unhandled", hh->dinfo);
++      } else if (hh->prim == (PH_CONTROL | CONFIRM)) {
++              if (hh->dinfo == HW_DEACTIVATE) 
++                      mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
++              else if (l1->debug)
++                      mISDN_debug(l1->inst.st->id, NULL,
++                              "l1from_down ctrl cnf %x unhandled", hh->dinfo);
++      } else if (hh->prim == (PH_SIGNAL | INDICATION)) {
++              if (hh->dinfo == ANYSIGNAL)
++                      mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
++              else if (hh->dinfo == LOSTFRAMING)
++                      mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
++              else if (hh->dinfo == INFO2)
++                      mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL);
++              else if (hh->dinfo == INFO4_P8)
++                      mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
++              else if (hh->dinfo == INFO4_P10)
++                      mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
++              else if (l1->debug)
++                      mISDN_debug(l1->inst.st->id, NULL,
++                              "l1from_down sig %x unhandled", hh->dinfo);
++      } else {
++              if (l1->debug)
++                      mISDN_debug(l1->inst.st->id, NULL,
++                              "l1from_down msg %x unhandled", hh->prim);
++              err = -EINVAL;
++      }
++      if (!err)
++              dev_kfree_skb(skb);
++      return(err);
++}
++
++static void
++release_l1(layer1_t *l1) {
++      mISDNinstance_t *inst = &l1->inst;
++
++      mISDN_FsmDelTimer(&l1->timer, 0);
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      list_del(&l1->list);
++      isdnl1.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      kfree(l1);
++}
++
++static int
++new_l1(mISDNstack_t *st, mISDN_pid_t *pid) {
++      layer1_t *nl1;
++      int err;
++
++      if (!st || !pid)
++              return(-EINVAL);
++      if (!(nl1 = kmalloc(sizeof(layer1_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc layer1_t failed\n");
++              return(-ENOMEM);
++      }
++      memset(nl1, 0, sizeof(layer1_t));
++      memcpy(&nl1->inst.pid, pid, sizeof(mISDN_pid_t));
++      mISDN_init_instance(&nl1->inst, &isdnl1, nl1);
++      if (!mISDN_SetHandledPID(&isdnl1, &nl1->inst.pid)) {
++              int_error();
++              return(-ENOPROTOOPT);
++      }
++      switch(pid->protocol[1]) {
++          case ISDN_PID_L1_TE_S0:
++              sprintf(nl1->inst.name, "l1TES0 %d", st->id);
++              nl1->l1m.fsm = &l1fsm_s;
++              nl1->l1m.state = ST_L1_F3;
++              nl1->Flags = 0;
++              break;
++          default:
++              printk(KERN_ERR "layer1 create failed prt %x\n",
++                      pid->protocol[1]);
++              kfree(nl1);
++              return(-ENOPROTOOPT);
++      }
++      nl1->debug = debug;
++      nl1->l1m.debug = debug;
++      nl1->l1m.userdata = nl1;
++      nl1->l1m.userint = 0;
++      nl1->l1m.printdebug = l1m_debug;
++      mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer);
++      list_add_tail(&nl1->list, &isdnl1.ilist);
++      err = isdnl1.ctrl(st, MGR_REGLAYER | INDICATION, &nl1->inst);
++      if (err) {
++              mISDN_FsmDelTimer(&nl1->timer, 0);
++              list_del(&nl1->list);
++              kfree(nl1);
++      }
++      return(err);
++}
++
++static int
++l1_status(layer1_t *l1, status_info_l1_t *si)
++{
++
++      if (!si)
++              return(-EINVAL);
++      memset(si, 0, sizeof(status_info_l1_t));
++      si->len = sizeof(status_info_l1_t) - 2*sizeof(int);
++      si->typ = STATUS_INFO_L1;
++      si->protocol = l1->inst.pid.protocol[1];
++      if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
++              si->status = 1;
++      si->state = l1->l1m.state;
++      si->Flags = l1->Flags;
++      si->T3 = TIMER3_VALUE;
++      si->debug = l1->delay;
++      si->debug = l1->debug;
++      return(0);
++}
++
++static char MName[] = "ISDNL1";
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++MODULE_PARM(debug, "1i");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++#define Isdnl1Init init_module
++#endif
++
++static int
++l1_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      layer1_t        *l1l;
++      int             err = -EINVAL;
++
++      if (debug & 0x10000)
++              printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n",
++                      __FUNCTION__, data, prim, arg);
++      if (!data)
++              return(err);
++      list_for_each_entry(l1l, &isdnl1.ilist, list) {
++              if (&l1l->inst == inst) {
++                      err = 0;
++                      break;
++              }
++      }
++      if (err && (prim != (MGR_NEWLAYER | REQUEST))) {
++              printk(KERN_WARNING "l1_manager connect no instance\n");
++              return(err);
++      }
++      
++      switch(prim) {
++          case MGR_NEWLAYER | REQUEST:
++              err = new_l1(data, arg);
++              break;
++          case MGR_CONNECT | REQUEST:
++              err = mISDN_ConnectIF(inst, arg);
++              break;
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              err = mISDN_SetIF(inst, arg, prim, l1from_up, l1from_down, l1l);
++              break;
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              err = mISDN_DisConnectIF(inst, arg);
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++          case MGR_RELEASE | INDICATION:
++              printk(KERN_DEBUG "release_l1 id %x\n", l1l->inst.st->id);
++              release_l1(l1l);
++              break;
++          case MGR_STATUS | REQUEST:
++              err = l1_status(l1l, arg);
++              break;
++          PRIM_NOT_HANDLED(MGR_CTRLREADY|INDICATION);
++          PRIM_NOT_HANDLED(MGR_ADDSTPARA|INDICATION);
++          default:
++              printk(KERN_WARNING "l1_manager prim %x not handled\n", prim);
++              err = -EINVAL;
++              break;
++      }
++      return(err);
++}
++
++int Isdnl1Init(void)
++{
++      int err;
++
++      printk(KERN_INFO "ISDN L1 driver version %s\n", mISDN_getrev(l1_revision));
++#ifdef MODULE
++      isdnl1.owner = THIS_MODULE;
++#endif
++      isdnl1.name = MName;
++      isdnl1.DPROTO.protocol[1] = ISDN_PID_L1_TE_S0;
++      isdnl1.own_ctrl = l1_manager;
++      INIT_LIST_HEAD(&isdnl1.ilist);
++#ifdef mISDN_UINTERFACE
++      isdnl1.DPROTO.protocol[1] |= ISDN_PID_L1_TE_U;
++      l1fsm_u.state_count = L1U_STATE_COUNT;
++      l1fsm_u.event_count = L1_EVENT_COUNT;
++      l1fsm_u.strEvent = strL1Event;
++      l1fsm_u.strState = strL1UState;
++      mISDN_FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT);
++#endif
++      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, L1S_FN_COUNT);
++      l1fsm_b.state_count = L1B_STATE_COUNT;
++      l1fsm_b.event_count = L1_EVENT_COUNT;
++      l1fsm_b.strEvent = strL1Event;
++      l1fsm_b.strState = strL1BState;
++      mISDN_FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
++      if ((err = mISDN_register(&isdnl1))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++#ifdef mISDN_UINTERFACE
++              mISDN_FsmFree(&l1fsm_u);
++#endif
++              mISDN_FsmFree(&l1fsm_s);
++              mISDN_FsmFree(&l1fsm_b);
++      }
++      return(err);
++}
++
++#ifdef MODULE
++void cleanup_module(void)
++{
++      int             err;
++      layer1_t        *l1, *nl1;
++
++      if ((err = mISDN_unregister(&isdnl1))) {
++              printk(KERN_ERR "Can't unregister ISDN layer 1 error(%d)\n", err);
++      }
++      if(!list_empty(&isdnl1.ilist)) {
++              printk(KERN_WARNING "mISDNl1 inst list not empty\n");
++              list_for_each_entry_safe(l1, nl1, &isdnl1.ilist, list)
++                      release_l1(l1);
++      }
++#ifdef mISDN_UINTERFACE
++      mISDN_FsmFree(&l1fsm_u);
++#endif
++      mISDN_FsmFree(&l1fsm_s);
++      mISDN_FsmFree(&l1fsm_b);
++}
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer1.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer1.h 2004-11-22 09:33:38.280724360 +0000
+@@ -0,0 +1,58 @@
++/* $Id$
++ *
++ * Layer 1 defines
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/mISDNif.h>
++#include "fsm.h"
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++#define D_RCVBUFREADY 0
++#define D_XMTBUFREADY 1
++#define D_L1STATECHANGE       2
++#define D_CLEARBUSY   3
++#define D_RX_MON0     4
++#define D_RX_MON1     5
++#define D_TX_MON0     6
++#define D_TX_MON1     7
++#define D_BLOCKEDATOMIC       8
++#define D_LOS         9
++#define D_LOS_OFF     10
++#define D_AIS         11
++#define D_AIS_OFF     12
++#define D_SLIP_TX     13
++#define D_SLIP_RX     14
++
++#define B_RCVBUFREADY 0
++#define B_XMTBUFREADY 1
++#define B_BLOCKEDATOMIC       2
++#define B_DTMFREADY   3
++
++#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
++
++/* L1 Debug */
++#define       L1_DEB_WARN             0x01
++#define       L1_DEB_INTSTAT          0x02
++#define       L1_DEB_ISAC             0x04
++#define       L1_DEB_ISAC_FIFO        0x08
++#define       L1_DEB_HSCX             0x10
++#define       L1_DEB_HSCX_FIFO        0x20
++#define       L1_DEB_LAPD             0x40
++#define       L1_DEB_IPAC             0x80
++#define       L1_DEB_RECEIVE_FRAME    0x100
++#define L1_DEB_MONITOR                0x200
++#define DEB_DLOG_HEX          0x400
++#define DEB_DLOG_VERBOSE      0x800
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.c 2004-11-22 09:33:38.291722688 +0000
+@@ -0,0 +1,2468 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ *            This file is (c) under GNU PUBLIC LICENSE
++ *            For changes and modifications please read
++ *            ../../../Documentation/isdn/mISDN.cert
++ *
++ */
++#include <linux/module.h>
++#include "layer2.h"
++#include "helper.h"
++#include "debug.h"
++
++static char *l2_revision = "$Revision$";
++
++static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
++
++static int debug = 0;
++static mISDNobject_t isdnl2;
++
++static
++struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL};
++
++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)
++
++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",
++};
++
++inline u_int
++l2headersize(layer2_t *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(layer2_t *l2)
++{
++      return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
++}
++
++static int
++l2_newid(layer2_t *l2)
++{
++      u_long  flags;
++      int     id;
++
++      spin_lock_irqsave(&l2->lock, flags);
++      id = l2->next_id++;
++      if (id == 0x7fff)
++              l2->next_id = 1;
++      spin_unlock_irqrestore(&l2->lock, flags);
++      id |= (l2->entity << 16);
++      return(id);
++}
++
++static int
++l2up(layer2_t *l2, u_int prim, int dinfo, struct sk_buff *skb)
++{
++      return(if_newhead(&l2->inst.up, prim, dinfo, skb));
++}
++
++static int
++l2up_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg)
++{
++      return(if_link(&l2->inst.up, prim, dinfo, len, arg, 0));
++}
++
++static int
++l2down_skb(layer2_t *l2, struct sk_buff *skb) {
++      mISDNif_t *down = &l2->inst.down;
++      int ret = -ENXIO;
++
++      if (down->func)
++              ret = down->func(down, skb);
++      if (ret && l2->debug)
++              printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret);
++      return(ret);
++}
++
++static int
++l2down_raw(layer2_t *l2, struct sk_buff *skb)
++{
++      mISDN_head_t *hh = mISDN_HEAD_P(skb);
++
++      if (hh->prim == PH_DATA_REQ) {
++              if (test_and_set_bit(FLG_L1_BUSY, &l2->flag)) {
++                      skb_queue_tail(&l2->down_queue, skb);
++                      return(0);
++              }
++              l2->down_id = mISDN_HEAD_DINFO(skb);
++      }
++      return(l2down_skb(l2, skb));
++}
++
++static int
++l2down(layer2_t *l2, u_int prim, int dinfo, struct sk_buff *skb)
++{
++      mISDN_sethead(prim, dinfo, skb);
++      return(l2down_raw(l2, skb));
++}
++
++static int
++l2down_create(layer2_t *l2, u_int prim, int dinfo, int len, void *arg)
++{
++      struct sk_buff  *skb;
++      int             err;
++
++      skb = create_link_skb(prim, dinfo, len, arg, 0);
++      if (!skb)
++              return(-ENOMEM);
++      err = l2down_raw(l2, skb);
++      if (err)
++              dev_kfree_skb(skb);
++      return(err);
++}
++
++static int
++l2_chain_down(mISDNif_t *hif, struct sk_buff *skb) {
++      if (!hif || !hif->fdata)
++              return(-EINVAL);
++      return(l2down_raw(hif->fdata, skb));
++}
++
++static int
++ph_data_confirm(mISDNif_t *up, mISDN_head_t *hh, struct sk_buff *skb) {
++      layer2_t *l2 = up->fdata;
++      struct sk_buff *nskb = skb; 
++      mISDNif_t *next = up->clone;
++      int ret = -EAGAIN;
++
++      if (test_bit(FLG_L1_BUSY, &l2->flag)) {
++              if (hh->dinfo == l2->down_id) {
++                      if ((nskb = skb_dequeue(&l2->down_queue))) {
++                              l2->down_id = mISDN_HEAD_DINFO(nskb);
++                              if (l2down_skb(l2, nskb)) {
++                                      dev_kfree_skb(nskb);
++                                      l2->down_id = MISDN_ID_NONE;
++                              }
++                      } else
++                              l2->down_id = MISDN_ID_NONE;
++                      if (next)
++                              ret = next->func(next, skb);
++                      if (ret) {
++                              dev_kfree_skb(skb);
++                              ret = 0;
++                      }
++                      if (l2->down_id == MISDN_ID_NONE) {
++                              test_and_clear_bit(FLG_L1_BUSY, &l2->flag);
++                              mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
++                      }
++              }
++      }
++      if (ret && next)
++              ret = next->func(next, skb);
++      if (!test_and_set_bit(FLG_L1_BUSY, &l2->flag)) {
++              if ((nskb = skb_dequeue(&l2->down_queue))) {
++                      l2->down_id = mISDN_HEAD_DINFO(nskb);
++                      if (l2down_skb(l2, nskb)) {
++                              dev_kfree_skb(nskb);
++                              l2->down_id = MISDN_ID_NONE;
++                              test_and_clear_bit(FLG_L1_BUSY, &l2->flag);
++                      }
++              } else
++                      test_and_clear_bit(FLG_L1_BUSY, &l2->flag);
++      }
++      return(ret);
++}
++
++static int
++l2mgr(layer2_t *l2, u_int prim, void *arg) {
++      long c = (long)arg;
++
++      printk(KERN_WARNING "l2mgr: prim %x %c\n", prim, (char)c);
++      if (test_bit(FLG_LAPD, &l2->flag) &&
++              !test_bit(FLG_FIXED_TEI, &l2->flag)) {
++              struct sk_buff  *skb;
++              switch(c) {
++                      case 'C':
++                      case 'D':
++                      case 'G':
++                      case 'H':
++                              skb = create_link_skb(prim, 0, 0, NULL, 0);
++                              if (!skb)
++                                      break;
++                              if (l2_tei(l2->tm, skb))
++                                      dev_kfree_skb(skb);
++                              break;
++              }
++      }
++      return(0);
++}
++
++static void
++set_peer_busy(layer2_t *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(layer2_t *l2) {
++      if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
++              test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
++}
++
++static void
++InitWin(layer2_t *l2)
++{
++      int i;
++
++      for (i = 0; i < MAX_WINDOW; i++)
++              l2->windowar[i] = NULL;
++}
++
++static int
++freewin(layer2_t *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(layer2_t *l2)
++{
++      int cnt;
++
++      if((cnt = freewin(l2)))
++              printk(KERN_WARNING "isdnl2 freed %d skbuffs in release\n", cnt);
++}
++
++inline unsigned int
++cansend(layer2_t *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(layer2_t *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(layer2_t *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);
++      }
++}
++
++inline static void
++enqueue_super(layer2_t *l2, struct sk_buff *skb)
++{
++      if (l2down(l2, PH_DATA | REQUEST, l2_newid(l2), skb))
++              dev_kfree_skb(skb);
++}
++
++inline static void
++enqueue_ui(layer2_t *l2, struct sk_buff *skb)
++{
++      if (l2down(l2, PH_DATA | REQUEST, mISDN_HEAD_DINFO(skb), 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, layer2_t *l2)
++{
++      if (test_bit(FLG_MOD128, &l2->flag))
++              return (data[0] == RR);
++      else
++              return ((data[0] & 0xf) == 1);
++}
++
++inline int
++IsSFrame(u_char * data, layer2_t *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, layer2_t *l2)
++{
++      u_char d = data[0] & ~0x10;
++
++      return (test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM);
++}
++
++inline int
++IsREJ(u_char * data, layer2_t *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, layer2_t *l2)
++{
++      return (test_bit(FLG_MOD128, &l2->flag) ? data[0] == RNR : (data[0] & 0xf) == RNR);
++}
++
++int
++iframe_error(layer2_t *l2, struct sk_buff *skb)
++{
++      u_int   i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1);
++      int     rsp = *skb->data & 0x2;
++
++      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(layer2_t *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(layer2_t *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(layer2_t *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(layer2_t *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 (l2->debug)
++                      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 (l2->debug)
++                      l2m_debug(&l2->l2m, "FRMR information %2x %2x %2x",
++                              datap[0], datap[1], datap[2]);
++      }
++      return 0;
++}
++
++static unsigned int
++legalnr(layer2_t *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(layer2_t *l2, unsigned int nr)
++{
++      u_long          flags;
++      struct sk_buff  *skb;
++
++      spin_lock_irqsave(&l2->lock, flags);
++      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;
++      }
++      spin_unlock_irqrestore(&l2->lock, flags);
++      while((skb =skb_dequeue(&l2->tmp_queue))) {
++              if (l2up(l2, DL_DATA | CONFIRM, mISDN_HEAD_DINFO(skb), skb))
++                      dev_kfree_skb(skb);
++      }
++}
++
++static void
++send_uframe(layer2_t *l2, struct sk_buff *skb, u_char cmd, u_char cr)
++{
++      u_char tmp[MAX_HEADER_LEN];
++      int i;
++
++      i = sethdraddr(l2, tmp, cr);
++      tmp[i++] = cmd;
++      if (skb)
++              skb_trim(skb, 0);
++      else if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
++              printk(KERN_WARNING "%s: can't alloc skbuff\n", __FUNCTION__);
++              return;
++      }
++      memcpy(skb_put(skb, i), tmp, i);
++      enqueue_super(l2, skb);
++}
++
++
++inline u_char
++get_PollFlag(layer2_t *l2, struct sk_buff * skb)
++{
++      return (skb->data[l2addrsize(l2)] & 0x10);
++}
++
++inline u_char
++get_PollFlagFree(layer2_t *l2, struct sk_buff *skb)
++{
++      u_char PF;
++
++      PF = get_PollFlag(l2, skb);
++      dev_kfree_skb(skb);
++      return (PF);
++}
++
++inline void
++start_t200(layer2_t *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(layer2_t *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(layer2_t *l2, int i)
++{
++      if(test_and_clear_bit(FLG_T200_RUN, &l2->flag))
++              mISDN_FsmDelTimer(&l2->t200, i);
++}
++
++inline void
++st5_dl_release_l2l3(layer2_t *l2)
++{
++      int pr;
++
++      if (test_and_clear_bit(FLG_PEND_REL, &l2->flag)) {
++              pr = DL_RELEASE | CONFIRM;
++      } else {
++              pr = DL_RELEASE | INDICATION;
++      }
++      l2up_create(l2, pr, 0, 0, NULL);
++}
++
++inline void
++lapb_dl_release_l2l3(layer2_t *l2, int f)
++{
++      if (test_bit(FLG_LAPB, &l2->flag))
++              l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL);
++      l2up_create(l2, DL_RELEASE | f, 0, 0, NULL);
++}
++
++static void
++establishlink(struct FsmInst *fi)
++{
++      layer2_t *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;
++      layer2_t *l2 = fi->userdata;
++
++      if (get_PollFlagFree(l2, skb))
++              l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'C');
++      else
++              l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'D');
++      
++}
++
++static void
++l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
++{
++      struct sk_buff *skb = arg;
++      layer2_t *l2 = fi->userdata;
++
++      if (get_PollFlagFree(l2, skb))
++              l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B');
++      else {
++              l2mgr(l2, MDL_ERROR | INDICATION, (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;
++      layer2_t *l2 = fi->userdata;
++
++      if (get_PollFlagFree(l2, skb))
++              l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'B');
++      else {
++              l2mgr(l2, MDL_ERROR | INDICATION, (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)
++{
++      layer2_t        *l2 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      mISDN_head_t    *hh;
++
++      mISDN_FsmChangeState(fi, ST_L2_3);
++      skb_trim(skb, 0);
++      hh = mISDN_HEAD_P(skb);
++      hh->prim = MDL_ASSIGN | INDICATION;
++      hh->dinfo = 0;
++      if (l2_tei(l2->tm, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      skb_queue_tail(&l2->ui_queue, skb);
++      mISDN_FsmChangeState(fi, ST_L2_2);
++      if ((skb = create_link_skb(MDL_ASSIGN | INDICATION, 0, 0, NULL, 0))) {
++              if (l2_tei(l2->tm, skb))
++                      dev_kfree_skb(skb);
++      }
++}
++
++static void
++l2_queue_ui(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      skb_queue_tail(&l2->ui_queue, skb);
++}
++
++static void
++tx_ui(layer2_t *l2)
++{
++      struct sk_buff *skb;
++      u_char header[MAX_HEADER_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)
++{
++      layer2_t *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)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      skb_pull(skb, l2headersize(l2, 1));
++/*
++ *            in states 1-3 for broadcast
++ */
++      if (l2up(l2, DL_UNITDATA | INDICATION, mISDN_HEAD_DINFO(skb), skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l2_establish(struct FsmInst *fi, int event, void *arg)
++{
++      struct sk_buff *skb = arg;
++      layer2_t *l2 = fi->userdata;
++
++      if (!test_bit(FLG_LAPD_NET, &l2->flag)) {
++              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;
++      layer2_t *l2 = fi->userdata;
++
++      discard_queue(&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;
++      layer2_t *l2 = fi->userdata;
++
++      discard_queue(&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)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      skb_trim(skb, 0);
++      if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l2_pend_rel(struct FsmInst *fi, int event, void *arg)
++{
++      struct sk_buff *skb = arg;
++      layer2_t *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)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&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)
++{
++      layer2_t        *l2 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      u_long          flags;
++
++      spin_lock_irqsave(&l2->lock, flags);
++      l2->vs = 0;
++      l2->va = 0;
++      l2->vr = 0;
++      l2->sow = 0;
++      spin_unlock_irqrestore(&l2->lock, flags);
++      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);
++      if (l2up(l2, DL_ESTABLISH | INDICATION, 0, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++l2_send_UA(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *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)
++{
++      layer2_t *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)
++{
++      layer2_t        *l2 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      int             est = 0;
++      u_long          flags;
++
++      send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
++
++      l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'F');
++
++      if (l2->vs != l2->va) {
++              discard_queue(&l2->i_queue);
++              est = 1;
++      }
++
++      clear_exception(l2);
++      spin_lock_irqsave(&l2->lock, flags);
++      l2->vs = 0;
++      l2->va = 0;
++      l2->vr = 0;
++      l2->sow = 0;
++      spin_unlock_irqrestore(&l2->lock, flags);
++      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 | INDICATION, 0, 0, NULL);
++
++      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)
++{
++      layer2_t        *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);
++      discard_queue(&l2->i_queue);
++      freewin(l2);
++      lapb_dl_release_l2l3(l2, INDICATION);
++}
++
++static void
++l2_connected(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t        *l2 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      int             pr=-1;
++      u_long          flags;
++
++      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 | CONFIRM;
++      } else if (l2->vs != l2->va) {
++              discard_queue(&l2->i_queue);
++              pr = DL_ESTABLISH | INDICATION;
++      }
++      stop_t200(l2, 5);
++      spin_lock_irqsave(&l2->lock, flags);
++      l2->vr = 0;
++      l2->vs = 0;
++      l2->va = 0;
++      l2->sow = 0;
++      spin_unlock_irqrestore(&l2->lock, flags);
++      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, 0, NULL);
++
++      if (skb_queue_len(&l2->i_queue) && cansend(l2))
++              mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
++}
++
++static void
++l2_released(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *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, CONFIRM);
++      mISDN_FsmChangeState(fi, ST_L2_4);
++}
++
++static void
++l2_reestablish(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *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)
++{
++      layer2_t *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))
++                      discard_queue(&l2->i_queue);
++              if (test_bit(FLG_LAPB, &l2->flag))
++                      l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL);
++              st5_dl_release_l2l3(l2);
++              mISDN_FsmChangeState(fi, ST_L2_4);
++      }
++}
++
++static void
++l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      if (get_PollFlagFree(l2, skb)) {
++              stop_t200(l2, 8);
++              lapb_dl_release_l2l3(l2, CONFIRM);
++              mISDN_FsmChangeState(fi, ST_L2_4);
++      }
++}
++
++void
++enquiry_cr(layer2_t *l2, u_char typ, u_char cr, u_char pf)
++{
++      struct sk_buff *skb;
++      u_char tmp[MAX_HEADER_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);
++      if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
++              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(layer2_t *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(layer2_t *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)
++{
++      layer2_t *l2 = fi->userdata;
++
++      l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'J');
++      establishlink(fi);
++      test_and_clear_bit(FLG_L3_INIT, &l2->flag);
++}
++
++static void
++invoke_retransmission(layer2_t *l2, unsigned int nr)
++{
++      u_int   p1;
++      u_long  flags;
++
++      spin_lock_irqsave(&l2->lock, flags);
++      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", __FUNCTION__, p1);
++                      l2->windowar[p1] = NULL;
++              }
++              spin_unlock_irqrestore(&l2->lock, flags);
++              mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
++      } else
++              spin_unlock_irqrestore(&l2->lock, flags);
++}
++
++static void
++l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *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 | INDICATION, (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)
++{
++      layer2_t *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)
++{
++      layer2_t *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)
++{
++      layer2_t *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)
++{
++      layer2_t        *l2 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      int             PollFlag, i;
++      u_int           ns, nr;
++      u_long          flags;
++
++      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 {
++              spin_lock_irqsave(&l2->lock, flags);
++              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);
++                      spin_unlock_irqrestore(&l2->lock, flags);
++                      if (PollFlag)
++                              enquiry_response(l2);
++                      else
++                              test_and_set_bit(FLG_ACK_PEND, &l2->flag);
++                      skb_pull(skb, l2headersize(l2, 0));
++                      if (l2up(l2, DL_DATA | INDICATION, mISDN_HEAD_DINFO(skb), skb))
++                              dev_kfree_skb(skb);
++              } else {
++                      /* n(s)!=v(r) */
++                      spin_unlock_irqrestore(&l2->lock, flags);
++                      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)
++{
++      layer2_t        *l2 = fi->userdata;
++      struct sk_buff  *skb = arg;
++      mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++
++      l2->tei = hh->dinfo;
++      dev_kfree_skb(skb);
++      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)
++{
++      layer2_t *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);
++              discard_queue(&l2->i_queue);
++              l2mgr(l2, MDL_ERROR | INDICATION, (void *) 'G');
++              if (test_bit(FLG_LAPB, &l2->flag))
++                      l2down_create(l2, PH_DEACTIVATE | REQUEST, 0, 0, NULL);
++              st5_dl_release_l2l3(l2);
++      } 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)
++{
++      layer2_t *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 | INDICATION, (void *) 'H');
++              lapb_dl_release_l2l3(l2, CONFIRM);
++      } 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)
++{
++      layer2_t *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)
++{
++      layer2_t *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 | INDICATION, (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)
++{
++      layer2_t *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)
++{
++      layer2_t        *l2 = fi->userdata;
++      struct sk_buff  *skb, *nskb, *oskb;
++      u_char          header[MAX_HEADER_LEN];
++      u_int           i, p1;
++      u_long          flags;
++
++      if (!cansend(l2))
++              return;
++
++      skb = skb_dequeue(&l2->i_queue);
++      if (!skb)
++              return;
++
++      spin_lock_irqsave(&l2->lock, flags);
++      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;
++      }
++      spin_unlock_irqrestore(&l2->lock, flags);
++
++      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 = alloc_skb(oskb->len + i, GFP_ATOMIC);
++              if (!nskb) {
++                      dev_kfree_skb(oskb);
++                      printk(KERN_WARNING "%s: no skb mem\n", __FUNCTION__);
++                      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, mISDN_HEAD_DINFO(skb), 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)
++{
++      layer2_t *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)
++{
++      layer2_t *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 | INDICATION, (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)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->ui_queue);
++      l2->tei = -1;
++      mISDN_FsmChangeState(fi, ST_L2_1);
++      dev_kfree_skb(skb);
++}
++
++static void
++l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->ui_queue);
++      l2->tei = -1;
++      skb_trim(skb, 0);
++      if (l2up(l2, DL_RELEASE | INDICATION, 0, skb))
++              dev_kfree_skb(skb);
++      mISDN_FsmChangeState(fi, ST_L2_1);
++}
++
++static void
++l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->i_queue);
++      discard_queue(&l2->ui_queue);
++      freewin(l2);
++      l2->tei = -1;
++      stop_t200(l2, 17);
++      st5_dl_release_l2l3(l2);
++      mISDN_FsmChangeState(fi, ST_L2_1);
++      dev_kfree_skb(skb);
++}
++
++static void
++l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->ui_queue);
++      l2->tei = -1;
++      stop_t200(l2, 18);
++      if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb))
++              dev_kfree_skb(skb);
++      mISDN_FsmChangeState(fi, ST_L2_1);
++}
++
++static void
++l2_tei_remove(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->i_queue);
++      discard_queue(&l2->ui_queue);
++      freewin(l2);
++      l2->tei = -1;
++      stop_t200(l2, 17);
++      mISDN_FsmDelTimer(&l2->t203, 19);
++      if (l2up(l2, DL_RELEASE | INDICATION, 0, skb))
++              dev_kfree_skb(skb);
++      mISDN_FsmChangeState(fi, ST_L2_1);
++}
++
++static void
++l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++      
++      discard_queue(&l2->i_queue);
++      discard_queue(&l2->ui_queue);
++      if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
++              if (!l2up(l2, DL_RELEASE | INDICATION, 0, skb))
++                      return;
++      dev_kfree_skb(skb);
++}
++
++static void
++l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->i_queue);
++      discard_queue(&l2->ui_queue);
++      freewin(l2);
++      stop_t200(l2, 19);
++      st5_dl_release_l2l3(l2);
++      mISDN_FsmChangeState(fi, ST_L2_4);
++      dev_kfree_skb(skb);
++}
++
++static void
++l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->ui_queue);
++      stop_t200(l2, 20);
++      if (l2up(l2, DL_RELEASE | CONFIRM, 0, skb))
++              dev_kfree_skb(skb);
++      mISDN_FsmChangeState(fi, ST_L2_4);
++}
++
++static void
++l2_persistant_da(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++      struct sk_buff *skb = arg;
++
++      discard_queue(&l2->i_queue);
++      discard_queue(&l2->ui_queue);
++      freewin(l2);
++      stop_t200(l2, 19);
++      mISDN_FsmDelTimer(&l2->t203, 19);
++      if (l2up(l2, DL_RELEASE | INDICATION, 0, skb))
++              dev_kfree_skb(skb);
++      mISDN_FsmChangeState(fi, ST_L2_4);
++}
++
++static void
++l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *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)
++{
++      layer2_t *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)
++{
++      layer2_t *l2 = fi->userdata;
++
++      l2mgr(l2, MDL_ERROR | INDICATION, arg);
++}
++
++static void
++l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
++{
++      layer2_t *l2 = fi->userdata;
++
++      l2mgr(l2, MDL_ERROR | INDICATION, 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(layer2_t *l2, mISDN_head_t *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)) {
++              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) && (psapi != TEI_SAPI))
++                      return(ret);
++              if (ptei == GROUP_TEI) {
++                      if (psapi == TEI_SAPI) {
++                              hh->prim = MDL_UNITDATA | INDICATION;
++                              return(l2_tei(l2->tm, skb));
++                      }
++              } else if ((ptei != l2->tei) || (psapi == TEI_SAPI)) {
++                      return(ret);
++              }
++      } else
++              datap += l;
++      if (!(*datap & 1)) {    /* I-Frame */
++              if(!(c = iframe_error(l2, skb)))
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb);
++      } else if (IsSFrame(datap, l2)) {       /* S-Frame */
++              if(!(c = super_error(l2, skb)))
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb);
++      } else if (IsUI(datap)) {
++              if(!(c = UI_error(l2, skb)))
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb);
++      } else if (IsSABME(datap, l2)) {
++              if(!(c = unnum_error(l2, skb, CMD)))
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb);
++      } else if (IsUA(datap)) {
++              if(!(c = unnum_error(l2, skb, RSP)))
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb);
++      } else if (IsDISC(datap)) {
++              if(!(c = unnum_error(l2, skb, CMD)))
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb);
++      } else if (IsDM(datap)) {
++              if(!(c = unnum_error(l2, skb, RSP)))
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb);
++      } else if (IsFRMR(datap)) {
++              if(!(c = FRMR_error(l2, skb)))
++                      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
++l2from_down(mISDNif_t *hif, struct sk_buff *askb)
++{
++      layer2_t        *l2;
++      int             ret = -EINVAL;
++      struct sk_buff  *cskb = askb;
++      mISDNif_t       *next;
++      mISDN_head_t    *hh, sh;
++
++      if (!hif || !askb)
++              return(-EINVAL);
++      l2 = hif->fdata;
++      hh = mISDN_HEAD_P(askb);
++      next = hif->clone;
++//    printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim);
++      if (!l2) {
++              if (next && next->func)
++                      ret = next->func(next, askb);
++              return(ret);
++      }
++      if (hh->prim == (PH_DATA | CONFIRM))
++              return(ph_data_confirm(hif, hh, askb));
++      if (hh->prim == (MDL_FINDTEI | REQUEST)) {
++              ret = -ESRCH;
++              if (test_bit(FLG_LAPD, &l2->flag)) {
++                      if (l2->tei == hh->dinfo) {
++                              void *p = ((u_char *)askb->data);
++                              teimgr_t **tp = p;
++                              if (tp) {
++                                      *tp = l2->tm;
++                                      dev_kfree_skb(askb);
++                                      return(0);
++                              }
++                      }
++              }
++              if (next && next->func)
++                      ret = next->func(next, askb);
++              return(ret);
++      }
++      if (next) {
++              if (next->func) {
++                      if (!(cskb = skb_clone(askb, GFP_ATOMIC)))
++                              return(next->func(next, askb));
++                      else
++                              sh = *hh;
++              }
++      }
++      switch (hh->prim) {
++              case (PH_DATA_IND):
++                      ret = ph_data_indication(l2, hh, cskb);
++                      break;
++              case (PH_CONTROL | INDICATION):
++                      if (hh->dinfo == HW_D_BLOCKED)
++                              test_and_set_bit(FLG_DCHAN_BUSY, &l2->flag);
++                      else if (hh->dinfo == HW_D_NOBLOCKED)
++                              test_and_clear_bit(FLG_DCHAN_BUSY, &l2->flag);
++                      else
++                              break;
++                      break;
++              case (PH_ACTIVATE | CONFIRM):
++              case (PH_ACTIVATE | INDICATION):
++                      test_and_set_bit(FLG_L1_ACTIV, &l2->flag);
++                      if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
++                              ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_ESTABLISH_REQ, cskb);
++                      break;
++              case (PH_DEACTIVATE | INDICATION):
++              case (PH_DEACTIVATE | CONFIRM):
++                      test_and_clear_bit(FLG_L1_ACTIV, &l2->flag);
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, cskb);
++                      if (ret)
++                              dev_kfree_skb(cskb);
++                      ret = 0;
++                      break;
++              default:
++                      if (l2->debug)
++                              l2m_debug(&l2->l2m, "l2 unknown pr %x", hh->prim);
++                      ret = -EINVAL;
++                      break;
++      }
++      if (ret) {
++              dev_kfree_skb(cskb);
++              ret = 0;
++      }
++      if (next && next->func) {
++              *hh = sh;
++              ret = next->func(next, askb);
++      }
++      return(ret);
++}
++
++static int
++l2from_up(mISDNif_t *hif, struct sk_buff *skb) {
++      layer2_t        *l2;
++      mISDN_head_t    *hh;
++      int             ret = -EINVAL;
++
++      if (!hif || !skb)
++              return(ret);
++      l2 = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++//    printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim);
++      if (!l2)
++              return(ret);
++      switch (hh->prim) {
++              case (DL_DATA | REQUEST):
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb);
++                      break;
++              case (DL_UNITDATA | REQUEST):
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb);
++                      break;
++              case (DL_ESTABLISH | REQUEST):
++                      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 | REQUEST, 0, skb);
++                      }
++                      break;
++              case (DL_RELEASE | REQUEST):
++                      if (test_bit(FLG_LAPB, &l2->flag))
++                              l2down_create(l2, PH_DEACTIVATE | REQUEST,
++                                      0, 0, NULL);
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ, skb);
++                      break;
++              case (MDL_ASSIGN | REQUEST):
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, skb);
++                      break;
++              case (MDL_REMOVE | REQUEST):
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, skb);
++                      break;
++              case (MDL_ERROR | RESPONSE):
++                      ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb);
++              case (MDL_STATUS | REQUEST):
++                      l2up_create(l2, MDL_STATUS | CONFIRM, hh->dinfo, 1,
++                              (void *)l2->tei);
++                      break;
++              default:
++                      if (l2->debug)
++                              l2m_debug(&l2->l2m, "l2 unknown pr %04x", hh->prim);
++      }
++      return(ret);
++}
++
++int
++tei_l2(layer2_t *l2, struct sk_buff *skb)
++{
++      mISDN_head_t    *hh;
++      int             ret = -EINVAL;
++
++      if (!l2 || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      if (l2->debug)
++              printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim);
++      switch(hh->prim) {
++          case (MDL_UNITDATA | REQUEST):
++              ret = l2down(l2, PH_DATA_REQ, l2_newid(l2), skb);
++              break;
++          case (MDL_ASSIGN | REQUEST):
++              ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, skb);
++              break;
++          case (MDL_REMOVE | REQUEST):
++              ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, skb);
++              break;
++          case (MDL_ERROR | RESPONSE):
++              ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, skb);
++              break;
++          case (MDL_FINDTEI | REQUEST):
++              ret = l2down_skb(l2, skb);
++              break;
++      }
++      return(ret);
++}
++
++static void
++l2m_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      layer2_t *l2 = fi->userdata;
++      logdata_t log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = l2->inst.name;
++      l2->inst.obj->ctrl(&l2->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++static void
++release_l2(layer2_t *l2)
++{
++      mISDNinstance_t  *inst = &l2->inst;
++
++      mISDN_FsmDelTimer(&l2->t200, 21);
++      mISDN_FsmDelTimer(&l2->t203, 16);
++      discard_queue(&l2->i_queue);
++      discard_queue(&l2->ui_queue);
++      discard_queue(&l2->down_queue);
++      ReleaseWin(l2);
++      if (test_bit(FLG_LAPD, &l2->flag))
++              release_tei(l2->tm);
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      if (l2->cloneif) {
++              printk(KERN_DEBUG "%s: cloneif(%p) owner(%p) peer(%p)\n",
++                      __FUNCTION__, l2->cloneif, l2->cloneif->owner, l2->cloneif->peer);
++              if (l2->cloneif->predecessor)
++                      l2->cloneif->predecessor->clone = l2->cloneif->clone;
++              if (l2->cloneif->clone)
++                      l2->cloneif->clone->predecessor = l2->cloneif->predecessor;
++              if (l2->cloneif->owner &&
++                      (l2->cloneif->owner->up.clone == l2->cloneif))
++                      l2->cloneif->owner->up.clone = l2->cloneif->clone;
++              kfree(l2->cloneif);
++              l2->cloneif = NULL;
++      }
++      list_del(&l2->list);
++      isdnl2.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      if (l2->entity != MISDN_ENTITY_NONE)
++              isdnl2.ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l2->entity);
++      kfree(l2);
++}
++
++static int
++new_l2(mISDNstack_t *st, mISDN_pid_t *pid, layer2_t **newl2) {
++      layer2_t *nl2;
++      int err;
++      u_char *p;
++
++      if (!st || !pid)
++              return(-EINVAL);
++      if (!(nl2 = kmalloc(sizeof(layer2_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc layer2 failed\n");
++              return(-ENOMEM);
++      }
++      memset(nl2, 0, sizeof(layer2_t));
++      spin_lock_init(&nl2->lock);
++      nl2->debug = debug;
++      nl2->next_id = 1;
++      nl2->down_id = MISDN_ID_NONE;
++      mISDN_init_instance(&nl2->inst, &isdnl2, nl2);
++      nl2->inst.extentions = EXT_INST_CLONE;
++      memcpy(&nl2->inst.pid, pid, sizeof(mISDN_pid_t));
++      if (!mISDN_SetHandledPID(&isdnl2, &nl2->inst.pid)) {
++              int_error();
++              return(-ENOPROTOOPT);
++      }
++      switch(pid->protocol[2] & ~ISDN_PID_FEATURE_MASK) {
++          case ISDN_PID_L2_LAPD_NET:
++              sprintf(nl2->inst.name, "lapdn %x", st->id);
++              test_and_set_bit(FLG_LAPD, &nl2->flag);
++              test_and_set_bit(FLG_LAPD_NET, &nl2->flag);
++              test_and_set_bit(FLG_FIXED_TEI, &nl2->flag);
++              test_and_set_bit(FLG_MOD128, &nl2->flag);
++              nl2->sapi = 0;
++              nl2->tei = 88;
++              nl2->maxlen = MAX_DFRAME_LEN;
++              nl2->window = 1;
++              nl2->T200 = 1000;
++              nl2->N200 = 3;
++              nl2->T203 = 10000;
++              if (create_teimgr(nl2)) {
++                      kfree(nl2);
++                      return(-EINVAL);
++              }
++              break;
++          case ISDN_PID_L2_LAPD:
++              sprintf(nl2->inst.name, "lapd %x", st->id);
++              test_and_set_bit(FLG_LAPD, &nl2->flag);
++              test_and_set_bit(FLG_MOD128, &nl2->flag);
++              test_and_set_bit(FLG_ORIG, &nl2->flag);
++              nl2->sapi = 0;
++              nl2->tei = -1;
++              if (pid->protocol[2] & ISDN_PID_L2_DF_PTP) {
++                      test_and_set_bit(FLG_PTP, &nl2->flag);
++                      test_and_set_bit(FLG_FIXED_TEI, &nl2->flag);
++                      nl2->tei = 0;
++              }
++              nl2->maxlen = MAX_DFRAME_LEN;
++              nl2->window = 1;
++              nl2->T200 = 1000;
++              nl2->N200 = 3;
++              nl2->T203 = 10000;
++              if (create_teimgr(nl2)) {
++                      kfree(nl2);
++                      return(-EINVAL);
++              }
++              break;
++          case ISDN_PID_L2_B_X75SLP:
++              test_and_set_bit(FLG_LAPB, &nl2->flag);
++              sprintf(nl2->inst.name, "lapb %x", st->id);
++              nl2->window = 7;
++              nl2->maxlen = MAX_DATA_SIZE;
++              nl2->T200 = 1000;
++              nl2->N200 = 4;
++              nl2->T203 = 5000;
++              nl2->addr.A = 3;
++              nl2->addr.B = 1;
++              if (nl2->inst.pid.global == 1)
++                      test_and_set_bit(FLG_ORIG, &nl2->flag);
++              if ((p=pid->param[2])) {
++                      if (*p>=4) {
++                              p++;
++                              nl2->addr.A = *p++;
++                              nl2->addr.B = *p++;
++                              if (*p++ == 128)
++                                      test_and_set_bit(FLG_MOD128, &nl2->flag);
++                              nl2->window = *p++;
++                              if (nl2->window > 7)
++                                      nl2->window = 7;
++                      }
++              }
++              break;
++          default:
++              printk(KERN_ERR "layer2 create failed prt %x\n",
++                      pid->protocol[2]);
++              kfree(nl2);
++              return(-ENOPROTOOPT);
++      }
++      skb_queue_head_init(&nl2->i_queue);
++      skb_queue_head_init(&nl2->ui_queue);
++      skb_queue_head_init(&nl2->down_queue);
++      skb_queue_head_init(&nl2->tmp_queue);
++      InitWin(nl2);
++      nl2->l2m.fsm = &l2fsm;
++      if (test_bit(FLG_LAPB, &nl2->flag) ||
++              test_bit(FLG_PTP, &nl2->flag) ||
++              test_bit(FLG_LAPD_NET, &nl2->flag))
++              nl2->l2m.state = ST_L2_4;
++      else
++              nl2->l2m.state = ST_L2_1;
++      nl2->l2m.debug = debug;
++      nl2->l2m.userdata = nl2;
++      nl2->l2m.userint = 0;
++      nl2->l2m.printdebug = l2m_debug;
++
++      mISDN_FsmInitTimer(&nl2->l2m, &nl2->t200);
++      mISDN_FsmInitTimer(&nl2->l2m, &nl2->t203);
++      list_add_tail(&nl2->list, &isdnl2.ilist);
++      err = isdnl2.ctrl(&nl2->inst, MGR_NEWENTITY | REQUEST, NULL);
++      if (err) {
++              printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n",
++                      __FUNCTION__, err);
++      }
++      err = isdnl2.ctrl(st, MGR_REGLAYER | INDICATION, &nl2->inst);
++      if (err) {
++              mISDN_FsmDelTimer(&nl2->t200, 0);
++              mISDN_FsmDelTimer(&nl2->t203, 0);
++              list_del(&nl2->list);
++              kfree(nl2);
++              nl2 = NULL;
++      } else {
++              mISDN_stPara_t  stp;
++
++              if (st->para.maxdatalen)
++                      nl2->maxlen = st->para.maxdatalen;
++              stp.maxdatalen = 0;
++              stp.up_headerlen = 0;
++              stp.down_headerlen = l2headersize(nl2, 0);
++              isdnl2.ctrl(st, MGR_ADDSTPARA | REQUEST, &stp);
++      }
++      if (newl2)
++              *newl2 = nl2;
++      return(err);
++}
++
++static int
++clone_l2(layer2_t *l2, mISDNinstance_t **new_ip) {
++      int err;
++      layer2_t        *nl2;
++      mISDNif_t       *nif;
++      mISDNstack_t    *st;
++
++      if (!l2)
++              return(-EINVAL);
++      if (!new_ip)
++              return(-EINVAL);
++      st = (mISDNstack_t *)*new_ip;
++      if (!st)
++              return(-EINVAL);
++      if (!l2->inst.down.peer)
++              return(-EINVAL);
++      if (!(nif = kmalloc(sizeof(mISDNif_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "clone l2 no if mem\n");
++              return(-ENOMEM);
++      }
++      err = new_l2(st, &l2->inst.pid, &nl2);
++      if (err) {
++              kfree(nif);
++              printk(KERN_ERR "clone l2 failed err(%d)\n", err);
++              return(err);
++      }
++      memset(nif, 0, sizeof(mISDNif_t));
++      nl2->cloneif = nif;
++      nif->func = l2from_down;
++      nif->fdata = nl2;
++      nif->owner = l2->inst.down.peer;
++      nif->peer = &nl2->inst;
++      nif->stat = IF_DOWN;
++      nif->predecessor = &nif->owner->up;
++      while(nif->predecessor->clone)
++              nif->predecessor = nif->predecessor->clone;
++      nif->predecessor->clone = nif;
++      nl2->inst.down.owner = &nl2->inst;
++      nl2->inst.down.peer = &l2->inst;
++      nl2->inst.down.func = l2_chain_down;
++      nl2->inst.down.fdata = l2;
++      nl2->inst.down.stat = IF_UP;
++      *new_ip = &nl2->inst;
++      return(err);
++}
++
++static int
++l2_status(layer2_t *l2, status_info_l2_t *si)
++{
++
++      if (!si)
++              return(-EINVAL);
++      memset(si, 0, sizeof(status_info_l2_t));
++      si->len = sizeof(status_info_l2_t) - 2*sizeof(int);
++      si->typ = STATUS_INFO_L2;
++      si->protocol = l2->inst.pid.protocol[2];
++      si->state = l2->l2m.state;
++      si->sapi = l2->sapi;
++      si->tei = l2->tei;
++      si->addr = l2->addr;
++      si->maxlen = l2->maxlen;
++      si->flag = l2->flag;
++      si->vs = l2->vs;
++      si->va = l2->va;
++      si->vr = l2->vr;
++      si->rc = l2->rc;
++      si->window = l2->window;
++      si->sow = l2->sow;
++      si->T200 = l2->T200;
++      si->N200 = l2->N200;
++      si->T203 = l2->T203;
++      si->len_i_queue = skb_queue_len(&l2->i_queue);
++      si->len_ui_queue = skb_queue_len(&l2->ui_queue);
++      si->len_d_queue = skb_queue_len(&l2->down_queue);
++      si->debug = l2->debug;
++      if (l2->tm) {
++              si->tei_state = l2->tm->tei_m.state;
++              si->tei_ri = l2->tm->ri;
++              si->T202 = l2->tm->T202;
++              si->N202 = l2->tm->N202;
++              si->tei_debug = l2->tm->debug;
++      }
++      return(0);
++}
++
++static char MName[] = "ISDNL2";
++ 
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++#endif
++
++static int
++l2_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      layer2_t        *l2l;
++      int             err = -EINVAL;
++
++      if (debug & 0x1000)
++              printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n", __FUNCTION__,
++                      data, prim, arg);
++      if (!data)
++              return(err);
++      list_for_each_entry(l2l, &isdnl2.ilist, list) {
++              if (&l2l->inst == inst) {
++                      err = 0;
++                      break;
++              }
++      }
++      if (prim == (MGR_NEWLAYER | REQUEST))
++              return(new_l2(data, arg, NULL));
++      if (err) {
++              if (debug & 0x1)
++                      printk(KERN_WARNING "l2_manager prim(%x) l2 no instance\n", prim);
++              return(err);
++      }
++      switch(prim) {
++          case MGR_NEWENTITY | CONFIRM:
++              l2l->entity = (int)arg;
++              break;
++          case MGR_ADDSTPARA | INDICATION:
++              if (((mISDN_stPara_t *)arg)->maxdatalen)
++                      l2l->maxlen = ((mISDN_stPara_t *)arg)->maxdatalen;
++          case MGR_CLRSTPARA | INDICATION:
++              break;
++          case MGR_CLONELAYER | REQUEST:
++              return(clone_l2(l2l, arg));
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              return(mISDN_SetIF(inst, arg, prim, l2from_up, l2from_down, l2l));
++          case MGR_ADDIF | REQUEST:
++              if (arg) {
++                      mISDNif_t *hif = arg;
++                      if (hif->stat & IF_UP) {
++                              hif->fdata = l2l;
++                              hif->func = l2_chain_down;
++                      }
++              }
++              break;
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_RELEASE | INDICATION:
++          case MGR_UNREGLAYER | REQUEST:
++              release_l2(l2l);
++              break;
++          case MGR_STATUS | REQUEST:
++              return(l2_status(l2l, arg));
++          default:
++              if (debug & 0x1)
++                      printk(KERN_WARNING "l2_manager prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++int Isdnl2_Init(void)
++{
++      int err;
++
++      printk(KERN_INFO "ISDN L2 driver version %s\n", mISDN_getrev(l2_revision));
++#ifdef MODULE
++      isdnl2.owner = THIS_MODULE;
++#endif
++      isdnl2.name = MName;
++      isdnl2.DPROTO.protocol[2] = ISDN_PID_L2_LAPD |
++              ISDN_PID_L2_LAPD_NET |
++              ISDN_PID_L2_DF_PTP;
++      isdnl2.BPROTO.protocol[2] = ISDN_PID_L2_B_X75SLP;
++      isdnl2.own_ctrl = l2_manager;
++      INIT_LIST_HEAD(&isdnl2.ilist);
++      l2fsm.state_count = L2_STATE_COUNT;
++      l2fsm.event_count = L2_EVENT_COUNT;
++      l2fsm.strEvent = strL2Event;
++      l2fsm.strState = strL2State;
++      mISDN_FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
++      TEIInit();
++      if ((err = mISDN_register(&isdnl2))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++              mISDN_FsmFree(&l2fsm);
++      }
++      return(err);
++}
++
++void Isdnl2_cleanup(void)
++{
++      int             err;
++      layer2_t        *l2, *nl2;
++
++      if ((err = mISDN_unregister(&isdnl2))) {
++              printk(KERN_ERR "Can't unregister ISDN layer 2 error(%d)\n", err);
++      }
++      if(!list_empty(&isdnl2.ilist)) {
++              printk(KERN_WARNING "mISDNl2 l2 list not empty\n");
++              list_for_each_entry_safe(l2, nl2, &isdnl2.ilist, list)
++                      release_l2(l2);
++      }
++      TEIFree();
++      mISDN_FsmFree(&l2fsm);
++}
++
++module_init(Isdnl2_Init);
++module_exit(Isdnl2_cleanup);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer2.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer2.h 2004-11-22 09:33:38.301721168 +0000
+@@ -0,0 +1,141 @@
++/* $Id$
++ *
++ * Layer 2 defines
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/mISDNif.h>
++#include <linux/skbuff.h>
++#include "fsm.h"
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++#define MAX_WINDOW    8
++
++typedef struct _teimgr {
++      int             ri;
++      struct FsmInst  tei_m;
++      struct FsmTimer t202;
++      int             T202, N202;
++      int             debug;
++      struct _layer2  *l2;
++} teimgr_t;
++
++typedef struct _laddr {
++      u_char  A;
++      u_char  B;
++} laddr_t;
++
++typedef struct _layer2 {
++      struct list_head        list;
++      int                     sapi;
++      int                     tei;
++      laddr_t                 addr;
++      u_int                   maxlen;
++      teimgr_t                *tm;
++      u_long                  flag;
++      u_int                   vs, va, vr;
++      int                     rc;
++      u_int                   window;
++      u_int                   sow;
++      int                     entity;
++      struct FsmInst          l2m;
++      struct FsmTimer         t200, t203;
++      int                     T200, N200, T203;
++      int                     debug;
++      mISDNinstance_t         inst;
++      mISDNif_t               *cloneif;
++      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;
++      spinlock_t              lock;
++} layer2_t;
++
++/* l2 status_info */
++typedef struct _status_info_l2 {
++      int     len;
++      int     typ;
++      int     protocol;
++      int     state;
++      int     sapi;
++      int     tei;
++      laddr_t addr;
++      u_int   maxlen;
++      u_long  flag;
++      u_int   vs;
++      u_int   va;
++      u_int   vr;
++      int     rc;
++      u_int   window;
++      u_int   sow;
++      int     T200;
++      int     N200;
++      int     T203;
++      int     len_i_queue;
++      int     len_ui_queue;
++      int     len_d_queue;
++      int     debug;
++      int     tei_state;
++      int     tei_ri;
++      int     T202;
++      int     N202;
++      int     tei_debug;
++} status_info_l2_t;
++
++/* from mISDN_l2.c */
++extern int tei_l2(layer2_t *l2, struct sk_buff *skb);
++
++/* from tei.c */
++extern int l2_tei(teimgr_t *tm, struct sk_buff *skb);
++extern int create_teimgr(layer2_t *l2);
++extern void release_tei(teimgr_t *tm);
++extern int TEIInit(void);
++extern void TEIFree(void);
++
++#define GROUP_TEI     127
++#define TEI_SAPI      63
++#define CTRL_SAPI     0
++
++#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_BUSY   17
++#define FLG_LAPD_NET  18
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.c 2004-11-22 09:33:38.311719648 +0000
+@@ -0,0 +1,607 @@
++/* $Id$
++ *
++ * Author       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    Jan den Ouden
++ *              Fritz Elfert
++ *
++ */
++#include "layer3.h"
++#include "helper.h"
++
++const char *l3_revision = "$Revision$";
++
++static
++struct Fsm l3fsm = {NULL, 0, 0, NULL, NULL};
++
++enum {
++      ST_L3_LC_REL,
++      ST_L3_LC_ESTAB_WAIT,
++      ST_L3_LC_REL_DELAY, 
++      ST_L3_LC_REL_WAIT,
++      ST_L3_LC_ESTAB,
++};
++
++#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1)
++
++static char *strL3State[] =
++{
++      "ST_L3_LC_REL",
++      "ST_L3_LC_ESTAB_WAIT",
++      "ST_L3_LC_REL_DELAY",
++      "ST_L3_LC_REL_WAIT",
++      "ST_L3_LC_ESTAB",
++};
++
++enum {
++      EV_ESTABLISH_REQ,
++      EV_ESTABLISH_IND,
++      EV_ESTABLISH_CNF,
++      EV_RELEASE_REQ,
++      EV_RELEASE_CNF,
++      EV_RELEASE_IND,
++      EV_TIMEOUT,
++};
++
++#define L3_EVENT_COUNT (EV_TIMEOUT+1)
++
++static char *strL3Event[] =
++{
++      "EV_ESTABLISH_REQ",
++      "EV_ESTABLISH_IND",
++      "EV_ESTABLISH_CNF",
++      "EV_RELEASE_REQ",
++      "EV_RELEASE_CNF",
++      "EV_RELEASE_IND",
++      "EV_TIMEOUT",
++};
++
++static void
++l3m_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      layer3_t *l3 = fi->userdata;
++      logdata_t log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = l3->inst.name;
++      l3->inst.obj->ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++void
++l3_debug(layer3_t *l3, char *fmt, ...)
++{
++      logdata_t log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = l3->inst.name;
++      l3->inst.obj->ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++static int
++l3_newid(layer3_t *l3)
++{
++      u_long  flags;
++      int     id;
++
++      spin_lock_irqsave(&l3->lock, flags);
++      id = l3->next_id++;
++      if (id == 0x7fff)
++              l3->next_id = 1;
++      spin_unlock_irqrestore(&l3->lock, flags);
++      id |= (l3->entity << 16);
++      return(id);
++}
++
++u_char *
++findie(u_char * p, int size, u_char ie, int wanted_set)
++{
++      int l, codeset, maincodeset;
++      u_char *pend = p + size;
++
++      /* skip protocol discriminator, callref and message type */
++      p++;
++      l = (*p++) & 0xf;
++      p += l;
++      p++;
++      codeset = 0;
++      maincodeset = 0;
++      /* while there are bytes left... */
++      while (p < pend) {
++              if ((*p & 0xf0) == 0x90) {
++                      codeset = *p & 0x07;
++                      if (!(*p & 0x08))
++                              maincodeset = codeset;
++              }
++              if (codeset == wanted_set) {
++                      if (*p == ie) {
++                              /* improved length check (Werner Cornelius) */
++                              if (!(*p & 0x80)) {
++                                      if ((pend - p) < 2)
++                                              return(NULL);
++                                      if (*(p+1) > (pend - (p+2)))
++                                              return(NULL);
++                                      p++; /* points to len */
++                              }
++                              return (p);
++                      } else if ((*p > ie) && !(*p & 0x80))
++                              return (NULL);
++              }
++              if (!(*p & 0x80)) {
++                      p++;
++                      l = *p;
++                      p += l;
++                      codeset = maincodeset;
++              }
++              p++;
++      }
++      return (NULL);
++}
++
++int
++getcallref(u_char * p)
++{
++      int l, cr = 0;
++
++      p++;                    /* prot discr */
++      if (*p & 0xfe)          /* wrong callref BRI only 1 octet*/
++              return(-2);
++      l = 0xf & *p++;         /* callref length */
++      if (!l)                 /* dummy CallRef */
++              return(-1);
++      cr = *p++;
++      return (cr);
++}
++
++int
++newcallref(layer3_t *l3)
++{
++      int max = 127;
++
++      if (test_bit(FLG_CRLEN2, &l3->Flag))
++              max = 32767;
++
++      if (l3->OrigCallRef >= max)
++              l3->OrigCallRef = 1;
++      else
++              l3->OrigCallRef++;
++      return (l3->OrigCallRef);
++}
++
++void
++newl3state(l3_process_t *pc, int state)
++{
++      if (pc->l3->debug & L3_DEB_STATE)
++              l3m_debug(&pc->l3->l3m, "newstate cr %d %d --> %d", 
++                       pc->callref & 0x7F,
++                       pc->state, state);
++      pc->state = state;
++}
++
++static void
++L3ExpireTimer(L3Timer_t *t)
++{
++      t->pc->l3->p_mgr(t->pc, t->event, NULL);
++}
++
++void
++L3InitTimer(l3_process_t *pc, L3Timer_t *t)
++{
++      t->pc = pc;
++      t->tl.function = (void *) L3ExpireTimer;
++      t->tl.data = (long) t;
++      init_timer(&t->tl);
++}
++
++void
++L3DelTimer(L3Timer_t *t)
++{
++      del_timer(&t->tl);
++}
++
++int
++L3AddTimer(L3Timer_t *t,
++         int millisec, int event)
++{
++      if (timer_pending(&t->tl)) {
++              printk(KERN_WARNING "L3AddTimer: timer already active!\n");
++              return -1;
++      }
++      init_timer(&t->tl);
++      t->event = event;
++      t->tl.expires = jiffies + (millisec * HZ) / 1000;
++      add_timer(&t->tl);
++      return 0;
++}
++
++void
++StopAllL3Timer(l3_process_t *pc)
++{
++      L3DelTimer(&pc->timer);
++      if (pc->t303skb) {
++              dev_kfree_skb(pc->t303skb);
++              pc->t303skb = NULL;
++      }
++}
++
++/*
++static void
++no_l3_proto(struct PStack *st, int pr, void *arg)
++{
++      struct sk_buff *skb = arg;
++
++      mISDN_putstatus(st->l1.hardware, "L3", "no D protocol");
++      if (skb) {
++              dev_kfree_skb(skb);
++      }
++}
++
++static int
++no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic)
++{
++      printk(KERN_WARNING "mISDN: no specific protocol handler for proto %lu\n",ic->arg & 0xFF);
++      return(-1);
++}
++*/
++
++l3_process_t
++*getl3proc(layer3_t *l3, int cr)
++{
++      l3_process_t *p;
++      
++      list_for_each_entry(p, &l3->plist, list)
++              if (p->callref == cr)
++                      return (p);
++      return (NULL);
++}
++
++l3_process_t
++*getl3proc4id(layer3_t *l3, u_int id)
++{
++      l3_process_t *p;
++
++      list_for_each_entry(p, &l3->plist, list)
++              if (p->id == id)
++                      return (p);
++      return (NULL);
++}
++
++l3_process_t
++*new_l3_process(layer3_t *l3, int cr, int n303, u_int id)
++{
++      l3_process_t    *p = NULL;
++      u_long          flags;
++
++      if (id == MISDN_ID_ANY) {
++              if (l3->entity == MISDN_ENTITY_NONE) {
++                      printk(KERN_WARNING "%s: no entity allocated for l3(%x)\n",
++                              __FUNCTION__, l3->id);
++                      return (NULL);
++              }
++              spin_lock_irqsave(&l3->lock, flags);
++              if (l3->pid_cnt == 0x7FFF)
++                      l3->pid_cnt = 0;
++              while(l3->pid_cnt <= 0x7FFF) {
++                      l3->pid_cnt++;
++                      id = l3->pid_cnt | (l3->entity << 16);
++                      p = getl3proc4id(l3, id);
++                      if (!p)
++                              break;
++              }
++              spin_unlock_irqrestore(&l3->lock, flags);
++              if (p) {
++                      printk(KERN_WARNING "%s: no free process_id for l3(%x) entity(%x)\n",
++                              __FUNCTION__, l3->id, l3->entity);
++                      return (NULL);
++              }
++      } else {
++              /* id from other entity */
++              p = getl3proc4id(l3, id);
++              if (p) {
++                      printk(KERN_WARNING "%s: process_id(%x) allready in use in l3(%x)\n",
++                              __FUNCTION__, id, l3->id);
++                      return (NULL);
++              }
++      }
++      if (!(p = kmalloc(sizeof(l3_process_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "mISDN can't get memory for cr %d\n", cr);
++              return (NULL);
++      }
++      memset(p, 0, sizeof(l3_process_t));
++      p->l3 = l3;
++      p->id = id;
++      p->callref = cr;
++      p->n303 = n303;
++      L3InitTimer(p, &p->timer);
++      list_add_tail(&p->list, &l3->plist);
++      return (p);
++};
++
++void
++release_l3_process(l3_process_t *p)
++{
++      layer3_t *l3;
++
++      if (!p)
++              return;
++      l3 = p->l3;
++      mISDN_l3up(p, CC_RELEASE_CR | INDICATION, NULL);
++      list_del(&p->list);
++      StopAllL3Timer(p);
++      kfree(p);
++      if (list_empty(&l3->plist) && !test_bit(FLG_PTP, &l3->Flag)) {
++              if (l3->debug)
++                      l3_debug(l3, "release_l3_process: last process");
++              if (!skb_queue_len(&l3->squeue)) {
++                      if (l3->debug)
++                              l3_debug(l3, "release_l3_process: release link");
++                      mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL);
++              } else {
++                      if (l3->debug)
++                              l3_debug(l3, "release_l3_process: not release link");
++              }
++      }
++};
++
++static void
++l3ml3p(layer3_t *l3, int pr)
++{
++      l3_process_t *p, *np;
++
++      list_for_each_entry_safe(p, np, &l3->plist, list) 
++              l3->p_mgr(p, pr, NULL);
++}
++
++int
++mISDN_l3up(l3_process_t *l3p, u_int prim, struct sk_buff *skb)
++{
++      layer3_t *l3;
++      int err = -EINVAL;
++
++      if (!l3p)
++              return(-EINVAL);
++      l3 = l3p->l3;
++      if (!skb)
++              err = if_link(&l3->inst.up, prim, l3p->id, 0, NULL, 0);
++      else
++              err = if_newhead(&l3->inst.up, prim, l3p->id, skb);
++      return(err);
++}
++
++static int
++l3down(layer3_t *l3, u_int prim, int dinfo, struct sk_buff *skb) {
++      int err = -EINVAL;
++
++      if (!skb)
++              err = if_link(&l3->inst.down, prim, dinfo, 0, NULL, 0);
++      else
++              err = if_newhead(&l3->inst.down, prim, dinfo, skb);
++      return(err);
++}
++
++#define DREL_TIMER_VALUE 40000
++
++static void
++lc_activate(struct FsmInst *fi, int event, void *arg)
++{
++      layer3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
++      l3down(l3, DL_ESTABLISH | REQUEST, 0, NULL);
++}
++
++static void
++lc_connect(struct FsmInst *fi, int event, void *arg)
++{
++      layer3_t *l3 = fi->userdata;
++      struct sk_buff *skb;
++      int dequeued = 0;
++
++      mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB);
++      while ((skb = skb_dequeue(&l3->squeue))) {
++              if (l3down(l3, DL_DATA | REQUEST, l3_newid(l3), skb))
++                      dev_kfree_skb(skb);
++              dequeued++;
++      }
++      if (list_empty(&l3->plist) &&  dequeued) {
++              if (l3->debug)
++                      l3m_debug(fi, "lc_connect: release link");
++              mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL);
++      } else
++              l3ml3p(l3, DL_ESTABLISH | INDICATION);
++}
++
++static void
++lc_connected(struct FsmInst *fi, int event, void *arg)
++{
++      layer3_t *l3 = fi->userdata;
++      struct sk_buff *skb;
++      int dequeued = 0;
++
++      mISDN_FsmDelTimer(&l3->l3m_timer, 51);
++      mISDN_FsmChangeState(fi, ST_L3_LC_ESTAB);
++      while ((skb = skb_dequeue(&l3->squeue))) {
++              if (l3down(l3, DL_DATA | REQUEST, l3_newid(l3), skb))
++                      dev_kfree_skb(skb);
++              dequeued++;
++      }
++      if (list_empty(&l3->plist) &&  dequeued) {
++              if (l3->debug)
++                      l3m_debug(fi, "lc_connected: release link");
++              mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL);
++      } else
++              l3ml3p(l3, DL_ESTABLISH | CONFIRM);
++}
++
++static void
++lc_start_delay(struct FsmInst *fi, int event, void *arg)
++{
++      layer3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L3_LC_REL_DELAY);
++      mISDN_FsmAddTimer(&l3->l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
++}
++
++static void
++lc_release_req(struct FsmInst *fi, int event, void *arg)
++{
++      layer3_t *l3 = fi->userdata;
++
++      if (test_bit(FLG_L2BLOCK, &l3->Flag)) {
++              if (l3->debug)
++                      l3m_debug(fi, "lc_release_req: l2 blocked");
++              /* restart release timer */
++              mISDN_FsmAddTimer(&l3->l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51);
++      } else {
++              mISDN_FsmChangeState(fi, ST_L3_LC_REL_WAIT);
++              l3down(l3, DL_RELEASE | REQUEST, 0, NULL);
++      }
++}
++
++static void
++lc_release_ind(struct FsmInst *fi, int event, void *arg)
++{
++      layer3_t *l3 = fi->userdata;
++
++      mISDN_FsmDelTimer(&l3->l3m_timer, 52);
++      mISDN_FsmChangeState(fi, ST_L3_LC_REL);
++      discard_queue(&l3->squeue);
++      l3ml3p(l3, DL_RELEASE | INDICATION);
++}
++
++static void
++lc_release_cnf(struct FsmInst *fi, int event, void *arg)
++{
++      layer3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_L3_LC_REL);
++      discard_queue(&l3->squeue);
++      l3ml3p(l3, DL_RELEASE | CONFIRM);
++}
++
++
++/* *INDENT-OFF* */
++static struct FsmNode L3FnList[] =
++{
++      {ST_L3_LC_REL,          EV_ESTABLISH_REQ,       lc_activate},
++      {ST_L3_LC_REL,          EV_ESTABLISH_IND,       lc_connect},
++      {ST_L3_LC_REL,          EV_ESTABLISH_CNF,       lc_connect},
++      {ST_L3_LC_ESTAB_WAIT,   EV_ESTABLISH_CNF,       lc_connected},
++      {ST_L3_LC_ESTAB_WAIT,   EV_RELEASE_REQ,         lc_start_delay},
++      {ST_L3_LC_ESTAB_WAIT,   EV_RELEASE_IND,         lc_release_ind},
++      {ST_L3_LC_ESTAB,        EV_RELEASE_IND,         lc_release_ind},
++      {ST_L3_LC_ESTAB,        EV_RELEASE_REQ,         lc_start_delay},
++        {ST_L3_LC_REL_DELAY,    EV_RELEASE_IND,         lc_release_ind},
++        {ST_L3_LC_REL_DELAY,    EV_ESTABLISH_REQ,       lc_connected},
++        {ST_L3_LC_REL_DELAY,    EV_TIMEOUT,             lc_release_req},
++      {ST_L3_LC_REL_WAIT,     EV_RELEASE_CNF,         lc_release_cnf},
++      {ST_L3_LC_REL_WAIT,     EV_ESTABLISH_REQ,       lc_activate},
++};
++/* *INDENT-ON* */
++
++#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode))
++
++int
++l3_msg(layer3_t *l3, u_int pr, int dinfo, int len, void *arg)
++{
++      switch (pr) {
++              case (DL_DATA | REQUEST):
++                      if (l3->l3m.state == ST_L3_LC_ESTAB) {
++                              return(l3down(l3, pr, l3_newid(l3), arg));
++                      } else {
++                              struct sk_buff *skb = arg;
++
++//                            printk(KERN_DEBUG "%s: queue skb %p len(%d)\n",
++//                                    __FUNCTION__, skb, skb->len);
++                              skb_queue_tail(&l3->squeue, skb);
++                              mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_REQ, NULL); 
++                      }
++                      break;
++              case (DL_ESTABLISH | REQUEST):
++                      mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_REQ, NULL);
++                      break;
++              case (DL_ESTABLISH | CONFIRM):
++                      mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_CNF, NULL);
++                      break;
++              case (DL_ESTABLISH | INDICATION):
++                      mISDN_FsmEvent(&l3->l3m, EV_ESTABLISH_IND, NULL);
++                      break;
++              case (DL_RELEASE | INDICATION):
++                      mISDN_FsmEvent(&l3->l3m, EV_RELEASE_IND, NULL);
++                      break;
++              case (DL_RELEASE | CONFIRM):
++                      mISDN_FsmEvent(&l3->l3m, EV_RELEASE_CNF, NULL);
++                      break;
++              case (DL_RELEASE | REQUEST):
++                      mISDN_FsmEvent(&l3->l3m, EV_RELEASE_REQ, NULL);
++                      break;
++      }
++      return(0);
++}
++
++void
++init_l3(layer3_t *l3)
++{
++      INIT_LIST_HEAD(&l3->plist);
++      l3->global = NULL;
++      l3->dummy = NULL;
++      l3->entity = MISDN_ENTITY_NONE;
++      l3->next_id = 1;
++      spin_lock_init(&l3->lock);
++      skb_queue_head_init(&l3->squeue);
++      l3->l3m.fsm = &l3fsm;
++      l3->l3m.state = ST_L3_LC_REL;
++      l3->l3m.debug = l3->debug;
++      l3->l3m.userdata = l3;
++      l3->l3m.userint = 0;
++      l3->l3m.printdebug = l3m_debug;
++        mISDN_FsmInitTimer(&l3->l3m, &l3->l3m_timer);
++}
++
++
++void
++release_l3(layer3_t *l3)
++{
++      l3_process_t *p, *np;
++
++      if (l3->l3m.debug)
++              printk(KERN_DEBUG "release_l3(%p) plist(%s) global(%p) dummy(%p)\n",
++                      l3, list_empty(&l3->plist) ? "no" : "yes", l3->global, l3->dummy);
++      list_for_each_entry_safe(p, np, &l3->plist, list)
++              release_l3_process(p);
++      if (l3->global) {
++              StopAllL3Timer(l3->global);
++              kfree(l3->global);
++              l3->global = NULL;
++      }
++      if (l3->dummy) {
++              StopAllL3Timer(l3->dummy);
++              kfree(l3->dummy);
++              l3->dummy = NULL;
++      }
++      mISDN_FsmDelTimer(&l3->l3m_timer, 54);
++      discard_queue(&l3->squeue);
++}
++
++void
++mISDNl3New(void)
++{
++      l3fsm.state_count = L3_STATE_COUNT;
++      l3fsm.event_count = L3_EVENT_COUNT;
++      l3fsm.strEvent = strL3Event;
++      l3fsm.strState = strL3State;
++      mISDN_FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
++}
++
++void
++mISDNl3Free(void)
++{
++      mISDN_FsmFree(&l3fsm);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/layer3.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/layer3.h 2004-11-22 09:33:38.321718128 +0000
+@@ -0,0 +1,95 @@
++/* $Id$
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include <linux/mISDNif.h>
++#include <linux/skbuff.h>
++#include "fsm.h"
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#endif
++
++#define SBIT(state) (1<<state)
++#define ALL_STATES  0x03ffffff
++
++#define PROTO_DIS_EURO        0x08
++
++#define L3_DEB_WARN   0x01
++#define L3_DEB_PROTERR        0x02
++#define L3_DEB_STATE  0x04
++#define L3_DEB_CHARGE 0x08
++#define L3_DEB_CHECK  0x10
++#define L3_DEB_SI     0x20
++
++#define FLG_L2BLOCK   1
++#define FLG_PTP               2
++#define FLG_EXTCID    3
++#define FLG_CRLEN2    4
++
++typedef struct _L3Timer {
++      struct _l3_process      *pc;
++      struct timer_list       tl;
++      int                     event;
++} L3Timer_t;
++
++typedef struct _l3_process {
++      struct list_head        list;
++      struct _layer3          *l3;
++      int                     callref;
++      int                     state;
++      L3Timer_t               timer;
++      int                     n303;
++      struct sk_buff          *t303skb;
++      u_int                   id;
++      int                     bc;
++      int                     err;
++} l3_process_t;
++
++typedef struct _layer3 {
++      struct list_head        list;
++      struct FsmInst          l3m;
++      struct FsmTimer         l3m_timer;
++      int                     entity;
++      int                     pid_cnt;
++      int                     next_id;
++      struct list_head        plist;
++      l3_process_t            *global;
++      l3_process_t            *dummy;
++      int                     (*p_mgr)(l3_process_t *, u_int, void *);
++      int                     down_headerlen;
++      u_int                   id;
++      int                     debug;
++      u_long                  Flag;
++      mISDNinstance_t         inst;
++      struct sk_buff_head     squeue;
++      spinlock_t              lock;
++      int                     OrigCallRef;
++} layer3_t;
++
++struct stateentry {
++      int     state;
++      u_int   primitive;
++      void    (*rout) (l3_process_t *, u_char, void *);
++};
++
++extern int            l3_msg(layer3_t *, u_int, int, int, void *);
++extern void           newl3state(l3_process_t *, int);
++extern void           L3InitTimer(l3_process_t *, L3Timer_t *);
++extern void           L3DelTimer(L3Timer_t *);
++extern int            L3AddTimer(L3Timer_t *, int, int);
++extern void           StopAllL3Timer(l3_process_t *);
++extern void           release_l3_process(l3_process_t *);
++extern l3_process_t   *getl3proc(layer3_t *, int);
++extern l3_process_t   *getl3proc4id(layer3_t *, u_int);
++extern l3_process_t   *new_l3_process(layer3_t *, int, int, u_int);
++extern u_char         *findie(u_char *, int, u_char, int);
++extern int            mISDN_l3up(l3_process_t *, u_int, struct sk_buff *);
++extern int            getcallref(u_char * p);
++extern int            newcallref(layer3_t *);
++extern void           init_l3(layer3_t *);
++extern void           release_l3(layer3_t *);
++extern void           mISDNl3New(void);
++extern void           mISDNl3Free(void);
++extern void           l3_debug(layer3_t *, char *, ...);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/listen.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/listen.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/listen.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/listen.c 2004-11-22 09:33:38.331716608 +0000
+@@ -0,0 +1,207 @@
++/* $Id$
++ *
++ */
++
++#include "m_capi.h"
++#include "helper.h"
++#include "debug.h"
++
++#define listenDebug(listen, lev, fmt, args...) \
++        capidebug(lev, fmt, ## args)
++
++// --------------------------------------------------------------------
++// LISTEN state machine
++
++enum {
++      ST_LISTEN_L_0,
++      ST_LISTEN_L_0_1,
++      ST_LISTEN_L_1,
++      ST_LISTEN_L_1_1,
++}
++
++const ST_LISTEN_COUNT = ST_LISTEN_L_1_1 + 1;
++
++static char *str_st_listen[] = {
++      "ST_LISTEN_L_0",
++      "ST_LISTEN_L_0_1",
++      "ST_LISTEN_L_1",
++      "ST_LISTEN_L_1_1",
++};
++
++enum {
++      EV_LISTEN_REQ,
++      EV_LISTEN_CONF,
++}
++
++const EV_LISTEN_COUNT = EV_LISTEN_CONF + 1;
++
++static char* str_ev_listen[] = {
++      "EV_LISTEN_REQ",
++      "EV_LISTEN_CONF",
++};
++
++static struct Fsm listen_fsm =
++{ 0, 0, 0, 0, 0 };
++
++static void
++listen_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      char tmp[128];
++      char *p = tmp;
++      va_list args;
++      Application_t *app = fi->userdata;
++      
++      if (!fi->debug)
++              return;
++      va_start(args, fmt);
++      p += sprintf(p, "Controller 0x%x ApplId %d listen ",
++                   app->contr->addr, app->ApplId);
++      p += vsprintf(p, fmt, args);
++      *p = 0;
++      listenDebug(app, CAPI_DBG_LISTEN_STATE, tmp);
++      va_end(args);
++}
++
++static void
++listen_req_l_x(struct FsmInst *fi, int event, void *arg, int state)
++{
++      Application_t   *app = fi->userdata;
++      _cmsg           *cmsg = arg;
++
++      mISDN_FsmChangeState(fi, state);
++
++      app->InfoMask = cmsg->InfoMask;
++      app->CIPmask = cmsg->CIPmask;
++      app->CIPmask2 = cmsg->CIPmask2;
++      listenDebug(app, CAPI_DBG_LISTEN_INFO, "set InfoMask to 0x%x", app->InfoMask);
++      listenDebug(app, CAPI_DBG_LISTEN_INFO, "set CIP to 0x%x,0x%x", app->CIPmask,
++              app->CIPmask2);
++
++      capi_cmsg_answer(cmsg);
++      cmsg->Info = CAPI_NOERROR;
++
++      if (mISDN_FsmEvent(&app->listen_m, EV_LISTEN_CONF, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++listen_req_l_0(struct FsmInst *fi, int event, void *arg)
++{
++      listen_req_l_x(fi, event, arg, ST_LISTEN_L_0_1);
++}
++
++static void
++listen_req_l_1(struct FsmInst *fi, int event, void *arg)
++{
++      listen_req_l_x(fi, event, arg, ST_LISTEN_L_1_1);
++}
++
++static void
++listen_conf_l_x_1(struct FsmInst *fi, int event, void *arg, int state)
++{
++      Application_t   *app = fi->userdata;
++      _cmsg           *cmsg = arg;
++
++      if (cmsg->Info != CAPI_NOERROR) {
++              mISDN_FsmChangeState(fi, state);
++      } else { // Info == 0
++              if (app->CIPmask == 0) {
++                      test_and_clear_bit(APPL_STATE_LISTEN, &app->state);
++                      mISDN_FsmChangeState(fi, ST_LISTEN_L_0);
++              } else {
++                      test_and_set_bit(APPL_STATE_LISTEN, &app->state);
++                      mISDN_FsmChangeState(fi, ST_LISTEN_L_1);
++              }
++      }
++      SendCmsg2Application(app, cmsg);
++}
++
++static void
++listen_conf_l_0_1(struct FsmInst *fi, int event, void *arg)
++{
++      listen_conf_l_x_1(fi, event, arg, ST_LISTEN_L_0);
++}
++
++static void
++listen_conf_l_1_1(struct FsmInst *fi, int event, void *arg)
++{
++      listen_conf_l_x_1(fi, event, arg, ST_LISTEN_L_1);
++}
++
++static struct FsmNode fn_listen_list[] =
++{
++      {ST_LISTEN_L_0,         EV_LISTEN_REQ,  listen_req_l_0},
++      {ST_LISTEN_L_0_1,       EV_LISTEN_CONF, listen_conf_l_0_1},
++      {ST_LISTEN_L_1,         EV_LISTEN_REQ,  listen_req_l_1},
++      {ST_LISTEN_L_1_1,       EV_LISTEN_CONF, listen_conf_l_1_1},
++};
++
++const int FN_LISTEN_COUNT = sizeof(fn_listen_list)/sizeof(struct FsmNode);
++
++// ----------------------------------------------------------------------
++// Methods
++
++void listenConstr(Application_t *app)
++{
++      app->listen_m.fsm = &listen_fsm;
++      app->listen_m.state = ST_LISTEN_L_0;
++      app->listen_m.debug = app->contr->debug & CAPI_DBG_LISTEN_STATE;
++      app->listen_m.userdata = app;
++      app->listen_m.printdebug = listen_debug;
++      app->InfoMask = 0;
++      app->CIPmask = 0;
++      app->CIPmask2 = 0;
++}
++
++void listenDestr(Application_t *app)
++{
++      test_and_clear_bit(APPL_STATE_LISTEN, &app->state);
++      listenDebug(app, CAPI_DBG_LISTEN, "%s", __FUNCTION__);
++}
++
++__u16
++listenSendMessage(Application_t *app, struct sk_buff *skb)
++{
++      _cmsg   *cmsg;
++
++      cmsg = cmsg_alloc();
++      if (!cmsg) {
++              int_error();
++              return (CAPI_MSGOSRESOURCEERR);
++      }
++      capi_message2cmsg(cmsg, skb->data);
++      switch (CMSGCMD(cmsg)) {
++              case CAPI_LISTEN_REQ:
++                      if (mISDN_FsmEvent(&app->listen_m, EV_LISTEN_REQ, cmsg))
++                              cmsg_free(cmsg);
++                      break;
++              default:
++                      int_error();
++                      cmsg_free(cmsg);
++      }
++      dev_kfree_skb(skb);
++      return(CAPI_NOERROR);
++}
++ 
++int listenHandle(Application_t *app, __u16 CIPValue)
++{
++      if ((app->CIPmask & 1) || 
++          (app->CIPmask & (1 << CIPValue)))
++              return 1;
++      return 0;
++}
++
++void init_listen(void)
++{
++      listen_fsm.state_count = ST_LISTEN_COUNT;
++      listen_fsm.event_count = EV_LISTEN_COUNT;
++      listen_fsm.strEvent = str_ev_listen;
++      listen_fsm.strState = str_st_listen;
++      
++      mISDN_FsmNew(&listen_fsm, fn_listen_list, FN_LISTEN_COUNT);
++}
++
++void free_listen(void)
++{
++      mISDN_FsmFree(&listen_fsm);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/mISDNManufacturer.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/mISDNManufacturer.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/mISDNManufacturer.h  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/mISDNManufacturer.h      2004-11-22 09:33:38.341715088 +0000
+@@ -0,0 +1,37 @@
++/* $Id$
++ *
++ * definition of mISDN own Manufacturer functions
++ *
++ */
++
++#ifndef mISDNManufacturer_H
++#define mISDNManufacturer_H
++
++#define mISDN_MANUFACTURER_ID 0x44534963      /* "mISD" */
++
++/* mISDN_MANUFACTURER message layout
++ *
++ * Controller         dword   Controller Address
++ * ManuID             dword   mISDN_MANUFACTURER_ID
++ * Class              dword   Function Class
++ * Function           dword   Function Identifier
++ * Function specific  struct  Data for this Function
++ *
++ * in a CONF the Function specific struct contain at least
++ * a word which is coded as Capi Info word (error code)
++ */
++ 
++/*
++ * HANDSET special functions
++ *
++ */
++#define mISDN_MF_CLASS_HANDSET                1
++
++#define mISDN_MF_HANDSET_ENABLE               1       /* no function specific data */
++#define mISDN_MF_HANDSET_DISABLE      2       /* no function specific data */
++#define mISDN_MF_HANDSET_SETMICVOLUME 3       /* word volume value */
++#define mISDN_MF_HANDSET_SETSPKVOLUME 4       /* word volume value */
++#define mISDN_MF_HANDSET_GETMICVOLUME 5       /* CONF: Info, word volume value */
++#define mISDN_MF_HANDSET_GETSPKVOLUME 6       /* CONF: Info, word volume value */
++
++#endif /* mISDNManufactor_H */
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/m_capi.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/m_capi.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/m_capi.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/m_capi.h 2004-11-22 09:33:38.351713568 +0000
+@@ -0,0 +1,636 @@
++/* $Id$
++ *
++ * Rewritten CAPI Layer (Layer4 in mISDN)
++ * 
++ * The CAPI layer knows following basic Objects
++ *
++ *  - Controller_t  : the contoller instance
++ *  - Application_t : applications object
++ *  - Plci_t        : PLCI object
++ *  - AppPlci_t     : per application PLCI object
++ *  - Ncci_t      : NCCI object
++ *
++ * For Supplementary Services
++ *  - SSProcess_t   : a process handling a service request
++ *
++ *  The controller is a Layer4 (D-channel) stack instance of
++ *  mISDN.
++ *
++ *  Applications are owned by the controller and only
++ *  handle this controller, multiplexing multiple
++ *  controller with one application is done in the higher
++ *  driver independ CAPI driver. The application contain
++ *  the Listen state machine.
++ *
++ *  Plcis are owned by the controller and are static (allocated
++ *  together with the controller). They maybe in use or inactiv.
++ *  Currently 8 PLCIs are available on a BRI (2 B-channel) controller
++ *  and 40 on a PRI (30 B-channel). They have a list of the associated
++ *  application PLCIs.
++ *  
++ *  AppPlcis are owned by the application and are
++ *  instance of the PLCI per application. They contain the
++ *  CAPI2.0 PCLI state machine
++ * 
++ *  Nccis are owned by the application Plcis. In the first version
++ *  this driver supports only one NCCI per PLCI.
++ *
++ *
++ */
++
++#ifndef __mISDN_CAPI_H__
++#define __mISDN_CAPI_H__
++
++#include <linux/mISDNif.h>
++#include <linux/skbuff.h>
++#include <linux/timer.h>
++#include <linux/capi.h>
++#include <linux/kernelcapi.h>
++#ifdef OLDCAPI_DRIVER_INTERFACE
++#include "../avmb1/capiutil.h"
++#include "../avmb1/capicmd.h"
++#include "../avmb1/capilli.h"
++#else
++#include <linux/list.h>
++#include <linux/isdn/capiutil.h>
++#include <linux/isdn/capicmd.h>
++#include <linux/isdn/capilli.h>
++#endif
++#include "asn1.h"
++#include "fsm.h"
++#ifdef MISDN_MEMDEBUG
++#include "memdbg.h"
++#define MISDN_KMEM_DEBUG              1
++#endif
++
++// ---------------------------------------------------------------------------
++// common stuff
++//     debuging levels and functions
++// ---------------------------------------------------------------------------
++
++#define CAPI_DBG_WARN         0x00000001
++#define CAPI_DBG_INFO         0x00000004
++#define CAPI_DBG_APPL         0x00000010
++#define CAPI_DBG_APPL_INFO    0x00000040
++#define CAPI_DBG_APPL_MSG     0x00000080
++#define CAPI_DBG_LISTEN               0x00000100
++#define CAPI_DBG_LISTEN_STATE 0x00000200
++#define CAPI_DBG_LISTEN_INFO  0x00000400
++#define CAPI_DBG_CONTR                0x00010000
++#define CAPI_DBG_CONTR_INFO   0x00040000
++#define CAPI_DBG_CONTR_MSG    0x00080000
++#define CAPI_DBG_PLCI         0x00100000
++#define CAPI_DBG_PLCI_STATE   0x00200000
++#define CAPI_DBG_PLCI_INFO    0x00400000
++#define CAPI_DBG_PLCI_L3      0x00800000
++#define CAPI_DBG_NCCI         0x01000000
++#define CAPI_DBG_NCCI_STATE   0x02000000
++#define CAPI_DBG_NCCI_INFO    0x04000000
++#define CAPI_DBG_NCCI_L3      0x08000000
++void capidebug(int, char *, ...);
++
++#ifdef OLDCAPI_DRIVER_INTERFACE
++extern struct capi_driver_interface *cdrv_if;                  
++extern struct capi_driver mISDN_driver;
++#endif
++
++// ---------------------------------------------------------------------------
++// Init/Exit functions
++// ---------------------------------------------------------------------------
++
++void init_listen(void);
++void init_AppPlci(void);
++void init_ncci(void);
++void free_Application(void);
++void free_listen(void);
++void free_AppPlci(void);
++void free_ncci(void);
++
++// ---------------------------------------------------------------------------
++// More CAPI defines
++// ---------------------------------------------------------------------------
++
++/* we implement 64 bit extentions */
++#define CAPI_B3_DATA_IND_HEADER_SIZE  30
++#define CAPI_MSG_DEFAULT_LEN          256
++
++#define CAPIMSG_REQ_DATAHANDLE(m)     (m[18] | (m[19]<<8))
++#define CAPIMSG_RESP_DATAHANDLE(m)    (m[12] | (m[13]<<8))
++
++#define CMSGCMD(cmsg)                 CAPICMD((cmsg)->Command, (cmsg)->Subcommand)
++
++#define CAPI_MAXPLCI_BRI              8
++#define CAPI_MAXPLCI_PRI              40
++
++__u16 q931CIPValue(Q931_info_t *);
++
++// ---------------------------------------------------------------------------
++// Basic CAPI types
++// ---------------------------------------------------------------------------
++
++typedef struct _Controller    Controller_t;
++typedef struct _Application   Application_t;
++typedef struct _Ncci          Ncci_t;
++typedef struct _Plci          Plci_t;
++typedef struct _AppPlci               AppPlci_t;
++typedef struct _SSProcess     SSProcess_t;
++
++// some helper types
++typedef struct _ConfQueue     ConfQueue_t;
++typedef struct _PLInst                PLInst_t;
++
++// Facility types
++typedef struct FacReqParm     FacReqParm_t;
++typedef struct FacConfParm    FacConfParm_t;
++
++// ---------------------------------------------------------------------------
++// Helper structs
++// ---------------------------------------------------------------------------
++
++struct _PLInst {
++      struct list_head        list;
++      u_int                   state;
++      mISDNstack_t            *st;
++      mISDNinstance_t         inst;
++};
++
++struct _ConfQueue { 
++      __u32   PktId; 
++      __u16   DataHandle;
++      __u16   MsgId; 
++};
++
++struct Bprotocol {
++      __u16   B1;
++      __u16   B2;
++      __u16   B3;
++      __u8    B1cfg[16];
++      __u8    B2cfg[16];
++      __u8    B3cfg[80];
++};
++
++// ---------------------------------------------------------------------------
++// struct Controller
++// ---------------------------------------------------------------------------
++
++struct _Controller {
++      struct list_head        list;
++      mISDNinstance_t         inst;
++      int                     nr_bc;
++      struct list_head        linklist;
++      struct capi_ctr         *ctrl;
++      __u32                   addr;
++      int                     entity;
++      int                     next_id;
++      spinlock_t              id_lock;
++      u_int                   debug;
++      int                     maxplci;
++      Plci_t                  *plcis;
++      struct list_head        Applications;
++      struct list_head        SSProcesse;
++      spinlock_t              list_lock;
++      __u32                   NotificationMask;
++      __u16                   LastInvokeId;
++      char                    infobuf[128];
++};
++
++// ---------------------------------------------------------------------------
++// struct Application
++// ---------------------------------------------------------------------------
++
++struct _Application {
++      struct list_head        head;
++      Controller_t            *contr;
++      __u16                   ApplId;
++      __u16                   MsgId;
++      __u32                   InfoMask;
++      __u32                   CIPmask;
++      __u32                   CIPmask2;
++      __u32                   NotificationMask;
++      u_long                  state;
++      struct FsmInst          listen_m;
++      int                     maxplci;
++      AppPlci_t               **AppPlcis;
++      capi_register_params    reg_params;
++};
++
++#define APPL_STATE_ACTIV      1
++#define APPL_STATE_RELEASE    2
++#define APPL_STATE_LISTEN     3
++#define APPL_STATE_DESTRUCTOR 4
++#define APPL_STATE_D2TRACE    8
++
++// ---------------------------------------------------------------------------
++// struct Plci
++// ---------------------------------------------------------------------------
++
++struct _Plci {
++      Controller_t            *contr;
++      __u32                   addr;
++      __u32                   l3id;
++      u_long                  state;
++      int                     nAppl;
++      struct list_head        AppPlcis;
++};
++
++#define PLCI_STATE_ACTIV      1
++#define PLCI_STATE_ALERTING   2
++#define PLCI_STATE_OUTGOING   3
++#define PLCI_STATE_STACKREADY 4
++#define PLCI_STATE_SENDDELAYED        5
++
++// ---------------------------------------------------------------------------
++// struct AppPlci
++// ---------------------------------------------------------------------------
++
++struct _AppPlci {
++      struct list_head        head;
++      __u32                   addr;
++      Plci_t                  *plci;
++      Application_t           *appl;
++      Controller_t            *contr;
++      PLInst_t                *link;
++      struct sk_buff_head     delayedq;
++      struct list_head        Nccis;
++      struct FsmInst          plci_m;
++      u_char                  cause[4];
++      int                     channel;
++      struct Bprotocol        Bprotocol;
++};
++
++// ---------------------------------------------------------------------------
++// struct Ncci
++// ---------------------------------------------------------------------------
++
++struct _Ncci {
++      struct list_head        head;
++      __u32                   addr;
++      PLInst_t                *link;
++      Controller_t            *contr;
++      AppPlci_t               *AppPlci;
++      Application_t           *appl;
++      struct FsmInst          ncci_m;
++      int                     savedstate;
++      int                     window;
++      u_long                  state;
++      ConfQueue_t             xmit_skb_handles[CAPI_MAXDATAWINDOW];
++      struct sk_buff          *recv_skb_handles[CAPI_MAXDATAWINDOW];
++      struct sk_buff_head     squeue;
++};
++
++#define NCCI_STATE_FCTRL      1
++#define NCCI_STATE_BUSY               2
++#define NCCI_STATE_L3TRANS    3
++#define       NCCI_STATE_APPLRELEASED 4
++
++// ---------------------------------------------------------------------------
++// struct SSProcess_t
++// ---------------------------------------------------------------------------
++
++struct _SSProcess {
++      struct list_head        head;
++      __u16                   invokeId;
++      __u16                   Function;  
++      __u32                   Handle;
++      __u32                   addr;
++      __u16                   ApplId;
++      Controller_t            *contr;
++      struct timer_list       tl;
++      __u8                    buf[128];
++};
++
++// ---------------------------------------------------------------------------
++// FUNCTION prototypes
++//
++// Controller prototypes
++// ---------------------------------------------------------------------------
++
++int           ControllerConstr(Controller_t **, mISDNstack_t *, mISDN_pid_t *, mISDNobject_t *);
++void          ControllerDestr(Controller_t *);
++void          ControllerRun(Controller_t *);
++void          ControllerDebug(Controller_t *, __u32, char *, ...);
++int           ControllerNewPlci(Controller_t *, Plci_t  **, u_int);
++int           ControllerReleasePlci(Plci_t *);
++Application_t *getApplication4Id(Controller_t *, __u16);
++Plci_t                *getPlci4Addr(Controller_t *, __u32);
++int           ControllerL4L3(Controller_t *, u_int, int, struct sk_buff *);
++int           ControllerL3L4(mISDNif_t *, struct sk_buff *);
++PLInst_t      *ControllerSelChannel(Controller_t *, u_int);
++void          ControllerAddSSProcess(Controller_t *, SSProcess_t *);
++SSProcess_t   *getSSProcess4Id(Controller_t *, __u16);
++int           ControllerNextId(Controller_t *);
++
++// ---------------------------------------------------------------------------
++// Application prototypes
++// ---------------------------------------------------------------------------
++
++int           ApplicationConstr(Controller_t *, __u16, capi_register_params *);
++int           ApplicationDestr(Application_t *, int);
++void          ApplicationDebug(Application_t *appl, __u32 level, char *fmt, ...);
++__u16         ApplicationSendMessage(Application_t *appl, struct sk_buff *skb);
++void          SendCmsg2Application(Application_t *, _cmsg *);
++void          SendCmsgAnswer2Application(Application_t *, _cmsg *, __u16);
++void          AnswerMessage2Application(Application_t *, struct sk_buff *, __u16);
++void          applManufacturerReq(Application_t *appl, struct sk_buff *skb);
++void          applD2Trace(Application_t *appl, u_char *buf, int len);
++AppPlci_t     *ApplicationNewAppPlci(Application_t *, Plci_t *);
++AppPlci_t     *getAppPlci4addr(Application_t *, __u32);
++void          ApplicationDelAppPlci(Application_t *, AppPlci_t *);
++
++void          listenConstr(Application_t *);
++void          listenDestr(Application_t *);
++__u16         listenSendMessage(Application_t *, struct sk_buff *);
++int           listenHandle(Application_t *, __u16);
++
++// ---------------------------------------------------------------------------
++// PLCI prototypes
++// ---------------------------------------------------------------------------
++
++void  plciInit(Controller_t *);
++void  plciDebug(Plci_t *, __u32, char *, ...);
++int   plci_l3l4(Plci_t *, int, struct sk_buff *);
++void  plciAttachAppPlci(Plci_t *, AppPlci_t *);
++void  plciDetachAppPlci(Plci_t *, AppPlci_t *);
++void  plciNewCrInd(Plci_t *, void *);
++void  plciNewCrReq(Plci_t *);
++int   plciL4L3(Plci_t *, __u32, struct sk_buff *);
++
++// ---------------------------------------------------------------------------
++// AppPLCI prototypes
++// ---------------------------------------------------------------------------
++
++int   AppPlciConstr(AppPlci_t **, Application_t *, Plci_t *);
++void  AppPlciDestr(AppPlci_t *);
++void  AppPlciDelNCCI(Ncci_t *);
++void  AppPlci_l3l4(AppPlci_t *, int, void *);
++__u16         AppPlciSendMessage(AppPlci_t *, struct sk_buff *);
++void  AppPlciRelease(AppPlci_t *);
++int   AppPlciFacSuspendReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *);
++int   AppPlciFacResumeReq(AppPlci_t *, FacReqParm_t *, FacConfParm_t *);
++void  AppPlciGetCmsg(AppPlci_t *, _cmsg *);
++Ncci_t        *getNCCI4addr(AppPlci_t *, __u32, int);
++int   ConnectB3Request(AppPlci_t *, struct sk_buff *);
++int   AppPlcimISDN_SetIF(AppPlci_t *, u_int, void *);
++
++#define       GET_NCCI_EXACT          1
++#define GET_NCCI_ONLY_PLCI    2
++#define GET_NCCI_PLCI         3
++
++// ---------------------------------------------------------------------------
++// NCCI prototypes
++// ---------------------------------------------------------------------------
++
++Ncci_t        *ncciConstr(AppPlci_t *);
++void  ncciDestr(Ncci_t *);
++void  ncciApplRelease(Ncci_t *);
++void  ncciDelAppPlci(Ncci_t *);
++__u16 ncciSendMessage(Ncci_t *, struct sk_buff *);
++int   ncci_l3l4(Ncci_t *, mISDN_head_t *, struct sk_buff *);
++void  ncciGetCmsg(Ncci_t *, _cmsg *);
++int   ncci_l3l4_direct(Ncci_t *, mISDN_head_t *, struct sk_buff *);
++void  ncciReleaseLink(Ncci_t *);
++
++// ---------------------------------------------------------------------------
++//  SSProcess prototypes
++// ---------------------------------------------------------------------------
++
++SSProcess_t   *SSProcessConstr(Application_t *, __u16, __u32);
++void          SSProcessDestr(SSProcess_t *);
++int           Supplementary_l3l4(Controller_t *, __u32, struct sk_buff *);
++void          SupplementaryFacilityReq(Application_t *, _cmsg *);
++
++// ---------------------------------------------------------------------------
++// INFOMASK defines (LISTEN commands)
++// ---------------------------------------------------------------------------
++
++#define CAPI_INFOMASK_CAUSE     (0x0001)
++#define CAPI_INFOMASK_DATETIME  (0x0002)
++#define CAPI_INFOMASK_DISPLAY   (0x0004)
++#define CAPI_INFOMASK_USERUSER  (0x0008)
++#define CAPI_INFOMASK_PROGRESS  (0x0010)
++#define CAPI_INFOMASK_FACILITY  (0x0020)
++//#define CAPI_INFOMASK_CHARGE    (0x0040)
++//#define CAPI_INFOMASK_CALLEDPN  (0x0080)
++#define CAPI_INFOMASK_CHANNELID (0x0100)
++#define CAPI_INFOMASK_EARLYB3   (0x0200)
++//#define CAPI_INFOMASK_REDIRECT  (0x0400)
++
++// ---------------------------------------------------------------------------
++// Supplementary Services
++// ---------------------------------------------------------------------------
++
++#define SuppServiceTP                 0x00000002
++#define SuppServiceCF                 0x00000010
++#define mISDNSupportedServices                (SuppServiceCF | SuppServiceTP)
++
++// ---------------------------------------------------------------------------
++// structs for Facillity requests
++// ---------------------------------------------------------------------------
++
++struct FacReqListen {
++      __u32 NotificationMask;
++};
++
++struct FacReqSuspend {
++      __u8 *CallIdentity;
++};
++
++struct FacReqResume {
++      __u8 *CallIdentity;
++};
++
++struct FacReqCFActivate {
++      __u32 Handle;
++      __u16 Procedure;
++      __u16 BasicService;
++      __u8  *ServedUserNumber;
++      __u8  *ForwardedToNumber;
++      __u8  *ForwardedToSubaddress;
++};
++
++struct FacReqCFDeactivate {
++      __u32 Handle;
++      __u16 Procedure;
++      __u16 BasicService;
++      __u8  *ServedUserNumber;
++};
++
++#define FacReqCFInterrogateParameters FacReqCFDeactivate
++
++struct FacReqCFInterrogateNumbers {
++      __u32 Handle;
++};
++
++struct FacReqParm {
++      __u16 Function;
++      union {
++              struct FacReqListen Listen;
++              struct FacReqSuspend Suspend;
++              struct FacReqResume Resume;
++              struct FacReqCFActivate CFActivate;
++              struct FacReqCFDeactivate CFDeactivate;
++              struct FacReqCFInterrogateParameters CFInterrogateParameters;
++              struct FacReqCFInterrogateNumbers CFInterrogateNumbers;
++      } u;
++};
++
++// ---------------------------------------------------------------------------
++// structs for Facillity confirms
++// ---------------------------------------------------------------------------
++
++struct FacConfGetSupportedServices {
++      __u16 SupplementaryServiceInfo;
++      __u32 SupportedServices;
++};
++
++struct FacConfInfo {
++      __u16 SupplementaryServiceInfo;
++};
++
++struct FacConfParm {
++      __u16 Function;
++      union {
++              struct FacConfGetSupportedServices GetSupportedServices;
++              struct FacConfInfo Info;
++      } u;
++};
++
++int capiEncodeWord(__u8 *dest, __u16 i);
++int capiEncodeDWord(__u8 *dest, __u32 i);
++int capiEncodeFacIndCFact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle);
++int capiEncodeFacIndCFdeact(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle);
++int capiEncodeFacIndCFNotAct(__u8 *dest, struct ActDivNotification *actNot);
++int capiEncodeFacIndCFNotDeact(__u8 *dest, struct DeactDivNotification *deactNot);
++int capiEncodeFacIndCFinterParameters(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, 
++                                    struct IntResultList *intResultList);
++int capiEncodeFacIndCFinterNumbers(__u8 *dest, __u16 SupplementaryServiceReason, __u32 Handle, 
++                                 struct ServedUserNumberList *list);
++int capiEncodeFacConfParm(__u8 *dest, struct FacConfParm *facConfParm);
++
++int capiEncodeFacIndSuspend(__u8 *dest, __u16  SupplementaryServiceReason);
++
++// ---------------------------------------------------------------------------
++// mISDN kmem cache managment functions
++// ---------------------------------------------------------------------------
++
++/* kmem caches */
++extern kmem_cache_t   *mISDN_cmsg_cp;
++extern kmem_cache_t   *mISDN_AppPlci_cp;
++extern kmem_cache_t   *mISDN_ncci_cp;
++extern kmem_cache_t   *mISDN_sspc_cp;
++
++#ifdef MISDN_KMEM_DEBUG
++typedef struct _kd_cmsg               _kd_cmsg_t;
++typedef struct _kd_Ncci               _kd_Ncci_t;
++typedef struct _kd_AppPlci    _kd_AppPlci_t;
++typedef struct _kd_SSProcess  _kd_SSProcess_t;
++typedef struct _kd_all                _kd_all_t;
++
++typedef       struct __km_dbg_item {
++      struct list_head        head;
++      long                    typ;
++      char                    *file;
++      u_int                   line;
++} km_dbg_item_t;
++
++struct _kd_cmsg {
++      km_dbg_item_t           kdi;
++      _cmsg                   cm;
++};
++
++struct _kd_AppPlci {
++      km_dbg_item_t           kdi;
++      AppPlci_t               ap;
++};
++
++struct _kd_Ncci {
++      km_dbg_item_t           kdi;
++      Ncci_t                  ni;
++};
++
++struct _kd_SSProcess {
++      km_dbg_item_t           kdi;
++      SSProcess_t             sp;
++};
++
++struct _kd_all {
++      km_dbg_item_t           kdi;
++      union {
++              _cmsg           cm;
++              AppPlci_t       ap;
++              Ncci_t          ni;
++              SSProcess_t     sp;
++      } a;
++};
++
++#define KDB_GET_KDI(kd)               ((km_dbg_item_t *)(((u_char *)kd) - sizeof(km_dbg_item_t)))
++#define KDB_GET_KDALL(kd)     ((_kd_all_t *)(((u_char *)kd) - sizeof(km_dbg_item_t)))
++
++#define KM_DBG_TYP_CM         1
++#define KM_DBG_TYP_AP         2
++#define KM_DBG_TYP_NI         3
++#define KM_DBG_TYP_SP         4
++
++#define cmsg_alloc()          _kd_cmsg_alloc(__FILE__, __LINE__)
++extern _cmsg                  *_kd_cmsg_alloc(char *, int);
++extern void                   cmsg_free(_cmsg *cm);
++
++#define AppPlci_alloc()               _kd_AppPlci_alloc(__FILE__, __LINE__)
++extern AppPlci_t              *_kd_AppPlci_alloc(char *, int);
++extern void                   AppPlci_free(AppPlci_t *ap);
++
++#define ncci_alloc()          _kd_ncci_alloc(__FILE__, __LINE__)
++extern Ncci_t                 *_kd_ncci_alloc(char *, int);
++extern void                   ncci_free(Ncci_t *ni);
++
++#define SSProcess_alloc()     _kd_SSProcess_alloc(__FILE__, __LINE__)
++extern SSProcess_t            *_kd_SSProcess_alloc(char *, int);
++extern void                   SSProcess_free(SSProcess_t *sp);
++
++#else /* ! MISDN_KMEM_DEBUG */
++
++static __inline__ _cmsg *cmsg_alloc(void)
++{
++      return(kmem_cache_alloc(mISDN_cmsg_cp, GFP_ATOMIC));
++}
++
++static __inline__ void cmsg_free(_cmsg *cm)
++{
++      kmem_cache_free(mISDN_cmsg_cp, cm);
++}
++
++static __inline__ AppPlci_t *AppPlci_alloc(void)
++{
++      return(kmem_cache_alloc(mISDN_AppPlci_cp, GFP_ATOMIC));
++}
++
++static __inline__ void AppPlci_free(AppPlci_t *ap)
++{
++      kmem_cache_free(mISDN_AppPlci_cp, ap);
++}
++
++static __inline__ Ncci_t *ncci_alloc(void)
++{
++      return(kmem_cache_alloc(mISDN_ncci_cp, GFP_ATOMIC));
++}
++
++static __inline__ void ncci_free(Ncci_t *ni)
++{
++      kmem_cache_free(mISDN_ncci_cp, ni);
++}
++
++static __inline__ SSProcess_t *SSProcess_alloc(void)
++{
++      return(kmem_cache_alloc(mISDN_sspc_cp, GFP_ATOMIC));
++}
++
++static __inline__ void SSProcess_free(SSProcess_t *sp)
++{
++      kmem_cache_free(mISDN_sspc_cp, sp);
++}
++
++#endif /* MISDN_KMEM_DEBUG */
++// cmsg_alloc with error handling for void functions
++#define CMSG_ALLOC(cm)        if (!(cm = cmsg_alloc())) {int_error();return;}
++
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.c 2004-11-22 09:33:38.361712048 +0000
+@@ -0,0 +1,293 @@
++#include <linux/stddef.h>
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/mISDNif.h>
++#ifdef CONFIG_KMOD
++#include <linux/kmod.h>
++#endif
++
++static struct list_head mISDN_memdbg_list = LIST_HEAD_INIT(mISDN_memdbg_list);
++static struct list_head mISDN_skbdbg_list = LIST_HEAD_INIT(mISDN_skbdbg_list);
++static spinlock_t     memdbg_lock = SPIN_LOCK_UNLOCKED;
++static spinlock_t     skbdbg_lock = SPIN_LOCK_UNLOCKED;
++static kmem_cache_t   *mid_sitem_cache;
++
++#define MAX_FILE_STRLEN               (64 - 3*sizeof(u_int) - sizeof(struct list_head))
++
++#define MID_ITEM_TYP_KMALLOC  1
++#define MID_ITEM_TYP_VMALLOC  2
++
++typedef struct _mid_item {
++      struct list_head        head;
++      u_int                   typ;
++      u_int                   size;
++      u_int                   line;
++      char                    file[MAX_FILE_STRLEN];
++} _mid_item_t;
++
++typedef struct _mid_sitem {
++      struct list_head        head;
++      struct sk_buff          *skb;
++      unsigned int            size;
++      int                     line;
++      char                    file[MAX_FILE_STRLEN];
++} _mid_sitem_t;
++
++void *
++__mid_kmalloc(size_t size, int ord, char *fn, int line)
++{
++      _mid_item_t     *mid;
++      u_long          flags;
++
++      mid = kmalloc(size + sizeof(_mid_item_t), ord);
++      if (mid) {
++              INIT_LIST_HEAD(&mid->head);
++              mid->typ  = MID_ITEM_TYP_KMALLOC;
++              mid->size = size;
++              mid->line = line;
++              memcpy(mid->file, fn, MAX_FILE_STRLEN);
++              mid->file[MAX_FILE_STRLEN-1] = 0;
++              spin_lock_irqsave(&memdbg_lock, flags);
++              list_add_tail(&mid->head, &mISDN_memdbg_list);
++              spin_unlock_irqrestore(&memdbg_lock, flags);
++              return((void *)&mid->file[MAX_FILE_STRLEN]);
++      } else
++              return(NULL);
++}
++
++void
++__mid_kfree(const void *p)
++{
++      _mid_item_t     *mid;
++      u_long          flags;
++
++      if (!p) {
++              printk(KERN_ERR "zero pointer kfree at %p", __builtin_return_address(0));
++              return;
++      }
++      mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t));
++      spin_lock_irqsave(&memdbg_lock, flags);
++      list_del(&mid->head);
++      spin_unlock_irqrestore(&memdbg_lock, flags);
++      kfree(mid);
++}
++
++void *
++__mid_vmalloc(size_t size, char *fn, int line)
++{
++      _mid_item_t     *mid;
++      u_long          flags;
++
++      mid = vmalloc(size + sizeof(_mid_item_t));
++      if (mid) {
++              INIT_LIST_HEAD(&mid->head);
++              mid->typ  = MID_ITEM_TYP_VMALLOC;
++              mid->size = size;
++              mid->line = line;
++              memcpy(mid->file, fn, MAX_FILE_STRLEN);
++              mid->file[MAX_FILE_STRLEN-1] = 0; 
++              spin_lock_irqsave(&memdbg_lock, flags);
++              list_add_tail(&mid->head, &mISDN_memdbg_list);
++              spin_unlock_irqrestore(&memdbg_lock, flags);
++              return((void *)&mid->file[MAX_FILE_STRLEN]);
++      } else
++              return(NULL);
++}
++
++void
++__mid_vfree(const void *p)
++{
++      _mid_item_t     *mid;
++      u_long          flags;
++
++      if (!p) {
++              printk(KERN_ERR "zero pointer vfree at %p", __builtin_return_address(0));
++              return;
++      }
++      mid = (_mid_item_t *)((u_char *)p - sizeof(_mid_item_t));
++      spin_lock_irqsave(&memdbg_lock, flags);
++      list_del(&mid->head);
++      spin_unlock_irqrestore(&memdbg_lock, flags);
++      vfree(mid);
++}
++
++static void
++__mid_skb_destructor(struct sk_buff *skb)
++{
++      struct list_head        *item;
++      _mid_sitem_t            *sid;
++      u_long          flags;
++
++      spin_lock_irqsave(&skbdbg_lock, flags);
++      list_for_each(item, &mISDN_skbdbg_list) {
++              sid = (_mid_sitem_t *)item;
++              if (sid->skb == skb) {
++                      list_del(&sid->head);
++                      spin_unlock_irqrestore(&skbdbg_lock, flags);
++                      kmem_cache_free(mid_sitem_cache, sid);
++                      return;
++              }
++      }
++      spin_unlock_irqrestore(&skbdbg_lock, flags);
++      printk(KERN_DEBUG "%s: item(%p) not in list\n", __FUNCTION__, skb);
++}
++
++static __inline__ void
++__mid_sitem_setup(struct sk_buff *skb, unsigned int size, char *fn, int line)
++{
++      _mid_sitem_t    *sid;
++      u_long          flags;
++
++      sid = kmem_cache_alloc(mid_sitem_cache, GFP_ATOMIC);
++      if (!sid) {
++              printk(KERN_DEBUG "%s: no memory for sitem skb %p %s:%d\n",
++                      __FUNCTION__, skb, fn, line);
++              return;
++      }
++      INIT_LIST_HEAD(&sid->head);
++      sid->skb = skb;
++      sid->size = size;
++      sid->line = line;
++      memcpy(sid->file, fn, MAX_FILE_STRLEN);
++      sid->file[MAX_FILE_STRLEN-1] = 0; 
++      skb->destructor = __mid_skb_destructor;
++      spin_lock_irqsave(&skbdbg_lock, flags);
++      list_add_tail(&sid->head, &mISDN_skbdbg_list);
++      spin_unlock_irqrestore(&skbdbg_lock, flags);
++}
++
++struct sk_buff *
++__mid_alloc_skb(unsigned int size, int gfp_mask, char *fn, int line)
++{
++      struct sk_buff  *skb = alloc_skb(size, gfp_mask);
++
++      if (!skb)
++              return(NULL);
++      __mid_sitem_setup(skb, size, fn, line);
++      return(skb);
++}
++
++struct sk_buff *
++__mid_dev_alloc_skb(unsigned int size, char *fn, int line)
++{
++      struct sk_buff  *skb = dev_alloc_skb(size);
++
++      if (!skb)
++              return(NULL);
++      __mid_sitem_setup(skb, size, fn, line);
++      return(skb);
++}
++
++struct sk_buff
++*__mid_skb_clone(struct sk_buff *skb, int gfp_mask, char *fn, int line)
++{
++      struct sk_buff  *nskb = skb_clone(skb, gfp_mask);
++
++      if (!nskb)
++              return(NULL);
++      __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line);
++      return(nskb);
++}
++
++struct sk_buff
++*__mid_skb_copy(struct sk_buff *skb, int gfp_mask, char *fn, int line)
++{
++      struct sk_buff  *nskb = skb_copy(skb, gfp_mask);
++
++      if (!nskb)
++              return(NULL);
++      __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line);
++      return(nskb);
++}
++
++struct sk_buff
++*__mid_skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom, char *fn, int line)
++{
++      struct sk_buff  *nskb = skb_realloc_headroom(skb, headroom);
++
++      if (!nskb || (nskb == skb))
++              return(nskb);
++      __mid_sitem_setup(nskb, (nskb->end - nskb->head), fn, line);
++      return(nskb);
++}
++
++void
++__mid_cleanup(void)
++{
++      struct list_head        *item, *next;
++      _mid_item_t             *mid;
++      _mid_sitem_t            *sid;
++      mISDN_head_t            *hh;
++      int                     n = 0;
++      u_long                  flags;
++
++      spin_lock_irqsave(&memdbg_lock, flags);
++      list_for_each_safe(item, next, &mISDN_memdbg_list) {
++              mid = (_mid_item_t *)item;
++              switch(mid->typ) {
++                      case MID_ITEM_TYP_KMALLOC:
++                              printk(KERN_ERR "not freed kmalloc size(%d) from %s:%d\n",
++                                      mid->size, mid->file, mid->line);
++                              kfree(mid);
++                              break;
++                      case MID_ITEM_TYP_VMALLOC:
++                              printk(KERN_ERR "not freed vmalloc size(%d) from %s:%d\n",
++                                      mid->size, mid->file, mid->line);
++                              vfree(mid);
++                              break;
++                      default:
++                              printk(KERN_ERR "unknown mid->typ(%d) size(%d) from %s:%d\n",
++                                      mid->typ, mid->size, mid->file, mid->line);
++                              break;
++              }
++              n++;
++      }
++      spin_unlock_irqrestore(&memdbg_lock, flags);
++      printk(KERN_DEBUG "%s: %d kmalloc item(s) freed\n", __FUNCTION__, n);
++      n = 0;
++      spin_lock_irqsave(&skbdbg_lock, flags);
++      list_for_each_safe(item, next, &mISDN_skbdbg_list) {
++              sid = (_mid_sitem_t *)item;
++              hh = mISDN_HEAD_P(sid->skb);
++              printk(KERN_ERR "not freed skb(%p) size(%d) prim(%x) dinfo(%x) allocated at %s:%d\n",
++                      sid->skb, sid->size, hh->prim, hh->dinfo, sid->file, sid->line);
++              /*maybe the skb is still aktiv */
++              sid->skb->destructor = NULL;
++              list_del(&sid->head);
++              kmem_cache_free(mid_sitem_cache, sid);
++              n++;
++      }
++      spin_unlock_irqrestore(&skbdbg_lock, flags);
++      if (mid_sitem_cache)
++              kmem_cache_destroy(mid_sitem_cache);
++      printk(KERN_DEBUG "%s: %d sk_buff item(s) freed\n", __FUNCTION__, n);
++}
++
++int
++__mid_init(void)
++{
++      mid_sitem_cache = kmem_cache_create("mISDN_skbdbg",
++                              sizeof(_mid_sitem_t),
++                              0, 0, NULL, NULL);
++      if (!mid_sitem_cache)
++              return(-ENOMEM);
++      return(0);
++}
++
++#ifdef MODULE
++EXPORT_SYMBOL(__mid_kmalloc);
++EXPORT_SYMBOL(__mid_kfree);
++EXPORT_SYMBOL(__mid_vmalloc);
++EXPORT_SYMBOL(__mid_vfree);
++EXPORT_SYMBOL(__mid_alloc_skb);
++EXPORT_SYMBOL(__mid_dev_alloc_skb);
++EXPORT_SYMBOL(__mid_skb_clone);
++EXPORT_SYMBOL(__mid_skb_copy);
++EXPORT_SYMBOL(__mid_skb_realloc_headroom);
++
++#endif
++
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/memdbg.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/memdbg.h 2004-11-22 09:33:38.371710528 +0000
+@@ -0,0 +1,41 @@
++#ifndef MEMDBG_H
++#define MEMDBG_H
++
++#ifdef MISDN_MEMDEBUG
++#include <linux/vmalloc.h>
++#include <linux/slab.h>
++
++#undef kmalloc
++#undef kfree
++#undef vmalloc
++#undef vfree
++#undef alloc_skb
++#undef dev_alloc_skb
++#undef skb_clone
++#undef skb_copy
++#undef skb_realloc_headroom
++
++#define kmalloc(a, b)                 __mid_kmalloc(a, b, __FILE__, __LINE__)
++#define kfree(a)                      __mid_kfree(a)
++#define vmalloc(s)                    __mid_vmalloc(s, __FILE__, __LINE__)
++#define vfree(p)                      __mid_vfree(p)
++#define alloc_skb(a, b)                       __mid_alloc_skb(a, b, __FILE__, __LINE__)
++#define dev_alloc_skb(a)              __mid_dev_alloc_skb(a, __FILE__, __LINE__)
++#define skb_clone(a, b)                       __mid_skb_clone(a, b, __FILE__, __LINE__)
++#define skb_copy(a, b)                        __mid_skb_copy(a, b, __FILE__, __LINE__)
++#define skb_realloc_headroom(a, b)    __mid_skb_realloc_headroom(a, b, __FILE__, __LINE__)
++
++extern void           *__mid_kmalloc(size_t, int, char *, int);
++extern void           __mid_kfree(const void *);
++extern void           *__mid_vmalloc(size_t, char *, int);
++extern void           __mid_vfree(const void *);
++extern void           __mid_cleanup(void);
++extern int            __mid_init(void);
++extern struct sk_buff *__mid_alloc_skb(unsigned int,int, char *, int);
++extern struct sk_buff *__mid_dev_alloc_skb(unsigned int,char *, int);
++extern struct sk_buff *__mid_skb_clone(struct sk_buff *, int, char *, int);
++extern struct sk_buff *__mid_skb_copy(struct sk_buff *, int, char *, int);
++extern struct sk_buff *__mid_skb_realloc_headroom(struct sk_buff *, unsigned int, char *, int);
++#endif
++
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/ncci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/ncci.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/ncci.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/ncci.c   2004-11-22 09:33:38.381709008 +0000
+@@ -0,0 +1,1394 @@
++/* $Id$
++ *
++ */
++
++#include "m_capi.h"
++#include "helper.h"
++#include "debug.h"
++#include "dss1.h"
++#include "mISDNManufacturer.h"
++
++static int    ncciL4L3(Ncci_t *, u_int, int, int, void *, struct sk_buff *);
++
++static char   logbuf[8000];
++
++void
++log_skbdata(struct sk_buff *skb)
++{
++      char *t = logbuf;
++
++      t += sprintf(t, "skbdata(%d):", skb->len);
++      mISDN_QuickHex(t, skb->data, skb->len);
++      printk(KERN_DEBUG "%s\n", logbuf);
++}
++
++// --------------------------------------------------------------------
++// NCCI state machine
++//
++// Some rules:
++//   *  EV_AP_*  events come from CAPI Application
++//   *  EV_DL_*  events come from the ISDN stack
++//   *  EV_NC_*  events generated in NCCI handling
++//   *  messages are send in the routine that handle the event
++//
++// --------------------------------------------------------------------
++enum {
++      ST_NCCI_N_0,
++      ST_NCCI_N_0_1,
++      ST_NCCI_N_1,
++      ST_NCCI_N_2,
++      ST_NCCI_N_ACT,
++      ST_NCCI_N_3,
++      ST_NCCI_N_4,
++      ST_NCCI_N_5,
++}
++
++const ST_NCCI_COUNT = ST_NCCI_N_5 + 1;
++
++static char *str_st_ncci[] = {
++      "ST_NCCI_N_0",
++      "ST_NCCI_N_0_1",
++      "ST_NCCI_N_1",
++      "ST_NCCI_N_2",
++      "ST_NCCI_N_ACT",
++      "ST_NCCI_N_3",
++      "ST_NCCI_N_4",
++      "ST_NCCI_N_5",
++}; 
++
++enum {
++      EV_AP_CONNECT_B3_REQ,
++      EV_NC_CONNECT_B3_CONF,
++      EV_NC_CONNECT_B3_IND,
++      EV_AP_CONNECT_B3_RESP,
++      EV_NC_CONNECT_B3_ACTIVE_IND,
++      EV_AP_CONNECT_B3_ACTIVE_RESP,
++      EV_AP_RESET_B3_REQ,
++      EV_NC_RESET_B3_IND,
++      EV_NC_RESET_B3_CONF,
++      EV_AP_RESET_B3_RESP,
++      EV_NC_CONNECT_B3_T90_ACTIVE_IND,
++      EV_AP_DISCONNECT_B3_REQ,
++      EV_NC_DISCONNECT_B3_IND,
++      EV_NC_DISCONNECT_B3_CONF,
++      EV_AP_DISCONNECT_B3_RESP,
++      EV_AP_FACILITY_REQ,
++      EV_AP_MANUFACTURER_REQ,
++      EV_DL_ESTABLISH_IND,
++      EV_DL_ESTABLISH_CONF,
++      EV_DL_RELEASE_IND,
++      EV_DL_RELEASE_CONF,
++      EV_DL_DOWN_IND,
++      EV_NC_LINKDOWN,
++      EV_AP_RELEASE,
++}
++
++const EV_NCCI_COUNT = EV_AP_RELEASE + 1;
++
++static char* str_ev_ncci[] = {
++      "EV_AP_CONNECT_B3_REQ",
++      "EV_NC_CONNECT_B3_CONF",
++      "EV_NC_CONNECT_B3_IND",
++      "EV_AP_CONNECT_B3_RESP",
++      "EV_NC_CONNECT_B3_ACTIVE_IND",
++      "EV_AP_CONNECT_B3_ACTIVE_RESP",
++      "EV_AP_RESET_B3_REQ",
++      "EV_NC_RESET_B3_IND",
++      "EV_NC_RESET_B3_CONF",
++      "EV_AP_RESET_B3_RESP",
++      "EV_NC_CONNECT_B3_T90_ACTIVE_IND",
++      "EV_AP_DISCONNECT_B3_REQ",
++      "EV_NC_DISCONNECT_B3_IND",
++      "EV_NC_DISCONNECT_B3_CONF",
++      "EV_AP_DISCONNECT_B3_RESP",
++      "EV_AP_FACILITY_REQ",
++      "EV_AP_MANUFACTURER_REQ",
++      "EV_DL_ESTABLISH_IND",
++      "EV_DL_ESTABLISH_CONF",
++      "EV_DL_RELEASE_IND",
++      "EV_DL_RELEASE_CONF",
++      "EV_DL_DOWN_IND",
++      "EV_NC_LINKDOWN",
++      "EV_AP_RELEASE",
++};
++
++static struct Fsm ncci_fsm = { 0, 0, 0, 0, 0 };
++static struct Fsm ncciD_fsm = { 0, 0, 0, 0, 0 };
++
++
++static int
++select_NCCIaddr(Ncci_t *ncci) {
++      __u32   addr;
++      Ncci_t  *test;
++
++      if (!ncci->AppPlci)
++              return(-EINVAL);
++      addr = 0x00010000 | ncci->AppPlci->addr;
++      while (addr < 0x00ffffff) { /* OK not more as 255 NCCI */
++              test = getNCCI4addr(ncci->AppPlci, addr, GET_NCCI_EXACT);
++              if (!test) {
++                      ncci->addr = addr;
++#ifdef OLDCAPI_DRIVER_INTERFACE
++                      ncci->contr->ctrl->new_ncci(ncci->contr->ctrl, ncci->appl->ApplId, ncci->addr, ncci->window);
++#endif
++                      return(0);
++              }
++              addr += 0x00010000;
++      }
++      ncci->addr = ncci->AppPlci->addr;
++      return(-EBUSY);
++}
++
++static void
++ncci_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      char tmp[128];
++      char *p = tmp;
++      va_list args;
++      Ncci_t *ncci = fi->userdata;
++      
++      if (!ncci->ncci_m.debug)
++              return;
++      va_start(args, fmt);
++      p += sprintf(p, "NCCI 0x%x: ", ncci->addr);
++      p += vsprintf(p, fmt, args);
++      *p++ = '\n';
++      *p = 0;
++      printk(KERN_DEBUG "%s", tmp);
++      va_end(args);
++}
++
++static inline void
++SKB2Application(Ncci_t *ncci, struct sk_buff *skb)
++{
++      if (!test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) {
++#ifdef OLDCAPI_DRIVER_INTERFACE
++              ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, skb);
++#else
++              capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, skb);
++#endif
++      }
++}
++
++static inline int
++SKB_l4l3(Ncci_t *ncci, struct sk_buff *skb)
++{
++      if (!ncci->link || !ncci->link->inst.down.func)
++              return(-ENXIO);
++      return(ncci->link->inst.down.func(&ncci->link->inst.down, skb));
++}
++
++static inline void
++Send2Application(Ncci_t *ncci, _cmsg *cmsg)
++{
++      SendCmsg2Application(ncci->appl, cmsg);
++}
++
++static inline void
++ncciCmsgHeader(Ncci_t *ncci, _cmsg *cmsg, __u8 cmd, __u8 subcmd)
++{
++      capi_cmsg_header(cmsg, ncci->appl->ApplId, cmd, subcmd, 
++                       ncci->appl->MsgId++, ncci->addr);
++}
++
++static void
++ncci_connect_b3_req(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg = arg;
++
++      // FIXME
++      if (!ncci->appl) {
++              cmsg_free(cmsg);
++              return;
++      }
++      mISDN_FsmChangeState(fi, ST_NCCI_N_0_1);
++      capi_cmsg_answer(cmsg);
++
++      // TODO: NCPI handling
++      /* We need a real addr now */
++      if (0xffff0000 & ncci->addr) {
++              int_error();
++              cmsg->Info = CapiNoNcciAvailable;
++              ncci->addr = ncci->AppPlci->addr;
++      } else {
++              cmsg->Info = 0;
++              if (select_NCCIaddr(ncci)) {
++                      int_error();
++                      cmsg->Info = CapiNoNcciAvailable;
++              }
++      }
++      cmsg->adr.adrNCCI = ncci->addr;
++      ncci_debug(fi, "ncci_connect_b3_req NCCI %x cmsg->Info(%x)",
++              ncci->addr, cmsg->Info);
++      if (mISDN_FsmEvent(fi, EV_NC_CONNECT_B3_CONF, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++ncci_connect_b3_ind(struct FsmInst *fi, int event, void *arg)
++{
++      // from DL_ESTABLISH
++      mISDN_FsmChangeState(fi, ST_NCCI_N_1);
++      Send2Application(fi->userdata, arg);
++}
++
++static void
++ncci_connect_b3_resp(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t *ncci = fi->userdata;
++      _cmsg *cmsg = arg;
++  
++      // FIXME
++      if (!ncci->appl) {
++              cmsg_free(cmsg);
++              return;
++      }
++      if (cmsg->Info == 0) {
++              mISDN_FsmChangeState(fi, ST_NCCI_N_2);
++              ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3_ACTIVE, CAPI_IND);
++              event = EV_NC_CONNECT_B3_ACTIVE_IND;
++      } else {
++              mISDN_FsmChangeState(fi, ST_NCCI_N_4);
++              cmsg->Info = 0;
++              ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND);
++              event = EV_NC_DISCONNECT_B3_IND;
++      }
++      if (mISDN_FsmEvent(&ncci->ncci_m, event, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++ncci_connect_b3_conf(struct FsmInst *fi, int event, void *arg)
++{
++      _cmsg   *cmsg = arg;
++  
++      if (cmsg->Info == 0) {
++              mISDN_FsmChangeState(fi, ST_NCCI_N_2);
++              Send2Application(fi->userdata, cmsg);
++              ncciL4L3(fi->userdata, DL_ESTABLISH | REQUEST, 0, 0, NULL, NULL);
++      } else {
++              mISDN_FsmChangeState(fi, ST_NCCI_N_0);
++              Send2Application(fi->userdata, cmsg);
++              ncciDestr(fi->userdata);
++      }
++}
++
++static void
++ncci_disconnect_b3_req(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg = arg;
++      __u16   Info = 0;
++
++      if (ncci->appl) { //FIXME
++              /* TODO: handle NCPI and wait for all DATA_B3_REQ confirmed on
++               * related protocols (voice, T30)
++               */ 
++              capi_cmsg_answer(cmsg);
++              cmsg->Info = Info;
++              if (mISDN_FsmEvent(fi, EV_NC_DISCONNECT_B3_CONF, cmsg))
++                      cmsg_free(cmsg);
++      } else {
++              cmsg_free(cmsg);
++              mISDN_FsmChangeState(fi, ST_NCCI_N_4);
++      }
++      ncciL4L3(ncci, DL_RELEASE | REQUEST, 0, 0, NULL, NULL);
++}
++
++static void
++ncci_disconnect_b3_conf(struct FsmInst *fi, int event, void *arg)
++{
++      _cmsg   *cmsg = arg;
++
++      if (cmsg->Info == 0) {
++              mISDN_FsmChangeState(fi, ST_NCCI_N_4);
++      }
++      Send2Application(fi->userdata, cmsg);
++}
++
++static void
++ncci_disconnect_b3_ind(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_NCCI_N_5);
++      if (ncci->appl) { // FIXME
++              Send2Application(ncci, arg);
++      } else {
++              cmsg_free(arg);
++              mISDN_FsmChangeState(fi, ST_NCCI_N_0);
++              ncciDestr(ncci);
++      }
++}
++
++static void
++ncci_disconnect_b3_resp(struct FsmInst *fi, int event, void *arg)
++{
++      if (arg)
++              cmsg_free(arg);
++      mISDN_FsmChangeState(fi, ST_NCCI_N_0);
++      ncciDestr(fi->userdata);
++}
++
++static void
++ncci_facility_req(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg = arg;
++      u_char  *p = cmsg->FacilityRequestParameter;
++      u16     func;
++      int     op;
++
++      if (!ncci->appl)
++              return;
++      capi_cmsg_answer(cmsg);
++      cmsg->Info = CAPI_NOERROR;
++      if (cmsg->FacilitySelector == 0) { // Handset
++              int err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_ON, 0, NULL, NULL);
++              if (err)
++                      cmsg->Info = CapiFacilityNotSupported;
++      } else if (cmsg->FacilitySelector != 1) { // not DTMF
++              cmsg->Info = CapiIllMessageParmCoding;
++      } else if (p && p[0]) {
++              func = CAPIMSG_U16(p, 1);
++              ncci_debug(fi, "%s: p %02x %02x %02x func(%x)",
++                      __FUNCTION__, p[0], p[1], p[2], func);
++              switch (func) {
++                      case 1:
++                              op = DTMF_TONE_START;
++                              ncciL4L3(ncci, PH_CONTROL | REQUEST, 0, sizeof(int), &op, NULL);
++                              break;
++                      case 2:
++                              op = DTMF_TONE_STOP;
++                              ncciL4L3(ncci, PH_CONTROL | REQUEST, 0, sizeof(int), &op, NULL);
++                              break;
++                      default:
++                              cmsg->Info = CapiFacilityNotSupported;
++                              break;
++              }
++      } else
++              cmsg->Info = CapiIllMessageParmCoding;
++              
++      Send2Application(ncci, cmsg);
++}
++
++static void
++ncci_manufacturer_req(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg = arg;
++      int     err, op;
++      struct  _manu_conf_para {
++                      u8      len     __attribute__((packed));
++                      u16     Info    __attribute__((packed));
++                      u16     vol     __attribute__((packed));
++              } mcp = {2, CAPI_NOERROR,0};
++      struct  _manu_req_para {
++                      u8      len     __attribute__((packed));
++                      u16     vol     __attribute__((packed));
++              } *mrp;
++
++      if (!ncci->appl)
++              return;
++      mrp = (struct  _manu_req_para *)cmsg->ManuData;
++      capi_cmsg_answer(cmsg);
++      if (cmsg->Class == mISDN_MF_CLASS_HANDSET) { // Handset
++              switch(cmsg->Function) {
++                      case mISDN_MF_HANDSET_ENABLE:
++                              err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_ON, 0, NULL, NULL);
++                              if (err)
++                                      mcp.Info = CapiFacilityNotSupported;
++                              break;
++                      case mISDN_MF_HANDSET_DISABLE:
++                              err = ncciL4L3(ncci, PH_CONTROL | REQUEST, HW_POTS_OFF, 0, NULL, NULL);
++                              if (err)
++                                      mcp.Info = CapiFacilityNotSupported;
++                              break;
++                      case mISDN_MF_HANDSET_SETMICVOLUME:
++                      case mISDN_MF_HANDSET_SETSPKVOLUME:
++                              if (!mrp || mrp->len != 2) {
++                                      mcp.Info = CapiIllMessageParmCoding;
++                                      break;
++                              }
++                              op = (cmsg->Function == mISDN_MF_HANDSET_SETSPKVOLUME) ?
++                                      HW_POTS_SETSPKVOL : HW_POTS_SETMICVOL;
++                              err = ncciL4L3(ncci, PH_CONTROL | REQUEST, op, 2, &mrp->vol, NULL);
++                              if (err == -ENODEV)
++                                      mcp.Info = CapiFacilityNotSupported;
++                              else if (err)
++                                      mcp.Info = CapiIllMessageParmCoding;
++                              break;
++                      /* not handled yet */
++                      case mISDN_MF_HANDSET_GETMICVOLUME:
++                      case mISDN_MF_HANDSET_GETSPKVOLUME:
++                      default:
++                              mcp.Info = CapiFacilityNotSupported;
++                              break;
++              }
++      } else
++              mcp.Info = CapiIllMessageParmCoding;
++
++      cmsg->ManuData = (_cstruct)&mcp;
++      Send2Application(ncci, cmsg);
++}
++
++static void
++ncci_connect_b3_active_ind(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t *ncci = fi->userdata;
++      int i;
++
++      mISDN_FsmChangeState(fi, ST_NCCI_N_ACT);
++      for (i = 0; i < CAPI_MAXDATAWINDOW; i++) {
++              ncci->xmit_skb_handles[i].PktId = 0;
++              ncci->recv_skb_handles[i] = 0;
++      }
++      Send2Application(ncci, arg);
++}
++
++static void
++ncci_connect_b3_active_resp(struct FsmInst *fi, int event, void *arg)
++{
++      cmsg_free(arg);
++}
++
++static void
++ncci_n0_dl_establish_ind_conf(struct FsmInst *fi, int event, void *arg)
++{
++      _cmsg   *cmsg;
++      Ncci_t  *ncci = fi->userdata;
++
++      if (!ncci->appl)
++              return;
++      if (!(0xffff0000 & ncci->addr)) {
++              if (select_NCCIaddr(ncci)) {
++                      int_error();
++                      return;
++              }
++      } else {
++              int_error();
++              return;
++      }
++      CMSG_ALLOC(cmsg);
++      ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3, CAPI_IND);
++      if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_CONNECT_B3_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++ncci_dl_establish_conf(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg;
++
++      if (!ncci->appl)
++              return;
++      CMSG_ALLOC(cmsg);
++      ncciCmsgHeader(ncci, cmsg, CAPI_CONNECT_B3_ACTIVE, CAPI_IND);
++      if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_CONNECT_B3_ACTIVE_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++ncci_dl_release_ind_conf(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND);
++      if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++ncci_linkdown(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND);
++      if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++ncci_dl_down_ind(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND);
++      cmsg->Reason_B3 = CapiProtocolErrorLayer1;
++      if (mISDN_FsmEvent(&ncci->ncci_m, EV_NC_DISCONNECT_B3_IND, cmsg))
++              cmsg_free(cmsg);
++}
++
++static void
++ncci_appl_release(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_NCCI_N_0);
++      ncciDestr(fi->userdata);
++}
++
++static void
++ncci_appl_release_disc(struct FsmInst *fi, int event, void *arg)
++{
++      ncciL4L3(fi->userdata, DL_RELEASE | REQUEST, 0, 0, NULL, NULL);
++}
++
++static struct FsmNode fn_ncci_list[] =
++{
++  {ST_NCCI_N_0,               EV_AP_CONNECT_B3_REQ,           ncci_connect_b3_req},
++  {ST_NCCI_N_0,               EV_NC_CONNECT_B3_IND,           ncci_connect_b3_ind},
++  {ST_NCCI_N_0,               EV_DL_ESTABLISH_CONF,           ncci_n0_dl_establish_ind_conf},
++  {ST_NCCI_N_0,               EV_DL_ESTABLISH_IND,            ncci_n0_dl_establish_ind_conf},
++  {ST_NCCI_N_0,               EV_AP_RELEASE,                  ncci_appl_release},
++
++  {ST_NCCI_N_0_1,     EV_NC_CONNECT_B3_CONF,          ncci_connect_b3_conf},
++  {ST_NCCI_N_0_1,     EV_AP_MANUFACTURER_REQ,         ncci_manufacturer_req},
++  {ST_NCCI_N_0_1,     EV_AP_RELEASE,                  ncci_appl_release},
++
++  {ST_NCCI_N_1,               EV_AP_CONNECT_B3_RESP,          ncci_connect_b3_resp},
++  {ST_NCCI_N_1,               EV_AP_DISCONNECT_B3_REQ,        ncci_disconnect_b3_req},
++  {ST_NCCI_N_1,               EV_NC_DISCONNECT_B3_IND,        ncci_disconnect_b3_ind},
++  {ST_NCCI_N_1,               EV_AP_MANUFACTURER_REQ,         ncci_manufacturer_req},
++  {ST_NCCI_N_1,               EV_AP_RELEASE,                  ncci_appl_release_disc},
++  {ST_NCCI_N_1,               EV_NC_LINKDOWN,                 ncci_linkdown},
++
++  {ST_NCCI_N_2,               EV_NC_CONNECT_B3_ACTIVE_IND,    ncci_connect_b3_active_ind},
++  {ST_NCCI_N_2,               EV_AP_DISCONNECT_B3_REQ,        ncci_disconnect_b3_req},
++  {ST_NCCI_N_2,               EV_NC_DISCONNECT_B3_IND,        ncci_disconnect_b3_ind},
++  {ST_NCCI_N_2,               EV_DL_ESTABLISH_CONF,           ncci_dl_establish_conf},
++  {ST_NCCI_N_2,               EV_DL_RELEASE_IND,              ncci_dl_release_ind_conf},
++  {ST_NCCI_N_2,               EV_AP_MANUFACTURER_REQ,         ncci_manufacturer_req},
++  {ST_NCCI_N_2,               EV_AP_RELEASE,                  ncci_appl_release_disc},
++  {ST_NCCI_N_2,               EV_NC_LINKDOWN,                 ncci_linkdown},
++     
++#if 0
++  {ST_NCCI_N_3,               EV_NC_RESET_B3_IND,             ncci_reset_b3_ind},
++  {ST_NCCI_N_3,               EV_DL_DOWN_IND,                 ncci_dl_down_ind},
++  {ST_NCCI_N_3,               EV_AP_DISCONNECT_B3_REQ,        ncci_disconnect_b3_req},
++  {ST_NCCI_N_3,               EV_NC_DISCONNECT_B3_IND,        ncci_disconnect_b3_ind},
++  {ST_NCCI_N_3,               EV_AP_RELEASE,                  ncci_appl_release_disc},
++  {ST_NCCI_N_3,               EV_NC_LINKDOWN,                 ncci_linkdown},
++#endif
++
++  {ST_NCCI_N_ACT,     EV_AP_CONNECT_B3_ACTIVE_RESP,   ncci_connect_b3_active_resp},
++  {ST_NCCI_N_ACT,     EV_AP_DISCONNECT_B3_REQ,        ncci_disconnect_b3_req},
++  {ST_NCCI_N_ACT,     EV_NC_DISCONNECT_B3_IND,        ncci_disconnect_b3_ind},
++  {ST_NCCI_N_ACT,     EV_DL_RELEASE_IND,              ncci_dl_release_ind_conf},
++  {ST_NCCI_N_ACT,     EV_DL_RELEASE_CONF,             ncci_dl_release_ind_conf},
++  {ST_NCCI_N_ACT,     EV_DL_DOWN_IND,                 ncci_dl_down_ind},
++  {ST_NCCI_N_ACT,     EV_AP_FACILITY_REQ,             ncci_facility_req},
++  {ST_NCCI_N_ACT,     EV_AP_MANUFACTURER_REQ,         ncci_manufacturer_req},
++  {ST_NCCI_N_ACT,     EV_AP_RELEASE,                  ncci_appl_release_disc},
++  {ST_NCCI_N_ACT,     EV_NC_LINKDOWN,                 ncci_linkdown},
++#if 0
++  {ST_NCCI_N_ACT,     EV_AP_RESET_B3_REQ,             ncci_reset_b3_req},
++  {ST_NCCI_N_ACT,     EV_NC_RESET_B3_IND,             ncci_reset_b3_ind},
++  {ST_NCCI_N_ACT,     EV_NC_CONNECT_B3_T90_ACTIVE_IND,ncci_connect_b3_t90_active_ind},
++#endif
++
++  {ST_NCCI_N_4,               EV_NC_DISCONNECT_B3_CONF,       ncci_disconnect_b3_conf},
++  {ST_NCCI_N_4,               EV_NC_DISCONNECT_B3_IND,        ncci_disconnect_b3_ind},
++  {ST_NCCI_N_4,               EV_DL_RELEASE_CONF,             ncci_dl_release_ind_conf},
++  {ST_NCCI_N_4,               EV_DL_DOWN_IND,                 ncci_dl_down_ind},
++  {ST_NCCI_N_4,               EV_AP_MANUFACTURER_REQ,         ncci_manufacturer_req},
++  {ST_NCCI_N_4,               EV_NC_LINKDOWN,                 ncci_linkdown},
++
++  {ST_NCCI_N_5,               EV_AP_DISCONNECT_B3_RESP,       ncci_disconnect_b3_resp},
++  {ST_NCCI_N_5,               EV_AP_RELEASE,                  ncci_appl_release},
++};
++const int FN_NCCI_COUNT = sizeof(fn_ncci_list)/sizeof(struct FsmNode);
++
++static void
++ncciD_connect_b3_req(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_NCCI_N_0_1);
++      if (SKB_l4l3(fi->userdata, arg))
++              dev_kfree_skb(arg);
++}
++
++static void
++ncciD_connect_b3_conf(struct FsmInst *fi, int event, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      __u16           info = CAPIMSG_U16(skb->data, 12);
++
++      if (info == 0)
++              mISDN_FsmChangeState(fi, ST_NCCI_N_2);
++      else
++              mISDN_FsmChangeState(fi, ST_NCCI_N_0);
++      SKB2Application(fi->userdata, skb);
++      if (info != 0)
++              ncciDestr(fi->userdata);
++}
++
++static void
++ncciD_connect_b3_ind(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_NCCI_N_1);
++      SKB2Application(fi->userdata, arg);
++}
++
++static void
++ncciD_connect_b3_resp(struct FsmInst *fi, int event, void *arg)
++{
++      struct sk_buff  *skb = arg;
++      __u16           rej = CAPIMSG_U16(skb->data, 4);
++
++      if (rej)
++              mISDN_FsmChangeState(fi, ST_NCCI_N_4);
++      else
++              mISDN_FsmChangeState(fi, ST_NCCI_N_2);
++      if (SKB_l4l3(fi->userdata, arg))
++              dev_kfree_skb(arg);
++}
++
++static void
++ncciD_connect_b3_active_ind(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_NCCI_N_ACT);
++      SKB2Application(fi->userdata, arg);
++}
++
++static void
++ncciD_connect_b3_active_resp(struct FsmInst *fi, int event, void *arg)
++{
++      if (SKB_l4l3(fi->userdata, arg))
++              dev_kfree_skb(arg);
++}
++
++static void
++ncciD_reset_b3_ind(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_NCCI_N_ACT);
++      SKB2Application(fi->userdata, arg);
++}
++
++static void
++ncciD_reset_b3_resp(struct FsmInst *fi, int event, void *arg)
++{
++      if (SKB_l4l3(fi->userdata, arg))
++              dev_kfree_skb(arg);
++}
++
++static void
++ncciD_reset_b3_conf(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_NCCI_N_3);
++      SKB2Application(fi->userdata, arg);
++}
++
++static void
++ncciD_reset_b3_req(struct FsmInst *fi, int event, void *arg)
++{
++      SKB2Application(fi->userdata, arg);
++}
++
++static void
++ncciD_disconnect_b3_req(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++
++      ncci->savedstate = fi->state;
++      mISDN_FsmChangeState(fi, ST_NCCI_N_4);
++      if (SKB_l4l3(fi->userdata, arg))
++              dev_kfree_skb(arg);
++}
++
++static void
++ncciD_disconnect_b3_conf(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      struct sk_buff  *skb = arg;
++      __u16           info = CAPIMSG_U16(skb->data, 12);
++
++      if (test_bit(NCCI_STATE_APPLRELEASED, &ncci->state))
++              return;
++      if (info != 0)
++              mISDN_FsmChangeState(fi, ncci->savedstate);
++      SKB2Application(ncci, skb);
++}
++
++static void
++ncciD_disconnect_b3_ind(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      mISDN_FsmChangeState(fi, ST_NCCI_N_5);
++      if (test_bit(NCCI_STATE_APPLRELEASED, &ncci->state)) {
++              skb_pull(skb, CAPIMSG_BASELEN);
++              skb_trim(skb, 4);
++              if_newhead(&ncci->link->inst.down, CAPI_DISCONNECT_B3_RESP, 0, skb);
++              ncciDestr(ncci);
++      } else
++              SKB2Application(ncci, arg);
++}
++
++static void
++ncciD_disconnect_b3_resp(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_NCCI_N_0);
++      if (SKB_l4l3(fi->userdata, arg))
++              dev_kfree_skb(arg);
++      ncciDestr(fi->userdata);
++}
++
++static void
++ncciD_linkdown(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      _cmsg   *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      ncciCmsgHeader(ncci, cmsg, CAPI_DISCONNECT_B3, CAPI_IND);
++      mISDN_FsmChangeState(fi, ST_NCCI_N_5);
++      Send2Application(ncci, cmsg);
++}
++
++static void
++ncciD_appl_release_disc(struct FsmInst *fi, int event, void *arg)
++{
++      Ncci_t  *ncci = fi->userdata;
++      u_char  parm[5];
++
++      capimsg_setu32(parm, 0, ncci->addr);
++      parm[4] = 0;
++      mISDN_FsmChangeState(fi, ST_NCCI_N_4);
++      if_link(&ncci->link->inst.down, CAPI_DISCONNECT_B3_REQ, 0, 5, parm, 0); 
++}
++
++static struct FsmNode fn_ncciD_list[] =
++{
++  {ST_NCCI_N_0,               EV_AP_CONNECT_B3_REQ,           ncciD_connect_b3_req},
++  {ST_NCCI_N_0,               EV_NC_CONNECT_B3_IND,           ncciD_connect_b3_ind},
++  {ST_NCCI_N_0,               EV_AP_RELEASE,                  ncci_appl_release},
++
++  {ST_NCCI_N_0_1,     EV_NC_CONNECT_B3_CONF,          ncciD_connect_b3_conf},
++  {ST_NCCI_N_0_1,     EV_AP_RELEASE,                  ncci_appl_release},
++
++  {ST_NCCI_N_1,               EV_AP_CONNECT_B3_RESP,          ncciD_connect_b3_resp},
++  {ST_NCCI_N_1,               EV_AP_DISCONNECT_B3_REQ,        ncciD_disconnect_b3_req},
++  {ST_NCCI_N_1,               EV_NC_DISCONNECT_B3_IND,        ncciD_disconnect_b3_ind},
++  {ST_NCCI_N_1,               EV_AP_RELEASE,                  ncciD_appl_release_disc},
++  {ST_NCCI_N_1,               EV_NC_LINKDOWN,                 ncciD_linkdown},
++
++  {ST_NCCI_N_2,               EV_NC_CONNECT_B3_ACTIVE_IND,    ncciD_connect_b3_active_ind},
++  {ST_NCCI_N_2,               EV_AP_DISCONNECT_B3_REQ,        ncciD_disconnect_b3_req},
++  {ST_NCCI_N_2,               EV_NC_DISCONNECT_B3_IND,        ncciD_disconnect_b3_ind},
++  {ST_NCCI_N_2,               EV_AP_RELEASE,                  ncciD_appl_release_disc},
++  {ST_NCCI_N_2,               EV_NC_LINKDOWN,                 ncciD_linkdown},
++     
++  {ST_NCCI_N_3,               EV_NC_RESET_B3_IND,             ncciD_reset_b3_ind},
++  {ST_NCCI_N_3,               EV_AP_DISCONNECT_B3_REQ,        ncciD_disconnect_b3_req},
++  {ST_NCCI_N_3,               EV_NC_DISCONNECT_B3_IND,        ncciD_disconnect_b3_ind},
++  {ST_NCCI_N_3,               EV_AP_RELEASE,                  ncciD_appl_release_disc},
++  {ST_NCCI_N_3,               EV_NC_LINKDOWN,                 ncciD_linkdown},
++
++  {ST_NCCI_N_ACT,     EV_AP_CONNECT_B3_ACTIVE_RESP,   ncciD_connect_b3_active_resp},
++  {ST_NCCI_N_ACT,     EV_AP_DISCONNECT_B3_REQ,        ncciD_disconnect_b3_req},
++  {ST_NCCI_N_ACT,     EV_NC_DISCONNECT_B3_IND,        ncciD_disconnect_b3_ind},
++  {ST_NCCI_N_ACT,     EV_AP_RELEASE,                  ncciD_appl_release_disc},
++  {ST_NCCI_N_ACT,     EV_NC_LINKDOWN,                 ncciD_linkdown},
++  {ST_NCCI_N_ACT,     EV_AP_RESET_B3_REQ,             ncciD_reset_b3_req},
++  {ST_NCCI_N_ACT,     EV_NC_RESET_B3_IND,             ncciD_reset_b3_ind},
++  {ST_NCCI_N_ACT,     EV_NC_RESET_B3_CONF,            ncciD_reset_b3_conf},
++  {ST_NCCI_N_ACT,     EV_AP_RESET_B3_RESP,            ncciD_reset_b3_resp},
++//{ST_NCCI_N_ACT,     EV_NC_CONNECT_B3_T90_ACTIVE_IND,ncciD_connect_b3_t90_active_ind},
++
++  {ST_NCCI_N_4,               EV_NC_DISCONNECT_B3_CONF,       ncciD_disconnect_b3_conf},
++  {ST_NCCI_N_4,               EV_NC_DISCONNECT_B3_IND,        ncciD_disconnect_b3_ind},
++  {ST_NCCI_N_4,               EV_NC_LINKDOWN,                 ncciD_linkdown},
++
++  {ST_NCCI_N_5,               EV_AP_DISCONNECT_B3_RESP,       ncciD_disconnect_b3_resp},
++  {ST_NCCI_N_5,               EV_AP_RELEASE,                  ncci_appl_release},
++};
++const int FN_NCCID_COUNT = sizeof(fn_ncciD_list)/sizeof(struct FsmNode);
++
++Ncci_t *
++ncciConstr(AppPlci_t *aplci)
++{
++      Ncci_t  *ncci = ncci_alloc();
++
++      if (!ncci)
++              return(NULL);
++
++      memset(ncci, 0, sizeof(Ncci_t));
++      ncci->ncci_m.state      = ST_NCCI_N_0;
++      ncci->ncci_m.debug      = aplci->plci->contr->debug & CAPI_DBG_NCCI_STATE;
++      ncci->ncci_m.userdata   = ncci;
++      ncci->ncci_m.printdebug = ncci_debug;
++      /* unused NCCI */
++      ncci->addr = aplci->addr;
++      ncci->AppPlci = aplci;
++      ncci->link = aplci->link;
++      ncci->contr = aplci->contr;
++      ncci->appl = aplci->appl;
++      ncci->window = aplci->appl->reg_params.datablkcnt;
++      if (aplci->Bprotocol.B2 != 0) /* X.75 has own flowctrl */
++              test_and_set_bit(NCCI_STATE_FCTRL, &ncci->state);
++      if (aplci->Bprotocol.B3 == 0) {
++              test_and_set_bit(NCCI_STATE_L3TRANS, &ncci->state);
++              ncci->ncci_m.fsm = &ncci_fsm;
++      } else
++              ncci->ncci_m.fsm = &ncciD_fsm;
++      skb_queue_head_init(&ncci->squeue);
++      if (ncci->window > CAPI_MAXDATAWINDOW) {
++              ncci->window = CAPI_MAXDATAWINDOW;
++      }
++      INIT_LIST_HEAD(&ncci->head);
++      list_add(&ncci->head, &aplci->Nccis);
++      if (ncci->ncci_m.debug)
++              printk(KERN_DEBUG "%s: ncci(%p) NCCI(%x) debug (%x/%x)\n",
++                      __FUNCTION__, ncci, ncci->addr, aplci->plci->contr->debug, CAPI_DBG_NCCI_STATE); 
++      return(ncci);
++}
++
++void
++ncciDestr(Ncci_t *ncci)
++{
++      int i;
++
++      capidebug(CAPI_DBG_NCCI, "ncciDestr NCCI %x", ncci->addr);
++
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      if (!test_bit(NCCI_STATE_APPLRELEASED, &ncci->state))
++              ncci->contr->ctrl->free_ncci(ncci->contr->ctrl, ncci->appl->ApplId, ncci->addr);
++#endif
++      /* cleanup data queues */
++      discard_queue(&ncci->squeue);
++      for (i = 0; i < ncci->window; i++) {
++              if (ncci->xmit_skb_handles[i].PktId)
++                      ncci->xmit_skb_handles[i].PktId = 0;
++      }
++      AppPlciDelNCCI(ncci);
++      ncci_free(ncci);
++}
++
++void
++ncciApplRelease(Ncci_t *ncci)
++{
++      test_and_set_bit(NCCI_STATE_APPLRELEASED, &ncci->state);
++      mISDN_FsmEvent(&ncci->ncci_m, EV_AP_RELEASE, NULL);
++}
++
++void
++ncciDelAppPlci(Ncci_t *ncci)
++{
++      printk(KERN_DEBUG "%s: ncci(%p) NCCI(%x)\n",
++              __FUNCTION__, ncci, ncci->addr); 
++      ncci->AppPlci = NULL;
++      /* maybe we should release the NCCI here */
++}
++
++void
++ncciReleaseLink(Ncci_t *ncci)
++{
++      /* this is normal shutdown on speech and other transparent protocols */
++      mISDN_FsmEvent(&ncci->ncci_m, EV_NC_LINKDOWN, NULL);
++}
++
++void
++ncciDataInd(Ncci_t *ncci, int pr, struct sk_buff *skb)
++{
++      struct sk_buff *nskb;
++      int i;
++
++      for (i = 0; i < CAPI_MAXDATAWINDOW; i++) {
++              if (ncci->recv_skb_handles[i] == 0)
++                      break;
++      }
++
++      if (i == CAPI_MAXDATAWINDOW) {
++              // FIXME: trigger flow control if supported by L2 protocol
++              printk(KERN_DEBUG "%s: frame %d dropped\n", __FUNCTION__, skb->len);
++              dev_kfree_skb(skb);
++              return;
++      }
++
++      if (skb_headroom(skb) < CAPI_B3_DATA_IND_HEADER_SIZE) {
++              capidebug(CAPI_DBG_NCCI_L3, "%s: only %d bytes headroom, need %d",
++                      __FUNCTION__, skb_headroom(skb), CAPI_B3_DATA_IND_HEADER_SIZE);
++              nskb = skb_realloc_headroom(skb, CAPI_B3_DATA_IND_HEADER_SIZE);
++              dev_kfree_skb(skb);
++              if (!nskb) {
++                      int_error();
++                      return;
++              }
++              } else { 
++              nskb = skb;
++      }
++      ncci->recv_skb_handles[i] = nskb;
++
++      skb_push(nskb, CAPI_B3_DATA_IND_HEADER_SIZE);
++      *((__u16*) nskb->data) = CAPI_B3_DATA_IND_HEADER_SIZE;
++      *((__u16*)(nskb->data+2)) = ncci->appl->ApplId;
++      *((__u8*) (nskb->data+4)) = CAPI_DATA_B3;
++      *((__u8*) (nskb->data+5)) = CAPI_IND;
++      *((__u16*)(nskb->data+6)) = ncci->appl->MsgId++;
++      *((__u32*)(nskb->data+8)) = ncci->addr;
++      if (sizeof(nskb) == 4) {
++              *((__u32*)(nskb->data+12)) = (__u32)(nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE);
++              *((__u64*)(nskb->data+22)) = 0;
++      } else {
++              *((__u32*)(nskb->data+12)) = 0;
++              *((__u64*)(nskb->data+22)) = (u_long)(nskb->data + CAPI_B3_DATA_IND_HEADER_SIZE);
++      }
++      *((__u16*)(nskb->data+16)) = nskb->len - CAPI_B3_DATA_IND_HEADER_SIZE;
++      *((__u16*)(nskb->data+18)) = i;
++      // FIXME FLAGS
++      *((__u16*)(nskb->data+20)) = 0;
++#ifdef OLDCAPI_DRIVER_INTERFACE
++      ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, nskb);
++#else
++      capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, nskb);
++#endif
++}
++
++__u16
++ncciDataReq(Ncci_t *ncci, struct sk_buff *skb)
++{
++      int     i, err;
++      __u16   len, capierr = 0;
++      _cmsg   *cmsg;
++      
++      len = CAPIMSG_LEN(skb->data);
++      if (len != 22 && len != 30) {
++              capierr = CapiIllMessageParmCoding;
++              int_error();
++              goto fail;
++      }
++      for (i = 0; i < ncci->window; i++) {
++              if (ncci->xmit_skb_handles[i].PktId == 0)
++                      break;
++      }
++      if (i == ncci->window) {
++              return(CAPI_SENDQUEUEFULL);
++      }
++      mISDN_HEAD_DINFO(skb) = ControllerNextId(ncci->contr);
++      ncci->xmit_skb_handles[i].PktId = mISDN_HEAD_DINFO(skb);
++      ncci->xmit_skb_handles[i].DataHandle = CAPIMSG_REQ_DATAHANDLE(skb->data);
++      ncci->xmit_skb_handles[i].MsgId = CAPIMSG_MSGID(skb->data);
++
++      /* the data begins behind the header, we don't use Data32/Data64 here */
++      skb_pull(skb, len);
++
++      if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) {
++              if (test_and_set_bit(NCCI_STATE_BUSY, &ncci->state)) {
++                      skb_queue_tail(&ncci->squeue, skb);
++                      return(CAPI_NOERROR);
++              }
++              if (skb_queue_len(&ncci->squeue)) {
++                      skb_queue_tail(&ncci->squeue, skb);
++                      skb = skb_dequeue(&ncci->squeue);
++                      i = -1;
++              }
++      }
++      
++      err = ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb), 0, NULL, skb);
++      if (!err)
++              return(CAPI_NOERROR);
++
++      int_error();
++      skb_push(skb, len);
++      capierr = CAPI_MSGBUSY;
++      if (i == -1) {
++              for (i = 0; i < ncci->window; i++) {
++                      if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb))
++                              break;
++              }
++              if (i == ncci->window)
++                      int_error();
++              else
++                      ncci->xmit_skb_handles[i].PktId = 0;
++      } else {
++              ncci->xmit_skb_handles[i].PktId = 0;
++              return(capierr);
++      }
++fail:
++      cmsg = cmsg_alloc();
++      if (!cmsg) {
++              int_error();
++              if (capierr != CAPI_MSGBUSY)
++                      return(CAPI_MSGOSRESOURCEERR);
++              /* we can not do error handling on a skb from the queue here */ 
++              dev_kfree_skb(skb);
++              return(CAPI_NOERROR);
++      }
++      capi_cmsg_header(cmsg, ncci->AppPlci->appl->ApplId, CAPI_DATA_B3, CAPI_CONF, 
++              CAPIMSG_MSGID(skb->data), ncci->addr);
++      /* illegal len (too short) ??? */
++      cmsg->DataHandle = CAPIMSG_REQ_DATAHANDLE(skb->data);
++      cmsg->Info = capierr;
++      Send2Application(ncci, cmsg);
++      dev_kfree_skb(skb);
++      return(CAPI_NOERROR);
++}
++
++int
++ncciDataConf(Ncci_t *ncci, int pr, struct sk_buff *skb)
++{
++      int     i;
++      _cmsg   *cmsg;
++
++      for (i = 0; i < ncci->window; i++) {
++              if (ncci->xmit_skb_handles[i].PktId == mISDN_HEAD_DINFO(skb))
++                      break;
++      }
++      if (i == ncci->window) {
++              int_error();
++              return(-EINVAL);
++      }
++      ncci->xmit_skb_handles[i].PktId = 0;
++      capidebug(CAPI_DBG_NCCI_L3, "%s: entry %d/%d handle (%x)",
++              __FUNCTION__, i, ncci->window, ncci->xmit_skb_handles[i].DataHandle);
++
++      cmsg = cmsg_alloc();
++      if (!cmsg) {
++              int_error();
++              return(-ENOMEM);
++      }
++      dev_kfree_skb(skb);
++      capi_cmsg_header(cmsg, ncci->AppPlci->appl->ApplId, CAPI_DATA_B3, CAPI_CONF, 
++                       ncci->xmit_skb_handles[i].MsgId, ncci->addr);
++      cmsg->DataHandle = ncci->xmit_skb_handles[i].DataHandle;
++      cmsg->Info = 0;
++      Send2Application(ncci, cmsg);
++      if (test_bit(NCCI_STATE_FCTRL, &ncci->state)) {
++              if (skb_queue_len(&ncci->squeue)) {
++                      skb = skb_dequeue(&ncci->squeue);
++                      if (ncciL4L3(ncci, DL_DATA | REQUEST, mISDN_HEAD_DINFO(skb),
++                              0, NULL, skb)) {
++                              int_error();
++                              dev_kfree_skb(skb);
++                      }
++              } else
++                      test_and_clear_bit(NCCI_STATE_BUSY, &ncci->state);
++      }
++      return(0);
++}     
++      
++void
++ncciDataResp(Ncci_t *ncci, struct sk_buff *skb)
++{
++      // FIXME: incoming flow control doesn't work yet
++
++      int i;
++
++      i = CAPIMSG_RESP_DATAHANDLE(skb->data);
++      if (i < 0 || i > CAPI_MAXDATAWINDOW) {
++              int_error();
++              return;
++      }
++      if (!ncci->recv_skb_handles[i]) {
++              int_error();
++              return;
++      }
++      ncci->recv_skb_handles[i] = 0;
++
++      dev_kfree_skb(skb);
++}
++
++int
++ncci_l4l3_direct(Ncci_t *ncci, struct sk_buff *skb) {
++      mISDN_head_t    *hh;
++      int             ret;
++
++      hh = mISDN_HEAD_P(skb);
++      if (ncci->ncci_m.debug)
++              log_skbdata(skb);
++      hh->prim = CAPIMSG_CMD(skb->data);
++      hh->dinfo = CAPIMSG_MSGID(skb->data);
++      skb_pull(skb, CAPIMSG_BASELEN);
++      if (ncci->ncci_m.debug)
++              log_skbdata(skb);
++      switch (hh->prim) {
++              case CAPI_DATA_B3_REQ:
++              case CAPI_DATA_B3_RESP:
++              case CAPI_FACILITY_REQ:
++              case CAPI_FACILITY_RESP:
++              case CAPI_MANUFACTURER_REQ:
++              case CAPI_MANUFACTURER_RESP:
++                      return(SKB_l4l3(ncci, skb));
++              case CAPI_CONNECT_B3_REQ:
++                      ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_REQ, skb);
++                      break;
++              case CAPI_CONNECT_B3_RESP:
++                      ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_RESP, skb);
++                      break;
++              case CAPI_CONNECT_B3_ACTIVE_RESP:
++                      ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_ACTIVE_RESP, skb);
++                      break;
++              case CAPI_DISCONNECT_B3_REQ:
++                      ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_REQ, skb);
++                      break;
++              case CAPI_DISCONNECT_B3_RESP:
++                      ret = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_RESP, skb);
++                      break;
++              default:
++                      int_error();
++                      ret = -1;
++      }
++      if (ret) {
++              int_error();
++              dev_kfree_skb(skb);
++      }
++      return(0);
++}
++
++void
++ncciGetCmsg(Ncci_t *ncci, _cmsg *cmsg)
++{
++      int     retval = 0;
++
++      if (!test_bit(NCCI_STATE_L3TRANS, &ncci->state)) {
++              int_error();
++              cmsg_free(cmsg);
++              return;
++      }
++      switch (CMSGCMD(cmsg)) {
++              case CAPI_CONNECT_B3_REQ:
++                      retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_REQ, cmsg);
++                      break;
++              case CAPI_CONNECT_B3_RESP:
++                      retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_RESP, cmsg);
++                      break;
++              case CAPI_CONNECT_B3_ACTIVE_RESP:
++                      retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_CONNECT_B3_ACTIVE_RESP, cmsg);
++                      break;
++              case CAPI_DISCONNECT_B3_REQ:
++                      retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_REQ, cmsg);
++                      break;
++              case CAPI_DISCONNECT_B3_RESP:
++                      retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_DISCONNECT_B3_RESP, cmsg);
++                      break;
++              case CAPI_FACILITY_REQ:
++                      retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_FACILITY_REQ, cmsg);
++                      break;
++              case CAPI_MANUFACTURER_REQ:
++                      retval = mISDN_FsmEvent(&ncci->ncci_m, EV_AP_MANUFACTURER_REQ, cmsg);
++                      break;
++              default:
++                      int_error();
++                      retval = -1;
++      }
++      if (retval) { 
++              if (cmsg->Command == CAPI_REQ) {
++                      capi_cmsg_answer(cmsg);
++                      cmsg->Info = CapiMessageNotSupportedInCurrentState;
++                      Send2Application(ncci, cmsg);
++              } else
++                      cmsg_free(cmsg);
++      }
++}
++
++__u16
++ncciSendMessage(Ncci_t *ncci, struct sk_buff *skb)
++{
++      int     ret;
++      _cmsg   *cmsg;
++
++      if (!test_bit(NCCI_STATE_L3TRANS, &ncci->state)) {
++              ret = ncci_l4l3_direct(ncci, skb);
++              switch(ret) {
++                      case 0:
++                              break;
++                      case -EINVAL:
++                      case -ENXIO:
++                              return(CAPI_MSGBUSY);
++                      case -EXFULL:
++                              return(CAPI_SENDQUEUEFULL);
++                      default:
++                              int_errtxt("ncci_l4l3_direct return(%d)", ret);
++                              dev_kfree_skb(skb);
++                              break;
++              }
++              return(CAPI_NOERROR);
++      }
++      // we're not using the cmsg for DATA_B3 for performance reasons
++      switch (CAPIMSG_CMD(skb->data)) {
++              case CAPI_DATA_B3_REQ:
++                      if (ncci->ncci_m.state == ST_NCCI_N_ACT) {
++                              return(ncciDataReq(ncci, skb));
++                      } else {
++                              AnswerMessage2Application(ncci->appl, skb, 
++                                      CapiMessageNotSupportedInCurrentState);
++                              dev_kfree_skb(skb);
++                      }
++                      return(CAPI_NOERROR);
++              case CAPI_DATA_B3_RESP:
++                      ncciDataResp(ncci, skb);
++                      return(CAPI_NOERROR);
++      }
++      cmsg = cmsg_alloc();
++      if (!cmsg) {
++              int_error();
++              return(CAPI_MSGOSRESOURCEERR);
++      }
++      capi_message2cmsg(cmsg, skb->data);
++      ncciGetCmsg(ncci, cmsg);
++      dev_kfree_skb(skb);
++      return(CAPI_NOERROR);
++}
++
++int
++ncci_l3l4_direct(Ncci_t *ncci, mISDN_head_t *hh, struct sk_buff *skb)
++{
++      __u16           msgnr;
++      int             event;
++
++      capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dinfo (%x) skb(%p) s(%x)",
++              __FUNCTION__, ncci->addr, hh->prim, hh->dinfo, skb, ncci->state);
++      if (ncci->ncci_m.debug)
++              log_skbdata(skb);
++      switch (hh->prim & 0xFF) {
++              case CAPI_IND:
++                      msgnr = ncci->appl->MsgId++;
++                      break;
++              case CAPI_CONF:
++                      msgnr = hh->dinfo & 0xffff;
++                      break;
++              default:
++                      int_error();
++                      return(-EINVAL);
++      }
++      if (skb_headroom(skb) < CAPIMSG_BASELEN) {
++              capidebug(CAPI_DBG_NCCI_L3, "%s: only %d bytes headroom, need %d",
++                      __FUNCTION__, skb_headroom(skb), CAPIMSG_BASELEN);
++              int_error();
++              return(-ENOSPC);
++      }
++      skb_push(skb, CAPIMSG_BASELEN);
++      CAPIMSG_SETLEN(skb->data, (hh->prim == CAPI_DATA_B3_IND) ?
++              CAPI_B3_DATA_IND_HEADER_SIZE : skb->len);
++      CAPIMSG_SETAPPID(skb->data, ncci->appl->ApplId);
++      CAPIMSG_SETCOMMAND(skb->data, (hh->prim>>8) & 0xff);
++      CAPIMSG_SETSUBCOMMAND(skb->data, hh->prim & 0xff); 
++      CAPIMSG_SETMSGID(skb->data, msgnr);
++      switch (hh->prim) {
++              case CAPI_DATA_B3_IND:
++              case CAPI_DATA_B3_CONF:
++              case CAPI_FACILITY_IND:
++              case CAPI_FACILITY_CONF:
++              case CAPI_MANUFACTURER_IND:
++              case CAPI_MANUFACTURER_CONF:
++#ifdef OLDCAPI_DRIVER_INTERFACE
++                      ncci->contr->ctrl->handle_capimsg(ncci->contr->ctrl, ncci->appl->ApplId, skb);
++#else
++                      capi_ctr_handle_message(ncci->contr->ctrl, ncci->appl->ApplId, skb);
++#endif
++                      return(0);
++              case CAPI_CONNECT_B3_IND:
++                      event = EV_NC_CONNECT_B3_IND;
++                      break;
++              case CAPI_CONNECT_B3_ACTIVE_IND:
++                      event = EV_NC_CONNECT_B3_ACTIVE_IND;
++                      break;
++              case CAPI_DISCONNECT_B3_IND:
++                      event = EV_NC_DISCONNECT_B3_IND;
++                      break;
++              case CAPI_RESET_B3_IND:
++                      event = EV_NC_RESET_B3_IND;
++                      break;
++              case CAPI_CONNECT_B3_CONF:
++                      event = EV_NC_CONNECT_B3_CONF;
++                      break;
++              case CAPI_DISCONNECT_B3_CONF:
++                      event = EV_NC_DISCONNECT_B3_CONF;
++                      break;
++              case CAPI_RESET_B3_CONF:
++                      event = EV_NC_RESET_B3_CONF;
++                      break;
++              default:
++                      int_error();
++                      return(-EINVAL);
++      }
++      if (mISDN_FsmEvent(&ncci->ncci_m, event, skb))
++              dev_kfree_skb(skb);
++      return(0);
++}
++
++int
++ncci_l3l4(Ncci_t *ncci, mISDN_head_t *hh, struct sk_buff *skb)
++{
++      capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dinfo (%x) skb(%p) s(%x)",
++              __FUNCTION__, ncci->addr, hh->prim, hh->dinfo, skb, ncci->state);
++      switch (hh->prim) {
++              // we're not using the Fsm for DL_DATA for performance reasons
++              case DL_DATA | INDICATION: 
++                      if (ncci->ncci_m.state == ST_NCCI_N_ACT) {
++                              ncciDataInd(ncci, hh->prim, skb);
++                              return(0);
++                      } 
++                      break;
++              case DL_DATA | CONFIRM:
++                      if (ncci->ncci_m.state == ST_NCCI_N_ACT) {
++                              return(ncciDataConf(ncci, hh->prim, skb));
++                      }
++                      break;
++              case DL_ESTABLISH | INDICATION:
++                      mISDN_FsmEvent(&ncci->ncci_m, EV_DL_ESTABLISH_IND, skb);
++                      break;
++              case DL_ESTABLISH | CONFIRM:
++                      mISDN_FsmEvent(&ncci->ncci_m, EV_DL_ESTABLISH_CONF, skb);
++                      break;
++              case DL_RELEASE | INDICATION:
++                      mISDN_FsmEvent(&ncci->ncci_m, EV_DL_RELEASE_IND, skb);
++                      break;
++              case DL_RELEASE | CONFIRM:
++                      mISDN_FsmEvent(&ncci->ncci_m, EV_DL_RELEASE_CONF, skb);
++                      break;
++              case PH_CONTROL | INDICATION: /* e.g touch tones */
++                      /* handled by AppPlci */
++                      AppPlci_l3l4(ncci->AppPlci, hh->prim, skb->data);
++                      break;
++              default:
++                      capidebug(CAPI_DBG_WARN, "%s: unknown prim(%x) dinfo(%x) len(%d) skb(%p)",
++                              __FUNCTION__, hh->prim, hh->dinfo, skb->len, skb);
++                      int_error();
++                      return(-EINVAL);
++      }
++      dev_kfree_skb(skb);
++      return(0);
++}
++
++static int
++ncciL4L3(Ncci_t *ncci, u_int prim, int dtyp, int len, void *arg, struct sk_buff *skb)
++{
++      capidebug(CAPI_DBG_NCCI_L3, "%s: NCCI %x prim(%x) dtyp(%x) len(%d) skb(%p)",
++              __FUNCTION__, ncci->addr, prim, dtyp, len, skb);
++      if (skb)
++              return(if_newhead(&ncci->link->inst.down, prim, dtyp, skb));
++      else
++              return(if_link(&ncci->link->inst.down, prim, dtyp,
++                      len, arg, 8));
++}
++
++void
++init_ncci(void)
++{
++      ncci_fsm.state_count = ST_NCCI_COUNT;
++      ncci_fsm.event_count = EV_NCCI_COUNT;
++      ncci_fsm.strEvent = str_ev_ncci;
++      ncci_fsm.strState = str_st_ncci;
++      mISDN_FsmNew(&ncci_fsm, fn_ncci_list, FN_NCCI_COUNT);
++
++      ncciD_fsm.state_count = ST_NCCI_COUNT;
++      ncciD_fsm.event_count = EV_NCCI_COUNT;
++      ncciD_fsm.strEvent = str_ev_ncci;
++      ncciD_fsm.strState = str_st_ncci;
++      mISDN_FsmNew(&ncciD_fsm, fn_ncciD_list, FN_NCCID_COUNT);
++}
++
++void
++free_ncci(void)
++{
++      mISDN_FsmFree(&ncci_fsm);
++      mISDN_FsmFree(&ncciD_fsm);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/plci.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/plci.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/plci.c       1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/plci.c   2004-11-22 09:33:38.392707336 +0000
+@@ -0,0 +1,154 @@
++/* $Id$
++ *
++ */
++
++#include "m_capi.h"
++#include "dss1.h"
++#include "helper.h"
++#include "debug.h"
++
++#define plciDebug(plci, lev, fmt, args...) \
++        capidebug(lev, fmt, ## args)
++
++
++void plciInit(Controller_t *contr)
++{
++      Plci_t  *plci = contr->plcis;
++      int     i;
++
++      for (i = 0; i < contr->maxplci; i++) {
++              memset(plci, 0, sizeof(Plci_t));
++              plci->addr = ((i + 1) << 8) | contr->addr;
++              plci->l3id = MISDN_ID_NONE;
++              INIT_LIST_HEAD(&plci->AppPlcis);
++              plci->contr = contr;
++              if (contr->debug & CAPI_DBG_PLCI)
++                      printk(KERN_DEBUG "%s: %p PLCI(%x) l3id(%x)\n",
++                              __FUNCTION__, plci, plci->addr, plci->l3id);
++              plci++;
++      }
++}
++
++void plciHandleSetupInd(Plci_t *plci, int pr, Q931_info_t *qi)
++{
++      __u16                   CIPValue;
++      Application_t           *appl;
++      AppPlci_t               *aplci;
++      struct list_head        *item, *next;
++
++      if (!qi || !plci->contr) {
++              int_error();
++              return;
++      }
++      CIPValue = q931CIPValue(qi);
++      list_for_each_safe(item, next, &plci->contr->Applications) {
++              appl = (Application_t *)item;
++              if (test_bit(APPL_STATE_RELEASE, &appl->state))
++                      continue;
++              if (listenHandle(appl, CIPValue)) {
++                      aplci = ApplicationNewAppPlci(appl, plci);
++                      if (!aplci) {
++                              int_error();
++                              break;
++                      }
++                      AppPlci_l3l4(aplci, pr, qi);
++              }
++      }
++      if (plci->nAppl == 0) {
++              struct sk_buff *skb = mISDN_alloc_l3msg(10, MT_RELEASE_COMPLETE);
++              u_char cause[4] = {IE_CAUSE,2,0x80,0xd8}; /* incompatible destination */
++
++              if (skb) {
++                      mISDN_AddvarIE(skb,cause);
++                      plciL4L3(plci, CC_RELEASE_COMPLETE | REQUEST, skb);
++              }
++              ControllerReleasePlci(plci);
++      }
++}
++
++int plci_l3l4(Plci_t *plci, int pr, struct sk_buff *skb)
++{
++      AppPlci_t               *aplci;
++      Q931_info_t             *qi;
++      struct list_head        *item, *next;
++
++      if (skb->len)
++              qi = (Q931_info_t *)skb->data;
++      else
++              qi = NULL;
++      switch (pr) {
++              case CC_SETUP | INDICATION:
++                      plciHandleSetupInd(plci, pr, qi);
++                      break;
++              case CC_RELEASE_CR | INDICATION:
++                      break;
++              default:
++                      list_for_each_safe(item, next, &plci->AppPlcis) {
++                              aplci = (AppPlci_t *)item;
++                              AppPlci_l3l4(aplci, pr, qi);
++                      }
++                      break;
++      }
++      dev_kfree_skb(skb);
++      return(0);
++}
++
++AppPlci_t *
++getAppPlci4Id(Plci_t *plci, __u16 appId) {
++      struct list_head        *item;
++      AppPlci_t               *aplci;
++
++      list_for_each(item, &plci->AppPlcis) {
++              aplci = (AppPlci_t *)item;
++              if (appId == aplci->appl->ApplId)
++                      return(aplci);
++      }
++      return(NULL);
++}
++
++void plciAttachAppPlci(Plci_t *plci, AppPlci_t *aplci)
++{
++      AppPlci_t       *test = getAppPlci4Id(plci, aplci->appl->ApplId);
++
++      if (test) {
++              int_error();
++              return;
++      }
++      list_add(&aplci->head, &plci->AppPlcis);
++      plci->nAppl++;
++}
++
++void
++plciDetachAppPlci(Plci_t *plci, AppPlci_t *aplci)
++{
++      aplci->plci = NULL;
++      list_del_init(&aplci->head);
++      plci->nAppl--;
++      if (!plci->nAppl)
++              ControllerReleasePlci(plci);
++}
++
++void plciNewCrReq(Plci_t *plci)
++{
++      plciL4L3(plci, CC_NEW_CR | REQUEST, NULL);
++}
++
++int
++plciL4L3(Plci_t *plci, __u32 prim, struct sk_buff *skb)
++{
++#define       MY_RESERVE      8
++      int     err;
++
++      if (!skb) {
++              if (!(skb = alloc_skb(MY_RESERVE, GFP_ATOMIC))) {
++                      printk(KERN_WARNING "%s: no skb size %d\n",
++                              __FUNCTION__, MY_RESERVE);
++                      return(-ENOMEM);
++              } else
++                      skb_reserve(skb, MY_RESERVE);
++      }
++      err = ControllerL4L3(plci->contr, prim, plci->l3id, skb);
++      if (err)
++              dev_kfree_skb(skb);
++      return(err);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/sedl_fax.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/sedl_fax.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/sedl_fax.c   1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/sedl_fax.c       2004-11-22 09:33:38.402705816 +0000
+@@ -0,0 +1,1009 @@
++/* $Id$
++ *
++ * sedl_fax.c  low level stuff for Sedlbauer Speedfax + cards
++ *
++ * Copyright  (C) 2000,2001 Karsten Keil (kkeil@suse.de)
++ *
++ * Author     Karsten Keil (kkeil@suse.de)
++ *
++ *
++ * Thanks to  Sedlbauer AG for informations
++ *            Marcus Niemann
++ *            Edgar Toernig
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++/* Supported cards:
++ * Card:      Chip:           Configuration:  Comment:
++ * ---------------------------------------------------------------------
++ * Speed Fax+ ISAC_ISAR       ISAPNP          Full analog support
++ * Speed Fax+         ISAC_ISAR       PCI PNP         Full analog support
++ *
++ * Important:
++ * For the sedlbauer speed fax+ to work properly you have to download 
++ * the firmware onto the card.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <asm/semaphore.h>
++#ifdef NEW_ISAPNP
++#include <linux/pnp.h>
++#else
++#include <linux/isapnp.h>
++#endif
++#include "dchannel.h"
++#include "bchannel.h"
++#include "isac.h"
++#include "isar.h"
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++
++#define SPIN_DEBUG
++#define LOCK_STATISTIC
++#include "hw_lock.h"
++
++extern const char *CardType[];
++
++const char *Sedlfax_revision = "$Revision$";
++
++const char *Sedlbauer_Types[] =
++      {"None", "speed fax+", "speed fax+ pyramid", "speed fax+ pci"};
++
++#ifndef PCI_VENDOR_ID_TIGERJET
++#define PCI_VENDOR_ID_TIGERJET                0xe159
++#endif
++#ifndef PCI_DEVICE_ID_TIGERJET_100
++#define PCI_DEVICE_ID_TIGERJET_100    0x0002
++#endif
++#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID        0x51
++#define PCI_SUBVENDOR_SPEEDFAX_PCI    0x54
++#define PCI_SUB_ID_SEDLBAUER          0x01
++ 
++#define SEDL_SPEEDFAX_ISA     1
++#define SEDL_SPEEDFAX_PYRAMID 2
++#define SEDL_SPEEDFAX_PCI     3
++
++#define byteout(addr,val) outb(val,addr)
++#define bytein(addr) inb(addr)
++
++#define SEDL_ISA_ISAC 4
++#define SEDL_ISA_ISAR 6
++#define SEDL_ISA_ADR  8
++#define SEDL_ISA_RESET_ON     10
++#define SEDL_ISA_RESET_OFF    12
++
++#define SEDL_PCI_ADR  0xc8
++#define SEDL_PCI_ISAC 0xd0
++#define SEDL_PCI_ISAR 0xe0
++
++/* TIGER 100 Registers */
++
++#define TIGER_RESET_ADDR      0x00
++#define TIGER_EXTERN_RESET_ON 0x01
++#define TIGER_EXTERN_RESET_OFF        0x00
++#define TIGER_AUX_CTRL                0x02
++#define TIGER_AUX_DATA                0x03
++#define TIGER_AUX_IRQMASK     0x05
++#define TIGER_AUX_STATUS      0x07
++
++/* Tiger AUX BITs */
++#define SEDL_AUX_IOMASK               0xdd    /* 1 and 5 are inputs */
++#define SEDL_ISAR_RESET_BIT_OFF 0x00
++#define SEDL_ISAR_RESET_BIT_ON        0x01
++#define SEDL_TIGER_IRQ_BIT    0x02
++#define SEDL_ISAR_PCI_LED1_BIT        0x08
++#define SEDL_ISAR_PCI_LED2_BIT        0x10
++
++#define SEDL_PCI_RESET_ON     (SEDL_ISAR_RESET_BIT_ON)
++#define SEDL_PCI_RESET_OFF    (SEDL_ISAR_PCI_LED1_BIT | SEDL_ISAR_PCI_LED2_BIT)
++
++
++#define SEDL_RESET      0x3   /* same as DOS driver */
++
++/* data struct */
++
++typedef struct _sedl_fax {
++      struct list_head        list;
++      void                    *pdev;
++      u_int                   subtyp;
++      u_int                   irq;
++      u_int                   irqcnt;
++      u_int                   cfg;
++      u_int                   addr;
++      u_int                   isac;
++      u_int                   isar;
++      mISDN_HWlock_t          lock;
++      isar_reg_t              ir;
++      isac_chip_t             isac_hw;
++      isar_hw_t               isar_hw[2];
++      dchannel_t              dch;
++      bchannel_t              bch[2];
++} sedl_fax;
++
++static int lock_dev(void *data, int nowait)
++{
++      register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock;
++      
++      return(lock_HW(lock, nowait));
++} 
++
++static void unlock_dev(void *data)
++{
++      register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock;
++
++      unlock_HW(lock);
++}
++
++static inline u_char
++readreg(unsigned int ale, unsigned int adr, u_char off)
++{
++      byteout(ale, off);
++      return (bytein(adr));
++}
++
++static inline void
++readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
++{
++      byteout(ale, off);
++      insb(adr, data, size);
++}
++
++
++static inline void
++writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
++{
++      byteout(ale, off);
++      byteout(adr, data);
++}
++
++static inline void
++writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
++{
++      byteout(ale, off);
++      outsb(adr, data, size);
++}
++
++/* Interface functions */
++
++static u_char
++ReadISAC(void *p, u_char offset)
++{
++      return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset));
++}
++
++static void
++WriteISAC(void *p, u_char offset, u_char value)
++{
++      writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset, value);
++}
++
++static void
++ReadISACfifo(void *p, u_char * data, int size)
++{
++      readfifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size);
++}
++
++static void
++WriteISACfifo(void *p, u_char * data, int size)
++{
++      writefifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size);
++}
++
++/* ISAR access routines
++ * mode = 0 access with IRQ on
++ * mode = 1 access with IRQ off
++ * mode = 2 access with IRQ off and using last offset
++ */
++  
++static u_char
++ReadISAR(void *p, int mode, u_char offset)
++{     
++      if (mode == 0)
++              return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset));
++      else if (mode == 1)
++              byteout(((sedl_fax *)p)->addr, offset);
++      return(bytein(((sedl_fax *)p)->isar));
++}
++
++static void
++WriteISAR(void *p, int mode, u_char offset, u_char value)
++{
++      if (mode == 0)
++              writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset, value);
++      else {
++              if (mode == 1)
++                      byteout(((sedl_fax *)p)->addr, offset);
++              byteout(((sedl_fax *)p)->isar, value);
++      }
++}
++
++inline void
++do_sedl_interrupt(sedl_fax *sf)
++{
++      u_char val;
++      int cnt = 8;
++
++      val = readreg(sf->addr, sf->isar, ISAR_IRQBIT);
++      Start_ISAR:
++      if (val & ISAR_IRQSTA)
++              isar_int_main(&sf->bch[0]);
++      val = readreg(sf->addr, sf->isac, ISAC_ISTA);
++      Start_ISAC:
++      if (val)
++              mISDN_isac_interrupt(&sf->dch, val);
++      val = readreg(sf->addr, sf->isar, ISAR_IRQBIT);
++      if ((val & ISAR_IRQSTA) && cnt) {
++              cnt--;
++              if (sf->dch.debug & L1_DEB_HSCX)
++                      printk(KERN_DEBUG "ISAR IntStat after IntRoutine cpu%d\n",
++                              smp_processor_id());
++              goto Start_ISAR;
++      }
++      val = readreg(sf->addr, sf->isac, ISAC_ISTA);
++      if (val && cnt) {
++              cnt--;
++              if (sf->dch.debug & L1_DEB_ISAC)
++                      printk(KERN_DEBUG "ISAC IntStat after IntRoutine cpu%d\n",
++                              smp_processor_id());
++              goto Start_ISAC;
++      }
++      if (!cnt)
++              if (sf->dch.debug & L1_DEB_ISAC)
++                      printk(KERN_DEBUG "Sedlbauer IRQ LOOP\n");
++      writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0);
++      writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF);
++      writereg(sf->addr, sf->isac, ISAC_MASK, 0x0);
++      writereg(sf->addr, sf->isar, ISAR_IRQBIT, ISAR_IRQMSK);
++}
++
++static irqreturn_t
++speedfax_isa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
++{
++      sedl_fax        *sf = dev_id;
++      u_long          flags;
++      
++      spin_lock_irqsave(&sf->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      sf->lock.spin_adr = (void *)0x2001;
++#endif
++      if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
++                      __FUNCTION__, sf->lock.state);
++#ifdef SPIN_DEBUG
++              printk(KERN_ERR "%s: previous lock:%p\n",
++                      __FUNCTION__, sf->lock.busy_adr);
++#endif
++#ifdef LOCK_STATISTIC
++              sf->lock.irq_fail++;
++#endif
++      } else {
++#ifdef LOCK_STATISTIC
++              sf->lock.irq_ok++;
++#endif
++#ifdef SPIN_DEBUG
++              sf->lock.busy_adr = speedfax_isa_interrupt;
++#endif
++      }
++
++      test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state);
++#ifdef SPIN_DEBUG
++      sf->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&sf->lock.lock, flags);
++      sf->irqcnt++;
++      do_sedl_interrupt(sf);
++      spin_lock_irqsave(&sf->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      sf->lock.spin_adr = (void *)0x2002;
++#endif
++      if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) {
++      }
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      __FUNCTION__, sf->lock.state);
++      }
++#ifdef SPIN_DEBUG
++      sf->lock.busy_adr = NULL;
++      sf->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&sf->lock.lock, flags);
++      return IRQ_HANDLED;
++}
++
++static irqreturn_t
++speedfax_pci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
++{
++      sedl_fax        *sf = dev_id;
++      u_long          flags;
++      u_char          val;
++
++      spin_lock_irqsave(&sf->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      sf->lock.spin_adr = (void *)0x3001;
++#endif
++      val = bytein(sf->cfg + TIGER_AUX_STATUS);
++      if (val & SEDL_TIGER_IRQ_BIT) { /* for us or shared ? */
++#ifdef SPIN_DEBUG
++              sf->lock.spin_adr = NULL;
++#endif
++              spin_unlock_irqrestore(&sf->lock.lock, flags);
++              return IRQ_NONE; /* shared */
++      }
++      sf->irqcnt++;
++      if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
++                      __FUNCTION__, sf->lock.state);
++#ifdef SPIN_DEBUG
++              printk(KERN_ERR "%s: previous lock:%p\n",
++                      __FUNCTION__, sf->lock.busy_adr);
++#endif
++#ifdef LOCK_STATISTIC
++              sf->lock.irq_fail++;
++#endif
++      } else {
++#ifdef LOCK_STATISTIC
++              sf->lock.irq_ok++;
++#endif
++#ifdef SPIN_DEBUG
++              sf->lock.busy_adr = speedfax_pci_interrupt;
++#endif
++      }
++
++      test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state);
++#ifdef SPIN_DEBUG
++      sf->lock.spin_adr= NULL;
++#endif
++      spin_unlock_irqrestore(&sf->lock.lock, flags);
++      do_sedl_interrupt(sf);
++      spin_lock_irqsave(&sf->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      sf->lock.spin_adr = (void *)0x3002;
++#endif
++      if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) {
++      }
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      __FUNCTION__, sf->lock.state);
++      }
++#ifdef SPIN_DEBUG
++      sf->lock.busy_adr = NULL;
++      sf->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&sf->lock.lock, flags);
++      return IRQ_HANDLED;
++}
++
++void
++release_sedlbauer(sedl_fax *sf)
++{
++      int bytecnt = 256;
++
++      if (sf->subtyp == SEDL_SPEEDFAX_ISA)
++              bytecnt = 16;
++      else
++              byteout(sf->cfg + TIGER_AUX_IRQMASK, 0);
++      if (sf->cfg)
++              release_region(sf->cfg, bytecnt);
++}
++
++static void
++reset_speedfax(sedl_fax *sf)
++{
++
++      printk(KERN_INFO "Sedlbauer: resetting card\n");
++
++      if (sf->subtyp == SEDL_SPEEDFAX_ISA) {
++              byteout(sf->cfg + SEDL_ISA_RESET_ON, SEDL_RESET);
++              mdelay(1);
++              byteout(sf->cfg + SEDL_ISA_RESET_OFF, 0);
++              mdelay(1);
++      } else {
++              byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_ON);
++              byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON);
++              mdelay(1);
++              byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_OFF);
++              byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF);
++              mdelay(1);
++      }
++}
++
++static int init_card(sedl_fax *sf)
++{
++      int     cnt = 3;
++      u_int   shared = SA_SHIRQ;
++      void    *irq_func = speedfax_pci_interrupt;
++
++      if (sf->subtyp == SEDL_SPEEDFAX_ISA) {
++              irq_func = speedfax_isa_interrupt;
++              shared = 0;
++      }
++      lock_dev(sf, 0);
++      if (request_irq(sf->irq, irq_func, shared, "speedfax", sf)) {
++              printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
++                      sf->irq);
++              unlock_dev(sf);
++              return(-EIO);
++      }
++      while (cnt) {
++              int     ret;
++
++              mISDN_clear_isac(&sf->dch);
++              if ((ret=mISDN_isac_init(&sf->dch))) {
++                      printk(KERN_WARNING "mISDN: mISDN_isac_init failed with %d\n", ret);
++                      break;
++              }
++              init_isar(&sf->bch[0]);
++              init_isar(&sf->bch[1]);
++              if (sf->subtyp != SEDL_SPEEDFAX_ISA)
++                      byteout(sf->cfg + TIGER_AUX_IRQMASK, SEDL_TIGER_IRQ_BIT);
++              WriteISAC(sf, ISAC_MASK, 0);
++              WriteISAR(sf, 0, ISAR_IRQBIT, ISAR_IRQMSK);
++              /* RESET Receiver and Transmitter */
++              WriteISAC(sf, ISAC_CMDR, 0x41);
++              unlock_dev(sf);
++              current->state = TASK_UNINTERRUPTIBLE;
++              /* Timeout 10ms */
++              schedule_timeout((10*HZ)/1000);
++              printk(KERN_INFO "%s: IRQ %d count %d\n",
++                      sf->dch.inst.name, sf->irq, sf->irqcnt);
++              if (!sf->irqcnt) {
++                      printk(KERN_WARNING
++                             "Sedlbauer speedfax: IRQ(%d) getting no interrupts during init %d\n",
++                             sf->irq, 4 - cnt);
++                      if (cnt == 1) {
++                              return (-EIO);
++                      } else {
++                              lock_dev(sf, 0);
++                              reset_speedfax(sf);
++                              cnt--;
++                      }
++              } else {
++                      return(0);
++              }
++      }
++      unlock_dev(sf);
++      return(-EIO);
++}
++
++
++#define MAX_CARDS     4
++#define MODULE_PARM_T "1-4i"
++static int sedl_cnt;
++static mISDNobject_t  speedfax;
++static int debug;
++static u_int protocol[MAX_CARDS];
++static int layermask[MAX_CARDS];
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++MODULE_PARM(protocol, MODULE_PARM_T);
++MODULE_PARM(layermask, MODULE_PARM_T);
++#endif
++
++static char SpeedfaxName[] = "Speedfax";
++
++int
++setup_speedfax(sedl_fax *sf)
++{
++      int bytecnt, ver;
++
++      bytecnt = (sf->subtyp == SEDL_SPEEDFAX_ISA) ? 16 : 256;
++      if (!request_region(sf->cfg, bytecnt, (sf->subtyp == SEDL_SPEEDFAX_ISA) ? "sedl PnP" : "sedl PCI")) {
++              printk(KERN_WARNING
++                     "mISDN: %s config port %x-%x already in use\n",
++                     "Speedfax +",
++                     sf->cfg,
++                     sf->cfg + bytecnt - 1);
++              return(-EIO);
++      }
++      sf->dch.read_reg = &ReadISAC;
++      sf->dch.write_reg = &WriteISAC;
++      sf->dch.read_fifo = &ReadISACfifo;
++      sf->dch.write_fifo = &WriteISACfifo;
++      sf->dch.hw = &sf->isac_hw;
++      if (sf->subtyp != SEDL_SPEEDFAX_ISA) {
++              sf->addr = sf->cfg + SEDL_PCI_ADR;
++              sf->isac = sf->cfg + SEDL_PCI_ISAC;
++              sf->isar = sf->cfg + SEDL_PCI_ISAR;
++              byteout(sf->cfg + TIGER_RESET_ADDR, 0xff);
++              mdelay(1);
++              byteout(sf->cfg + TIGER_RESET_ADDR, 0x00);
++              mdelay(1);
++              byteout(sf->cfg + TIGER_AUX_CTRL, SEDL_AUX_IOMASK);
++              byteout(sf->cfg + TIGER_AUX_IRQMASK, 0);
++              byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON);
++              mdelay(1);
++              byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF);
++              mdelay(1);
++      } else {
++              sf->addr = sf->cfg + SEDL_ISA_ADR;
++              sf->isac = sf->cfg + SEDL_ISA_ISAC;
++              sf->isar = sf->cfg + SEDL_ISA_ISAR;
++      }
++      sf->isar_hw[0].reg = &sf->ir;
++      sf->isar_hw[1].reg = &sf->ir;
++      sf->bch[0].hw = &sf->isar_hw[0];
++      sf->bch[1].hw = &sf->isar_hw[1];
++      sf->bch[0].Read_Reg = &ReadISAR;
++      sf->bch[0].Write_Reg = &WriteISAR;
++      sf->bch[1].Read_Reg = &ReadISAR;
++      sf->bch[1].Write_Reg = &WriteISAR;
++      lock_dev(sf, 0);
++#ifdef SPIN_DEBUG
++      printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &sf->lock.spin_adr, sf->lock.spin_adr);
++      printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &sf->lock.busy_adr, sf->lock.busy_adr);
++#endif
++      writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0);
++      writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF);
++      ver = ISARVersion(&sf->bch[0], "Sedlbauer:");
++      unlock_dev(sf);
++      if (ver < 0) {
++              printk(KERN_WARNING
++                      "Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
++              release_sedlbauer(sf);
++              return (-EIO);
++      }
++      return (0);
++}
++
++static void
++release_card(sedl_fax *card) {
++
++#ifdef LOCK_STATISTIC
++      printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",
++              card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq);
++      printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
++              card->lock.irq_ok, card->lock.irq_fail);
++#endif
++      lock_dev(card, 0);
++      free_irq(card->irq, card);
++      free_isar(&card->bch[1]);
++      free_isar(&card->bch[0]);
++      mISDN_isac_free(&card->dch);
++      WriteISAR(card, 0, ISAR_IRQBIT, 0);
++      WriteISAC(card, ISAC_MASK, 0xFF);
++      reset_speedfax(card);
++      WriteISAR(card, 0, ISAR_IRQBIT, 0);
++      WriteISAC(card, ISAC_MASK, 0xFF);
++      release_sedlbauer(card);
++      mISDN_free_bch(&card->bch[1]);
++      mISDN_free_bch(&card->bch[0]);
++      mISDN_free_dch(&card->dch);
++      speedfax.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up);
++      speedfax.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
++      list_del(&card->list);
++      unlock_dev(card);
++      if (card->subtyp == SEDL_SPEEDFAX_ISA) {
++              pnp_disable_dev(card->pdev);
++              pnp_set_drvdata(card->pdev, NULL);
++      } else {
++              pci_disable_device(card->pdev);
++              pci_set_drvdata(card->pdev, NULL);
++      }
++      kfree(card);
++      sedl_cnt--;
++}
++
++static int
++speedfax_manager(void *data, u_int prim, void *arg) {
++      sedl_fax        *card;
++      mISDNinstance_t *inst=data;
++      int             channel = -1;
++      struct sk_buff  *skb;
++
++      printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n",
++              __FUNCTION__, data, prim, arg);
++      if (!data) {
++              MGR_HASPROTOCOL_HANDLER(prim,arg,&speedfax)
++              printk(KERN_ERR "speedfax_manager no data prim %x arg %p\n",
++                      prim, arg);
++              return(-EINVAL);
++      }
++      list_for_each_entry(card, &speedfax.ilist, list) {
++              if (&card->dch.inst == inst) {
++                      channel = 2;
++                      break;
++              }
++              if (&card->bch[0].inst == inst) {
++                      channel = 0;
++                      break;
++              }
++              if (&card->bch[1].inst == inst) {
++                      channel = 1;
++                      break;
++              }
++      }
++      if (channel<0) {
++              printk(KERN_ERR "speedfax_manager no channel data %p prim %x arg %p\n",
++                      data, prim, arg);
++              return(-EINVAL);
++      }
++      switch(prim) {
++          case MGR_REGLAYER | CONFIRM:
++              if (channel == 2)
++                      dch_set_para(&card->dch, &inst->st->para);
++              else
++                      bch_set_para(&card->bch[channel], &inst->st->para);
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++              if (channel == 2) {
++                      inst->down.fdata = &card->dch;
++                      if ((skb = create_link_skb(PH_CONTROL | REQUEST,
++                              HW_DEACTIVATE, 0, NULL, 0))) {
++                              if (mISDN_ISAC_l1hw(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              } else {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (isar_down(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              }
++              speedfax.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
++              speedfax.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++              break;
++          case MGR_CLRSTPARA | INDICATION:
++              arg = NULL;
++          case MGR_ADDSTPARA | INDICATION:
++              if (channel == 2)
++                      dch_set_para(&card->dch, arg);
++              else
++                      bch_set_para(&card->bch[channel], arg);
++              break;
++          case MGR_RELEASE | INDICATION:
++              if (channel == 2) {
++                      release_card(card);
++              } else {
++                      speedfax.refcnt--;
++              }
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              if (channel==2)
++                      return(mISDN_SetIF(inst, arg, prim, mISDN_ISAC_l1hw, NULL, &card->dch));
++              else
++                      return(mISDN_SetIF(inst, arg, prim, isar_down, NULL, &card->bch[channel]));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_LOADFIRM | REQUEST:
++              {
++                      struct firm {
++                              int     len;
++                              void    *data;
++                      } *firm = arg;
++                      
++                      if (!arg)
++                              return(-EINVAL);
++                      return(isar_load_firmware(&card->bch[0], firm->data, firm->len));
++              }
++          case MGR_LOADFIRM | CONFIRM:
++              speedfax.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL);
++              break;
++          case MGR_SETSTACK | CONFIRM:
++              if ((channel!=2) && (inst->pid.global == 2)) {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(PH_ACTIVATE | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (isar_down(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++                      if ((inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) ||
++                              (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANSDTMF))
++                              if_link(&inst->up, DL_ESTABLISH | INDICATION,
++                                      0, 0, NULL, 0);
++                      else
++                              if_link(&inst->up, PH_ACTIVATE | INDICATION,
++                                      0, 0, NULL, 0);
++              }
++              break;
++          PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
++          PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST);
++          default:
++              printk(KERN_WARNING "speedfax_manager prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static int __devinit setup_instance(sedl_fax *card)
++{
++      int             i, err;
++      mISDN_pid_t     pid;
++      
++      if (sedl_cnt >= MAX_CARDS) {
++              kfree(card);
++              return(-EINVAL);
++      }
++      list_add_tail(&card->list, &speedfax.ilist);
++      card->dch.debug = debug;
++      lock_HW_init(&card->lock);
++      card->dch.inst.lock = lock_dev;
++      card->dch.inst.unlock = unlock_dev;
++      card->dch.inst.pid.layermask = ISDN_LAYER(0);
++      card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
++      mISDN_init_instance(&card->dch.inst, &speedfax, card);
++      sprintf(card->dch.inst.name, "SpeedFax%d", sedl_cnt+1);
++      mISDN_set_dchannel_pid(&pid, protocol[sedl_cnt], layermask[sedl_cnt]);
++      mISDN_init_dch(&card->dch);
++      for (i=0; i<2; i++) {
++              card->bch[i].channel = i;
++              mISDN_init_instance(&card->bch[i].inst, &speedfax, card);
++              card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
++              card->bch[i].inst.lock = lock_dev;
++              card->bch[i].inst.unlock = unlock_dev;
++              card->bch[i].debug = debug;
++              sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1);
++              mISDN_init_bch(&card->bch[i]);
++      }
++      printk(KERN_DEBUG "sfax card %p dch %p bch1 %p bch2 %p\n",
++              card, &card->dch, &card->bch[0], &card->bch[1]);
++      err = setup_speedfax(card);
++      if (err) {
++              mISDN_free_dch(&card->dch);
++              mISDN_free_bch(&card->bch[1]);
++              mISDN_free_bch(&card->bch[0]);
++              list_del(&card->list);
++              kfree(card);
++              return(err);
++      }
++      sedl_cnt++;
++      err = speedfax.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst);
++      if (err) {
++              release_card(card);
++              return(err);
++      }
++      for (i=0; i<2; i++) {
++              err = speedfax.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst);
++              if (err) {
++                      printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
++                      speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++                      return(err);
++              }
++      }
++      err = speedfax.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid);
++      if (err) {
++              printk(KERN_ERR  "MGR_SETSTACK REQUEST dch err(%d)\n", err);
++              speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(err);
++      }
++      err = init_card(card);
++      if (err) {
++              speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(err);
++      }
++      printk(KERN_INFO "SpeedFax %d cards installed\n", sedl_cnt);
++      return(0);
++}
++
++static int __devinit sedlpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++      int             err = -ENOMEM;
++      sedl_fax        *card;
++
++      if (!(card = kmalloc(sizeof(sedl_fax), GFP_ATOMIC))) {
++              printk(KERN_ERR "No kmem for Speedfax + PCI\n");
++              return(err);
++      }
++      memset(card, 0, sizeof(sedl_fax));
++      card->pdev = pdev;
++      if (PCI_SUBVENDOR_SPEEDFAX_PYRAMID == pdev->subsystem_vendor)
++              card->subtyp = SEDL_SPEEDFAX_PYRAMID;
++      else
++              card->subtyp = SEDL_SPEEDFAX_PCI;
++      err = pci_enable_device(pdev);
++      if (err) {
++              kfree(card);
++              return(err);
++      }
++
++      printk(KERN_INFO "mISDN: sedlpci found adapter %s at %s\n",
++             (char *) ent->driver_data, pdev->slot_name);
++
++      card->cfg = pci_resource_start(pdev, 0);
++      card->irq = pdev->irq;
++      pci_set_drvdata(pdev, card);
++      err = setup_instance(card);
++      if (err)
++              pci_set_drvdata(pdev, NULL);
++      return(err);
++}
++
++#if defined(CONFIG_PNP)
++#ifdef NEW_ISAPNP
++static int __devinit sedlpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
++#else
++static int __devinit sedlpnp_probe(struct pci_dev *pdev, const struct isapnp_device_id *dev_id)
++#endif
++{
++      int             err;
++      sedl_fax        *card;
++
++      if (!pdev)
++              return(-ENODEV);
++
++      if (!(card = kmalloc(sizeof(sedl_fax), GFP_ATOMIC))) {
++              printk(KERN_ERR "No kmem for Speedfax + PnP\n");
++              return(-ENOMEM);
++      }
++      memset(card, 0, sizeof(sedl_fax));
++      card->subtyp = SEDL_SPEEDFAX_ISA;
++      card->pdev = pdev;
++      pnp_disable_dev(pdev);
++      err = pnp_activate_dev(pdev);
++      if (err<0) {
++              printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __FUNCTION__,
++                      (char *)dev_id->driver_data, err);
++              kfree(card);
++              return(err);
++      }
++      card->cfg = pnp_port_start(pdev, 0);
++      card->irq = pnp_irq(pdev, 0);
++
++      printk(KERN_INFO "mISDN: sedlpnp_probe found adapter %s at IO %#x irq %d\n",
++             (char *)dev_id->driver_data, card->addr, card->irq);
++
++      pnp_set_drvdata(pdev, card);
++      err = setup_instance(card);
++      if (err)
++              pnp_set_drvdata(pdev, NULL);
++      return(err);
++}
++#endif /* CONFIG_PNP */
++
++static void __devexit sedl_remove_pci(struct pci_dev *pdev)
++{
++      sedl_fax        *card = pci_get_drvdata(pdev);
++
++      if (card)
++              speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++      else
++              printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__);
++}
++
++#if defined(CONFIG_PNP)
++#ifdef NEW_ISAPNP
++static void __devexit sedl_remove_pnp(struct pnp_dev *pdev)
++#else
++static void __devexit sedl_remove_pnp(struct pci_dev *pdev)
++#endif
++{
++      sedl_fax        *card = pnp_get_drvdata(pdev);
++
++      if (card)
++              speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++      else
++              printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__);
++}
++#endif
++
++static struct pci_device_id sedlpci_ids[] __devinitdata = {
++      { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER,
++        0, 0, (unsigned long) "Pyramid Speedfax + PCI" },
++      { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER,
++        0, 0, (unsigned long) "Sedlbauer Speedfax + PCI" },
++      { }
++};
++MODULE_DEVICE_TABLE(pci, sedlpci_ids);
++
++static struct pci_driver sedlpci_driver = {
++      name:     "speedfax pci",
++      probe:    sedlpci_probe,
++      remove:   __devexit_p(sedl_remove_pci),
++      id_table: sedlpci_ids,
++};
++
++#if defined(CONFIG_PNP)
++#ifdef NEW_ISAPNP
++static struct pnp_device_id sedlpnp_ids[] __devinitdata = {
++      { 
++              .id             = "SAG0002",
++              .driver_data    = (unsigned long) "Speedfax + PnP",
++      },
++};
++
++static struct pnp_driver sedlpnp_driver = {
++#else
++static struct isapnp_device_id sedlpnp_ids[] __devinitdata = {
++      { ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02),
++        ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02), 
++        (unsigned long) "Speedfax + PnP" },
++      { }
++};
++MODULE_DEVICE_TABLE(isapnp, sedlpnp_ids);
++
++static struct isapnp_driver sedlpnp_driver = {
++#endif
++      name:     "speedfax pnp",
++      probe:    sedlpnp_probe,
++      remove:   __devexit_p(sedl_remove_pnp),
++      id_table: sedlpnp_ids,
++};
++#endif /* CONFIG_PNP */
++
++static int __init Speedfax_init(void)
++{
++      int err, pci_nr_found;
++
++#ifdef MODULE
++      speedfax.owner = THIS_MODULE;
++#endif
++      INIT_LIST_HEAD(&speedfax.ilist);
++      speedfax.name = SpeedfaxName;
++      speedfax.own_ctrl = speedfax_manager;
++      speedfax.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0;
++      speedfax.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS |
++                                    ISDN_PID_L1_B_64HDLC |
++                                    ISDN_PID_L1_B_T30FAX |
++                                    ISDN_PID_L1_B_MODEM_ASYNC;
++      speedfax.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS |
++                                    ISDN_PID_L2_B_T30;
++
++      if ((err = mISDN_register(&speedfax))) {
++              printk(KERN_ERR "Can't register Speedfax error(%d)\n", err);
++              return(err);
++      }
++      err = pci_register_driver(&sedlpci_driver);
++      if (err < 0)
++              goto out;
++      pci_nr_found = err;
++#if defined(CONFIG_PNP)
++      err = pnp_register_driver(&sedlpnp_driver);
++      if (err < 0)
++              goto out_unregister_pci;
++#endif
++#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
++      if (pci_nr_found + err == 0) {
++              err = -ENODEV;
++              goto out_unregister_isapnp;
++      }
++#endif
++      return 0;
++
++#if !defined(CONFIG_HOTPLUG) || defined(MODULE)
++ out_unregister_isapnp:
++#if defined(CONFIG_PNP)
++      pnp_unregister_driver(&sedlpnp_driver);
++#endif
++#endif
++ out_unregister_pci:
++      pci_unregister_driver(&sedlpci_driver);
++ out:
++      return err;
++}
++
++static void __exit Speedfax_cleanup(void)
++{
++      int             err;
++      sedl_fax        *card, *next;
++
++      if ((err = mISDN_unregister(&speedfax))) {
++              printk(KERN_ERR "Can't unregister Speedfax PCI error(%d)\n", err);
++      }
++      list_for_each_entry_safe(card, next, &speedfax.ilist, list) {
++              printk(KERN_ERR "Speedfax PCI card struct not empty refs %d\n",
++                      speedfax.refcnt);
++              release_card(card);
++      }
++#if defined(CONFIG_PNP)
++      pnp_unregister_driver(&sedlpnp_driver);
++#endif
++      pci_unregister_driver(&sedlpci_driver);
++}
++
++module_init(Speedfax_init);
++module_exit(Speedfax_cleanup);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/stack.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/stack.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/stack.c      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/stack.c  2004-11-22 09:33:38.412704296 +0000
+@@ -0,0 +1,762 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++
++#include "core.h"
++
++LIST_HEAD(mISDN_stacklist);
++LIST_HEAD(mISDN_instlist);
++
++int
++get_stack_cnt(void)
++{
++      int cnt = 0;
++      mISDNstack_t *st;
++
++      list_for_each_entry(st, &mISDN_stacklist, list)
++              cnt++;
++      return(cnt);
++}
++
++void
++get_stack_info(struct sk_buff *skb)
++{
++      mISDN_head_t    *hp;
++      mISDNstack_t    *cst, *st;
++      stack_info_t    *si;
++      mISDNlayer_t    *lay;
++
++      hp = mISDN_HEAD_P(skb);
++      st = get_stack4id(hp->addr);
++      if (!st)
++              hp->len = 0;
++      else {
++              si = (stack_info_t *)skb->data;
++              memset(si, 0, sizeof(stack_info_t));
++              si->id = st->id;
++              si->extentions = st->extentions;
++              if (st->mgr)
++                      si->mgr = st->mgr->id;
++              else
++                      si->mgr = 0;
++              memcpy(&si->pid, &st->pid, sizeof(mISDN_pid_t));
++              memcpy(&si->para, &st->para, sizeof(mISDN_stPara_t));
++              si->instcnt = 0;
++              list_for_each_entry(lay, &st->layerlist, list) {
++                      if (lay->inst) {
++                              si->inst[si->instcnt] = lay->inst->id;
++                              si->instcnt++;
++                      }
++              }
++              si->childcnt = 0;
++              list_for_each_entry(cst, &st->childlist, list) {
++                      si->child[si->childcnt] = cst->id;
++                      si->childcnt++;
++              }
++              hp->len = sizeof(stack_info_t);
++              if (si->childcnt>2)
++                      hp->len += (si->childcnt-2)*sizeof(int);
++      }
++      skb_put(skb, hp->len);
++}
++
++static int
++get_free_stackid(mISDNstack_t *mst, int flag) {
++      u_int           id=1, found;
++      mISDNstack_t    *st;
++
++      if (!mst) {
++              while(id<127) {
++                      found = 0;
++                      list_for_each_entry(st, &mISDN_stacklist, list) {
++                              if (st->id == id) {
++                                      found++;
++                                      break;
++                              }
++                      }
++                      if (found)
++                              id++;
++                      else
++                              return(id);
++              }
++      } else if (flag & FLG_CLONE_STACK) {
++              id = mst->id | FLG_CLONE_STACK;
++              while(id < CLONE_ID_MAX) {
++                      found = 0;
++                      id += CLONE_ID_INC;
++                      list_for_each_entry(st, &mISDN_stacklist, list) {
++                              if (st->id == id) {
++                                      found++;
++                                      break;
++                              }
++                      }
++                      if (!found)
++                              return(id);
++              }
++      } else if (flag & FLG_CHILD_STACK) {
++              id = mst->id | FLG_CHILD_STACK;
++              while(id < CHILD_ID_MAX) {
++                      id += CHILD_ID_INC;
++                      found = 0;
++                      list_for_each_entry(st, &mst->childlist, list) {
++                              if (st->id == id) {
++                                      found++;
++                                      break;
++                              }
++                      }
++                      if (!found)
++                              return(id);
++              }
++      }
++      return(0);
++}
++
++mISDNstack_t *
++get_stack4id(u_int id)
++{
++      mISDNstack_t *cst, *st;
++
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "get_stack4id(%x)\n", id);
++      if (!id) /* 0 isn't a valid id */
++              return(NULL);
++      list_for_each_entry(st, &mISDN_stacklist, list) {       
++              if (id == st->id)
++                      return(st);
++              list_for_each_entry(cst, &st->childlist, list) {
++                      if (cst->id == id)
++                              return(cst);
++              }
++      }
++      return(NULL);
++}
++
++mISDNlayer_t *
++getlayer4lay(mISDNstack_t *st, int layermask)
++{
++      mISDNlayer_t    *layer;
++      mISDNinstance_t *inst;
++
++      if (!st) {
++              int_error();
++              return(NULL);
++      }
++      list_for_each_entry(layer, &st->layerlist, list) {
++              inst = layer->inst;
++              if(inst && (inst->pid.layermask & layermask))
++                      return(layer);
++      }
++      return(NULL);
++}
++
++mISDNinstance_t *
++get_instance(mISDNstack_t *st, int layer_nr, int protocol)
++{
++      mISDNlayer_t    *layer;
++      mISDNinstance_t *inst=NULL;
++
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "get_instance st(%p) lnr(%d) prot(%x)\n",
++                      st, layer_nr, protocol);
++      if (!st) {
++              int_error();
++              return(NULL);
++      }
++      if ((layer_nr<0) || (layer_nr>MAX_LAYER_NR)) {
++              int_errtxt("lnr %d", layer_nr);
++              return(NULL);
++      }
++      list_for_each_entry(layer, &st->layerlist, list) {
++              inst = layer->inst;
++              if (inst) {
++                      if (core_debug & DEBUG_CORE_FUNC)
++                              printk(KERN_DEBUG "get_instance inst(%p, %x) lm %x/%x prot %x/%x\n",
++                                      inst, inst->id, inst->pid.layermask, ISDN_LAYER(layer_nr),
++                                      inst->pid.protocol[layer_nr], protocol);
++                      if ((inst->pid.layermask & ISDN_LAYER(layer_nr)) &&
++                              (inst->pid.protocol[layer_nr] == protocol))
++                              goto out;
++                      inst = NULL;
++              }
++              if (list_empty(&layer->list)) {
++                      int_errtxt("deadloop layer %p", layer);
++                      return(NULL);
++              }
++      }
++out:
++      return(inst);
++}
++
++mISDNinstance_t *
++get_instance4id(u_int id)
++{
++      mISDNinstance_t *inst;
++
++      list_for_each_entry(inst, &mISDN_instlist, list)
++              if (inst->id == id)
++                      return(inst);
++      return(NULL);
++}
++
++int
++get_layermask(mISDNlayer_t *layer)
++{
++      int mask = 0;
++
++      if (layer->inst)
++              mask |= layer->inst->pid.layermask;
++      return(mask);
++}
++
++int
++insertlayer(mISDNstack_t *st, mISDNlayer_t *layer, int layermask)
++{
++      mISDNlayer_t *item;
++      
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s(%p, %p, %x)\n",
++                      __FUNCTION__, st, layer, layermask);  
++      if (!st || !layer) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (list_empty(&st->layerlist)) {
++              list_add(&layer->list, &st->layerlist);
++      } else {
++              list_for_each_entry(item, &st->layerlist, list) {
++                      if (layermask < get_layermask(item)) {
++                              list_add_tail(&layer->list, &item->list); 
++                              return(0);
++                      }
++              }
++              list_add_tail(&layer->list, &st->layerlist);
++      }
++      return(0);
++}
++
++mISDNstack_t *
++new_stack(mISDNstack_t *master, mISDNinstance_t *inst)
++{
++      mISDNstack_t *newst;
++
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "create %s stack inst(%p)\n",
++                      master ? "child" : "master", inst);
++      if (!(newst = kmalloc(sizeof(mISDNstack_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc mISDN_stack failed\n");
++              return(NULL);
++      }
++      memset(newst, 0, sizeof(mISDNstack_t));
++      INIT_LIST_HEAD(&newst->layerlist);
++      INIT_LIST_HEAD(&newst->childlist);
++      if (!master) {
++              if (inst && inst->st) {
++                      newst->id = get_free_stackid(inst->st, FLG_CLONE_STACK);
++              } else {
++                      newst->id = get_free_stackid(NULL, 0);
++              }
++      } else {
++              newst->id = get_free_stackid(master, FLG_CHILD_STACK);
++      }
++      newst->mgr = inst;
++      if (master) {
++              list_add_tail(&newst->list, &master->childlist);
++      } else {
++              list_add_tail(&newst->list, &mISDN_stacklist);
++      }
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "Stack id %x added\n", newst->id);
++      if (inst)
++              inst->st = newst;
++      return(newst);
++}
++
++
++static int
++release_layers(mISDNstack_t *st, u_int prim)
++{
++      mISDNinstance_t *inst;
++      mISDNlayer_t    *layer, *nl;
++      int             cnt = 0;
++
++      list_for_each_entry_safe(layer, nl, &st->layerlist, list) {
++              inst = layer->inst;
++              if (inst) {
++                      if (core_debug & DEBUG_CORE_FUNC)
++                              printk(KERN_DEBUG  "%s: st(%p) inst(%p):%x %s lm(%x)\n",
++                                      __FUNCTION__, st, inst, inst->id,
++                                      inst->name, inst->pid.layermask);
++                      inst->obj->own_ctrl(inst, prim, NULL);
++              }
++              list_del(&layer->list);
++              kfree(layer);
++              if (cnt++ > 1000) {
++                      int_errtxt("release_layers endless loop st(%p)", st);
++                      return(-EINVAL);
++              }
++      }
++      return(0);
++}
++
++int
++do_for_all_layers(mISDNstack_t *st, u_int prim, void *arg)
++{
++      mISDNinstance_t *inst;
++      mISDNlayer_t    *layer, *nl;
++      int             cnt = 0;
++
++      if (!st) {
++              int_error();
++              return(-EINVAL);
++      }
++      list_for_each_entry_safe(layer, nl, &st->layerlist, list) {
++              inst = layer->inst;
++              if (inst) {
++                      if (core_debug & DEBUG_CORE_FUNC)
++                              printk(KERN_DEBUG  "%s: st(%p) inst(%p):%x %s prim(%x) arg(%p)\n",
++                                      __FUNCTION__, st, inst, inst->id, inst->name, prim, arg);
++                      inst->obj->own_ctrl(inst, prim, arg);
++              }
++              if (cnt++ > 1000) {
++                      int_errtxt("do_for_all_layers endless loop st(%p)", st);
++                      return(-EINVAL);
++              }
++      }
++      return(0);
++}
++
++int
++change_stack_para(mISDNstack_t *st, u_int prim, mISDN_stPara_t *stpara)
++{
++      int     changed = 0;
++      if (!st) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (prim == (MGR_ADDSTPARA | REQUEST)) {
++              if (!stpara) {
++                      int_error();
++                      return(-EINVAL);
++              }
++              prim = MGR_ADDSTPARA | INDICATION;
++              if (stpara->maxdatalen > 0 && stpara->maxdatalen < st->para.maxdatalen) {
++                      changed++;
++                      st->para.maxdatalen = stpara->maxdatalen;
++              }
++              if (stpara->up_headerlen > st->para.up_headerlen) {
++                      changed++;
++                      st->para.up_headerlen = stpara->up_headerlen;
++              }
++              if (stpara->down_headerlen > st->para.down_headerlen) {
++                      changed++;
++                      st->para.down_headerlen = stpara->down_headerlen;
++              }
++              if (!changed)
++                      return(0);
++              stpara = &st->para;
++      } else if (prim == (MGR_CLRSTPARA | REQUEST)) {
++              prim = MGR_CLRSTPARA | INDICATION;
++              memset(&st->para, 0, sizeof(mISDN_stPara_t));
++              stpara = NULL;
++      }
++      return(do_for_all_layers(st, prim, stpara));
++}
++
++int
++release_stack(mISDNstack_t *st) {
++      int err;
++      mISDNstack_t *cst, *nst;
++
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st);
++      list_for_each_entry_safe(cst, nst, &st->childlist, list) {
++              if (core_debug & DEBUG_CORE_FUNC)
++                      printk(KERN_DEBUG "%s: cst(%p)\n", __FUNCTION__, cst);
++              if ((err = release_layers(cst, MGR_RELEASE | INDICATION))) {
++                      printk(KERN_WARNING "release_stack child err(%d)\n", err);
++                      return(err);
++              }
++              list_del(&cst->list);
++              kfree(cst);
++      }
++      if ((err = release_layers(st, MGR_RELEASE | INDICATION))) {
++              printk(KERN_WARNING "release_stack err(%d)\n", err);
++              return(err);
++      }
++      list_del(&st->list);
++      kfree(st);
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s: mISDN_stacklist(%p<-%p->%p)\n", __FUNCTION__,
++                      mISDN_stacklist.prev, &mISDN_stacklist, mISDN_stacklist.next);
++      return(0);
++}
++
++void
++release_stacks(mISDNobject_t *obj) {
++      mISDNstack_t *st, *tmp;
++      mISDNlayer_t *layer, *ltmp;
++      int rel;
++
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s: obj(%p) %s\n",
++                      __FUNCTION__, obj, obj->name);
++      list_for_each_entry_safe(st, tmp, &mISDN_stacklist, list) {
++              rel = 0;
++              if (core_debug & DEBUG_CORE_FUNC)
++                      printk(KERN_DEBUG "%s: st(%p)\n",
++                              __FUNCTION__, st);
++              list_for_each_entry_safe(layer, ltmp, &st->layerlist, list) {
++                      if (core_debug & DEBUG_CORE_FUNC)
++                              printk(KERN_DEBUG "%s: layer(%p) inst(%p)\n",
++                                      __FUNCTION__, layer, layer->inst);
++                      if (layer->inst && layer->inst->obj == obj)
++                              rel++;
++              }               
++              if (rel)
++                      release_stack(st);
++      }
++      if (obj->refcnt)
++              printk(KERN_WARNING "release_stacks obj %s refcnt is %d\n",
++                      obj->name, obj->refcnt);
++}
++
++
++static void
++get_free_instid(mISDNstack_t *st, mISDNinstance_t *inst) {
++      mISDNinstance_t *il;
++
++      inst->id = mISDN_get_lowlayer(inst->pid.layermask)<<20;
++      inst->id |= FLG_INSTANCE;
++      if (st) {
++              inst->id |= st->id;
++      } else {
++              list_for_each_entry(il, &mISDN_instlist, list) {
++                      if (il->id == inst->id) {
++                              if ((inst->id & IF_INSTMASK) >= INST_ID_MAX) {
++                                      inst->id = 0;
++                                      return;
++                              }
++                              inst->id += INST_ID_INC;
++                              il = list_entry(mISDN_instlist.next, mISDNinstance_t, list);
++                      }
++              }
++      }
++}
++
++int
++register_layer(mISDNstack_t *st, mISDNinstance_t *inst) {
++      mISDNlayer_t    *layer = NULL;
++      int             refinc = 0;
++
++      if (!inst)
++              return(-EINVAL);
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s:st(%p) inst(%p/%p) lmask(%x) id(%x)\n",
++                      __FUNCTION__, st, inst, inst->obj,
++                      inst->pid.layermask, inst->id);
++      if (inst->id) { /* allready registered */
++              if (inst->st || !st) {
++                      int_errtxt("register duplicate %08x %p %p",
++                              inst->id, inst->st, st);
++                      return(-EBUSY);
++              }
++      }
++      if (st) {
++              if ((layer = getlayer4lay(st, inst->pid.layermask))) {
++                      if (layer->inst) {
++                              int_errtxt("stack %08x has layer %08x",
++                                      st->id, layer->inst->id);
++                              return(-EBUSY);
++                      }
++              } else if (!(layer = kmalloc(sizeof(mISDNlayer_t), GFP_ATOMIC))) {
++                      int_errtxt("no mem for layer %x", inst->pid.layermask);
++                      return(-ENOMEM);
++              }
++              memset(layer, 0, sizeof(mISDNlayer_t));
++              insertlayer(st, layer, inst->pid.layermask);
++              layer->inst = inst;
++      }
++      if (!inst->id)
++              refinc++;
++      get_free_instid(st, inst);
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s: inst(%p/%p) id(%x)%s\n", __FUNCTION__,
++                      inst, inst->obj, inst->id, refinc ? " changed" : "");
++      if (!inst->id) {
++              int_errtxt("no free inst->id for layer %x", inst->pid.layermask);
++              if (st && layer) {
++                      list_del(&layer->list);
++                      kfree(layer);
++              }
++              return(-EINVAL);
++      }
++      inst->st = st;
++      if (refinc)
++              inst->obj->refcnt++;
++      list_add_tail(&inst->list, &mISDN_instlist);
++      return(0);
++}
++
++int
++unregister_instance(mISDNinstance_t *inst) {
++      mISDNlayer_t *layer;
++      int err = 0;
++
++      if (!inst)
++              return(-EINVAL);
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s: st(%p) inst(%p):%x lay(%x)\n",
++                      __FUNCTION__, inst->st, inst, inst->id, inst->pid.layermask);
++      if (inst->st) {
++              if ((layer = getlayer4lay(inst->st, inst->pid.layermask))) {
++                      if (core_debug & DEBUG_CORE_FUNC)
++                              printk(KERN_DEBUG "%s: layer(%p)->inst(%p)\n",
++                                      __FUNCTION__, layer, layer->inst);
++                      layer->inst = NULL;
++              } else {
++                      printk(KERN_WARNING "%s: no layer found\n", __FUNCTION__);
++                      err = -ENODEV;
++              }
++              if (inst->st && (inst->st->mgr != inst))
++                      inst->st = NULL;
++      }
++      list_del_init(&inst->list);
++      inst->id = 0;
++      inst->obj->refcnt--;
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s: mISDN_instlist(%p<-%p->%p)\n", __FUNCTION__,
++                      mISDN_instlist.prev, &mISDN_instlist, mISDN_instlist.next);
++      return(0);
++}
++
++int
++copy_pid(mISDN_pid_t *dpid, mISDN_pid_t *spid, u_char *pbuf)
++{
++      u_int   i, off;
++
++      memcpy(dpid, spid, sizeof(mISDN_pid_t));
++      if (spid->pbuf) {
++              if (!pbuf) {
++                      int_error();
++                      return(-ENOMEM);
++              }
++              dpid->pbuf = pbuf;
++              memcpy(dpid->pbuf, spid->pbuf, spid->maxplen);
++              for (i = 0; i <= MAX_LAYER_NR; i++) {
++                      if (spid->param[i]) {
++                              off = (u_int)(spid->param[i] - spid->pbuf);
++                              dpid->param[i] = dpid->pbuf + off;
++                      }
++              }
++      }
++      return(0);
++}
++
++int
++set_stack(mISDNstack_t *st, mISDN_pid_t *pid)
++{
++      int             err;
++      u_char          *pbuf = NULL;
++      mISDNinstance_t *inst;
++      mISDNlayer_t    *hl, *hln;
++
++      if (!st || !pid) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (!st->mgr || !st->mgr->obj || !st->mgr->obj->ctrl) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (pid->pbuf)
++              pbuf = kmalloc(pid->maxplen, GFP_ATOMIC);
++      err = copy_pid(&st->pid, pid, pbuf);
++      if (err)
++              return(err);
++      memcpy(&st->mgr->pid, &st->pid, sizeof(mISDN_pid_t));
++      if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) {
++              int_error();
++              return(-ENOPROTOOPT);
++      } else {
++              mISDN_RemoveUsedPID(pid, &st->mgr->pid);
++      }
++      err = st->mgr->obj->ctrl(st, MGR_REGLAYER | REQUEST, st->mgr);
++      if (err) {
++              int_error();
++              return(err);
++      }
++      while (pid->layermask) {
++              inst = get_next_instance(st, pid);
++              if (!inst) {
++                      int_error();
++                      st->mgr->obj->ctrl(st, MGR_CLEARSTACK| REQUEST, NULL);
++                      return(-ENOPROTOOPT);
++              }
++              mISDN_RemoveUsedPID(pid, &inst->pid);
++      }
++      
++      list_for_each_entry_safe(hl, hln, &st->layerlist, list) {
++              if (hl->list.next == &st->layerlist)
++                      break;
++              if (!hl->inst) {
++                      int_error();
++                      return(-EINVAL);
++              }
++              if (!hl->inst->obj) {
++                      int_error();
++                      return(-EINVAL);
++              }
++              if (!hl->inst->obj->own_ctrl) {
++                      int_error();
++                      return(-EINVAL);
++              }
++              hl->inst->obj->own_ctrl(hl->inst, MGR_CONNECT | REQUEST,
++                      hln->inst);
++      }
++      st->mgr->obj->own_ctrl(st->mgr, MGR_SETSTACK |CONFIRM, NULL);
++      return(0);
++}
++
++int
++clear_stack(mISDNstack_t *st) {
++
++      if (!st)
++              return(-EINVAL);
++      if (core_debug & DEBUG_CORE_FUNC)
++              printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st);
++      if (st->pid.pbuf)
++              kfree(st->pid.pbuf);
++      memset(&st->pid, 0, sizeof(mISDN_pid_t));
++      memset(&st->para, 0, sizeof(mISDN_stPara_t));
++      return(release_layers(st, MGR_UNREGLAYER | REQUEST));
++}
++
++static int
++test_stack_protocol(mISDNstack_t *st, u_int l1prot, u_int l2prot, u_int l3prot)
++{
++      int             cnt = MAX_LAYER_NR + 1, ret = 1;
++      mISDN_pid_t     pid;
++      mISDNinstance_t *inst;
++      
++      clear_stack(st);
++      memset(&pid, 0, sizeof(mISDN_pid_t));
++      pid.layermask = ISDN_LAYER(1);
++      if (!(((l2prot == 2) || (l2prot == 0x40)) && (l3prot == 1)))
++              pid.layermask |= ISDN_LAYER(2);
++      if (!(l3prot == 1))
++              pid.layermask |= ISDN_LAYER(3);
++      
++      pid.protocol[1] = l1prot | ISDN_PID_LAYER(1) | ISDN_PID_BCHANNEL_BIT;
++      if (pid.layermask & ISDN_LAYER(2))
++              pid.protocol[2] = l2prot | ISDN_PID_LAYER(2) | ISDN_PID_BCHANNEL_BIT;
++      if (pid.layermask & ISDN_LAYER(3))
++              pid.protocol[3] = l3prot | ISDN_PID_LAYER(3) | ISDN_PID_BCHANNEL_BIT;
++      copy_pid(&st->pid, &pid, NULL);
++      memcpy(&st->mgr->pid, &pid, sizeof(mISDN_pid_t));
++      if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) {
++              memset(&st->pid, 0, sizeof(mISDN_pid_t));
++              return(-ENOPROTOOPT);
++      } else {
++              mISDN_RemoveUsedPID(&pid, &st->mgr->pid);
++      }
++      if (!pid.layermask) {
++              memset(&st->pid, 0, sizeof(mISDN_pid_t));
++              return(0);
++      }
++      ret = st->mgr->obj->ctrl(st, MGR_REGLAYER | REQUEST, st->mgr);
++      if (ret) {
++              clear_stack(st);
++              return(ret);
++      }
++      while (pid.layermask && cnt--) {
++              inst = get_next_instance(st, &pid);
++              if (!inst) {
++                      st->mgr->obj->ctrl(st, MGR_CLEARSTACK| REQUEST, NULL);
++                      return(-ENOPROTOOPT);
++              }
++              mISDN_RemoveUsedPID(&pid, &inst->pid);
++      }
++      if (!cnt)
++              ret = -ENOPROTOOPT;
++      clear_stack(st);
++      return(ret);
++}
++
++static u_int  validL1pid4L2[ISDN_PID_IDX_MAX + 1] = {
++                      0x022d,
++                      0x03ff,
++                      0x0000,
++                      0x0000,
++                      0x0010,
++                      0x022d,
++                      0x03ff,
++                      0x0380,
++                      0x022d,
++                      0x022d,
++                      0x022d,
++                      0x01c6,
++                      0x0000,
++};
++
++static u_int  validL2pid4L3[ISDN_PID_IDX_MAX + 1] = {
++                      0x1fff,
++                      0x0000,
++                      0x0101,
++                      0x0101,
++                      0x0010,
++                      0x0010,
++                      0x0000,
++                      0x00c0,
++                      0x0000,
++};
++
++int
++evaluate_stack_pids(mISDNstack_t *st, mISDN_pid_t *pid)
++{
++      int             err;
++      mISDN_pid_t     pidmask;
++      u_int           l1bitm, l2bitm, l3bitm;
++      u_int           l1idx, l2idx, l3idx;
++
++      if (!st || !pid) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (!st->mgr || !st->mgr->obj || !st->mgr->obj->ctrl) {
++              int_error();
++              return(-EINVAL);
++      }
++      copy_pid(&pidmask, pid, NULL);
++      memset(pid, 0, sizeof(mISDN_pid_t));
++      for (l1idx=0; l1idx <= ISDN_PID_IDX_MAX; l1idx++) {
++              l1bitm = 1 << l1idx;
++              if (!(pidmask.protocol[1] & l1bitm))
++                      continue;
++              for (l2idx=0; l2idx <= ISDN_PID_IDX_MAX; l2idx++) {
++                      l2bitm = 1 << l2idx;
++                      if (!(pidmask.protocol[2] & l2bitm))
++                              continue;
++                      if (!(validL1pid4L2[l2idx] & l1bitm))
++                              continue;
++                      for (l3idx=0; l3idx <= ISDN_PID_IDX_MAX; l3idx++) {
++                              err = 1;
++                              l3bitm = 1 << l3idx;
++                              if (!(pidmask.protocol[3] & l3bitm))
++                                      continue;
++                              if (!(validL2pid4L3[l3idx] & l2bitm))
++                                      continue;
++                              err = test_stack_protocol(st, l1bitm, l2bitm, l3bitm);
++                              if (!err) {
++                                      pid->protocol[3] |= l3bitm;
++                                      pid->protocol[2] |= l2bitm;
++                                      pid->protocol[1] |= l1bitm;
++                              }
++                      }
++              }
++      }
++      return(0);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/supp_serv.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/supp_serv.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/supp_serv.c  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/supp_serv.c      2004-11-22 09:33:38.422702776 +0000
+@@ -0,0 +1,678 @@
++/* $Id$
++ *
++ */
++
++#include "m_capi.h"
++#include "asn1_comp.h"
++#include "asn1_enc.h"
++#include "dss1.h"
++#include "helper.h"
++
++#define T_ACTIVATE    4000
++#define T_DEACTIVATE  4000
++#define T_INTERROGATE 4000
++
++static void   SSProcessAddTimer(SSProcess_t *, int);
++
++static __u8 *
++encodeInvokeComponentHead(__u8 *p)
++{
++      *p++ = 0;     // length -- not known yet
++      *p++ = 0x91;  // remote operations protocol
++      *p++ = 0xa1;  // invoke component
++      *p++ = 0;     // length -- not known yet
++      return p;
++}
++
++static void
++encodeInvokeComponentLength(__u8 *msg, __u8 *p)
++{
++      msg[3] = p - &msg[5];
++      msg[0] = p - &msg[1];
++}
++
++
++static int
++SSProcess_L4L3(SSProcess_t *spc, __u32 prim, struct sk_buff *skb) {
++      int     err;
++
++// FIXME      err = ControllerL4L3(contr, prim, contr->addr | DUMMY_CR_FLAG, skb);
++      err = ControllerL4L3(spc->contr, prim, MISDN_ID_DUMMY, skb);
++      if (err)
++              dev_kfree_skb(skb);
++      return(err);
++}
++
++static __inline__ int capiGetWord(__u8 *p, __u8 *end, __u16 *word)
++{
++      if (p + 2 > end) {
++              return -1;
++      }
++      *word = *p + (*(p+1) << 8);
++
++      return 2;
++}
++
++static __inline__ int capiGetDWord(__u8 *p, __u8 *end, __u32 *dword)
++{
++      if (p + 4 > end) {
++              return -1;
++      }
++      *dword = *p + (*(p+1) << 8) + (*(p+2) << 16) + (*(p+3) << 24);
++
++      return 4;
++}
++
++static __inline__ int capiGetStruct(__u8 *p, __u8 *_end, __u8 **strct)
++{
++      int len = *p++;
++      __u8 *end = p + len;
++
++      if (end > _end) return -1;
++
++      *strct = p - 1;
++
++      return len + 1;
++}
++
++#define CAPI_GET(func, parm) \
++      ret = func(p, end, parm); \
++      if (ret < 0) return -1; \
++      p += ret
++
++static __inline__ int capiGetFacReqListen(__u8 *p, __u8 *_end, struct FacReqListen *listen)
++{
++      int len = *p++;
++      int ret;
++      __u8 *end = p + len;
++
++      if (end > _end) return -1;
++
++      CAPI_GET(capiGetDWord, &listen->NotificationMask);
++
++      if (p != end) return -1;
++      return len + 1;
++}
++
++static __inline__ int capiGetFacReqSuspend(__u8 *p, __u8 *_end, struct FacReqSuspend *suspend)
++{
++      int len = *p++;
++      int ret;
++      __u8 *end = p + len;
++
++      if (end > _end) return -1;
++
++      CAPI_GET(capiGetStruct, &suspend->CallIdentity);
++
++      if (p != end) return -1;
++      return len + 1;
++}
++
++static __inline__ int capiGetFacReqResume(__u8 *p, __u8 *_end, struct FacReqResume *resume)
++{
++      int len = *p++;
++      int ret;
++      __u8 *end = p + len;
++
++      if (end > _end) return -1;
++
++      CAPI_GET(capiGetStruct, &resume->CallIdentity);
++
++      if (p != end) return -1;
++      return len + 1;
++}
++
++static __inline__ int capiGetFacReqCFActivate(__u8 *p, __u8 *_end, struct FacReqCFActivate *CFActivate)
++{
++      int len = *p++;
++      int ret;
++      __u8 *end = p + len;
++
++      if (end > _end) return -1;
++
++      CAPI_GET(capiGetDWord, &CFActivate->Handle);
++      CAPI_GET(capiGetWord, &CFActivate->Procedure);
++      CAPI_GET(capiGetWord, &CFActivate->BasicService);
++      CAPI_GET(capiGetStruct, &CFActivate->ServedUserNumber);
++      CAPI_GET(capiGetStruct, &CFActivate->ForwardedToNumber);
++      CAPI_GET(capiGetStruct, &CFActivate->ForwardedToSubaddress);
++
++      if (p != end) return -1;
++      return len + 1;
++}
++
++static __inline__ int capiGetFacReqCFDeactivate(__u8 *p, __u8 *_end, struct FacReqCFDeactivate *CFDeactivate)
++{
++      int len = *p++;
++      int ret;
++      __u8 *end = p + len;
++
++      if (end > _end) return -1;
++
++      CAPI_GET(capiGetDWord, &CFDeactivate->Handle);
++      CAPI_GET(capiGetWord, &CFDeactivate->Procedure);
++      CAPI_GET(capiGetWord, &CFDeactivate->BasicService);
++      CAPI_GET(capiGetStruct, &CFDeactivate->ServedUserNumber);
++
++      if (p != end) return -1;
++      return len + 1;
++}
++
++#define capiGetFacReqCFInterrogateParameters capiGetFacReqCFDeactivate
++
++static __inline__ int capiGetFacReqCFInterrogateNumbers(__u8 *p, __u8 *_end, struct FacReqCFInterrogateNumbers *CFInterrogateNumbers)
++{
++      int len = *p++;
++      int ret;
++      __u8 *end = p + len;
++
++      if (end > _end) return -1;
++
++      CAPI_GET(capiGetDWord, &CFInterrogateNumbers->Handle);
++
++      if (p != end)
++              return(-1);
++      return(len + 1);
++}
++
++
++static __inline__ int capiGetFacReqParm(__u8 *p, struct FacReqParm *facReqParm)
++{
++      int len = *p++;
++      int ret;
++      __u8 *end = p + len;
++
++      CAPI_GET(capiGetWord, &facReqParm->Function);
++
++      switch (facReqParm->Function) {
++              case 0x0000: // GetSupportedServices
++                      if (*p++ != 0x00) return -1; // empty struct
++                      break;
++              case 0x0001: // Listen
++                      CAPI_GET(capiGetFacReqListen, &facReqParm->u.Listen);
++                      break;
++              case 0x0004: // Suspend
++                      CAPI_GET(capiGetFacReqSuspend, &facReqParm->u.Suspend);
++                      break;
++              case 0x0005: // Resume
++                      CAPI_GET(capiGetFacReqResume, &facReqParm->u.Resume);
++                      break;
++              case 0x0009: // CF Activate
++                      CAPI_GET(capiGetFacReqCFActivate, &facReqParm->u.CFActivate);
++                      break;
++              case 0x000a: // CF Deactivate
++                      CAPI_GET(capiGetFacReqCFDeactivate, &facReqParm->u.CFDeactivate);
++                      break;
++              case 0x000b: // CF Interrogate Parameters
++                      CAPI_GET(capiGetFacReqCFInterrogateParameters, &facReqParm->u.CFInterrogateParameters);
++                      break;
++              case 0x000c: // CF Interrogate Numbers
++                      CAPI_GET(capiGetFacReqCFInterrogateNumbers, &facReqParm->u.CFInterrogateNumbers);
++                      break;
++              default:
++                      return(len + 1);
++      }
++
++      if (p != end)
++              return(-1);
++      return(len + 1);
++}
++
++static int
++GetSupportedServices(FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      facConfParm->u.GetSupportedServices.SupplementaryServiceInfo = CapiSuccess;
++      facConfParm->u.GetSupportedServices.SupportedServices = mISDNSupportedServices;
++      return CapiSuccess;
++}
++static int
++FacListen(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      if (facReqParm->u.Listen.NotificationMask &~ mISDNSupportedServices) {
++              facConfParm->u.Info.SupplementaryServiceInfo = CapiSupplementaryServiceNotSupported;
++      } else {
++              appl->NotificationMask = facReqParm->u.Listen.NotificationMask;
++              facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
++      }
++      return CapiSuccess;
++}
++
++static int
++FacCFInterrogateParameters(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      SSProcess_t     *sspc;
++      struct sk_buff  *skb = mISDN_alloc_l3msg(260, MT_FACILITY);
++      __u8            *p;
++
++      if (!skb)
++              return CAPI_MSGOSRESOURCEERR;
++      sspc = SSProcessConstr(appl, facReqParm->Function, 
++                                facReqParm->u.CFInterrogateParameters.Handle);
++      if (!sspc) {
++              kfree_skb(skb);
++              return CAPI_MSGOSRESOURCEERR;
++      }
++
++      p = encodeInvokeComponentHead(sspc->buf);
++      p += encodeInt(p, sspc->invokeId);
++      p += encodeInt(p, 0x0b); // interrogationDiversion
++      p += encodeInterrogationDiversion(p,  &facReqParm->u.CFInterrogateParameters);
++      encodeInvokeComponentLength(sspc->buf, p);
++      mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
++
++      SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb);
++      SSProcessAddTimer(sspc, T_INTERROGATE);
++
++      facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
++      return CapiSuccess;
++}
++
++static int
++FacCFInterrogateNumbers(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      SSProcess_t     *sspc;
++      struct sk_buff  *skb = mISDN_alloc_l3msg(260, MT_FACILITY);
++      __u8            *p;
++
++      if (!skb)
++              return CAPI_MSGOSRESOURCEERR;
++      sspc = SSProcessConstr(appl, facReqParm->Function, 
++                                facReqParm->u.CFInterrogateNumbers.Handle);
++      if (!sspc) {
++              kfree_skb(skb);
++              return CAPI_MSGOSRESOURCEERR;
++      }
++
++      p = encodeInvokeComponentHead(sspc->buf);
++      p += encodeInt(p, sspc->invokeId);
++      p += encodeInt(p, 0x11); // InterrogateServedUserNumbers
++      encodeInvokeComponentLength(sspc->buf, p);
++      mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
++      SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb);
++      SSProcessAddTimer(sspc, T_INTERROGATE);
++
++      facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
++      return CapiSuccess;
++}
++
++static int
++FacCFActivate(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      SSProcess_t     *sspc;
++      struct sk_buff  *skb = mISDN_alloc_l3msg(260, MT_FACILITY);
++      __u8            *p;
++
++      if (!skb)
++              return CAPI_MSGOSRESOURCEERR;
++      sspc = SSProcessConstr(appl, facReqParm->Function, facReqParm->u.CFActivate.Handle);
++      if (!sspc) {
++              kfree_skb(skb);
++              return CAPI_MSGOSRESOURCEERR;
++      }
++      p = encodeInvokeComponentHead(sspc->buf);
++      p += encodeInt(p, sspc->invokeId);
++      p += encodeInt(p, 0x07); // activationDiversion
++      p += encodeActivationDiversion(p, &facReqParm->u.CFActivate);
++      encodeInvokeComponentLength(sspc->buf, p);
++      mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
++      SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb);
++      SSProcessAddTimer(sspc, T_ACTIVATE);
++
++      facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
++      return CapiSuccess;
++}
++
++static int
++FacCFDeactivate(Application_t *appl, FacReqParm_t *facReqParm, FacConfParm_t *facConfParm)
++{
++      SSProcess_t     *sspc;
++      struct sk_buff  *skb = mISDN_alloc_l3msg(260, MT_FACILITY);
++      __u8            *p;
++
++      if (!skb)
++              return CAPI_MSGOSRESOURCEERR;
++      sspc = SSProcessConstr(appl, facReqParm->Function, facReqParm->u.CFDeactivate.Handle);
++      if (!sspc) {
++              kfree_skb(skb);
++              return CAPI_MSGOSRESOURCEERR;
++      }
++      p = encodeInvokeComponentHead(sspc->buf);
++      p += encodeInt(p, sspc->invokeId);
++      p += encodeInt(p, 0x08); // dectivationDiversion
++      p += encodeDeactivationDiversion(p, &facReqParm->u.CFDeactivate);
++      encodeInvokeComponentLength(sspc->buf, p);
++      mISDN_AddIE(skb, IE_FACILITY, sspc->buf);
++
++      SSProcess_L4L3(sspc, CC_FACILITY | REQUEST, skb);
++      SSProcessAddTimer(sspc, T_DEACTIVATE);
++
++      facConfParm->u.Info.SupplementaryServiceInfo = CapiSuccess;
++      return CapiSuccess;
++}
++ 
++void
++SupplementaryFacilityReq(Application_t *appl, _cmsg *cmsg)
++{
++      __u8            tmp[10];
++      __u16           Info;
++      FacReqParm_t    facReqParm;
++      FacConfParm_t   facConfParm;
++      Plci_t          *plci;
++      AppPlci_t       *aplci;
++      int             ret;
++
++      if (capiGetFacReqParm(cmsg->FacilityRequestParameter, &facReqParm) < 0) {
++              SendCmsgAnswer2Application(appl, cmsg, CapiIllMessageParmCoding);
++              return;
++      }
++      facConfParm.Function = facReqParm.Function;
++      switch (facReqParm.Function) {
++              case 0x0000: // GetSupportedServices
++                      Info = GetSupportedServices(&facReqParm, &facConfParm);
++                      break;
++              case 0x0001: // Listen
++                      Info = FacListen(appl, &facReqParm, &facConfParm);
++                      break;
++              case 0x0004: // Suspend
++                      aplci = getAppPlci4addr(appl, cmsg->adr.adrPLCI);
++                      if (!aplci) {
++                              Info = CapiIllContrPlciNcci;
++                              break;
++                      }
++                      Info = AppPlciFacSuspendReq(aplci, &facReqParm, &facConfParm);
++                      break;
++              case 0x0005: // Resume
++                      ret = ControllerNewPlci(appl->contr, &plci, MISDN_ID_ANY);
++                      if (ret) {
++                              Info = CapiNoPlciAvailable;
++                              break;
++                      }
++                      aplci = ApplicationNewAppPlci(appl, plci);
++                      if (!aplci) {
++                              ControllerReleasePlci(plci);
++                              Info = CapiNoPlciAvailable;
++                              break;
++                      }
++                      Info = AppPlciFacResumeReq(aplci, &facReqParm, &facConfParm);
++                      if (Info == CapiSuccess)
++                              cmsg->adr.adrPLCI = plci->addr;
++                      break;
++              case 0x0009: // CF Activate
++                      Info = FacCFActivate(appl, &facReqParm, &facConfParm);
++                      break;
++              case 0x000a: // CF Deactivate
++                      Info = FacCFDeactivate(appl, &facReqParm, &facConfParm);
++                      break;
++              case 0x000b: // CF Interrogate Parameters
++                      Info = FacCFInterrogateParameters(appl, &facReqParm, &facConfParm);
++                      break;
++              case 0x000c: // CF Interrogate Numbers
++                      Info = FacCFInterrogateNumbers(appl, &facReqParm, &facConfParm);
++                      break;
++              default:
++                      Info = CapiSuccess;
++                      facConfParm.u.Info.SupplementaryServiceInfo = CapiSupplementaryServiceNotSupported;
++      }
++
++      if (Info == 0x0000)
++              capiEncodeFacConfParm(tmp, &facConfParm);
++      else
++              tmp[0] = 0;
++      cmsg->FacilityConfirmationParameter = tmp;
++      SendCmsgAnswer2Application(appl, cmsg, Info);
++}
++
++SSProcess_t *
++SSProcessConstr(Application_t *appl, __u16 Function, __u32 Handle)
++{
++      SSProcess_t     *sspc;
++
++      sspc = SSProcess_alloc();
++      if (!sspc)
++              return(NULL);
++      memset(sspc, 0, sizeof(SSProcess_t));
++      sspc->ApplId = appl->ApplId;
++      sspc->Function = Function;
++      sspc->Handle = Handle;
++      ControllerAddSSProcess(appl->contr, sspc);
++      return(sspc);
++}
++
++void SSProcessDestr(SSProcess_t *sspc)
++{
++      del_timer(&sspc->tl);
++      list_del_init(&sspc->head);
++      SSProcess_free(sspc);
++}
++
++static void
++SendSSFacilityInd(Application_t *appl, __u32 addr, __u8 *para)
++{
++      _cmsg   *cmsg;
++
++      CMSG_ALLOC(cmsg);
++      capi_cmsg_header(cmsg, appl->ApplId, CAPI_FACILITY, CAPI_IND, appl->MsgId++, addr);
++      cmsg->FacilitySelector = 0x0003;
++      cmsg->FacilityIndicationParameter = para;
++      SendCmsg2Application(appl, cmsg);
++}
++
++static void
++SendSSFacilityInd2All(Controller_t *contr, __u32 nMask, __u8 *para)
++{
++      struct list_head        *item;
++      Application_t           *appl;
++
++      list_for_each(item, &contr->Applications) {
++              appl = (Application_t *)item;
++              if (test_bit(APPL_STATE_RELEASE, &appl->state))
++                      continue;
++              if (!(appl->NotificationMask & nMask))
++                      continue;
++              SendSSFacilityInd(appl, contr->addr, para);
++      }
++}
++
++void
++SSProcessTimeout(unsigned long arg)
++{
++      SSProcess_t     *sspc = (SSProcess_t *) arg;
++      Application_t   *appl;
++      __u8            tmp[10], *p;
++
++      appl = getApplication4Id(sspc->contr, sspc->ApplId);
++      if (!appl) {
++              SSProcessDestr(sspc);
++              return;
++      }
++      p = &tmp[1];
++      p += capiEncodeWord(p, sspc->Function);
++      p += capiEncodeFacIndCFact(p, CapiTimeOut, sspc->Handle);
++      tmp[0] = p - &tmp[1];
++      SendSSFacilityInd(appl, sspc->addr, tmp);
++      SSProcessDestr(sspc);
++}
++
++void SSProcessAddTimer(SSProcess_t *sspc, int msec)
++{
++      sspc->tl.function = SSProcessTimeout;
++      sspc->tl.data = (unsigned long) sspc;
++      init_timer(&sspc->tl);
++      sspc->tl.expires = jiffies + (msec * HZ) / 1000;
++      add_timer(&sspc->tl);
++}
++
++
++#if 0
++void printPublicPartyNumber(struct PublicPartyNumber *publicPartyNumber)
++{
++      printk("(%d) %s\n", publicPartyNumber->publicTypeOfNumber, 
++             publicPartyNumber->numberDigits);
++}
++
++void printPartyNumber(struct PartyNumber *partyNumber)
++{
++      switch (partyNumber->type) {
++      case 0: 
++              printk("unknown %s\n", partyNumber->p.unknown);
++              break;
++      case 1:
++              printPublicPartyNumber(&partyNumber->p.publicPartyNumber);
++              break;
++      }
++}
++
++void printServedUserNr(struct ServedUserNr *servedUserNr)
++{
++      if (servedUserNr->all) {
++              printk("all\n");
++      } else {
++              printPartyNumber(&servedUserNr->partyNumber);
++      }
++}
++
++void printAddress(struct Address *address)
++{
++      printPartyNumber(&address->partyNumber);
++      if (address->partySubaddress[0]) {
++              printk("sub %s\n", address->partySubaddress);
++      }
++}
++#endif
++
++static void 
++SSProcessFacility(Controller_t *contr, Q931_info_t *qi)
++{
++      Application_t           *appl;
++        int                   ie_len;
++      struct asn1_parm        parm;
++      SSProcess_t             *sspc;
++      __u8                    tmp[256];
++        __u8                  *p, *end;
++
++        if (!qi || !qi->facility) {
++              int_error();
++                return;
++      }
++      p = (__u8 *)qi;
++      p += L3_EXTRA_SIZE + qi->facility;
++      p++;
++        ie_len = *p++;
++        end = p + ie_len;
++//        if (end > skb->data + skb->len) {
++//                int_error();
++//                return;
++//        }
++
++        if (*p++ != 0x91) { // Supplementary Service Applications
++              int_error();
++                return;
++        }
++      ParseComponent(&parm, p, end);
++      switch (parm.comp) {
++              case invoke:
++#if 0
++                      printk("invokeId %d\n", parm.u.inv.invokeId);
++                      printk("operationValue %d\n", parm.u.inv.operationValue);
++#endif
++                      switch (parm.u.inv.operationValue) {
++                              case 0x0009: 
++#if 0
++                                      printk("procedure %d basicService %d\n", parm.c.inv.o.actNot.procedure,
++                                              parm.c.inv.o.actNot.basicService);
++                                      printServedUserNr(&parm.c.inv.o.actNot.servedUserNr);
++                                      printAddress(&parm.c.inv.o.actNot.address);
++#endif
++                                      p = &tmp[1];
++                                      p += capiEncodeWord(p, 0x8006);
++                                      p += capiEncodeFacIndCFNotAct(p, &parm.u.inv.o.actNot);
++                                      tmp[0] = p - &tmp[1];
++                                      SendSSFacilityInd2All(contr, SuppServiceCF, tmp);
++                                      break;
++                              case 0x000a: 
++#if 0
++                                      printk("procedure %d basicService %d\n", parm.c.inv.o.deactNot.procedure,
++                                              parm.c.inv.o.deactNot.basicService);
++                                      printServedUserNr(&parm.c.inv.o.deactNot.servedUserNr);
++#endif
++                                      p = &tmp[1];
++                                      p += capiEncodeWord(p, 0x8007);
++                                      p += capiEncodeFacIndCFNotDeact(p, &parm.u.inv.o.deactNot);
++                                      tmp[0] = p - &tmp[1];
++                                      SendSSFacilityInd2All(contr, SuppServiceCF, tmp);
++                                      break;
++                              default:
++                                      int_error();
++                      }
++                      break;
++              case returnResult:
++                      sspc = getSSProcess4Id(contr, parm.u.retResult.invokeId);
++                      if (!sspc)
++                              return;
++                      appl = getApplication4Id(contr, sspc->ApplId);
++                      if (!appl)
++                              return;
++                      p = &tmp[1];
++                      p += capiEncodeWord(p, sspc->Function);
++                      switch (sspc->Function) {
++                              case 0x0009:
++                                      p += capiEncodeFacIndCFact(p, 0, sspc->Handle);
++                                      break;
++                              case 0x000a:
++                                      p += capiEncodeFacIndCFdeact(p, 0, sspc->Handle);
++                                      break;
++                              case 0x000b:
++                                      p += capiEncodeFacIndCFinterParameters(p, 0, sspc->Handle, 
++                                              &parm.u.retResult.o.resultList);
++                                      break;
++                              case 0x000c:
++                                      p += capiEncodeFacIndCFinterNumbers(p, 0, sspc->Handle, 
++                                              &parm.u.retResult.o.list);
++                                      break;
++                              default:
++                                      int_error();
++                                      break;
++                      }
++                      tmp[0] = p - &tmp[1];
++                      SendSSFacilityInd(appl, sspc->addr, tmp);
++                      SSProcessDestr(sspc);
++                      break;
++              case returnError:
++                      sspc = getSSProcess4Id(contr, parm.u.retResult.invokeId);
++                      if (!sspc)
++                              return;
++                      appl = getApplication4Id(contr, sspc->ApplId);
++                      if (!appl)
++                              return;
++                      p = &tmp[1];
++                      p += capiEncodeWord(p, sspc->Function);
++                      p += capiEncodeFacIndCFact(p, 0x3600 | (parm.u.retError.errorValue & 0xff), 
++                              sspc->Handle);
++                      tmp[0] = p - &tmp[1];
++                      SendSSFacilityInd(appl, sspc->addr, tmp);
++                      SSProcessDestr(sspc);
++                      break;
++              default:
++                      int_error();
++      }
++}
++
++int
++Supplementary_l3l4(Controller_t *contr, __u32 prim, struct sk_buff *skb)
++{
++      int ret = -EINVAL;
++
++      if (!skb)
++              return(ret);
++      switch (prim) {
++              case CC_FACILITY | INDICATION:
++                      if (skb->len)
++                              SSProcessFacility(contr, (Q931_info_t *)skb->data);
++                      dev_kfree_skb(skb);
++                      ret = 0;
++                      break;
++              default:
++                      int_error();
++      }
++      return(ret);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/tei.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/tei.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/tei.c        1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/tei.c    2004-11-22 09:33:38.432701256 +0000
+@@ -0,0 +1,520 @@
++/* $Id$
++ *
++ * Author       Karsten Keil (keil@isdn4linux.de)
++ *
++ *            This file is (c) under GNU PUBLIC LICENSE
++ *            For changes and modifications please read
++ *            ../../../Documentation/isdn/mISDN.cert
++ *
++ */
++#include "layer2.h"
++#include "helper.h"
++#include "debug.h"
++#include <linux/random.h>
++
++const char *tei_revision = "$Revision$";
++
++#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
++
++static
++struct Fsm teifsm =
++{NULL, 0, 0, NULL, NULL};
++
++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_REMOVE,
++      EV_VERIFY,
++      EV_T202,
++};
++
++#define TEI_EVENT_COUNT (EV_T202+1)
++
++static char *strTeiEvent[] =
++{
++      "EV_IDREQ",
++      "EV_ASSIGN",
++      "EV_ASSIGN_REQ",
++      "EV_DENIED",
++      "EV_CHKREQ",
++      "EV_REMOVE",
++      "EV_VERIFY",
++      "EV_T202",
++};
++
++unsigned int
++random_ri(void)
++{
++      unsigned int x;
++
++      get_random_bytes(&x, sizeof(x));
++      return (x & 0xffff);
++}
++
++static teimgr_t *
++findtei(teimgr_t *tm, int tei)
++{
++      static teimgr_t *ptr = NULL;
++      struct sk_buff *skb;
++
++      if (tei == 127)
++              return (NULL);
++      skb = create_link_skb(MDL_FINDTEI | REQUEST, tei, sizeof(void), &ptr, 0);
++      if (!skb)
++              return (NULL);
++      if (!tei_l2(tm->l2, skb))
++              return(ptr);
++      dev_kfree_skb(skb);
++      return (NULL);
++}
++
++static void
++put_tei_msg(teimgr_t *tm, 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(FLG_LAPD_NET, &tm->l2->flag))
++              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 = create_link_skb(MDL_UNITDATA | REQUEST, 0, 8, bp, 0);
++      if (!skb) {
++              printk(KERN_WARNING "mISDN: No skb for TEI manager\n");
++              return;
++      }
++      if (tei_l2(tm->l2, skb))
++              dev_kfree_skb(skb);
++}
++
++static void
++tei_id_request(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *tm = fi->userdata;
++
++      if (tm->l2->tei != -1) {
++              tm->tei_m.printdebug(&tm->tei_m,
++                      "assign request for allready assigned tei %d",
++                      tm->l2->tei);
++              return;
++      }
++      tm->ri = random_ri();
++      if (tm->debug)
++              tm->tei_m.printdebug(&tm->tei_m,
++                      "assign request ri %d", tm->ri);
++      put_tei_msg(tm, ID_REQUEST, tm->ri, 127);
++      mISDN_FsmChangeState(fi, ST_TEI_IDREQ);
++      mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 1);
++      tm->N202 = 3;
++}
++
++static void
++tei_assign_req(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *tm = fi->userdata;
++      u_char *dp = arg;
++
++      if (tm->l2->tei == -1) {
++              tm->tei_m.printdebug(&tm->tei_m,
++                      "net tei assign request without tei");
++              return;
++      }
++      tm->ri = ((unsigned int) *dp++ << 8);
++      tm->ri += *dp++;
++      if (tm->debug)
++              tm->tei_m.printdebug(&tm->tei_m,
++                      "net assign request ri %d teim %d", tm->ri, *dp);
++      put_tei_msg(tm, ID_ASSIGNED, tm->ri, tm->l2->tei);
++      mISDN_FsmChangeState(fi, ST_TEI_NOP);
++}
++
++static void
++tei_id_assign(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *otm, *tm = fi->userdata;
++      struct sk_buff *skb;
++      u_char *dp = arg;
++      int ri, tei;
++
++      ri = ((unsigned int) *dp++ << 8);
++      ri += *dp++;
++      dp++;
++      tei = *dp >> 1;
++      if (tm->debug)
++              tm->tei_m.printdebug(fi, "identity assign ri %d tei %d",
++                      ri, tei);
++      if ((otm = findtei(tm, tei))) { /* same tei is in use */
++              if (ri != otm->ri) {
++                      tm->tei_m.printdebug(fi,
++                              "possible duplicate assignment tei %d", tei);
++                      skb = create_link_skb(MDL_ERROR | RESPONSE, 0, 0,
++                              NULL, 0);
++                      if (!skb)
++                              return;
++                      if (tei_l2(otm->l2, skb))
++                              dev_kfree_skb(skb);
++              }
++      } else if (ri == tm->ri) {
++              mISDN_FsmDelTimer(&tm->t202, 1);
++              mISDN_FsmChangeState(fi, ST_TEI_NOP);
++              skb = create_link_skb(MDL_ASSIGN | REQUEST, tei, 0, NULL, 0);
++              if (!skb)
++                      return;
++              if (tei_l2(tm->l2, skb))
++                      dev_kfree_skb(skb);
++//            cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
++      }
++}
++
++static void
++tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *otm, *tm = fi->userdata;
++      u_char *dp = arg;
++      int tei, ri;
++
++      ri = ((unsigned int) *dp++ << 8);
++      ri += *dp++;
++      dp++;
++      tei = *dp >> 1;
++      if (tm->debug)
++              tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d",
++                      ri, tei);
++      if ((otm = findtei(tm, tei))) { /* same tei is in use */
++              if (ri != otm->ri) {    /* and it wasn't our request */
++                      tm->tei_m.printdebug(fi,
++                              "possible duplicate assignment tei %d", tei);
++                      mISDN_FsmEvent(&otm->tei_m, EV_VERIFY, NULL);
++              }
++      } 
++}
++
++static void
++tei_id_denied(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *tm = fi->userdata;
++      u_char *dp = arg;
++      int ri, tei;
++
++      ri = ((unsigned int) *dp++ << 8);
++      ri += *dp++;
++      dp++;
++      tei = *dp >> 1;
++      if (tm->debug)
++              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)
++{
++      teimgr_t *tm = fi->userdata;
++      u_char *dp = arg;
++      int tei;
++
++      tei = *(dp+3) >> 1;
++      if (tm->debug)
++              tm->tei_m.printdebug(fi, "identity check req tei %d", tei);
++      if ((tm->l2->tei != -1) && ((tei == GROUP_TEI) || (tei == tm->l2->tei))) {
++              mISDN_FsmDelTimer(&tm->t202, 4);
++              mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
++              put_tei_msg(tm, ID_CHK_RES, random_ri(), tm->l2->tei);
++      }
++}
++
++static void
++tei_id_remove(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *tm = fi->userdata;
++      u_char *dp = arg;
++      struct sk_buff *skb;
++      int tei;
++
++      tei = *(dp+3) >> 1;
++      if (tm->debug)
++              tm->tei_m.printdebug(fi, "identity remove tei %d", tei);
++      if ((tm->l2->tei != -1) && ((tei == GROUP_TEI) || (tei == tm->l2->tei))) {
++              mISDN_FsmDelTimer(&tm->t202, 5);
++              mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
++              skb = create_link_skb(MDL_REMOVE | REQUEST, 0, 0, NULL, 0);
++              if (!skb)
++                      return;
++              if (tei_l2(tm->l2, skb))
++                      dev_kfree_skb(skb);
++//            cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
++      }
++}
++
++static void
++tei_id_verify(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *tm = fi->userdata;
++
++      if (tm->debug)
++              tm->tei_m.printdebug(fi, "id verify request for tei %d",
++                      tm->l2->tei);
++      put_tei_msg(tm, ID_VERIFY, 0, tm->l2->tei);
++      mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
++      mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 2);
++      tm->N202 = 2;
++}
++
++static void
++tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *tm = fi->userdata;
++      struct sk_buff *skb;
++
++      if (--tm->N202) {
++              tm->ri = random_ri();
++              if (tm->debug)
++                      tm->tei_m.printdebug(fi, "assign req(%d) ri %d",
++                              4 - tm->N202, tm->ri);
++              put_tei_msg(tm, ID_REQUEST, tm->ri, 127);
++              mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 3);
++      } else {
++              tm->tei_m.printdebug(fi, "assign req failed");
++              skb = create_link_skb(MDL_ERROR | REQUEST, 0, 0, NULL, 0);
++              if (!skb)
++                      return;
++              if (tei_l2(tm->l2, skb))
++                      dev_kfree_skb(skb);
++//            cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
++              mISDN_FsmChangeState(fi, ST_TEI_NOP);
++      }
++}
++
++static void
++tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
++{
++      teimgr_t *tm = fi->userdata;
++      struct sk_buff *skb;
++
++      if (--tm->N202) {
++              if (tm->debug)
++                      tm->tei_m.printdebug(fi,
++                              "id verify req(%d) for tei %d",
++                              3 - tm->N202, tm->l2->tei);
++              put_tei_msg(tm, ID_VERIFY, 0, tm->l2->tei);
++              mISDN_FsmAddTimer(&tm->t202, tm->T202, EV_T202, NULL, 4);
++      } else {
++              tm->tei_m.printdebug(fi, "verify req for tei %d failed",
++                      tm->l2->tei);
++              skb = create_link_skb(MDL_REMOVE | REQUEST, 0, 0, NULL, 0);
++              if (!skb)
++                      return;
++              if (tei_l2(tm->l2, skb))
++                      dev_kfree_skb(skb);
++//            cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
++              mISDN_FsmChangeState(fi, ST_TEI_NOP);
++      }
++}
++
++static int
++tei_ph_data_ind(teimgr_t *tm, int dtyp, struct sk_buff *skb)
++{
++      u_char *dp;
++      int mt;
++      int ret = -EINVAL;
++
++      if (!skb)
++              return(ret);
++      if (test_bit(FLG_FIXED_TEI, &tm->l2->flag) &&
++              !test_bit(FLG_LAPD_NET, &tm->l2->flag))
++              return(ret);
++      if (skb->len < 8) {
++              tm->tei_m.printdebug(&tm->tei_m,
++                      "short mgr frame %ld/8", skb->len);
++              return(ret);
++      }
++      dp = skb->data + 2;
++      if ((*dp & 0xef) != UI) {
++              tm->tei_m.printdebug(&tm->tei_m,
++                      "mgr frame is not ui %x", *dp);
++              return(ret);
++      }
++      dp++;
++      if (*dp++ != TEI_ENTITY_ID) {
++              /* wrong management entity identifier, ignore */
++              dp--;
++              tm->tei_m.printdebug(&tm->tei_m,
++                      "tei handler wrong entity id %x", *dp);
++              return(ret);
++      } else {
++              mt = *(dp+2);
++              if (tm->debug)
++                      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_REQUEST && 
++                      test_bit(FLG_LAPD_NET, &tm->l2->flag))
++                      mISDN_FsmEvent(&tm->tei_m, EV_ASSIGN_REQ, dp);
++              else {
++                      tm->tei_m.printdebug(&tm->tei_m,
++                              "tei handler wrong mt %x", mt);
++                      return(ret);
++              }
++      }
++      dev_kfree_skb(skb);
++      return(0);
++}
++
++int
++l2_tei(teimgr_t *tm, struct sk_buff *skb)
++{
++      mISDN_head_t    *hh;
++      int             ret = -EINVAL;
++
++      if (!tm || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      if (tm->debug)
++              printk(KERN_DEBUG "%s: prim(%x)\n", __FUNCTION__, hh->prim);
++      switch(hh->prim) {
++          case (MDL_UNITDATA | INDICATION):
++              return(tei_ph_data_ind(tm, hh->dinfo, skb));
++          case (MDL_ASSIGN | INDICATION):
++              if (test_bit(FLG_FIXED_TEI, &tm->l2->flag)) {
++                      if (tm->debug)
++                              tm->tei_m.printdebug(&tm->tei_m,
++                                      "fixed assign tei %d", tm->l2->tei);
++                      skb_trim(skb, 0);
++                      mISDN_sethead(MDL_ASSIGN | REQUEST, tm->l2->tei, skb);
++                      if (!tei_l2(tm->l2, skb))
++                              return(0);
++//                    cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
++              } else
++                      mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL);
++              break;
++          case (MDL_ERROR | INDICATION):
++              if (!test_bit(FLG_FIXED_TEI, &tm->l2->flag))
++                      mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL);
++              break;
++      }
++      dev_kfree_skb(skb);
++      return(0);
++}
++
++static void
++tei_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      teimgr_t        *tm = fi->userdata;
++      logdata_t       log;
++      char            head[24];
++
++      va_start(log.args, fmt);
++      sprintf(head,"tei %s", tm->l2->inst.name);
++      log.fmt = fmt;
++      log.head = head;
++      tm->l2->inst.obj->ctrl(&tm->l2->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++static struct FsmNode TeiFnList[] =
++{
++      {ST_TEI_NOP, EV_IDREQ, tei_id_request},
++      {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
++      {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req},
++      {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_T202, tei_id_req_tout},
++      {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
++      {ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
++      {ST_TEI_IDVERIFY, EV_T202, tei_id_ver_tout},
++      {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
++      {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
++};
++
++#define TEI_FN_COUNT (sizeof(TeiFnList)/sizeof(struct FsmNode))
++
++void
++release_tei(teimgr_t *tm)
++{
++      mISDN_FsmDelTimer(&tm->t202, 1);
++      kfree(tm);
++}
++
++int
++create_teimgr(layer2_t *l2) {
++      teimgr_t *ntei;
++
++      if (!l2) {
++              printk(KERN_ERR "create_tei no layer2\n");
++              return(-EINVAL);
++      }
++      if (!(ntei = kmalloc(sizeof(teimgr_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc teimgr failed\n");
++              return(-ENOMEM);
++      }
++      memset(ntei, 0, sizeof(teimgr_t));
++      ntei->l2 = l2;
++      ntei->T202 = 2000;      /* T202  2000 milliseconds */
++      ntei->debug = l2->debug;
++      ntei->tei_m.debug = l2->debug;
++      ntei->tei_m.userdata = ntei;
++      ntei->tei_m.printdebug = tei_debug;
++      if (test_bit(FLG_LAPD_NET, &l2->flag)) {
++              ntei->tei_m.fsm = &teifsm;
++              ntei->tei_m.state = ST_TEI_NOP;
++      } else {
++              ntei->tei_m.fsm = &teifsm;
++              ntei->tei_m.state = ST_TEI_NOP;
++      }
++      mISDN_FsmInitTimer(&ntei->tei_m, &ntei->t202);
++      l2->tm = ntei;
++      return(0);
++}
++
++int TEIInit(void)
++{
++      teifsm.state_count = TEI_STATE_COUNT;
++      teifsm.event_count = TEI_EVENT_COUNT;
++      teifsm.strEvent = strTeiEvent;
++      teifsm.strState = strTeiState;
++      mISDN_FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
++      return(0);
++}
++
++void TEIFree(void)
++{
++      mISDN_FsmFree(&teifsm);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/udevice.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/udevice.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/udevice.c    1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/udevice.c        2004-11-22 09:33:38.442699736 +0000
+@@ -0,0 +1,1882 @@
++/* $Id$
++ *
++ * Copyright 2000  by Karsten Keil <kkeil@isdn4linux.de>
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/stddef.h>
++#include <linux/poll.h>
++#include <linux/vmalloc.h>
++#include <linux/config.h>
++#include <linux/timer.h>
++#include "core.h"
++
++#define MAX_HEADER_LEN        4
++
++#define FLG_MGR_SETSTACK      1
++#define FLG_MGR_OWNSTACK      2
++
++#define FLG_MGR_TIMER_INIT    1
++#define       FLG_MGR_TIMER_RUNING    2
++
++
++typedef struct _devicelayer {
++      struct list_head        list;
++      mISDNdevice_t           *dev;
++      mISDNinstance_t         inst;
++      mISDNinstance_t         *slave;
++      mISDNif_t               s_up;
++      mISDNif_t               s_down;
++      int                     iaddr;
++      int                     lm_st;
++      u_long                  Flags;
++} devicelayer_t;
++
++typedef struct _devicestack {
++      struct list_head        list;
++      mISDNdevice_t           *dev;
++      mISDNstack_t            *st;
++      int                     extentions;
++} devicestack_t;
++
++typedef struct _mISDNtimer {
++      struct list_head        list;
++      struct _mISDNdevice     *dev;
++      struct timer_list       tl;
++      int                     id;
++      u_long                  Flags;
++} mISDNtimer_t;
++
++typedef struct entity_item {
++      struct list_head        head;
++      int                     entity;
++} entity_item_t;
++
++static LIST_HEAD(mISDN_devicelist);
++static rwlock_t       mISDN_device_lock = RW_LOCK_UNLOCKED;
++
++static mISDNobject_t  udev_obj;
++static char MName[] = "UserDevice";
++
++static int device_debug = 0;
++
++static int from_up_down(mISDNif_t *, struct sk_buff *);
++
++// static int from_peer(mISDNif_t *, u_int, int, int, void *);
++// static int to_peer(mISDNif_t *, u_int, int, int, void *);
++
++
++static mISDNdevice_t *
++get_mISDNdevice4minor(int minor)
++{
++      mISDNdevice_t   *dev;
++
++      read_lock(&mISDN_device_lock);
++      list_for_each_entry(dev, &mISDN_devicelist, list) {
++              if (dev->minor == minor) {
++                      read_unlock(&mISDN_device_lock);
++                      return(dev);
++              }
++      }
++      read_unlock(&mISDN_device_lock);
++      return(NULL);
++}
++
++static int
++mISDN_rdata_raw(mISDNif_t *hif, struct sk_buff *skb) {
++      mISDNdevice_t   *dev;
++      mISDN_head_t    *hh;
++      u_long          flags;
++      int             retval = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      dev = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      if (hh->prim == (PH_DATA | INDICATION)) {
++              if (test_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag)) {
++                      spin_lock_irqsave(&dev->rport.lock, flags);
++                      if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen)
++                              retval = -ENOSPC;
++                      else
++                              skb_queue_tail(&dev->rport.queue, skb);
++                      spin_unlock_irqrestore(&dev->rport.lock, flags);
++                      wake_up_interruptible(&dev->rport.procq);
++              } else {
++                      printk(KERN_WARNING "%s: PH_DATA_IND device(%d) not read open\n",
++                              __FUNCTION__, dev->minor);
++                      retval = -ENOENT;
++              }
++      } else if (hh->prim == (PH_DATA | CONFIRM)) {
++              test_and_clear_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag);
++              dev_kfree_skb_any(skb);
++              spin_lock_irqsave(&dev->wport.lock, flags);
++              if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) {
++                      spin_unlock_irqrestore(&dev->wport.lock, flags);
++                      return(0);
++              }
++              while ((skb = skb_dequeue(&dev->wport.queue))) {
++                      if (device_debug & DEBUG_DEV_OP)
++                              printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag);
++                      if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) {
++                              skb_queue_head(&dev->wport.queue, skb); 
++                              break;
++                      }
++                      if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) {
++                              spin_unlock_irqrestore(&dev->wport.lock, flags);
++                              retval = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb);
++                              spin_lock_irqsave(&dev->wport.lock, flags);
++                              if (retval) {
++                                      printk(KERN_WARNING "%s: dev(%d) down err(%d)\n",
++                                              __FUNCTION__, dev->minor, retval);
++                                      dev_kfree_skb(skb);
++                              } else {
++                                      test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag);
++                                      wake_up(&dev->wport.procq);
++                                      break;
++                              }
++                      } else {
++                              printk(KERN_WARNING "%s: dev(%d) wport not enabled\n",
++                                      __FUNCTION__, dev->minor);
++                              dev_kfree_skb(skb);
++                      }
++                      wake_up(&dev->wport.procq);
++              }
++              test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag);
++              spin_unlock_irqrestore(&dev->wport.lock, flags);
++      } else if ((hh->prim == (PH_ACTIVATE | CONFIRM)) ||
++              (hh->prim == (PH_ACTIVATE | INDICATION))) {
++                      test_and_set_bit(FLG_mISDNPORT_ENABLED,
++                              &dev->wport.Flag);
++                      test_and_clear_bit(FLG_mISDNPORT_BLOCK,
++                              &dev->wport.Flag);
++      } else if ((hh->prim == (PH_DEACTIVATE | CONFIRM)) ||
++              (hh->prim == (PH_DEACTIVATE | INDICATION))) {
++                      test_and_clear_bit(FLG_mISDNPORT_ENABLED,
++                              &dev->wport.Flag);
++      } else {
++              printk(KERN_WARNING "%s: prim(%x) dinfo(%x) not supported\n",
++                      __FUNCTION__, hh->prim, hh->dinfo);
++              retval = -EINVAL;
++      }
++      if (!retval)
++              dev_kfree_skb_any(skb);
++      return(retval);
++}
++
++static int
++mISDN_rdata(mISDNdevice_t *dev, struct sk_buff *skb)
++{
++      mISDN_head_t    *hp;
++      u_long          flags;
++
++      hp = mISDN_HEAD_P(skb);
++      if (hp->len <= 0)
++              skb_trim(skb, 0);
++      if (device_debug & DEBUG_RDATA)
++              printk(KERN_DEBUG "%s: %x:%x %x %d %d\n",
++                      __FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len);
++      spin_lock_irqsave(&dev->rport.lock, flags);
++      if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen) {
++              spin_unlock_irqrestore(&dev->rport.lock, flags);
++              printk(KERN_WARNING "%s: rport queue overflow %d/%d\n",
++                      __FUNCTION__, skb_queue_len(&dev->rport.queue), dev->rport.maxqlen);
++              return(-ENOSPC);
++      }
++      skb_queue_tail(&dev->rport.queue, skb);
++      spin_unlock_irqrestore(&dev->rport.lock, flags);
++      wake_up_interruptible(&dev->rport.procq);
++      return(0);
++}
++static int
++error_answer(mISDNdevice_t *dev, struct sk_buff *skb, int err)
++{
++      mISDN_head_t    *hp;
++
++      hp = mISDN_HEAD_P(skb);
++      hp->prim |= 1; /* CONFIRM or RESPONSE */
++      hp->len = err;
++      return(mISDN_rdata(dev, skb));
++}
++
++static devicelayer_t
++*get_devlayer(mISDNdevice_t *dev, int addr) {
++      devicelayer_t *dl;
++
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr);
++      list_for_each_entry(dl, &dev->layerlist, list) {
++//            if (device_debug & DEBUG_MGR_FUNC)
++//                    printk(KERN_DEBUG "%s: dl(%p) iaddr:%x\n",
++//                            __FUNCTION__, dl, dl->iaddr);
++              if ((u_int)dl->iaddr == (IF_IADDRMASK & addr))
++                      return(dl);
++      }
++      return(NULL);
++}
++
++static devicestack_t
++*get_devstack(mISDNdevice_t *dev, int addr)
++{
++      devicestack_t *ds;
++
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr);
++      list_for_each_entry(ds, &dev->stacklist, list) {
++              if (ds->st && (ds->st->id == (u_int)addr))
++                      return(ds);
++      }
++      return(NULL);
++}
++
++static mISDNtimer_t
++*get_devtimer(mISDNdevice_t *dev, int id)
++{
++      mISDNtimer_t    *ht;
++
++      if (device_debug & DEBUG_DEV_TIMER)
++              printk(KERN_DEBUG "%s: dev:%p id:%x\n", __FUNCTION__, dev, id);
++      list_for_each_entry(ht, &dev->timerlist, list) {
++              if (ht->id == id)
++                      return(ht);
++      }
++      return(NULL);
++}
++
++static int
++stack_inst_flg(mISDNdevice_t *dev, mISDNstack_t *st, int bit, int clear)
++{
++      int ret;
++      devicelayer_t *dl;
++
++      list_for_each_entry(dl, &dev->layerlist, list) {
++              if (dl->inst.st == st) {
++                      if (clear)
++                              ret = test_and_clear_bit(bit, &dl->Flags);
++                      else
++                              ret = test_and_set_bit(bit, &dl->Flags);
++                      return(ret);
++              }
++      }
++      return(-1);
++}
++
++static int
++new_devstack(mISDNdevice_t *dev, stack_info_t *si)
++{
++      int             err;
++      mISDNstack_t    *st;
++      mISDNinstance_t inst;
++      devicestack_t   *nds;
++
++      memset(&inst, 0, sizeof(mISDNinstance_t));
++      st = get_stack4id(si->id);
++      if (si->extentions & EXT_STACK_CLONE) {
++              if (st) {
++                      inst.st = st;
++              } else {
++                      int_errtxt("ext(%x) st(%x)", si->extentions, si->id);
++                      return(-EINVAL);
++              }
++      }
++      err = udev_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &inst);
++      if (err) {
++              int_error();
++              return(err);
++      }
++      if (!(nds = kmalloc(sizeof(devicestack_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc devicestack failed\n");
++              udev_obj.ctrl(inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(-ENOMEM);
++      }
++      memset(nds, 0, sizeof(devicestack_t));
++      nds->dev = dev;
++      if (si->extentions & EXT_STACK_CLONE) {
++//            memcpy(&inst.st->pid, &st->pid, sizeof(mISDN_pid_t));
++              // FIXME that is a ugly idea, but I don't have a better one 
++              inst.st->childlist.prev = &st->childlist;
++      } else {
++              memcpy(&inst.st->pid, &si->pid, sizeof(mISDN_pid_t));
++      }
++      nds->extentions = si->extentions;
++      inst.st->extentions |= si->extentions;
++      inst.st->mgr = get_instance4id(si->mgr);
++      nds->st = inst.st;
++      list_add_tail(&nds->list, &dev->stacklist);
++      return(inst.st->id);
++}
++
++static mISDNstack_t *
++sel_channel(u_int addr, u_int channel)
++{
++      mISDNstack_t    *st;
++      channel_info_t  ci;
++
++      st = get_stack4id(addr);
++      if (!st)
++              return(NULL);
++      ci.channel = channel;
++      ci.st.p = NULL;
++      if (udev_obj.ctrl(st, MGR_SELCHANNEL | REQUEST, &ci))
++              return(NULL);
++      return(ci.st.p);
++}
++
++static int
++create_layer(mISDNdevice_t *dev, struct sk_buff *skb)
++{
++      layer_info_t    *linfo;
++      mISDNlayer_t    *layer;
++      mISDNstack_t    *st;
++      int             i, ret;
++      devicelayer_t   *nl;
++      mISDNobject_t   *obj;
++      mISDNinstance_t *inst = NULL;
++      mISDN_head_t            *hp;
++
++      hp = mISDN_HEAD_P(skb);
++      linfo = (layer_info_t *)skb->data;
++      if (!(st = get_stack4id(linfo->st))) {
++              int_error();
++              return(-ENODEV);
++      }
++      if (linfo->object_id != -1) {
++              obj = get_object(linfo->object_id);
++              if (!obj) {
++                      printk(KERN_WARNING "%s: no object %x found\n",
++                              __FUNCTION__, linfo->object_id);
++                      return(-ENODEV);
++              }
++              ret = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, &linfo->pid);
++              if (ret) {
++                      printk(KERN_WARNING "%s: error nl req %d\n",
++                              __FUNCTION__, ret);
++                      return(ret);
++              }
++              layer = getlayer4lay(st, linfo->pid.layermask);
++              if (!layer) {
++                      printk(KERN_WARNING "%s: no layer for lm(%x)\n",
++                              __FUNCTION__, linfo->pid.layermask);
++                      return(-EINVAL);
++              }
++              inst = layer->inst;
++              if (!inst) {
++                      printk(KERN_WARNING "%s: no inst in layer(%p)\n",
++                              __FUNCTION__, layer);
++                      return(-EINVAL);
++              }
++      } else if ((layer = getlayer4lay(st, linfo->pid.layermask))) {
++              if (!(linfo->extentions & EXT_INST_MIDDLE)) {
++                      printk(KERN_WARNING
++                              "mISDN create_layer st(%x) LM(%x) inst not empty(%p)\n",
++                              st->id, linfo->pid.layermask, layer);
++                      return(-EBUSY);
++              }
++      }
++      if (!(nl = kmalloc(sizeof(devicelayer_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc devicelayer failed\n");
++              return(-ENOMEM);
++      }
++      memset(nl, 0, sizeof(devicelayer_t));
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG
++                      "mISDN create_layer LM(%x) nl(%p) nl inst(%p)\n",
++                      linfo->pid.layermask, nl, &nl->inst);
++      nl->dev = dev;
++      memcpy(&nl->inst.pid, &linfo->pid, sizeof(mISDN_pid_t));
++      strcpy(nl->inst.name, linfo->name);
++      nl->inst.extentions = linfo->extentions;
++      for (i=0; i<= MAX_LAYER_NR; i++) {
++              if (linfo->pid.layermask & ISDN_LAYER(i)) {
++                      if (st && (st->pid.protocol[i] == ISDN_PID_NONE)) {
++                              st->pid.protocol[i] = linfo->pid.protocol[i];
++                              nl->lm_st |= ISDN_LAYER(i);
++                      }
++              }
++      }
++      if (st && (linfo->extentions & EXT_INST_MGR)) {
++              st->mgr = &nl->inst;
++              test_and_set_bit(FLG_MGR_OWNSTACK, &nl->Flags);
++      }
++      nl->inst.down.owner = &nl->inst;
++      nl->inst.up.owner = &nl->inst;
++      nl->inst.obj = &udev_obj;
++      nl->inst.data = nl;
++      list_add_tail(&nl->list, &dev->layerlist);
++      nl->inst.obj->ctrl(st, MGR_REGLAYER | INDICATION, &nl->inst);
++      nl->iaddr = nl->inst.id;
++      skb_trim(skb, 0);
++      memcpy(skb_put(skb, sizeof(nl->iaddr)), &nl->iaddr, sizeof(nl->iaddr));
++      if (inst) {
++              nl->slave = inst;
++              memcpy(skb_put(skb, sizeof(inst->id)), &inst->id, sizeof(inst->id));
++      } else {
++              memset(skb_put(skb, sizeof(nl->iaddr)), 0, sizeof(nl->iaddr));
++      }
++      return(8);
++}
++
++static int
++remove_if(devicelayer_t *dl, int stat) {
++      mISDNif_t *hif,*phif,*shif;
++      int err;
++
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: dl(%p) stat(%x)\n", __FUNCTION__,
++                      dl, stat);
++      phif = NULL;
++      if (stat & IF_UP) {
++              hif = &dl->inst.up;
++              shif = &dl->s_up;
++              if (shif->owner)
++                      phif = &shif->owner->down;
++      } else if (stat & IF_DOWN) {
++              hif = &dl->inst.down;
++              shif = &dl->s_down;
++              if (shif->owner)
++                      phif = &shif->owner->up;
++      } else {
++              printk(KERN_WARNING "%s: stat not UP/DOWN\n", __FUNCTION__);
++              return(-EINVAL);
++      }
++      err = udev_obj.ctrl(hif->peer, MGR_DISCONNECT | REQUEST, hif);
++      if (phif) {
++              memcpy(phif, shif, sizeof(mISDNif_t));
++              memset(shif, 0, sizeof(mISDNif_t));
++      }
++      if (hif->predecessor)
++              hif->predecessor->clone = hif->clone;
++      if (hif->clone)
++              hif->clone->predecessor = hif->predecessor;
++      return(err);
++}
++
++static int
++del_stack(devicestack_t *ds)
++{
++      mISDNdevice_t   *dev;
++
++      if (!ds) {
++              int_error();
++              return(-EINVAL);
++      }
++      dev = ds->dev;
++      if (device_debug & DEBUG_MGR_FUNC) {
++              printk(KERN_DEBUG "%s: ds(%p) dev(%p)\n", 
++                      __FUNCTION__, ds, dev);
++      }
++      if (!dev)
++              return(-EINVAL);
++      if (ds->st) {
++              if (ds->extentions & EXT_STACK_CLONE)
++                      INIT_LIST_HEAD(&ds->st->childlist);
++              udev_obj.ctrl(ds->st, MGR_DELSTACK | REQUEST, NULL);
++      }
++      list_del(&ds->list);
++      kfree(ds);
++      return(0);
++}
++
++static int
++del_layer(devicelayer_t *dl)
++{
++      mISDNinstance_t *inst = &dl->inst;
++      mISDNdevice_t   *dev = dl->dev;
++      int             i;
++
++      if (device_debug & DEBUG_MGR_FUNC) {
++              printk(KERN_DEBUG "%s: dl(%p) inst(%p) LM(%x) dev(%p)\n", 
++                      __FUNCTION__, dl, inst, inst->pid.layermask, dev);
++              printk(KERN_DEBUG "%s: iaddr %x inst %s slave %p\n",
++                      __FUNCTION__, dl->iaddr, inst->name, dl->slave);
++      }
++      remove_if(dl, IF_UP);
++      remove_if(dl, IF_DOWN);
++      if (dl->slave) {
++              if (dl->slave->obj)
++                      dl->slave->obj->own_ctrl(dl->slave,
++                              MGR_UNREGLAYER | REQUEST, NULL);
++              else
++                      dl->slave = NULL; 
++      }
++      if (dl->lm_st && inst->st) {
++              for (i=0; i<= MAX_LAYER_NR; i++) {
++                      if (dl->lm_st & ISDN_LAYER(i)) {
++                              inst->st->pid.protocol[i] = ISDN_PID_NONE;
++                      }
++              }
++              dl->lm_st = 0;
++      }
++      if (test_and_clear_bit(FLG_MGR_SETSTACK, &dl->Flags) && inst->st) {
++              if (device_debug & DEBUG_MGR_FUNC)
++                      printk(KERN_DEBUG "del_layer: CLEARSTACK id(%x)\n",
++                              inst->st->id);
++              udev_obj.ctrl(inst->st, MGR_CLEARSTACK | REQUEST, NULL);
++      }
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      dl->iaddr = 0;
++      list_del(&dl->list);
++      udev_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      if (test_and_clear_bit(FLG_MGR_OWNSTACK, &dl->Flags)) {
++              if (dl->inst.st) {
++                      del_stack(get_devstack(dev, dl->inst.st->id));
++              }
++      }
++      kfree(dl);
++      return(0);
++}
++
++static mISDNinstance_t *
++clone_instance(devicelayer_t *dl, mISDNstack_t  *st, mISDNinstance_t *peer)
++{
++      int             err;
++
++      if (dl->slave) {
++              printk(KERN_WARNING "%s: layer has slave, cannot clone\n",
++                      __FUNCTION__);
++              return(NULL);
++      }
++      if (!(peer->extentions & EXT_INST_CLONE)) {
++              printk(KERN_WARNING "%s: peer cannot clone\n", __FUNCTION__);
++              return(NULL);
++      }
++      dl->slave = (mISDNinstance_t *)st;
++      if ((err = peer->obj->own_ctrl(peer, MGR_CLONELAYER | REQUEST,
++              &dl->slave))) {
++              dl->slave = NULL;
++              printk(KERN_WARNING "%s: peer clone error %d\n",
++                      __FUNCTION__, err);
++              return(NULL);
++      }
++      return(dl->slave);
++}
++
++static int
++connect_if_req(mISDNdevice_t *dev, struct sk_buff *skb)
++{
++      devicelayer_t           *dl;
++      interface_info_t        *ifi = (interface_info_t *)skb->data;
++      mISDNinstance_t         *owner;
++      mISDNinstance_t         *peer;
++      mISDNinstance_t         *pp;
++      mISDNif_t               *hifp;
++      int                     stat;
++      mISDN_head_t            *hp;
++
++      hp = mISDN_HEAD_P(skb);
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n",
++                      __FUNCTION__, hp->addr, ifi->owner, ifi->peer);
++      if (!(dl=get_devlayer(dev, ifi->owner))) {
++              int_errtxt("no devive_layer for %08x", ifi->owner);
++              return(-ENXIO);
++      }
++      if (!(owner = get_instance4id(ifi->owner))) {
++              printk(KERN_WARNING "%s: owner(%x) not found\n",
++                      __FUNCTION__, ifi->owner);
++              return(-ENODEV);
++      }
++      if (!(peer = get_instance4id(ifi->peer))) {
++              printk(KERN_WARNING "%s: peer(%x) not found\n",
++                      __FUNCTION__, ifi->peer);
++              return(-ENODEV);
++      }
++      if (owner->pid.layermask < peer->pid.layermask) {
++              hifp = &peer->down;
++              stat = IF_DOWN;
++      } else if (owner->pid.layermask > peer->pid.layermask) {
++              hifp = &peer->up;
++              stat = IF_UP;
++      } else {
++              int_errtxt("OLM == PLM: %x", owner->pid.layermask);
++              return(-EINVAL);
++      }
++      if (ifi->extentions == EXT_IF_CHAIN) {
++              if (!(pp = hifp->peer)) {
++                      printk(KERN_WARNING "%s: peer if has no peer\n",
++                              __FUNCTION__);
++                      return(-EINVAL);
++              }
++              if (stat == IF_UP) {
++                      memcpy(&owner->up, hifp, sizeof(mISDNif_t));
++                      memcpy(&dl->s_up, hifp, sizeof(mISDNif_t));
++                      owner->up.owner = owner;
++                      hifp->peer = owner;
++                      hifp->func = from_up_down;
++                      hifp->fdata = dl;
++                      hifp = &pp->down;
++                      memcpy(&owner->down, hifp, sizeof(mISDNif_t));
++                      memcpy(&dl->s_down, hifp, sizeof(mISDNif_t));
++                      owner->down.owner = owner;
++                      hifp->peer = owner;
++                      hifp->func = from_up_down;
++                      hifp->fdata = dl;
++              } else {
++                      memcpy(&owner->down, hifp, sizeof(mISDNif_t));
++                      memcpy(&dl->s_down, hifp, sizeof(mISDNif_t));
++                      owner->up.owner = owner;
++                      hifp->peer = owner;
++                      hifp->func = from_up_down;
++                      hifp->fdata = dl;
++                      hifp = &pp->up;
++                      memcpy(&owner->up, hifp, sizeof(mISDNif_t));
++                      memcpy(&dl->s_up, hifp, sizeof(mISDNif_t));
++                      owner->down.owner = owner;
++                      hifp->peer = owner;
++                      hifp->func = from_up_down;
++                      hifp->fdata = dl;
++              }
++              return(0);
++      }
++      if (ifi->extentions & EXT_IF_CREATE) {
++              /* create new instance if allready in use */
++              if (hifp->stat != IF_NOACTIV) {
++                      if ((peer = clone_instance(dl, owner->st, peer))) {
++                              if (stat == IF_UP)
++                                      hifp = &peer->up;
++                              else
++                                      hifp = &peer->down;
++                      } else {
++                              printk(KERN_WARNING "%s: cannot create new peer instance\n",
++                                      __FUNCTION__);
++                              return(-EBUSY);
++                      }
++              }
++      }
++      if (ifi->extentions & EXT_IF_EXCLUSIV) {
++              if (hifp->stat != IF_NOACTIV) {
++                      printk(KERN_WARNING "%s: peer if is in use\n",
++                              __FUNCTION__);
++                      return(-EBUSY);
++              }
++      }                       
++      return(mISDN_ConnectIF(owner, peer));
++}
++
++static int
++set_if_req(mISDNdevice_t *dev, struct sk_buff *skb)
++{
++      mISDNif_t               *hif,*phif,*shif;
++      int                     stat;
++      interface_info_t        *ifi = (interface_info_t *)skb->data;
++      devicelayer_t           *dl;
++      mISDNinstance_t         *inst, *peer;
++      mISDN_head_t            *hp;
++
++      hp = mISDN_HEAD_P(skb);
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n",
++                      __FUNCTION__, hp->addr, ifi->owner, ifi->peer);
++      if (!(dl=get_devlayer(dev, hp->addr)))
++              return(-ENXIO);
++      if (!(inst = get_instance4id(ifi->owner))) {
++              printk(KERN_WARNING "%s: owner(%x) not found\n",
++                      __FUNCTION__, ifi->owner);
++              return(-ENODEV);
++      }
++      if (!(peer = get_instance4id(ifi->peer))) {
++              printk(KERN_WARNING "%s: peer(%x) not found\n",
++                      __FUNCTION__, ifi->peer);
++              return(-ENODEV);
++      }
++
++      if (ifi->stat == IF_UP) {
++              hif = &dl->inst.up;
++              phif = &peer->down;
++              shif = &dl->s_up;
++              stat = IF_DOWN;
++      } else if (ifi->stat == IF_DOWN) {
++              hif = &dl->inst.down;
++              shif = &dl->s_down;
++              phif = &peer->up;
++              stat = IF_UP;
++      } else {
++              printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__);
++              return(-EINVAL);
++      }
++
++      
++      if (shif->stat != IF_NOACTIV) {
++              printk(KERN_WARNING "%s: save if busy\n", __FUNCTION__);
++              return(-EBUSY);
++      }
++      if (hif->stat != IF_NOACTIV) {
++              printk(KERN_WARNING "%s: own if busy\n", __FUNCTION__);
++              return(-EBUSY);
++      }
++      hif->stat = stat;
++      hif->owner = inst;
++      memcpy(shif, phif, sizeof(mISDNif_t));
++      memset(phif, 0, sizeof(mISDNif_t));
++      return(peer->obj->own_ctrl(peer, hp->prim, hif));
++}
++
++static int
++add_if_req(mISDNdevice_t *dev, struct sk_buff *skb)
++{
++      mISDNif_t               *hif;
++      interface_info_t        *ifi = (interface_info_t *)skb->data;
++      mISDNinstance_t         *inst, *peer;
++      mISDN_head_t            *hp;
++
++      hp = mISDN_HEAD_P(skb);
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n",
++                      __FUNCTION__, hp->addr, ifi->owner, ifi->peer);
++      if (!(inst = get_instance4id(ifi->owner))) {
++              printk(KERN_WARNING "%s: owner(%x) not found\n",
++                      __FUNCTION__, ifi->owner);
++              return(-ENODEV);
++      }
++      if (!(peer = get_instance4id(ifi->peer))) {
++              printk(KERN_WARNING "%s: peer(%x) not found\n",
++                      __FUNCTION__, ifi->peer);
++              return(-ENODEV);
++      }
++
++      if (ifi->stat == IF_DOWN) {
++              hif = &inst->up;
++      } else if (ifi->stat == IF_UP) {
++              hif = &inst->down;
++      } else {
++              printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__);
++              return(-EINVAL);
++      }
++      return(peer->obj->ctrl(peer, hp->prim, hif));
++}
++
++static int
++del_if_req(mISDNdevice_t *dev, u_int addr)
++{
++      devicelayer_t *dl;
++
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr);
++      if (!(dl=get_devlayer(dev, addr)))
++              return(-ENXIO);
++      return(remove_if(dl, addr));
++}
++
++static int
++new_entity_req(mISDNdevice_t *dev, int *entity)
++{
++      int             ret;
++      entity_item_t   *ei = kmalloc(sizeof(entity_item_t), GFP_ATOMIC);
++
++      if (!ei)
++              return(-ENOMEM);
++      ret = mISDN_alloc_entity(entity);
++      ei->entity = *entity;
++      if (ret)
++              kfree(entity);
++      else
++              list_add((struct list_head *)ei, &dev->entitylist);
++      return(ret);
++}
++
++static int
++del_entity_req(mISDNdevice_t *dev, int entity)
++{
++      struct list_head        *item, *nxt;
++
++      list_for_each_safe(item, nxt, &dev->entitylist) {
++              if (((entity_item_t *)item)->entity == entity) {
++                      list_del(item);
++                      mISDN_delete_entity(entity);
++                      kfree(item);
++                      return(0);
++              }
++      }
++      return(-ENODEV);
++}
++
++static void
++dev_expire_timer(mISDNtimer_t *ht)
++{
++      struct sk_buff  *skb;
++      mISDN_head_t    *hp;
++
++      if (device_debug & DEBUG_DEV_TIMER)
++              printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id);
++      if (test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags)) {
++              skb = alloc_stack_skb(16, 0);
++              if (!skb) {
++                      printk(KERN_WARNING "%s: timer(%x) no skb\n",
++                              __FUNCTION__, ht->id);
++                      return;
++              }
++              hp = mISDN_HEAD_P(skb);
++              hp->dinfo = 0;
++              hp->prim = MGR_TIMER | INDICATION;
++              hp->addr = ht->id;
++              hp->len = 0;
++              if (mISDN_rdata(ht->dev, skb))
++                      dev_kfree_skb(skb);
++      } else
++              printk(KERN_WARNING "%s: timer(%x) not active\n",
++                      __FUNCTION__, ht->id);
++}
++
++static int
++dev_init_timer(mISDNdevice_t *dev, u_int id)
++{
++      mISDNtimer_t    *ht;
++
++      ht = get_devtimer(dev, id);
++      if (!ht) {
++              ht = kmalloc(sizeof(mISDNtimer_t), GFP_ATOMIC);
++              if (!ht)
++                      return(-ENOMEM);
++              ht->dev = dev;
++              ht->id = id;
++              ht->tl.data = (long) ht;
++              ht->tl.function = (void *) dev_expire_timer;
++              init_timer(&ht->tl);
++              list_add_tail(&ht->list, &dev->timerlist);
++              if (device_debug & DEBUG_DEV_TIMER)
++                      printk(KERN_DEBUG "%s: new(%x)\n", __FUNCTION__, ht->id);
++      } else if (device_debug & DEBUG_DEV_TIMER)
++              printk(KERN_DEBUG "%s: old(%x)\n", __FUNCTION__, ht->id);
++      if (timer_pending(&ht->tl)) {
++              printk(KERN_WARNING "%s: timer(%x) pending\n", __FUNCTION__,
++                      ht->id);
++              del_timer(&ht->tl);
++      }
++      init_timer(&ht->tl);
++      test_and_set_bit(FLG_MGR_TIMER_INIT, &ht->Flags);
++      return(0);
++}
++
++static int
++dev_add_timer(mISDNdevice_t *dev, mISDN_head_t *hp)
++{
++      mISDNtimer_t    *ht;
++
++      ht = get_devtimer(dev, hp->addr);
++      if (!ht) {
++              printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__,
++                      hp->addr);
++              return(-ENODEV);
++      }
++      if (timer_pending(&ht->tl)) {
++              printk(KERN_WARNING "%s: timer(%x) pending\n",
++                      __FUNCTION__, ht->id);
++              return(-EBUSY);
++      }
++      if (hp->dinfo < 10) {
++              printk(KERN_WARNING "%s: timer(%x): %d ms too short\n",
++                      __FUNCTION__, ht->id, hp->dinfo);
++              return(-EINVAL);
++      }
++      if (device_debug & DEBUG_DEV_TIMER)
++              printk(KERN_DEBUG "%s: timer(%x) %d ms\n",
++                      __FUNCTION__, ht->id, hp->dinfo);
++      init_timer(&ht->tl);
++      ht->tl.expires = jiffies + (hp->dinfo * HZ) / 1000;
++      test_and_set_bit(FLG_MGR_TIMER_RUNING, &ht->Flags);
++      add_timer(&ht->tl);
++      return(0);
++}
++
++static int
++dev_del_timer(mISDNdevice_t *dev, u_int id)
++{
++      mISDNtimer_t    *ht;
++
++      ht = get_devtimer(dev, id);
++      if (!ht) {
++              printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__,
++                      id);
++              return(-ENODEV);
++      }
++      if (device_debug & DEBUG_DEV_TIMER)
++              printk(KERN_DEBUG "%s: timer(%x)\n",
++                      __FUNCTION__, ht->id);
++      del_timer(&ht->tl);
++      if (!test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags))
++              printk(KERN_WARNING "%s: timer(%x) not running\n",
++                      __FUNCTION__, ht->id);
++      return(0);
++}
++
++static void
++dev_free_timer(mISDNtimer_t *ht)
++{
++      if (device_debug & DEBUG_DEV_TIMER)
++              printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id);
++      del_timer(&ht->tl);
++      list_del(&ht->list);
++      kfree(ht);
++}
++
++static int
++dev_remove_timer(mISDNdevice_t *dev, u_int id)
++{
++      mISDNtimer_t    *ht;
++
++      ht = get_devtimer(dev, id);
++      if (!ht)  {
++              printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, id);
++              return(-ENODEV);
++      }
++      dev_free_timer(ht);
++      return(0);
++}
++
++static int
++get_status(struct sk_buff *skb)
++{
++      mISDN_head_t    *hp;
++      status_info_t   *si = (status_info_t *)skb->data;
++      mISDNinstance_t *inst;
++      int             err;
++
++      hp = mISDN_HEAD_P(skb);
++      if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) {
++              printk(KERN_WARNING "%s: no instance\n", __FUNCTION__);
++              err = -ENODEV;
++      } else {
++              err = inst->obj->own_ctrl(inst, MGR_STATUS | REQUEST, si);
++      }
++      if (err)
++              hp->len = err;
++      else {
++              hp->len = si->len + 2*sizeof(int);
++              skb_put(skb, hp->len);
++      }
++      return(err);    
++}
++
++static void
++get_layer_info(struct sk_buff *skb)
++{
++      mISDN_head_t    *hp;
++      mISDNinstance_t *inst;
++      layer_info_t    *li = (layer_info_t *)skb->data;
++
++      hp = mISDN_HEAD_P(skb);
++      if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) {
++              printk(KERN_WARNING "%s: no instance\n", __FUNCTION__);
++              hp->len = -ENODEV;
++              return;
++      }
++      memset(li, 0, sizeof(layer_info_t));
++      if (inst->obj)
++              li->object_id = inst->obj->id;
++      strcpy(li->name, inst->name);
++      li->extentions = inst->extentions;
++      li->id = inst->id;
++      if (inst->st)
++              li->st = inst->st->id;
++      memcpy(&li->pid, &inst->pid, sizeof(mISDN_pid_t));
++      hp->len = sizeof(layer_info_t);
++      skb_put(skb, hp->len);
++}
++
++static void
++get_if_info(struct sk_buff *skb)
++{
++      mISDN_head_t            *hp;
++      mISDNinstance_t         *inst;
++      mISDNif_t               *hif;
++      interface_info_t        *ii = (interface_info_t *)skb->data;
++      
++      hp = mISDN_HEAD_P(skb);
++      if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) {
++              printk(KERN_WARNING "%s: no instance\n", __FUNCTION__);
++              hp->len = -ENODEV;
++              return;
++      }
++      if (hp->dinfo == IF_DOWN)
++              hif = &inst->down;
++      else if (hp->dinfo == IF_UP)
++              hif = &inst->up;
++      else {
++              printk(KERN_WARNING "%s: wrong interface %x\n",
++                      __FUNCTION__, hp->dinfo);
++              hp->len = -EINVAL;
++              return;
++      }
++      hp->dinfo = 0;
++      memset(ii, 0, sizeof(interface_info_t));
++      if (hif->owner)
++              ii->owner = hif->owner->id;
++      if (hif->peer)
++              ii->peer = hif->peer->id;
++      ii->extentions = hif->extentions;
++      ii->stat = hif->stat;
++      hp->len = sizeof(interface_info_t);
++      skb_put(skb, hp->len);
++}
++
++static int
++wdata_frame(mISDNdevice_t *dev, struct sk_buff *skb)
++{
++      mISDN_head_t    *hp;
++      mISDNif_t       *hif = NULL;
++      devicelayer_t   *dl;
++      int             err = -ENXIO;
++
++      hp = mISDN_HEAD_P(skb);
++      if (device_debug & DEBUG_WDATA)
++              printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, hp->addr);
++      if (!(dl=get_devlayer(dev, hp->addr)))
++              return(err);
++      if (hp->addr & IF_UP) {
++              hif = &dl->inst.up;
++              if (IF_TYPE(hif) != IF_DOWN) {
++                      printk(KERN_WARNING "%s: inst.up no down\n", __FUNCTION__);
++                      hif = NULL;
++              }
++      } else if (hp->addr & IF_DOWN) {
++              hif = &dl->inst.down;
++              if (IF_TYPE(hif) != IF_UP) {
++                      printk(KERN_WARNING "%s: inst.down no up\n", __FUNCTION__);
++                      hif = NULL;
++              }
++      }
++      if (hif) {
++              if (device_debug & DEBUG_WDATA)
++                      printk(KERN_DEBUG "%s: pr(%x) di(%x) l(%d)\n",
++                              __FUNCTION__, hp->prim, hp->dinfo, hp->len);
++              if (hp->len < 0) {
++                      printk(KERN_WARNING "%s: data negativ(%d)\n",
++                              __FUNCTION__, hp->len);
++                      return(-EINVAL);
++              }
++              err = hif->func(hif, skb);
++              if (device_debug & DEBUG_WDATA && err)
++                      printk(KERN_DEBUG "%s: hif->func ret(%x)\n",
++                              __FUNCTION__, err);
++      } else {
++              if (device_debug & DEBUG_WDATA)
++                      printk(KERN_DEBUG "mISDN: no matching interface\n");
++      }
++      return(err);
++}
++
++static int
++mISDN_wdata_if(mISDNdevice_t *dev, struct sk_buff *skb)
++{
++      struct sk_buff  *nskb = NULL;
++      mISDN_head_t    *hp;
++      mISDNstack_t    *st;
++      devicelayer_t   *dl;
++      mISDNlayer_t    *layer;
++      int             lay;
++      int             err = 0;
++
++      hp = mISDN_HEAD_P(skb);
++      if (device_debug & DEBUG_WDATA)
++              printk(KERN_DEBUG "%s: %x:%x %x %d %d\n",
++                      __FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len);
++      if ((hp->len > 0) && (skb->len < hp->len)) {
++              printk(KERN_WARNING "%s: frame(%d/%d) too short\n",
++                      __FUNCTION__, skb->len, hp->len);
++              return(error_answer(dev, skb, -EINVAL));
++      }
++      switch(hp->prim) {
++          case (MGR_VERSION | REQUEST):
++              hp->prim = MGR_VERSION | CONFIRM;
++              hp->len = 0;
++              hp->dinfo = MISDN_VERSION;
++              break;
++          case (MGR_GETSTACK | REQUEST):
++              hp->prim = MGR_GETSTACK | CONFIRM;
++              hp->dinfo = 0;
++              if (hp->addr <= 0) {
++                      hp->dinfo = get_stack_cnt();
++                      hp->len = 0;
++              } else {
++                      nskb = alloc_stack_skb(1000, 0);
++                      if (!nskb)
++                              return(error_answer(dev, skb, -ENOMEM));
++                      memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
++                      get_stack_info(nskb);
++              }
++              break;
++          case (MGR_SETSTACK | REQUEST):
++              if (skb->len < sizeof(mISDN_pid_t))
++                      return(error_answer(dev, skb, -EINVAL));
++              hp->dinfo = 0;
++              if ((st = get_stack4id(hp->addr))) {
++                      stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 0);
++                      hp->len = udev_obj.ctrl(st, hp->prim, skb->data);
++              } else
++                      hp->len = -ENODEV;
++              hp->prim = MGR_SETSTACK | CONFIRM;
++              break;
++          case (MGR_NEWSTACK | REQUEST):
++              hp->dinfo = 0;
++              hp->prim = MGR_NEWSTACK | CONFIRM;
++              hp->len = 0;
++              err = new_devstack(dev, (stack_info_t *)skb->data);
++              if (err<0)
++                      hp->len = err;
++              else
++                      hp->dinfo = err;
++              break;  
++          case (MGR_CLEARSTACK | REQUEST):
++              hp->dinfo = 0;
++              if ((st = get_stack4id(hp->addr))) {
++                      stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 1);
++                      hp->len = udev_obj.ctrl(st, hp->prim, NULL);
++              } else
++                      hp->len = -ENODEV;
++              hp->prim = MGR_CLEARSTACK | CONFIRM;
++              break;
++          case (MGR_SELCHANNEL | REQUEST):
++              hp->prim = MGR_SELCHANNEL | CONFIRM;
++              st = sel_channel(hp->addr, hp->dinfo);
++              if (st) {
++                      hp->len = 0;
++                      hp->dinfo = st->id;
++              } else {
++                      hp->dinfo = 0;
++                      hp->len = -ENODEV;
++              }
++              break;
++          case (MGR_GETLAYERID | REQUEST):
++              hp->prim = MGR_GETLAYERID | CONFIRM;
++              lay = hp->dinfo;
++              hp->dinfo = 0;
++              if (LAYER_OUTRANGE(lay)) {
++                      hp->len = -EINVAL;
++              } else {
++                      hp->len = 0;
++                      lay = ISDN_LAYER(lay);
++                      if ((st = get_stack4id(hp->addr))) {
++                              if ((layer = getlayer4lay(st, lay))) {
++                                      if (layer->inst)
++                                              hp->dinfo = layer->inst->id;
++                              }
++                      }
++              }
++              break;
++          case (MGR_GETLAYER | REQUEST):
++              hp->prim = MGR_GETLAYER | CONFIRM;
++              hp->dinfo = 0;
++              skb_trim(skb, 0);
++              if (skb_tailroom(skb) < sizeof(layer_info_t)) {
++                      nskb = alloc_stack_skb(sizeof(layer_info_t), 0);
++                      if (!nskb)
++                              return(error_answer(dev, skb, -ENOMEM));
++                      memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
++                      get_layer_info(nskb);
++              } else {
++                      get_layer_info(skb);
++              }
++              break;
++          case (MGR_NEWLAYER | REQUEST):
++              if (skb->len < sizeof(layer_info_t))
++                      return(error_answer(dev, skb, -EINVAL));
++              hp->dinfo = 0;
++              hp->prim = MGR_NEWLAYER | CONFIRM;
++              hp->len = create_layer(dev, skb);
++              break;  
++          case (MGR_DELLAYER | REQUEST):
++              hp->prim = MGR_DELLAYER | CONFIRM;
++              hp->dinfo = 0;
++              if ((dl = get_devlayer(dev, hp->addr)))
++                      hp->len = del_layer(dl);
++              else
++                      hp->len = -ENXIO;
++              break;
++          case (MGR_GETIF | REQUEST):
++              hp->prim = MGR_GETIF | CONFIRM;
++              hp->dinfo = 0;
++              skb_trim(skb, 0);
++              if (skb_tailroom(skb) < sizeof(interface_info_t)) {
++                      nskb = alloc_stack_skb(sizeof(interface_info_t), 0);
++                      if (!nskb)
++                              return(error_answer(dev, skb, -ENOMEM));
++                      memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
++                      get_if_info(nskb);
++              } else {
++                      get_if_info(skb);
++              }
++              break;
++          case (MGR_CONNECT | REQUEST):
++              if (skb->len < sizeof(interface_info_t))
++                      return(error_answer(dev, skb, -EINVAL));
++              hp->len = connect_if_req(dev, skb);
++              hp->dinfo = 0;
++              hp->prim = MGR_CONNECT | CONFIRM;
++              break;
++          case (MGR_SETIF | REQUEST):
++              hp->len = set_if_req(dev, skb);
++              hp->prim = MGR_SETIF | CONFIRM;
++              hp->dinfo = 0;
++              break;
++          case (MGR_ADDIF | REQUEST):
++              hp->len = add_if_req(dev, skb);
++              hp->prim = MGR_ADDIF | CONFIRM;
++              hp->dinfo = 0;
++              break;
++          case (MGR_DISCONNECT | REQUEST):
++              hp->len = del_if_req(dev, hp->addr);
++              hp->prim = MGR_DISCONNECT | CONFIRM;
++              hp->dinfo = 0;
++              break;
++          case (MGR_NEWENTITY | REQUEST):
++              hp->prim = MGR_NEWENTITY | CONFIRM;
++              hp->len = new_entity_req(dev, &hp->dinfo);
++              break;
++          case (MGR_DELENTITY | REQUEST):
++              hp->prim = MGR_DELENTITY | CONFIRM;
++              hp->len = del_entity_req(dev, hp->dinfo);
++              break;
++          case (MGR_INITTIMER | REQUEST):
++              hp->len = dev_init_timer(dev, hp->addr);
++              hp->prim = MGR_INITTIMER | CONFIRM;
++              break;
++          case (MGR_ADDTIMER | REQUEST):
++              hp->len = dev_add_timer(dev, hp);
++              hp->prim = MGR_ADDTIMER | CONFIRM;
++              hp->dinfo = 0;
++              break;
++          case (MGR_DELTIMER | REQUEST):
++              hp->len = dev_del_timer(dev, hp->addr);
++              hp->prim = MGR_DELTIMER | CONFIRM;
++              break;
++          case (MGR_REMOVETIMER | REQUEST):
++              hp->len = dev_remove_timer(dev, hp->addr);
++              hp->prim = MGR_REMOVETIMER | CONFIRM;
++              hp->dinfo = 0;
++              break;
++          case (MGR_TIMER | RESPONSE):
++              dev_kfree_skb(skb);
++              return(0);
++              break;
++          case (MGR_STATUS | REQUEST):
++              hp->prim = MGR_STATUS | CONFIRM;
++              nskb = alloc_stack_skb(1000, 0);
++              if (!nskb)
++                      return(error_answer(dev, skb, -ENOMEM));
++              memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
++              get_status(nskb);
++              hp->dinfo = 0;
++              break;
++          case (MGR_SETDEVOPT | REQUEST):
++              hp->prim = MGR_SETDEVOPT | CONFIRM;
++              hp->len = 0;
++              if (hp->dinfo == FLG_mISDNPORT_ONEFRAME) {
++                      test_and_set_bit(FLG_mISDNPORT_ONEFRAME,
++                              &dev->rport.Flag);
++              } else if (!hp->dinfo) {
++                      test_and_clear_bit(FLG_mISDNPORT_ONEFRAME,
++                              &dev->rport.Flag);
++              } else {
++                      hp->len = -EINVAL;
++              }
++              hp->dinfo = 0;
++              break;
++          case (MGR_GETDEVOPT | REQUEST):
++              hp->prim = MGR_GETDEVOPT | CONFIRM;
++              hp->len = 0;
++              if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag))
++                      hp->dinfo = FLG_mISDNPORT_ONEFRAME;
++              else
++                      hp->dinfo = 0;
++              break;
++          default:
++              if (hp->addr & IF_TYPEMASK) {
++                      err = wdata_frame(dev, skb);
++                      if (err) {
++                              if (device_debug & DEBUG_WDATA)
++                                      printk(KERN_DEBUG "wdata_frame returns error %d\n", err);
++                              err = error_answer(dev, skb, err);
++                      }
++              } else {
++                      printk(KERN_WARNING "mISDN: prim %x addr %x not implemented\n",
++                              hp->prim, hp->addr);
++                      err = error_answer(dev, skb, -EINVAL);
++              }
++              return(err);
++              break;
++      }
++      if (nskb) {
++              err = mISDN_rdata(dev, nskb);
++              if (err)
++                      kfree_skb(nskb);
++              else
++                      kfree_skb(skb);
++      } else
++              err = mISDN_rdata(dev, skb);
++      return(err);
++}
++
++static mISDNdevice_t *
++init_device(u_int minor) {
++      mISDNdevice_t   *dev;
++      u_long          flags;
++
++      dev = kmalloc(sizeof(mISDNdevice_t), GFP_KERNEL);
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: dev(%d) %p\n",
++                      __FUNCTION__, minor, dev); 
++      if (dev) {
++              memset(dev, 0, sizeof(mISDNdevice_t));
++              dev->minor = minor;
++              init_waitqueue_head(&dev->rport.procq);
++              init_waitqueue_head(&dev->wport.procq);
++              skb_queue_head_init(&dev->rport.queue);
++              skb_queue_head_init(&dev->wport.queue);
++              init_MUTEX(&dev->io_sema);
++              INIT_LIST_HEAD(&dev->layerlist);
++              INIT_LIST_HEAD(&dev->stacklist);
++              INIT_LIST_HEAD(&dev->timerlist);
++              INIT_LIST_HEAD(&dev->entitylist);
++              write_lock_irqsave(&mISDN_device_lock, flags);
++              list_add_tail(&dev->list, &mISDN_devicelist);
++              write_unlock_irqrestore(&mISDN_device_lock, flags);
++      }
++      return(dev);
++}
++
++mISDNdevice_t *
++get_free_rawdevice(void)
++{
++      mISDNdevice_t   *dev;
++      u_int           minor;
++
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s:\n", __FUNCTION__);
++      for (minor=mISDN_MINOR_RAW_MIN; minor<=mISDN_MINOR_RAW_MAX; minor++) {
++              dev = get_mISDNdevice4minor(minor);
++              if (device_debug & DEBUG_MGR_FUNC)
++                      printk(KERN_DEBUG "%s: dev(%d) %p\n",
++                              __FUNCTION__, minor, dev); 
++              if (!dev) {
++                      dev = init_device(minor);
++                      if (!dev)
++                              return(NULL);
++                      dev->rport.pif.func = mISDN_rdata_raw;
++                      dev->rport.pif.fdata = dev;
++                      return(dev);
++              }
++      }
++      return(NULL);
++}
++
++int
++free_device(mISDNdevice_t *dev)
++{
++      struct list_head *item, *ni;
++      u_long  flags;
++
++      if (!dev)
++              return(-ENODEV);
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "%s: dev(%d)\n", __FUNCTION__, dev->minor);
++      /* release related stuff */
++      list_for_each_safe(item, ni, &dev->layerlist)
++              del_layer(list_entry(item, devicelayer_t, list));
++      list_for_each_safe(item, ni, &dev->stacklist)
++              del_stack(list_entry(item, devicestack_t, list));
++      list_for_each_safe(item, ni, &dev->timerlist)
++              dev_free_timer(list_entry(item, mISDNtimer_t, list));
++      if (!skb_queue_empty(&dev->rport.queue))
++              discard_queue(&dev->rport.queue);
++      if (!skb_queue_empty(&dev->wport.queue))
++              discard_queue(&dev->wport.queue);
++      write_lock_irqsave(&mISDN_device_lock, flags);
++      list_del(&dev->list);
++      write_unlock_irqrestore(&mISDN_device_lock, flags);
++      if (!list_empty(&dev->entitylist)) {
++              printk(KERN_WARNING "MISDN %s: entitylist not empty\n", __FUNCTION__);
++              list_for_each_safe(item, ni, &dev->entitylist) {
++                      struct entity_item *ei = list_entry(item, struct entity_item, head);
++                      list_del(item);
++                      mISDN_delete_entity(ei->entity);
++                      kfree(ei);
++              }
++      }
++      kfree(dev);
++      return(0);
++}
++
++static int
++mISDN_open(struct inode *ino, struct file *filep)
++{
++      u_int           minor = iminor(ino);
++      mISDNdevice_t   *dev = NULL;
++      int             isnew = 0;
++
++      if (device_debug & DEBUG_DEV_OP)
++              printk(KERN_DEBUG "mISDN_open in: minor(%d) %p %p mode(%x)\n",
++                      minor, filep, filep->private_data, filep->f_mode);
++      if (minor) {
++              dev = get_mISDNdevice4minor(minor);
++              if (dev) {
++                      if ((dev->open_mode & filep->f_mode) & (FMODE_READ | FMODE_WRITE))
++                              return(-EBUSY);
++              } else
++                      return(-ENODEV);
++      } else if ((dev = init_device(minor)))
++              isnew = 1;
++      else
++              return(-ENOMEM);
++      dev->open_mode |= filep->f_mode & (FMODE_READ | FMODE_WRITE);
++      if (dev->open_mode & FMODE_READ){
++              dev->rport.lock = SPIN_LOCK_UNLOCKED;
++              dev->rport.maxqlen = DEFAULT_PORT_QUEUELEN;
++              test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag);
++      }
++      if (dev->open_mode & FMODE_WRITE) {
++              dev->wport.lock = SPIN_LOCK_UNLOCKED;
++              dev->wport.maxqlen = DEFAULT_PORT_QUEUELEN;
++              test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->wport.Flag);
++      }
++      filep->private_data = dev;
++      if (device_debug & DEBUG_DEV_OP)
++              printk(KERN_DEBUG "mISDN_open out: %p %p\n", filep, filep->private_data);
++      return(0);
++}
++
++static int
++mISDN_close(struct inode *ino, struct file *filep)
++{
++      mISDNdevice_t   *dev, *nd;
++
++      if (device_debug & DEBUG_DEV_OP)
++              printk(KERN_DEBUG "mISDN: mISDN_close %p %p\n", filep, filep->private_data);
++      read_lock(&mISDN_device_lock);
++      list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) {
++              if (dev == filep->private_data) {
++                      if (device_debug & DEBUG_DEV_OP)
++                              printk(KERN_DEBUG "mISDN: dev(%d) %p mode %x/%x\n",
++                                      dev->minor, dev, dev->open_mode, filep->f_mode);
++                      dev->open_mode &= ~filep->f_mode;
++                      read_unlock(&mISDN_device_lock);
++                      if (filep->f_mode & FMODE_READ) {
++                              test_and_clear_bit(FLG_mISDNPORT_OPEN,
++                                      &dev->rport.Flag);
++                      }
++                      if (filep->f_mode & FMODE_WRITE) {
++                              test_and_clear_bit(FLG_mISDNPORT_OPEN,
++                                      &dev->wport.Flag);
++                      }
++                      filep->private_data = NULL;
++                      if (!dev->minor)
++                              free_device(dev);
++                      return 0;
++              }
++      }
++      read_unlock(&mISDN_device_lock);
++      printk(KERN_WARNING "mISDN: No private data while closing device\n");
++      return 0;
++}
++
++static __inline__ ssize_t
++do_mISDN_read(struct file *file, char *buf, size_t count, loff_t * off)
++{
++      mISDNdevice_t   *dev = file->private_data;
++      size_t          len;
++      u_long          flags;
++      struct sk_buff  *skb;
++
++      if (*off != file->f_pos)
++              return(-ESPIPE);
++      if (!access_ok(VERIFY_WRITE, buf, count))
++              return(-EFAULT);
++      if ((dev->minor == 0) && (count < mISDN_HEADER_LEN)) {
++              printk(KERN_WARNING "mISDN_read: count(%d) too small\n", count);
++              return(-ENOSPC);
++      }
++      if (device_debug & DEBUG_DEV_OP)
++              printk(KERN_DEBUG "mISDN_read: file(%d) %p max %d\n",
++                      dev->minor, file, count);
++      spin_lock_irqsave(&dev->rport.lock, flags);
++      while (skb_queue_empty(&dev->rport.queue)) {
++              spin_unlock_irqrestore(&dev->rport.lock, flags);
++              if (file->f_flags & O_NONBLOCK)
++                      return(-EAGAIN);
++              interruptible_sleep_on(&(dev->rport.procq));
++              if (signal_pending(current))
++                      return(-ERESTARTSYS);
++              spin_lock_irqsave(&dev->rport.lock, flags);
++      }
++      len = 0;
++      while ((skb = skb_dequeue(&dev->rport.queue))) {
++              if (dev->minor == mISDN_CORE_DEVICE) {
++                      if ((skb->len + mISDN_HEADER_LEN) > (count - len))
++                              goto nospace;
++                      if (copy_to_user(buf, skb->cb, mISDN_HEADER_LEN))
++                              goto efault;
++                      len += mISDN_HEADER_LEN;
++                      buf += mISDN_HEADER_LEN;
++              } else {
++                      if (skb->len > (count - len)) {
++                          nospace:
++                              skb_queue_head(&dev->rport.queue, skb);
++                              if (len)
++                                      break;
++                              spin_unlock_irqrestore(&dev->rport.lock, flags);
++                              return(-ENOSPC);
++                      }
++              }
++              if (skb->len) {
++                      if (copy_to_user(buf, skb->data, skb->len)) {
++                          efault:
++                              skb_queue_head(&dev->rport.queue, skb);
++                              spin_unlock_irqrestore(&dev->rport.lock, flags);
++                              return(-EFAULT);
++                      }
++                      len += skb->len;
++                      buf += skb->len;
++              }
++              dev_kfree_skb(skb);
++              if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag))
++                      break;
++      }
++      *off += len;
++      spin_unlock_irqrestore(&dev->rport.lock, flags);
++      if (device_debug & DEBUG_DEV_OP)
++              printk(KERN_DEBUG "mISDN_read: file(%d) %d\n",
++                      dev->minor, len);
++      return(len);
++}
++
++static ssize_t
++mISDN_read(struct file *file, char *buf, size_t count, loff_t * off)
++{
++      mISDNdevice_t   *dev = file->private_data;
++      ssize_t         ret;
++
++      if (!dev)
++              return(-ENODEV);
++      down(&dev->io_sema);
++      ret = do_mISDN_read(file, buf, count, off);
++      up(&dev->io_sema);
++      return(ret);
++}
++
++static loff_t
++mISDN_llseek(struct file *file, loff_t offset, int orig)
++{
++      return -ESPIPE;
++}
++
++static __inline__ ssize_t
++do_mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off)
++{
++      mISDNdevice_t   *dev = file->private_data;
++      size_t          len;
++      u_long          flags;
++      struct sk_buff  *skb;
++      mISDN_head_t    head;
++
++      if (*off != file->f_pos)
++              return(-ESPIPE);
++      if (device_debug & DEBUG_DEV_OP)
++              printk(KERN_DEBUG "mISDN_write: file(%d) %p count %d queue(%d)\n",
++                      dev->minor, file, count, skb_queue_len(&dev->wport.queue));
++      if (!access_ok(VERIFY_WRITE, buf, count))
++              return(-EFAULT);
++      if (dev->minor == 0) {
++              if (count < mISDN_HEADER_LEN)
++                      return(-EINVAL);
++      }
++      spin_lock_irqsave(&dev->wport.lock, flags);
++      while (skb_queue_len(&dev->wport.queue) >= dev->wport.maxqlen) {
++              spin_unlock_irqrestore(&dev->wport.lock, flags);
++              if (file->f_flags & O_NONBLOCK)
++                      return(-EAGAIN);
++              interruptible_sleep_on(&(dev->wport.procq));
++              if (signal_pending(current))
++                      return(-ERESTARTSYS);
++              spin_lock_irqsave(&dev->wport.lock, flags);
++      }
++      if (dev->minor == mISDN_CORE_DEVICE) {
++              len = count;
++              while (len >= mISDN_HEADER_LEN) {
++                      if (copy_from_user(&head.addr, buf, mISDN_HEADER_LEN)) {
++                              spin_unlock_irqrestore(&dev->rport.lock, flags);
++                              return(-EFAULT);
++                      }
++                      if (head.len > 0)
++                              skb = alloc_stack_skb((head.len > PORT_SKB_MINIMUM) ? 
++                                      head.len : PORT_SKB_MINIMUM, PORT_SKB_RESERVE);
++                      else
++                              skb = alloc_stack_skb(PORT_SKB_MINIMUM, PORT_SKB_RESERVE);
++                      if (!skb)
++                              break;
++                      memcpy(skb->cb, &head.addr, mISDN_HEADER_LEN);
++                      len -= mISDN_HEADER_LEN;
++                      buf += mISDN_HEADER_LEN;
++                      if (head.len > 0) {
++                              if (head.len > len) {
++                                      /* since header is complete we can handle this later */
++                                      if (copy_from_user(skb_put(skb, len), buf, len)) {
++                                              dev_kfree_skb(skb);
++                                              spin_unlock_irqrestore(&dev->rport.lock, flags);
++                                              return(-EFAULT);
++                                      }
++                                      len = 0;
++                              } else {
++                                      if (copy_from_user(skb_put(skb, head.len), buf, head.len)) {
++                                              dev_kfree_skb(skb);
++                                              spin_unlock_irqrestore(&dev->rport.lock, flags);
++                                              return(-EFAULT);
++                                      }
++                                      len -= head.len;
++                                      buf += head.len;
++                              }
++                      }
++                      skb_queue_tail(&dev->wport.queue, skb);
++              }
++              if (len)
++                      printk(KERN_WARNING "%s: incomplete frame data (%d/%d)\n", __FUNCTION__, len, count);
++              if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) {
++                      spin_unlock_irqrestore(&dev->wport.lock, flags);
++                      return(count-len);
++              }
++              spin_unlock_irqrestore(&dev->wport.lock, flags);
++              while ((skb = skb_dequeue(&dev->wport.queue))) {
++                      if (mISDN_wdata_if(dev, skb))
++                              dev_kfree_skb(skb);
++                      wake_up(&dev->wport.procq);
++              }
++              test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag);
++      } else { /* raw device */
++              skb = alloc_stack_skb(count, PORT_SKB_RESERVE);
++              if (skb) {
++                      spin_unlock_irqrestore(&dev->wport.lock, flags);
++                      return(0);
++              }
++              if (copy_from_user(skb_put(skb, count), buf, count)) {
++                      dev_kfree_skb(skb);
++                      spin_unlock_irqrestore(&dev->wport.lock, flags);
++                      return(-EFAULT);
++              }
++              len = 0;
++              skb_queue_tail(&dev->wport.queue, skb);
++              if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) {
++                      spin_unlock_irqrestore(&dev->wport.lock, flags);
++                      return(count);
++              }
++              while ((skb = skb_dequeue(&dev->wport.queue))) {
++                      if (device_debug & DEBUG_DEV_OP)
++                              printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag);
++                      if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) {
++                              skb_queue_head(&dev->wport.queue, skb); 
++                              break;
++                      }
++                      if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) {
++                              int ret;
++                              spin_unlock_irqrestore(&dev->wport.lock, flags);
++                              ret = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb);
++                              spin_lock_irqsave(&dev->wport.lock, flags);
++                              if (ret) {
++                                      printk(KERN_WARNING "%s: dev(%d) down err(%d)\n",
++                                              __FUNCTION__, dev->minor, ret);
++                                      dev_kfree_skb(skb);
++                              } else
++                                      test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag);
++                      } else {
++                              printk(KERN_WARNING "%s: dev(%d) wport not enabled\n",
++                                      __FUNCTION__, dev->minor);
++                              dev_kfree_skb(skb);
++                      }
++                      wake_up(&dev->wport.procq);
++              }
++              test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag);
++              spin_unlock_irqrestore(&dev->wport.lock, flags);
++      }
++      return(count - len);
++}
++
++static ssize_t
++mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off)
++{
++      mISDNdevice_t   *dev = file->private_data;
++      ssize_t         ret;
++
++      if (!dev)
++              return(-ENODEV);
++      down(&dev->io_sema);
++      ret = do_mISDN_write(file, buf, count, off);
++      up(&dev->io_sema);
++      return(ret);
++}
++
++static unsigned int
++mISDN_poll(struct file *file, poll_table * wait)
++{
++      unsigned int    mask = POLLERR;
++      mISDNdevice_t   *dev = file->private_data;
++      mISDNport_t     *rport = (file->f_mode & FMODE_READ) ?
++                                      &dev->rport : NULL;
++      mISDNport_t     *wport = (file->f_mode & FMODE_WRITE) ?
++                                      &dev->wport : NULL;
++
++      if (dev) {
++              if (device_debug & DEBUG_DEV_OP)
++                      printk(KERN_DEBUG "mISDN_poll in: file(%d) %p\n",
++                              dev->minor, file);
++              if (rport) {
++                      poll_wait(file, &rport->procq, wait);
++                      mask = 0;
++                      if (!skb_queue_empty(&rport->queue))
++                              mask |= (POLLIN | POLLRDNORM);
++              }
++              if (wport) {
++                      poll_wait(file, &wport->procq, wait);
++                      if (mask == POLLERR)
++                              mask = 0;
++                      if (skb_queue_len(&wport->queue) < wport->maxqlen)
++                              mask |= (POLLOUT | POLLWRNORM);
++              }
++      }
++      if (device_debug & DEBUG_DEV_OP)
++              printk(KERN_DEBUG "mISDN_poll out: file %p mask %x\n",
++                      file, mask);
++      return(mask);
++}
++
++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 int
++from_up_down(mISDNif_t *hif, struct sk_buff *skb) {
++      
++      devicelayer_t   *dl;
++      mISDN_head_t    *hh; 
++      int             retval = -EINVAL;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      dl = hif->fdata;
++      hh = mISDN_HEAD_P(skb);
++      hh->len = skb->len;
++      hh->addr = dl->iaddr | IF_TYPE(hif);
++      if (device_debug & DEBUG_RDATA)
++              printk(KERN_DEBUG "from_up_down: %x(%x) dinfo:%x len:%d\n",
++                      hh->prim, hh->addr, hh->dinfo, hh->len);
++      retval = mISDN_rdata(dl->dev, skb);
++      return(retval);
++}
++
++
++static int
++set_if(devicelayer_t *dl, u_int prim, mISDNif_t *hif)
++{
++      int err = 0;
++
++      err = mISDN_SetIF(&dl->inst, hif, prim, from_up_down, from_up_down, dl);
++      return(err);
++}
++
++static int
++udev_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      mISDNdevice_t   *dev;
++      devicelayer_t   *dl;
++      int             err = -EINVAL;
++
++      if (device_debug & DEBUG_MGR_FUNC)
++              printk(KERN_DEBUG "udev_manager data:%p prim:%x arg:%p\n",
++                      data, prim, arg);
++      if (!data)
++              return(-EINVAL);
++      read_lock(&mISDN_device_lock);
++      list_for_each_entry(dev, &mISDN_devicelist, list) {
++              list_for_each_entry(dl, &dev->layerlist, list) {
++                      if (&dl->inst == inst) {
++                              err = 0;
++                              break;
++                      }
++              }
++              if (!err)
++                      break;
++      }
++      if (err) {
++              printk(KERN_WARNING "dev_manager prim %x without device layer\n", prim);
++              goto out;
++      }
++      switch(prim) {
++          case MGR_CONNECT | REQUEST:
++              err = mISDN_ConnectIF(inst, arg);
++              break;
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              err = set_if(dl, prim, arg);
++              break;
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              err = mISDN_DisConnectIF(inst, arg);
++              break;
++          case MGR_RELEASE | INDICATION:
++              if (device_debug & DEBUG_MGR_FUNC)
++                      printk(KERN_DEBUG "release_dev id %x\n",
++                              dl->inst.st->id);
++              del_layer(dl);
++              err = 0;
++              break;
++          default:
++              printk(KERN_WARNING "dev_manager prim %x not handled\n", prim);
++              err = -EINVAL;
++              break;
++      }
++out:
++      read_unlock(&mISDN_device_lock);
++      return(err);
++}
++
++int init_mISDNdev (int debug) {
++      int err,i;
++
++      udev_obj.name = MName;
++      for (i=0; i<=MAX_LAYER_NR; i++) {
++              udev_obj.DPROTO.protocol[i] = ISDN_PID_ANY;
++              udev_obj.BPROTO.protocol[i] = ISDN_PID_ANY;
++      }
++      INIT_LIST_HEAD(&udev_obj.ilist);
++      udev_obj.own_ctrl = udev_manager;
++      device_debug = debug;
++      if (register_chrdev(mISDN_MAJOR, "mISDN", &mISDN_fops)) {
++              printk(KERN_WARNING "mISDN: Could not register devices\n");
++              return(-EIO);
++      }
++      if ((err = mISDN_register(&udev_obj))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++      }
++      return(err);
++}
++
++int free_mISDNdev(void) {
++      int             err = 0;
++      mISDNdevice_t   *dev, *nd;
++
++      if (!list_empty(&mISDN_devicelist)) {
++              printk(KERN_WARNING "mISDN: devices open on remove\n");
++              list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) {
++                      free_device(dev);
++              }
++              err = -EBUSY;
++      }
++      if ((err = mISDN_unregister(&udev_obj))) {
++              printk(KERN_ERR "Can't unregister UserDevice(%d)\n", err);
++      }
++      if ((err = unregister_chrdev(mISDN_MAJOR, "mISDN"))) {
++              printk(KERN_WARNING "mISDN: devices busy on remove\n");
++      }
++      return(err);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.c      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.c  2004-11-22 09:33:38.453698064 +0000
+@@ -0,0 +1,1612 @@
++/* $Id$
++
++ * w6692.c     low level driver for CCD's hfc-pci based cards
++ *
++ * Author      Karsten Keil <kkeil@suse.de>
++ *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
++ *
++ * 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/config.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++
++#include "dchannel.h"
++#include "bchannel.h"
++#include "layer1.h"
++#include "helper.h"
++#include "debug.h"
++#include "w6692.h"
++
++#include <linux/isdn_compat.h>
++
++#define SPIN_DEBUG
++#define LOCK_STATISTIC
++#include "hw_lock.h"
++
++extern const char *CardType[];
++
++const char *w6692_rev = "$Revision$";
++
++#define DBUSY_TIMER_VALUE     80
++
++typedef struct _w6692_bc {
++      struct timer_list       timer;
++      u_char                  b_mode;
++} w6692_bc;
++
++typedef struct _w6692pci {
++      struct list_head        list;
++      void                    *pdev;
++      u_int                   irq;
++      u_int                   irqcnt;
++      u_int                   addr;
++      int                     pots;
++      int                     led;
++      mISDN_HWlock_t          lock;
++      u_char                  imask;
++      u_char                  pctl;
++      u_char                  xaddr;
++      u_char                  xdata;
++      w6692_bc                wbc[2];
++      dchannel_t              dch;
++      bchannel_t              bch[2];
++} w6692pci;
++
++#define W_LED1_ON     1
++#define W_LED1_S0STATUS       2
++
++static int lock_dev(void *data, int nowait)
++{
++      register mISDN_HWlock_t *lock = &((w6692pci *)data)->lock;
++
++      return(lock_HW(lock, nowait));
++} 
++
++static void unlock_dev(void *data)
++{
++      register mISDN_HWlock_t *lock = &((w6692pci *)data)->lock;
++
++      unlock_HW(lock);
++}
++
++static __inline__ u_char
++ReadW6692(w6692pci *card, u_char offset)
++{
++      return (inb(card->addr + offset));
++}
++
++static __inline__ void
++WriteW6692(w6692pci *card, u_char offset, u_char value)
++{
++      outb(value, card->addr + offset);
++}
++
++static __inline__ u_char
++ReadW6692B(w6692pci *card, int bchan, u_char offset)
++{
++      return (inb(card->addr + (bchan ? 0x40 : 0) + offset));
++}
++
++static __inline__ void
++WriteW6692B(w6692pci *card, int bchan, u_char offset, u_char value)
++{
++      outb(value, card->addr + (bchan ? 0x40 : 0) + offset);
++}
++
++static char *W6692Ver[] __initdata =
++{"W6692 V00", "W6692 V01", "W6692 V10",
++ "W6692 V11"};
++
++static void
++W6692Version(w6692pci *card, char *s)
++{
++      int val;
++
++      val = ReadW6692(card, W_D_RBCH);
++      printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]);
++}
++
++static void
++w6692_led_handler(w6692pci *card, int on)
++{
++      if (!card->led)
++              return;
++      if (on) {
++              card->xdata &= 0xfb;    /*  LED ON */
++              WriteW6692(card, W_XDATA, card->xdata);
++      } else {
++              card->xdata |= 0x04;    /*  LED OFF */
++              WriteW6692(card, W_XDATA, card->xdata);
++      }
++}
++
++static void
++ph_command(w6692pci *card, u_char command)
++{
++      if (card->dch.debug & L1_DEB_ISAC)
++              mISDN_debugprint(&card->dch.inst, "ph_command %x", command);
++      WriteW6692(card, W_CIX, command);
++}
++
++static void
++W6692_new_ph(dchannel_t *dch)
++{
++      u_int           prim = PH_SIGNAL | INDICATION;
++      u_int           para = 0;
++      mISDNif_t       *upif = &dch->inst.up;
++
++      if (dch->debug)
++              printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event);
++      if (!test_and_clear_bit(D_L1STATECHANGE, &dch->event))
++              return;
++      switch (dch->ph_state) {
++              case W_L1CMD_RST:
++                      dch->inst.lock(dch->inst.data, 0);
++                      ph_command(dch->hw, W_L1CMD_DRC);
++                      dch->inst.unlock(dch->inst.data);
++                      prim = PH_CONTROL | INDICATION;
++                      para = HW_RESET;
++                      while(upif) {
++                              if_link(upif, prim, para, 0, NULL, 0);
++                              upif = upif->clone;
++                      }
++                      upif = &dch->inst.up;
++                      /* fall trough */
++              case W_L1IND_CD:
++                      prim = PH_CONTROL | CONFIRM;
++                      para = HW_DEACTIVATE;
++                      break;
++              case W_L1IND_DRD:
++                      prim = PH_CONTROL | INDICATION;
++                      para = HW_DEACTIVATE;
++                      break;
++              case W_L1IND_CE:
++                      prim = PH_CONTROL | INDICATION;
++                      para = HW_POWERUP;
++                      break;
++              case W_L1IND_LD:
++                      para = ANYSIGNAL;
++                      break;
++              case W_L1IND_ARD:
++                      para = INFO2;
++                      break;
++              case W_L1IND_AI8:
++                      para = INFO4_P8;
++                      break;
++              case W_L1IND_AI10:
++                      para = INFO4_P10;
++                      break;
++              default:
++                      return;
++      }
++      while(upif) {
++              if_link(upif, prim, para, 0, NULL, 0);
++              upif = upif->clone;
++      }
++}
++
++static void
++W6692_empty_Dfifo(w6692pci *card, int count)
++{
++      dchannel_t      *dch = &card->dch;
++      u_char          *ptr;
++
++      if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
++              mISDN_debugprint(&dch->inst, "empty_Dfifo");
++
++      if (!dch->rx_skb) {
++              if (!(dch->rx_skb = alloc_stack_skb(MAX_DFRAME_LEN_L1, dch->up_headerlen))) {
++                      printk(KERN_WARNING "mISDN: D receive out of memory\n");
++                      WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
++                      return;
++              }
++      }
++      if ((dch->rx_skb->len + count) >= MAX_DFRAME_LEN_L1) {
++              if (dch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&dch->inst, "empty_Dfifo overrun %d",
++                              dch->rx_skb->len + count);
++              WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
++              return;
++      }
++      ptr = skb_put(dch->rx_skb, count);
++      insb(card->addr + W_D_RFIFO, ptr, count);
++      WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
++      if (dch->debug & L1_DEB_ISAC_FIFO) {
++              char *t = dch->dlog;
++
++              t += sprintf(t, "empty_Dfifo cnt %d", count);
++              mISDN_QuickHex(t, ptr, count);
++              mISDN_debugprint(&dch->inst, dch->dlog);
++      }
++}
++
++static void
++W6692_fill_Dfifo(w6692pci *card)
++{
++      dchannel_t      *dch = &card->dch;
++      int             count;
++      u_char          *ptr;
++      u_char          cmd = W_D_CMDR_XMS;
++
++      if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
++              mISDN_debugprint(&dch->inst, "fill_Dfifo");
++
++      count = dch->tx_len - dch->tx_idx;
++      if (count <= 0)
++              return;
++      if (count > 32) {
++              count = 32;
++      } else
++              cmd |= W_D_CMDR_XME;
++      ptr = dch->tx_buf + dch->tx_idx;
++      dch->tx_idx += count;
++      outsb(card->addr + W_D_XFIFO, ptr, count);
++      WriteW6692(card, W_D_CMDR, cmd);
++      if (test_and_set_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
++              mISDN_debugprint(&dch->inst, "fill_Dfifo dbusytimer running");
++              del_timer(&dch->dbusytimer);
++      }
++      init_timer(&dch->dbusytimer);
++      dch->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
++      add_timer(&dch->dbusytimer);
++      if (dch->debug & L1_DEB_ISAC_FIFO) {
++              char *t = dch->dlog;
++
++              t += sprintf(t, "fill_Dfifo cnt %d", count);
++              mISDN_QuickHex(t, ptr, count);
++              mISDN_debugprint(&dch->inst, dch->dlog);
++      }
++}
++
++static void
++d_retransmit(w6692pci *card)
++{
++      dchannel_t *dch = &card->dch;
++
++      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++              del_timer(&dch->dbusytimer);
++      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++              dchannel_sched_event(dch, D_CLEARBUSY);
++      if (test_bit(FLG_TX_BUSY, &dch->DFlags)) {
++              /* Restart frame */
++              dch->tx_idx = 0;
++              W6692_fill_Dfifo(card);
++      } else {
++              printk(KERN_WARNING "mISDN: w6692 XDU no TX_BUSY\n");
++              mISDN_debugprint(&dch->inst, "XDU no TX_BUSY");
++              if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
++                      if (dch->next_skb) {
++                              dch->tx_len = dch->next_skb->len;
++                              memcpy(dch->tx_buf,
++                                      dch->next_skb->data,
++                                      dch->tx_len);
++                              dch->tx_idx = 0;
++                              W6692_fill_Dfifo(card);
++                              dchannel_sched_event(dch, D_XMTBUFREADY);
++                      } else {
++                              printk(KERN_WARNING "w6692 xdu irq TX_NEXT without skb\n");
++                      }
++              }
++      }
++}
++
++static void
++handle_rxD(w6692pci *card) {
++      u_char  stat;
++      int     count;
++
++      stat = ReadW6692(card, W_D_RSTA);
++      if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
++              if (stat & W_D_RSTA_RDOV) {
++                      if (card->dch.debug & L1_DEB_WARN)
++                              mISDN_debugprint(&card->dch.inst, "D-channel RDOV");
++#ifdef ERROR_STATISTIC
++                      card->dch.err_rx++;
++#endif
++              }
++              if (stat & W_D_RSTA_CRCE) {
++                      if (card->dch.debug & L1_DEB_WARN)
++                              mISDN_debugprint(&card->dch.inst, "D-channel CRC error");
++#ifdef ERROR_STATISTIC
++                      card->dch.err_crc++;
++#endif
++              }
++              if (stat & W_D_RSTA_RMB) {
++                      if (card->dch.debug & L1_DEB_WARN)
++                              mISDN_debugprint(&card->dch.inst, "D-channel ABORT");
++#ifdef ERROR_STATISTIC
++                      card->dch.err_rx++;
++#endif
++              }
++              if (card->dch.rx_skb)
++                      dev_kfree_skb(card->dch.rx_skb);
++              WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
++      } else {
++              count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
++              if (count == 0)
++                      count = W_D_FIFO_THRESH;
++              W6692_empty_Dfifo(card, count);
++              if (card->dch.rx_skb) {
++                      skb_queue_tail(&card->dch.rqueue, card->dch.rx_skb);
++              }
++      }
++      card->dch.rx_skb = NULL;
++      dchannel_sched_event(&card->dch, D_RCVBUFREADY);
++}
++
++static void
++handle_txD(w6692pci *card) {
++      register dchannel_t     *dch = &card->dch;
++
++      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++              del_timer(&dch->dbusytimer);
++      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++              dchannel_sched_event(dch, D_CLEARBUSY);
++      if (dch->tx_idx < dch->tx_len) {
++              W6692_fill_Dfifo(card);
++      } else {
++              if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
++                      if (dch->next_skb) {
++                              dch->tx_len = dch->next_skb->len;
++                              memcpy(dch->tx_buf,
++                                      dch->next_skb->data, dch->tx_len);
++                              dch->tx_idx = 0;
++                              W6692_fill_Dfifo(card);
++                              dchannel_sched_event(dch, D_XMTBUFREADY);
++                      } else {
++                              printk(KERN_WARNING "w6692 txD irq TX_NEXT without skb\n");
++                              test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++                      }
++              } else
++                      test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++      }
++}
++
++static void
++handle_statusD(w6692pci *card) {
++      register dchannel_t     *dch = &card->dch;
++      u_char                  exval, v1, cir;
++
++      exval = ReadW6692(card, W_D_EXIR);
++      
++      if (card->dch.debug & L1_DEB_ISAC)
++              mISDN_debugprint(&card->dch.inst, "D_EXIR %02x", exval);
++      if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {    /* Transmit underrun/collision */
++              if (card->dch.debug & L1_DEB_WARN)
++                      mISDN_debugprint(&card->dch.inst, "D-channel underrun/collision");
++#ifdef ERROR_STATISTIC
++              dch->err_tx++;
++#endif
++              d_retransmit(card);
++      }
++      if (exval & W_D_EXI_RDOV) {     /* RDOV */
++              if (card->dch.debug & L1_DEB_WARN)
++                      mISDN_debugprint(&card->dch.inst, "D-channel RDOV");
++              WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);
++      }
++      if (exval & W_D_EXI_TIN2)       /* TIN2 - never */
++              if (card->dch.debug & L1_DEB_WARN)
++                      mISDN_debugprint(&card->dch.inst, "spurious TIN2 interrupt");
++      if (exval & W_D_EXI_MOC) {      /* MOC - not supported */
++              v1 = ReadW6692(card, W_MOSR);
++              if (card->dch.debug & L1_DEB_ISAC) {
++                      mISDN_debugprint(&card->dch.inst, "spurious MOC interrupt");
++                      mISDN_debugprint(&card->dch.inst, "MOSR %02x", v1);
++              }
++      }
++      if (exval & W_D_EXI_ISC) {      /* ISC - Level1 change */
++              cir = ReadW6692(card, W_CIR);
++              if (card->dch.debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&card->dch.inst, "ISC CIR=0x%02X", cir);
++              if (cir & W_CIR_ICC) {
++                      v1 = cir & W_CIR_COD_MASK;
++                      if (card->dch.debug & L1_DEB_ISAC)
++                              mISDN_debugprint(&card->dch.inst, "ph_state_change %x -> %x",
++                                      dch->ph_state, v1);
++                      dch->ph_state = v1;
++                      if (card->led & W_LED1_S0STATUS) {
++                              switch (v1) {
++                                      case W_L1IND_AI8:
++                                      case W_L1IND_AI10:
++                                              w6692_led_handler(card, 1);
++                                              break;
++                                      default:
++                                              w6692_led_handler(card, 0);
++                                              break;
++                              }
++                      }
++                      dchannel_sched_event(dch, D_L1STATECHANGE);
++              }
++              if (cir & W_CIR_SCC) {
++                      v1 = ReadW6692(card, W_SQR);
++                      if (card->dch.debug & L1_DEB_ISAC)
++                              mISDN_debugprint(&card->dch.inst, "SCC SQR=0x%02X", v1);
++              }
++      }
++      if (exval & W_D_EXI_WEXP) {
++              if (card->dch.debug & L1_DEB_WARN)
++                      mISDN_debugprint(&card->dch.inst, "spurious WEXP interrupt!");
++      }
++      if (exval & W_D_EXI_TEXP) {
++              if (card->dch.debug & L1_DEB_WARN)
++                      mISDN_debugprint(&card->dch.inst, "spurious TEXP interrupt!");
++      }
++}
++
++static void
++W6692_empty_Bfifo(bchannel_t *bch, int count)
++{
++      u_char          *ptr;
++      w6692pci        *card = bch->inst.data;
++
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "empty_Bfifo %d", count);
++      if (bch->protocol == ISDN_PID_NONE) {
++              if (bch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&bch->inst, "empty_Bfifo ISDN_PID_NONE");
++              WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
++              bch->rx_idx = 0;
++              return;
++      }
++      if (bch->rx_idx + count > MAX_DATA_MEM) {
++              if (bch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&bch->inst, "empty_Bfifo incoming packet too large");
++              WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
++              bch->rx_idx = 0;
++              return;
++      }
++      ptr = bch->rx_buf + bch->rx_idx;
++      bch->rx_idx += count;
++      insb(card->addr + W_B_RFIFO + (bch->channel ? 0x40 : 0), ptr, count);
++      WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
++
++      if (bch->debug & L1_DEB_HSCX_FIFO) {
++              char *t = bch->blog;
++
++              t += sprintf(t, "empty_Bfifo B%d cnt %d", bch->channel, count);
++              mISDN_QuickHex(t, ptr, count);
++              mISDN_debugprint(&bch->inst, bch->blog);
++      }
++}
++
++static void
++W6692_fill_Bfifo(bchannel_t *bch)
++{
++      w6692pci        *card = bch->inst.data;
++      int             count;
++      u_char          *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;
++
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "%s", __FUNCTION__);
++      count = bch->tx_len - bch->tx_idx;
++      if (count <= 0)
++              return;
++      ptr = bch->tx_buf + bch->tx_idx;
++      if (count > W_B_FIFO_THRESH) {
++              count = W_B_FIFO_THRESH;
++      } else {
++              if (bch->protocol != ISDN_PID_L1_B_64TRANS)
++                      cmd |= W_B_CMDR_XME;
++      }
++      if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
++              mISDN_debugprint(&bch->inst, "%s: %d/%d", __FUNCTION__,
++                      count, bch->tx_idx);
++      bch->tx_idx += count;
++      outsb(card->addr + W_B_XFIFO + (bch->channel ? 0x40 : 0), ptr, count);
++      WriteW6692B(card, bch->channel, W_B_CMDR, cmd);
++      if (bch->debug & L1_DEB_HSCX_FIFO) {
++              char *t = bch->blog;
++
++              t += sprintf(t, "fill_Bfifo  B%d cnt %d",
++                           bch->channel, count);
++              mISDN_QuickHex(t, ptr, count);
++              mISDN_debugprint(&bch->inst, bch->blog);
++      }
++}
++
++static int
++setvolume(bchannel_t *bch, int mic, struct sk_buff *skb)
++{
++      w6692pci        *card = bch->inst.data;
++      u16             *vol = (u16 *)skb->data;
++      u_char          val;
++
++      if ((card->pots == 0) || (bch->protocol != ISDN_PID_L1_B_64TRANS))
++              return(-ENODEV);
++      if (skb->len < 2)
++              return(-EINVAL);
++      if (*vol > 7)
++              return(-EINVAL);
++      val = *vol & 7;
++      val = 7 - val;
++      if (mic) {
++              val <<= 3;
++              card->xaddr &= 0xc7;
++      } else {
++              card->xaddr &= 0xf8;
++      }
++      card->xaddr |= val;
++      WriteW6692(card, W_XADDR, card->xaddr);
++      return(0);
++}
++
++static int
++enable_pots(bchannel_t *bch)
++{
++      w6692_bc        *bhw  = bch->hw;
++      w6692pci        *card = bch->inst.data;
++
++      if ((card->pots == 0) || (bch->protocol != ISDN_PID_L1_B_64TRANS))
++              return(-ENODEV);
++      
++      bhw->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;
++      WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
++      WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
++      card->pctl |= (bch->channel ? W_PCTL_PCX : 0);
++      WriteW6692(card, W_PCTL, card->pctl);
++      return(0);
++}
++
++static int
++disable_pots(bchannel_t *bch)
++{
++      w6692_bc        *bhw  = bch->hw;
++      w6692pci        *card = bch->inst.data;
++
++      if (card->pots == 0)
++              return(-ENODEV);
++      bhw->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);
++      WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
++      WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST);
++      return(0);
++}
++
++static int
++mode_w6692(bchannel_t *bch, int bc, int protocol)
++{
++      w6692pci        *card = bch->inst.data;
++      w6692_bc        *bhw  = bch->hw;
++      
++
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "B%d protocol %x-->%x ch %d-->%d",
++                      bch->channel, bch->protocol, protocol, bch->channel, bc);
++      switch (protocol) {
++              case (-1): /* used for init */
++                      bch->protocol = -1;
++                      bch->channel = bc;
++              case (ISDN_PID_NONE):
++                      if (bch->protocol == ISDN_PID_NONE)
++                              break;
++                      bch->protocol = ISDN_PID_NONE;
++                      if (card->pots && (bhw->b_mode & W_B_MODE_EPCM))
++                              disable_pots(bch);
++                      bhw->b_mode = 0;
++                      bch->tx_len = 0;
++                      bch->tx_idx = 0;
++                      bch->rx_idx = 0;
++                      if (bch->next_skb) {
++                              dev_kfree_skb(bch->next_skb);
++                              bch->next_skb = NULL;
++                      }
++                      discard_queue(&bch->rqueue);
++                      WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
++                      WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
++                      break;
++              case (ISDN_PID_L1_B_64TRANS):
++                      bch->protocol = protocol;
++                      bhw->b_mode = W_B_MODE_MMS;
++                      WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
++                      WriteW6692B(card, bch->channel, W_B_EXIM, 0);
++                      WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST);
++                      bch_sched_event(bch, B_XMTBUFREADY);
++                      break;
++              case (ISDN_PID_L1_B_64HDLC):
++                      bch->protocol = protocol;
++                      bhw->b_mode = W_B_MODE_ITF;
++                      WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
++                      WriteW6692B(card, bch->channel, W_B_ADM1, 0xff);
++                      WriteW6692B(card, bch->channel, W_B_ADM2, 0xff);
++                      WriteW6692B(card, bch->channel, W_B_EXIM, 0);
++                      WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST);
++                      bch_sched_event(bch, B_XMTBUFREADY);
++                      break;
++              default:
++                      mISDN_debugprint(&bch->inst, "prot not known %x", protocol);
++                      return(-ENOPROTOOPT);
++      }
++      return(0);
++}
++
++static void
++send_next(bchannel_t *bch)
++{
++      if (bch->protocol == ISDN_PID_NONE)
++              return;
++      if (bch->tx_idx < bch->tx_len)
++              W6692_fill_Bfifo(bch);
++      else {
++              bch->tx_idx = 0;
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      if (bch->next_skb) {
++                              bch->tx_len = bch->next_skb->len;
++                              memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len);
++                              W6692_fill_Bfifo(bch);
++                      } else {
++                              bch->tx_len = 0;
++                              printk(KERN_WARNING "W6692 tx irq TX_NEXT without skb\n");
++                              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++                      }
++              } else {
++                      bch->tx_len = 0;
++                      test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              }
++              bch_sched_event(bch, B_XMTBUFREADY);
++      }
++}
++
++static void
++W6692B_interrupt(w6692pci *card, int ch)
++{
++      bchannel_t      *bch = &card->bch[ch];
++      int             count;
++      u_char          stat, star = 0;
++      struct sk_buff  *skb;
++
++      stat = ReadW6692B(card, ch, W_B_EXIR);
++      if (bch->debug & L1_DEB_HSCX)
++              mISDN_debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat);
++
++      if (stat & W_B_EXI_RME) {
++              star = ReadW6692B(card, ch, W_B_STAR);
++              if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
++                      if ((star & W_B_STAR_RDOV) && (bch->protocol != ISDN_PID_NONE)) {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "B%d RDOV protocol=%x",
++                                              ch +1, bch->protocol);
++#ifdef ERROR_STATISTIC
++                              bch->err_rdo++;
++#endif
++                      }
++                      if ((star & W_B_STAR_CRCE) && (bch->protocol == ISDN_PID_L1_B_64HDLC)) {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "B%d CRC error", ch +1);
++#ifdef ERROR_STATISTIC
++                              bch->err_crc++;
++#endif
++                      }
++                      if ((star & W_B_STAR_RMB) && (bch->protocol == ISDN_PID_L1_B_64HDLC)) {
++                              if (bch->debug & L1_DEB_WARN)
++                                      mISDN_debugprint(&bch->inst, "B%d message abort", ch +1);
++#ifdef ERROR_STATISTIC
++                              bch->err_inv++;
++#endif
++                      }
++                      WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
++              } else {
++                      count = ReadW6692B(card, ch, W_B_RBCL) & (W_B_FIFO_THRESH - 1);
++                      if (count == 0)
++                              count = W_B_FIFO_THRESH;
++                      W6692_empty_Bfifo(bch, count);
++                      if (bch->rx_idx > 0) {
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "Bchan Frame %d", bch->rx_idx);
++                              if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen)))
++                                      printk(KERN_WARNING "Bchan receive out of memory\n");
++                              else {
++                                      memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx);
++                                      skb_queue_tail(&bch->rqueue, skb);
++                              }
++                              bch_sched_event(bch, B_RCVBUFREADY);
++                      }
++              }
++              bch->rx_idx = 0;
++      }
++      if (stat & W_B_EXI_RMR) {
++              if (!(stat & W_B_EXI_RME))
++                      star = ReadW6692B(card, ch, W_B_STAR);
++              if (star & W_B_STAR_RDOV) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "B%d RDOV protocol=%x",
++                                      ch +1, bch->protocol);
++#ifdef ERROR_STATISTIC
++                      bch->err_rdo++;
++#endif
++                      WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
++              } else {
++                      W6692_empty_Bfifo(bch, W_B_FIFO_THRESH);
++                      if ((bch->protocol == ISDN_PID_L1_B_64TRANS) && (bch->rx_idx > 0)) {
++                              /* receive audio data */
++                              if (bch->debug & L1_DEB_HSCX)
++                                      mISDN_debugprint(&bch->inst, "Bchan Frame %d", bch->rx_idx);
++                              if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen)))
++                                      printk(KERN_WARNING "Bchan receive out of memory\n");
++                              else {
++                                      memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx);
++                                      skb_queue_tail(&bch->rqueue, skb);
++                              }
++                              bch_sched_event(bch, B_RCVBUFREADY);
++                              bch->rx_idx = 0;
++                      }
++              }
++      }
++      if (stat & W_B_EXI_RDOV) {
++              if (!(star & W_B_STAR_RDOV)) { /* only if it is not handled yet */
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "B%d RDOV IRQ protocol=%x",
++                                      ch +1, bch->protocol);
++#ifdef ERROR_STATISTIC
++                      bch->err_rdo++;
++#endif
++                      WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
++              }
++      }
++      if (stat & W_B_EXI_XFR) {
++              if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {
++                      star = ReadW6692B(card, ch, W_B_STAR);
++                      if (bch->debug & L1_DEB_HSCX)
++                              mISDN_debugprint(&bch->inst, "B%d star %02x", ch +1, star);
++              }
++              if (star & W_B_STAR_XDOW) {
++                      if (bch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&bch->inst, "B%d XDOW protocol=%x",
++                                      ch +1, bch->protocol);
++#ifdef ERROR_STATISTIC
++                      bch->err_xdu++;
++#endif
++                      WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
++                      /* resend */
++                      if (bch->tx_len) {
++                              if (bch->protocol != ISDN_PID_L1_B_64TRANS)
++                                      bch->tx_idx = 0;
++                      }
++              }
++              send_next(bch);
++              if (stat & W_B_EXI_XDUN)
++                      return; /* handle XDOW only once */
++      }
++      if (stat & W_B_EXI_XDUN) {
++              if (bch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&bch->inst, "B%d XDUN protocol=%x",
++                              ch +1, bch->protocol);
++#ifdef ERROR_STATISTIC
++              bch->err_xdu++;
++#endif
++              WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
++              /* resend */
++              if (bch->tx_len) {
++                      if (bch->protocol != ISDN_PID_L1_B_64TRANS)
++                              bch->tx_idx = 0;
++              }
++              send_next(bch);
++      }
++}
++
++static irqreturn_t
++w6692_interrupt(int intno, void *dev_id, struct pt_regs *regs)
++{
++      w6692pci        *card = dev_id;
++      u_long          flags;
++      u_char          ista;
++
++      spin_lock_irqsave(&card->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      card->lock.spin_adr = (void *)0x2001;
++#endif
++      ista = ReadW6692(card, W_ISTA);
++      if ((ista | card->imask) == card->imask) {
++              /* possible a shared  IRQ reqest */
++#ifdef SPIN_DEBUG
++              card->lock.spin_adr = NULL;
++#endif
++              spin_unlock_irqrestore(&card->lock.lock, flags);
++              return IRQ_NONE;
++      }
++      card->irqcnt++;
++      if (test_and_set_bit(STATE_FLAG_BUSY, &card->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
++                      __FUNCTION__, card->lock.state);
++#ifdef SPIN_DEBUG
++              printk(KERN_ERR "%s: previous lock:%p\n",
++                      __FUNCTION__, card->lock.busy_adr);
++#endif
++#ifdef LOCK_STATISTIC
++              card->lock.irq_fail++;
++#endif
++      } else {
++#ifdef LOCK_STATISTIC
++              card->lock.irq_ok++;
++#endif
++#ifdef SPIN_DEBUG
++              card->lock.busy_adr = w6692_interrupt;
++#endif
++      }
++
++      test_and_set_bit(STATE_FLAG_INIRQ, &card->lock.state);
++#ifdef SPIN_DEBUG
++      card->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&card->lock.lock, flags);
++/* Begin IRQ handler */
++      if (card->dch.debug & L1_DEB_ISAC)
++              mISDN_debugprint(&card->dch.inst, "ista %02x", ista);
++      ista &= ~card->imask;
++      if (ista & W_INT_B1_EXI)
++              W6692B_interrupt(card, 0);
++      if (ista & W_INT_B2_EXI)
++              W6692B_interrupt(card, 1);
++      if (ista & W_INT_D_RME)
++              handle_rxD(card);
++      if (ista & W_INT_D_RMR)
++              W6692_empty_Dfifo(card, W_D_FIFO_THRESH);
++      if (ista & W_INT_D_XFR)
++              handle_txD(card);
++      if (ista & W_INT_D_EXI)
++              handle_statusD(card);
++      if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */
++              mISDN_debugprint(&card->dch.inst, "W6692 spurious XINT!");
++/* End IRQ Handler */
++      spin_lock_irqsave(&card->lock.lock, flags);
++#ifdef SPIN_DEBUG
++      card->lock.spin_adr = (void *)0x2002;
++#endif
++      if (!test_and_clear_bit(STATE_FLAG_INIRQ, &card->lock.state)) {
++      }
++      if (!test_and_clear_bit(STATE_FLAG_BUSY, &card->lock.state)) {
++              printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
++                      __FUNCTION__, card->lock.state);
++      }
++#ifdef SPIN_DEBUG
++      card->lock.busy_adr = NULL;
++      card->lock.spin_adr = NULL;
++#endif
++      spin_unlock_irqrestore(&card->lock.lock, flags);
++      return IRQ_HANDLED;
++}
++
++static void
++dbusy_timer_handler(dchannel_t *dch)
++{
++      w6692pci        *card = dch->hw;
++      int     rbch, star;
++
++      if (test_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
++              if (dch->inst.lock(dch->inst.data, 1)) {
++                      dch->dbusytimer.expires = jiffies + 1;
++                      add_timer(&dch->dbusytimer);
++                      return;
++              }
++              rbch = ReadW6692(card, W_D_RBCH);
++              star = ReadW6692(card, W_D_STAR);
++              if (dch->debug) 
++                      mISDN_debugprint(&dch->inst, "D-Channel Busy RBCH %02x STAR %02x",
++                              rbch, star);
++              if (star & W_D_STAR_XBZ) {      /* D-Channel Busy */
++                      test_and_set_bit(FLG_L1_DBUSY, &dch->DFlags);
++              } else {
++                      /* discard frame; reset transceiver */
++                      test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags);
++                      if (dch->tx_idx) {
++                              dch->tx_idx = 0;
++                      } else {
++                              printk(KERN_WARNING "mISDN: W6692 D-Channel Busy no tx_idx\n");
++                              mISDN_debugprint(&dch->inst, "D-Channel Busy no tx_idx");
++                      }
++                      /* Transmitter reset */
++                      WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST);      /* Transmitter reset */
++              }
++              dch->inst.unlock(dch->inst.data);
++      }
++}
++
++void initW6692(w6692pci *card)
++{
++      u_char  val;
++
++      card->dch.hw_bh = W6692_new_ph;
++      card->dch.dbusytimer.function = (void *) dbusy_timer_handler;
++      card->dch.dbusytimer.data = (u_long) &card->dch;
++      init_timer(&card->dch.dbusytimer);
++      mode_w6692(&card->bch[0], 0, -1);
++      mode_w6692(&card->bch[1], 1, -1);
++      WriteW6692(card, W_D_CTL, 0x00);
++      WriteW6692(card, W_IMASK, 0xff);
++      WriteW6692(card, W_D_SAM, 0xff);
++      WriteW6692(card, W_D_TAM, 0xff);
++      WriteW6692(card, W_D_MODE, W_D_MODE_RACT);
++      card->dch.ph_state = W_L1CMD_RST;
++      ph_command(card, W_L1CMD_RST);
++      ph_command(card, W_L1CMD_ECK);
++      /* Reenable all IRQ */
++      card->imask = 0x18;
++      WriteW6692(card, W_IMASK, card->imask);
++      WriteW6692(card, W_D_EXIM, 0x00);
++      WriteW6692B(card, 0, W_B_EXIM, 0);
++      WriteW6692B(card, 1, W_B_EXIM, 0);
++      /* Reset D-chan receiver and transmitter */
++      WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
++      /* Reset B-chan receiver and transmitter */
++      WriteW6692B(card, 0, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
++      WriteW6692B(card, 1, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
++      /* enable peripheral */
++      card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | W_PCTL_OE1 | W_PCTL_OE0;
++      if (card->pots) {
++              card->xaddr = 0x00; /* all sw off */
++              card->xdata = 0x06;  /*  LED OFF / POWER UP / ALAW */
++              WriteW6692(card, W_PCTL, card->pctl);
++              WriteW6692(card, W_XADDR, card->xaddr);
++              WriteW6692(card, W_XDATA, card->xdata);
++              val = ReadW6692(card, W_XADDR);
++              if (card->dch.debug & L1_DEB_ISAC)
++                      mISDN_debugprint(&card->dch.inst, "W_XADDR: %02x", val);
++              if (card->led & W_LED1_ON)
++                      w6692_led_handler(card, 1);
++              else
++                      w6692_led_handler(card, 0);
++      }
++}
++
++static void reset_w6692(w6692pci *card)
++{
++      WriteW6692(card, W_D_CTL, W_D_CTL_SRST);
++      mdelay(10);
++      WriteW6692(card, W_D_CTL, 0);
++}
++
++static int init_card(w6692pci *card)
++{
++      int             cnt = 3;
++
++      lock_dev(card, 0);
++      if (request_irq(card->irq, w6692_interrupt, SA_SHIRQ,
++              "w6692", card)) {
++              printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
++                      card->irq);
++              unlock_dev(card);
++              return(-EIO);
++      }
++      while (cnt) {
++              initW6692(card);
++              /* RESET Receiver and Transmitter */
++              unlock_dev(card);
++              /* Timeout 10ms */
++              current->state = TASK_UNINTERRUPTIBLE;
++              schedule_timeout((10*HZ)/1000);
++              printk(KERN_INFO "w6692: IRQ %d count %d\n",
++                      card->irq, card->irqcnt);
++              if (!card->irqcnt) {
++                      printk(KERN_WARNING
++                             "w6692: IRQ(%d) getting no interrupts during init %d\n",
++                             card->irq, 4 - cnt);
++                      if (cnt == 1) {
++                              return (-EIO);
++                      } else {
++                              reset_w6692(card);
++                              cnt--;
++                      }
++              } else {
++                      return(0);
++              }
++              lock_dev(card, 0);
++      }
++      unlock_dev(card);
++      return(-EIO);
++}
++
++#define MAX_CARDS     4
++#define MODULE_PARM_T "1-4i"
++static int w6692_cnt;
++static u_int protocol[MAX_CARDS];
++static int layermask[MAX_CARDS];
++
++static mISDNobject_t  w6692;
++static int debug;
++static int pots[MAX_CARDS];
++static int led[MAX_CARDS];
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++MODULE_PARM(debug, "1i");
++MODULE_PARM(led, MODULE_PARM_T);
++MODULE_PARM(pots, MODULE_PARM_T);
++MODULE_PARM(protocol, MODULE_PARM_T);
++MODULE_PARM(layermask, MODULE_PARM_T);
++#endif
++
++/******************************/
++/* Layer2 -> Layer 1 Transfer */
++/******************************/
++static int
++w6692_l2l1B(mISDNif_t *hif, struct sk_buff *skb)
++{
++      bchannel_t      *bch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      bch = hif->fdata;
++      if ((hh->prim == PH_DATA_REQ) ||
++              (hh->prim == (DL_DATA | REQUEST))) {
++              if (bch->next_skb) {
++                      printk(KERN_WARNING "%s: next_skb exist ERROR\n",
++                              __FUNCTION__);
++                      return(-EBUSY);
++              }
++              bch->inst.lock(bch->inst.data, 0);
++              if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) {
++                      test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag);
++                      bch->next_skb = skb;
++                      bch->inst.unlock(bch->inst.data);
++                      return(0);
++              } else {
++                      bch->tx_len = skb->len;
++                      memcpy(bch->tx_buf, skb->data, bch->tx_len);
++                      bch->tx_idx = 0;
++                      W6692_fill_Bfifo(bch);
++                      bch->inst.unlock(bch->inst.data);
++                      if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                              && bch->dev)
++                              hif = &bch->dev->rport.pif;
++                      else
++                              hif = &bch->inst.up;
++                      skb_trim(skb, 0);
++                      return(if_newhead(hif, hh->prim | CONFIRM,
++                              hh->dinfo, skb));
++              }
++      } else if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_ESTABLISH  | REQUEST))) {
++              if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
++                      ret = 0;
++              else {
++                      bch->inst.lock(bch->inst.data, 0);
++                      ret = mode_w6692(bch, bch->channel, bch->inst.pid.protocol[1]);
++                      bch->inst.unlock(bch->inst.data);
++              }
++              if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                      if (bch->dev)
++                              if_link(&bch->dev->rport.pif,
++                                      hh->prim | CONFIRM, 0, 0, NULL, 0);
++              skb_trim(skb, 0);
++              return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
++      } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) ||
++              (hh->prim == (DL_RELEASE | REQUEST)) ||
++              (hh->prim == (MGR_DISCONNECT | REQUEST))) {
++              bch->inst.lock(bch->inst.data, 0);
++              if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
++                      dev_kfree_skb(bch->next_skb);
++                      bch->next_skb = NULL;
++              }
++              test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
++              mode_w6692(bch, bch->channel, ISDN_PID_NONE);
++              test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
++              bch->inst.unlock(bch->inst.data);
++              skb_trim(skb, 0);
++              if (hh->prim != (MGR_DISCONNECT | REQUEST)) {
++                      if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
++                              if (bch->dev)
++                                      if_link(&bch->dev->rport.pif,
++                                              hh->prim | CONFIRM, 0, 0, NULL, 0);
++                      if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
++                              return(0);
++              }
++              ret = 0;
++      } else if (hh->prim == (PH_CONTROL | REQUEST)) {
++              ret = 0;
++              bch->inst.lock(bch->inst.data, 0);
++              if (hh->dinfo == HW_POTS_ON) {
++                      ret = enable_pots(bch);
++              } else if (hh->dinfo == HW_POTS_OFF) {
++                      ret = disable_pots(bch);
++              } else if (hh->dinfo == HW_POTS_SETMICVOL) {
++                      ret = setvolume(bch, 1, skb);
++              } else if (hh->dinfo == HW_POTS_SETSPKVOL) {
++                      ret = setvolume(bch, 0, skb);
++              } else
++                      ret = -EINVAL;
++              bch->inst.unlock(bch->inst.data);
++      } else {
++              printk(KERN_WARNING "%s: unknown prim(%x)\n",
++                      __FUNCTION__, hh->prim);
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++static int
++w6692_l1hwD(mISDNif_t *hif, struct sk_buff *skb)
++{
++      dchannel_t      *dch;
++      int             ret = -EINVAL;
++      mISDN_head_t    *hh;
++
++      if (!hif || !skb)
++              return(ret);
++      hh = mISDN_HEAD_P(skb);
++      dch = hif->fdata;
++      ret = 0;
++      if (hh->prim == PH_DATA_REQ) {
++              if (dch->next_skb) {
++                      mISDN_debugprint(&dch->inst, "w6692 l2l1 next_skb exist this shouldn't happen");
++                      return(-EBUSY);
++              }
++              dch->inst.lock(dch->inst.data,0);
++              if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) {
++                      test_and_set_bit(FLG_TX_NEXT, &dch->DFlags);
++                      dch->next_skb = skb;
++                      dch->inst.unlock(dch->inst.data);
++                      return(0);
++              } else {
++                      dch->tx_len = skb->len;
++                      memcpy(dch->tx_buf, skb->data, dch->tx_len);
++                      dch->tx_idx = 0;
++                      W6692_fill_Dfifo(dch->hw);
++                      dch->inst.unlock(dch->inst.data);
++                      return(if_newhead(&dch->inst.up, PH_DATA_CNF,
++                              hh->dinfo, skb));
++              }
++      } else if (hh->prim == (PH_SIGNAL | REQUEST)) {
++              dch->inst.lock(dch->inst.data,0);
++              if (hh->dinfo == INFO3_P8)
++                      ph_command(dch->hw, W_L1CMD_AR8);
++              else if (hh->dinfo == INFO3_P10)
++                      ph_command(dch->hw, W_L1CMD_AR10);
++              else
++                      ret = -EINVAL;
++              dch->inst.unlock(dch->inst.data);
++      } else if (hh->prim == (PH_CONTROL | REQUEST)) {
++              dch->inst.lock(dch->inst.data,0);
++              if (hh->dinfo == HW_RESET) {
++                      if (dch->ph_state != W_L1IND_DRD)
++                              ph_command(dch->hw, W_L1CMD_RST);
++                      ph_command(dch->hw, W_L1CMD_ECK);
++              } else if (hh->dinfo == HW_POWERUP) {
++                      ph_command(dch->hw, W_L1CMD_ECK);
++              } else if (hh->dinfo == HW_DEACTIVATE) {
++                      discard_queue(&dch->rqueue);
++                      if (dch->next_skb) {
++                              dev_kfree_skb(dch->next_skb);
++                              dch->next_skb = NULL;
++                      }
++                      test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags);
++                      test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
++                      if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
++                              del_timer(&dch->dbusytimer);
++                      if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
++                              dchannel_sched_event(dch, D_CLEARBUSY);
++              } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) {
++                      u_char  val = 0;
++
++                      if (1 & hh->dinfo)
++                              val |= 0x0c;
++                      if (2 & hh->dinfo)
++                              val |= 0x3;
++                      /* !!! not implemented yet */
++              } else {
++                      if (dch->debug & L1_DEB_WARN)
++                              mISDN_debugprint(&dch->inst, "w6692_l1hw unknown ctrl %x",
++                                      hh->dinfo);
++                      ret = -EINVAL;
++              }
++              dch->inst.unlock(dch->inst.data);
++      } else {
++              if (dch->debug & L1_DEB_WARN)
++                      mISDN_debugprint(&dch->inst, "w6692_l1hw unknown prim %x",
++                              hh->prim);
++              ret = -EINVAL;
++      }
++      if (!ret)
++              dev_kfree_skb(skb);
++      return(ret);
++}
++
++int __init 
++setup_w6692(w6692pci *card)
++{
++      u_int   val;
++      if (!request_region(card->addr, 256, "w6692")) {
++              printk(KERN_WARNING
++                     "mISDN: %s config port %x-%x already in use\n",
++                     "w6692",
++                     card->addr,
++                     card->addr + 255);
++              return(-EIO);
++      }
++      W6692Version(card, "W6692:");
++      val = ReadW6692(card, W_ISTA);
++      if (debug)
++              printk(KERN_DEBUG "W6692 ISTA=0x%X\n", val);
++      val = ReadW6692(card, W_IMASK);
++      if (debug)
++              printk(KERN_DEBUG "W6692 IMASK=0x%X\n", val);
++      val = ReadW6692(card, W_D_EXIR);
++      if (debug)
++              printk(KERN_DEBUG "W6692 D_EXIR=0x%X\n", val);
++      val = ReadW6692(card, W_D_EXIM);
++      if (debug)
++              printk(KERN_DEBUG "W6692 D_EXIM=0x%X\n", val);
++      val = ReadW6692(card, W_D_RSTA);
++      if (debug)
++              printk(KERN_DEBUG "W6692 D_RSTA=0x%X\n", val);
++      return (0);
++}
++
++static void
++release_card(w6692pci *card)
++{
++#ifdef LOCK_STATISTIC
++      printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",
++              card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq);
++      printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
++              card->lock.irq_ok, card->lock.irq_fail);
++#endif
++      lock_dev(card, 0);
++      /* disable all IRQ */
++      WriteW6692(card, W_IMASK, 0xff);
++      free_irq(card->irq, card);
++      mode_w6692(&card->bch[0], 0, ISDN_PID_NONE);
++      mode_w6692(&card->bch[1], 1, ISDN_PID_NONE);
++      if (card->led) {
++              card->xdata |= 0x04;    /*  LED OFF */
++              WriteW6692(card, W_XDATA, card->xdata);
++      }
++      release_region(card->addr, 256);
++      mISDN_free_bch(&card->bch[1]);
++      mISDN_free_bch(&card->bch[0]);
++      mISDN_free_dch(&card->dch);
++      w6692.ctrl(card->dch.inst.up.peer, MGR_DISCONNECT | REQUEST, &card->dch.inst.up);
++      w6692.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
++      list_del(&card->list);
++      unlock_dev(card);
++      pci_disable_device(card->pdev);
++      pci_set_drvdata(card->pdev, NULL);
++      kfree(card);
++}
++
++static int
++w6692_manager(void *data, u_int prim, void *arg) {
++      w6692pci        *card;
++      mISDNinstance_t *inst = data;
++      struct sk_buff  *skb;
++      int             channel = -1;
++
++      if (debug & 0x10000)
++              printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n",
++                      __FUNCTION__, data, prim, arg);
++      if (!data) {
++              MGR_HASPROTOCOL_HANDLER(prim,arg,&w6692)
++              printk(KERN_ERR "%s: no data prim %x arg %p\n",
++                      __FUNCTION__, prim, arg);
++              return(-EINVAL);
++      }
++      list_for_each_entry(card, &w6692.ilist, list) {
++              if (&card->dch.inst == inst) {
++                      channel = 2;
++                      break;
++              }
++              if (&card->bch[0].inst == inst) {
++                      channel = 0;
++                      break;
++              }
++              if (&card->bch[1].inst == inst) {
++                      channel = 1;
++                      break;
++              }
++      }
++      if (channel<0) {
++              printk(KERN_WARNING "%s: no channel data %p prim %x arg %p\n",
++                      __FUNCTION__, data, prim, arg);
++              return(-EINVAL);
++      }
++
++      switch(prim) {
++          case MGR_REGLAYER | CONFIRM:
++              if (channel == 2)
++                      dch_set_para(&card->dch, &inst->st->para);
++              else
++                      bch_set_para(&card->bch[channel], &inst->st->para);
++              break;
++          case MGR_UNREGLAYER | REQUEST:
++              if (channel == 2) {
++                      inst->down.fdata = &card->dch;
++                      if ((skb = create_link_skb(PH_CONTROL | REQUEST,
++                              HW_DEACTIVATE, 0, NULL, 0))) {
++                              if (w6692_l1hwD(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              } else {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (w6692_l2l1B(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++              }
++              w6692.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
++              w6692.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++              break;
++          case MGR_CLRSTPARA | INDICATION:
++              arg = NULL;
++          case MGR_ADDSTPARA | INDICATION:
++              if (channel == 2)
++                      dch_set_para(&card->dch, arg);
++              else
++                      bch_set_para(&card->bch[channel], arg);
++              break;
++          case MGR_RELEASE | INDICATION:
++              if (channel == 2) {
++                      release_card(card);
++              } else {
++                      w6692.refcnt--;
++              }
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              if (channel==2)
++                      return(mISDN_SetIF(inst, arg, prim, w6692_l1hwD, NULL,
++                              &card->dch));
++              else
++                      return(mISDN_SetIF(inst, arg, prim, w6692_l2l1B, NULL,
++                              &card->bch[channel]));
++              break;
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_SETSTACK | CONFIRM:
++              if ((channel!=2) && (inst->pid.global == 2)) {
++                      inst->down.fdata = &card->bch[channel];
++                      if ((skb = create_link_skb(PH_ACTIVATE | REQUEST,
++                              0, 0, NULL, 0))) {
++                              if (w6692_l2l1B(&inst->down, skb))
++                                      dev_kfree_skb(skb);
++                      }
++                      if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
++                              if_link(&inst->up, DL_ESTABLISH | INDICATION,
++                                      0, 0, NULL, 0);
++                      else
++                              if_link(&inst->up, PH_ACTIVATE | INDICATION,
++                                      0, 0, NULL, 0);
++              }
++              break;
++          case MGR_GLOBALOPT | REQUEST:
++              if (arg) {
++                      /* FIXME: detect cards with HEADSET */
++                      u_int *gopt = arg;
++                      *gopt = GLOBALOPT_INTERNAL_CTRL |
++                              GLOBALOPT_EXTERNAL_EQUIPMENT |
++                              GLOBALOPT_HANDSET;
++              } else
++                      return(-EINVAL);
++              break;
++          case MGR_SELCHANNEL | REQUEST:
++              /* no special procedure */
++              return(-EINVAL);
++          PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
++          default:
++              printk(KERN_WARNING "%s: prim %x not handled\n",
++                      __FUNCTION__, prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static int __devinit setup_instance(w6692pci *card)
++{
++      int             i, err;
++      mISDN_pid_t     pid;
++      
++      list_add_tail(&card->list, &w6692.ilist);
++      card->dch.debug = debug;
++      lock_HW_init(&card->lock);
++      card->dch.inst.lock = lock_dev;
++      card->dch.inst.unlock = unlock_dev;
++      card->dch.inst.pid.layermask = ISDN_LAYER(0);
++      card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
++      mISDN_init_instance(&card->dch.inst, &w6692, card);
++      sprintf(card->dch.inst.name, "W6692_%d", w6692_cnt+1);
++      mISDN_set_dchannel_pid(&pid, protocol[w6692_cnt], layermask[w6692_cnt]);
++      mISDN_init_dch(&card->dch);
++      card->dch.hw = card;
++      for (i=0; i<2; i++) {
++              card->bch[i].channel = i;
++              mISDN_init_instance(&card->bch[i].inst, &w6692, card);
++              card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
++              card->bch[i].inst.lock = lock_dev;
++              card->bch[i].inst.unlock = unlock_dev;
++              card->bch[i].debug = debug;
++              sprintf(card->bch[i].inst.name, "%s B%d", card->dch.inst.name, i+1);
++              mISDN_init_bch(&card->bch[i]);
++              card->bch[i].hw = &card->wbc[i];
++      }
++      if (debug)
++              printk(KERN_DEBUG "w6692 card %p dch %p bch1 %p bch2 %p\n",
++                      card, &card->dch, &card->bch[0], &card->bch[1]);
++      err = setup_w6692(card);
++      if (err) {
++              mISDN_free_dch(&card->dch);
++              mISDN_free_bch(&card->bch[1]);
++              mISDN_free_bch(&card->bch[0]);
++              list_del(&card->list);
++              kfree(card);
++              return(err);
++      }
++      card->pots = pots[w6692_cnt];
++      card->led = led[w6692_cnt];
++      w6692_cnt++;
++      err = w6692.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst);
++      if (err) {
++              release_card(card);
++              return(err);
++      }
++      for (i=0; i<2; i++) {
++              err = w6692.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst);
++              if (err) {
++                      printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
++                      w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++                      return(err);
++              }
++      }
++      err = w6692.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid);
++      if (err) {
++              printk(KERN_ERR  "MGR_SETSTACK REQUEST dch err(%d)\n", err);
++              w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(err);
++      }
++      err = init_card(card);
++      if (err) {
++              w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++              return(err);
++      }
++      w6692.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL);
++      printk(KERN_INFO "w6692 %d cards installed\n", w6692_cnt);
++      return(0);
++}
++
++static int __devinit w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++      int             err = -ENOMEM;
++      w6692pci        *card;
++
++      if (!(card = kmalloc(sizeof(w6692pci), GFP_ATOMIC))) {
++              printk(KERN_ERR "No kmem for w6692 card\n");
++              return(err);
++      }
++      memset(card, 0, sizeof(w6692pci));
++      card->pdev = pdev;
++      err = pci_enable_device(pdev);
++      if (err) {
++              kfree(card);
++              return(err);
++      }
++
++      printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
++             (char *) ent->driver_data, pdev->slot_name);
++
++      card->addr = pci_resource_start(pdev, 1);
++      card->irq = pdev->irq;
++      pci_set_drvdata(pdev, card);
++      err = setup_instance(card);
++      return(err);
++}
++
++static void __devexit w6692_remove_pci(struct pci_dev *pdev)
++{
++      w6692pci        *card = pci_get_drvdata(pdev);
++
++      if (card)
++              w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
++      else
++              printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__);
++}
++
++/* table entry in the PCI devices list */
++typedef struct {
++      int vendor_id;
++      int device_id;
++      char *vendor_name;
++      char *card_name;
++} PCI_ENTRY;
++
++static const PCI_ENTRY id_list[] =
++{
++      {PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"},
++      {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"},
++      {0, 0, NULL, NULL}
++};
++
++static struct pci_device_id w6692_ids[] __devinitdata = {
++      { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, PCI_ANY_ID, PCI_ANY_ID,
++        0, 0, (unsigned long) "Dynalink/AsusCom IS64PH" },
++      { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, PCI_ANY_ID, PCI_ANY_ID,
++        0, 0, (unsigned long) "Winbond W6692" },
++      { }
++};
++MODULE_DEVICE_TABLE(pci, w6692_ids);
++
++static struct pci_driver w6692_driver = {
++      name:     "w6692",
++      probe:    w6692_probe,
++      remove:   __devexit_p(w6692_remove_pci),
++      id_table: w6692_ids,
++};
++
++
++static char W6692Name[] = "W6692";
++
++static int __init w6692_init(void)
++{
++      int     err;
++
++      printk(KERN_INFO "Winbond W6692 PCI driver Rev. %s\n", mISDN_getrev(w6692_rev));
++
++#ifdef MODULE
++      w6692.owner = THIS_MODULE;
++#endif
++      INIT_LIST_HEAD(&w6692.ilist);
++      w6692.name = W6692Name;
++      w6692.own_ctrl = w6692_manager;
++      w6692.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0;
++      w6692.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS |
++                                  ISDN_PID_L1_B_64HDLC;
++      w6692.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS;
++      if ((err = mISDN_register(&w6692))) {
++              printk(KERN_ERR "Can't register Winbond W6692 PCI error(%d)\n", err);
++              return(err);
++      }
++      err = pci_register_driver(&w6692_driver);
++      if (err < 0)
++              goto out;
++
++      if (err == 0) {
++              err = -ENODEV;
++              pci_unregister_driver(&w6692_driver);
++              goto out;
++      }
++      return 0;
++
++ out:
++      mISDN_unregister(&w6692);
++      return err;
++}
++
++static void __exit w6692_cleanup(void)
++{
++      int             err;
++      w6692pci        *card, *next;
++
++      if ((err = mISDN_unregister(&w6692))) {
++              printk(KERN_ERR "Can't unregister Winbond W6692 PCI error(%d)\n", err);
++      }
++      list_for_each_entry_safe(card, next, &w6692.ilist, list) {
++              printk(KERN_ERR "Winbond W6692 PCI card struct not empty refs %d\n",
++                      w6692.refcnt);
++              release_card(card);
++      }
++      pci_unregister_driver(&w6692_driver);
++}
++
++module_init(w6692_init);
++module_exit(w6692_cleanup);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/w6692.h      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/w6692.h  2004-11-22 09:33:38.463696544 +0000
+@@ -0,0 +1,179 @@
++/* $Id$
++ *
++ * Winbond W6692 specific defines
++ *
++ * Author      Karsten Keil <kkeil@suse.de>
++ *             based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
++ * 
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++/* Specifications of W6692 registers */
++
++#define W_D_RFIFO     0x00    /* R */
++#define W_D_XFIFO     0x04    /* W */
++#define W_D_CMDR      0x08    /* W */
++#define W_D_MODE      0x0c    /* R/W */
++#define W_D_TIMR      0x10    /* R/W */
++#define W_ISTA                0x14    /* R_clr */
++#define W_IMASK               0x18    /* R/W */
++#define W_D_EXIR      0x1c    /* R_clr */
++#define W_D_EXIM      0x20    /* R/W */
++#define W_D_STAR      0x24    /* R */
++#define W_D_RSTA      0x28    /* R */
++#define W_D_SAM               0x2c    /* R/W */
++#define W_D_SAP1      0x30    /* R/W */
++#define W_D_SAP2      0x34    /* R/W */
++#define W_D_TAM               0x38    /* R/W */
++#define W_D_TEI1      0x3c    /* R/W */
++#define W_D_TEI2      0x40    /* R/W */
++#define W_D_RBCH      0x44    /* R */
++#define W_D_RBCL      0x48    /* R */
++#define W_TIMR2               0x4c    /* W */
++#define W_L1_RC               0x50    /* R/W */
++#define W_D_CTL               0x54    /* R/W */
++#define W_CIR         0x58    /* R */
++#define W_CIX         0x5c    /* W */
++#define W_SQR         0x60    /* R */
++#define W_SQX         0x64    /* W */
++#define W_PCTL                0x68    /* R/W */
++#define W_MOR         0x6c    /* R */
++#define W_MOX         0x70    /* R/W */
++#define W_MOSR                0x74    /* R_clr */
++#define W_MOCR                0x78    /* R/W */
++#define W_GCR         0x7c    /* R/W */
++
++#define       W_B_RFIFO       0x80    /* R */
++#define       W_B_XFIFO       0x84    /* W */
++#define       W_B_CMDR        0x88    /* W */
++#define       W_B_MODE        0x8c    /* R/W */
++#define       W_B_EXIR        0x90    /* R_clr */
++#define       W_B_EXIM        0x94    /* R/W */
++#define       W_B_STAR        0x98    /* R */
++#define       W_B_ADM1        0x9c    /* R/W */
++#define       W_B_ADM2        0xa0    /* R/W */
++#define       W_B_ADR1        0xa4    /* R/W */
++#define       W_B_ADR2        0xa8    /* R/W */
++#define       W_B_RBCL        0xac    /* R */
++#define       W_B_RBCH        0xb0    /* R */
++
++#define W_XADDR               0xf4    /* R/W */
++#define W_XDATA               0xf8    /* R/W */
++#define W_EPCTL               0xfc    /* W */
++
++/* W6692 register bits */
++
++#define       W_D_CMDR_XRST   0x01
++#define       W_D_CMDR_XME    0x02
++#define       W_D_CMDR_XMS    0x08
++#define       W_D_CMDR_STT    0x10
++#define       W_D_CMDR_RRST   0x40
++#define       W_D_CMDR_RACK   0x80
++
++#define       W_D_MODE_RLP    0x01
++#define       W_D_MODE_DLP    0x02
++#define       W_D_MODE_MFD    0x04
++#define       W_D_MODE_TEE    0x08
++#define       W_D_MODE_TMS    0x10
++#define       W_D_MODE_RACT   0x40
++#define       W_D_MODE_MMS    0x80
++
++#define W_INT_B2_EXI  0x01
++#define W_INT_B1_EXI  0x02
++#define W_INT_D_EXI   0x04
++#define W_INT_XINT0   0x08
++#define W_INT_XINT1   0x10
++#define W_INT_D_XFR   0x20
++#define W_INT_D_RME   0x40
++#define W_INT_D_RMR   0x80
++
++#define W_D_EXI_WEXP  0x01
++#define W_D_EXI_TEXP  0x02
++#define W_D_EXI_ISC   0x04
++#define W_D_EXI_MOC   0x08
++#define W_D_EXI_TIN2  0x10
++#define W_D_EXI_XCOL  0x20
++#define W_D_EXI_XDUN  0x40
++#define W_D_EXI_RDOV  0x80
++
++#define       W_D_STAR_DRDY   0x10
++#define       W_D_STAR_XBZ    0x20
++#define       W_D_STAR_XDOW   0x80
++
++#define W_D_RSTA_RMB  0x10
++#define W_D_RSTA_CRCE 0x20
++#define W_D_RSTA_RDOV 0x40
++
++#define W_D_CTL_SRST  0x20
++
++#define W_CIR_SCC     0x80
++#define W_CIR_ICC     0x40
++#define W_CIR_COD_MASK        0x0f
++
++#define W_PCTL_PCX    0x01
++#define W_PCTL_XMODE  0x02
++#define W_PCTL_OE0    0x04
++#define W_PCTL_OE1    0x08
++#define W_PCTL_OE2    0x10
++#define W_PCTL_OE3    0x20
++#define W_PCTL_OE4    0x40
++#define W_PCTL_OE5    0x80
++
++#define       W_B_CMDR_XRST   0x01
++#define       W_B_CMDR_XME    0x02
++#define       W_B_CMDR_XMS    0x04
++#define       W_B_CMDR_RACT   0x20
++#define       W_B_CMDR_RRST   0x40
++#define       W_B_CMDR_RACK   0x80
++
++#define       W_B_MODE_FTS0   0x01
++#define       W_B_MODE_FTS1   0x02
++#define       W_B_MODE_SW56   0x04
++#define       W_B_MODE_BSW0   0x08
++#define       W_B_MODE_BSW1   0x10
++#define       W_B_MODE_EPCM   0x20
++#define       W_B_MODE_ITF    0x40
++#define       W_B_MODE_MMS    0x80
++
++#define       W_B_EXI_XDUN    0x01
++#define       W_B_EXI_XFR     0x02
++#define       W_B_EXI_RDOV    0x10
++#define       W_B_EXI_RME     0x20
++#define       W_B_EXI_RMR     0x40
++
++#define       W_B_STAR_XBZ    0x01
++#define       W_B_STAR_XDOW   0x04
++#define       W_B_STAR_RMB    0x10
++#define       W_B_STAR_CRCE   0x20
++#define       W_B_STAR_RDOV   0x40
++
++#define       W_B_RBCH_LOV    0x20
++
++/* W6692 Layer1 commands */
++
++#define       W_L1CMD_ECK     0x00
++#define W_L1CMD_RST   0x01
++#define W_L1CMD_SCP   0x04
++#define W_L1CMD_SSP   0x02
++#define W_L1CMD_AR8   0x08
++#define W_L1CMD_AR10  0x09
++#define W_L1CMD_EAL   0x0a
++#define W_L1CMD_DRC   0x0f
++
++/* W6692 Layer1 indications */
++
++#define W_L1IND_CE    0x07
++#define W_L1IND_DRD   0x00
++#define W_L1IND_LD    0x04
++#define W_L1IND_ARD   0x08
++#define W_L1IND_TI    0x0a
++#define W_L1IND_ATI   0x0b
++#define W_L1IND_AI8   0x0c
++#define W_L1IND_AI10  0x0d
++#define W_L1IND_CD    0x0f
++
++/* FIFO thresholds */
++#define W_D_FIFO_THRESH       64
++#define W_B_FIFO_THRESH       64
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_dte.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_dte.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_dte.c    1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_dte.c        2004-11-22 09:33:38.473695024 +0000
+@@ -0,0 +1,1370 @@
++/* $Id$
++ *
++ * Linux modular ISDN subsystem, mISDN
++ * X.25/X.31 Layer3 for DTE mode   
++ *
++ * Author     Karsten Keil (kkeil@suse.de)
++ *
++ * Copyright 2003 by Karsten Keil (kkeil@suse.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include "x25_l3.h"
++#include "helper.h"
++#include "debug.h"
++
++static int debug = 0;
++
++static mISDNobject_t x25dte_obj;
++
++static char *mISDN_dte_revision = "$Revision$";
++
++/* local prototypes */
++static x25_channel_t *        dte_create_channel(x25_l3_t *, int, u_char, __u16, int, u_char *);
++
++
++/* X.25 Restart state machine */
++static struct Fsm dte_rfsm = {NULL, 0, 0, NULL, NULL};
++
++/* X.25 connection state machine */
++static struct Fsm dte_pfsm = {NULL, 0, 0, NULL, NULL};
++
++/* X.25 Flowcontrol state machine */
++static struct Fsm dte_dfsm = {NULL, 0, 0, NULL, NULL};
++
++
++/* X.25 Restart state machine implementation */
++
++static void
++r_llready(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_R1);
++      if (test_and_clear_bit(X25_STATE_ESTABLISH, &l3->state)) {
++              mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL);
++      }
++}
++
++static void
++r_r0_restart_l3(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t        *l3 = fi->userdata;
++
++      if (arg)
++              memcpy(l3->cause, arg, 2);
++      else
++              memset(l3->cause, 0, 2);
++      test_and_set_bit(X25_STATE_ESTABLISH, &l3->state);
++      mISDN_FsmEvent(&l3->l2l3m, EV_L3_ESTABLISH_REQ, NULL);
++}
++
++static void
++r_restart_l3(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t        *l3 = fi->userdata;
++
++      if (arg)
++              memcpy(l3->cause, arg, 2);
++      mISDN_FsmChangeState(fi, ST_R2);
++      l3->TRval = T20_VALUE;
++      l3->TRrep = R20_VALUE;
++      X25sendL3frame(NULL, l3, X25_PTYPE_RESTART, 2, l3->cause);
++      mISDN_FsmAddTimer(&l3->TR, l3->TRval, EV_L3_RESTART_TOUT, NULL, 0);
++}
++
++static void
++r_restart_ind(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t        *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_R3);
++      X25sendL3frame(NULL, l3, X25_PTYPE_RESTART_CNF, 0, NULL);
++      mISDN_FsmChangeState(fi, ST_R1);
++      mISDN_FsmDelTimer(&l3->TR, 0);
++      X25_restart(l3);
++}
++
++static void
++r_restart_cnf(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t        *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_R1);
++      mISDN_FsmDelTimer(&l3->TR, 0);
++      X25_restart(l3);
++}
++
++static void
++r_restart_cnf_err(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t        *l3 = fi->userdata;
++      u_char          cause[2] = {0, 17};
++
++      if (fi->state == ST_R3)
++              cause[1] = 19;
++      mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, cause);
++}
++
++static void
++r_timeout(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t        *l3 = fi->userdata;
++
++      if (l3->TRrep) {
++              X25sendL3frame(NULL, l3, X25_PTYPE_RESTART, 2, l3->cause);
++              mISDN_FsmRestartTimer(&l3->TR, l3->TRval, EV_L3_RESTART_TOUT, NULL, 0);
++              l3->TRrep--;
++      } else {
++              mISDN_FsmDelTimer(&l3->TR, 0);
++              mISDN_FsmChangeState(fi, ST_R1);
++              /* signal failure */
++      }
++}
++
++/* *INDENT-OFF* */
++static struct FsmNode RFnList[] =
++{
++      {ST_R0, EV_LL_READY,            r_llready},
++      {ST_R0, EV_L3_RESTART_REQ,      r_r0_restart_l3},
++      {ST_R1, EV_LL_READY,            r_llready},
++      {ST_R1, EV_L3_RESTART_REQ,      r_restart_l3},
++      {ST_R1, EV_L2_RESTART,          r_restart_ind},
++      {ST_R1, EV_L2_RESTART_CNF,      r_restart_cnf_err},
++      {ST_R2, EV_L3_RESTART_REQ,      r_restart_l3},
++      {ST_R2, EV_L2_RESTART,          r_restart_ind},
++      {ST_R2, EV_L2_RESTART_CNF,      r_restart_cnf},
++      {ST_R2, EV_L3_RESTART_TOUT,     r_timeout},
++      {ST_R2, EV_LL_READY,            r_llready},
++      {ST_R3, EV_L3_RESTART_REQ,      r_restart_l3},
++      {ST_R3, EV_L2_RESTART_CNF,      r_restart_cnf_err},
++      {ST_R3, EV_LL_READY,            r_llready},
++};
++/* *INDENT-ON* */
++
++#define R_FN_COUNT (sizeof(RFnList)/sizeof(struct FsmNode))
++
++/* X.25 connection state machine */
++
++static void
++p_p0_ready(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      if (test_bit(X25_STATE_PERMANENT, &l3c->state)) {
++              mISDN_FsmChangeState(fi, ST_P4);
++              /* connect */
++              mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL);
++      } else {
++              mISDN_FsmChangeState(fi, ST_P1);
++              if (test_bit(X25_STATE_ORGINATE, &l3c->state))
++                      mISDN_FsmEvent(fi, EV_L3_OUTGOING_CALL, NULL);
++      }
++}
++
++static void
++X25_clear_connection(x25_channel_t *l3c, struct sk_buff *skb, int reason)
++{
++      if (skb) {
++              if (test_bit(X25_STATE_DBIT, &l3c->state))
++                      reason |= 0x10000;
++              X25sendL4frame(l3c, l3c->l3, CAPI_DISCONNECT_B3_IND, reason, skb->len, skb->data);
++      } else
++              X25sendL4frame(l3c, l3c->l3, CAPI_DISCONNECT_B3_IND, reason, 0, NULL);
++}
++
++static void
++p_p0_outgoing(struct FsmInst *fi, int event, void *arg)
++{
++}
++
++static void
++p_ready(struct FsmInst *fi, int event, void *arg)
++{
++//    x25_channel_t   *l3c = fi->userdata;
++}
++
++static void
++p_outgoing(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      mISDN_FsmChangeState(fi, ST_P2);
++      if (skb)
++              X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL, skb->len, skb->data);
++      else
++              X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL, l3c->ncpi_len, l3c->ncpi_data);
++      mISDN_FsmAddTimer(&l3c->TP, T21_VALUE, EV_L3_CALL_TOUT, NULL, 0);
++}
++
++static void
++p_incoming(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++      int             flg = 0;
++
++      if (test_bit(X25_STATE_DBIT, &l3c->state))
++              flg = 0x10000;
++      mISDN_FsmChangeState(fi, ST_P3);
++      X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_IND, flg, l3c->ncpi_len, l3c->ncpi_data);
++}
++
++static void
++p_call_accept(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      if (skb)
++              X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL_CNF, skb->len, skb->data);
++      else
++              X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CALL_CNF, 0, NULL);
++      mISDN_FsmChangeState(fi, ST_P4);
++      mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL);
++      X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, 0, 0, NULL);
++}
++
++static void
++p_collision(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_P5);
++}
++
++static void
++p_connect(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++      struct sk_buff  *skb = arg;
++      int             flg = 0;
++
++      mISDN_FsmDelTimer(&l3c->TP, 0);
++      mISDN_FsmChangeState(fi, ST_P4);
++      if (test_bit(X25_STATE_DBIT, &l3c->state))
++              flg = 0x10000;
++      mISDN_FsmEvent(&l3c->x25d, EV_L3_CONNECT, NULL);
++      if (skb)
++              X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, flg, skb->len, skb->data);
++      else
++              X25sendL4frame(l3c, l3c->l3, CAPI_CONNECT_B3_ACTIVE_IND, flg, 0, NULL);
++}
++
++static void
++p_call_timeout(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_P6);
++      l3c->cause[0] = 0;
++      l3c->cause[1] = 49;
++      l3c->TPval = T23_VALUE;
++      l3c->TPrep = R23_VALUE;
++      mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0);
++      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause);
++}
++
++static void
++p_clear_ind(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_P7);
++      mISDN_FsmDelTimer(&l3c->TP, 0);
++      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR_CNF, 0, NULL);
++      mISDN_FsmChangeState(fi, ST_P1);
++      X25_clear_connection(l3c, arg, 0);
++}
++
++static void
++p_clear_cnf(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_P1);
++      mISDN_FsmDelTimer(&l3c->TP, 0);
++      X25_clear_connection(l3c, arg, 0);
++}
++
++static void
++p_clear_timeout(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      if (l3c->TPrep) {
++              mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0);
++              X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause);
++      } else {
++              l3c->cause[0] = 0;
++              l3c->cause[0] = 50;
++              mISDN_FsmChangeState(fi, ST_P1);
++              X25_clear_connection(l3c, NULL, 0x3303);
++      }
++}
++
++static void
++p_clearing_req(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      mISDN_FsmChangeState(fi, ST_P6);
++      l3c->TPval = T23_VALUE;
++      l3c->TPrep = R23_VALUE;
++      mISDN_FsmAddTimer(&l3c->TP, l3c->TPval, EV_L3_CLEAR_TOUT, NULL, 0);
++      if (skb) {
++              if (skb->len >= 2) {
++                      memcpy(l3c->cause, skb->data, 2);
++                      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, skb->len, skb->data);
++                      return;
++              }
++              l3c->cause[0] = 0;
++              l3c->cause[1] = 0;
++      }
++      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause);
++}
++
++static void
++p_invalid_pkt(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      l3c->cause[0] = 0;
++      switch(fi->state) {
++              case ST_P1:
++                      l3c->cause[1] = 20;
++                      break;
++              case ST_P2:
++                      l3c->cause[1] = 21;
++                      break;
++              case ST_P3:
++                      l3c->cause[1] = 22;
++                      break;
++              case ST_P4:
++                      l3c->cause[1] = 23;
++                      break;
++              case ST_P5:
++                      l3c->cause[1] = 24;
++                      break;
++              case ST_P6:
++                      l3c->cause[1] = 25;
++                      break;
++              case ST_P7:
++                      l3c->cause[1] = 26;
++                      break;
++              default:
++                      l3c->cause[1] = 16;
++                      break;
++      }
++      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_CLEAR, 2, l3c->cause);
++}
++
++/* *INDENT-OFF* */
++static struct FsmNode PFnList[] =
++{
++      {ST_P0, EV_L3_READY,            p_p0_ready},
++      {ST_P0, EV_L3_OUTGOING_CALL,    p_p0_outgoing},
++      {ST_P0, EV_L3_CLEARING,         p_clearing_req},
++      {ST_P1, EV_L3_READY,            p_ready},
++      {ST_P1, EV_L3_OUTGOING_CALL,    p_outgoing},            
++      {ST_P1, EV_L2_INCOMING_CALL,    p_incoming},
++      {ST_P1, EV_L2_CLEAR,            p_clear_ind},
++      {ST_P1, EV_L2_CALL_CNF,         p_invalid_pkt},
++      {ST_P1, EV_L2_CLEAR_CNF,        p_invalid_pkt},
++      {ST_P1, EV_L2_INVALPKT,         p_invalid_pkt},
++      {ST_P1, EV_L3_CLEARING,         p_clearing_req},
++      {ST_P2, EV_L2_INCOMING_CALL,    p_collision},
++      {ST_P2, EV_L2_CALL_CNF,         p_connect},
++      {ST_P2, EV_L2_CLEAR,            p_clear_ind},
++      {ST_P2, EV_L3_CALL_TOUT,        p_call_timeout},
++      {ST_P2, EV_L2_CLEAR_CNF,        p_invalid_pkt},
++      {ST_P2, EV_L2_INVALPKT,         p_invalid_pkt},
++      {ST_P2, EV_L3_CLEARING,         p_clearing_req},
++      {ST_P3, EV_L3_CALL_ACCEPT,      p_call_accept},
++      {ST_P3, EV_L2_CLEAR,            p_clear_ind},
++      {ST_P3, EV_L2_INCOMING_CALL,    p_invalid_pkt},
++      {ST_P3, EV_L2_CALL_CNF,         p_invalid_pkt},
++      {ST_P3, EV_L2_CLEAR_CNF,        p_invalid_pkt},
++      {ST_P3, EV_L2_INVALPKT,         p_invalid_pkt},
++      {ST_P3, EV_L3_CLEARING,         p_clearing_req},
++      {ST_P4, EV_L2_CLEAR,            p_clear_ind},
++      {ST_P4, EV_L2_INCOMING_CALL,    p_invalid_pkt},
++      {ST_P4, EV_L2_CALL_CNF,         p_invalid_pkt},
++      {ST_P4, EV_L2_CLEAR_CNF,        p_invalid_pkt},
++      {ST_P4, EV_L3_CLEARING,         p_clearing_req},
++      {ST_P5, EV_L2_CALL_CNF,         p_connect},
++      {ST_P5, EV_L3_CALL_ACCEPT,      p_call_accept},
++      {ST_P5, EV_L2_CLEAR,            p_clear_ind},
++      {ST_P5, EV_L3_CALL_TOUT,        p_call_timeout},
++      {ST_P5, EV_L2_INCOMING_CALL,    p_invalid_pkt},
++      {ST_P5, EV_L2_CLEAR_CNF,        p_invalid_pkt},
++      {ST_P5, EV_L2_INVALPKT,         p_invalid_pkt},
++      {ST_P5, EV_L3_CLEARING,         p_clearing_req},
++      {ST_P6, EV_L2_CLEAR_CNF,        p_clear_cnf},
++      {ST_P6, EV_L3_CLEAR_TOUT,       p_clear_timeout},
++      {ST_P6, EV_L3_CLEARING,         p_clearing_req},
++      {ST_P7, EV_L2_INCOMING_CALL,    p_invalid_pkt},
++      {ST_P7, EV_L2_CALL_CNF,         p_invalid_pkt},
++      {ST_P7, EV_L2_CLEAR_CNF,        p_invalid_pkt},
++      {ST_P7, EV_L2_INVALPKT,         p_invalid_pkt},
++};
++/* *INDENT-ON* */
++
++#define P_FN_COUNT (sizeof(PFnList)/sizeof(struct FsmNode))
++
++static void
++d_connect(struct FsmInst *fi, int event, void *arg)
++{
++      mISDN_FsmChangeState(fi, ST_D1);
++}
++
++static void
++d_reset_req(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++      struct sk_buff  *skb = arg;
++
++      mISDN_FsmChangeState(fi, ST_D2);
++      l3c->TDval = T22_VALUE;
++      l3c->TDrep = R22_VALUE;
++      mISDN_FsmAddTimer(&l3c->TD, l3c->TDval, EV_L3_RESET_TOUT, NULL, 0);
++      if (skb) {
++              if (skb->len >= 2) {
++                      memcpy(l3c->cause, skb->data, 2);
++                      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, skb->len, skb->data);
++                      return;
++              }
++              l3c->cause[0] = 0;
++              l3c->cause[1] = 0;
++      }
++      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause);
++}
++
++static void
++d_reset_ind(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_D3);
++      X25_reset_channel(l3c, arg);
++      mISDN_FsmChangeState(fi, ST_D1);
++      /* TODO normal operation trigger */
++}
++
++static void
++d_reset_cnf(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      mISDN_FsmDelTimer(&l3c->TD, 0);
++      X25_reset_channel(l3c, NULL);
++      /* TODO normal opration trigger */
++      mISDN_FsmChangeState(fi, ST_D1);
++}
++
++static void
++d_reset_cnf_err(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      if (arg)
++              memcpy(l3c->cause, arg, 2);
++      mISDN_FsmChangeState(fi, ST_D2);
++      l3c->TDval = T22_VALUE;
++      l3c->TDrep = R22_VALUE;
++      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause);
++      mISDN_FsmAddTimer(&l3c->TD, l3c->TDval, EV_L3_RESET_TOUT, NULL, 0);
++}
++
++static void
++d_reset_timeout(struct FsmInst *fi, int event, void *arg)
++{
++      x25_channel_t   *l3c = fi->userdata;
++
++      if (l3c->TDrep) {
++              X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RESET, 2, l3c->cause);
++              l3c->TDrep--;
++      } else {
++              l3c->cause[0] = 0;
++              l3c->cause[1] = 51;
++              mISDN_FsmChangeState(fi, ST_D0);
++              if (test_bit(X25_STATE_PERMANENT, &l3c->state))
++                      X25_clear_connection(l3c, NULL, 0x3303); 
++              else
++                      mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, NULL);
++      }
++}
++
++/* *INDENT-OFF* */
++static struct FsmNode DFnList[] =
++{
++      {ST_D0, EV_L3_CONNECT,          d_connect},
++      {ST_D1, EV_L2_RESET,            d_reset_ind},
++      {ST_D1, EV_L2_RESET_CNF,        d_reset_cnf_err},
++      {ST_D1, EV_L3_RESETING,         d_reset_req},
++      {ST_D2, EV_L2_RESET,            d_reset_cnf},
++      {ST_D2, EV_L2_RESET_CNF,        d_reset_cnf},
++      {ST_D3, EV_L2_RESET_CNF,        d_reset_cnf_err},
++      {ST_D3, EV_L3_RESETING,         d_reset_req},
++      {ST_D1, EV_L3_RESET_TOUT,       d_reset_timeout},
++};
++/* *INDENT-ON* */
++
++#define D_FN_COUNT (sizeof(DFnList)/sizeof(struct FsmNode))
++
++static int
++got_diagnositic(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel)
++{
++      /* not implemented yet */
++      return(X25_ERRCODE_DISCARD);
++}
++
++static int
++got_register(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel)
++{
++      /* not implemented yet */
++      return(X25_ERRCODE_DISCARD);
++}
++
++static int
++got_register_cnf(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel)
++{
++      /* not implemented yet */
++      return(X25_ERRCODE_DISCARD);
++}
++
++static int
++dte_data_ind_d(x25_channel_t *chan, struct sk_buff *skb, u_char gfi, u_char ptype)
++{
++      int     pr_m, ps, event;
++      u_char  ptype_s = ptype;
++
++      if ((ptype & 1) == 0)
++              ptype = X25_PTYPE_DATA;
++      else {
++              if ((!test_bit(X25_STATE_MOD128, &chan->state)) &&
++                      !test_bit(X25_STATE_MOD32768, &chan->state))
++                      ptype = ptype & 0x1f;
++              if ((ptype != X25_PTYPE_RR) &&
++                      (ptype != X25_PTYPE_RNR) &&
++                      (ptype != X25_PTYPE_REJ))
++                      ptype = ptype_s;
++      }
++      switch (ptype) {
++              case X25_PTYPE_RESET:
++                      event = EV_L2_RESET;
++                      break;
++              case X25_PTYPE_RESET_CNF:
++                      event = EV_L2_RESET_CNF;
++                      break;
++              case X25_PTYPE_NOTYPE:
++                      chan->cause[0] = 0;
++                      chan->cause[1] = 38;
++                      event = EV_L3_RESETING;
++                      break;
++              case X25_PTYPE_RESTART:
++              case X25_PTYPE_RESTART_CNF:
++                      chan->cause[0] = 0;
++                      chan->cause[1] = 41;
++                      event = EV_L3_RESETING;
++                      break;
++              case X25_PTYPE_INTERRUPT:
++              case X25_PTYPE_INTERRUPT_CNF:
++              case X25_PTYPE_DATA:
++              case X25_PTYPE_RR:
++              case X25_PTYPE_RNR:
++              case X25_PTYPE_REJ:
++                      if (chan->x25d.state == ST_D2)
++                              return(X25_ERRCODE_DISCARD);
++                      else if (chan->x25d.state == ST_D3) {
++                              chan->cause[0] = 0;
++                              chan->cause[1] = 29;
++                              event = EV_L3_RESETING;
++                      } else
++                              event = -1;
++                      break;
++              default:
++                      /* unknown paket type */
++                      chan->cause[0] = 0;
++                      chan->cause[1] = 33;
++                      event = EV_L3_RESETING;
++                      break;
++      }
++      if (event != -1) {
++              if (event == EV_L3_RESETING)
++                      mISDN_FsmEvent(&chan->x25d, event, NULL);
++              else
++                      mISDN_FsmEvent(&chan->x25d, event, skb);
++              return(X25_ERRCODE_DISCARD);
++      }
++      switch (ptype) {
++              case X25_PTYPE_INTERRUPT:
++                      if (test_and_set_bit(X25_STATE_DXE_INTSENT, &chan->state)) {
++                              chan->cause[0] = 0;
++                              chan->cause[1] = 44;
++                              mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL);
++                      } else {
++                              // X25_got_interrupt(chan, skb);
++                      }
++                      break;
++              case X25_PTYPE_INTERRUPT_CNF:
++                      if (!test_and_clear_bit(X25_STATE_DTE_INTSENT, &chan->state)) {
++                              chan->cause[0] = 0;
++                              chan->cause[1] = 43;
++                              mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL);
++                      }
++                      break;
++              case X25_PTYPE_RR:
++              case X25_PTYPE_RNR:
++              case X25_PTYPE_REJ:
++                      pr_m = X25_get_and_test_pr(chan, ptype_s, skb);
++                      if (pr_m < 0) {
++                              chan->cause[0] = 0;
++                              chan->cause[1] = -pr_m;
++                              mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL);
++                      } else if (skb->len) {
++                              chan->cause[0] = 0;
++                              chan->cause[1] = 39;
++                              mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL);
++                      } else {
++                              if (ptype == X25_PTYPE_RR) {
++                                      test_and_clear_bit(X25_STATE_DXE_RNR, &chan->state);
++                                      if (X25_cansend(chan))
++                                              X25_invoke_sending(chan);
++                              } else if (ptype == X25_PTYPE_RNR) {
++                                      if (!test_and_set_bit(X25_STATE_DXE_RNR, &chan->state)) {
++                                              /* action for DXE RNR */
++                                      }
++                              } else {
++                                      /* TODO REJ */
++                              }
++                      }
++                      break;
++              case X25_PTYPE_DATA:
++                      ps = X25_get_and_test_ps(chan, ptype_s, skb);
++                      if (ps == -38)
++                              pr_m = ps;
++                      else
++                              pr_m = X25_get_and_test_pr(chan, ptype_s, skb);
++                      if (pr_m < 0) {
++                              chan->cause[0] = 0;
++                              chan->cause[1] = -pr_m;
++                              mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL);
++                      } else if (ps < 0) {
++                              chan->cause[0] = 0;
++                              chan->cause[1] = -ps;
++                              mISDN_FsmEvent(&chan->x25d, EV_L3_RESETING, NULL);
++                      } else {
++                              int flag = (pr_m & 1) ? CAPI_FLAG_MOREDATA : 0;
++                              
++                              if (gfi & X25_GFI_QBIT)
++                                      flag |= CAPI_FLAG_QUALIFIER;
++                              if (gfi & X25_GFI_DBIT)
++                                      flag |= CAPI_FLAG_DELIVERCONF;
++                              return(X25_receive_data(chan, ps, flag, skb));
++                      }
++                      break;
++      }
++      return(X25_ERRCODE_DISCARD);
++}
++
++static int
++dte_data_ind_p(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel, u_char ptype)
++{
++      x25_channel_t   *chan = X25_get_channel(l3, channel);
++      int             event, ret = X25_ERRCODE_DISCARD;
++
++      if (ptype == X25_PTYPE_CALL) {
++              if (!chan)
++                      chan = dte_create_channel(l3, X25_CHANNEL_INCOMING, gfi, channel, skb->len, skb->data);
++              if (chan) {
++                      mISDN_FsmEvent(&chan->x25p, EV_L2_INCOMING_CALL, skb);
++              } else {
++                      ret = 36; /* unassigned channel */
++              }
++              return(ret);
++      }
++      if (!chan)
++              return(36); /* unassigned channel */
++      switch (ptype) {
++              case X25_PTYPE_CALL_CNF:
++                      event = EV_L2_CALL_CNF;
++                      break;
++              case X25_PTYPE_CLEAR:
++                      event = EV_L2_CLEAR;
++                      break;
++              case X25_PTYPE_CLEAR_CNF:
++                      event = EV_L2_CLEAR_CNF;
++                      break;
++              case X25_PTYPE_NOTYPE:
++                      chan->cause[0] = 0;
++                      chan->cause[1] = 38;
++                      event = EV_L3_CLEARING;
++                      break;
++              case X25_PTYPE_RESTART:
++              case X25_PTYPE_RESTART_CNF:
++                      chan->cause[0] = 0;
++                      chan->cause[1] = 41;
++                      event = EV_L3_CLEARING;
++                      break;
++              case X25_PTYPE_RESET:
++              case X25_PTYPE_RESET_CNF:
++              case X25_PTYPE_INTERRUPT:
++              case X25_PTYPE_INTERRUPT_CNF:
++                      event = EV_L2_INVALPKT;
++                      break;
++              default:
++                      if ((ptype & 1) == 0) {
++                              event = EV_L2_INVALPKT;
++                              break;
++                      }
++                      if (!test_bit(X25_STATE_MOD128, &chan->state) &&
++                              !test_bit(X25_STATE_MOD32768, &chan->state))
++                              event = ptype & 0x1f;
++                      else
++                              event = ptype;
++                      if ((event == X25_PTYPE_RR) ||
++                              (event == X25_PTYPE_RNR) ||
++                              (event == X25_PTYPE_REJ)) {
++                              event = EV_L2_INVALPKT;
++                              break;
++                      }
++                      /* unknown paket type */
++                      chan->cause[0] = 0;
++                      chan->cause[1] = 33;
++                      event = EV_L3_CLEARING;
++                      break;
++      }
++      if (chan->x25p.state == ST_P4) {
++              if ((event == EV_L2_INVALPKT) || (event == EV_L3_CLEARING))
++                      return(dte_data_ind_d(chan, skb, gfi, ptype));
++      }
++      if (event == EV_L3_CLEARING)
++              mISDN_FsmEvent(&chan->x25p, event, NULL);
++      else
++              mISDN_FsmEvent(&chan->x25p, event, skb);
++      return(ret);
++}
++
++static int
++dte_data_ind_r(x25_l3_t *l3, struct sk_buff *skb, u_char gfi, __u16 channel, u_char ptype)
++{
++      int     ret = X25_ERRCODE_DISCARD;
++
++      
++      if (ptype == X25_PTYPE_NOTYPE) {
++              if (channel) {
++                      if (l3->x25r.state == ST_R1)
++                              return(dte_data_ind_p(l3, skb, gfi, channel, ptype));
++                      if (l3->x25r.state == ST_R2) {
++                              l3->cause[0] = 0;
++                              l3->cause[1] = 38;
++                              mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL);
++                      }
++              } else {
++                      if (l3->x25r.state == ST_R1)
++                              ret = 38; // packet too short
++                      else if (l3->x25r.state == ST_R3) {
++                              l3->cause[0] = 0;
++                              l3->cause[1] = 38;
++                              mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL);
++                      }
++              }
++              return(ret);
++      }
++      if ((ptype == X25_PTYPE_RESTART) || (ptype == X25_PTYPE_RESTART_CNF)) {
++              if (channel) {
++                      if (l3->x25r.state == ST_R1)
++                              return(dte_data_ind_p(l3, skb, gfi, channel, ptype));
++                      else if (l3->x25r.state == ST_R3) {
++                              l3->cause[0] = 0;
++                              l3->cause[1] = 41;
++                              mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL);
++                      }
++                      return(ret);
++              }
++              if (ptype == X25_PTYPE_RESTART)
++                      mISDN_FsmEvent(&l3->x25r, EV_L2_RESTART, skb);
++              else
++                      mISDN_FsmEvent(&l3->x25r, EV_L2_RESTART_CNF, skb);
++      } else {
++              if (l3->x25r.state == ST_R1)
++                      return(dte_data_ind_p(l3, skb, gfi, channel, ptype));
++              if (l3->x25r.state == ST_R3) {
++                      l3->cause[0] = 0;
++                      l3->cause[1] = 19;
++                      mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, NULL);
++              }
++      }
++      return(ret);
++}
++
++static int
++dte_dl_data_ind(x25_l3_t *l3, struct sk_buff *skb)
++{
++      int     ret;
++      __u16   channel;
++      u_char  gfi, ptype = 0;
++
++      ret = X25_get_header(l3, skb, &gfi, &channel, &ptype);
++      if (ret && (ptype != X25_PTYPE_NOTYPE)) {
++              if (test_bit(X25_STATE_DTEDTE, &l3->state))
++                      X25_send_diagnostic(l3, skb, ret, channel);
++              dev_kfree_skb(skb);
++              return(0);
++      }
++      switch(ptype) {
++              case X25_PTYPE_DIAGNOSTIC:
++                      ret = got_diagnositic(l3, skb, gfi, channel);
++                      break;
++              case X25_PTYPE_REGISTER:
++                      ret = got_register(l3, skb, gfi, channel);
++                      break;
++              case X25_PTYPE_REGISTER_CNF:
++                      ret = got_register_cnf(l3, skb, gfi, channel);
++                      break;
++              default:
++                      ret = dte_data_ind_r(l3, skb, gfi, channel, ptype);
++                      break;
++      }
++      if (ret == X25_ERRCODE_DISCARD) {
++              dev_kfree_skb(skb);
++              ret = 0;
++      } else if (ret) {
++              if (test_bit(X25_STATE_DTEDTE, &l3->state)) {
++                      if (ptype != X25_PTYPE_NOTYPE) {
++                              if (test_bit(X25_STATE_MOD32768, &l3->state))
++                                      skb_push(skb, 4);
++                              else
++                                      skb_push(skb, 3);
++                      }
++                      X25_send_diagnostic(l3, skb, ret, channel);
++              }
++              dev_kfree_skb(skb);
++              ret = 0;
++      }
++      return(ret);
++}
++
++static int
++dte_from_down(mISDNif_t *hif,  struct sk_buff *skb)
++{
++      x25_l3_t        *l3;
++      mISDN_head_t    *hh;
++      int             ret = -EINVAL;
++
++      if (!hif || !hif->fdata || !skb)
++              return(ret);
++      l3 = hif->fdata;
++      if (!l3->inst.up.func) {
++              return(-ENXIO);
++      }
++      hh = mISDN_HEAD_P(skb);
++      if (l3->debug)
++              printk(KERN_DEBUG "%s: prim(%x) dinfo(%x)\n", __FUNCTION__, hh->prim, hh->dinfo);
++      switch(hh->prim) {
++              case DL_DATA_IND:
++                      ret = dte_dl_data_ind(l3, skb);
++                      break;
++              case DL_DATA | CONFIRM:
++                      break;
++              case DL_ESTABLISH_CNF:
++                      ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_ESTABLISH_CNF, NULL);
++                      if (ret) {
++                              int_error();
++                      }
++                      ret = 0;
++                      dev_kfree_skb(skb);
++                      break;
++              case DL_ESTABLISH_IND:
++                      ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_ESTABLISH_IND, NULL);
++                      if (ret) {
++                              int_error();
++                      }
++                      ret = 0;
++                      dev_kfree_skb(skb);
++                      break;
++              case DL_RELEASE_CNF:
++                      ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_RELEASE_CNF, NULL);
++                      if (ret) {
++                              int_error();
++                      }
++                      ret = 0;
++                      dev_kfree_skb(skb);
++                      break;
++              case DL_RELEASE_IND:
++                      ret = mISDN_FsmEvent(&l3->l2l3m, EV_LL_RELEASE_IND, NULL);
++                      if (ret) {
++                              int_error();
++                      }
++                      ret = 0;
++                      dev_kfree_skb(skb);
++                      break;
++              default:
++                      int_error();
++                      break;
++      }
++      return(ret);
++}
++
++static x25_channel_t *
++dte_create_channel(x25_l3_t *l3, int typ, u_char flag, __u16 ch, int len, u_char *data)
++{
++      x25_channel_t   *l3c;
++      __u16           nch = ch;
++      int             ret;
++      
++      if (typ == X25_CHANNEL_OUTGOING) {
++              if (ch == 0) {
++                      /* first search for allready created channels in P1 state */
++                      if (l3->B3cfg.HOC) {
++                              for (nch = l3->B3cfg.HOC; (nch && (nch >= l3->B3cfg.LOC)); nch--) {
++                                      l3c = X25_get_channel(l3, nch);
++                                      if (l3c && (l3c->x25p.state == ST_P1)) {
++                                              X25_realloc_ncpi_data(l3c, len, data);
++                                              return(l3c);
++                                      }
++                              }
++                      }
++                      if (l3->B3cfg.HTC) {
++                              for (nch = l3->B3cfg.HTC; (nch && (nch >= l3->B3cfg.LTC)); nch--) {
++                                      l3c = X25_get_channel(l3, nch);
++                                      if (l3c && (l3c->x25p.state == ST_P1)) {
++                                              X25_realloc_ncpi_data(l3c, len, data);
++                                              return(l3c);
++                                      }
++                              }
++                      }
++                      /* now search for still unused channels */
++                      nch = 0;
++                      if (l3->B3cfg.HOC) {
++                              l3c = (x25_channel_t *)1; /* if loop is not executed */
++                              for (nch = l3->B3cfg.HOC; (nch && (nch >= l3->B3cfg.LOC)); nch--) {
++                                      l3c = X25_get_channel(l3, nch);
++                                      if (!l3c)
++                                              break;
++                              }
++                              if (l3c)
++                                      nch = 0;
++                      }
++                      if ((nch == 0) && l3->B3cfg.HTC) {
++                              l3c = (x25_channel_t *)1; /* if loop is not executed */
++                              for (nch = l3->B3cfg.HTC; (nch && (nch >= l3->B3cfg.LTC)); nch--) {
++                                      l3c = X25_get_channel(l3, nch);
++                                      if (!l3c)
++                                              break;
++                              }
++                              if (l3c)
++                                      nch = 0;
++                      }
++              } else {
++                      if (ch >= l3->B3cfg.LIC) /* not a permanent channel */
++                              return(NULL);
++                      l3c = X25_get_channel(l3, nch);
++                      if (l3c) {
++                              if (test_bit(X25_STATE_PERMANENT, &l3c->state)) {
++                                      if (l3c->ncci) /* allready in use */
++                                              return(NULL);
++                                      else {
++                                              X25_realloc_ncpi_data(l3c, len, data);
++                                              return(l3c);
++                                      }
++                              }
++                      }
++                      nch = ch;
++              }
++              if (flag & 1)
++                      flag = X25_GFI_DBIT;
++      } else {
++              if (!ch) /* not Reference Number procedure implemented */
++                      return(NULL);
++              if (l3->B3cfg.HTC) {
++                      if (ch > l3->B3cfg.HTC)
++                              return(NULL);
++              } else if (l3->B3cfg.HIC) {
++                      if (ch > l3->B3cfg.HIC)
++                              return(NULL);
++              }
++              nch = ch;
++              if (l3->B3cfg.LIC && ch < l3->B3cfg.LIC) /* permanent channel */
++                      nch = ch;
++              else {
++                      nch = ch;
++                      ch = 0;
++              }
++      }
++      if (!nch)
++              return(NULL);
++      ret = new_x25_channel(l3, &l3c, nch, len, data);
++      if (ret)
++              return(NULL);
++      l3c->x25p.fsm = &dte_pfsm;
++      l3c->x25d.fsm = &dte_dfsm;
++      if (ch) {
++              test_and_set_bit(X25_STATE_PERMANENT, &l3c->state);
++              if (l3c->l3->x25r.state == ST_R1) {
++                      l3c->x25p.state = ST_P4;
++                      l3c->x25d.state = ST_D1;
++              } else {
++                      l3c->x25p.state = ST_P0;
++                      l3c->x25d.state = ST_D0;
++              }
++      } else {
++              test_and_clear_bit(X25_STATE_PERMANENT, &l3c->state);
++              if (l3c->l3->x25r.state == ST_R1) {
++                      l3c->x25p.state = ST_P1;
++                      l3c->x25d.state = ST_D0;
++              } else {
++                      l3c->x25p.state = ST_P0;
++                      l3c->x25d.state = ST_D0;
++              }
++      }
++      if (flag & X25_GFI_DBIT)
++              test_and_set_bit(X25_STATE_DBIT, &l3c->state);
++      else
++              test_and_clear_bit(X25_STATE_DBIT, &l3c->state);
++      if (flag & X25_GFI_ABIT)
++              test_and_set_bit(X25_STATE_ABIT, &l3c->state);
++      else
++              test_and_clear_bit(X25_STATE_DBIT, &l3c->state);
++      return(l3c);
++}
++
++static int
++new_l3(mISDNstack_t *st, mISDN_pid_t *pid) {
++      x25_l3_t        *n_l3;
++      int             err;
++
++      err = new_x25_l3(&n_l3, st, pid, &x25dte_obj, debug);
++      if (err)
++              return(err);
++
++      n_l3->x25r.fsm = &dte_rfsm;
++      n_l3->x25r.state = ST_R0;
++      return(0);
++}
++
++static int
++dte_from_up(mISDNif_t *hif, struct sk_buff *skb)
++{
++      x25_l3_t        *l3;
++      x25_channel_t   *l3c;
++      mISDN_head_t    *hh;
++      __u32           addr;
++      __u16           info = 0;
++      int             err = 0;
++
++      if (!hif || !hif->fdata || !skb)
++              return(-EINVAL);
++      l3 = hif->fdata;
++      if (!l3->inst.down.func) {
++              return(-ENXIO);
++      }
++      hh = mISDN_HEAD_P(skb);
++      if (l3->debug)
++              printk(KERN_DEBUG "%s: prim(%x) dinfo(%x) len(%d)\n", __FUNCTION__, hh->prim, hh->dinfo, skb->len);
++      if (skb->len < 4) {
++              printk(KERN_WARNING "%s: skb too short (%d)\n", __FUNCTION__, skb->len);
++              return(-EINVAL);
++      } else {
++              addr = CAPIMSG_U32(skb->data, 0);
++              skb_pull(skb, 4);
++      }
++      if (l3->debug)
++              printk(KERN_DEBUG "%s: addr(%x)\n", __FUNCTION__, addr);
++      l3c = X25_get_channel4NCCI(l3, addr);
++      switch(hh->prim) {
++              case CAPI_DATA_B3_REQ:
++                      info = x25_data_b3_req(l3c, hh->dinfo, skb);
++                      if (info) {
++                              if (info == CAPI_SENDQUEUEFULL) {
++                                      err = -EXFULL;
++                                      break;
++                              }
++                              skb_trim(skb, 2);
++                              memcpy(skb->data, &info, 2);
++                              err = X25sendL4skb(l3c, l3, addr, CAPI_RESET_B3_CONF, hh->dinfo, skb);
++                      } else
++                              err = 0;
++                      break;
++              case CAPI_DATA_B3_RESP:
++                      return(x25_data_b3_resp(l3c, hh->dinfo, skb));
++              case CAPI_CONNECT_B3_REQ:
++                      if (!l3c) {
++                              x25_ncpi_t      *ncpi;
++                              if (skb->len <= 4) { // default NCPI
++                                      u_char  a = 0;
++                                      l3c = dte_create_channel(l3, X25_CHANNEL_OUTGOING, 0, 0, 1, &a);
++                              } else {
++                                      ncpi = (x25_ncpi_t *)skb->data;
++                                      l3c = dte_create_channel(l3, X25_CHANNEL_OUTGOING, ncpi->Flags,
++                                              (ncpi->Group<<8) | ncpi->Channel,
++                                              ncpi->len - 3, &ncpi->Contens[0]);  
++                              }
++                              if (l3c)
++                                      l3c->ncci = addr | (l3c->lchan << 16);
++                      }
++                      if (l3c) {
++                              err = 0;
++                              if (l3->x25r.state == ST_R0)
++                                      mISDN_FsmEvent(&l3->x25r, EV_L3_RESTART_REQ, "\000\000");
++                              else
++                                      err = mISDN_FsmEvent(&l3c->x25p, EV_L3_OUTGOING_CALL, NULL);
++                              if (err)
++                                      info = 0x2001; /* wrong state */
++                              else
++                                      info = 0;
++                      } else
++                              info = 0x2004; /* no NCCI available */
++                      skb_trim(skb, 2);
++                      memcpy(skb->data, &info, 2);
++                      err = X25sendL4skb(l3c, l3, addr, CAPI_CONNECT_B3_CONF, hh->dinfo, skb);
++                      break;
++              case CAPI_RESET_B3_REQ:
++                      if (l3c) {
++                              if (!(l3c->ncci & 0xffff))
++                                      l3c->ncci = addr;
++                              if (skb->len <= 4) { // default NCPI
++                                      l3c->cause[0] = 0;
++                                      l3c->cause[1] = 0;
++                                      err = mISDN_FsmEvent(&l3c->x25d, EV_L3_RESETING, NULL);
++                              } else {
++                                      skb_pull(skb, 4);
++                                      err = mISDN_FsmEvent(&l3c->x25d, EV_L3_RESETING, skb);
++                                      skb_push(skb, 4);
++                              }
++                              if (err)
++                                      info = 0x2001;
++                              else
++                                      info = 0;
++                      } else
++                              info = 0x2002;
++                      skb_trim(skb, 2);
++                      memcpy(skb->data, &info, 2);
++                      err = X25sendL4skb(l3c, l3, addr, CAPI_RESET_B3_CONF, hh->dinfo, skb);
++                      break;
++              case CAPI_DISCONNECT_B3_REQ:
++                      if (l3c) {
++                              if (!(l3c->ncci & 0xffff))
++                                      l3c->ncci = addr;
++                              if (skb->len <= 4) { // default NCPI
++                                      l3c->cause[0] = 0;
++                                      l3c->cause[1] = 0;
++                                      err = mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, NULL);
++                              } else {
++                                      skb_pull(skb, 4);
++                                      err = mISDN_FsmEvent(&l3c->x25p, EV_L3_CLEARING, skb);
++                                      skb_push(skb, 4);
++                              }
++                              if (err)
++                                      info = 0x2001;
++                              else
++                                      info = 0;
++                      } else
++                              info = 0x2002;
++                      skb_trim(skb, 2);
++                      memcpy(skb->data, &info, 2);
++                      err = X25sendL4skb(l3c, l3, addr, CAPI_DISCONNECT_B3_CONF, hh->dinfo, skb);
++                      break;
++              case CAPI_CONNECT_B3_RESP:
++                      if (l3c) {
++                              int event = EV_L3_CLEARING;
++
++                              l3c->ncci = addr;
++                              if (skb->len <= 2) {
++                                      printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP skb too short (%d)\n",
++                                              __FUNCTION__, skb->len);
++                                      skb_push(skb, 4);
++                                      return(-EINVAL);
++                              }
++                              info = CAPIMSG_U16(skb->data, 0);
++                              skb_pull(skb, 2);
++                              if (info == 0)
++                                      event = EV_L3_CALL_ACCEPT;
++                              if (skb->len <= 4) { // default NCPI
++                                      l3c->cause[0] = 0;
++                                      l3c->cause[1] = 0;
++                                      err = mISDN_FsmEvent(&l3c->x25p, event, NULL);
++                              } else {
++                                      skb_pull(skb, 4);
++                                      err = mISDN_FsmEvent(&l3c->x25p, event, skb);
++                              }
++                      } else {
++                              skb_push(skb, 4);
++                              printk(KERN_WARNING "%s: CAPI_CONNECT_B3_RESP no channel found\n",
++                                      __FUNCTION__);
++                              return(-ENODEV);
++                      }
++                      dev_kfree_skb(skb);
++                      err = 0;
++                      break;
++              case CAPI_CONNECT_B3_ACTIVE_RESP:
++              case CAPI_RESET_B3_RESP:
++                      if (!l3c) {
++                              skb_push(skb, 4);
++                              printk(KERN_WARNING "%s: prim %x dinfo %x no channel found\n",
++                                      __FUNCTION__, hh->prim, hh->dinfo);
++                              return(-ENODEV);
++                      }
++                      dev_kfree_skb(skb);
++                      err = 0;
++                      break;
++              case CAPI_DISCONNECT_B3_RESP:
++                      if (l3c) {
++                              l3c->ncci = 0;
++                              // TODO release NCCI
++                      } else {
++                              skb_push(skb, 4);
++                              printk(KERN_WARNING "%s: CAPI_DISCONNECT_B3_RESP no channel found\n",
++                                      __FUNCTION__);
++                              return(-ENODEV);
++                      }
++                      dev_kfree_skb(skb);
++                      err = 0;
++                      break;
++              default:
++                      printk(KERN_WARNING "%s: unknown prim %x dinfo %x\n",
++                              __FUNCTION__, hh->prim, hh->dinfo);
++                      err = -EINVAL;
++      }
++      return(err);
++}
++
++
++static char MName[] = "X25_DTE";
++
++#ifdef MODULE
++MODULE_AUTHOR("Karsten Keil");
++MODULE_PARM(debug, "1i");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++#endif
++
++static int
++dte_manager(void *data, u_int prim, void *arg) {
++      mISDNinstance_t *inst = data;
++      x25_l3_t        *l3_l;
++      int             err = -EINVAL;
++
++      if (debug & DEBUG_L3X25_MGR)
++              printk(KERN_DEBUG "l3x25_manager data:%p prim:%x arg:%p\n", data, prim, arg);
++      if (!data)
++              return(err);
++      list_for_each_entry(l3_l, &x25dte_obj.ilist, list) {
++              if (&l3_l->inst == inst) {
++                      err = 0;
++                      break;
++              }
++      }
++      if (prim == (MGR_NEWLAYER | REQUEST))
++              return(new_l3(data, arg));
++      if (err) {
++              printk(KERN_WARNING "l3x25_manager prim(%x) no instance\n", prim);
++              return(err);
++      }
++      switch(prim) {
++          case MGR_NEWENTITY | CONFIRM:
++              l3_l->entity = (int)arg;
++              break;
++          case MGR_ADDSTPARA | INDICATION:
++              {
++                      mISDN_stPara_t *stp = arg;
++
++                      if (stp->down_headerlen)
++                              l3_l->down_headerlen = stp->down_headerlen;
++                      if (stp->up_headerlen)
++                              l3_l->up_headerlen = stp->up_headerlen;
++                      printk(KERN_DEBUG "MGR_ADDSTPARA: (%d/%d/%d)\n",
++                              stp->maxdatalen, stp->down_headerlen, stp->up_headerlen);
++              }
++              break;
++          case MGR_CLRSTPARA | INDICATION:
++          case MGR_CLONELAYER | REQUEST:
++              break;
++          case MGR_CONNECT | REQUEST:
++              return(mISDN_ConnectIF(inst, arg));
++          case MGR_SETIF | REQUEST:
++          case MGR_SETIF | INDICATION:
++              return(mISDN_SetIF(inst, arg, prim, dte_from_up, dte_from_down, l3_l));
++          case MGR_DISCONNECT | REQUEST:
++          case MGR_DISCONNECT | INDICATION:
++              return(mISDN_DisConnectIF(inst, arg));
++          case MGR_UNREGLAYER | REQUEST:
++          case MGR_RELEASE | INDICATION:
++              if (debug & DEBUG_L3X25_MGR)
++                      printk(KERN_DEBUG "X25_release_l3 id %x\n", l3_l->inst.st->id);
++              X25_release_l3(l3_l);
++              break;
++//        case MGR_STATUS | REQUEST:
++//            return(l3x25_status(l3x25_l, arg));
++          default:
++              if (debug & DEBUG_L3X25_MGR)
++                      printk(KERN_WARNING "l3x25_manager prim %x not handled\n", prim);
++              return(-EINVAL);
++      }
++      return(0);
++}
++
++static int
++x25_dte_init(void)
++{
++      int err;
++
++      printk(KERN_INFO "X25 DTE modul version %s\n", mISDN_getrev(mISDN_dte_revision));
++#ifdef MODULE
++      x25dte_obj.owner = THIS_MODULE;
++#endif
++      x25dte_obj.name = MName;
++      x25dte_obj.BPROTO.protocol[3] = ISDN_PID_L3_B_X25DTE;
++      x25dte_obj.own_ctrl = dte_manager;
++      INIT_LIST_HEAD(&x25dte_obj.ilist);
++      if ((err = mISDN_register(&x25dte_obj))) {
++              printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
++      } else {
++              X25_l3_init();
++              dte_rfsm.state_count = R_STATE_COUNT;
++              dte_rfsm.event_count = R_EVENT_COUNT;
++              dte_rfsm.strEvent = X25strREvent;
++              dte_rfsm.strState = X25strRState;
++              mISDN_FsmNew(&dte_rfsm, RFnList, R_FN_COUNT);
++              dte_pfsm.state_count = P_STATE_COUNT;
++              dte_pfsm.event_count = P_EVENT_COUNT;
++              dte_pfsm.strEvent = X25strPEvent;
++              dte_pfsm.strState = X25strPState;
++              mISDN_FsmNew(&dte_pfsm, PFnList, P_FN_COUNT);
++              dte_dfsm.state_count = D_STATE_COUNT;
++              dte_dfsm.event_count = D_EVENT_COUNT;
++              dte_dfsm.strEvent = X25strDEvent;
++              dte_dfsm.strState = X25strDState;
++              mISDN_FsmNew(&dte_dfsm, DFnList, D_FN_COUNT);
++      }
++      return(err);
++}
++
++static void
++x25_dte_cleanup(void)
++{
++      x25_l3_t        *l3, *nl3;
++      int             err;
++
++      if ((err = mISDN_unregister(&x25dte_obj))) {
++              printk(KERN_ERR "Can't unregister l3x25 error(%d)\n", err);
++      }
++      if(!list_empty(&x25dte_obj.ilist)) {
++              printk(KERN_WARNING "l3x25 inst list not empty\n");
++              list_for_each_entry_safe(l3, nl3, &x25dte_obj.ilist, list)
++                      X25_release_l3(l3);
++      }
++      X25_l3_cleanup();
++      mISDN_FsmFree(&dte_rfsm);
++      mISDN_FsmFree(&dte_pfsm);
++      mISDN_FsmFree(&dte_dfsm);
++}
++
++module_init(x25_dte_init);
++module_exit(x25_dte_cleanup);
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.c linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.c
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.c     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.c 2004-11-22 09:33:38.483693504 +0000
+@@ -0,0 +1,1178 @@
++/* $Id$
++ *
++ * Linux modular ISDN subsystem, mISDN
++ * X.25/X.31 common Layer3 functions 
++ *
++ * Author     Karsten Keil (kkeil@suse.de)
++ *
++ * Copyright 2003 by Karsten Keil (kkeil@suse.de)
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU General Public License, incorporated herein by reference.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include "x25_l3.h"
++#include "helper.h"
++#include "debug.h"
++
++/* LinkLayer (L2) maintained by L3 statemachine */
++
++static struct Fsm llfsm = {NULL, 0, 0, NULL, NULL};
++
++enum {
++      ST_LL_REL,
++      ST_LL_ESTAB_WAIT,
++      ST_LL_REL_WAIT,
++      ST_LL_ESTAB,
++};
++
++#define LL_STATE_COUNT        (ST_LL_ESTAB+1)
++
++static char *strLLState[] =
++{
++      "ST_LL_REL",
++      "ST_LL_ESTAB_WAIT",
++      "ST_LL_REL_WAIT",
++      "ST_LL_ESTAB",
++};
++
++static char *strLLEvent[] =
++{
++      "EV_L3_ESTABLISH_REQ",
++      "EV_LL_ESTABLISH_IND",
++      "EV_LL_ESTABLISH_CNF",
++      "EV_L3_RELEASE_REQ",
++      "EV_LL_RELEASE_CNF",
++      "EV_LL_RELEASE_IND",
++};
++
++/* X.25 Restart state machine */
++
++char *X25strRState[] =
++{
++      "ST_R0",
++      "ST_R1",
++      "ST_R2",
++      "ST_R3",
++};
++
++char *X25strREvent[] =
++{
++      "EV_LL_READY",
++      "EV_L3_RESTART_REQ",
++      "EV_L2_RESTART",
++      "EV_L2_RESTART_CNF",
++      "EV_L3_RESTART_TOUT",
++};
++
++/* X.25 connection state machine */
++
++char *X25strPState[] =
++{
++      "ST_P0",
++      "ST_P1",
++      "ST_P2",
++      "ST_P3",
++      "ST_P4",
++      "ST_P5",
++      "ST_P6",
++      "ST_P7",
++};
++
++char *X25strPEvent[] =
++{
++      "EV_L3_READY",
++      "EV_L3_OUTGOING_CALL",
++      "EV_L2_INCOMING_CALL",
++      "EV_L2_CALL_CNF",
++      "EV_L3_CALL_ACCEPT",
++      "EV_L3_CLEARING",
++      "EV_L2_CLEAR",
++      "EV_L2_CLEAR_CNF",
++      "EV_L2_INVALPKT",
++      "EV_L3_CALL_TOUT",
++      "EV_L3_CLEAR_TOUT",
++};
++
++/* X.25 Flowcontrol state machine */
++
++char *X25strDState[] =
++{
++      "ST_D0",
++      "ST_D1",
++      "ST_D2",
++      "ST_D3",
++};
++
++char *X25strDEvent[] =
++{
++      "EV_L3_CONNECT",
++      "EV_L2_RESETING",
++      "EV_L2_RESET",
++      "EV_L2_RESET_CNF",
++      "EV_L3_RESET_TOUT",
++};
++
++static void
++l3m_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      x25_l3_t        *l3 = fi->userdata;
++      logdata_t       log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = l3->inst.name;
++      l3->inst.obj->ctrl(&l3->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++/* LinkLayer (L2) maintained by L3 statemachine */
++
++static void
++ll_activate(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_LL_ESTAB_WAIT);
++      X25_l3down(l3, DL_ESTABLISH | REQUEST, 0, NULL);
++}
++
++static void
++ll_connect(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t *l3 = fi->userdata;
++      struct sk_buff *skb;
++      int dequeued = 0;
++
++      mISDN_FsmChangeState(fi, ST_LL_ESTAB);
++      mISDN_FsmEvent(&l3->x25r, EV_LL_READY, NULL);
++      while ((skb = skb_dequeue(&l3->downq))) {
++              mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++              if (X25_l3down(l3, hh->prim, hh->dinfo, skb))
++                      dev_kfree_skb(skb);
++              dequeued++;
++      }
++}
++
++static void
++ll_connected(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t *l3 = fi->userdata;
++      struct sk_buff *skb;
++      int dequeued = 0;
++
++      mISDN_FsmChangeState(fi, ST_LL_ESTAB);
++      mISDN_FsmEvent(&l3->x25r, EV_LL_READY, NULL);
++      while ((skb = skb_dequeue(&l3->downq))) {
++              mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++              if (X25_l3down(l3, hh->prim, hh->dinfo, skb))
++                      dev_kfree_skb(skb);
++              dequeued++;
++      }
++}
++
++static void
++ll_release_req(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_LL_REL_WAIT);
++      X25_l3down(l3, DL_RELEASE | REQUEST, 0, NULL);
++}
++
++static void
++ll_release_ind(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_LL_REL);
++      discard_queue(&l3->downq);
++}
++
++static void
++ll_release_cnf(struct FsmInst *fi, int event, void *arg)
++{
++      x25_l3_t *l3 = fi->userdata;
++
++      mISDN_FsmChangeState(fi, ST_LL_REL);
++      discard_queue(&l3->downq);
++}
++
++
++/* *INDENT-OFF* */
++static struct FsmNode LLFnList[] =
++{
++      {ST_LL_REL,             EV_L3_ESTABLISH_REQ,    ll_activate},
++      {ST_LL_REL,             EV_LL_ESTABLISH_IND,    ll_connect},
++      {ST_LL_REL,             EV_LL_ESTABLISH_CNF,    ll_connect},
++      {ST_LL_ESTAB_WAIT,      EV_LL_ESTABLISH_CNF,    ll_connected},
++      {ST_LL_ESTAB_WAIT,      EV_L3_RELEASE_REQ,      ll_release_req},
++      {ST_LL_ESTAB_WAIT,      EV_LL_RELEASE_IND,      ll_release_ind},
++      {ST_LL_ESTAB,           EV_LL_RELEASE_IND,      ll_release_ind},
++      {ST_LL_ESTAB,           EV_L3_RELEASE_REQ,      ll_release_req},
++      {ST_LL_REL_WAIT,        EV_LL_RELEASE_CNF,      ll_release_cnf},
++      {ST_LL_REL_WAIT,        EV_L3_ESTABLISH_REQ,    ll_activate},
++};
++/* *INDENT-ON* */
++
++#define LL_FN_COUNT (sizeof(LLFnList)/sizeof(struct FsmNode))
++
++static void
++l3c_debug(struct FsmInst *fi, char *fmt, ...)
++{
++      x25_channel_t   *l3c = fi->userdata;
++      logdata_t       log;
++
++      va_start(log.args, fmt);
++      log.fmt = fmt;
++      log.head = l3c->l3->inst.name;
++      l3c->l3->inst.obj->ctrl(&l3c->l3->inst, MGR_DEBUGDATA | REQUEST, &log);
++      va_end(log.args);
++}
++
++static void
++discard_confq(x25_channel_t *l3c)
++{
++      int             i;
++      x25_ConfQueue_t *cq = l3c->confq;
++
++      for (i = 0; i < l3c->lwin; i++) {
++              if (cq->PktId) {
++                      dev_kfree_skb(cq->skb);
++                      cq->PktId = 0;
++              }
++              cq++;
++      }
++}
++
++int
++X25_reset_channel(x25_channel_t *l3c, struct sk_buff *skb)
++{
++      discard_confq(l3c);
++      l3c->pr = 0;
++      l3c->ps = 0;
++      l3c->rps = 0;
++      // TODO requeue outstanding pakets
++      // TODO timers ???
++      if (skb) {
++              if (skb->len == 2)
++                      memcpy(l3c->cause, skb->data, 2);
++              else
++                      printk(KERN_DEBUG "%s: skb len not 2 (%d)\n", __FUNCTION__, skb->len);
++      }
++      if (ST_P4 == l3c->x25p.state) {
++              X25sendL4frame(l3c, l3c->l3, CAPI_RESET_B3_IND, 0, 2, l3c->cause);
++              // invoke send ???
++      }
++      return(0);
++}
++
++int
++X25_restart(x25_l3_t *l3)
++{
++      x25_channel_t   *l3c;
++
++      list_for_each_entry(l3c, &l3->channellist, list) {
++              memcpy(l3c->cause, l3->cause, 2);
++              X25_reset_channel(l3c, NULL);
++              mISDN_FsmEvent(&l3c->x25p, EV_L3_READY, NULL);
++      }
++      return(0);
++}
++
++int
++X25_next_id(x25_l3_t *l3)
++{
++      u_long  flags;
++      int     id;
++
++      spin_lock_irqsave(&l3->lock, flags);
++      id = l3->next_id++;
++      if (id == 0x0fff)
++              l3->next_id = 1;
++      spin_unlock_irqrestore(&l3->lock, flags);
++      id |= (l3->entity << 16);
++      return(id);
++}
++
++int
++X25_get_header(x25_l3_t *l3, struct sk_buff *skb, u_char *gfi, __u16 *channel, u_char *ptype)
++{
++      u_char  *p = skb->data;
++      int     l = 3;
++
++      if (skb->len < 2)
++              return(38); // packet too short
++      if ((*p & 0x30) == 0x30) {
++              if (*p != 0x30)
++                      return(40); // invalid GFI
++              p++;
++              l++;
++              if (skb->len < 3)
++                      return(38); // packet too short
++              if ((*p & 0x30)!= 0x30)
++                      return(40); // invalid GFI
++              if (!test_bit(X25_STATE_MOD32768, &l3->state))
++                      return(40); // invalid GFI
++      }
++      *gfi = (*p & 0xf0);
++      if (!(*gfi & 0x30))
++              return(40); // invalid GFI
++      if (((*gfi & 0x30) == 0x20) && !test_bit(X25_STATE_MOD128, &l3->state)) 
++              return(40); // invalid GFI
++      *channel = (*p & 0xf) << 8;
++      p++;
++      *channel |= *p++;
++      if (skb->len < l) {
++              *ptype = X25_PTYPE_NOTYPE;
++              return(38);
++      }
++      *ptype = *p;
++      skb_pull(skb, l);
++      return(0);
++}
++
++int
++X25_cansend(x25_channel_t *chan)
++{
++      u_int   m = 7;
++
++      if (test_bit(X25_STATE_MOD128, &chan->state))
++              m = 0x7f;
++      else if (test_bit(X25_STATE_MOD32768, &chan->state))
++              m = 0x7fff;
++      
++      return((((chan->ps - chan->pr) & m) < chan->lwin) &&
++              !test_bit(X25_STATE_DTE_RNR, &chan->state) && (chan->x25d.state == ST_D1));
++}
++
++void
++X25_confirmed(x25_channel_t *chan)
++{
++      int             i, ret;
++      x25_ConfQueue_t *cq = chan->confq;
++      struct sk_buff  *skb;
++
++      for (i = 0; i < chan->lwin; i++) {
++              if ((cq->PktId & 0x7fff) == chan->pr)
++                      break;
++              cq++;
++      }
++      if (i == chan->lwin) {
++              int_error();
++              return;
++      }
++      skb = cq->skb;
++      cq->skb = NULL;
++      if (!skb) {
++              return;
++      }
++      skb_push(skb, 8);
++      skb_trim(skb, 0);
++      capimsg_setu32(skb_put(skb, 4), 0, chan->ncci);
++      capimsg_setu16(skb_put(skb, 2), 0, cq->DataHandle);
++      capimsg_setu16(skb_put(skb, 2), 0, 0);
++      i = cq->MsgId;
++      /* free entry */
++      cq->PktId = 0;
++      ret = if_newhead(&chan->l3->inst.up, CAPI_DATA_B3_CONF, i, skb);
++      if (ret) {
++              printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret);
++              dev_kfree_skb(skb);
++      }
++      return;
++}
++
++void
++X25_confirm_pr(x25_channel_t *chan, u_int pr)
++{
++      u_int   mod = 8;
++
++      if (test_bit(X25_STATE_MOD128, &chan->state))
++              mod = 128;
++      else if (test_bit(X25_STATE_MOD32768, &chan->state))
++              mod = 32768;
++      while (chan->pr != pr) {
++              X25_confirmed(chan);
++              chan->pr++;
++              if (chan->pr >= mod)
++                      chan->pr = 0;
++      }
++}
++
++int
++X25_receive_data(x25_channel_t *chan, int ps, int flag, struct sk_buff *skb)
++{
++      int             l, i, m = 8;
++      u_char          *p = skb->data;
++      struct sk_buff  *nskb;
++      
++      if (test_bit(X25_STATE_DTE_RNR, &chan->state))
++              return(X25_ERRCODE_DISCARD);
++      for (i = 0; i < CAPI_MAXDATAWINDOW; i++) {
++              if (chan->recv_handles[i] == 0)
++                      break;
++      }
++
++      if (i == CAPI_MAXDATAWINDOW) {
++              test_and_set_bit(X25_STATE_DTE_RNR, &chan->state);
++              printk(KERN_DEBUG "%s: frame %d dropped\n", __FUNCTION__, skb->len);
++              return(X25_ERRCODE_DISCARD);
++      }
++
++      if (skb_headroom(skb) < CAPI_B3_DATA_IND_HEADER_SIZE) {
++              printk(KERN_DEBUG "%s: only %d bytes headroom, need %d",
++                      __FUNCTION__, skb_headroom(skb), CAPI_B3_DATA_IND_HEADER_SIZE);
++              nskb = skb_realloc_headroom(skb, CAPI_B3_DATA_IND_HEADER_SIZE);
++              dev_kfree_skb(skb);
++              if (!nskb) {
++                      int_error();
++                      return(0);
++              }
++              } else { 
++              nskb = skb;
++      }
++      chan->recv_handles[i] = 0x100 | flag;
++      l = skb->len;
++      skb_push(nskb, CAPI_B3_DATA_IND_HEADER_SIZE - CAPIMSG_BASELEN);
++      capimsg_setu32(nskb->data, 0, chan->ncci);
++      if (sizeof(nskb) == 4) {
++              capimsg_setu32(nskb->data, 4, (u_long)p);
++              capimsg_setu32(nskb->data, 14, 0);
++              capimsg_setu32(nskb->data, 18, 0);
++              
++      } else {
++              capimsg_setu32(nskb->data, 4, 0);
++              capimsg_setu32(nskb->data, 14, ((u_long)p) & 0xffffffff);
++              capimsg_setu32(nskb->data, 18, (((__u64)((u_long)p)) >> 32) & 0xffffffff);
++      }
++      capimsg_setu16(nskb->data, 8, l);
++      capimsg_setu16(nskb->data, 10, i);
++      capimsg_setu16(nskb->data, 12, flag);
++
++      if (if_newhead(&chan->l3->inst.up, CAPI_DATA_B3_IND, 0, nskb)) {
++              chan->recv_handles[i] = 0;
++              return(X25_ERRCODE_DISCARD);
++      }
++      if (!(flag & CAPI_FLAG_DELIVERCONF)) {
++              if (test_bit(X25_STATE_MOD32768, &chan->state))
++                      m = 32768;
++              else if (test_bit(X25_STATE_MOD128, &chan->state))
++                      m = 128;
++              chan->rps++;
++              if (chan->rps >= m)
++                      chan->rps = 0;
++              if (X25_cansend(chan) && skb_queue_len(&chan->dataq))
++                      X25_invoke_sending(chan);
++              else {
++                      if (test_bit(X25_STATE_DTE_RNR, &chan->state))
++                              X25sendL3frame(chan, chan->l3, X25_PTYPE_RNR, 0, NULL);
++                      else
++                              X25sendL3frame(chan, chan->l3, X25_PTYPE_RR, 0, NULL);
++              }
++      }
++      return(0);
++}
++
++int
++X25_get_and_test_pr(x25_channel_t *chan, u_char ptype, struct sk_buff *skb)
++{
++      u_char  *p = skb->data;
++      u_int   pr_m, pr, m = 7;
++
++      if (test_bit(X25_STATE_MOD128, &chan->state)) {
++              if (skb->len < 1)
++                      return(-38);
++              pr_m = *p;
++              skb_pull(skb, 1);
++              m = 0x7f;
++      } else if (test_bit(X25_STATE_MOD32768, &chan->state)) {
++              if (skb->len < 2)
++                      return(-38);
++              pr_m = *p++;
++              pr_m |= (*p << 8);
++              skb_pull(skb, 2);
++              m = 0x7fff;
++      } else {
++              pr_m = ptype >> 4; 
++      }
++      pr = pr_m >> 1;
++      if (chan->debug)
++              printk(KERN_DEBUG "%s: pr(%d) chan: pr(%d) ps(%d)\n",
++                      __FUNCTION__, pr, chan->pr, chan->ps);
++      if (((pr - chan->pr) & m) <= ((chan->ps - chan->pr) & m)) {
++              if (chan->pr != pr)
++                      X25_confirm_pr(chan, pr);
++              return(pr_m);
++      } else
++              return(-1);
++}
++
++int
++X25_get_and_test_ps(x25_channel_t *chan, u_char ptype, struct sk_buff *skb)
++{
++      u_char  *p = skb->data;
++      int     m = 128, ps = ptype >> 1;
++
++      if (test_bit(X25_STATE_MOD32768, &chan->state)) {
++              if (skb->len < 1)
++                      return(-38);
++              ps |= (*p << 7);
++              skb_pull(skb, 1);
++              m = 32768;
++      } else if (!test_bit(X25_STATE_MOD128, &chan->state)) {
++              ps &= 7;
++              m = 8;
++      }
++      if (chan->debug)
++              printk(KERN_DEBUG "%s: ps(%d) chan: rps(%d)\n",
++                      __FUNCTION__, ps, chan->rps);
++      if (ps != chan->rps)
++              return(-2);
++      return(ps);
++}
++
++void
++X25_release_channel(x25_channel_t *l3c)
++{
++      list_del(&l3c->list);
++      if (l3c->ncpi_data)
++              kfree(l3c->ncpi_data);
++      l3c->ncpi_data = NULL;
++      l3c->ncpi_len = 0;
++      discard_queue(&l3c->dataq);
++      mISDN_FsmDelTimer(&l3c->TP, 1);
++      mISDN_FsmDelTimer(&l3c->TD, 2);
++      discard_queue(&l3c->dataq);
++      discard_confq(l3c);
++      if (l3c->confq) {
++              kfree(l3c->confq);
++              l3c->confq = NULL;
++      }
++      kfree(l3c);
++}
++
++void
++X25_release_l3(x25_l3_t *l3) {
++      mISDNinstance_t *inst = &l3->inst;
++      x25_channel_t   *ch, *nch;
++
++      if (inst->up.peer) {
++              inst->up.peer->obj->ctrl(inst->up.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->up);
++      }
++      if (inst->down.peer) {
++              inst->down.peer->obj->ctrl(inst->down.peer,
++                      MGR_DISCONNECT | REQUEST, &inst->down);
++      }
++      if (inst->obj) {
++              list_del_init(&l3->list);
++      }
++      discard_queue(&l3->downq);
++      list_for_each_entry_safe(ch, nch, &l3->channellist, list)
++              X25_release_channel(ch);
++      mISDN_FsmDelTimer(&l3->TR, 3);
++      if (inst->obj) {
++              if (l3->entity != MISDN_ENTITY_NONE)
++                      inst->obj->ctrl(inst, MGR_DELENTITY | REQUEST, (void *)l3->entity);
++              inst->obj->ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
++      }
++      kfree(l3);
++}
++
++int
++X25_realloc_ncpi_data(x25_channel_t *l3c, int len, u_char *data)
++{
++      if (len) {
++              if (len > l3c->ncpi_len) {
++                      if (l3c->ncpi_data)
++                              kfree(l3c->ncpi_data);
++                      l3c->ncpi_data = kmalloc(len, GFP_ATOMIC);
++                      if (!l3c->ncpi_data) {
++                              l3c->ncpi_len = 0;
++                              return(-ENOMEM);
++                      }
++              }
++              memcpy(l3c->ncpi_data, data, len);
++      } else if (l3c->ncpi_data) {
++              kfree(l3c->ncpi_data);
++              l3c->ncpi_data = NULL;
++      }
++      l3c->ncpi_len = len;
++      return(0);
++}
++
++int
++new_x25_channel(x25_l3_t *l3, x25_channel_t **ch_p, __u16 ch, int dlen, u_char *data)
++{
++      x25_channel_t   *l3c;
++
++      l3c = kmalloc(sizeof(x25_channel_t), GFP_ATOMIC);
++      if (!l3c) {
++              printk(KERN_ERR "kmalloc x25_channel_t failed\n");
++              return(-ENOMEM);
++      }
++      memset(l3c, 0, sizeof(x25_channel_t));
++      if (X25_realloc_ncpi_data(l3c, dlen, data)) {
++              printk(KERN_ERR "kmalloc ncpi_data (%d) failed\n", dlen);
++              kfree(l3c);
++              return(-ENOMEM);
++      }
++      l3c->lwin = l3->B3cfg.winsize;
++      l3c->rwin = l3->B3cfg.winsize;
++      l3c->datasize = l3->maxdatalen;
++      l3c->lchan = ch;
++      l3c->ncci = ch << 16;
++      l3c->confq = kmalloc(l3c->lwin * sizeof(x25_ConfQueue_t), GFP_ATOMIC);
++      if (!l3c->confq) {
++              printk(KERN_ERR "kmalloc confq %d entries failed\n", l3c->lwin);
++              if (l3c->ncpi_data)
++                      kfree(l3c->ncpi_data);
++              kfree(l3c);
++              return(-ENOMEM);
++      }
++      memset(l3c->confq, 0, l3c->lwin * sizeof(x25_ConfQueue_t));
++      l3c->l3 = l3;
++      l3c->debug = l3->debug;
++      l3c->state = l3->state;
++
++      l3c->x25p.debug = l3->debug;
++      l3c->x25p.userdata = l3c;
++      l3c->x25p.userint = 0;
++      l3c->x25p.printdebug = l3c_debug;
++      mISDN_FsmInitTimer(&l3c->x25p, &l3c->TP);
++
++      l3c->x25d.debug = l3->debug;
++      l3c->x25d.userdata = l3c;
++      l3c->x25d.userint = 0;
++      l3c->x25d.printdebug = l3c_debug;
++      mISDN_FsmInitTimer(&l3c->x25d, &l3c->TD);
++      skb_queue_head_init(&l3c->dataq);
++
++      list_add_tail(&l3c->list, &l3->channellist);
++      *ch_p = l3c;
++      return(0);
++}
++
++int
++new_x25_l3(x25_l3_t **l3_p, mISDNstack_t *st, mISDN_pid_t *pid, mISDNobject_t *obj, int debug) {
++      x25_l3_t        *n_l3;
++      int             err;
++
++      if (!st || !pid)
++              return(-EINVAL);
++      if (!(n_l3 = kmalloc(sizeof(x25_l3_t), GFP_ATOMIC))) {
++              printk(KERN_ERR "kmalloc x25_l3_t failed\n");
++              return(-ENOMEM);
++      }
++      memset(n_l3, 0, sizeof(x25_l3_t));
++      INIT_LIST_HEAD(&n_l3->channellist);
++      n_l3->entity = MISDN_ENTITY_NONE;
++      n_l3->next_id = 1;
++      spin_lock_init(&n_l3->lock);
++      memcpy(&n_l3->inst.pid, pid, sizeof(mISDN_pid_t));
++      mISDN_init_instance(&n_l3->inst, obj, n_l3);
++      if (!mISDN_SetHandledPID(obj, &n_l3->inst.pid)) {
++              int_error();
++              kfree(n_l3);
++              return(-ENOPROTOOPT);
++      }
++      n_l3->debug = debug;
++      n_l3->B3cfg = (x25_B3_cfg_t)DEFAULT_X25_B3_CFG;
++      if (pid->param[3]) {
++              u_char  *p = pid->param[3];
++              memcpy(&n_l3->B3cfg, &p[1], p[0]);
++      }
++      if (n_l3->B3cfg.modulo == 128)
++              test_and_set_bit(X25_STATE_MOD128, &n_l3->state);
++      if (n_l3->inst.pid.global == 1)
++              test_and_set_bit(X25_STATE_ORGINATE, &n_l3->state);
++
++      n_l3->l2l3m.fsm = &llfsm;
++      n_l3->l2l3m.state = ST_LL_REL;
++      n_l3->l2l3m.debug = debug;
++      n_l3->l2l3m.userdata = n_l3;
++      n_l3->l2l3m.userint = 0;
++      n_l3->l2l3m.printdebug = l3m_debug;
++
++      n_l3->x25r.debug = debug;
++      n_l3->x25r.userdata = n_l3;
++      n_l3->x25r.userint = 0;
++      n_l3->x25r.printdebug = l3m_debug;
++      mISDN_FsmInitTimer(&n_l3->x25r, &n_l3->TR);
++      skb_queue_head_init(&n_l3->downq);
++
++      list_add_tail(&n_l3->list, &obj->ilist);
++      err = obj->ctrl(&n_l3->inst, MGR_NEWENTITY | REQUEST, NULL);
++      if (err) {
++              printk(KERN_WARNING "mISDN %s: MGR_NEWENTITY REQUEST failed err(%x)\n",
++                      __FUNCTION__, err);
++      }
++      err = obj->ctrl(st, MGR_REGLAYER | INDICATION, &n_l3->inst);
++      if (err) {
++              list_del(&n_l3->list);
++              kfree(n_l3);
++              n_l3 = NULL;
++      } else {
++              if (st->para.maxdatalen)
++                      n_l3->maxdatalen = st->para.maxdatalen;
++              if (st->para.up_headerlen)
++                      n_l3->up_headerlen = st->para.up_headerlen;
++              if (st->para.down_headerlen)
++                      n_l3->down_headerlen = st->para.down_headerlen;
++              if (debug)
++                      printk(KERN_DEBUG "%s:mlen(%d) hup(%d) hdown(%d)\n", __FUNCTION__,
++                              n_l3->maxdatalen, n_l3->up_headerlen, n_l3->down_headerlen);
++      }
++      *l3_p = n_l3;
++      return(err);
++}
++
++int
++X25_add_header(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, u_char *head, u_char flag)
++{
++      u_char  *p = head;
++
++      if (test_bit(X25_STATE_MOD32768, &l3->state)) {
++              *p++ = 0x30;
++              *p = 0x30;
++      } else if (test_bit(X25_STATE_MOD128, &l3->state))
++              *p = 0x20;
++      else
++              *p = 0x10;
++      switch (pt) {
++              case X25_PTYPE_RESTART:
++              case X25_PTYPE_RESTART_CNF:
++              case X25_PTYPE_REGISTER:
++              case X25_PTYPE_REGISTER_CNF:
++              case X25_PTYPE_DIAGNOSTIC:
++                      p++;
++                      *p++ = 0;
++                      *p++ = pt;
++                      break;
++              case X25_PTYPE_RESET:
++              case X25_PTYPE_RESET_CNF:
++              case X25_PTYPE_INTERRUPT:
++              case X25_PTYPE_INTERRUPT_CNF:
++                      *p++ |= (((l3c->lchan) >> 8) & 0xf);
++                      *p++ = l3c->lchan & 0xff;
++                      *p++ = pt;
++                      break;
++              case X25_PTYPE_CALL:
++              case X25_PTYPE_CLEAR:
++                      if (test_bit(X25_STATE_DBIT, &l3c->state))
++                              *p |= X25_GFI_DBIT;
++              case X25_PTYPE_CALL_CNF:
++              case X25_PTYPE_CLEAR_CNF:
++                      if (test_bit(X25_STATE_ABIT, &l3c->state))
++                              *p |= X25_GFI_ABIT;
++                      *p++ |= (((l3c->lchan) >> 8) & 0xf);
++                      *p++ = l3c->lchan & 0xff;
++                      *p++ = pt;
++                      break;
++              case X25_PTYPE_RR:
++              case X25_PTYPE_RNR:
++              case X25_PTYPE_REJ:
++                      if (*p == 0x10)
++                              pt |= (l3c->rps << 5);
++                      *p++ |= (((l3c->lchan) >> 8) & 0xf);
++                      *p++ = l3c->lchan & 0xff;
++                      *p++ = pt;
++                      if (test_bit(X25_STATE_MOD128, &l3->state))
++                              *p++ = l3c->rps << 1;
++                      else if (test_bit(X25_STATE_MOD32768, &l3->state)) {
++                              *p++ = (0x7f & l3c->rps) << 1;
++                              *p++ = l3c->rps >> 7;
++                      }
++                      break;
++              case X25_PTYPE_DATA:
++                      if (*p == 0x10) {
++                              *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT));
++                              *p++ |= (((l3c->lchan) >> 8) & 0xf);
++                              *p++ = l3c->lchan & 0xff;
++                              if (flag & X25_MBIT)
++                                      pt |= X25_MBIT_MOD8;
++                              pt |= (l3c->rps << 5);
++                              pt |= (l3c->ps << 1);
++                              *p++ = pt;
++                              l3c->ps++;
++                              if (l3c->ps > 7)
++                                      l3c->ps = 0;
++                      } else if (*p == 0x20) {
++                              *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT));
++                              *p++ |= (((l3c->lchan) >> 8) & 0xf);
++                              *p++ = l3c->lchan & 0xff;
++                              *p++ = (l3c->ps << 1);
++                              *p = (flag & X25_MBIT) ? 1 : 0; 
++                              *p++ |= (l3c->rps << 1);
++                              l3c->ps++;
++                              if (l3c->ps > 0x7f)
++                                      l3c->ps = 0;
++                      } else {
++                              *p |= (flag & (X25_GFI_DBIT | X25_GFI_QBIT));
++                              *p++ |= (((l3c->lchan) >> 8) & 0xf);
++                              *p++ = l3c->lchan & 0xff;
++                              *p++ = ((l3c->ps & 0x7f) << 1);
++                              *p++ = (l3c->ps >> 7);
++                              *p = (flag & X25_MBIT) ? 1 : 0;
++                              *p++ = ((l3c->rps & 0x7f) << 1);
++                              *p++ = (l3c->rps >> 7);
++                              l3c->ps++;
++                              if (l3c->ps > 0x7fff)
++                                      l3c->ps = 0;
++                      }
++                      break;
++              default:
++                      return(-EINVAL);
++      }
++      return(p - head);
++}
++
++int
++X25sendL3frame(x25_channel_t *l3c, x25_l3_t *l3, u_char pt, int len, void *arg)
++{
++      struct sk_buff  *skb;
++      int             ret;
++
++      skb = alloc_stack_skb(len + X25_MINSIZE, l3->down_headerlen);
++      if (!skb)
++              return(-ENOMEM);
++      ret = X25_add_header(l3c, l3, pt, skb->tail, 0);
++      if (ret<0) {
++              int_error();
++              dev_kfree_skb(skb);
++              return(ret);
++      }
++      skb_put(skb, ret);
++      if (arg && len)
++              memcpy(skb_put(skb, len), arg, len);
++
++      mISDN_sethead(DL_DATA_REQ, X25_next_id(l3), skb);
++
++      if (l3->l2l3m.state == ST_LL_ESTAB) {
++              mISDNif_t       *down = &l3->inst.down;
++
++              ret = down->func(down, skb);
++              if (ret) {
++                      dev_kfree_skb(skb);
++              }
++      } else {
++              skb_queue_tail(&l3->downq, skb);
++              ret = 0;
++      }
++      return(ret);
++}
++
++int
++X25_l3down(x25_l3_t *l3, u_int prim, u_int dinfo, struct sk_buff *skb)
++{
++      mISDNif_t       *down = &l3->inst.down;
++      int             ret;
++
++      if (!skb) {
++              if (!(skb = alloc_stack_skb(0, l3->down_headerlen)))
++                      return(-ENOMEM);
++      }
++      mISDN_sethead(prim, dinfo, skb);
++      ret = down->func(down, skb);
++      if (ret) {
++              dev_kfree_skb(skb);
++      }
++      return(0);
++}
++
++void
++X25_send_diagnostic(x25_l3_t *l3, struct sk_buff *skb, int err, int channel)
++{
++      u_char  diagp[8], *p;
++      u_int   i,l = 3;
++
++      p = diagp;
++      *p++ = err & 0xff;
++      if (test_bit(X25_STATE_MOD32768, &l3->state)) {
++              *p++ = 0x30;
++              l++;
++      }
++      if (skb) {
++              if (skb->len < l)
++                      l = skb->len;
++              for (i = 0; i < l; i++)
++                      *p++ = skb->data[i];
++      } else {
++              if ((err & 0xf0) == 0x30) { /* Timer Expired */
++                      if (test_bit(X25_STATE_MOD32768, &l3->state))
++                              *p = 0x30;
++                      else if (test_bit(X25_STATE_MOD128, &l3->state))
++                              *p = 0x20;
++                      else
++                              *p = 0x10;
++                      if (err == 0x34)
++                              channel = 0; 
++                      *p |= ((channel >> 8) & 0x0f);
++                      p++;
++                      *p++ = channel & 0xff;
++              }
++      }
++      X25sendL3frame(NULL, l3, X25_PTYPE_DIAGNOSTIC, p - diagp, diagp);
++}
++
++x25_channel_t *
++X25_get_channel(x25_l3_t *l3, __u16 ch)
++{
++      x25_channel_t   *l3c;
++
++      list_for_each_entry(l3c, &l3->channellist, list) {
++              if (l3c->lchan == ch)
++                      return(l3c);
++      }
++      return(NULL);
++}
++
++x25_channel_t *
++X25_get_channel4NCCI(x25_l3_t *l3, __u32 addr)
++{
++      x25_channel_t   *l3c;
++
++      list_for_each_entry(l3c, &l3->channellist, list) {
++              if ((l3c->ncci & 0xffff0000) == (addr & 0xffff0000))
++                      return(l3c);
++      }
++      return(NULL);
++}
++
++int
++X25sendL4skb(x25_channel_t *l3c, x25_l3_t *l3, __u32 addr, int prim, int dinfo, struct sk_buff *skb)
++{
++      skb_push(skb, 4);
++      if (l3c)
++              capimsg_setu32(skb->data, 0, l3c->ncci);
++      else
++              capimsg_setu32(skb->data, 0, addr);
++      return(if_newhead(&l3->inst.up, prim, dinfo, skb));              
++}
++
++int
++X25sendL4frame(x25_channel_t *l3c, x25_l3_t *l3, int prim, int flags, int len, void *arg)
++{
++      struct sk_buff  *skb;
++      u_char          *p;
++      int             ret;
++
++      skb = alloc_stack_skb(len + X25_MINSIZE + 2, l3->up_headerlen);
++      if (!skb)
++              return(-ENOMEM);
++
++      capimsg_setu32(skb_put(skb, 4), 0, l3c->ncci);
++      switch(prim) {
++              case CAPI_DISCONNECT_B3_IND:
++                      capimsg_setu16(skb_put(skb, 2), 0, flags & 0xffff);
++              case CAPI_CONNECT_B3_IND:
++              case CAPI_CONNECT_B3_ACTIVE_IND:
++              case CAPI_RESET_B3_IND:
++                      if (len) {
++                              p = skb_put(skb, len + 4);
++                              *p++ = len +3;
++                              if (flags & 0x10000)
++                                      *p++ = 1;
++                              else
++                                      *p++ = 0;
++                              *p++ = l3c->lchan >> 8;
++                              *p++ = l3c->lchan & 0xff;
++                              memcpy(p, arg, len);
++                      } else {
++                              p = skb_put(skb, 1);
++                              *p = 0;
++                      }
++                      break;
++              default:
++                      dev_kfree_skb(skb);
++                      return(-EINVAL);
++      }
++      ret = if_newhead(&l3->inst.up, prim, 0, skb);
++      if (ret) {
++              printk(KERN_WARNING "%s: up error %d\n", __FUNCTION__, ret);
++              dev_kfree_skb(skb);
++      }
++      return(ret);
++}
++
++static int
++confq_len(x25_channel_t *l3c)
++{
++      int             i,n = 0;
++      x25_ConfQueue_t *cq = l3c->confq;
++
++      for (i = 0; i < l3c->lwin; i++)
++              if (cq[i].PktId)
++                      n++;
++      return(n);
++}
++
++static inline x25_ConfQueue_t *
++get_free_confentry(x25_channel_t *l3c)
++{
++      int             i;
++      x25_ConfQueue_t *cq = l3c->confq;
++
++      for (i = 0; i < l3c->lwin; i++) {
++              if (!cq->PktId)
++                      break;
++              cq++;
++      }
++      if (i == l3c->lwin)
++              return(NULL);
++      return(cq);
++}
++
++int
++X25_invoke_sending(x25_channel_t *l3c)
++{
++      int             l,n = 0;
++      x25_ConfQueue_t *cq;
++      struct sk_buff  *skb, *nskb;
++      u_char          flg;
++
++      if (!X25_cansend(l3c))
++              return(0);
++      cq = get_free_confentry(l3c);
++      skb = skb_dequeue(&l3c->dataq);
++      while(cq && skb) {
++              mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++
++              cq->MsgId = hh->dinfo;
++              hh++;
++              cq->DataHandle = hh->prim;
++              nskb = skb_clone(skb, GFP_ATOMIC);
++              if (!nskb) {
++                      skb_queue_head(&l3c->dataq, skb);
++                      break;
++              }
++              cq->skb = skb;
++              cq->PktId = 0x10000 | l3c->ps;
++              flg = (hh->dinfo & CAPI_FLAG_DELIVERCONF) ? X25_GFI_DBIT : 0;
++              if (hh->dinfo & CAPI_FLAG_QUALIFIER)
++                      flg |= X25_GFI_QBIT;
++              if (hh->dinfo & CAPI_FLAG_MOREDATA)
++                      flg |= X25_MBIT;
++              l = 3;
++              if (test_bit(X25_STATE_MOD128, &l3c->state))
++                      l++;
++              else if (test_bit(X25_STATE_MOD32768, &l3c->state))
++                      l += 4;
++              skb_push(nskb, l);
++              if (l != X25_add_header(l3c, l3c->l3, X25_PTYPE_DATA, nskb->data, flg))
++                      int_error();
++              if (l3c->l3->l2l3m.state == ST_LL_ESTAB)
++                      X25_l3down(l3c->l3, DL_DATA_REQ, X25_next_id(l3c->l3), nskb);
++              else {
++                      mISDN_sethead(DL_DATA_REQ, X25_next_id(l3c->l3), nskb); 
++                      skb_queue_tail(&l3c->l3->downq, nskb);
++                      break;
++              }
++              cq = get_free_confentry(l3c);
++              skb = skb_dequeue(&l3c->dataq);
++              n++;
++      }
++      return(n);
++}
++
++__u16
++x25_data_b3_req(x25_channel_t *l3c, int dinfo, struct sk_buff *skb)
++{
++      __u16           size;
++      mISDN_head_t    *hh = mISDN_HEAD_P(skb);
++
++      if (!l3c)
++              return(0x2002);
++      if (skb->len < 10)
++              return(0x2007);
++      if ((confq_len(l3c) + skb_queue_len(&l3c->dataq)) > 7)
++              return(CAPI_SENDQUEUEFULL);
++      if ((l3c->x25p.state != ST_P4) && (l3c->x25d.state != ST_D1))
++              return(0x2001);
++
++      size =  CAPIMSG_U16(skb->data, 4);
++
++      /* we save DataHandle and Flags in a area after normal mISDN_HEAD */ 
++      hh++;
++      hh->prim = CAPIMSG_U16(skb->data, 6);
++      hh->dinfo = CAPIMSG_U16(skb->data, 8);
++      /* the data begins behind the header, we don't use Data32/Data64 here */
++      if ((skb->len - size) == 18)
++              skb_pull(skb, 18);
++      else if ((skb->len - size) == 10) // old format
++              skb_pull(skb, 10);
++      else
++              return(0x2007);
++      if (hh->dinfo & CAPI_FLAG_EXPEDITED) { // TODO Interrupt packet
++      }
++
++      skb_queue_tail(&l3c->dataq, skb);
++      X25_invoke_sending(l3c);
++      return(0);
++}
++
++int
++x25_data_b3_resp(x25_channel_t *l3c, int dinfo, struct sk_buff *skb)
++{
++      int             i, m = 8;
++
++      if (!l3c)
++              return(-ENODEV);
++
++      i = CAPIMSG_U16(skb->data, 0);
++      dev_kfree_skb(skb);
++      if (i >= CAPI_MAXDATAWINDOW) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (l3c->recv_handles[i] == 0) {
++              int_error();
++              return(-EINVAL);
++      }
++      if (l3c->recv_handles[i] & CAPI_FLAG_DELIVERCONF) {
++              if (test_bit(X25_STATE_MOD32768, &l3c->state))
++                      m = 32768;
++              else if (test_bit(X25_STATE_MOD128, &l3c->state))
++                      m = 128;
++              l3c->rps++;
++              if (l3c->rps >= m)
++                      l3c->rps = 0;
++              l3c->recv_handles[i] = 0;
++              i = 0;
++              if (X25_cansend(l3c) && skb_queue_len(&l3c->dataq))
++                      X25_invoke_sending(l3c);
++              else {
++                      i = 1;
++                      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RR, 0, NULL);
++              }
++      } else {
++              l3c->recv_handles[i] = 0;
++              i = 0;
++      }
++      if (test_and_clear_bit(X25_STATE_DTE_RNR, &l3c->state)) {
++              if (!i)
++                      X25sendL3frame(l3c, l3c->l3, X25_PTYPE_RR, 0, NULL);
++      }       
++      return(0);
++}
++
++int
++X25_l3_init(void)
++{
++      llfsm.state_count = LL_STATE_COUNT;
++      llfsm.event_count = LL_EVENT_COUNT;
++      llfsm.strEvent = strLLEvent;
++      llfsm.strState = strLLState;
++      mISDN_FsmNew(&llfsm, LLFnList, LL_FN_COUNT);
++      return(0);
++}
++
++void
++X25_l3_cleanup(void)
++{
++      mISDN_FsmFree(&llfsm);
++}
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.h linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.h
+--- linux-2.6.8.1.org/drivers/isdn/hardware/mISDN/x25_l3.h     1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hardware/mISDN/x25_l3.h 2004-11-22 09:33:38.493691984 +0000
+@@ -0,0 +1,283 @@
++/* $Id$
++ *
++ * Layer 3 X.25 defines
++ *
++ * This file is (c) under GNU PUBLIC LICENSE
++ *
++ */
++#ifndef _L3_X25_H
++#define _L3_X25_H
++#include "m_capi.h"
++
++typedef struct _x25_l3                x25_l3_t;
++typedef struct _x25_channel   x25_channel_t;
++typedef struct _x25_B3_cfg    x25_B3_cfg_t;
++typedef struct _x25_ncpi      x25_ncpi_t;
++typedef struct _x25_ConfQueue x25_ConfQueue_t;
++
++#define DEBUG_L3X25_WARN      0x0001
++#define       DEBUG_L3X25_MGR         0x1000
++
++struct _x25_B3_cfg {
++      __u16   LIC;
++      __u16   HIC;
++      __u16   LTC;
++      __u16   HTC;
++      __u16   LOC;
++      __u16   HOC;
++      __u16   modulo;
++      __u16   winsize;
++};
++
++#define DEFAULT_X25_B3_CFG    {0, 0, 1, 1, 0, 0, 8, 2}
++
++struct _x25_ncpi {
++      __u8    len             __attribute__((packed));
++      __u8    Flags           __attribute__((packed));
++      __u8    Group           __attribute__((packed));
++      __u8    Channel         __attribute__((packed));
++      __u8    Contens[4]      __attribute__((packed)); /* Note this can be less/more bytes in use */ 
++};
++
++struct _x25_ConfQueue { 
++      __u32           PktId; 
++      __u16           DataHandle;
++      __u16           MsgId; 
++      struct sk_buff  *skb;
++};
++
++struct _x25_l3 {
++      struct list_head        list;
++      mISDNinstance_t         inst;
++      struct list_head        channellist;
++      struct FsmInst          l2l3m;
++      struct FsmInst          x25r;
++      struct FsmTimer         TR;
++      int                     TRval;
++      int                     TRrep;
++      int                     entity;
++      int                     next_id;
++      struct sk_buff_head     downq;
++      int                     down_headerlen;
++      int                     up_headerlen;
++      int                     maxdatalen;
++      x25_B3_cfg_t            B3cfg;
++      u_long                  state;
++      u_int                   debug;
++      spinlock_t              lock;
++      u_char                  cause[2];
++};
++
++struct _x25_channel {
++      struct list_head        list;
++      x25_l3_t                *l3;
++      struct FsmInst          x25p;
++      struct FsmInst          x25d;
++      struct FsmTimer         TP;
++      int                     TPval;
++      int                     TPrep;
++      struct FsmTimer         TD;
++      int                     TDval;
++      int                     TDrep;
++      __u32                   ncci;
++      u_char                  *ncpi_data;
++      u_int                   ncpi_len;
++      u_long                  state;
++      u_int                   debug;
++      spinlock_t              lock;
++      u_int                   pr;
++      u_int                   ps;
++      u_int                   rps;
++      u_int                   lwin;
++      u_int                   rwin;
++      u_int                   datasize;
++      struct sk_buff_head     dataq;
++      x25_ConfQueue_t         *confq;
++      u_int                   recv_handles[CAPI_MAXDATAWINDOW];
++      __u16                   lchan;
++      u_char                  cause[2];
++};
++
++#define X25_CHANNEL_INCOMING  1
++#define X25_CHANNEL_OUTGOING  2
++
++#define X25_STATE_ORGINATE    0
++#define X25_STATE_DCE         1
++#define X25_STATE_DTEDTE      2
++#define X25_STATE_PERMANENT   3
++#define X25_STATE_MOD128      4
++#define X25_STATE_MOD32768    5
++#define X25_STATE_ABIT                6
++#define X25_STATE_DBIT                7
++#define X25_STATE_ESTABLISH   16
++#define X25_STATE_DXE_INTSENT 17
++#define X25_STATE_DTE_INTSENT   18
++#define X25_STATE_DXE_RNR     19
++#define X25_STATE_DTE_RNR     20
++
++#define X25_MINSIZE           8
++
++#define X25_GFI_ABIT          0x80
++#define X25_GFI_DBIT          0x40
++#define X25_GFI_QBIT          0x80
++
++#define X25_MBIT              0x01
++#define X25_MBIT_MOD8         0x10
++
++#define CAPI_FLAG_QUALIFIER   0x01
++#define CAPI_FLAG_MOREDATA    0x02
++#define CAPI_FLAG_DELIVERCONF 0x04
++#define CAPI_FLAG_EXPEDITED   0x08
++
++#define X25_PTYPE_CALL                0x0b
++#define X25_PTYPE_CALL_CNF    0x0f
++#define X25_PTYPE_CLEAR               0x13
++#define X25_PTYPE_CLEAR_CNF   0x17
++#define X25_PTYPE_INTERRUPT   0x23
++#define X25_PTYPE_INTERRUPT_CNF       0x27
++#define X25_PTYPE_DATA                0x00
++#define X25_PTYPE_RR          0x01
++#define X25_PTYPE_RNR         0x05
++#define X25_PTYPE_REJ         0x09
++#define X25_PTYPE_RESET               0x1f
++#define X25_PTYPE_RESET_CNF   0x1b
++#define X25_PTYPE_RESTART     0xfb
++#define X25_PTYPE_RESTART_CNF 0xff
++#define X25_PTYPE_REGISTER    0xf3
++#define X25_PTYPE_REGISTER_CNF        0xf7
++#define X25_PTYPE_DIAGNOSTIC  0xf1
++#define X25_PTYPE_NOTYPE      0x7F
++
++#define       T10_VALUE               60000
++#define T11_VALUE             180000
++#define       T12_VALUE               60000
++#define T13_VALUE             60000
++#define       T20_VALUE               180000
++#define T21_VALUE             200000
++#define       T22_VALUE               180000
++#define T23_VALUE             180000
++#define T24_VALUE             60000
++#define T25_VALUE             200000
++#define T26_VALUE             180000
++#define T27_VALUE             60000
++#define T28_VALUE             300000
++
++
++#define R20_VALUE             1
++#define R22_VALUE             1
++#define R23_VALUE             1
++#define R25_VALUE             0
++#define R27_VALUE             0
++#define R28_VALUE             1
++
++#define X25_ERRCODE_DISCARD   0x0100
++
++/* LinkLayer (L2) maintained by L3 statemachine */
++enum {
++      EV_L3_ESTABLISH_REQ,
++      EV_LL_ESTABLISH_IND,
++      EV_LL_ESTABLISH_CNF,
++      EV_L3_RELEASE_REQ,
++      EV_LL_RELEASE_CNF,
++      EV_LL_RELEASE_IND,
++};
++#define LL_EVENT_COUNT        (EV_LL_RELEASE_IND+1)
++
++/* X.25 Restart state machine */
++enum {
++      ST_R0,
++      ST_R1,
++      ST_R2,
++      ST_R3,
++};
++#define R_STATE_COUNT (ST_R3+1)
++extern char *X25strRState[];
++
++enum {
++      EV_LL_READY,
++      EV_L3_RESTART_REQ,
++      EV_L2_RESTART,
++      EV_L2_RESTART_CNF,
++      EV_L3_RESTART_TOUT,
++};
++#define R_EVENT_COUNT (EV_L3_RESTART_TOUT+1)
++extern char *X25strREvent[];
++
++/* X.25 connection state machine */
++enum {
++      ST_P0,
++      ST_P1,
++      ST_P2,
++      ST_P3,
++      ST_P4,
++      ST_P5,
++      ST_P6,
++      ST_P7,
++};
++#define P_STATE_COUNT (ST_P7+1)
++extern char *X25strPState[];
++
++enum {
++      EV_L3_READY,
++      EV_L3_OUTGOING_CALL,
++      EV_L2_INCOMING_CALL,
++      EV_L2_CALL_CNF,
++      EV_L3_CALL_ACCEPT,
++      EV_L3_CLEARING,
++      EV_L2_CLEAR,
++      EV_L2_CLEAR_CNF,
++      EV_L2_INVALPKT,
++      EV_L3_CALL_TOUT,
++      EV_L3_CLEAR_TOUT,
++};
++#define P_EVENT_COUNT (EV_L3_CLEAR_TOUT+1)
++extern char *X25strPEvent[];
++
++/* X.25 Flowcontrol state machine */
++enum {
++      ST_D0,
++      ST_D1,
++      ST_D2,
++      ST_D3,
++};
++#define D_STATE_COUNT (ST_D3+1)
++extern char *X25strDState[];
++
++enum {
++      EV_L3_CONNECT,
++      EV_L3_RESETING,
++      EV_L2_RESET,
++      EV_L2_RESET_CNF,
++      EV_L3_RESET_TOUT,
++};
++#define D_EVENT_COUNT (EV_L3_RESET_TOUT+1)
++extern char *X25strDEvent[];
++
++extern x25_channel_t  *X25_get_channel(x25_l3_t *, __u16);
++extern x25_channel_t  *X25_get_channel4NCCI(x25_l3_t *, __u32);
++extern int            X25_reset_channel(x25_channel_t *, struct sk_buff *);
++extern int            X25_restart(x25_l3_t *);
++extern int            X25_get_header(x25_l3_t *, struct sk_buff *, u_char *, __u16 *, u_char *);
++extern void           X25_release_channel(x25_channel_t *);
++extern void           X25_release_l3(x25_l3_t *);
++extern int            X25_realloc_ncpi_data(x25_channel_t *, int, u_char *);
++extern int            new_x25_channel(x25_l3_t *, x25_channel_t **, __u16, int, u_char *);
++extern int            new_x25_l3(x25_l3_t **, mISDNstack_t *, mISDN_pid_t *, mISDNobject_t *, int);
++extern int            X25_next_id(x25_l3_t *);
++extern int            X25_add_header(x25_channel_t *, x25_l3_t *, u_char , u_char *, u_char);
++extern int            X25sendL3frame(x25_channel_t *, x25_l3_t *, u_char, int, void *);
++extern int            X25sendL4frame(x25_channel_t *, x25_l3_t *, int, int, int, void *);
++extern int            X25sendL4skb(x25_channel_t *, x25_l3_t *, __u32, int, int, struct sk_buff *);
++extern void           X25_send_diagnostic(x25_l3_t *, struct sk_buff *, int, int);
++extern int            X25_l3down(x25_l3_t *, u_int, u_int, struct sk_buff *);
++extern int            X25_l3_init(void);
++extern void           X25_l3_cleanup(void);
++extern int            X25_get_and_test_pr(x25_channel_t *, u_char, struct sk_buff *);
++extern int            X25_get_and_test_ps(x25_channel_t *, u_char, struct sk_buff *);
++extern int            X25_cansend(x25_channel_t *);
++extern __u16          x25_data_b3_req(x25_channel_t *, int, struct sk_buff *);
++extern int            x25_data_b3_resp(x25_channel_t *, int, struct sk_buff *);
++extern int            X25_invoke_sending(x25_channel_t *);
++extern int            X25_receive_data(x25_channel_t *, int, int, struct sk_buff *);
++
++#endif
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hisax/callc.c linux-2.6.8.1/drivers/isdn/hisax/callc.c
+--- linux-2.6.8.1.org/drivers/isdn/hisax/callc.c       2004-08-14 10:55:32.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hisax/callc.c   2004-11-22 09:32:47.940377256 +0000
+@@ -283,6 +283,7 @@
+ {
+       struct Channel *chanp = fi->userdata;
++      printk(KERN_DEBUG "%s: chanp(%p) arg(%p) chanp->cs->card_ops(%p)\n", __FUNCTION__, chanp, arg, chanp->cs->card_ops);
+       FsmDelTimer(&chanp->drel_timer, 60);
+       FsmDelTimer(&chanp->dial_timer, 73);
+       chanp->l2_active_protocol = chanp->l2_protocol;
+@@ -1508,26 +1509,25 @@
+                       ic->command, ic->driver);
+               return -ENODEV;
+       }
++      printk(KERN_DEBUG "%s: command(%x) arg(%lx)\n", __FUNCTION__, ic->command, ic->arg);
++      chanp = csta->channel + (ic->arg & 0xff);
+       switch (ic->command) {
++              case (ISDN_CMD_CLREAZ):
+               case (ISDN_CMD_SETEAZ):
+-                      chanp = csta->channel + ic->arg;
+                       break;
+               case (ISDN_CMD_SETL2):
+-                      chanp = csta->channel + (ic->arg & 0xff);
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "SETL2 card %d %ld",
+                                       csta->cardnr + 1, ic->arg >> 8);
+                       chanp->l2_protocol = ic->arg >> 8;
+                       break;
+               case (ISDN_CMD_SETL3):
+-                      chanp = csta->channel + (ic->arg & 0xff);
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "SETL3 card %d %ld",
+                                       csta->cardnr + 1, ic->arg >> 8);
+                       chanp->l3_protocol = ic->arg >> 8;
+                       break;
+               case (ISDN_CMD_DIAL):
+-                      chanp = csta->channel + (ic->arg & 0xff);
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)",
+                                       ic->parm.setup.eazmsn, ic->parm.setup.phone,
+@@ -1545,26 +1545,22 @@
+                       }
+                       break;
+               case (ISDN_CMD_ACCEPTB):
+-                      chanp = csta->channel + ic->arg;
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "ACCEPTB");
+                       FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
+                       break;
+               case (ISDN_CMD_ACCEPTD):
+-                      chanp = csta->channel + ic->arg;
+                       memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "ACCEPTD");
+                       FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
+                       break;
+               case (ISDN_CMD_HANGUP):
+-                      chanp = csta->channel + ic->arg;
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "HANGUP");
+                       FsmEvent(&chanp->fi, EV_HANGUP, NULL);
+                       break;
+               case (CAPI_PUT_MESSAGE):
+-                      chanp = csta->channel + ic->arg;
+                       if (chanp->debug & 1)
+                               capi_debug(chanp, &ic->parm.cmsg);
+                       if (ic->parm.cmsg.Length < 8)
+@@ -1702,21 +1698,18 @@
+                       break;
+               
+               case (ISDN_CMD_PROCEED):
+-                      chanp = csta->channel + ic->arg;
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "PROCEED");
+                       FsmEvent(&chanp->fi, EV_PROCEED, NULL);
+                       break;
+               case (ISDN_CMD_ALERT):
+-                      chanp = csta->channel + ic->arg;
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "ALERT");
+                       FsmEvent(&chanp->fi, EV_ALERT, NULL);
+                       break;
+               case (ISDN_CMD_REDIR):
+-                      chanp = csta->channel + ic->arg;
+                       if (chanp->debug & 1)
+                               link_debug(chanp, 1, "REDIR");
+                       memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/drivers/isdn/hisax/config.c linux-2.6.8.1/drivers/isdn/hisax/config.c
+--- linux-2.6.8.1.org/drivers/isdn/hisax/config.c      2004-08-14 10:56:01.000000000 +0000
++++ linux-2.6.8.1/drivers/isdn/hisax/config.c  2004-11-22 09:32:47.944376648 +0000
+@@ -1569,7 +1569,7 @@
+       struct IsdnCardState *cs;
+       for (i = 0; i < HISAX_MAX_CARDS; i++) {
+-              if (!cards[i].typ)
++              if (!cards[i].cs)
+                       break;
+       }
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/include/linux/isdn_compat.h linux-2.6.8.1/include/linux/isdn_compat.h
+--- linux-2.6.8.1.org/include/linux/isdn_compat.h      1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/include/linux/isdn_compat.h  2004-11-22 09:33:38.506690008 +0000
+@@ -0,0 +1,88 @@
++#ifndef _LINUX_ISDN_COMPAT_H
++#define _LINUX_ISDN_COMPAT_H
++
++#ifdef __KERNEL__
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
++
++#define set_current_state(sta) (current->state = sta)
++#define module_init(x)  int init_module(void) { return x(); }
++#define module_exit(x)  void cleanup_module(void) { x(); }
++#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0 = 0; } while (0)
++#define init_MUTEX(x)                           *(x)=MUTEX
++#define init_MUTEX_LOCKED(x)                    *(x)=MUTEX_LOCKED
++#define __devinit
++#define __devinitdata
++
++#else
++#define COMPAT_HAS_NEW_WAITQ
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
++
++#define COMPAT_HAS_2_2_PCI
++#define get_pcibase(ps,nr) ps->base_address[nr]
++#define pci_resource_start_io(pdev,nr)        pdev->base_address[nr] & PCI_BASE_ADDRESS_IO_MASK
++#define pci_resource_start_mem(pdev,nr)       pdev->base_address[nr] & PCI_BASE_ADDRESS_MEM_MASK
++#define pci_get_sub_vendor(pdev, id)  pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &id)
++#define pci_get_sub_system(pdev, id)  pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &id)
++#define dev_kfree_skb_any(a)          dev_kfree_skb(a)
++#define dev_kfree_skb_irq(a)          dev_kfree_skb(a)
++typedef       struct timer_list               timer_t;
++#else /* 2.4.0 and later */
++#include <linux/netdevice.h>
++#define pci_resource_start_io(pdev, nr) pci_resource_start(pdev, nr)
++#define pci_resource_start_mem(pdev, nr) pci_resource_start(pdev, nr)
++#define get_pcibase(ps, nr) ps->resource[nr].start
++#define pci_get_sub_system(pdev, id)  id = pdev->subsystem_device
++#define pci_get_sub_vendor(pdev, id)  id = pdev->subsystem_vendor
++#endif /* 2,4,0 */
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
++#ifndef IRQ_HANDLED /* maybe these are also defined in include/linux/interrupt.h */
++typedef void irqreturn_t;
++#define IRQ_NONE
++#define IRQ_HANDLED
++#define IRQ_RETVAL(x)
++#endif
++#define CAPI_SendMessage_void
++#define OLDCAPI_DRIVER_INTERFACE
++#undef  HAS_WORKQUEUE
++#define work_struct   tq_struct
++#define INIT_WORK(q, f, d)    (q)->routine=f;(q)->data=d;
++#define schedule_work(q)      queue_task(q, &tq_immediate);mark_bh(IMMEDIATE_BH);
++#define MAKEDAEMON(n)         daemonize();strcpy(current->comm, n)
++#undef NEW_ISAPNP
++#define pnp_register_driver(d)                isapnp_register_driver(d)
++#define pnp_unregister_driver(d)      isapnp_unregister_driver(d)
++#define pnp_get_drvdata(d)            pci_get_drvdata(d)
++#define pnp_set_drvdata(p,d)          pci_set_drvdata(p,d)
++#define pnp_activate_dev(d)           isapnp_activate_dev(d, "mISDN")
++#define pnp_disable_dev(d)            ((struct pci_dev *)d)->prepare(d);((struct pci_dev *)d)->deactivate(d)
++#define pnp_port_start(d,n)           d->resource[n].start
++#define pnp_irq(d,n)                  d->irq_resource[n].start
++#undef iminor
++#define iminor(i)     MINOR(i->i_rdev)
++#else
++#undef  OLDCAPI_DRIVER_INTERFACE
++#define HAS_WORKQUEUE
++#undef  MINOR
++#define MINOR(inode)  minor(inode)
++#define NEED_JIFFIES_INCLUDE
++#define MAKEDAEMON(n) daemonize(n)
++#define NEW_ISAPNP
++#endif /* 2,5,0 */
++
++#ifndef COMPAT_HAS_NEW_WAITQ
++typedef struct wait_queue wait_queue_t;
++typedef struct wait_queue *wait_queue_head_t;
++
++#define DECLARE_WAITQUEUE(wait, current)      struct wait_queue wait = { current, NULL }
++#define DECLARE_WAIT_QUEUE_HEAD(wait)         wait_queue_head_t wait
++#define init_waitqueue_head(x)                        *(x)=NULL
++#define init_waitqueue_entry(q,p)             ((q)->task)=(p)
++#endif /* COMPAT_HAS_NEW_WAITQ */
++
++#endif /* __KERNEL__ */
++#endif /* _LINUX_ISDN_COMPAT_H */
+diff -Nur --exclude '*.cmd' --exclude '*.mod' --exclude '*.orig' --exclude '*.rej' --exclude '*.o' linux-2.6.8.1.org/include/linux/mISDNif.h linux-2.6.8.1/include/linux/mISDNif.h
+--- linux-2.6.8.1.org/include/linux/mISDNif.h  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.8.1/include/linux/mISDNif.h      2004-11-22 09:33:38.516688488 +0000
+@@ -0,0 +1,748 @@
++/* $Id$
++ *
++ */
++
++#ifndef mISDNIF_H
++#define mISDNIF_H
++
++#include <stdarg.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++
++/* primitives for information exchange
++ * generell format
++ * <8 bit reserved>
++ * <4 bit flags>
++ * <4 bit layer>
++ * <8 bit command>
++ * <8 bit subcommand>
++ *
++ */
++ 
++/*
++ * ABI Version 32 bit
++ *
++ * <16 bit> Major version
++ *            - changed if any interface become backwards incompatible
++ *
++ * <16 bit> Minor version
++ *              - changed if any interface is extended but backwards compatible
++ *
++ */
++#define       MISDN_MAJOR_VERSION     1
++#define       MISDN_MINOR_VERSION     0
++#define       MISDN_VERSION           ((MISDN_MAJOR_VERSION<<16) | MISDN_MINOR_VERSION)
++
++#define MISDN_REVISION                "$Revision$"
++#define MISDN_DATE            "$Date$"
++
++/* SUBCOMMANDS */
++#define REQUEST               0x80
++#define CONFIRM               0x81
++#define INDICATION    0x82
++#define RESPONSE      0x83
++#define SUB_ERROR     0xff
++
++/* management */
++#define MGR_FUNCTION  0x0f0000
++#define MGR_GETOBJECT 0x0f0100
++#define MGR_NEWOBJECT 0x0f0200
++#define MGR_DELOBJECT 0x0f0300
++#define MGR_NEWENTITY 0x0f0600
++#define MGR_DELENTITY 0x0f0700
++#define MGR_GETSTACK  0x0f1100
++#define MGR_NEWSTACK  0x0f1200
++#define MGR_DELSTACK  0x0f1300
++#define MGR_SETSTACK  0x0f1400
++#define MGR_CLEARSTACK        0x0f1500
++#define MGR_REGLAYER  0x0f1600
++#define MGR_UNREGLAYER        0x0f1700
++#define MGR_SELCHANNEL        0x0f1800
++#define MGR_SETSTACK_NW       0x0f1900
++#define MGR_ADDSTPARA 0x0f1A00
++#define MGR_CLRSTPARA 0x0f1B00
++#define MGR_GETLAYER  0x0f2100
++#define MGR_GETLAYERID        0x0f2200
++#define MGR_NEWLAYER  0x0f2300
++#define MGR_DELLAYER  0x0f2400
++#define MGR_CLONELAYER        0x0f2500
++#define MGR_GETIF     0x0f3100
++#define MGR_CONNECT   0x0f3200
++#define MGR_DISCONNECT        0x0f3300
++#define MGR_SETIF     0x0f3400
++#define MGR_ADDIF     0x0f3500
++#define MGR_QUEUEIF   0x0f3600
++#define MGR_CTRLREADY 0x0f4100
++#define MGR_RELEASE   0x0f4500
++#define MGR_GETDEVICE 0x0f5100
++#define MGR_DELDEVICE 0x0f5200
++#define MGR_SETDEVOPT 0x0f5300
++#define MGR_GETDEVOPT 0x0f5400
++#define MGR_INITTIMER 0x0f8100
++#define MGR_ADDTIMER  0x0f8200
++#define MGR_DELTIMER  0x0f8300
++#define MGR_REMOVETIMER       0x0f8400
++#define MGR_TIMER     0x0f8800
++#define MGR_CONTROL   0x0fe100
++#define MGR_STATUS    0x0fe200
++#define MGR_HASPROTOCOL 0x0fe300
++#define MGR_EVALSTACK 0x0fe400
++#define MGR_GLOBALOPT 0x0fe500
++#define MGR_LOADFIRM  0x0ff000
++#define MGR_LOGDATA   0x0ff100
++#define MGR_DEBUGDATA 0x0ff200
++#define MGR_VERSION   0x0fff00
++
++/* layer 1 <-> hardware */
++#define PH_SIGNAL     0x000100
++#define PH_CONTROL    0x000200
++#define PH_STATUS     0x000300
++
++/* PH_SIGNAL parameter */
++#define INFO0         0x1000
++#define INFO1         0x1100
++#define INFO2         0x1200
++#define INFO3_P8      0x1308
++#define INFO3_P10     0x130a
++#define INFO4_P8      0x1408
++#define INFO4_P10     0x140a
++#define LOSTFRAMING   0x1f00
++#define ANYSIGNAL     0x1f01
++
++/* PH_CONTROL parameter */
++#define HW_RESET      0x0001 
++#define HW_POWERDOWN  0x0100 
++#define HW_POWERUP    0x0101
++#define HW_DEACTIVATE 0x0200
++#define HW_ACTIVATE   0x0201
++#define HW_MOD_FRM    0x0400
++#define HW_MOD_FRH    0x0401
++#define HW_MOD_FTM    0x0402
++#define HW_MOD_FTH    0x0403
++#define HW_MOD_FTS    0x0404
++#define HW_MOD_CONNECT        0x0410
++#define HW_MOD_OK     0x0411
++#define HW_MOD_NOCARR 0x0412
++#define HW_MOD_FCERROR        0x0413
++#define HW_MOD_READY  0x0414
++#define HW_MOD_LASTDATA       0x0415
++#define HW_MOD_SILENCE        0x0416
++#define HW_FEATURES   0x04ff
++#define HW_HFC_COEFF  0x0500
++#define HW_LOS                0x0501
++#define HW_LOS_OFF    0x0502
++#define HW_AIS                0x0503
++#define HW_AIS_OFF    0x0504
++#define HW_SLIP_TX    0x0505
++#define HW_SLIP_RX    0x0506
++#define HW_PCM_CONN   0x0580
++#define HW_PCM_DISC   0x0581
++#define HW_CONF_JOIN  0x0582
++#define HW_CONF_SPLIT 0x0583
++#define HW_RECEIVE_OFF        0x0584
++#define HW_RECEIVE_ON 0x0585
++#define HW_SPL_LOOP_ON        0x0586
++#define HW_SPL_LOOP_OFF       0x0587
++#define HW_TESTLOOP   0xFF00
++#define HW_FIRM_START 0xFF10
++#define HW_FIRM_DATA  0xFF11
++#define HW_FIRM_END   0xFF12
++#define HW_D_BLOCKED  0xFF20 
++#define HW_D_NOBLOCKED        0xFF21 
++#define HW_TESTRX_RAW 0xFF40
++#define HW_TESTRX_HDLC        0xFF41
++#define HW_TESTRX_OFF 0xFF4f
++/* 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 CMX_CONF_JOIN 0x2300
++#define CMX_CONF_SPLIT        0x2301
++#define CMX_ECHO_ON   0x2302
++#define CMX_ECHO_OFF  0x2303
++#define CMX_RECEIVE_OFF       0x2304
++#define CMX_RECEIVE_ON        0x2305
++#define CMX_MIX_ON    0x2306
++#define CMX_MIX_OFF   0x2307
++#define TONE_PATT_ON  0x2310
++#define TONE_PATT_OFF 0x2311
++#define VOL_CHANGE_TX 0x2312
++#define VOL_CHANGE_RX 0x2313
++#define BF_ENABLE_KEY 0x2314
++#define BF_DISABLE    0x2315
++#define BF_ACCEPT     0x2316
++#define BF_REJECT     0x2317
++#define HW_POTS_ON            0x1001
++#define HW_POTS_OFF           0x1002
++#define HW_POTS_SETMICVOL     0x1100
++#define HW_POTS_SETSPKVOL     0x1101
++#define HW_POTS_GETMICVOL     0x1110
++#define HW_POTS_GETSPKVOL     0x1111
++
++/* 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
++
++#define GLOBALOPT_INTERNAL_CTRL               0x0001
++#define GLOBALOPT_EXTERNAL_EQUIPMENT  0x0002
++#define GLOBALOPT_HANDSET             0x0004
++#define GLOBALOPT_DTMF                        0x0008
++#define GLOBALOPT_SUPPLEMENTARY_SERVICE       0x0010
++#define GLOBALOPT_CHANNEL_ALLOCATION  0x0020
++#define GLOBALOPT_PARAMETER_B_CHANNEL 0x0040
++#define GLOBALOPT_LINE_INTERCONNECT   0x0080
++
++
++/* layer 1 */
++#define PH_ACTIVATE   0x010100
++#define PH_DEACTIVATE 0x010000
++#define PH_DATA               0x110200
++#define MPH_DEACTIVATE        0x011000
++#define MPH_ACTIVATE  0x011100
++#define MPH_INFORMATION       0x012000
++ 
++/* layer 2 */
++#define DL_ESTABLISH  0x020100
++#define DL_RELEASE    0x020000
++#define DL_DATA               0x120200
++#define DL_UNITDATA   0x120300
++#define MDL_UNITDATA  0x121200
++#define MDL_ASSIGN    0x022100
++#define MDL_REMOVE    0x022000
++#define MDL_ERROR     0x023000
++#define MDL_INFORMATION       0x024000
++#define MDL_STATUS    0x028100
++#define MDL_FINDTEI   0x028200
++
++/* layer 3 */
++#define CC_ALERTING           0x030100
++#define CC_PROCEEDING         0x030200
++#define CC_PROGRESS           0x030300
++#define CC_SETUP              0x030500
++#define CC_CONNECT            0x030700
++#define CC_SETUP_ACKNOWLEDGE  0x030d00
++#define CC_CONNECT_ACKNOWLEDGE        0x030f00
++#define CC_USER_INFORMATION   0x032000
++#define CC_SUSPEND_REJECT     0x032100
++#define CC_RESUME_REJECT      0x032200
++#define CC_HOLD                       0x032400
++#define CC_SUSPEND            0x032500
++#define CC_RESUME             0x032600
++#define CC_HOLD_ACKNOWLEDGE   0x032800
++#define CC_SUSPEND_ACKNOWLEDGE        0x032d00
++#define CC_RESUME_ACKNOWLEDGE 0x032e00
++#define CC_HOLD_REJECT                0x033000
++#define CC_RETRIEVE           0x033100
++#define CC_RETRIEVE_ACKNOWLEDGE       0x033300
++#define CC_RETRIEVE_REJECT    0x033700
++#define CC_DISCONNECT         0x034500
++#define CC_RESTART            0x034600
++#define CC_RELEASE            0x034d00
++#define CC_RELEASE_COMPLETE   0x035a00
++#define CC_FACILITY           0x036200
++#define CC_NOTIFY             0x036e00
++#define CC_STATUS_ENQUIRY     0x037500
++#define CC_INFORMATION                0x037b00
++#define CC_STATUS             0x037d00
++
++#define CC_NEW_CR             0x03f000
++#define CC_RELEASE_CR         0x03f100
++#define CC_TIMEOUT            0x03ff00
++
++#define CC_B3_DATA            0x138600
++
++#define LAYER_MASK    0x0F0000
++#define COMMAND_MASK  0x00FF00
++#define SUBCOMMAND_MASK       0x0000FF
++#define DATA_COMMAND  0x100000
++#define CMD_IS_DATA(p)        (p & DATA_COMMAND)
++
++/* short cuts layer 1 */
++#define PH_ACTIVATE_REQ               (PH_ACTIVATE | REQUEST)
++#define PH_ACTIVATE_IND               (PH_ACTIVATE | INDICATION)
++#define PH_DEACTIVATE_IND     (PH_DEACTIVATE | INDICATION)
++#define PH_DATA_REQ           (PH_DATA | REQUEST)
++#define PH_DATA_IND           (PH_DATA | INDICATION)
++#define PH_DATA_CNF           (PH_DATA | CONFIRM)
++#define PH_DATA_RSP           (PH_DATA | RESPONSE)
++#define MPH_ACTIVATE_REQ      (MPH_ACTIVATE | REQUEST)
++#define MPH_DEACTIVATE_REQ    (MPH_DEACTIVATE | REQUEST)
++#define MPH_INFORMATION_IND   (MPH_INFORMATION | INDICATION)
++
++/* short cuts layer 2 */
++#define DL_ESTABLISH_REQ      (DL_ESTABLISH | REQUEST)
++#define DL_ESTABLISH_IND      (DL_ESTABLISH | INDICATION)
++#define DL_ESTABLISH_CNF      (DL_ESTABLISH | CONFIRM)
++#define DL_RELEASE_REQ                (DL_RELEASE | REQUEST)
++#define DL_RELEASE_IND                (DL_RELEASE | INDICATION)
++#define DL_RELEASE_CNF                (DL_RELEASE | CONFIRM)
++#define DL_DATA_REQ           (DL_DATA | REQUEST)
++#define DL_DATA_IND           (DL_DATA | INDICATION)
++#define DL_UNITDATA_REQ               (DL_UNITDATA | REQUEST)
++#define DL_UNITDATA_IND               (DL_UNITDATA | INDICATION)
++#define MDL_ASSIGN_REQ                (MDL_ASSIGN | REQUEST)
++#define MDL_ASSIGN_IND                (MDL_ASSIGN | INDICATION)
++#define MDL_REMOVE_REQ                (MDL_REMOVE | REQUEST)
++#define MDL_ERROR_IND         (MDL_ERROR | INDICATION)
++#define MDL_ERROR_RSP         (MDL_ERROR | RESPONSE)
++#define MDL_INFORMATION_IND   (MDL_INFORMATION | INDICATION)
++
++/* protocol id */
++#define ISDN_PID_NONE                 0
++#define ISDN_PID_ANY                  0xffffffff
++#define ISDN_PID_L0_TE_S0             0x00000001
++#define ISDN_PID_L0_NT_S0             0x00000100
++#define ISDN_PID_L0_TE_U              0x00000002
++#define ISDN_PID_L0_NT_U              0x00000200
++#define ISDN_PID_L0_TE_UP2            0x00000004
++#define ISDN_PID_L0_NT_UP2            0x00000400
++#define ISDN_PID_L0_TE_E1             0x00000008
++#define ISDN_PID_L0_NT_E1             0x00000800
++#define ISDN_PID_L1_TE_S0             0x01000001
++#define ISDN_PID_L1_NT_S0             0x01000100
++#define ISDN_PID_L1_TE_U              0x01000002
++#define ISDN_PID_L1_NT_U              0x01000200
++#define ISDN_PID_L1_TE_UP2            0x01000004
++#define ISDN_PID_L1_NT_UP2            0x01000400
++#define ISDN_PID_L1_TE_E1             0x01000008
++#define ISDN_PID_L1_NT_E1             0x01000800
++/* Bit 15-0  reserved for CAPI defined protocols */
++#define ISDN_PID_L1_B_64HDLC          0x41000001
++#define ISDN_PID_L1_B_64TRANS         0x41000002
++#define ISDN_PID_L1_B_V110_ASYNC      0x41000004
++#define ISDN_PID_L1_B_V110_HDLC               0x41000008
++#define ISDN_PID_L1_B_T30FAX          0x41000010
++#define ISDN_PID_L1_B_64HDLC_INV      0x41000020
++#define ISDN_PID_L1_B_56TRANS         0x41000040
++#define ISDN_PID_L1_B_MODEM_ALL               0x41000080
++#define ISDN_PID_L1_B_MODEM_ASYNC     0x41000100
++#define ISDN_PID_L1_B_MODEM_HDLC      0x41000200
++#define ISDN_PID_L2_LAPD              0x02000001
++#define ISDN_PID_L2_LAPD_NET          0x02000002
++/* Bit 15-0  reserved for CAPI defined protocols */
++#define ISDN_PID_L2_B_X75SLP          0x42000001
++#define ISDN_PID_L2_B_TRANS           0x42000002
++#define ISDN_PID_L2_B_TRANSDTMF               0x42300002
++#define ISDN_PID_L2_B_RAWDEV          0x42400002
++#define ISDN_PID_L2_B_SDLC            0x42000004
++#define ISDN_PID_L2_B_LAPD            0x42000008
++#define ISDN_PID_L2_B_T30             0x42000010
++#define ISDN_PID_L2_B_PPP             0x42000020
++#define ISDN_PID_L2_B_TRANSNOERR      0x42000040
++#define ISDN_PID_L2_B_MODEM           0x42000080
++#define ISDN_PID_L2_B_X75SLPV42BIS    0x42000100
++#define ISDN_PID_L2_B_V120ASYNC               0x42000200
++#define ISDN_PID_L2_B_V120ASYNCV42BIS 0x42000400
++#define ISDN_PID_L2_B_V120TRANS               0x42000800
++#define ISDN_PID_L2_B_LAPDFREESAPI    0x42001000
++#define ISDN_PID_L3_B_TRANS           0x43000001
++#define ISDN_PID_L3_B_T90NL           0x43000002
++#define ISDN_PID_L3_B_X25DTE          0x43000004
++#define ISDN_PID_L3_B_X25DCE          0x43000008
++#define ISDN_PID_L3_B_T30             0x43000010
++#define ISDN_PID_L3_B_T30EXT          0x43000020
++#define ISDN_PID_L3_B_MODEM           0x43000080
++/* Bit 15-0  reserved for CAPI defined protocols */
++#define ISDN_PID_L3_B_DSP             0x43010000
++#define ISDN_PID_L3_DSS1USER          0x03000001
++#define ISDN_PID_L3_DSS1NET           0x03000100
++#define ISDN_PID_L4_CAPI20            0x04000001
++#define ISDN_PID_L4_B_CAPI20          0x44000001
++
++#define ISDN_PID_BCHANNEL_BIT         0x40000000
++#define ISDN_PID_LAYER_MASK           0x0f000000
++#define ISDN_PID_LAYER(n)             (n<<24)
++#define ISDN_PID_FEATURE_MASK         0x00F00000
++#define ISDN_PID_IDX_MAX              23
++
++#define ISDN_PID_L2_DF_PTP            0x00100000
++#define ISDN_PID_L2_DF_MULT_TEI               0x00200000
++#define       ISDN_PID_L3_DF_PTP              0x00100000
++#define ISDN_PID_L3_DF_EXTCID         0x00200000
++#define ISDN_PID_L3_DF_CRLEN2         0x00400000
++
++#define mISDN_CORE_DEVICE     0
++#define mISDN_RAW_DEVICE      128
++
++#define FLG_mISDNPORT_BUSY    1
++#define FLG_mISDNPORT_ENABLED 2
++#define FLG_mISDNPORT_BLOCK   3
++#define FLG_mISDNPORT_OPEN    4
++#define FLG_mISDNPORT_ONEFRAME        5
++
++
++/*
++ * A "ENTITY" is a instance which assign id's with a none local
++ * scope. A id has a local part (a range of ids which the instance
++ * maintains) and the global ENTITY ID.
++ * A instance which wan't to assign IDs have to request a unique
++ * ENTITY ID with MGR_NEWENTITY | REQUEST.
++ * If the instance is deleted or don't need this feature anymore
++ * it has to delete the ENTITY with MGR_DELENTITY | REQUEST.
++ * ENTITY ID 0xFFFF is a special one.
++ * One example for using this is the L3/L4 process ID.
++ */
++
++#define MISDN_MAX_ENTITY      2048
++#define MISDN_ENTITY_NONE     0x0000ffff
++#define MISDN_ID_ENTITYMASK   0xffff0000
++#define MISDN_ID_LOCALMASK    0x0000FFFF
++#define MISDN_ID_ANY          0xffffffff
++#define MISDN_ID_NONE         0xfffffffe
++#define MISDN_ID_DUMMY                0xffff0001
++#define MISDN_ID_GLOBAL               0xffff0002
++
++#define MAX_LAYER_NR  7
++#define ISDN_LAYER(n) (1<<n)
++#define LAYER_OUTRANGE(layer) ((layer<0) || (layer>MAX_LAYER_NR))
++#define mISDN_MAX_IDLEN       16
++
++#define IF_NOACTIV    0x00000000
++#define IF_DOWN               0x01000000
++#define IF_UP         0x02000000
++#define IF_CHAIN      0x04000000
++#define IF_HANDSHAKE  0x08000000
++#define IF_TYPEMASK   0x07000000
++#define IF_ADDRMASK   0xF0FFFFFF
++#define IF_IADDRMASK  0xF0FFFFFF
++#define IF_CONTRMASK  0x000000FF
++#define IF_CHILDMASK  0x1000FF00
++#define IF_CLONEMASK  0x2000FF00
++#define IF_INSTMASK   0x400F0000
++#define IF_LAYERMASK  0x00F00000
++#define IF_TYPE(i)    ((i)->stat & IF_TYPEMASK)
++#define CHILD_ID_INC  0x00000100
++#define CHILD_ID_MAX  0x1000FF00
++#define CLONE_ID_INC  0x00000100
++#define CLONE_ID_MAX  0x2000FF00
++#define INST_ID_INC   0x00010000
++#define INST_ID_MAX   0x400F0000
++#define FLG_CHILD_STACK       0x10000000
++#define FLG_CLONE_STACK       0x20000000
++#define FLG_INSTANCE  0x40000000
++
++#define DUMMY_CR_FLAG 0x7FFFFF00
++#define CONTROLER_MASK        0x000000FF
++
++/* stack channel values */
++#define CHANNEL_NUMBER        0x000000FF
++#define CHANNEL_RXSLOT        0x0000FF00
++#define CHANNEL_TXSLOT        0x00FF0000
++#define CHANNEL_EXTINFO       0xFF000000
++#define CHANNEL_NR_D  0x00000000
++#define CHANNEL_NR_B1 0x00000001
++#define CHANNEL_NR_B2 0x00000002
++#define CHANNEL_EXT_PCM       0x01000000
++#define CHANNEL_EXT_REV       0x02000000
++
++/* interface extentions */
++#define EXT_STACK_CLONE 0x00000001
++#define EXT_INST_CLONE        0x00000100
++#define EXT_INST_MGR  0x00000200
++#define EXT_INST_MIDDLE       0x00000400
++#define EXT_IF_CHAIN  0x00010000
++#define EXT_IF_EXCLUSIV       0x00020000
++#define EXT_IF_CREATE 0x00040000
++#define EXT_IF_SPLIT  0x00080000
++
++
++/* special packet type */
++#define PACKET_NOACK  250
++
++/* limits for buffers */
++
++#define MAX_PHONE_DIGIT               31
++#define MAX_DFRAME_LEN                260
++#define MAX_DATA_SIZE         2048
++#define MAX_DATA_MEM          2080
++#define MAX_HEADER_LEN                4
++#define       DEFAULT_PORT_QUEUELEN   256
++#define PORT_SKB_RESERVE      L3_EXTRA_SIZE
++#define PORT_SKB_MINIMUM      128
++
++/* structure for information exchange between layer/entity boundaries */
++
++#define STATUS_INFO_L1        1
++#define STATUS_INFO_L2        2
++
++typedef struct _mISDN_head {
++      u_int   addr __attribute__((packed));
++      u_int   prim __attribute__((packed));
++      int     dinfo __attribute__((packed));
++      int     len __attribute__((packed));
++} mISDN_head_t;
++
++#define mISDN_HEADER_LEN      sizeof(mISDN_head_t)
++
++typedef struct _status_info {
++      int     len;
++      int     typ;
++      u_char  info[120];
++} status_info_t;
++
++typedef struct _logdata {
++      char    *head;
++      char    *fmt;
++      va_list args;   
++} logdata_t;
++
++typedef struct _moditem {
++      char    *name;
++      int     protocol;
++} moditem_t;
++
++typedef struct _mISDN_pid {
++      int     protocol[MAX_LAYER_NR +1];
++      u_char  *param[MAX_LAYER_NR +1];
++      __u16   global;
++      int     layermask;
++      int     maxplen;
++      u_char  *pbuf;
++} mISDN_pid_t;
++
++typedef struct _mISDN_stPara {
++      int     maxdatalen;
++      int     up_headerlen;
++      int     down_headerlen;
++} mISDN_stPara_t;
++
++typedef struct _stack_info {
++      u_int           id;
++      mISDN_pid_t     pid;
++      mISDN_stPara_t  para;
++      u_int           extentions;
++      u_int           mgr;
++      int             instcnt;
++      int             inst[MAX_LAYER_NR +1];
++      int             childcnt;
++      u_int           child[2]; /* this is correct handled for PRI see get_stack_info() */
++} stack_info_t;
++
++typedef struct _layer_info {
++      char            name[mISDN_MAX_IDLEN];
++      int             object_id;
++      int             extentions;
++      u_int           id;
++      u_int           st;
++      mISDN_pid_t     pid;
++} layer_info_t;
++
++
++typedef struct _interface_info {
++      int             extentions;
++      u_int           owner;
++      u_int           peer;
++      int             stat;
++} interface_info_t;
++
++typedef struct _channel_info {
++      u_int           channel;
++      union {
++              u_int   id;
++              void    *p;
++      } st;
++} channel_info_t;
++
++/* l3 pointer arrays */
++
++typedef struct _Q931_info {
++      u_char  type __attribute__((packed));
++      u_char  crlen __attribute__((packed));
++      u16     cr __attribute__((packed));
++      u16     bearer_capability __attribute__((packed));
++      u16     cause __attribute__((packed));
++      u16     call_id __attribute__((packed));
++      u16     call_state __attribute__((packed));
++      u16     channel_id __attribute__((packed));
++      u16     facility __attribute__((packed));
++      u16     progress __attribute__((packed));
++      u16     net_fac __attribute__((packed));
++      u16     notify __attribute__((packed));
++      u16     display __attribute__((packed));
++      u16     date __attribute__((packed));
++      u16     keypad __attribute__((packed));
++      u16     signal __attribute__((packed));
++      u16     info_rate __attribute__((packed));
++      u16     end2end_transit __attribute__((packed));
++      u16     transit_delay_sel __attribute__((packed));
++      u16     pktl_bin_para __attribute__((packed));
++      u16     pktl_window __attribute__((packed));
++      u16     pkt_size __attribute__((packed));
++      u16     closed_userg __attribute__((packed));
++      u16     connected_nr __attribute__((packed));
++      u16     connected_sub __attribute__((packed));
++      u16     calling_nr __attribute__((packed));
++      u16     calling_sub __attribute__((packed));
++      u16     called_nr __attribute__((packed));
++      u16     called_sub __attribute__((packed));
++      u16     redirect_nr __attribute__((packed));
++      u16     transit_net_sel __attribute__((packed));
++      u16     restart_ind __attribute__((packed));
++      u16     llc __attribute__((packed));
++      u16     hlc __attribute__((packed));
++      u16     useruser __attribute__((packed));
++      u16     more_data __attribute__((packed));
++      u16     sending_complete __attribute__((packed));
++      u16     congestion_level __attribute__((packed));
++      u16     fill1 __attribute__((packed));
++} Q931_info_t;
++
++#define L3_EXTRA_SIZE sizeof(Q931_info_t)
++
++#ifdef __KERNEL__
++#include <linux/isdn_compat.h>
++#include <linux/list.h>
++#include <linux/skbuff.h>
++
++typedef struct _mISDNobject   mISDNobject_t;
++typedef struct _mISDNinstance mISDNinstance_t;
++typedef struct _mISDNlayer    mISDNlayer_t;
++typedef struct _mISDNstack    mISDNstack_t;
++typedef struct _mISDNport     mISDNport_t;
++typedef struct _mISDNdevice   mISDNdevice_t;
++typedef struct _mISDNif               mISDNif_t;
++typedef int   (ctrl_func_t)(void *, u_int, void *);
++typedef int   (if_func_t)(struct _mISDNif *, struct sk_buff *);
++typedef int   (lock_func_t)(void *, int);
++typedef void  (unlock_func_t)(void *);
++
++#define mISDN_HEAD_P(s)               ((mISDN_head_t *)&s->cb[0])
++#define mISDN_HEAD_PRIM(s)    ((mISDN_head_t *)&s->cb[0])->prim
++#define mISDN_HEAD_DINFO(s)   ((mISDN_head_t *)&s->cb[0])->dinfo
++
++typedef struct _mISDN_headext {
++      u_int   addr __attribute__((packed));
++      u_int   prim __attribute__((packed));
++      int     dinfo  __attribute__((packed));
++      void    *data[3];
++      union {
++              ctrl_func_t     *ctrl;
++              if_func_t       *iff;
++              void            *func;
++      } func;
++} mISDN_headext_t;
++
++#define mISDN_HEADEXT_P(s) ((mISDN_headext_t *)&s->cb[0])
++
++/* Basic struct of a mISDN component */
++struct _mISDNobject {
++      struct list_head        list;
++      char                    *name;
++      int                     id;
++      int                     refcnt;
++      mISDN_pid_t             DPROTO;
++      mISDN_pid_t             BPROTO;
++      ctrl_func_t             *own_ctrl;
++      ctrl_func_t             *ctrl;
++      struct list_head        ilist;
++      struct module           *owner;
++};
++
++/* the interface between two mISDNinstances */
++struct _mISDNif {
++      if_func_t               *func;
++      void                    *fdata;
++      mISDNif_t               *clone;
++      mISDNif_t               *predecessor;
++      int                     extentions;
++      int                     stat;
++      mISDNstack_t            *st;
++      mISDNinstance_t         *owner;
++      mISDNinstance_t         *peer;
++};
++
++/* a instance of a mISDNobject */
++struct _mISDNinstance {
++      struct list_head        list;
++      char                    name[mISDN_MAX_IDLEN];
++      int                     extentions;
++      u_int                   id;
++      mISDN_pid_t             pid;
++      mISDNstack_t            *st;
++      mISDNobject_t           *obj;
++      void                    *data;
++      mISDNif_t               up;
++      mISDNif_t               down;
++      lock_func_t             *lock;
++      unlock_func_t           *unlock;
++};
++
++/* a list of parallel (horizontal) mISDNinstances in the same layer
++ * normally here is only one instance per layer only if the information
++ * will be splitted here are more instances */
++struct _mISDNlayer {
++      struct list_head        list;
++      mISDNinstance_t         *inst;
++};
++
++/* the STACK; a (vertical) chain of layers */
++ 
++struct _mISDNstack {
++      struct list_head        list;
++      u_int                   id;
++      u_int                   extentions;
++      mISDN_pid_t             pid;
++      mISDN_stPara_t          para;
++      struct list_head        layerlist;
++      mISDNinstance_t         *mgr;
++      struct list_head        childlist;
++};
++
++/* lowlevel read/write struct for the mISDNdevice */
++struct _mISDNport {
++      wait_queue_head_t       procq;
++      spinlock_t              lock;
++      mISDNif_t               pif;
++      u_long                  Flag;
++      struct sk_buff_head     queue;
++      u_int                   maxqlen;
++};
++
++/* the user interface to handle /dev/mISDN */
++struct _mISDNdevice {
++      struct list_head        list;
++      int                     minor;
++      struct semaphore        io_sema;
++      int                     open_mode;
++      mISDNport_t             rport;
++      mISDNport_t             wport;
++      struct list_head        layerlist;
++      struct list_head        stacklist;
++      struct list_head        timerlist;
++      struct list_head        entitylist;
++};
++
++/* common helper functions */
++extern int    mISDN_bprotocol2pid(void *, mISDN_pid_t *);
++extern int    mISDN_SetIF(mISDNinstance_t *, mISDNif_t *, u_int, void *, void *, void *);
++extern int    mISDN_ConnectIF(mISDNinstance_t *, mISDNinstance_t *);
++extern int    mISDN_DisConnectIF(mISDNinstance_t *, mISDNif_t *);
++
++/* global register/unregister functions */
++
++extern int    mISDN_register(mISDNobject_t *obj);
++extern int    mISDN_unregister(mISDNobject_t *obj);
++
++#endif /* __KERNEL__ */
++#endif /* mISDNIF_H */
This page took 1.867914 seconds and 4 git commands to generate.